import logging from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_security import Security, SQLAlchemyUserDatastore, hash_password from email_validator import validate_email from pathlib import Path from .door_handle import DoorHandle security = Security() db = SQLAlchemy() # create admin users (only if they don't exist already) def create_super_admins(app, user_datastore): admin_file = Path(app.config.get('ADMIN_FILE')) # setup user database when starting the app new_admin_data = [] if not admin_file.exists(): app.logger.warning( f"Admin user creation file not found at path " f"{admin_file.absolute()}." f"No super admins have been created in the datastore." ) else: # store data for new admins in memory s.t. the file can be deleted # afterwards admin_data = admin_file.read_text().split('\n') for i, line in enumerate(admin_data): if not line.strip().startswith('#'): try: user, email, pw = line.split() validate_email(email) new_admin_data.append( {'username': user, 'email': email, 'password': pw}) except Exception as e: app.logger.error( f"Error while parsing line {i} in admin config file. " f"Config file should contain lines of " f" \\n'\n " f"Exception: {e}\nAdmin account could not be created." ) with app.app_context(): db.create_all() super_admin_role = user_datastore.find_or_create_role( 'super_admin') # root admin = can create other admins admin_role = user_datastore.find_or_create_role( 'admin') # 'normal' admin local_role = user_datastore.find_or_create_role( 'local') # LDAP user or local user for d in new_admin_data: if user_datastore.find_user(email=d['email'], username=d['username']) is None: roles = [super_admin_role, admin_role] if not d['password'] == 'LDAP': roles.append(local_role) # create new admin (only if admin does not already exist) new_admin = user_datastore.create_user( email=d['email'], username=d['username'], password=hash_password(d['password']), roles=roles ) app.logger.info( f"New super admin user created with username " f"'{new_admin.username}' and email '{new_admin.email}'" f", roles = {[r.name for r in new_admin.roles]}" ) db.session.commit() def create_app(): app = Flask(__name__) app.config.from_object( 'imaginaerraum_door_admin.default_app_config.DefaultConfig' ) app.config.from_envvar('APPLICATION_SETTINGS', silent=True) token_file = Path(app.config.get('TOKEN_FILE')) if not token_file.exists(): app.logger.warning( f"Token file not found at {token_file.absolute()}. " "An empty token file will be created." ) token_file.touch() # create door objects which provides access to the token file and current # door state via MQTT app.door = DoorHandle( token_file=token_file, mqtt_host=app.config['MQTT_HOST'], nfc_socket=app.config['NFC_SOCKET'], logger=app.logger ) # Mail Config #mail = Mail(app) # Create database connection object db.init_app(app) from . webapp import door_app app.register_blueprint(door_app) # Setup Flask-Security from .auth import ExtendedLoginForm, User, Role user_datastore = SQLAlchemyUserDatastore(db, User, Role) security.init_app(app, user_datastore, login_form=ExtendedLoginForm) create_super_admins(app, user_datastore) return app