From 32066b10052a45146d4ec3a163e892b39e24309c Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Sun, 23 Aug 2020 23:58:26 +0200 Subject: [PATCH] Make it possible to configure plugins. * Reworked configuration --- flaschengeist/__init__.py | 58 ++----- flaschengeist/config.example.yml | 22 --- flaschengeist/flaschengeist.example.cfg | 37 ++++ flaschengeist/modules/__init__.py | 6 + flaschengeist/modules/auth_ldap/__init__.py | 20 +++ flaschengeist/system/config.py | 162 ++++++++++++++++++ flaschengeist/system/configparser.py | 135 --------------- flaschengeist/system/controller/__init__.py | 9 - .../controller/mainController/__init__.py | 2 +- .../mainController/mainUserController.py | 10 +- setup.py | 3 +- 11 files changed, 249 insertions(+), 215 deletions(-) delete mode 100644 flaschengeist/config.example.yml create mode 100644 flaschengeist/flaschengeist.example.cfg create mode 100644 flaschengeist/modules/auth_ldap/__init__.py create mode 100644 flaschengeist/system/config.py delete mode 100644 flaschengeist/system/configparser.py diff --git a/flaschengeist/__init__.py b/flaschengeist/__init__.py index 9d97fd0..beb4b32 100644 --- a/flaschengeist/__init__.py +++ b/flaschengeist/__init__.py @@ -29,56 +29,32 @@ def create_app(): CORS(app) with app.app_context(): - from .system.controller import dbConfig, ldapConfig from .system.database import db - app.config['SECRET_KEY'] = '0a657b97ef546da90b2db91862ad4e29' - - app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://{user}:{passwd}@{host}/{database}'.format( - user=dbConfig['user'], - passwd=dbConfig['passwd'], - host=dbConfig['URL'], - database=dbConfig['database']) - app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False - -# app.config['MYSQL_CURSORCLASS'] = 'DictCursor' - app.config['LDAP_SERVER'] = ldapConfig['URL'] - app.config['LDAP_PORT'] = ldapConfig['PORT'] - if ldapConfig['BIND_DN']: - app.config['LDAP_BINDDN'] = ldapConfig['BIND_DN'] - else: - app.config['LDAP_BINDDN'] = ldapConfig['DN'] - if ldapConfig['BIND_SECRET']: - app.config['LDAP_SECRET'] = ldapConfig['BIND_SECRET'] - app.config['LDAP_USE_TLS'] = False - app.config['LDAP_USE_SSL'] = ldapConfig['SSL'] - app.config['LDAP_TLS_VERSION'] = ssl.PROTOCOL_TLSv1_2 - app.config['LDAP_REQUIRE_CERT'] = ssl.CERT_NONE - app.config['FORCE_ATTRIBUTE_VALUE_AS_LIST'] = True - - app.config['FG_AUTH_BACKENDS'] = [ - entry_point.load() for entry_point in pkg_resources.iter_entry_points('flaschengeist.auth') - ] - - ldap = LDAPConn(app) + from .system.config import configure_app, config + configure_app(app) db.init_app(app) + for entry_point in pkg_resources.iter_entry_points('flaschengeist.auth'): + logger.debug('Found authentification plugin: %s', entry_point.name) + if entry_point.name == config['FLASCHENGEIST']['AUTH']: + app.config['FG_AUTH_BACKEND'] = entry_point.load()() + app.config['FG_AUTH_BACKEND'].configure(config[entry_point.name] if config.has_section(entry_point.name) else None) + logger.info('Loaded authentification plugin > %s <', entry_point.name) + break + if not app.config['FG_AUTH_BACKEND']: + logger.error('No authentification plugin configured or authentification plugin not found') + + logger.info('Search for plugins') discovered_plugins = { entry_point.name: entry_point.load() for entry_point in pkg_resources.iter_entry_points('flaschengeist.plugin') } -#from geruecht import routes -#from geruecht.baruser.routes import baruser -#from geruecht.finanzer.routes import finanzer -#from geruecht.user.routes import user -#from geruecht.vorstand.routes import vorstand -#from geruecht.gastro.routes import gastrouser -#from geruecht.registration_route import registration - - app.logger.info("Registrate bluebrints") for name in discovered_plugins: - app.logger.info("Register plugin: %s" % name) - app.register_blueprint(discovered_plugins[name]()) + logger.debug("Found plugin: %s", name) + if config.get(name, 'enabled', fallback=False): + logger.info('Loaded plugin > %s <', name) + app.register_blueprint(discovered_plugins[name]()) return app #app.register_blueprint(baruser) diff --git a/flaschengeist/config.example.yml b/flaschengeist/config.example.yml deleted file mode 100644 index 62a16a2..0000000 --- a/flaschengeist/config.example.yml +++ /dev/null @@ -1,22 +0,0 @@ -AccessTokenLifeTime: 1800 -Database: - URL: - user: - passwd: - database: -LDAP: - URL: - DN: - BIND_DN: - BIND_SECRET: - SSL: - USER_DN: - ADMIN_DN: - ADMIN_SECRET: -Mail: - URL: - port: - user: - passwd: - email: - crypt: SSL/STARTLS \ No newline at end of file diff --git a/flaschengeist/flaschengeist.example.cfg b/flaschengeist/flaschengeist.example.cfg new file mode 100644 index 0000000..1631a1c --- /dev/null +++ b/flaschengeist/flaschengeist.example.cfg @@ -0,0 +1,37 @@ +[FLASCHENGEIST] +# Set lifetime of session (idle time until you get logged out) +AccessTokenLifetime = 1800 +# Select authentification provider +AUTH = auth_plain + +[DATABASE] +USER = +HOST = +PASSWD = +DATABASE = + +# [LDAP] +# URL = +# PORT = +# BINDDN = +# SECRET = +# USE_SSL = +## ADMIN_DN: +## ADMIN_SECRET: + +[MAIL] +URL = +PORT = +USER = +PASSWD = +MAIL = +CRYPT = SSL/STARTLS + +############################ +# Configuration of plugins # +############################ +[geruecht] +enable = true + +[schubu] +enable = false diff --git a/flaschengeist/modules/__init__.py b/flaschengeist/modules/__init__.py index a0c81bc..728864c 100644 --- a/flaschengeist/modules/__init__.py +++ b/flaschengeist/modules/__init__.py @@ -1,4 +1,10 @@ class Auth(): + def defaultConfig(self): + return None + + def configure(self, config): + pass + def login(self, user, pw): """ user User class containing at least the uid diff --git a/flaschengeist/modules/auth_ldap/__init__.py b/flaschengeist/modules/auth_ldap/__init__.py new file mode 100644 index 0000000..b69f2c9 --- /dev/null +++ b/flaschengeist/modules/auth_ldap/__init__.py @@ -0,0 +1,20 @@ +import flaschengeist.modules as modules +from flask import current_app as app + +class AuthPlain(modules.Auth): + def configure(self, config): + app.config.update( + 'LDAP_SERVER' = config['URL'], + 'LDAP_PORT' = config['PORT'], + 'LDAP_BINDDN' = config['BINDDN'], + 'LDAP_SECRET' = config['SECRET'] + 'LDAP_USE_SSL' = config['USE_SSL'], + 'LDAP_TLS_VERSION' = ssl.PROTOCOL_TLSv1_2, + 'LDAP_REQUIRE_CERT' = ssl.CERT_NONE, + 'FORCE_ATTRIBUTE_VALUE_AS_LIST' = True + ) + + def login(self, user, password): + if not user: + return False + return False diff --git a/flaschengeist/system/config.py b/flaschengeist/system/config.py new file mode 100644 index 0000000..1ba87c5 --- /dev/null +++ b/flaschengeist/system/config.py @@ -0,0 +1,162 @@ +import configparser +import os +from pathlib import Path +from .. import _modpath, logger + +default = { + 'FLASCHENGEIST': { + 'AccessTokenLifeTime': 1800 + }, + 'MAIL': { + 'CRYPT': 'SSL/STARTLS' + } +} + +config = configparser.ConfigParser() +config.read_dict(default) +pathes = [_modpath, Path.home()/".config"] +if 'FLASCHENGEIST_CONF' in os.environ: + pathes.append(Path(os.environ.get("FLASCHENGEIST_CONF"))) +for loc in pathes: + try: + with (loc/"flaschengeist.cfg").open() as source: + config.read_file(source) + except IOError: + pass +# Always enable this buildin plugins! +config.read_dict({ + 'auth': { + 'enabled': True + } +}) + + +def configure_app(app): + if not config.has_option('FLASCHENGEIST', 'SECRET_KEY'): + logger.warn('No secret key was configured, please configure one for production systems!') + app.config['SECRET_KEY'] = config.get('FLASCHENGEIST', 'SECRET_KEY', fallback='0a657b97ef546da90b2db91862ad4e29') + + app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://{user}:{passwd}@{host}/{database}'.format( + user=config['DATABASE']['user'], + passwd=config['DATABASE']['passwd'], + host=config['DATABASE']['host'], + database=config['DATABASE']['database'] + ) + app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + +#class ConifgParser(): +# def __init__(self, file='config.yml'): +# self.file = file +# with open(file, 'r') as f: +# self.config = yaml.safe_load(f) +# +# if 'Database' not in self.config: +# self.__error__( +# 'Wrong Configuration for Database. You should configure databaseconfig with "URL", "user", "passwd", "database"') +# if 'URL' not in self.config['Database'] or 'user' not in self.config['Database'] or 'passwd' not in self.config['Database'] or 'database' not in self.config['Database']: +# self.__error__( +# 'Wrong Configuration for Database. You should configure databaseconfig with "URL", "user", "passwd", "database"') +# +# self.db = self.config['Database'] +# logger.debug("Set Databaseconfig: {}".format(self.db)) +# +# if 'LDAP' not in self.config: +# self.__error__( +# 'Wrong Configuration for LDAP. You should configure ldapconfig with "URL" and "BIND_DN"') +# if 'URL' not in self.config['LDAP'] or 'DN' not in self.config['LDAP']: +# self.__error__( +# 'Wrong Configuration for LDAP. You should configure ldapconfig with "URL" and "BIND_DN"') +# if 'PORT' not in self.config['LDAP']: +# logger.info( +# 'No Config for port in LDAP found. Set it to default: {}'.format(389)) +# self.config['LDAP']['PORT'] = 389 +# if 'ADMIN_DN' not in self.config['LDAP']: +# logger.info( +# 'No Config for ADMIN_DN in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) +# ) +# self.config['LDAP']['ADMIN_DN'] = None +# if 'ADMIN_SECRET' not in self.config['LDAP']: +# logger.info( +# 'No Config for ADMIN_SECRET in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) +# ) +# self.config['LDAP']['ADMIN_SECRET'] = None +# if 'USER_DN' not in self.config['LDAP']: +# logger.info( +# 'No Config for USER_DN in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) +# ) +# self.config['LDAP']['USER_DN'] = None +# if 'BIND_DN' not in self.config['LDAP']: +# logger.info( +# 'No Config for BIND_DN in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) +# ) +# self.config['LDAP']['BIND_DN'] = None +# if 'BIND_SECRET' not in self.config['LDAP']: +# logger.info( +# 'No Config for BIND_SECRET in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) +# ) +# self.config['LDAP']['BIND_SECRET'] = None +# if 'SSL' not in self.config['LDAP']: +# logger.info( +# 'No Config for SSL in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(False) +# ) +# self.config['LDAP']['SSL'] = False +# else: +# self.config['LDAP']['SSL'] = bool(self.config['LDAP']['SSL']) +# self.ldap = self.config['LDAP'] +# logger.debug("Set LDAPconfig: {}".format(self.ldap)) +# if 'AccessTokenLifeTime' in self.config: +# self.accessTokenLifeTime = int(self.config['AccessTokenLifeTime']) +# logger.info("Set AccessTokenLifeTime: {}".format( +# self.accessTokenLifeTime)) +# else: +# self.accessTokenLifeTime = default['AccessTokenLifeTime'] +# logger.info("No Config for AccessTokenLifetime found. Set it to default: {}".format( +# self.accessTokenLifeTime)) +# +# if 'Mail' not in self.config: +# self.config['Mail'] = default['Mail'] +# logger.info('No Conifg for Mail found. Set it to defaul: {}'.format( +# self.config['Mail'])) +# if 'URL' not in self.config['Mail']: +# self.config['Mail']['URL'] = default['Mail']['URL'] +# logger.info("No Config for URL in Mail found. Set it to default") +# if 'port' not in self.config['Mail']: +# self.config['Mail']['port'] = default['Mail']['port'] +# logger.info("No Config for port in Mail found. Set it to default") +# else: +# self.config['Mail']['port'] = int(self.config['Mail']['port']) +# logger.info("No Conifg for port in Mail found. Set it to default") +# if 'user' not in self.config['Mail']: +# self.config['Mail']['user'] = default['Mail']['user'] +# logger.info("No Config for user in Mail found. Set it to default") +# if 'passwd' not in self.config['Mail']: +# self.config['Mail']['passwd'] = default['Mail']['passwd'] +# logger.info("No Config for passwd in Mail found. Set it to default") +# if 'email' not in self.config['Mail']: +# self.config['Mail']['email'] = default['Mail']['email'] +# logger.info("No Config for email in Mail found. Set it to default") +# if 'crypt' not in self.config['Mail']: +# self.config['Mail']['crypt'] = default['Mail']['crypt'] +# logger.info("No Config for crypt in Mail found. Set it to default") +# self.mail = self.config['Mail'] +# logger.debug('Set Mailconfig: {}'.format(self.mail)) +# +# def getLDAP(self): +# return self.ldap +# +# def getDatabase(self): +# return self.db +# +# def getAccessToken(self): +# return self.accessTokenLifeTime +# +# def getMail(self): +# return self.mail +# +# def __error__(self, msg): +# logger.error(msg, exc_info=True) +# sys.exit(-1) +# +# +#if __name__ == '__main__': +# ConifgParser() diff --git a/flaschengeist/system/configparser.py b/flaschengeist/system/configparser.py deleted file mode 100644 index 94297e4..0000000 --- a/flaschengeist/system/configparser.py +++ /dev/null @@ -1,135 +0,0 @@ -import yaml -import sys -from flask import current_app -from werkzeug.local import LocalProxy -logger = LocalProxy(lambda: current_app.logger) - -default = { - 'AccessTokenLifeTime': 1800, - 'Mail': { - 'URL': '', - 'port': 0, - 'user': '', - 'passwd': '', - 'email': '', - 'crypt': 'STARTTLS' - } -} - - -class ConifgParser(): - def __init__(self, file='config.yml'): - self.file = file - with open(file, 'r') as f: - self.config = yaml.safe_load(f) - - if 'Database' not in self.config: - self.__error__( - 'Wrong Configuration for Database. You should configure databaseconfig with "URL", "user", "passwd", "database"') - if 'URL' not in self.config['Database'] or 'user' not in self.config['Database'] or 'passwd' not in self.config['Database'] or 'database' not in self.config['Database']: - self.__error__( - 'Wrong Configuration for Database. You should configure databaseconfig with "URL", "user", "passwd", "database"') - - self.db = self.config['Database'] - logger.debug("Set Databaseconfig: {}".format(self.db)) - - if 'LDAP' not in self.config: - self.__error__( - 'Wrong Configuration for LDAP. You should configure ldapconfig with "URL" and "BIND_DN"') - if 'URL' not in self.config['LDAP'] or 'DN' not in self.config['LDAP']: - self.__error__( - 'Wrong Configuration for LDAP. You should configure ldapconfig with "URL" and "BIND_DN"') - if 'PORT' not in self.config['LDAP']: - logger.info( - 'No Config for port in LDAP found. Set it to default: {}'.format(389)) - self.config['LDAP']['PORT'] = 389 - if 'ADMIN_DN' not in self.config['LDAP']: - logger.info( - 'No Config for ADMIN_DN in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) - ) - self.config['LDAP']['ADMIN_DN'] = None - if 'ADMIN_SECRET' not in self.config['LDAP']: - logger.info( - 'No Config for ADMIN_SECRET in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) - ) - self.config['LDAP']['ADMIN_SECRET'] = None - if 'USER_DN' not in self.config['LDAP']: - logger.info( - 'No Config for USER_DN in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) - ) - self.config['LDAP']['USER_DN'] = None - if 'BIND_DN' not in self.config['LDAP']: - logger.info( - 'No Config for BIND_DN in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) - ) - self.config['LDAP']['BIND_DN'] = None - if 'BIND_SECRET' not in self.config['LDAP']: - logger.info( - 'No Config for BIND_SECRET in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) - ) - self.config['LDAP']['BIND_SECRET'] = None - if 'SSL' not in self.config['LDAP']: - logger.info( - 'No Config for SSL in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(False) - ) - self.config['LDAP']['SSL'] = False - else: - self.config['LDAP']['SSL'] = bool(self.config['LDAP']['SSL']) - self.ldap = self.config['LDAP'] - logger.debug("Set LDAPconfig: {}".format(self.ldap)) - if 'AccessTokenLifeTime' in self.config: - self.accessTokenLifeTime = int(self.config['AccessTokenLifeTime']) - logger.info("Set AccessTokenLifeTime: {}".format( - self.accessTokenLifeTime)) - else: - self.accessTokenLifeTime = default['AccessTokenLifeTime'] - logger.info("No Config for AccessTokenLifetime found. Set it to default: {}".format( - self.accessTokenLifeTime)) - - if 'Mail' not in self.config: - self.config['Mail'] = default['Mail'] - logger.info('No Conifg for Mail found. Set it to defaul: {}'.format( - self.config['Mail'])) - if 'URL' not in self.config['Mail']: - self.config['Mail']['URL'] = default['Mail']['URL'] - logger.info("No Config for URL in Mail found. Set it to default") - if 'port' not in self.config['Mail']: - self.config['Mail']['port'] = default['Mail']['port'] - logger.info("No Config for port in Mail found. Set it to default") - else: - self.config['Mail']['port'] = int(self.config['Mail']['port']) - logger.info("No Conifg for port in Mail found. Set it to default") - if 'user' not in self.config['Mail']: - self.config['Mail']['user'] = default['Mail']['user'] - logger.info("No Config for user in Mail found. Set it to default") - if 'passwd' not in self.config['Mail']: - self.config['Mail']['passwd'] = default['Mail']['passwd'] - logger.info("No Config for passwd in Mail found. Set it to default") - if 'email' not in self.config['Mail']: - self.config['Mail']['email'] = default['Mail']['email'] - logger.info("No Config for email in Mail found. Set it to default") - if 'crypt' not in self.config['Mail']: - self.config['Mail']['crypt'] = default['Mail']['crypt'] - logger.info("No Config for crypt in Mail found. Set it to default") - self.mail = self.config['Mail'] - logger.debug('Set Mailconfig: {}'.format(self.mail)) - - def getLDAP(self): - return self.ldap - - def getDatabase(self): - return self.db - - def getAccessToken(self): - return self.accessTokenLifeTime - - def getMail(self): - return self.mail - - def __error__(self, msg): - logger.error(msg, exc_info=True) - sys.exit(-1) - - -if __name__ == '__main__': - ConifgParser() diff --git a/flaschengeist/system/controller/__init__.py b/flaschengeist/system/controller/__init__.py index 095c03c..83ca20c 100644 --- a/flaschengeist/system/controller/__init__.py +++ b/flaschengeist/system/controller/__init__.py @@ -1,8 +1,3 @@ -from ..configparser import ConifgParser -from flaschengeist import _modpath - -config = ConifgParser(_modpath/'config.yml') - class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): @@ -10,7 +5,3 @@ class Singleton(type): cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] -dbConfig = config.getDatabase() -ldapConfig = config.getLDAP() -accConfig = config.getAccessToken() -mailConfig = config.getMail() diff --git a/flaschengeist/system/controller/mainController/__init__.py b/flaschengeist/system/controller/mainController/__init__.py index d426514..4fad656 100644 --- a/flaschengeist/system/controller/mainController/__init__.py +++ b/flaschengeist/system/controller/mainController/__init__.py @@ -1,4 +1,4 @@ -from .. import Singleton, mailConfig +from .. import Singleton from ...models.user import User from datetime import datetime, timedelta from . import mainUserController diff --git a/flaschengeist/system/controller/mainController/mainUserController.py b/flaschengeist/system/controller/mainController/mainUserController.py index 1f553fa..d37cb6f 100644 --- a/flaschengeist/system/controller/mainController/mainUserController.py +++ b/flaschengeist/system/controller/mainController/mainUserController.py @@ -167,10 +167,8 @@ class Base: user = User.query.filter_by(uid=username).first() if user is None: user = User(uid=username) - for backend in current_app.config['FG_AUTH_BACKENDS']: - b = backend() - if b.login(user, password): - db.session.add(user) - db.session.commit() - return user + if current_app.config['FG_AUTH_BACKEND'].login(user, password): + db.session.add(user) + db.session.commit() + return user raise PermissionDenied() diff --git a/setup.py b/setup.py index b16c5c4..83cff6d 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,8 @@ setup( 'auth = flaschengeist.modules.auth:register' ], 'flaschengeist.auth': [ - 'plain_auth = flaschengeist.modules.auth_plain:AuthPlain' + 'auth_plain = flaschengeist.modules.auth_plain:AuthPlain', + 'auth_ldap = flaschengeist.modules.auth_ldap:AuthLDAP' ] } )