141 lines
3.8 KiB
Python
141 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Co-routines for handling various forms in Buckler.
|
|
"""
|
|
from passlib.hash import argon2
|
|
|
|
import tools
|
|
|
|
|
|
async def invite_user(request):
|
|
"""Allows an admin to invite a new user."""
|
|
if not request['session']['admin']:
|
|
return {'main': "You do not have permission to do that."}
|
|
|
|
data = await request.post()
|
|
email = data.get('email')
|
|
|
|
if not email:
|
|
return {'invite_user': "This field is required."}
|
|
# TODO: validate email address
|
|
|
|
await tools.send_invite(request, email)
|
|
return {}
|
|
|
|
|
|
async def change_user_perms(request):
|
|
"""Allows an admin to change user permissions."""
|
|
data = await request.post()
|
|
pluses = []
|
|
for key, value in data.items():
|
|
if key == 'form_name':
|
|
continue
|
|
username, _, app_name = key.partition('-')
|
|
pluses.append((username, app_name))
|
|
|
|
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)) "
|
|
"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)",
|
|
minuses)
|
|
return {}
|
|
|
|
|
|
async def new_app(request):
|
|
"""Allows an admin to add a new app to be managed by Buckler."""
|
|
data = await request.post()
|
|
app_name = data.get('app_name')
|
|
app_url = data.get('app_url')
|
|
app_key = data.get('app_key')
|
|
key_hash = argon2.hash(app_key)
|
|
|
|
async with request.app['pool'].acquire() as conn:
|
|
await conn.execute(
|
|
"INSERT INTO app_info (name, url, key_hash) "
|
|
"VALUES ($1, $2, $3)",
|
|
app_name, app_url, key_hash)
|
|
return {}
|
|
|
|
|
|
async def change_password(request):
|
|
"""Allows a user to change their password."""
|
|
errors = {}
|
|
data = await request.post()
|
|
current_pw = data.get('current_password')
|
|
new_pw = data.get('new_password')
|
|
verify_pw = data.get('verify_password')
|
|
|
|
if not all((current_pw, new_pw, verify_pw)):
|
|
errors['change_password'] = "All fields are required."
|
|
return errors
|
|
if not new_pw == verify_pw:
|
|
errors['change_password'] = "Passwords do not match."
|
|
return errors
|
|
|
|
async with request.app['pool'].acquire() as conn:
|
|
pw_hash = await conn.fetchrow(
|
|
"SELECT password_hash FROM user_info WHERE id = $1",
|
|
request['session']['id'])
|
|
if not argon2.verify(current_pw, pw_hash['password_hash']):
|
|
errors['change_password'] = "Invalid password."
|
|
return errors
|
|
h = argon2.hash(new_pw)
|
|
await conn.execute(
|
|
"UPDATE user_info SET password_hash = $1 WHERE id = $2",
|
|
h, request['session']['id'])
|
|
return errors
|
|
|
|
|
|
async def delete_key(request):
|
|
"""Allows a user to delete a security key."""
|
|
data = await request.post()
|
|
async with request.app['pool'].acquire() as conn:
|
|
for key, value in data.items():
|
|
key_id = key.replace('fido-', '')
|
|
if not key_id:
|
|
continue
|
|
try:
|
|
key_id = int(key_id)
|
|
except ValueError:
|
|
continue
|
|
if value != 'on':
|
|
continue
|
|
await conn.execute(
|
|
"DELETE FROM user_credential "
|
|
"WHERE id = $1 AND user_id = $2",
|
|
key_id, request['session']['id'])
|
|
return {}
|
|
|
|
|
|
async def delete_session(request):
|
|
"""Allows a user to delete a session ."""
|
|
data = await request.post()
|
|
async with request.app['pool'].acquire() as conn:
|
|
for key, value in data.items():
|
|
session_id = key.replace('session-', '', 1)
|
|
if not session_id:
|
|
continue
|
|
if value != 'on':
|
|
continue
|
|
await conn.execute(
|
|
"DELETE FROM user_session "
|
|
"WHERE id = $1 AND user_id = $2",
|
|
session_id, request['session']['id'])
|
|
return {}
|