diff --git a/imaginaerraum_door_admin/webapp.py b/imaginaerraum_door_admin/webapp.py index cbaf43a..af44cfb 100644 --- a/imaginaerraum_door_admin/webapp.py +++ b/imaginaerraum_door_admin/webapp.py @@ -12,7 +12,7 @@ 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 +from email_validator import validate_email, EmailNotValidError import json import secrets @@ -146,7 +146,7 @@ def create_application(config): ldap_server = ldap3.Server(config.ldap_url) local_ldap_cache = {} # dict for caching LDAP authorization locally (stores username + hashed password) - def validate_ldap(user, password): + def validate_ldap(username, password): """Validate the user and password through an LDAP server. If the connection completes successfully the given user and password is authorized and the password is stored @@ -165,7 +165,7 @@ def create_application(config): """ try: - con = ldap3.Connection(ldap_server, user="uid=%s,ou=Users,dc=imaginaerraum,dc=de" % (user.username,), + con = ldap3.Connection(ldap_server, user=f"uid={username},ou=Users,dc=imaginaerraum,dc=de", password=password, auto_bind=True) except ldap3.core.exceptions.LDAPBindError: # server reachable but user unauthorized -> fail @@ -173,11 +173,14 @@ def create_application(config): except LDAPSocketOpenError: # server not reachable -> try cached authorization data return user.username in local_ldap_cache and verify_password(password, local_ldap_cache[user.username]) - except Exception: + except Exception as e: # for other Exceptions we just fail return False # TODO check if user has permission to edit tokens + lock_permission = con.search('ou=Users,dc=imaginaerraum,dc=de', f'(&(uid={user.username})(memberof=cn=Members,ou=Groups,dc=imaginaerraum,dc=de))') + token_granting_permission = con.search('ou=Users,dc=imaginaerraum,dc=de', + f'(&(uid={user.username})(memberof=cn=Vorstand,ou=Groups,dc=imaginaerraum,dc=de))') # if LDAP authorization succeeds we cache the password locally (in memory) to allow LDAP authentication even if # the server is not reachable local_ldap_cache[user.username] = hash_password(password) @@ -188,17 +191,32 @@ def create_application(config): password = PasswordField('Passwort', [Required()]) def validate(self): - # try authorizing locally using Flask security user datastore - authorized = super(ExtendedLoginForm, self).validate() + # try authorizing using LDAP + # authorization in LDAP uses username -> get username associated with email from the database + try: + # if an email (instead of a username) was entered for authentication we check if there already is a user + # with that email in the database + validate_email(self.email.data) + user = find_user(self.email.data) + if user is not None: + username = user.username + else: + # this means there is no user with that email in the database + username = None + except EmailNotValidError: + # else we use the entered credentials as username + username = self.email.data + + authorized = validate_ldap(username, self.password.data) + + if authorized: + logger.info(f"Admin user with credentials '{self.email.data}' authorized through LDAP") if not authorized: - # try authorizing using LDAP - # authorization in LDAP uses username -> get username associated with email from the database - user = find_user(self.email.data) - authorized = validate_ldap(user, self.password.data) - if authorized: - logger.info(f"Admin user with credentials '{self.email.data}' authorized through LDAP") - else: + # try authorizing locally using Flask security user datastore + authorized = super(ExtendedLoginForm, self).validate() + + if authorized: logger.info(f"Admin user with credentials '{self.email.data}' authorized through local database") # if any of the authorization methods is successful we authorize the user