fifth commit
This commit is contained in:
parent
6acc767923
commit
325d2af09c
45
buckler.py
45
buckler.py
|
@ -6,6 +6,7 @@ import json
|
||||||
import secrets
|
import secrets
|
||||||
import functools
|
import functools
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
import jinja2
|
import jinja2
|
||||||
|
@ -37,14 +38,17 @@ def auth_required(func):
|
||||||
if not sid or not user_id:
|
if not sid or not user_id:
|
||||||
raise web.HTTPFound(location=login_url)
|
raise web.HTTPFound(location=login_url)
|
||||||
async with request.app['pool'].acquire() as conn:
|
async with request.app['pool'].acquire() as conn:
|
||||||
sessions = await conn.fetch(
|
session = await conn.fetchrow(
|
||||||
"SELECT * FROM user_session "
|
"SELECT user_info.id, user_info.username, user_info.admin, "
|
||||||
"WHERE user_id = $1 AND expires > NOW()",
|
"user_session.last_used "
|
||||||
user_id)
|
"FROM user_info LEFT JOIN user_session "
|
||||||
session = [s for s in sessions if s.get('id') == sid]
|
"ON (user_info.id = user_session.user_id) "
|
||||||
|
"WHERE user_info.id = $1 AND user_session.id = $2 "
|
||||||
|
"AND user_session.expires > NOW()",
|
||||||
|
user_id, sid)
|
||||||
if session:
|
if session:
|
||||||
|
request['session'] = dict(session)
|
||||||
resp = await func(request, *args, **kwargs)
|
resp = await func(request, *args, **kwargs)
|
||||||
session = session[0]
|
|
||||||
tz = session['last_used'].tzinfo
|
tz = session['last_used'].tzinfo
|
||||||
delta = datetime.now(tz) - session['last_used']
|
delta = datetime.now(tz) - session['last_used']
|
||||||
if delta.seconds > 600:
|
if delta.seconds > 600:
|
||||||
|
@ -76,6 +80,27 @@ def auth_required(func):
|
||||||
@auth_required
|
@auth_required
|
||||||
async def index(request):
|
async def index(request):
|
||||||
"""The index page."""
|
"""The index page."""
|
||||||
|
async with request.app['pool'].acquire() as conn:
|
||||||
|
avail_sites = await conn.fetch(
|
||||||
|
"SELECT app_info.name, app_info.url "
|
||||||
|
"FROM app_info LEFT JOIN app_user "
|
||||||
|
"ON (app_info.id = app_user.app_id) "
|
||||||
|
"WHERE app_user.user_id = $1",
|
||||||
|
request['session']['id'])
|
||||||
|
if request['session']['admin']:
|
||||||
|
apps = await conn.fetch(
|
||||||
|
"SELECT name FROM app_info")
|
||||||
|
user_perms = await conn.fetch(
|
||||||
|
"SELECT user_info.id, user_info.username, app_user.app_id "
|
||||||
|
"FROM user_info LEFT JOIN app_user "
|
||||||
|
"ON (user_info.id = app_user.user_id)")
|
||||||
|
|
||||||
|
if request['session']['admin']:
|
||||||
|
apps = [app['name'] for app in apps]
|
||||||
|
users = defaultdict(lambda: [False]*len(apps))
|
||||||
|
for user_perm in user_perms:
|
||||||
|
users[user_perm['username']][user_perm['app_id']-1] = True
|
||||||
|
users_json = json.dumps(users)
|
||||||
return render_template("index.html", request, locals())
|
return render_template("index.html", request, locals())
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,8 +126,11 @@ async def login(request):
|
||||||
return render_template("login.html", request, locals())
|
return render_template("login.html", request, locals())
|
||||||
|
|
||||||
if argon2.verify(password, user_info['password_hash']):
|
if argon2.verify(password, user_info['password_hash']):
|
||||||
index_url = request.app.router['index'].url_for()
|
url = request.cookies.get('redirect')
|
||||||
resp = web.HTTPFound(location=index_url)
|
if not url:
|
||||||
|
url = request.app.router['index'].url_for()
|
||||||
|
resp = web.HTTPFound(location=url)
|
||||||
|
resp.set_cookie('redirect', '', max_age=0)
|
||||||
resp.set_cookie(
|
resp.set_cookie(
|
||||||
'userid',
|
'userid',
|
||||||
user_info['id'],
|
user_info['id'],
|
||||||
|
@ -240,7 +268,6 @@ async def get_session(request):
|
||||||
'meta': data_meta,
|
'meta': data_meta,
|
||||||
'session_data': json.loads(data_meta.pop('session_data'))}
|
'session_data': json.loads(data_meta.pop('session_data'))}
|
||||||
|
|
||||||
print(data)
|
|
||||||
return web.json_response(data)
|
return web.json_response(data)
|
||||||
else:
|
else:
|
||||||
error = {'error': "User ID or Session ID invalid."}
|
error = {'error': "User ID or Session ID invalid."}
|
||||||
|
|
46
static/buckler.css
Normal file
46
static/buckler.css
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
body {
|
||||||
|
margin-left: 10%;
|
||||||
|
margin-right: 10%;
|
||||||
|
background-color: lightgray;
|
||||||
|
font-family: Helvetica,sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
margin-bottom: 3em;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logo {
|
||||||
|
height: 5em;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: grid;
|
||||||
|
gap: 2em;
|
||||||
|
background-color: whitesmoke;
|
||||||
|
padding: 5%;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14),
|
||||||
|
0 3px 1px -2px rgba(0, 0, 0, 0.2),
|
||||||
|
0 1px 5px 0 rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
#avail_sites {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1em;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#users {
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
#users tr {
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
#users td {
|
||||||
|
text-align: center;
|
||||||
|
}
|
20
static/buckler.js
Normal file
20
static/buckler.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
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.push(true);
|
||||||
|
} else {
|
||||||
|
row_perms.push(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let perms_changed = false;
|
||||||
|
for (let i = 0; i < user_perms.length; i++) {
|
||||||
|
if (user_perms[i] != row_perms[i]) {
|
||||||
|
perms_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("perms_changed = " + perms_changed);
|
||||||
|
}
|
4
static/buckler_icon.svg
Normal file
4
static/buckler_icon.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="50" cy="50" r="48" stroke="black" fill="none"/>
|
||||||
|
<circle cx="50" cy="50" r="18" stroke="black" fill="none"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 192 B |
|
@ -2,9 +2,49 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Buckler</title>
|
<title>Buckler</title>
|
||||||
|
<script>
|
||||||
|
var users_perms = {{ users_json|safe }};
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="/static/buckler.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/buckler.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Buckler</h1>
|
<header>
|
||||||
Secret page.
|
<object id="logo" data="/static/buckler_icon.svg"></object>
|
||||||
|
<h1>Buckler</h1>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<div>
|
||||||
|
Available sites
|
||||||
|
<ul id="avail_sites">
|
||||||
|
{% for site in avail_sites %}
|
||||||
|
<li><a href="{{ site['url'] }}">{{ site['name'] }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% if request['session']['admin'] %}
|
||||||
|
<table id="users">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>User</th>
|
||||||
|
{% for app in apps %}
|
||||||
|
<th>{{ app }}</th>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for username, values in users.items() %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ username }}</td>
|
||||||
|
{% for value in values %}
|
||||||
|
<td><input type="checkbox" onchange="perm_change(this.parentElement.parentElement)"{% if value %} checked{% endif %}></td>
|
||||||
|
{% endfor %}
|
||||||
|
<td><input type="submit"></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -4,16 +4,20 @@
|
||||||
<title>Buckler - Login</title>
|
<title>Buckler - Login</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Buckler Login</h1>
|
<header>
|
||||||
<form action="{{ request.app.router['login'].url_for() }}" method="post" enctype="application/x-www-form-urlencoded">
|
<h1>Buckler Login</h1>
|
||||||
<label for="username">Username</label>
|
</header>
|
||||||
<input id="username" name="username" type="text"><br>
|
<main>
|
||||||
<label for="password">Password</label>
|
<form action="{{ request.app.router['login'].url_for() }}" method="post" enctype="application/x-www-form-urlencoded">
|
||||||
<input id="password" name="password" type="password"><br>
|
<label for="username">Username</label>
|
||||||
{% if login_failed %}
|
<input id="username" name="username" type="text"><br>
|
||||||
<ul><li>Username and/or password incorrect</li></ul>
|
<label for="password">Password</label>
|
||||||
{% endif %}
|
<input id="password" name="password" type="password"><br>
|
||||||
<input type="submit" value="Login">
|
{% if login_failed %}
|
||||||
</form>
|
<ul><li>Username and/or password incorrect</li></ul>
|
||||||
|
{% endif %}
|
||||||
|
<input type="submit" value="Login">
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user