diff --git a/imaginaerraum_door_admin/templates/admins.html b/imaginaerraum_door_admin/templates/admins.html index 30d712a..afa8b4d 100644 --- a/imaginaerraum_door_admin/templates/admins.html +++ b/imaginaerraum_door_admin/templates/admins.html @@ -8,20 +8,35 @@ {% block content %} - + + + {% for data in admin_data %} + {% if data['active'] %} - {% for field in ['username', 'email', 'active'] %} + {% else %} + + {% endif %} + {% for field in ['username', 'email', 'active', 'admin', 'super_admin'] %} {% endfor %} {% endfor %} @@ -29,7 +44,7 @@
UsernameBenutzer E-Mail AktivAdminSuper-Admin Aktionen
{{ data[field] if data[field] }} + {% if not data['super_admin'] %} Toggle active Delete + {% endif %} + {% if data['admin'] %} + {% if not data['super_admin'] %} + Demote + {% endif %} + {% else %} + Promote + {% endif %}

- Neuen Admin erstellen: + Neuen Benutzer erstellen:

diff --git a/imaginaerraum_door_admin/templates/base.html b/imaginaerraum_door_admin/templates/base.html index e29fe6c..d728c62 100644 --- a/imaginaerraum_door_admin/templates/base.html +++ b/imaginaerraum_door_admin/templates/base.html @@ -48,7 +48,7 @@ {% if current_user.has_role('super_admin') %} {% endif %} diff --git a/imaginaerraum_door_admin/templates/delete_admin.html b/imaginaerraum_door_admin/templates/delete_user.html similarity index 81% rename from imaginaerraum_door_admin/templates/delete_admin.html rename to imaginaerraum_door_admin/templates/delete_user.html index 5b6edff..6acbe2f 100644 --- a/imaginaerraum_door_admin/templates/delete_admin.html +++ b/imaginaerraum_door_admin/templates/delete_user.html @@ -1,12 +1,12 @@ {% extends 'base.html' %} {% block header %} - {% block title %}

Admin Benutzer löschen

{% endblock %} + {% block title %}

Benutzer löschen

