rewrite user permissions panel

This commit is contained in:
iou1name 2020-11-20 19:32:32 -05:00
parent 976ffe4551
commit fc434f0d62
5 changed files with 63 additions and 78 deletions

View File

@ -55,11 +55,13 @@ async def index(request):
request['session']['id']) request['session']['id'])
if request['session']['admin']: if request['session']['admin']:
apps = await conn.fetch( 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( 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 " "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( fido2_keys = await conn.fetch(
"SELECT * FROM user_credential WHERE user_id = $1", "SELECT * FROM user_credential WHERE user_id = $1",
request['session']['id']) request['session']['id'])
@ -70,12 +72,18 @@ async def index(request):
request['session']['id']) request['session']['id'])
if request['session']['admin']: if request['session']['admin']:
users = defaultdict(lambda: {app['name']: False for app in apps}) user_perms_dict = {}
for user_perm in user_perms: for user in users:
index = tools.find_dict(apps, 'id', user_perm['app_id']) lst = [up for up in user_perms if up['id'] == user['id']]
if index != -1: d = {app['id']: False for app in apps}
users[user_perm['username']][apps[index]['name']] = True for perm in lst:
users_json = json.dumps(users) 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()) return render_template("index.html", request, locals())

View File

@ -2,6 +2,8 @@
""" """
Co-routines for handling various forms in Buckler. Co-routines for handling various forms in Buckler.
""" """
import json
from passlib.hash import argon2 from passlib.hash import argon2
import tools import tools
@ -26,33 +28,24 @@ async def invite_user(request):
async def change_user_perms(request): async def change_user_perms(request):
"""Allows an admin to change user permissions.""" """Allows an admin to change user permissions."""
data = await request.post() data = await request.post()
data = json.loads(data['perms'])
pluses = [] pluses = []
for key, value in data.items(): minuses = []
if key == 'form_name': for user_id, perms in data.items():
continue for app_id, value in perms.items():
username, _, app_name = key.partition('-') if value: # TODO: check for TypeError
pluses.append((username, app_name)) 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: 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( await conn.executemany(
"INSERT INTO app_user (user_id, app_id) " "INSERT INTO app_user (user_id, app_id) VALUES ($1, $2) "
"VALUES ((SELECT id FROM user_info WHERE username = $1), "
"(SELECT id FROM app_info WHERE name = $2)) "
"ON CONFLICT ON CONSTRAINT app_user_pkey DO NOTHING", "ON CONFLICT ON CONSTRAINT app_user_pkey DO NOTHING",
pluses) pluses)
await conn.executemany( await conn.executemany(
"DELETE FROM app_user " "DELETE FROM app_user WHERE user_id = $1 AND app_id = $2",
"WHERE user_id = (SELECT id FROM user_info WHERE username = $1) "
"AND app_id = (SELECT id FROM app_info WHERE name = $2)",
minuses) minuses)
return {} return {}

View File

@ -15,44 +15,39 @@ function load() {
} }
}); });
}); });
document.querySelector('#user_perm_form').addEventListener('submit', submit_user_perms);
} }
function perm_change(row) { function submit_user_perms(event) {
let user_perms = users_perms[row.children[0].textContent]; event.preventDefault();
let row_perms = {}; let perms_diff = {};
for (let child of row.children) { for (let input of event.target.querySelectorAll('input[type=checkbox]')) {
if (child.firstChild.type == "checkbox") { let user_id = input.dataset['userId'];
if (child.firstChild.checked) { let app_id = input.dataset['appId'];
row_perms[child.firstChild.dataset.appName] = true; if (input.checked != user_perms[user_id][app_id]) {
} else { let perms = perms_diff[user_id] || {};
row_perms[child.firstChild.dataset.appName] = false; 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;
}
}
console.log("perms_changed = " + perms_changed);
}
function submit_user_perms(row) { let form_body = {
let row_perms = {}; 'form_name': 'change_user_perms',
for (let child of row.children) { 'perms': JSON.stringify(perms_diff),
if (child.firstChild.type == "checkbox") {
if (child.firstChild.checked) {
row_perms[child.firstChild.dataset.appName] = true;
} else {
row_perms[child.firstChild.dataset.appName] = false;
} }
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('&');
console.log(row_perms);
fetch(window.location.pathname, { fetch(window.location.pathname, {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: row_perms body: payload,
}).then(function(response) {
window.location.reload();
}); });
} }

View File

@ -4,7 +4,7 @@
<title>Buckler</title> <title>Buckler</title>
<link rel="stylesheet" type="text/css" href="/static/buckler.css"> <link rel="stylesheet" type="text/css" href="/static/buckler.css">
<script> <script>
var users_perms = {{ users_json|safe }}; var user_perms = {{ user_perms_json|safe }};
</script> </script>
<script type="text/javascript" src="/static/buckler.js"></script> <script type="text/javascript" src="/static/buckler.js"></script>
<script>window.onload = load;</script> <script>window.onload = load;</script>
@ -38,23 +38,23 @@
<h3>User Permissions</h3> <h3>User Permissions</h3>
<article style="display: none;"> <article style="display: none;">
<hr> <hr>
<form method="post" enctype="application/x-www-form-urlencoded"> <form id="user_perm_form">
<input name="form_name" type="hidden" value="change_user_perms"> <input name="form_name" type="hidden" value="change_user_perms">
<table id="users"> <table id="users">
<thead> <thead>
<tr> <tr>
<th>User</th> <th>User</th>
{% for app in apps %} {% for app in apps %}
<th>{{ app['name'] }}</th> <th data-app-id="{{ app['id'] }}">{{ app['name'] }}</th>
{% endfor %} {% endfor %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for username, values in users.items() %} {% for user_id, values in user_perms_dict.items() %}
<tr> <tr>
<td>{{ username }}</td> <td data-user-id="{{ user_id }}">{{ users_dict[user_id] }}</td>
{% for app_name, value in values.items() %} {% for app_id, value in values.items() %}
<td><input aria-label="{{ username }}-{{ app_name }}" name="{{ username }}-{{ app_name }}" data-app-name={{ app_name }} type="checkbox"{% if value %} checked{% endif %}></td> <td><input aria-label="{{ users_dict[user_id] }}-{{ apps_dict[app_id] }}" data-user-id="{{ user_id }}" data-app-id="{{ app_id }}" type="checkbox"{% if value %} checked{% endif %}></td>
{% endfor %} {% endfor %}
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -85,14 +85,3 @@ async def validate_register(request, form):
errors['email'].append("Email already in use.") errors['email'].append("Email already in use.")
return errors 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