Compare commits

...

3 Commits

Author SHA1 Message Date
4309d65c85 lighthouse score 2019-09-26 14:39:48 -04:00
0cad501405 user_perms not hard coded 2019-09-26 13:33:06 -04:00
83ba097d6e moved mail.py and validation.py to tools.py 2019-09-26 13:00:02 -04:00
5 changed files with 62 additions and 54 deletions

View File

@ -16,9 +16,8 @@ import asyncpg
import uvloop import uvloop
import auth import auth
import mail import tools
import config import config
import validation
uvloop.install() uvloop.install()
routes = web.RouteTableDef() routes = web.RouteTableDef()
@ -36,7 +35,7 @@ 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 name FROM app_info") "SELECT id, name FROM app_info")
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, user_info.username, app_user.app_id "
"FROM user_info LEFT JOIN app_user " "FROM user_info LEFT JOIN app_user "
@ -45,15 +44,16 @@ async def index(request):
"SELECT * FROM user_credential WHERE user_id = $1", "SELECT * FROM user_credential WHERE user_id = $1",
request['session']['id']) request['session']['id'])
active_sessions = await conn.fetch( active_sessions = await conn.fetch(
"SELECT ip_address FROM user_session " "SELECT id, ip_address FROM user_session "
"WHERE user_id = $1", "WHERE user_id = $1",
request['session']['id']) request['session']['id'])
if request['session']['admin']: if request['session']['admin']:
apps = [app['name'] for app in apps] users = defaultdict(lambda: {app['name']: False for app in apps})
users = defaultdict(lambda: [False]*len(apps))
for user_perm in user_perms: for user_perm in user_perms:
users[user_perm['username']][user_perm['app_id']-1] = True 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) users_json = json.dumps(users)
return render_template("index.html", request, locals()) return render_template("index.html", request, locals())
@ -170,7 +170,7 @@ async def register(request):
return render_template("register.html", request, locals()) return render_template("register.html", request, locals())
form = await request.post() form = await request.post()
errors = await validation.validate_register(request, form) errors = await tools.validate_register(request, form)
if any(errors.values()): if any(errors.values()):
return render_template("register.html", request, locals()) return render_template("register.html", request, locals())
@ -186,7 +186,7 @@ async def register(request):
await conn.execute( await conn.execute(
"DELETE FROM invite WHERE token = $1", "DELETE FROM invite WHERE token = $1",
invite_token) invite_token)
await mail.send_confirmation(request, user['id'], email) await tools.send_confirmation(request, user['id'], email)
message = "An email has been sent." # TODO: more better message = "An email has been sent." # TODO: more better
else: else:
message = "Invalid invitation token." message = "Invalid invitation token."

View File

