Distinguish between normal users and admin users. Normal user can only lock and unlock the door. Admin users can register new tokens.
Also added option to grant and revoke admin permissions for super-Admins.
This commit is contained in:
parent
7684268002
commit
2cb93d1d3b
|
@ -8,20 +8,35 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<th scope="col">Username</th>
|
<th scope="col">Benutzer</th>
|
||||||
<th scope="col">E-Mail</th>
|
<th scope="col">E-Mail</th>
|
||||||
<th scope="col">Aktiv</th>
|
<th scope="col">Aktiv</th>
|
||||||
|
<th scope="col">Admin</th>
|
||||||
|
<th scope="col">Super-Admin</th>
|
||||||
<th scope="col">Aktionen</th>
|
<th scope="col">Aktionen</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for data in admin_data %}
|
{% for data in admin_data %}
|
||||||
|
{% if data['active'] %}
|
||||||
<tr>
|
<tr>
|
||||||
{% for field in ['username', 'email', 'active'] %}
|
{% else %}
|
||||||
|
<tr style="background-color: lightgrey">
|
||||||
|
{% endif %}
|
||||||
|
{% for field in ['username', 'email', 'active', 'admin', 'super_admin'] %}
|
||||||
<th scope="row">{{ data[field] if data[field] }}</th>
|
<th scope="row">{{ data[field] if data[field] }}</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<td>
|
<td>
|
||||||
|
{% if not data['super_admin'] %}
|
||||||
<a href="{{ url_for('admin_toggle_active', username=data['username']) }}"><img src="static/stop.png" title="Aktivieren/Deaktivieren" alt="Toggle active"></a>
|
<a href="{{ url_for('admin_toggle_active', username=data['username']) }}"><img src="static/stop.png" title="Aktivieren/Deaktivieren" alt="Toggle active"></a>
|
||||||
<a href="{{ url_for('delete_admins', username=data['username']) }}"><img src="static/delete.png" title="Löschen" alt="Delete"></a>
|
<a href="{{ url_for('delete_admins', username=data['username']) }}"><img src="static/delete.png" title="Löschen" alt="Delete"></a>
|
||||||
|
{% endif %}
|
||||||
|
{% if data['admin'] %}
|
||||||
|
{% if not data['super_admin'] %}
|
||||||
|
<a href="{{ url_for('demote_admin', username=data['username']) }}"><img src="static/demote.png" title="Admin-Rechte widerrufen" alt="Demote"></a>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ url_for('promote_admin', username=data['username']) }}"><img src="static/promote.png" title="Zu Admin machen" alt="Promote"></a>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -29,7 +44,7 @@
|
||||||
</table>
|
</table>
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
Neuen Admin erstellen:
|
Neuen Benutzer erstellen:
|
||||||
</p>
|
</p>
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<table>
|
<table>
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
|
|
||||||
{% if current_user.has_role('super_admin') %}
|
{% if current_user.has_role('super_admin') %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{{ url_for('manage_admins') }}">Admins verwalten</a>
|
<a class="nav-link" href="{{ url_for('manage_admins') }}">Benutzer verwalten</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block header %}
|
{% block header %}
|
||||||
{% block title %}<h1>Admin Benutzer löschen</h1>{% endblock %}
|
{% block title %}<h1>Benutzer löschen</h1>{% endblock %}
|
||||||
<script src="../static/js/jquery-3.6.0.js"></script>
|
<script src="../static/js/jquery-3.6.0.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div>
|
<div>
|
||||||
Achtung, Admin '{{ username }}' wird gelöscht.
|
Achtung, Benutzer '{{ username }}' wird gelöscht.
|
||||||
Bitte zur Bestätigung den Nutzernamen eingeben:
|
Bitte zur Bestätigung den Nutzernamen eingeben:
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<table>
|
<table>
|
|
@ -188,13 +188,12 @@ def create_application(config):
|
||||||
attributes=ldap3.ALL_ATTRIBUTES)
|
attributes=ldap3.ALL_ATTRIBUTES)
|
||||||
if lock_permission:
|
if lock_permission:
|
||||||
new_user_data['email'] = con.entries[0].mail.value
|
new_user_data['email'] = con.entries[0].mail.value
|
||||||
new_user_data['roles'].append('admin')
|
|
||||||
else:
|
else:
|
||||||
new_user_data['email'] = None
|
new_user_data['email'] = None
|
||||||
token_granting_permission = con.search('ou=Users,dc=imaginaerraum,dc=de',
|
token_granting_permission = con.search('ou=Users,dc=imaginaerraum,dc=de',
|
||||||
f'(&(uid={username})(memberof=cn=Vorstand,ou=Groups,dc=imaginaerraum,dc=de))')
|
f'(&(uid={username})(memberof=cn=Vorstand,ou=Groups,dc=imaginaerraum,dc=de))')
|
||||||
if token_granting_permission:
|
if token_granting_permission:
|
||||||
new_user_data['roles'].append('super_admin')
|
new_user_data['roles'].append('admin')
|
||||||
|
|
||||||
return True, new_user_data
|
return True, new_user_data
|
||||||
|
|
||||||
|
@ -257,21 +256,22 @@ def create_application(config):
|
||||||
form = AdminCreationForm()
|
form = AdminCreationForm()
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
users = user_datastore.user_model.query.all()
|
users = user_datastore.user_model.query.all()
|
||||||
admin_data = [{'username': u.username, 'email': u.email, 'active': u.is_active} for u in users]
|
admin_data = [{'username': u.username, 'email': u.email, 'active': u.is_active,
|
||||||
|
'admin': u.has_role('admin'), 'super_admin': u.has_role('super_admin'),
|
||||||
|
} for u in users]
|
||||||
return render_template('admins.html', admin_data=admin_data, form=form)
|
return render_template('admins.html', admin_data=admin_data, form=form)
|
||||||
elif form.validate():
|
elif form.validate():
|
||||||
if user_datastore.find_user(username=form.name.data) is not None or \
|
if user_datastore.find_user(username=form.name.data) is not None or \
|
||||||
user_datastore.find_user(email=form.email.data) is not None:
|
user_datastore.find_user(email=form.email.data) is not None:
|
||||||
flash("A user with the same name or email is already registered!")
|
flash("Ein Benutzer mit diesem Nutzernamen oder dieser E-Mail-Adresse existiert bereits!")
|
||||||
return redirect('/manage_admins')
|
return redirect('/manage_admins')
|
||||||
else:
|
else:
|
||||||
pw = secrets.token_urlsafe(16)
|
pw = secrets.token_urlsafe(16)
|
||||||
new_user = user_datastore.create_user(username=form.name.data, email=form.email.data, roles=['admin'],
|
new_user = user_datastore.create_user(username=form.name.data, email=form.email.data,
|
||||||
password=hash_password(pw))
|
password=hash_password(pw))
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Super admin {current_user.username} created new admin account for {new_user.username} <{new_user.email}>")
|
f"Super admin {current_user.username} created new user account for {new_user.username} <{new_user.email}>")
|
||||||
flash(
|
flash(f"Ein Account für den Nutzer {new_user.username} wurde erstellt. Verwende das Passwort {pw} um den Nutzer einzuloggen.")
|
||||||
f"An account for the new admin user {new_user.username} has been created. Use the randomly generated password {pw} to log in.")
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return redirect('/manage_admins')
|
return redirect('/manage_admins')
|
||||||
|
|
||||||
|
@ -280,13 +280,13 @@ def create_application(config):
|
||||||
def delete_admins(username):
|
def delete_admins(username):
|
||||||
user = user_datastore.find_user(username=username)
|
user = user_datastore.find_user(username=username)
|
||||||
if user is None:
|
if user is None:
|
||||||
flash(f"Invalid user {username}")
|
flash(f"Ungültiger Nutzer {username}")
|
||||||
return redirect('/manage_admins')
|
return redirect('/manage_admins')
|
||||||
if user.has_role('super_admin'):
|
if user.has_role('super_admin'):
|
||||||
flash('Cannot delete super admins!')
|
flash('Super-Admins können nicht gelöscht werden!')
|
||||||
return redirect('/manage_admins')
|
return redirect('/manage_admins')
|
||||||
if user.is_active:
|
if user.is_active:
|
||||||
flash('Cannot delete active users. Please deactivate user first!')
|
flash('Aktive Nutzer können nicht gelöscht werden! Bitte den Benutzer zuerst deaktivieren.')
|
||||||
return redirect('/manage_admins')
|
return redirect('/manage_admins')
|
||||||
|
|
||||||
# set up form for confirming deletion
|
# set up form for confirming deletion
|
||||||
|
@ -295,15 +295,15 @@ def create_application(config):
|
||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
# return page asking the user to confirm delete
|
# return page asking the user to confirm delete
|
||||||
return render_template('delete_admin.html', username=username, form=form)
|
return render_template('delete_user.html', username=username, form=form)
|
||||||
elif form.validate():
|
elif form.validate():
|
||||||
user_datastore.delete_user(user)
|
user_datastore.delete_user(user)
|
||||||
flash(f"Username {username} was deleted.")
|
flash(f"Benutzer {username} wurde gelöscht.")
|
||||||
logger.info(f"Super admin {current_user.username} deleted admin user {username}")
|
logger.info(f"Super admin {current_user.username} deleted admin user {username}")
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return redirect('/manage_admins')
|
return redirect('/manage_admins')
|
||||||
else:
|
else:
|
||||||
flash("Username does not match. User was not deleted!")
|
flash("Der eingegebene Nutzername stimmt nicht überein. Der Benutzer wurde nicht gelöscht!")
|
||||||
return redirect('/manage_admins')
|
return redirect('/manage_admins')
|
||||||
|
|
||||||
@app.route('/admin_toggle_active/<username>')
|
@app.route('/admin_toggle_active/<username>')
|
||||||
|
@ -311,10 +311,10 @@ def create_application(config):
|
||||||
def admin_toggle_active(username):
|
def admin_toggle_active(username):
|
||||||
user = user_datastore.find_user(username=username)
|
user = user_datastore.find_user(username=username)
|
||||||
if user is None:
|
if user is None:
|
||||||
flash(f"Invalid user {username}")
|
flash(f"Ungültiger Nutzer {username}")
|
||||||
return redirect('/manage_admins')
|
return redirect('/manage_admins')
|
||||||
if user.has_role('super_admin'):
|
if user.has_role('super_admin'):
|
||||||
flash('Cannot deactivate super admins!')
|
flash('Super-Admins können nicht deaktiviert werden!')
|
||||||
return redirect('/manage_admins')
|
return redirect('/manage_admins')
|
||||||
user_datastore.toggle_active(user)
|
user_datastore.toggle_active(user)
|
||||||
if user.is_active:
|
if user.is_active:
|
||||||
|
@ -324,12 +324,46 @@ def create_application(config):
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return redirect('/manage_admins')
|
return redirect('/manage_admins')
|
||||||
|
|
||||||
|
@app.route('/promote_admin/<username>')
|
||||||
|
@roles_required('super_admin')
|
||||||
|
def promote_admin(username):
|
||||||
|
user = user_datastore.find_user(username=username)
|
||||||
|
if user is None:
|
||||||
|
flash(f"Ungültiger Nutzer {username}")
|
||||||
|
return redirect('/manage_admins')
|
||||||
|
if user.has_role('admin'):
|
||||||
|
flash(f'Benutzer {username} hat bereits Admin-Rechte!')
|
||||||
|
return redirect('/manage_admins')
|
||||||
|
user_datastore.add_role_to_user(user, 'admin')
|
||||||
|
logger.info(f"Super admin {current_user.username} granted admin privileges to user {username}")
|
||||||
|
db.session.commit()
|
||||||
|
return redirect('/manage_admins')
|
||||||
|
|
||||||
|
@app.route('/demote_admin/<username>')
|
||||||
|
@roles_required('super_admin')
|
||||||
|
def demote_admin(username):
|
||||||
|
user = user_datastore.find_user(username=username)
|
||||||
|
if user is None:
|
||||||
|
flash(f"Ungültiger Nutzer {username}")
|
||||||
|
return redirect('/manage_admins')
|
||||||
|
if user.has_role('super_admin'):
|
||||||
|
flash(f'Benutzer {username} hat Super-Admin-Rechte und kann nicht verändert werden!')
|
||||||
|
return redirect('/manage_admins')
|
||||||
|
if user.has_role('admin'):
|
||||||
|
user_datastore.remove_role_from_user(user, 'admin')
|
||||||
|
logger.info(f"Super admin {current_user.username} revoked admin privileges of user {username}")
|
||||||
|
db.session.commit()
|
||||||
|
else:
|
||||||
|
flash(f'Benutzer {username} ist bereits kein Admin!')
|
||||||
|
return redirect('/manage_admins')
|
||||||
|
|
||||||
@app.route('/backup_user_datastore')
|
@app.route('/backup_user_datastore')
|
||||||
@roles_required('super_admin')
|
@roles_required('super_admin')
|
||||||
def backup_user_datastore():
|
def backup_user_datastore():
|
||||||
# get list of defined admin users for backup
|
# get list of defined admin users for backup
|
||||||
users = user_datastore.user_model.query.all()
|
users = user_datastore.user_model.query.all()
|
||||||
user_data = [{'username': u.username, 'email': u.email, 'active': u.is_active, 'password_hash': u.password}
|
user_data = [{'username': u.username, 'email': u.email, 'active': u.is_active, 'password_hash': u.password,
|
||||||
|
'roles': [r.name for r in u.roles]}
|
||||||
for u in users if not u.has_role('super_admin')]
|
for u in users if not u.has_role('super_admin')]
|
||||||
try:
|
try:
|
||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
@ -362,18 +396,20 @@ def create_application(config):
|
||||||
valid &= all(type(d) == dict for d in user_data)
|
valid &= all(type(d) == dict for d in user_data)
|
||||||
if valid:
|
if valid:
|
||||||
for d in user_data:
|
for d in user_data:
|
||||||
entry_valid = set(d.keys()) == { 'active', 'email', 'password_hash', 'username'}
|
entry_valid = set(d.keys()) == { 'active', 'email', 'password_hash', 'username', 'roles'}
|
||||||
entry_valid &= all(len(d[key]) > 0 for key in ['email', 'password_hash', 'username'])
|
entry_valid &= all(len(d[key]) > 0 for key in ['email', 'password_hash', 'username'])
|
||||||
entry_valid &= type(d['active']) == bool
|
entry_valid &= type(d['active']) == bool
|
||||||
|
entry_valid &= type(d['roles']) == list
|
||||||
validate_email(d['email'])
|
validate_email(d['email'])
|
||||||
if entry_valid:
|
if entry_valid:
|
||||||
existing_user = user_datastore.find_user(username=d['username'], email=d['email'])
|
existing_user = user_datastore.find_user(username=d['username'], email=d['email'])
|
||||||
if existing_user is None:
|
if existing_user is None:
|
||||||
user_datastore.create_user(username=d['username'], email=d['email'], password=d['password_hash'],
|
user_datastore.create_user(username=d['username'], email=d['email'],
|
||||||
roles=['admin'], active=d['active'])
|
password=d['password_hash'], active=d['active'],
|
||||||
flash(f"Admin Account für Benutzer '{d['username']} wurde wiederhergestellt.")
|
roles=d['roles'])
|
||||||
|
flash(f"Account für Benutzer '{d['username']} wurde wiederhergestellt.")
|
||||||
else:
|
else:
|
||||||
flash(f"Admin '{d['username']} existiert bereits. Eintrag wird übersprungen.")
|
flash(f"Benutzer '{d['username']} existiert bereits. Eintrag wird übersprungen.")
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Ungültige Daten für User Entry {d}")
|
raise ValueError(f"Ungültige Daten für User Entry {d}")
|
||||||
else:
|
else:
|
||||||
|
@ -381,7 +417,7 @@ def create_application(config):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
flash(f"Die Datei konnte nicht gelesen werden. Exception: {e}")
|
flash(f"Die Datei konnte nicht gelesen werden. Exception: {e}")
|
||||||
return redirect('/manage_admins')
|
return redirect('/manage_admins')
|
||||||
flash("Admin Benutzer aus Datei gelesen.")
|
flash("Benutzer aus Datei gelesen.")
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
else:
|
else:
|
||||||
flash("Ungültige Dateiendung")
|
flash("Ungültige Dateiendung")
|
||||||
|
@ -393,7 +429,7 @@ def create_application(config):
|
||||||
|
|
||||||
# token overview
|
# token overview
|
||||||
@app.route('/tokens')
|
@app.route('/tokens')
|
||||||
@auth_required()
|
@roles_required('admin')
|
||||||
def list_tokens():
|
def list_tokens():
|
||||||
tokens = door.get_tokens()
|
tokens = door.get_tokens()
|
||||||
assigned_tokens = {t: data for t, data in tokens.items() if not data['inactive']}
|
assigned_tokens = {t: data for t, data in tokens.items() if not data['inactive']}
|
||||||
|
@ -402,7 +438,7 @@ def create_application(config):
|
||||||
|
|
||||||
# routes for registering, editing and deleting tokens
|
# routes for registering, editing and deleting tokens
|
||||||
@app.route('/register-token', methods=['GET', 'POST'])
|
@app.route('/register-token', methods=['GET', 'POST'])
|
||||||
@auth_required()
|
@roles_required('admin')
|
||||||
def register():
|
def register():
|
||||||
"""Register new token for locking and unlocking the door.
|
"""Register new token for locking and unlocking the door.
|
||||||
|
|
||||||
|
@ -434,7 +470,7 @@ def create_application(config):
|
||||||
return render_template('register.html', token=door.get_most_recent_token(), form=form)
|
return render_template('register.html', token=door.get_most_recent_token(), form=form)
|
||||||
|
|
||||||
@app.route('/edit-token/<token>', methods=['GET', 'POST'])
|
@app.route('/edit-token/<token>', methods=['GET', 'POST'])
|
||||||
@auth_required()
|
@roles_required('admin')
|
||||||
def edit_token(token):
|
def edit_token(token):
|
||||||
"""Edit data in the token file (name, email, valid_thru date, active/inactive).
|
"""Edit data in the token file (name, email, valid_thru date, active/inactive).
|
||||||
|
|
||||||
|
@ -489,7 +525,7 @@ def create_application(config):
|
||||||
return render_template('edit.html', token=token, form=form)
|
return render_template('edit.html', token=token, form=form)
|
||||||
|
|
||||||
@app.route('/store-token')
|
@app.route('/store-token')
|
||||||
@auth_required()
|
@roles_required('admin')
|
||||||
def store_token():
|
def store_token():
|
||||||
"""Store token to the token file on disk.
|
"""Store token to the token file on disk.
|
||||||
|
|
||||||
|
@ -511,7 +547,7 @@ def create_application(config):
|
||||||
return redirect('/tokens')
|
return redirect('/tokens')
|
||||||
|
|
||||||
@app.route('/delete-token/<token>', methods=['GET', 'POST'])
|
@app.route('/delete-token/<token>', methods=['GET', 'POST'])
|
||||||
@auth_required()
|
@roles_required('admin')
|
||||||
def delete_token(token):
|
def delete_token(token):
|
||||||
"""Delete the given token from the token file and store the new token file to disk
|
"""Delete the given token from the token file and store the new token file to disk
|
||||||
|
|
||||||
|
@ -552,7 +588,7 @@ def create_application(config):
|
||||||
return redirect('/tokens')
|
return redirect('/tokens')
|
||||||
|
|
||||||
@app.route('/deactivate-token/<token>')
|
@app.route('/deactivate-token/<token>')
|
||||||
@auth_required()
|
@roles_required('admin')
|
||||||
def deactivate_token(token):
|
def deactivate_token(token):
|
||||||
"""Deactivate access for the given token. This updates the token file on disk.
|
"""Deactivate access for the given token. This updates the token file on disk.
|
||||||
|
|
||||||
|
@ -572,7 +608,7 @@ def create_application(config):
|
||||||
return redirect('/tokens')
|
return redirect('/tokens')
|
||||||
|
|
||||||
@app.route('/backup_tokens')
|
@app.route('/backup_tokens')
|
||||||
@auth_required()
|
@roles_required('admin')
|
||||||
def backup_tokens():
|
def backup_tokens():
|
||||||
# get list of defined admin users for backup
|
# get list of defined admin users for backup
|
||||||
tokens = door.get_tokens()
|
tokens = door.get_tokens()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user