diff --git a/buckler.py b/buckler.py index 2a20d9b..a1bf501 100644 --- a/buckler.py +++ b/buckler.py @@ -55,11 +55,13 @@ async def index(request): request['session']['id']) if request['session']['admin']: apps = await conn.fetch( - "SELECT id, name FROM app_info") + "SELECT id, name FROM app_info ORDER BY id ASC") + users = await conn.fetch( + "SELECT id, username FROM user_info ORDER BY id ASC") user_perms = await conn.fetch( - "SELECT user_info.id, user_info.username, app_user.app_id " + "SELECT user_info.id, app_user.app_id " "FROM user_info LEFT JOIN app_user " - "ON (user_info.id = app_user.user_id)") + "ON (user_info.id = app_user.user_id) ") fido2_keys = await conn.fetch( "SELECT * FROM user_credential WHERE user_id = $1", request['session']['id']) @@ -70,12 +72,18 @@ async def index(request): request['session']['id']) if request['session']['admin']: - users = defaultdict(lambda: {app['name']: False for app in apps}) - for user_perm in user_perms: - index = tools.find_dict(apps, 'id', user_perm['app_id']) - if index != -1: - users[user_perm['username']][apps[index]['name']] = True - users_json = json.dumps(users) + user_perms_dict = {} + for user in users: + lst = [up for up in user_perms if up['id'] == user['id']] + d = {app['id']: False for app in apps} + for perm in lst: + if perm['app_id'] not in d: + continue + d[perm['app_id']] = True + user_perms_dict[user['id']] = d + users_dict = {user['id']: user['username'] for user in users} + apps_dict = {app['id']: app['name'] for app in apps} + user_perms_json = json.dumps(user_perms_dict) return render_template("index.html", request, locals()) diff --git a/forms.py b/forms.py index e818b87..35d0339 100644 --- a/forms.py +++ b/forms.py @@ -2,6 +2,8 @@ """ Co-routines for handling various forms in Buckler. """ +import json + from passlib.hash import argon2 import tools @@ -26,33 +28,24 @@ async def invite_user(request): async def change_user_perms(request): """Allows an admin to change user permissions.""" data = await request.post() + data = json.loads(data['perms']) + pluses = [] - for key, value in data.items(): - if key == 'form_name': - continue - username, _, app_name = key.partition('-') - pluses.append((username, app_name)) + minuses = [] + for user_id, perms in data.items(): + for app_id, value in perms.items(): + if value: # TODO: check for TypeError + pluses.append((int(user_id), int(app_id))) + else: + minuses.append((int(user_id), int(app_id))) async with request.app['pool'].acquire() as conn: - apps = await conn.fetch( - "SELECT id, name FROM app_info") - users = {p[0] for p in pluses} - minuses = [] - for user in users: - for app in apps: - minuses.append((user, app['name'])) - minuses = [m for m in minuses if m not in pluses] - await conn.executemany( - "INSERT INTO app_user (user_id, app_id) " - "VALUES ((SELECT id FROM user_info WHERE username = $1), " - "(SELECT id FROM app_info WHERE name = $2)) " + "INSERT INTO app_user (user_id, app_id) VALUES ($1, $2) " "ON CONFLICT ON CONSTRAINT app_user_pkey DO NOTHING", pluses) await conn.executemany( - "DELETE FROM app_user " - "WHERE user_id = (SELECT id FROM user_info WHERE username = $1) " - "AND app_id = (SELECT id FROM app_info WHERE name = $2)", + "DELETE FROM app_user WHERE user_id = $1 AND app_id = $2", minuses) return {} diff --git a/static/buckler.js b/static/buckler.js index fa085a4..0009e6c 100644 --- a/static/buckler.js +++ b/static/buckler.js @@ -15,44 +15,39 @@ function load() { } }); }); + document.querySelector('#user_perm_form').addEventListener('submit', submit_user_perms); } -function perm_change(row) { - let user_perms = users_perms[row.children[0].textContent]; - let row_perms = {}; - for (let child of row.children) { - if (child.firstChild.type == "checkbox") { - if (child.firstChild.checked) { - row_perms[child.firstChild.dataset.appName] = true; - } else { - row_perms[child.firstChild.dataset.appName] = false; - } +function submit_user_perms(event) { + event.preventDefault(); + let perms_diff = {}; + for (let input of event.target.querySelectorAll('input[type=checkbox]')) { + let user_id = input.dataset['userId']; + let app_id = input.dataset['appId']; + if (input.checked != user_perms[user_id][app_id]) { + let perms = perms_diff[user_id] || {}; + perms[app_id] = input.checked; + perms_diff[user_id] = perms; } } - let perms_changed = false; - for (let app_name of Object.keys(user_perms)) { - if (user_perms[app_name] != row_perms[app_name]) { - perms_changed = true; - } + + let form_body = { + 'form_name': 'change_user_perms', + 'perms': JSON.stringify(perms_diff), } - console.log("perms_changed = " + perms_changed); -} + let payload = []; + for (let property in form_body) { + let encoded_key = encodeURIComponent(property); + let encoded_value = encodeURIComponent(form_body[property]); + payload.push(encoded_key + '=' + encoded_value); + } + payload = payload.join('&'); -function submit_user_perms(row) { - let row_perms = {}; - for (let child of row.children) { - if (child.firstChild.type == "checkbox") { - if (child.firstChild.checked) { - row_perms[child.firstChild.dataset.appName] = true; - } else { - row_perms[child.firstChild.dataset.appName] = false; - } - } - } - console.log(row_perms); fetch(window.location.pathname, { method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: row_perms + headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + body: payload, + }).then(function(response) { + window.location.reload(); }); } diff --git a/templates/index.html b/templates/index.html index bbdcad2..f6208f5 100644 --- a/templates/index.html +++ b/templates/index.html @@ -4,7 +4,7 @@ Buckler @@ -38,23 +38,23 @@

User Permissions


-
+ {% for app in apps %} - + {% endfor %} - {% for username, values in users.items() %} + {% for user_id, values in user_perms_dict.items() %} - - {% for app_name, value in values.items() %} - + + {% for app_id, value in values.items() %} + {% endfor %} {% endfor %} diff --git a/tools.py b/tools.py index be96312..01bbfaf 100644 --- a/tools.py +++ b/tools.py @@ -85,14 +85,3 @@ async def validate_register(request, form): errors['email'].append("Email already in use.") return errors - - -def find_dict(lst, key, value): - """ - Returns the index of the dictionary in the given `lst` which - has d[`key`] == `value`. - """ - for i, d in enumerate(lst): - if d[key] == value: - return i - return -1
User{{ app['name'] }}{{ app['name'] }}
{{ username }}{{ users_dict[user_id] }}