@ -14,19 +14,19 @@ function load() {
function perm_change(row) { function perm_change(row) {
let user_perms = users_perms[row.children[0].textContent]; let user_perms = users_perms[row.children[0].textContent];
let row_perms = []; let row_perms = {};
for (let child of row.children) { for (let child of row.children) {
if (child.firstChild.type == "checkbox") { if (child.firstChild.type == "checkbox") {
if (child.firstChild.checked) { if (child.firstChild.checked) {
row_perms.push(true); row_perms[child.firstChild.dataset.appName] = true;
} else { } else {
row_perms.push(false); row_perms[child.firstChild.dataset.appName] = false;
} }
} }
} }
let perms_changed = false; let perms_changed = false;
for (let i = 0; i < user_perms.length; i++) { for (let app_name of Object.keys(user_perms)) {
if (user_perms[i] != row_perms[i]) { if (user_perms[app_name] != row_perms[app_name]) {
perms_changed = true; perms_changed = true;
} }
} }

View File

@ -38,7 +38,7 @@
<tr> <tr>
<th>User</th> <th>User</th>
{% for app in apps %} {% for app in apps %}
<th>{{ app }}</th> <th>{{ app['name'] }}</th>
{% endfor %} {% endfor %}
<th></th> <th></th>
</tr> </tr>
@ -47,8 +47,8 @@
{% for username, values in users.items() %} {% for username, values in users.items() %}
<tr> <tr>
<td>{{ username }}</td> <td>{{ username }}</td>
{% for value in values %} {% for name, value in values.items() %}
<td><input type="checkbox" onchange="perm_change(this.parentElement.parentElement)"{% if value %} checked{% endif %}></td> <td><input aria-label="{{ username }}-{{ name }}" data-app-name={{ name }} type="checkbox" onchange="perm_change(this.parentElement.parentElement)"{% if value %} checked{% endif %}></td>
{% endfor %} {% endfor %}
<td><input type="submit"></td> <td><input type="submit"></td>
</tr> </tr>
@ -89,7 +89,7 @@
{% for key in fido2_keys %} {% for key in fido2_keys %}
<tr> <tr>
<td>{{ key['nick'] }}</td> <td>{{ key['nick'] }}</td>
<td><input type="checkbox"></td> <td><input aria-label="Delete {{ key['nick'] }}" id="fido-{{ key['id'] }}" name="fido-{{ key['id'] }}" type="checkbox"></td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -115,7 +115,7 @@
{% for session in active_sessions %} {% for session in active_sessions %}
<tr> <tr>
<td>{{ session['ip_address'] }}</td> <td>{{ session['ip_address'] }}</td>
<td><input type="checkbox"></td> <td><input aria-label="Delete {{ session['id'][:5] }}" id="session-{{ session['id'][:5] }}" name="session-{{ session['id'][:5] }}" type="checkbox"></td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Tools for sending emails. Various different tools for Buckler.
""" """
import email.mime.text import email.mime.text
import smtplib import smtplib
@ -8,6 +8,7 @@ import secrets
import config import config
def send_mail(to_addr, subject, body): def send_mail(to_addr, subject, body):
""" """
Sends an email. Sends an email.
@ -53,4 +54,44 @@ async def send_confirmation(request, user_id, to_addr):
confirm_url = request.app.router['register'].url_for().with_query(d) confirm_url = request.app.router['register'].url_for().with_query(d)
confirm_url = config.server_domain + str(confirm_url) confirm_url = config.server_domain + str(confirm_url)
body = "Buckle up.\n" + confirm_url body = "Buckle up.\n" + confirm_url
send_mail(to_addr, "Buckler Invite", body)
async def validate_register(request, form):
"""Validate data from the registration form."""
username = form.get('username')
email = form.get('email')
password = form.get('password')
password_verify = form.get('password_verify')
async with request.app['pool'].acquire() as conn:
users = await conn.fetch(
"SELECT username, email FROM user_info "
"WHERE username = $1 OR email = $2",
username, email)
errors = {'password': [], 'username': [], 'email': []}
if password != password_verify:
errors['password'].append("Passwords do not match.")
if len(password) < 8 or len(password) > 10240:
errors['password'].append(
"Password must be between 8 and 10240 characters long.")
if len(username) < 3 or len(username) > 20:
errors['username'].append(
"Username must be between 3 and 20 characters long.")
if username in [user['username'] for user in users]:
errors['username'].append("Username already in use.")
if email in [user['email'] for user in users]:
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

View File

@ -1,33 +0,0 @@
#!/usr/bin/env python3
"""
Functions for validating forms.
"""
async def validate_register(request, form):
"""Validate data from the registration form."""
username = form.get('username')
email = form.get('email')
password = form.get('password')
password_verify = form.get('password_verify')
async with request.app['pool'].acquire() as conn:
users = await conn.fetch(
"SELECT username, email FROM user_info "
"WHERE username = $1 OR email = $2",
username, email)
errors = {'password': [], 'username': [], 'email': []}
if password != password_verify:
errors['password'].append("Passwords do not match.")
if len(password) < 8 or len(password) > 10240:
errors['password'].append(
"Password must be between 8 and 10240 characters long.")
if len(username) < 3 or len(username) > 20:
errors['username'].append(
"Username must be between 3 and 20 characters long.")
if username in [user['username'] for user in users]:
errors['username'].append("Username already in use.")
if email in [user['email'] for user in users]:
errors['email'].append("Email already in use.")
return errors