diff --git a/imaginaerraum_door_admin/templates/admins.html b/imaginaerraum_door_admin/templates/admins.html index 9d348e0..6770097 100644 --- a/imaginaerraum_door_admin/templates/admins.html +++ b/imaginaerraum_door_admin/templates/admins.html @@ -47,4 +47,11 @@ +
+ {% endblock %} \ No newline at end of file diff --git a/imaginaerraum_door_admin/webapp.py b/imaginaerraum_door_admin/webapp.py index a5becec..c980b80 100644 --- a/imaginaerraum_door_admin/webapp.py +++ b/imaginaerraum_door_admin/webapp.py @@ -1,23 +1,27 @@ import os -from flask import Flask, render_template, request, flash, redirect, session, url_for +from flask import Flask, render_template, request, flash, redirect, session, send_file +from werkzeug.utils import secure_filename from flask_wtf import FlaskForm from wtforms.fields.html5 import DateField, EmailField from wtforms.fields import StringField, BooleanField from wtforms.validators import DataRequired, ValidationError, EqualTo from flask_sqlalchemy import SQLAlchemy -from flask_security import Security, SQLAlchemyUserDatastore, auth_required, hash_password, uia_email_mapper, current_user, roles_required +from flask_security import Security, SQLAlchemyUserDatastore, auth_required, hash_password, uia_email_mapper, \ + current_user, roles_required from flask_security.models import fsqla_v2 as fsqla from flask_security.forms import LoginForm, Required, PasswordField from flask_security.utils import find_user, verify_password from flask_mail import Mail from email_validator import validate_email +import json import secrets import bleach import ldap3 from ldap3.core.exceptions import LDAPBindError, LDAPSocketOpenError from pathlib import Path import logging +import tempfile from datetime import date from .door_handle import DoorHandle @@ -43,18 +47,22 @@ class TokenForm(FlaskForm): active = BooleanField('Aktiv?') dsgvo = BooleanField('Einwilligung Nutzungsbedingungen erfragt?', validators=[DataRequired()]) -class TokenDeleteForm(FlaskForm): + +class ConfirmDeleteForm(FlaskForm): name = StringField('Name', validators=[DataRequired(), EqualTo('name_confirm', 'Name stimmt nicht überein')]) name_confirm = StringField('Name confirm') + class AdminCreationForm(FlaskForm): name = StringField('Name', validators=[DataRequired()]) email = EmailField('E-Mail', validators=[DataRequired()]) + def uia_username_mapper(identity): # we allow pretty much anything - but we bleach it. return bleach.clean(identity, strip=True) + def create_application(config): # set up logging for the web app logger = logging.getLogger('webapp') @@ -136,7 +144,7 @@ def create_application(config): # LDAP ldap_server = ldap3.Server(config.ldap_url) - local_ldap_cache = {} # dict for caching LDAP authorization locally (stores username + hashed password) + local_ldap_cache = {} # dict for caching LDAP authorization locally (stores username + hashed password) def validate_ldap(user, password): """Validate the user and password through an LDAP server. @@ -158,7 +166,7 @@ def create_application(config): try: con = ldap3.Connection(ldap_server, user="uid=%s,ou=Users,dc=imaginaerraum,dc=de" % (user.username,), - password=password, auto_bind=True) + password=password, auto_bind=True) except ldap3.core.exceptions.LDAPBindError: # server reachable but user unauthorized -> fail return False @@ -200,8 +208,7 @@ def create_application(config): user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(app, user_datastore, login_form=ExtendedLoginForm) - - + # admin user management @app.route('/manage_admins', methods=['GET', 'POST']) @roles_required('super_admin') def manage_admins(): @@ -212,15 +219,17 @@ def create_application(config): 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!") - return redirect('/manage_admins') + user_datastore.find_user(email=form.email.data) is not None: + flash("A user with the same name or email is already registered!") + 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'], 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.") + 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.") db.session.commit() return redirect('/manage_admins') @@ -239,7 +248,7 @@ def create_application(config): return redirect('/manage_admins') # set up form for confirming deletion - form = TokenDeleteForm() + form = ConfirmDeleteForm() form.name_confirm.data = username if request.method == 'GET': @@ -247,9 +256,13 @@ def create_application(config): return render_template('delete_admin.html', username=username, form=form) elif form.validate(): user_datastore.delete_user(user) + flash(f"Username {username} was deleted.") 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!") + return redirect('/manage_admins') @app.route('/admin_toggle_active/