Use flask logger, fixing app creation, split geruecht and user

This commit is contained in:
Ferdinand Thiessen 2020-08-22 14:02:39 +02:00
parent ec0bd12caa
commit 187dc40730
13 changed files with 204 additions and 222 deletions

4
.gitignore vendored
View File

@ -117,8 +117,8 @@ dmypy.json
#ide #ide
.idea .idea
.swp *.swp
.swo *.swo
.vscode/ .vscode/
*.log *.log

View File

@ -1,5 +1,84 @@
""" Server-package """ 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.
"""
from pathlib import Path from pathlib import Path
_modpath = Path(__file__).parent _modpath = Path(__file__).parent
from flask import Flask
from flask_cors import CORS
from flask_ldapconn import LDAPConn
import ssl
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)
def create_app():
app = Flask(__name__)
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)
db.init_app(app)
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]())
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,72 +0,0 @@
""" 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.
"""
from .system.logger import getDebugLogger
from .system.controller import dbConfig, ldapConfig
from flask_mysqldb import MySQL
from flask_ldapconn import LDAPConn
import ssl
DEBUG = getDebugLogger()
DEBUG.info("Initialize App")
from flask import Flask
from flask_cors import CORS
DEBUG.info("Build APP")
app = Flask(__name__)
CORS(app)
app.config['SECRET_KEY'] = '0a657b97ef546da90b2db91862ad4e29'
app.config['MYSQL_HOST'] = dbConfig['URL']
app.config['MYSQL_USER'] = dbConfig['user']
app.config['MYSQL_PASSWORD'] = dbConfig['passwd']
app.config['MYSQL_DB'] = dbConfig['database']
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
ldap = LDAPConn(app)
db = MySQL(app)
import pkg_resources
discovered_plugins = {
entry_point.name: entry_point.load()
for entry_point
in pkg_resources.iter_entry_points('flaschengeist.plugins')
}
#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
DEBUG.info("Registrate bluebrints")
for name in discovered_plugins:
DEBUG.info("Register %s" % name)
app.register_blueprint(discovered_plugins[name]())
#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,7 +1,8 @@
import yaml import yaml
import sys import sys
from .logger import getDebugLogger from flask import current_app
DEBUG = getDebugLogger() from werkzeug.local import LocalProxy
logger = LocalProxy(lambda: current_app.logger)
default = { default = {
'AccessTokenLifeTime': 1800, 'AccessTokenLifeTime': 1800,
@ -30,7 +31,7 @@ class ConifgParser():
'Wrong Configuration for Database. You should configure databaseconfig with "URL", "user", "passwd", "database"') 'Wrong Configuration for Database. You should configure databaseconfig with "URL", "user", "passwd", "database"')
self.db = self.config['Database'] self.db = self.config['Database']
DEBUG.debug("Set Databaseconfig: {}".format(self.db)) logger.debug("Set Databaseconfig: {}".format(self.db))
if 'LDAP' not in self.config: if 'LDAP' not in self.config:
self.__error__( self.__error__(
@ -39,79 +40,79 @@ class ConifgParser():
self.__error__( self.__error__(
'Wrong Configuration for LDAP. You should configure ldapconfig with "URL" and "BIND_DN"') 'Wrong Configuration for LDAP. You should configure ldapconfig with "URL" and "BIND_DN"')
if 'PORT' not in self.config['LDAP']: if 'PORT' not in self.config['LDAP']:
DEBUG.info( logger.info(
'No Config for port in LDAP found. Set it to default: {}'.format(389)) 'No Config for port in LDAP found. Set it to default: {}'.format(389))
self.config['LDAP']['PORT'] = 389 self.config['LDAP']['PORT'] = 389
if 'ADMIN_DN' not in self.config['LDAP']: if 'ADMIN_DN' not in self.config['LDAP']:
DEBUG.info( logger.info(
'No Config for ADMIN_DN in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) '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 self.config['LDAP']['ADMIN_DN'] = None
if 'ADMIN_SECRET' not in self.config['LDAP']: if 'ADMIN_SECRET' not in self.config['LDAP']:
DEBUG.info( logger.info(
'No Config for ADMIN_SECRET in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) '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 self.config['LDAP']['ADMIN_SECRET'] = None
if 'USER_DN' not in self.config['LDAP']: if 'USER_DN' not in self.config['LDAP']:
DEBUG.info( logger.info(
'No Config for USER_DN in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) '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 self.config['LDAP']['USER_DN'] = None
if 'BIND_DN' not in self.config['LDAP']: if 'BIND_DN' not in self.config['LDAP']:
DEBUG.info( logger.info(
'No Config for BIND_DN in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) '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 self.config['LDAP']['BIND_DN'] = None
if 'BIND_SECRET' not in self.config['LDAP']: if 'BIND_SECRET' not in self.config['LDAP']:
DEBUG.info( logger.info(
'No Config for BIND_SECRET in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) '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 self.config['LDAP']['BIND_SECRET'] = None
if 'SSL' not in self.config['LDAP']: if 'SSL' not in self.config['LDAP']:
DEBUG.info( logger.info(
'No Config for SSL in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(False) 'No Config for SSL in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(False)
) )
self.config['LDAP']['SSL'] = False self.config['LDAP']['SSL'] = False
else: else:
self.config['LDAP']['SSL'] = bool(self.config['LDAP']['SSL']) self.config['LDAP']['SSL'] = bool(self.config['LDAP']['SSL'])
self.ldap = self.config['LDAP'] self.ldap = self.config['LDAP']
DEBUG.info("Set LDAPconfig: {}".format(self.ldap)) logger.debug("Set LDAPconfig: {}".format(self.ldap))
if 'AccessTokenLifeTime' in self.config: if 'AccessTokenLifeTime' in self.config:
self.accessTokenLifeTime = int(self.config['AccessTokenLifeTime']) self.accessTokenLifeTime = int(self.config['AccessTokenLifeTime'])
DEBUG.info("Set AccessTokenLifeTime: {}".format( logger.info("Set AccessTokenLifeTime: {}".format(
self.accessTokenLifeTime)) self.accessTokenLifeTime))
else: else:
self.accessTokenLifeTime = default['AccessTokenLifeTime'] self.accessTokenLifeTime = default['AccessTokenLifeTime']
DEBUG.info("No Config for AccessTokenLifetime found. Set it to default: {}".format( logger.info("No Config for AccessTokenLifetime found. Set it to default: {}".format(
self.accessTokenLifeTime)) self.accessTokenLifeTime))
if 'Mail' not in self.config: if 'Mail' not in self.config:
self.config['Mail'] = default['Mail'] self.config['Mail'] = default['Mail']
DEBUG.info('No Conifg for Mail found. Set it to defaul: {}'.format( logger.info('No Conifg for Mail found. Set it to defaul: {}'.format(
self.config['Mail'])) self.config['Mail']))
if 'URL' not in self.config['Mail']: if 'URL' not in self.config['Mail']:
self.config['Mail']['URL'] = default['Mail']['URL'] self.config['Mail']['URL'] = default['Mail']['URL']
DEBUG.info("No Config for URL in Mail found. Set it to default") logger.info("No Config for URL in Mail found. Set it to default")
if 'port' not in self.config['Mail']: if 'port' not in self.config['Mail']:
self.config['Mail']['port'] = default['Mail']['port'] self.config['Mail']['port'] = default['Mail']['port']
DEBUG.info("No Config for port in Mail found. Set it to default") logger.info("No Config for port in Mail found. Set it to default")
else: else:
self.config['Mail']['port'] = int(self.config['Mail']['port']) self.config['Mail']['port'] = int(self.config['Mail']['port'])
DEBUG.info("No Conifg for port in Mail found. Set it to default") logger.info("No Conifg for port in Mail found. Set it to default")
if 'user' not in self.config['Mail']: if 'user' not in self.config['Mail']:
self.config['Mail']['user'] = default['Mail']['user'] self.config['Mail']['user'] = default['Mail']['user']
DEBUG.info("No Config for user in Mail found. Set it to default") logger.info("No Config for user in Mail found. Set it to default")
if 'passwd' not in self.config['Mail']: if 'passwd' not in self.config['Mail']:
self.config['Mail']['passwd'] = default['Mail']['passwd'] self.config['Mail']['passwd'] = default['Mail']['passwd']
DEBUG.info("No Config for passwd in Mail found. Set it to default") logger.info("No Config for passwd in Mail found. Set it to default")
if 'email' not in self.config['Mail']: if 'email' not in self.config['Mail']:
self.config['Mail']['email'] = default['Mail']['email'] self.config['Mail']['email'] = default['Mail']['email']
DEBUG.info("No Config for email in Mail found. Set it to default") logger.info("No Config for email in Mail found. Set it to default")
if 'crypt' not in self.config['Mail']: if 'crypt' not in self.config['Mail']:
self.config['Mail']['crypt'] = default['Mail']['crypt'] self.config['Mail']['crypt'] = default['Mail']['crypt']
DEBUG.info("No Config for crypt in Mail found. Set it to default") logger.info("No Config for crypt in Mail found. Set it to default")
self.mail = self.config['Mail'] self.mail = self.config['Mail']
DEBUG.info('Set Mailconfig: {}'.format(self.mail)) logger.debug('Set Mailconfig: {}'.format(self.mail))
def getLDAP(self): def getLDAP(self):
return self.ldap return self.ldap
@ -126,7 +127,7 @@ class ConifgParser():
return self.mail return self.mail
def __error__(self, msg): def __error__(self, msg):
DEBUG.error(msg, exc_info=True) logger.error(msg, exc_info=True)
sys.exit(-1) sys.exit(-1)

View File

@ -0,0 +1,3 @@
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()

View File

@ -1,6 +1,7 @@
from functools import wraps from functools import wraps
from .logger import getDebugLogger from flask import current_app
DEBUG = getDebugLogger() from werkzeug.local import LocalProxy
logger = LocalProxy(lambda: current_app.logger)
def login_required(**kwargs): def login_required(**kwargs):
@ -14,24 +15,24 @@ def login_required(**kwargs):
groups = kwargs["groups"] groups = kwargs["groups"]
if "bar" in kwargs: if "bar" in kwargs:
bar = kwargs["bar"] bar = kwargs["bar"]
DEBUG.debug("groups are {{ {} }}".format(groups)) logger.debug("groups are {{ {} }}".format(groups))
def real_decorator(func): def real_decorator(func):
@wraps(func) @wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
token = request.headers.get('Token') token = request.headers.get('Token')
DEBUG.debug("token is {{ {} }}".format(token)) logger.debug("token is {{ {} }}".format(token))
accToken = accessController.validateAccessToken(token, groups) accToken = accessController.validateAccessToken(token, groups)
DEBUG.debug("accToken is {{ {} }}".format(accToken)) logger.debug("accToken is {{ {} }}".format(accToken))
kwargs['accToken'] = accToken kwargs['accToken'] = accToken
if accToken: if accToken:
DEBUG.debug("token {{ {} }} is valid".format(token)) logger.debug("token {{ {} }} is valid".format(token))
if accToken.lock_bar and not bar: if accToken.lock_bar and not bar:
return jsonify({"error": "error", return jsonify({"error": "error",
"message": "permission forbidden"}), 403 "message": "permission forbidden"}), 403
return func(*args, **kwargs) return func(*args, **kwargs)
else: else:
DEBUG.warning("token {{ {} }} is not valid".format(token)) logger.warning("token {{ {} }} is not valid".format(token))
return jsonify({"error": "error", return jsonify({"error": "error",
"message": "permission denied"}), 401 "message": "permission denied"}), 401
return wrapper return wrapper

View File

@ -1,32 +0,0 @@
import logging
import logging.config
import yaml
from os import path, makedirs, getcwd
from .. import _modpath
fname = _modpath/'logging.yml'
if not path.exists("geruecht/log/debug"):
a = path.join(path.curdir, "geruecht", "log", "debug")
makedirs(a)
if not path.exists("geruecht/log/info"):
b = path.join(path.curdir, "geruecht", "log", "info")
makedirs(b)
with fname.open(mode='rb') as file:
config = yaml.safe_load(file.read())
logging.config.dictConfig(config)
def getDebugLogger():
return logging.getLogger("debug_logger")
def getCreditLogger():
return logging.getLogger("credit_logger")
def getJobsLogger():
return logging.getLogger("jobs_logger")

View File

@ -1,49 +0,0 @@
from ..logger import getDebugLogger
from datetime import datetime
debug = getDebugLogger()
class User():
""" Database Object for User
Table for all safed User
Attributes:
id: Id in Database as Primary Key.
username: Username of the User to Login
firstname: Firstname of the User
lastname: Lastname of the User
mail: mail address of the User
"""
def __init__(self, data):
debug.info("init user")
if 'id' in data:
self.id = int(data['id'])
self.firstname = data['firstname']
self.lastname = data['lastname']
if 'mail' in data:
self.mail = data['mail']
else:
self.mail = ''
if 'username' in data:
self.username = data['username']
else:
self.username = None
debug.debug("user is {{ {} }}".format(self))
def updateData(self, data):
debug.info("update data of user")
if 'firstname' in data:
self.firstname = data['firstname']
if 'lastname' in data:
self.lastname = data['lastname']
if 'mail' in data:
self.mail = data['mail']
if 'username' in data:
self.username = data['username']
def __repr__(self):
return "User({}, {})".format(self.uid, self.username)
# TODO: user attributes (uid|key|value), getter, setter, has

View File

@ -1,9 +1,10 @@
from datetime import datetime from datetime import datetime
from ..logger import getDebugLogger from ..database import db
from flask import current_app
from werkzeug.local import LocalProxy
logger = LocalProxy(lambda: current_app.logger)
debug = getDebugLogger() class AccessToken(db.Model):
class AccessToken():
""" Model for an AccessToken """ Model for an AccessToken
Attributes: Attributes:
@ -11,39 +12,23 @@ class AccessToken():
user: Is an User. user: Is an User.
token: String to verify access later. token: String to verify access later.
""" """
__tablename__ = 'session'
timestamp = None id = db.Column(db.Integer, primary_key=True)
user = None timestamp = db.Column(db.DateTime, default=datetime.utcnow)
token = None user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
user = db.relationship("User", back_populates="sessions")
def __init__(self, id, user, token, lifetime, lock_bar=False, timestamp=datetime.now(), browser=None, platform=None): token = db.Column(db.String(30))
""" Initialize Class AccessToken lifetime = db.Column(db.Integer)
browser = db.Column(db.String(30))
No more to say. platform = db.Column(db.String(30))
Args:
User: Is an User to set.
token: Is a String to verify later
timestamp: Default current time, but can set to an other datetime-Object.
"""
debug.debug("init accesstoken")
self.id = id
self.user = user
self.timestamp = timestamp
self.lifetime = lifetime
self.token = token
self.lock_bar = lock_bar
self.browser = browser
self.platform = platform
debug.debug("accesstoken is {{ {} }}".format(self))
def updateTimestamp(self): def updateTimestamp(self):
""" Update the Timestamp """ Update the Timestamp
Update the Timestamp to the current Time. Update the Timestamp to the current Time.
""" """
debug.debug("update timestamp from accesstoken {{ {} }}".format(self)) logger.debug("update timestamp from accesstoken {{ {} }}".format(self))
self.timestamp = datetime.now() self.timestamp = datetime.utcnow()
def toJSON(self): def toJSON(self):
""" Create Dic to dump in JSON """ Create Dic to dump in JSON

View File

@ -0,0 +1,63 @@
from datetime import datetime
from ..database import db
from .accessToken import AccessToken
from sqlalchemy.orm.collections import attribute_mapped_collection
from flask import current_app
from werkzeug.local import LocalProxy
logger = LocalProxy(lambda: current_app.logger)
association_table = db.Table('user_group',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('group_id', db.Integer, db.ForeignKey('group.id'))
)
class User(db.Model):
""" Database Object for User
Table for all safed User
Attributes:
id: Id in Database as Primary Key.
uid: User ID used by authentification provider
displayname: Name to show
firstname: Firstname of the User
lastname: Lastname of the User
mail: mail address of the User
"""
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
uid = db.Column(db.String(30))
displayname = db.Column(db.String(20))
firstname = db.Column(db.String(20))
lastname = db.Column(db.String(20))
mail = db.Column(db.String(20))
groups = db.relationship("UserGroup", secondary=association_table)
sessions = db.relationship("AccessToken", back_populates="user")
attributes = db.relationship("UserAttribute", collection_class=attribute_mapped_collection('name'), cascade="all, delete")
def updateData(self, data):
logger.debug("update data of user")
if 'uid' in data:
self.uid = data['uid']
if 'firstname' in data:
self.firstname = data['firstname']
if 'lastname' in data:
self.lastname = data['lastname']
if 'mail' in data:
self.mail = data['mail']
if 'displayname' in data:
self.displayname = data['displayname']
class UserAttribute(db.Model):
__tablename__ = 'userAttribute'
id = db.Column(db.Integer, primary_key=True)
user = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
name = db.Column(db.String(30))
value = db.Column(db.String(192))
class UserGroup(db.Model):
__tablename__ = 'group'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30))

View File

@ -1,5 +1,5 @@
#!/usr/bin/python3 #!/usr/bin/python3
from flaschengeist.app import app from flaschengeist import create_app
""" Main """ Main
@ -7,4 +7,4 @@ from flaschengeist.app import app
""" """
if __name__ == '__main__': if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0') create_app().run(debug=True, host='0.0.0.0')

View File

@ -10,10 +10,13 @@ setup(
packages=find_packages(), packages=find_packages(),
package_data={'': ['*.yml']}, package_data={'': ['*.yml']},
scripts=['run_flaschengeist'], scripts=['run_flaschengeist'],
install_requires=['Flask >= 1.1', 'PyYAML>=5.3.1', "flask_mysqldb", "flask_ldapconn", "flask_cors"], install_requires=['Flask >= 1.1', 'PyYAML>=5.3.1', 'sqlalchemy>=1.3', "flask_sqlalchemy", "flask_ldapconn", "flask_cors"],
entry_points = { entry_points = {
'flaschengeist.plugins': [ 'flaschengeist.plugin': [
'users = flaschengeist.modules.user:register' 'user = flaschengeist.modules.user:register'
],
'flaschengeist.auth': [
'plain_auth = flaschengeist.modules.auth_plain:AuthPlain'
] ]
} }
) )