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 exists already) def create_super_admins(app, db, user_datastore, logger): # setup user database when starting the app with app.app_context(): new_admin_data = [] if app.config['ADMIN_FILE'] is not None: if not Path(app.config['ADMIN_FILE']).exists(): logger.warning( f"Admin user creation file not found at {app.config['ADMIN_FILE']}") else: # store data for new admins in memory s.t. the file can be deleted afterwards with open(app.config['ADMIN_FILE']) as f: for i, line in enumerate(f.readlines()): 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: print( f"Error while parsing line {i} in admin config file. Config file should contain lines of " f"' \\n'\n Exception: {e}\nAdmin account could not be created.") 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) logger.info( f"New super admin user created with username '{d['username']}' and email '{d['email']}', roles = {[r.name for r in roles]}") # 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) db.session.commit() def setup_logging(app): # set up logging for the web app logger = logging.getLogger('webapp') logger.setLevel(logging.INFO) if app.config['LOG_FILE'] is not None: ch = logging.FileHandler(app.config['LOG_FILE']) ch.setLevel(logging.INFO) else: # create console handler and set level to debug ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) # create formatter formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s') # add formatter to ch ch.setFormatter(formatter) # add ch to logger logger.addHandler(ch) return logger def create_app(): app = Flask(__name__) app.config.from_object('imaginaerraum_door_admin.default_app_config.DefaultConfig') app.config.from_envvar('APPLICATION_SETTINGS') logger = setup_logging(app) token_file = Path(app.config.get('TOKEN_FILE')) if not token_file.exists(): 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=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, db, user_datastore, logger) return app