2019-09-29 13:25:07 -04:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
"""
|
|
|
|
Co-routines for handling various forms in Buckler.
|
|
|
|
"""
|
2020-11-20 19:32:32 -05:00
|
|
|
import json
|
|
|
|
|
2019-10-17 13:47:25 -04:00
|
|
|
from passlib.hash import argon2
|
|
|
|
|
2019-09-29 13:25:07 -04:00
|
|
|
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 {}
|
|
|
|
|
|
|
|
|
2019-10-17 20:58:20 -04:00
|
|
|
async def change_user_perms(request):
|
|
|
|
"""Allows an admin to change user permissions."""
|
2020-11-21 13:23:15 -05:00
|
|
|
if not request['session']['admin']:
|
|
|
|
return {'main': "You do not have permission to do that."}
|
2019-10-17 20:58:20 -04:00
|
|
|
data = await request.post()
|
2020-11-20 19:32:32 -05:00
|
|
|
data = json.loads(data['perms'])
|
|
|
|
|
2019-10-17 20:58:20 -04:00
|
|
|
pluses = []
|
2020-11-20 19:32:32 -05:00
|
|
|
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)))
|
2019-10-17 20:58:20 -04:00
|
|
|
|
|
|
|
async with request.app['pool'].acquire() as conn:
|
|
|
|
await conn.executemany(
|
2020-11-20 19:32:32 -05:00
|
|
|
"INSERT INTO app_user (user_id, app_id) VALUES ($1, $2) "
|
2019-10-17 20:58:20 -04:00
|
|
|
"ON CONFLICT ON CONSTRAINT app_user_pkey DO NOTHING",
|
|
|
|
pluses)
|
|
|
|
await conn.executemany(
|
2020-11-20 19:32:32 -05:00
|
|
|
"DELETE FROM app_user WHERE user_id = $1 AND app_id = $2",
|
2019-10-17 20:58:20 -04:00
|
|
|
minuses)
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
async def new_app(request):
|
|
|
|
"""Allows an admin to add a new app to be managed by Buckler."""
|
2020-11-21 13:23:15 -05:00
|
|
|
if not request['session']['admin']:
|
|
|
|
return {'main': "You do not have permission to do that."}
|
2019-10-17 20:58:20 -04:00
|
|
|
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 {}
|
|
|
|
|
|
|
|
|
2019-09-29 13:25:07 -04:00
|
|
|
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:
|
2020-04-09 07:58:38 -04:00
|
|
|
pw_hash = await conn.fetchrow(
|
2019-09-29 13:25:07 -04:00
|
|
|
"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)
|
2020-04-09 07:58:38 -04:00
|
|
|
await conn.execute(
|
2019-09-29 13:25:07 -04:00
|
|
|
"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 {}
|