This commit is contained in:
Ferdinand Thiessen 2020-09-03 17:56:12 +02:00
parent 5bfa305c41
commit ea107a28dd
8 changed files with 65 additions and 179 deletions

View File

@ -1,26 +1,25 @@
""" Server-package
Initialize app, cors, database and bcrypt (for passwordhashing) and added it to the application.
Initialize also a singelton for the AccesTokenControler and start the Thread.
Initialize app, CORS, database and add it to the application.
Initialize also a singleton for the AccessTokenController and start the Thread.
"""
from pathlib import Path
_modpath = Path(__file__).parent
from flask import Flask
from flask_cors import CORS
import pkg_resources, yaml
from logging.config import dictConfig
with (_modpath/'logging.yml').open(mode='rb') as file:
config = yaml.safe_load(file.read())
dictConfig(config)
import yaml
import logging
import pkg_resources
from flask import Flask
from pathlib import Path
from flask_cors import CORS
from logging.config import dictConfig
from werkzeug.local import LocalProxy
_module_path = Path(__file__).parent
logger = LocalProxy(lambda: logging.getLogger(__name__))
with (_module_path / 'logging.yml').open(mode='rb') as file:
config = yaml.safe_load(file.read())
logging.config.dictConfig(config)
def create_app():
app = Flask(__name__)
@ -33,14 +32,15 @@ def create_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)
logger.debug('Found authentication 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 {})
logger.info('Loaded authentification plugin > %s <', entry_point.name)
app.config['FG_AUTH_BACKEND'].configure(
config[entry_point.name] if config.has_section(entry_point.name) else {})
logger.info('Loaded authentication 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.error('No authentication plugin configured or authentication plugin not found')
logger.info('Search for plugins')
for entry_point in pkg_resources.iter_entry_points('flaschengeist.plugin'):
@ -50,9 +50,3 @@ def create_app():
app.register_blueprint(entry_point.load()())
return app
#app.register_blueprint(baruser)
#app.register_blueprint(finanzer)
#app.register_blueprint(user)
#app.register_blueprint(vorstand)
#app.register_blueprint(gastrouser)
#app.register_blueprint(registration)

View File

@ -1,8 +1,10 @@
[FLASCHENGEIST]
# Set lifetime of session (idle time until you get logged out)
AccessTokenLifetime = 1800
# Select authentification provider
# Select authentication provider (builtin: auth_plain, auth_ldap)
AUTH = auth_plain
# Enable if you run flaschengeist behind a proxy, e.g. nginx + gunicorn
# PROXY = false
# Set root path, prefixes all routes
# ROOT = /
[DATABASE]
USER =

View File

@ -1,4 +1,4 @@
class Auth():
class Auth:
def configure(self, config):
pass
@ -7,14 +7,14 @@ class Auth():
user User class containing at least the uid
pw given password
HAS TO BE IMPLEMENTED!
MUST BE IMPLEMENTED!
should return False if not found or invalid credentials
should return True if success
"""
return False
raise NotImplementedError
def updateUser(self, user):
def update_user(self, user):
"""
user User class
@ -22,11 +22,10 @@ class Auth():
"""
pass
def modifyUser(self, user):
def modify_user(self, user):
"""
user User class
If backend is using (writeable) external data, then update the external database with the user provided.
"""
pass

View File

@ -45,7 +45,7 @@ def _login():
try:
logger.debug("search {{ {} }} in database".format(username))
main_controller = mc.MainController()
user = main_controller.loginUser(username, password)
user = main_controller.login_user(username, password)
logger.debug("user is {{ {} }}".format(user))
token = access_controller.create(user, user_agent=request.user_agent)
logger.debug("access token is {{ {} }}".format(token))

View File

@ -1,3 +1,5 @@
from ldap3.core.exceptions import LDAPException
import flaschengeist.modules as modules
from flaschengeist import logger
from flask import current_app as app
@ -39,11 +41,11 @@ class AuthLDAP(modules.Auth):
try:
r = self.ldap.authenticate(user.uid, password, 'uid', self.dn)
return r == True
except Exception as err:
except LDAPException as err:
logger.warning("Exception while login into ldap", exc_info=True)
return False
def modifyUser(self, user):
def modify_user(self, user):
try:
ldap_conn = self.ldap.bind(user.uid, password)
if attributes:
@ -66,7 +68,7 @@ class AuthLDAP(modules.Auth):
"username exists on ldap, rechange username on database", exc_info=True)
db.changeUsername(user, user.uid)
raise Exception(err)
except LDAPExcetpion as err:
except LDAPException as err:
if 'username' in attributes:
db.changeUsername(user, user.uid)
raise Exception(err)
@ -105,7 +107,7 @@ class AuthLDAP(modules.Auth):
debug.warning("exception in modify user data from ldap", exc_info=True)
raise LDAPExcetpion("Something went wrong in LDAP: {}".format(err))
def updateUser(self, user):
def update_user(self, user):
self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid={})'.format(user.uid), SUBTREE,
attributes=['uid', 'givenName', 'sn', 'mail'])
r = self.ldap.connection.response[0]['attributes']

View File

@ -5,23 +5,25 @@ import os
import flaschengeist.modules as modules
class AuthPlain(modules.Auth):
def login(self, user, password):
if not user:
return False
if 'password' in user.attributes:
return self._verify_password(user.attributes['password'].value, password)
return False
def _hash_password(self, password):
def _hash_password(password):
salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
pass_hash = hashlib.pbkdf2_hmac('sha3-512', password.encode('utf-8'), salt, 100000)
pass_hash = binascii.hexlify(pass_hash)
return (salt + pass_hash).decode('ascii')
def _verify_password(self, stored_password, provided_password):
def _verify_password(stored_password, provided_password):
salt = stored_password[:64]
stored_password = stored_password[64:]
pass_hash = hashlib.pbkdf2_hmac('sha3-512', provided_password.encode('utf-8'), salt.encode('ascii'), 100000)
pass_hash = binascii.hexlify(pass_hash).decode('ascii')
return pass_hash == stored_password
class AuthPlain(modules.Auth):
def login(self, user, password):
if not user:
return False
if 'password' in user.attributes:
return _verify_password(user.attributes['password'].value, password)
return False

View File

@ -1,12 +1,10 @@
import configparser
import os
import configparser
from pathlib import Path
from .. import _modpath, logger
from werkzeug.middleware.proxy_fix import ProxyFix
from .. import _module_path, logger
default = {
'FLASCHENGEIST': {
'AccessTokenLifeTime': 1800
},
'MAIL': {
'CRYPT': 'SSL/STARTLS'
}
@ -14,7 +12,7 @@ default = {
config = configparser.ConfigParser()
config.read_dict(default)
paths = [_modpath, Path.home()/".config"]
paths = [_module_path, Path.home()/".config"]
if 'FLASCHENGEIST_CONF' in os.environ:
paths.append(Path(os.environ.get("FLASCHENGEIST_CONF")))
for loc in paths:
@ -23,7 +21,8 @@ for loc in paths:
config.read_file(source)
except IOError:
pass
# Always enable this buildin plugins!
# Always enable this builtin plugins!
config.read_dict({
'auth': {
'enabled': True
@ -44,119 +43,7 @@ def configure_app(app):
)
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()
if config.has_option("FLASCHENGEIST", "ROOT"):
app.config["APPLICATION_ROOT"] = config["FLASCHENGEIST"]["ROOT"]
if config.getboolean("FLASCHENGEIST", "PROXY", fallback=False):
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)

View File

@ -7,14 +7,14 @@ from flaschengeist import logger
class Base:
def loginUser(self, username, password):
def login_user(self, username, password):
logger.info("login user {{ {} }}".format(username))
user = User.query.filter_by(uid=username).first()
if user is None:
user = User(uid=username)
if current_app.config['FG_AUTH_BACKEND'].login(user, password):
db.session.add(user)
current_app.config['FG_AUTH_BACKEND'].updateUser(user)
current_app.config['FG_AUTH_BACKEND'].update_user(user)
db.session.commit()
return user
raise PermissionDenied()