added LDAP authorization

This commit is contained in:
Simon Pirkelmann 2021-03-22 21:22:51 +01:00
parent e79713e094
commit 1e87406fdb
2 changed files with 36 additions and 1 deletions

View File

@ -3,13 +3,14 @@ import argparse
from imaginaerraum_door_admin.webapp import create_application from imaginaerraum_door_admin.webapp import create_application
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--token_file", default="/etc/door_tokens", help="path to the file with door tokens and users") parser.add_argument("--token_file", default="/etc/door_tokens", help="path to the file with door tokens and users")
parser.add_argument("--nfc_socket", default="/tmp/nfc.sock", help="socket for handling NFC reader commands") parser.add_argument("--nfc_socket", default="/tmp/nfc.sock", help="socket for handling NFC reader commands")
parser.add_argument("--template_folder", default="templates", help="path to Flask templates folder") parser.add_argument("--template_folder", default="templates", help="path to Flask templates folder")
parser.add_argument("--static_folder", default="static", help="path to Flask static folder") parser.add_argument("--static_folder", default="static", help="path to Flask static folder")
parser.add_argument("--admin_file", help="Path to file for creating initial admin users") parser.add_argument("--admin_file", help="Path to file for creating initial admin users")
parser.add_argument("--ldap_url", default="ldaps://do.imaginaerraum.de",
help="URL for LDAP server for alternative user authorization")
parser.add_argument("--mqtt_host", default="10.10.21.2", help="IP address of MQTT broker") parser.add_argument("--mqtt_host", default="10.10.21.2", help="IP address of MQTT broker")
parser.add_argument("--port", default=80, help="Port for running the Flask server") parser.add_argument("--port", default=80, help="Port for running the Flask server")
parser.add_argument("--mail_server", default="smtp.googlemail.com", help="email server for sending security messages") parser.add_argument("--mail_server", default="smtp.googlemail.com", help="email server for sending security messages")

View File

@ -9,8 +9,10 @@ from flask_security import Security, SQLAlchemyUserDatastore, auth_required, has
from flask_security.models import fsqla_v2 as fsqla from flask_security.models import fsqla_v2 as fsqla
from flask_security.forms import LoginForm, Required, PasswordField from flask_security.forms import LoginForm, Required, PasswordField
from flask_mail import Mail from flask_mail import Mail
from flask_security.utils import find_user
from email_validator import validate_email from email_validator import validate_email
import bleach import bleach
import ldap3
from datetime import date from datetime import date
from .door_handle import DoorHandle from .door_handle import DoorHandle
@ -96,10 +98,42 @@ def create_application(config):
class User(db.Model, fsqla.FsUserMixin): class User(db.Model, fsqla.FsUserMixin):
username = db.Column(db.String(255)) username = db.Column(db.String(255))
# LDAP
ldap_server = ldap3.Server(config.ldap_url)
def validate_ldap(user, password):
# try to connect to the LDAP server
# if the connection completes successfully the given user and password is authorized
try:
con = ldap3.Connection(ldap_server, user="uid=%s,ou=Users,dc=imaginaerraum,dc=de" % (user.username,),
password=password, auto_bind=True)
except Exception:
return False
return con is not None
class ExtendedLoginForm(LoginForm): class ExtendedLoginForm(LoginForm):
email = StringField('Benutzername oder E-Mail', [Required()]) email = StringField('Benutzername oder E-Mail', [Required()])
password = PasswordField('Passwort', [Required()]) password = PasswordField('Passwort', [Required()])
def validate(self):
# authorization in LDAP uses username -> get username associated with email from the database
user = find_user(self.email.data)
# try authorizing using LDAP
response_ldap = validate_ldap(user, self.password.data)
if response_ldap:
# if LDAP authorization succeeds we update the currently stored password in the Flask user datastore
# with the one used for LDAP authorization. This way we can authorize with the LDAP password later
# even if the server is not reachable
user.password = hash_password(self.password.data)
# try authorizing using Flask security
response_orig = super(ExtendedLoginForm, self).validate()
# if any of the authorization methods is successful we authorize the user
return response_ldap or response_orig
app.config['SECURITY_MSG_USERID_NOT_PROVIDED'] = ('User ID not provided', 'error')
# Setup Flask-Security # Setup Flask-Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role) user_datastore = SQLAlchemyUserDatastore(db, User, Role)