{% endblock %} {% endblock %} {% block content %}
- Achtung, Admin '{{ username }}' wird gelöscht. + Achtung, Benutzer '{{ username }}' wird gelöscht. Bitte zur Bestätigung den Nutzernamen eingeben:
diff --git a/imaginaerraum_door_admin/webapp.py b/imaginaerraum_door_admin/webapp.py index a77b431..e88b483 100644 --- a/imaginaerraum_door_admin/webapp.py +++ b/imaginaerraum_door_admin/webapp.py @@ -188,13 +188,12 @@ def create_application(config): attributes=ldap3.ALL_ATTRIBUTES) if lock_permission: new_user_data['email'] = con.entries[0].mail.value - new_user_data['roles'].append('admin') else: new_user_data['email'] = None token_granting_permission = con.search('ou=Users,dc=imaginaerraum,dc=de', f'(&(uid={username})(memberof=cn=Vorstand,ou=Groups,dc=imaginaerraum,dc=de))') if token_granting_permission: - new_user_data['roles'].append('super_admin') + new_user_data['roles'].append('admin') return True, new_user_data @@ -257,21 +256,22 @@ def create_application(config): form = AdminCreationForm() if request.method == 'GET': 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) elif form.validate(): if user_datastore.find_user(username=form.name.data) is not None or \ 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') else: 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)) logger.info( - f"Super admin {current_user.username} created new admin account for {new_user.username} <{new_user.email}>") - flash( - f"An account for the new admin user {new_user.username} has been created. Use the randomly generated password {pw} to log in.") + f"Super admin {current_user.username} created new user account for {new_user.username} <{new_user.email}>") + flash(f"Ein Account für den Nutzer {new_user.username} wurde erstellt. Verwende das Passwort {pw} um den Nutzer einzuloggen.") db.session.commit() return redirect('/manage_admins') @@ -280,13 +280,13 @@ def create_application(config): def delete_admins(username): user = user_datastore.find_user(username=username) if user is None: - flash(f"Invalid user {username}") + flash(f"Ungültiger Nutzer {username}") return redirect('/manage_admins') if user.has_role('super_admin'): - flash('Cannot delete super admins!') + flash('Super-Admins können nicht gelöscht werden!') return redirect('/manage_admins') 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') # set up form for confirming deletion @@ -295,15 +295,15 @@ def create_application(config): if request.method == 'GET': # 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(): 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}") db.session.commit() return redirect('/manage_admins') 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') @app.route('/admin_toggle_active/') @@ -311,10 +311,10 @@ def create_application(config): def admin_toggle_active(username): user = user_datastore.find_user(username=username) if user is None: - flash(f"Invalid user {username}") + flash(f"Ungültiger Nutzer {username}") return redirect('/manage_admins') if user.has_role('super_admin'): - flash('Cannot deactivate super admins!') + flash('Super-Admins können nicht deaktiviert werden!') return redirect('/manage_admins') user_datastore.toggle_active(user) if user.is_active: @@ -324,12 +324,46 @@ def create_application(config): db.session.commit() return redirect('/manage_admins') + @app.route('/promote_admin/') + @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/') + @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') @roles_required('super_admin') def backup_user_datastore(): # get list of defined admin users for backup 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')] try: with tempfile.TemporaryDirectory() as tmpdir: @@ -362,18 +396,20 @@ def create_application(config): valid &= all(type(d) == dict for d in user_data) if valid: 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 &= type(d['active']) == bool + entry_valid &= type(d['roles']) == list validate_email(d['email']) if entry_valid: existing_user = user_datastore.find_user(username=d['username'], email=d['email']) if existing_user is None: - user_datastore.create_user(username=d['username'], email=d['email'], password=d['password_hash'], - roles=['admin'], active=d['active']) - flash(f"Admin Account für Benutzer '{d['username']} wurde wiederhergestellt.") + user_datastore.create_user(username=d['username'], email=d['email'], + password=d['password_hash'], active=d['active'], + roles=d['roles']) + flash(f"Account für Benutzer '{d['username']} wurde wiederhergestellt.") else: - flash(f"Admin '{d['username']} existiert bereits. Eintrag wird übersprungen.") + flash(f"Benutzer '{d['username']} existiert bereits. Eintrag wird übersprungen.") else: raise ValueError(f"Ungültige Daten für User Entry {d}") else: @@ -381,7 +417,7 @@ def create_application(config): except Exception as e: flash(f"Die Datei konnte nicht gelesen werden. Exception: {e}") return redirect('/manage_admins') - flash("Admin Benutzer aus Datei gelesen.") + flash("Benutzer aus Datei gelesen.") db.session.commit() else: flash("Ungültige Dateiendung") @@ -393,7 +429,7 @@ def create_application(config): # token overview @app.route('/tokens') - @auth_required() + @roles_required('admin') def list_tokens(): tokens = door.get_tokens() 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 @app.route('/register-token', methods=['GET', 'POST']) - @auth_required() + @roles_required('admin') def register(): """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) @app.route('/edit-token/', methods=['GET', 'POST']) - @auth_required() + @roles_required('admin') def edit_token(token): """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) @app.route('/store-token') - @auth_required() + @roles_required('admin') def store_token(): """Store token to the token file on disk. @@ -511,7 +547,7 @@ def create_application(config): return redirect('/tokens') @app.route('/delete-token/', methods=['GET', 'POST']) - @auth_required() + @roles_required('admin') def delete_token(token): """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') @app.route('/deactivate-token/') - @auth_required() + @roles_required('admin') def deactivate_token(token): """Deactivate access for the given token. This updates the token file on disk. @@ -572,7 +608,7 @@ def create_application(config): return redirect('/tokens') @app.route('/backup_tokens') - @auth_required() + @roles_required('admin') def backup_tokens(): # get list of defined admin users for backup tokens = door.get_tokens()