Format code with black (line length: 120)

https://github.com/psf/black
This commit is contained in:
Ferdinand Thiessen 2020-10-15 22:10:50 +02:00
parent 2c55edf6a8
commit 41e60425a9
19 changed files with 284 additions and 306 deletions

View File

@ -11,11 +11,11 @@ from pathlib import Path
from logging.config import dictConfig from logging.config import dictConfig
from werkzeug.local import LocalProxy from werkzeug.local import LocalProxy
__version__ = pkg_resources.get_distribution('flaschengeist').version __version__ = pkg_resources.get_distribution("flaschengeist").version
_module_path = Path(__file__).parent _module_path = Path(__file__).parent
logger = LocalProxy(lambda: logging.getLogger(__name__)) logger = LocalProxy(lambda: logging.getLogger(__name__))
with (_module_path / 'logging.yml').open(mode='rb') as file: with (_module_path / "logging.yml").open(mode="rb") as file:
config = yaml.safe_load(file.read()) config = yaml.safe_load(file.read())
logging.config.dictConfig(config) logging.config.dictConfig(config)

View File

@ -32,31 +32,32 @@ class CustomJSONEncoder(JSONEncoder):
def __load_plugins(app): def __load_plugins(app):
logger.info('Search for plugins') logger.info("Search for plugins")
app.config['FG_PLUGINS'] = {} app.config["FG_PLUGINS"] = {}
for entry_point in pkg_resources.iter_entry_points('flaschengeist.plugin'): for entry_point in pkg_resources.iter_entry_points("flaschengeist.plugin"):
logger.debug("Found plugin: >{}<".format(entry_point.name)) logger.debug("Found plugin: >{}<".format(entry_point.name))
plugin = None plugin = None
if config.get(entry_point.name, 'enabled', fallback=False): if config.get(entry_point.name, "enabled", fallback=False):
plugin = entry_point.load()(config[entry_point.name] if config.has_section(entry_point.name) else {}) plugin = entry_point.load()(config[entry_point.name] if config.has_section(entry_point.name) else {})
if plugin.blueprint: if plugin.blueprint:
app.register_blueprint(plugin.blueprint) app.register_blueprint(plugin.blueprint)
logger.info("Load plugin >{}<".format(entry_point.name)) logger.info("Load plugin >{}<".format(entry_point.name))
if isinstance(plugin, AuthPlugin): if isinstance(plugin, AuthPlugin):
logger.debug('Found authentication plugin: %s', entry_point.name) logger.debug("Found authentication plugin: %s", entry_point.name)
if entry_point.name == config['FLASCHENGEIST']['AUTH']: if entry_point.name == config["FLASCHENGEIST"]["AUTH"]:
app.config['FG_AUTH_BACKEND'] = plugin app.config["FG_AUTH_BACKEND"] = plugin
else: else:
del plugin del plugin
else: else:
app.config["FG_PLUGINS"][entry_point.name] = plugin app.config["FG_PLUGINS"][entry_point.name] = plugin
if 'FG_AUTH_BACKEND' not in app.config: if "FG_AUTH_BACKEND" not in app.config:
logger.error('No authentication plugin configured or authentication plugin not found') logger.error("No authentication plugin configured or authentication plugin not found")
def install_all(): def install_all():
from flaschengeist.system.database import db from flaschengeist.system.database import db
from flaschengeist.system.models import user, event, accessToken from flaschengeist.system.models import user, event, accessToken
db.create_all() db.create_all()
db.session.commit() db.session.commit()
for name, plugin in current_app.config["FG_PLUGINS"].items(): for name, plugin in current_app.config["FG_PLUGINS"].items():
@ -73,6 +74,7 @@ def create_app():
with app.app_context(): with app.app_context():
from .system.database import db from .system.database import db
configure_app(app) configure_app(app)
db.init_app(app) db.init_app(app)
__load_plugins(app) __load_plugins(app)
@ -80,6 +82,7 @@ def create_app():
@app.route("/", methods=["GET"]) @app.route("/", methods=["GET"])
def __get_state(): def __get_state():
from . import __version__ as version from . import __version__ as version
return jsonify({"plugins": app.config["FG_PLUGINS"], "version": version}) return jsonify({"plugins": app.config["FG_PLUGINS"], "version": version})
@app.errorhandler(Exception) @app.errorhandler(Exception)

View File

@ -9,7 +9,7 @@ class Plugin:
self.permissions = permissions self.permissions = permissions
def install(self): def install(self):
""" Installation routine """Installation routine
Is always called with Flask application context Is always called with Flask application context
""" """
pass pass
@ -17,7 +17,7 @@ class Plugin:
class AuthPlugin(Plugin): class AuthPlugin(Plugin):
def login(self, user, pw): def login(self, user, pw):
""" Login routine, MUST BE IMPLEMENTED! """Login routine, MUST BE IMPLEMENTED!
Args: Args:
user: User class containing at least the uid user: User class containing at least the uid

View File

@ -14,37 +14,37 @@ from flaschengeist.system.decorator import login_required
from flaschengeist.system.controller import accessTokenController, userController from flaschengeist.system.controller import accessTokenController, userController
access_controller = LocalProxy(lambda: accessTokenController.AccessTokenController()) access_controller = LocalProxy(lambda: accessTokenController.AccessTokenController())
auth_bp = Blueprint('auth', __name__) auth_bp = Blueprint("auth", __name__)
class AuthRoutePlugin(Plugin): class AuthRoutePlugin(Plugin):
def __init__(self, conf): def __init__(self, conf):
super().__init__(blueprint=auth_bp) super().__init__(blueprint=auth_bp)
################################################# #################################################
# Routes # # Routes #
# # # #
# /auth POST: login (new token) # # /auth POST: login (new token) #
# GET: get all tokens for user # # GET: get all tokens for user #
# /auth/<token> GET: get lifetime of token # # /auth/<token> GET: get lifetime of token #
# PUT: set new lifetime # # PUT: set new lifetime #
# DELETE: logout / delete token # # DELETE: logout / delete token #
################################################# #################################################
@auth_bp.route("/auth", methods=['POST']) @auth_bp.route("/auth", methods=["POST"])
def _create_token(): def _create_token():
""" Login User """Login User
Login in User and create an AccessToken for the User. Login in User and create an AccessToken for the User.
Requires POST data {'userid': string, 'password': string} Requires POST data {'userid': string, 'password': string}
Returns: Returns:
A JSON-File with user information and created token or errors A JSON-File with user information and created token or errors
""" """
logger.debug("Start log in.") logger.debug("Start log in.")
data = request.get_json() data = request.get_json()
try: try:
userid = data['userid'] userid = data["userid"]
password = data['password'] password = data["password"]
except KeyError: except KeyError:
raise BadRequest("Missing parameter(s)") raise BadRequest("Missing parameter(s)")
@ -61,15 +61,13 @@ class AuthRoutePlugin(Plugin):
access_controller.clear_expired() access_controller.clear_expired()
return jsonify({"user": user, "token": token, "permissions": user.get_permissions()}) return jsonify({"user": user, "token": token, "permissions": user.get_permissions()})
@auth_bp.route("/auth", methods=["GET"])
@auth_bp.route("/auth", methods=['GET'])
@login_required() @login_required()
def _get_tokens(access_token, **kwargs): def _get_tokens(access_token, **kwargs):
tokens = access_controller.get_users_tokens(access_token.user) tokens = access_controller.get_users_tokens(access_token.user)
return jsonify(tokens) return jsonify(tokens)
@auth_bp.route("/auth/<token>", methods=["DELETE"])
@auth_bp.route("/auth/<token>", methods=['DELETE'])
@login_required() @login_required()
def _delete_token(token, access_token, **kwargs): def _delete_token(token, access_token, **kwargs):
logger.debug("Try to delete access token {{ {} }}".format(token)) logger.debug("Try to delete access token {{ {} }}".format(token))
@ -83,8 +81,7 @@ class AuthRoutePlugin(Plugin):
access_controller.clear_expired() access_controller.clear_expired()
return jsonify({"ok": "ok"}) return jsonify({"ok": "ok"})
@auth_bp.route("/auth/<token>", methods=["GET"])
@auth_bp.route("/auth/<token>", methods=['GET'])
@login_required() @login_required()
def _get_token(token, access_token, **kwargs): def _get_token(token, access_token, **kwargs):
logger.debug("get token {{ {} }}".format(token)) logger.debug("get token {{ {} }}".format(token))
@ -95,8 +92,7 @@ class AuthRoutePlugin(Plugin):
raise Forbidden raise Forbidden
return jsonify(token) return jsonify(token)
@auth_bp.route("/auth/<token>", methods=["PUT"])
@auth_bp.route("/auth/<token>", methods=['PUT'])
@login_required() @login_required()
def _set_lifetime(token, access_token, **kwargs): def _set_lifetime(token, access_token, **kwargs):
token = access_controller.get_token(token, access_token.user) token = access_controller.get_token(token, access_token.user)
@ -105,7 +101,7 @@ class AuthRoutePlugin(Plugin):
# Valid tokens from other users and invalid tokens now are looking the same # Valid tokens from other users and invalid tokens now are looking the same
raise Forbidden raise Forbidden
try: try:
lifetime = request.get_json()['value'] lifetime = request.get_json()["value"]
logger.debug("set lifetime {{ {} }} to access token {{ {} }}".format(lifetime, token)) logger.debug("set lifetime {{ {} }} to access token {{ {} }}".format(lifetime, token))
access_controller.set_lifetime(token, lifetime) access_controller.set_lifetime(token, lifetime)
return jsonify({"ok": "ok"}) return jsonify({"ok": "ok"})

View File

@ -14,83 +14,89 @@ class AuthLDAP(AuthPlugin):
def __init__(self, config): def __init__(self, config):
super().__init__() super().__init__()
defaults = { defaults = {"PORT": "389", "USE_SSL": "False"}
'PORT': '389',
'USE_SSL': 'False'
}
for name in defaults: for name in defaults:
if name not in config: if name not in config:
config[name] = defaults[name] config[name] = defaults[name]
app.config.update( app.config.update(
LDAP_SERVER=config['URL'], LDAP_SERVER=config["URL"],
LDAP_PORT=config.getint('PORT'), LDAP_PORT=config.getint("PORT"),
LDAP_BINDDN=config['BINDDN'], LDAP_BINDDN=config["BINDDN"],
LDAP_USE_TLS=False, LDAP_USE_TLS=False,
LDAP_USE_SSL=config.getboolean('USE_SSL'), LDAP_USE_SSL=config.getboolean("USE_SSL"),
LDAP_TLS_VERSION=ssl.PROTOCOL_TLSv1_2, LDAP_TLS_VERSION=ssl.PROTOCOL_TLSv1_2,
LDAP_REQUIRE_CERT=ssl.CERT_NONE, LDAP_REQUIRE_CERT=ssl.CERT_NONE,
FORCE_ATTRIBUTE_VALUE_AS_LIST=True FORCE_ATTRIBUTE_VALUE_AS_LIST=True,
) )
if 'SECRET' in config: if "SECRET" in config:
app.config['LDAP_SECRET'] = config['SECRET'], app.config["LDAP_SECRET"] = (config["SECRET"],)
self.ldap = LDAPConn(app) self.ldap = LDAPConn(app)
self.dn = config['BASEDN'] self.dn = config["BASEDN"]
def login(self, user, password): def login(self, user, password):
if not user: if not user:
return False return False
return self.ldap.authenticate(user.uid, password, 'uid', self.dn) return self.ldap.authenticate(user.uid, password, "uid", self.dn)
def update_user(self, user): def update_user(self, user):
self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid={})'.format(user.uid), SUBTREE, self.ldap.connection.search(
attributes=['uid', 'givenName', 'sn', 'mail']) "ou=user,{}".format(self.dn),
r = self.ldap.connection.response[0]['attributes'] "(uid={})".format(user.uid),
if r['uid'][0] == user.uid: SUBTREE,
user.set_attribute('DN', self.ldap.connection.response[0]['dn']) attributes=["uid", "givenName", "sn", "mail"],
user.firstname = r['givenName'][0] )
user.lastname = r['sn'][0] r = self.ldap.connection.response[0]["attributes"]
if r['mail']: if r["uid"][0] == user.uid:
user.mail = r['mail'][0] user.set_attribute("DN", self.ldap.connection.response[0]["dn"])
if 'displayName' in r: user.firstname = r["givenName"][0]
user.display_name = r['displayName'][0] user.lastname = r["sn"][0]
if r["mail"]:
user.mail = r["mail"][0]
if "displayName" in r:
user.display_name = r["displayName"][0]
for group in self._get_groups(user.uid): for group in self._get_groups(user.uid):
user.add_role(group) user.add_role(group)
def _get_groups(self, uid): def _get_groups(self, uid):
groups = [] groups = []
self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid={})'.format(uid), SUBTREE, self.ldap.connection.search(
attributes=['gidNumber']) "ou=user,{}".format(self.dn), "(uid={})".format(uid), SUBTREE, attributes=["gidNumber"]
main_group_number = self.ldap.connection.response[0]['attributes']['gidNumber'] )
main_group_number = self.ldap.connection.response[0]["attributes"]["gidNumber"]
if main_group_number: if main_group_number:
if type(main_group_number) is list: if type(main_group_number) is list:
main_group_number = main_group_number[0] main_group_number = main_group_number[0]
self.ldap.connection.search('ou=group,{}'.format(self.dn), '(gidNumber={})'.format(main_group_number), self.ldap.connection.search(
attributes=['cn']) "ou=group,{}".format(self.dn), "(gidNumber={})".format(main_group_number), attributes=["cn"]
groups.append(self.ldap.connection.response[0]['attributes']['cn'][0]) )
groups.append(self.ldap.connection.response[0]["attributes"]["cn"][0])
self.ldap.connection.search('ou=group,{}'.format(self.dn), '(memberUID={})'.format(uid), SUBTREE, self.ldap.connection.search(
attributes=['cn']) "ou=group,{}".format(self.dn), "(memberUID={})".format(uid), SUBTREE, attributes=["cn"]
)
groups_data = self.ldap.connection.response groups_data = self.ldap.connection.response
for data in groups_data: for data in groups_data:
groups.append(data['attributes']['cn'][0]) groups.append(data["attributes"]["cn"][0])
return groups return groups
def modify_user(self, user: User, password, new_password=None): def modify_user(self, user: User, password, new_password=None):
try: try:
dn = user.attributes['DN'].value dn = user.attributes["DN"].value
ldap_conn = self.ldap.connect(dn, password) ldap_conn = self.ldap.connect(dn, password)
modifier = {} modifier = {}
for name, ldap_name in [("firstname", "givenName"), for name, ldap_name in [
("lastname", "sn"), ("firstname", "givenName"),
("mail", "mail"), ("lastname", "sn"),
("display_name", "displayName")]: ("mail", "mail"),
("display_name", "displayName"),
]:
if getattr(user, name): if getattr(user, name):
modifier[ldap_name] = [(MODIFY_REPLACE, [getattr(user, name)])] modifier[ldap_name] = [(MODIFY_REPLACE, [getattr(user, name)])]
if new_password: if new_password:
salted_password = hashed(HASHED_SALTED_SHA512, new_password) salted_password = hashed(HASHED_SALTED_SHA512, new_password)
modifier['userPassword'] = [(MODIFY_REPLACE, [salted_password])] modifier["userPassword"] = [(MODIFY_REPLACE, [salted_password])]
ldap_conn.modify(dn, modifier) ldap_conn.modify(dn, modifier)
except (LDAPPasswordIsMandatoryError, LDAPBindError): except (LDAPPasswordIsMandatoryError, LDAPBindError):
raise BadRequest raise BadRequest

View File

@ -7,24 +7,24 @@ from flaschengeist.system.models.user import User
def _hash_password(password): def _hash_password(password):
salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii') salt = hashlib.sha256(os.urandom(60)).hexdigest().encode("ascii")
pass_hash = hashlib.pbkdf2_hmac('sha3-512', password.encode('utf-8'), salt, 100000) pass_hash = hashlib.pbkdf2_hmac("sha3-512", password.encode("utf-8"), salt, 100000)
pass_hash = binascii.hexlify(pass_hash) pass_hash = binascii.hexlify(pass_hash)
return (salt + pass_hash).decode('ascii') return (salt + pass_hash).decode("ascii")
def _verify_password(stored_password, provided_password): def _verify_password(stored_password, provided_password):
salt = stored_password[:64] salt = stored_password[:64]
stored_password = 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 = hashlib.pbkdf2_hmac("sha3-512", provided_password.encode("utf-8"), salt.encode("ascii"), 100000)
pass_hash = binascii.hexlify(pass_hash).decode('ascii') pass_hash = binascii.hexlify(pass_hash).decode("ascii")
return pass_hash == stored_password return pass_hash == stored_password
class AuthPlain(modules.Auth): class AuthPlain(modules.Auth):
def login(self, user: User, password: str): def login(self, user: User, password: str):
if user and 'password' in user.attributes: if user and "password" in user.attributes:
return _verify_password(user.attributes['password'].value, password) return _verify_password(user.attributes["password"].value, password)
return False return False
def modify_user(self, user, password, new_password=None): def modify_user(self, user, password, new_password=None):

View File

@ -11,12 +11,12 @@ from . import Plugin, send_message_hook
class MailMessagePlugin(Plugin): class MailMessagePlugin(Plugin):
def __init__(self, config): def __init__(self, config):
super().__init__() super().__init__()
self.server = config['SERVER'] self.server = config["SERVER"]
self.port = config['PORT'] self.port = config["PORT"]
self.user = config['USER'] self.user = config["USER"]
self.password = config['PASSWORD'] self.password = config["PASSWORD"]
self.crypt = config['CRYPT'] self.crypt = config["CRYPT"]
self.mail = config['MAIL'] self.mail = config["MAIL"]
@send_message_hook @send_message_hook
def send_mail(self, msg: Message): def send_mail(self, msg: Message):
@ -26,18 +26,18 @@ class MailMessagePlugin(Plugin):
recipients = userController.get_user_by_role(msg.receiver) recipients = userController.get_user_by_role(msg.receiver)
mail = MIMEMultipart() mail = MIMEMultipart()
mail['From'] = self.mail mail["From"] = self.mail
mail['To'] = ", ".join(recipients) mail["To"] = ", ".join(recipients)
mail['Subject'] = msg.subject mail["Subject"] = msg.subject
msg.attach(msg.message) msg.attach(msg.message)
if not self.smtp: if not self.smtp:
self.__connect() self.__connect()
self.smtp.sendmail(self.mail, recipients, msg.as_string()) self.smtp.sendmail(self.mail, recipients, msg.as_string())
def __connect(self): def __connect(self):
if self.crypt == 'SSL': if self.crypt == "SSL":
self.smtp = smtplib.SMTP_SSL(self.server, self.port) self.smtp = smtplib.SMTP_SSL(self.server, self.port)
if self.crypt == 'STARTTLS': if self.crypt == "STARTTLS":
self.smtp = smtplib.SMTP(self.smtpServer, self.port) self.smtp = smtplib.SMTP(self.smtpServer, self.port)
self.smtp.starttls() self.smtp.starttls()
self.smtp.login(self.user, self.password) self.smtp.login(self.user, self.password)

View File

@ -12,18 +12,18 @@ class RolesPlugin(Plugin):
def __init__(self, config): def __init__(self, config):
super().__init__(config, roles_bp) super().__init__(config, roles_bp)
###################################################### ######################################################
# Routes # # Routes #
# # # #
# /roles POST: register new # # /roles POST: register new #
# GET: get all roles # # GET: get all roles #
# /roles/permissions GET: get all permissions # # /roles/permissions GET: get all permissions #
# /roles/<rid> GET: get role with rid # # /roles/<rid> GET: get role with rid #
# PUT: modify role / permission # # PUT: modify role / permission #
# DELETE: remove role # # DELETE: remove role #
###################################################### ######################################################
@roles_bp.route("/roles", methods=['POST']) @roles_bp.route("/roles", methods=["POST"])
@login_required() @login_required()
def add_role(self): def add_role(self):
data = request.get_json() data = request.get_json()
@ -34,31 +34,27 @@ class RolesPlugin(Plugin):
role = roleController.create_role(data["name"], permissions) role = roleController.create_role(data["name"], permissions)
return jsonify({"ok": "ok", "id": role.id}) return jsonify({"ok": "ok", "id": role.id})
@roles_bp.route("/roles", methods=['GET']) @roles_bp.route("/roles", methods=["GET"])
@login_required() @login_required()
def list_roles(self, **kwargs): def list_roles(self, **kwargs):
roles = roleController.get_roles() roles = roleController.get_roles()
return jsonify(roles) return jsonify(roles)
@roles_bp.route("/roles/permissions", methods=['GET']) @roles_bp.route("/roles/permissions", methods=["GET"])
@login_required() @login_required()
def list_permissions(self, **kwargs): def list_permissions(self, **kwargs):
permissions = roleController.get_permissions() permissions = roleController.get_permissions()
return jsonify(permissions) return jsonify(permissions)
@roles_bp.route("/roles/<rid>", methods=['GET']) @roles_bp.route("/roles/<rid>", methods=["GET"])
@login_required() @login_required()
def __get_role(self, rid, **kwargs): def __get_role(self, rid, **kwargs):
role = roleController.get_role(rid) role = roleController.get_role(rid)
if role: if role:
return jsonify({ return jsonify({"id": role.id, "name": role, "permissions": role.permissions})
"id": role.id,
"name": role,
"permissions": role.permissions
})
raise NotFound raise NotFound
@roles_bp.route("/roles/<rid>", methods=['PUT']) @roles_bp.route("/roles/<rid>", methods=["PUT"])
@login_required() @login_required()
def __edit_role(self, rid, **kwargs): def __edit_role(self, rid, **kwargs):
role = roleController.get_role(rid) role = roleController.get_role(rid)
@ -66,14 +62,14 @@ class RolesPlugin(Plugin):
raise NotFound raise NotFound
data = request.get_json() data = request.get_json()
if 'name' in data: if "name" in data:
role.name = data["name"] role.name = data["name"]
if "permissions" in data: if "permissions" in data:
roleController.set_permissions(role, data["permissions"]) roleController.set_permissions(role, data["permissions"])
roleController.update_role(role) roleController.update_role(role)
return jsonify({"ok": "ok"}) return jsonify({"ok": "ok"})
@roles_bp.route("/roles/<rid>", methods=['DELETE']) @roles_bp.route("/roles/<rid>", methods=["DELETE"])
@login_required() @login_required()
def __delete_role(self, rid, **kwargs): def __delete_role(self, rid, **kwargs):
if not roleController.delete_role(rid): if not roleController.delete_role(rid):

View File

@ -16,28 +16,28 @@ class SchedulePlugin(Plugin):
def __init__(self, config): def __init__(self, config):
super().__init__(blueprint=schedule_bp) super().__init__(blueprint=schedule_bp)
#################################################################################### ####################################################################################
# Routes # # Routes #
# # # #
# /schedule/events POST: create new # # /schedule/events POST: create new #
# GET: get all events this month # # GET: get all events this month #
# /schedule/events/<year>/<month> GET: get events by month / date # # /schedule/events/<year>/<month> GET: get events by month / date #
# /<year>/<month>/<day> # # /<year>/<month>/<day> #
# /schedule/events/<id> GET: get event by ID # # /schedule/events/<id> GET: get event by ID #
# DELETE: delete specified event # # DELETE: delete specified event #
# PUT: modify specified event # # PUT: modify specified event #
# /schedule/events/<id>/slots GET: get EventSlots of Event # # /schedule/events/<id>/slots GET: get EventSlots of Event #
# POST: add new EventSlot to Event # # POST: add new EventSlot to Event #
# /schedule/events/<id>/slots/<sid> PUT: modify EventSlot # # /schedule/events/<id>/slots/<sid> PUT: modify EventSlot #
# GET: get specified EventSlot # # GET: get specified EventSlot #
# /schedule/events/<id>/slots/<sid>/jobs POST: add User # # /schedule/events/<id>/slots/<sid>/jobs POST: add User #
# /schedule/eventKinds # /schedule/eventKinds
# /schedule/eventKinds/<id> # /schedule/eventKinds/<id>
# PUT: modify user # # PUT: modify user #
# DELETE: remove user # # DELETE: remove user #
#################################################################################### ####################################################################################
@schedule_bp.route("/events/<int:id>", methods=['GET']) @schedule_bp.route("/events/<int:id>", methods=["GET"])
@login_required() # roles=['schedule_read']) @login_required() # roles=['schedule_read'])
def __get_event(self, id, **kwargs): def __get_event(self, id, **kwargs):
event = eventController.get_event(id) event = eventController.get_event(id)
@ -45,9 +45,9 @@ class SchedulePlugin(Plugin):
raise NotFound raise NotFound
return jsonify(event) return jsonify(event)
@schedule_bp.route("/events", methods=['GET']) @schedule_bp.route("/events", methods=["GET"])
@schedule_bp.route("/events/<int:year>/<int:month>", methods=['GET']) @schedule_bp.route("/events/<int:year>/<int:month>", methods=["GET"])
@schedule_bp.route("/events/<int:year>/<int:month>/<int:day>", methods=['GET']) @schedule_bp.route("/events/<int:year>/<int:month>/<int:day>", methods=["GET"])
@login_required() # roles=['schedule_read']) @login_required() # roles=['schedule_read'])
def __get_events(self, year=datetime.now().year, month=datetime.now().month, day=None, **kwrags): def __get_events(self, year=datetime.now().year, month=datetime.now().month, day=None, **kwrags):
"""Get Event objects for specified date (or month or year), """Get Event objects for specified date (or month or year),
@ -72,15 +72,14 @@ class SchedulePlugin(Plugin):
if month == 12: if month == 12:
end = datetime(year=year + 1, month=1, day=1) end = datetime(year=year + 1, month=1, day=1)
else: else:
end = datetime(year=year, month=month+1, day=1) end = datetime(year=year, month=month + 1, day=1)
events = eventController.get_events(begin, end) events = eventController.get_events(begin, end)
return jsonify(events) return jsonify(events)
except ValueError: except ValueError:
raise BadRequest("Invalid date given") raise BadRequest("Invalid date given")
@schedule_bp.route("/eventKinds", methods=["POST"])
@schedule_bp.route("/eventKinds", methods=['POST'])
@login_required() @login_required()
def __new_event_kind(self, **kwargs): def __new_event_kind(self, **kwargs):
data = request.get_json() data = request.get_json()
@ -89,7 +88,6 @@ class SchedulePlugin(Plugin):
kind = eventController.create_event_kind(data["name"]) kind = eventController.create_event_kind(data["name"])
return jsonify({"ok": "ok", "id": kind.id}) return jsonify({"ok": "ok", "id": kind.id})
@schedule_bp.route("/slotKinds", methods=["POST"]) @schedule_bp.route("/slotKinds", methods=["POST"])
@login_required() @login_required()
def __new_slot_kind(self, **kwargs): def __new_slot_kind(self, **kwargs):
@ -99,26 +97,25 @@ class SchedulePlugin(Plugin):
kind = eventController.create_job_kind(data["name"]) kind = eventController.create_job_kind(data["name"])
return jsonify({"ok": "ok", "id": kind.id}) return jsonify({"ok": "ok", "id": kind.id})
@schedule_bp.route("/events", methods=["POST"])
@schedule_bp.route("/events", methods=['POST'])
@login_required() @login_required()
def __new_event(self, **kwargs): def __new_event(self, **kwargs):
data = request.get_json() data = request.get_json()
event = eventController.create_event(begin=parser.isoparse(data["begin"]), event = eventController.create_event(
end=parser.isoparse(data["end"]), begin=parser.isoparse(data["begin"]),
description=data["description"], end=parser.isoparse(data["end"]),
kind=EventKind.query.get(data["kind"])) description=data["description"],
kind=EventKind.query.get(data["kind"]),
)
return jsonify({"ok": "ok", "id": event.id}) return jsonify({"ok": "ok", "id": event.id})
@schedule_bp.route("/events/<int:id>", methods=["DELETE"]) @schedule_bp.route("/events/<int:id>", methods=["DELETE"])
@login_required() @login_required()
def __delete_event(self, id, **kwargs): def __delete_event(self, id, **kwargs):
if not eventController.delete_event(id): if not eventController.delete_event(id):
raise NotFound raise NotFound
db.session.commit() db.session.commit()
return jsonify({'ok': 'ok'}) return jsonify({"ok": "ok"})
@schedule_bp.route("/eventKinds/<int:id>", methods=["PUT"]) @schedule_bp.route("/eventKinds/<int:id>", methods=["PUT"])
@login_required() @login_required()
@ -129,7 +126,6 @@ class SchedulePlugin(Plugin):
eventController.rename_event_kind(id, data["name"]) eventController.rename_event_kind(id, data["name"])
return jsonify({"ok": "ok"}) return jsonify({"ok": "ok"})
@schedule_bp.route("/events/<int:event_id>/slots", methods=["GET"]) @schedule_bp.route("/events/<int:event_id>/slots", methods=["GET"])
@login_required() @login_required()
def __get_slots(self, event_id, **kwargs): def __get_slots(self, event_id, **kwargs):
@ -138,7 +134,6 @@ class SchedulePlugin(Plugin):
raise NotFound raise NotFound
return jsonify({event.slots}) return jsonify({event.slots})
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["GET"]) @schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["GET"])
@login_required() @login_required()
def __get_slot(self, event_id, slot_id, **kwargs): def __get_slot(self, event_id, slot_id, **kwargs):
@ -147,7 +142,6 @@ class SchedulePlugin(Plugin):
return jsonify(slot) return jsonify(slot)
raise NotFound raise NotFound
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["DELETE"]) @schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["DELETE"])
@login_required() @login_required()
def __delete_slot(self, event_id, slot_id, **kwargs): def __delete_slot(self, event_id, slot_id, **kwargs):
@ -155,7 +149,6 @@ class SchedulePlugin(Plugin):
return jsonify({"ok": "ok"}) return jsonify({"ok": "ok"})
raise NotFound raise NotFound
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["PUT"]) @schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["PUT"])
@login_required() @login_required()
def __update_slot(self, event_id, slot_id, **kwargs): def __update_slot(self, event_id, slot_id, **kwargs):
@ -163,13 +156,12 @@ class SchedulePlugin(Plugin):
if not data: if not data:
raise BadRequest raise BadRequest
for job in data['jobs']: for job in data["jobs"]:
eventController.add_job(job.kind, job.user) eventController.add_job(job.kind, job.user)
if eventController.delete_event_slot(slot_id, event_id): if eventController.delete_event_slot(slot_id, event_id):
return jsonify({"ok": "ok"}) return jsonify({"ok": "ok"})
raise NotFound raise NotFound
@schedule_bp.route("/events/<int:event_id>/slots", methods=["POST"]) @schedule_bp.route("/events/<int:event_id>/slots", methods=["POST"])
@login_required() @login_required()
def __add_slot(self, event_id, **kwargs): def __add_slot(self, event_id, **kwargs):
@ -190,6 +182,5 @@ class SchedulePlugin(Plugin):
eventController.add_slot(event, **attr) eventController.add_slot(event, **attr)
return jsonify({"ok": "ok"}) return jsonify({"ok": "ok"})
def __edit_event(self): def __edit_event(self):
... ...

View File

@ -7,36 +7,36 @@ from flaschengeist.system.decorator import login_required
from flaschengeist.system.controller import userController from flaschengeist.system.controller import userController
users_bp = Blueprint("users", __name__) users_bp = Blueprint("users", __name__)
permissions = {'EDIT_USER': 'edit_user'} permissions = {"EDIT_USER": "edit_user"}
class UsersPlugin(Plugin): class UsersPlugin(Plugin):
def __init__(self, config): def __init__(self, config):
super().__init__(blueprint=users_bp, permissions=permissions) super().__init__(blueprint=users_bp, permissions=permissions)
################################################# #################################################
# Routes # # Routes #
# # # #
# /users POST: register new # # /users POST: register new #
# GET: get all users # # GET: get all users #
# /users/<uid> GET: get user with uid # # /users/<uid> GET: get user with uid #
# PUT: modify user # # PUT: modify user #
# DELETE: remove user # # DELETE: remove user #
################################################# #################################################
@users_bp.route("/users", methods=['POST']) @users_bp.route("/users", methods=["POST"])
def __registration(self): def __registration(self):
logger.debug("Register new User...") logger.debug("Register new User...")
return jsonify({"ok": "ok... well not implemented"}) return jsonify({"ok": "ok... well not implemented"})
@users_bp.route("/users", methods=['GET']) @users_bp.route("/users", methods=["GET"])
@login_required() @login_required()
def __list_users(self, **kwargs): def __list_users(self, **kwargs):
logger.debug("Retrieve list of all users") logger.debug("Retrieve list of all users")
users = userController.get_users() users = userController.get_users()
return jsonify(users) return jsonify(users)
@users_bp.route("/users/<uid>", methods=['GET']) @users_bp.route("/users/<uid>", methods=["GET"])
@login_required() @login_required()
def __get_user(self, uid, **kwargs): def __get_user(self, uid, **kwargs):
logger.debug("Get information of user {{ {} }}".format(uid)) logger.debug("Get information of user {{ {} }}".format(uid))
@ -45,7 +45,7 @@ class UsersPlugin(Plugin):
return jsonify(user) return jsonify(user)
raise NotFound raise NotFound
@users_bp.route("/users/<uid>", methods=['PUT']) @users_bp.route("/users/<uid>", methods=["PUT"])
@login_required() @login_required()
def __edit_user(self, uid, **kwargs): def __edit_user(self, uid, **kwargs):
logger.debug("Modify information of user {{ {} }}".format(uid)) logger.debug("Modify information of user {{ {} }}".format(uid))
@ -53,16 +53,16 @@ class UsersPlugin(Plugin):
if not user: if not user:
raise NotFound raise NotFound
if uid != kwargs['access_token'].user.uid and user.has_permissions(permissions['EDIT_USER']): if uid != kwargs["access_token"].user.uid and user.has_permissions(permissions["EDIT_USER"]):
return Forbidden return Forbidden
data = request.get_json() data = request.get_json()
if 'password' not in data: if "password" not in data:
raise BadRequest("Password is missing") raise BadRequest("Password is missing")
for key in ["firstname", "lastname", "display_name", "mail"]: for key in ["firstname", "lastname", "display_name", "mail"]:
if key in data: if key in data:
setattr(user, key, data[key]) setattr(user, key, data[key])
new_password = data['new_password'] if 'new_password' in data else None new_password = data["new_password"] if "new_password" in data else None
userController.modify_user(user, data['password'], new_password) userController.modify_user(user, data["password"], new_password)
userController.update_user(user) userController.update_user(user)
return jsonify({"ok": "ok"}) return jsonify({"ok": "ok"})

View File

@ -4,51 +4,37 @@ from pathlib import Path
from werkzeug.middleware.proxy_fix import ProxyFix from werkzeug.middleware.proxy_fix import ProxyFix
from .. import _module_path, logger from .. import _module_path, logger
default = { default = {"MAIL": {"CRYPT": "SSL/STARTLS"}}
'MAIL': {
'CRYPT': 'SSL/STARTLS'
}
}
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read_dict(default) config.read_dict(default)
paths = [_module_path, Path.home()/".config"] paths = [_module_path, Path.home() / ".config"]
if 'FLASCHENGEIST_CONF' in os.environ: if "FLASCHENGEIST_CONF" in os.environ:
paths.append(Path(os.environ.get("FLASCHENGEIST_CONF"))) paths.append(Path(os.environ.get("FLASCHENGEIST_CONF")))
for loc in paths: for loc in paths:
try: try:
with (loc/"flaschengeist.cfg").open() as source: with (loc / "flaschengeist.cfg").open() as source:
logger.info("Reading config file from >{}<".format(loc)) logger.info("Reading config file from >{}<".format(loc))
config.read_file(source) config.read_file(source)
except IOError: except IOError:
pass pass
# Always enable this builtin plugins! # Always enable this builtin plugins!
config.read_dict({ config.read_dict({"auth": {"enabled": True}, "roles": {"enabled": True}, "users": {"enabled": True}})
'auth': {
'enabled': True
},
'roles': {
'enabled': True
},
'users': {
'enabled': True
}
})
def configure_app(app): def configure_app(app):
if not config.has_option('FLASCHENGEIST', 'SECRET_KEY'): if not config.has_option("FLASCHENGEIST", "SECRET_KEY"):
logger.warn('No secret key was configured, please configure one for production systems!') 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["SECRET_KEY"] = config.get("FLASCHENGEIST", "SECRET_KEY", fallback="0a657b97ef546da90b2db91862ad4e29")
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://{user}:{passwd}@{host}/{database}'.format( app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://{user}:{passwd}@{host}/{database}".format(
user=config['DATABASE']['USER'], user=config["DATABASE"]["USER"],
passwd=config['DATABASE']['PASSWORD'], passwd=config["DATABASE"]["PASSWORD"],
host=config['DATABASE']['HOST'], host=config["DATABASE"]["HOST"],
database=config['DATABASE']['DATABASE'] database=config["DATABASE"]["DATABASE"],
) )
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
if config.has_option("FLASCHENGEIST", "ROOT"): if config.has_option("FLASCHENGEIST", "ROOT"):
logger.debug("Setting application root to >{}<".format(config["FLASCHENGEIST"]["ROOT"])) logger.debug("Setting application root to >{}<".format(config["FLASCHENGEIST"]["ROOT"]))

View File

@ -8,37 +8,37 @@ from . import Singleton
class AccessTokenController(metaclass=Singleton): class AccessTokenController(metaclass=Singleton):
""" Control all created AccessToken """Control all created AccessToken
This Class create, delete, find and manage AccessToken. This Class create, delete, find and manage AccessToken.
Attributes: Attributes:
lifetime: Variable for the Lifetime of one AccessToken in seconds. lifetime: Variable for the Lifetime of one AccessToken in seconds.
""" """
def __init__(self, lifetime=1800): def __init__(self, lifetime=1800):
self.lifetime = lifetime self.lifetime = lifetime
def validate_token(self, token, user_agent, permissions): def validate_token(self, token, user_agent, permissions):
""" Verify access token """Verify access token
Verify an AccessToken and Roles so if the User has permission or not. Verify an AccessToken and Roles so if the User has permission or not.
Retrieves the access token if valid else retrieves False Retrieves the access token if valid else retrieves False
Args: Args:
token: Token to verify. token: Token to verify.
user_agent: User agent of browser to check user_agent: User agent of browser to check
permissions: Permissions needed to access restricted routes permissions: Permissions needed to access restricted routes
Returns: Returns:
An the AccessToken for this given Token or False. An the AccessToken for this given Token or False.
""" """
logger.debug("check token {{ {} }} is valid".format(token)) logger.debug("check token {{ {} }} is valid".format(token))
access_token = AccessToken.query.filter_by(token=token).one_or_none() access_token = AccessToken.query.filter_by(token=token).one_or_none()
if access_token: if access_token:
logger.debug("token found, check if expired or invalid user agent differs") logger.debug("token found, check if expired or invalid user agent differs")
if access_token.expires >= datetime.utcnow() and ( if access_token.expires >= datetime.utcnow() and (
access_token.browser == user_agent.browser and access_token.browser == user_agent.browser and access_token.platform == user_agent.platform
access_token.platform == user_agent.platform): ):
if not permissions or access_token.user.has_permissions(permissions): if not permissions or access_token.user.has_permissions(permissions):
access_token.refresh() access_token.refresh()
db.session.commit() db.session.commit()
@ -50,7 +50,7 @@ class AccessTokenController(metaclass=Singleton):
return False return False
def create(self, user, user_agent=None) -> AccessToken: def create(self, user, user_agent=None) -> AccessToken:
""" Create an AccessToken """Create an AccessToken
Args: Args:
user: For which User is to create an AccessToken user: For which User is to create an AccessToken
@ -61,8 +61,9 @@ class AccessTokenController(metaclass=Singleton):
""" """
logger.debug("create access token") logger.debug("create access token")
token_str = secrets.token_hex(16) token_str = secrets.token_hex(16)
token = AccessToken(token=token_str, user=user, lifetime=self.lifetime, token = AccessToken(
browser=user_agent.browser, platform=user_agent.platform) token=token_str, user=user, lifetime=self.lifetime, browser=user_agent.browser, platform=user_agent.platform
)
token.refresh() token.refresh()
db.session.add(token) db.session.add(token)
db.session.commit() db.session.commit()

View File

@ -33,10 +33,7 @@ def delete_event(id):
def create_event(begin, kind, end=None, description=None): def create_event(begin, kind, end=None, description=None):
try: try:
event = Event(begin=begin, event = Event(begin=begin, end=end, description=description, kind=kind)
end=end,
description=description,
kind=kind)
db.session.add(event) db.session.add(event)
db.session.commit() db.session.commit()
return event return event

View File

@ -11,14 +11,14 @@ def login_user(username, password):
if user is None: if user is None:
user = User(uid=username) user = User(uid=username)
db.session.add(user) db.session.add(user)
if current_app.config['FG_AUTH_BACKEND'].login(user, password): if current_app.config["FG_AUTH_BACKEND"].login(user, password):
update_user(user) update_user(user)
return user return user
return None return None
def update_user(user): def update_user(user):
current_app.config['FG_AUTH_BACKEND'].update_user(user) current_app.config["FG_AUTH_BACKEND"].update_user(user)
db.session.commit() db.session.commit()
@ -37,7 +37,7 @@ def modify_user(user, password, new_password=None):
NotImplemented: If backend is not capable of this operation NotImplemented: If backend is not capable of this operation
BadRequest: Password is wrong or other logic issues BadRequest: Password is wrong or other logic issues
""" """
current_app.config['FG_AUTH_BACKEND'].modify_user(user, password, new_password) current_app.config["FG_AUTH_BACKEND"].modify_user(user, password, new_password)
def get_users(): def get_users():

View File

@ -7,6 +7,7 @@ from flaschengeist import logger
def login_required(**kwargs): def login_required(**kwargs):
from .controller.accessTokenController import AccessTokenController from .controller.accessTokenController import AccessTokenController
ac_controller = AccessTokenController() ac_controller = AccessTokenController()
permissions = None permissions = None
if "permissions" in kwargs: if "permissions" in kwargs:
@ -15,14 +16,16 @@ def login_required(**kwargs):
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")
access_token = ac_controller.validate_token(token, request.user_agent, permissions) access_token = ac_controller.validate_token(token, request.user_agent, permissions)
if access_token: if access_token:
kwargs['access_token'] = access_token kwargs["access_token"] = access_token
logger.debug("token {{ {} }} is valid".format(token)) logger.debug("token {{ {} }} is valid".format(token))
return func(*args, **kwargs) return func(*args, **kwargs)
else: else:
logger.info("token {{ {} }} is not valid".format(token)) logger.info("token {{ {} }} is not valid".format(token))
raise Unauthorized raise Unauthorized
return wrapper return wrapper
return real_decorator return real_decorator

View File

@ -5,16 +5,17 @@ from flaschengeist import logger
class AccessToken(db.Model): class AccessToken(db.Model):
""" Model for an AccessToken """Model for an AccessToken
Args: Args:
expires: Is a Datetime from current Time. expires: Is a Datetime from current Time.
user: Is an User. user: Is an User.
token: String to verify access later. token: String to verify access later.
""" """
__tablename__ = 'session'
__tablename__ = "session"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id')) user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
user = db.relationship("User", back_populates="sessions") user = db.relationship("User", back_populates="sessions")
expires = db.Column(db.DateTime) expires = db.Column(db.DateTime)
@ -24,25 +25,25 @@ class AccessToken(db.Model):
platform = db.Column(db.String(30)) platform = db.Column(db.String(30))
def refresh(self): def refresh(self):
""" Update the Timestamp """Update the Timestamp
Update the Timestamp to the current Time. Update the Timestamp to the current Time.
""" """
logger.debug("update timestamp from access token {{ {} }}".format(self)) logger.debug("update timestamp from access token {{ {} }}".format(self))
self.expires = datetime.utcnow() + timedelta(seconds=self.lifetime) self.expires = datetime.utcnow() + timedelta(seconds=self.lifetime)
def serialize(self): def serialize(self):
""" Create Dic to dump in JSON """Create Dic to dump in JSON
Returns: Returns:
A Dic with static Attributes. A Dic with static Attributes.
""" """
return { return {
"token": self.token, "token": self.token,
"expires": self.expires.replace(tzinfo=timezone.utc), "expires": self.expires.replace(tzinfo=timezone.utc),
"lifetime": self.lifetime, "lifetime": self.lifetime,
"browser": self.browser, "browser": self.browser,
"platform": self.platform "platform": self.platform,
} }
def __eq__(self, token): def __eq__(self, token):
@ -50,4 +51,5 @@ class AccessToken(db.Model):
def __str__(self): def __str__(self):
return "AccessToken(user={}, token={}, expires={}, lifetime={})".format( return "AccessToken(user={}, token={}, expires={}, lifetime={})".format(
self.user, self.token, self.expires, self.lifetime) self.user, self.token, self.expires, self.lifetime
)

View File

@ -3,46 +3,40 @@ from ..database import db
class Event(db.Model): class Event(db.Model):
"""Model for an Event""" """Model for an Event"""
__tablename__ = 'event'
__tablename__ = "event"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
begin = db.Column(db.DateTime, nullable=False) begin = db.Column(db.DateTime, nullable=False)
end = db.Column(db.DateTime) end = db.Column(db.DateTime)
description = db.Column(db.String(240)) description = db.Column(db.String(240))
kind_id = db.Column(db.Integer, db.ForeignKey('event_kind.id', ondelete="CASCADE"), nullable=False) kind_id = db.Column(db.Integer, db.ForeignKey("event_kind.id", ondelete="CASCADE"), nullable=False)
kind = db.relationship("EventKind") kind = db.relationship("EventKind")
slots = db.relationship("EventSlot", back_populates="event", cascade="all, delete") slots = db.relationship("EventSlot", back_populates="event", cascade="all, delete")
#notices = db.relationship("EventNotice", back_populates="event") # notices = db.relationship("EventNotice", back_populates="event")
def serialize(self): def serialize(self):
return { return {"id": self.id, "begin": self.begin, "end": self.end, "description": self.description, "kind": self.kind}
"id": self.id,
"begin": self.begin,
"end": self.end,
"description": self.description,
"kind": self.kind
}
class EventKind(db.Model): class EventKind(db.Model):
"""Model for an EventKind""" """Model for an EventKind"""
__tablename__ = "event_kind" __tablename__ = "event_kind"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30), nullable=False, unique=True) name = db.Column(db.String(30), nullable=False, unique=True)
def serialize(self): def serialize(self):
return { return {"id": self.id, "name": self.name}
"id": self.id,
"name": self.name
}
class EventSlot(db.Model): class EventSlot(db.Model):
"""Model for an EventSlot""" """Model for an EventSlot"""
__tablename__ = "event_slot" __tablename__ = "event_slot"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
start = db.Column(db.DateTime) start = db.Column(db.DateTime)
end = db.Column(db.DateTime) end = db.Column(db.DateTime)
event_id = db.Column(db.Integer, db.ForeignKey('event.id'), nullable=False) event_id = db.Column(db.Integer, db.ForeignKey("event.id"), nullable=False)
event = db.relationship("Event", back_populates="slots") event = db.relationship("Event", back_populates="slots")
slots = db.relationship("JobSlot", back_populates="event_slot") slots = db.relationship("JobSlot", back_populates="event_slot")
@ -59,9 +53,9 @@ class JobSlot(db.Model):
__tablename__ = "job_slot" __tablename__ = "job_slot"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
needed_persons = db.Column(db.Numeric(precision=4, scale=2)) needed_persons = db.Column(db.Numeric(precision=4, scale=2))
event_slot_id = db.Column(db.Integer, db.ForeignKey('event_slot.id')) event_slot_id = db.Column(db.Integer, db.ForeignKey("event_slot.id"))
event_slot = db.relationship("EventSlot", back_populates="slots") event_slot = db.relationship("EventSlot", back_populates="slots")
kind_id = db.Column(db.Integer, db.ForeignKey('job_kind.id')) kind_id = db.Column(db.Integer, db.ForeignKey("job_kind.id"))
kind = db.relationship("JobKind") kind = db.relationship("JobKind")
jobs = db.relationship("Job", back_populates="slot") jobs = db.relationship("Job", back_populates="slot")
@ -70,9 +64,9 @@ class Job(db.Model):
__tablename__ = "job" __tablename__ = "job"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.Numeric(precision=3, scale=2)) value = db.Column(db.Numeric(precision=3, scale=2))
user_id = db.Column(db.Integer, db.ForeignKey('user.id')) user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
user = db.relationship("User") user = db.relationship("User")
slot_id = db.Column(db.Integer, db.ForeignKey('job_slot.id')) slot_id = db.Column(db.Integer, db.ForeignKey("job_slot.id"))
slot = db.relationship("JobSlot") slot = db.relationship("JobSlot")

View File

@ -5,26 +5,28 @@ from werkzeug.local import LocalProxy
logger = LocalProxy(lambda: current_app.logger) logger = LocalProxy(lambda: current_app.logger)
association_table = db.Table('user_x_role', association_table = db.Table(
db.Column('user_id', db.Integer, db.ForeignKey('user.id')), "user_x_role",
db.Column('role_id', db.Integer, db.ForeignKey('role.id')) db.Column("user_id", db.Integer, db.ForeignKey("user.id")),
) db.Column("role_id", db.Integer, db.ForeignKey("role.id")),
)
class User(db.Model): class User(db.Model):
""" Database Object for User """Database Object for User
Table for all saved User Table for all saved User
Attributes: Attributes:
id: Id in Database as Primary Key. id: Id in Database as Primary Key.
uid: User ID used by authentication provider uid: User ID used by authentication provider
display_name: Name to show display_name: Name to show
firstname: Firstname of the User firstname: Firstname of the User
lastname: Lastname of the User lastname: Lastname of the User
mail: mail address of the User mail: mail address of the User
""" """
__tablename__ = 'user'
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
uid = db.Column(db.String(30)) uid = db.Column(db.String(30))
display_name = db.Column(db.String(30)) display_name = db.Column(db.String(30))
@ -33,8 +35,9 @@ class User(db.Model):
mail = db.Column(db.String(30)) mail = db.Column(db.String(30))
roles = db.relationship("Role", secondary=association_table) roles = db.relationship("Role", secondary=association_table)
sessions = db.relationship("AccessToken", back_populates="user") sessions = db.relationship("AccessToken", back_populates="user")
attributes = db.relationship("UserAttribute", collection_class=attribute_mapped_collection('name'), attributes = db.relationship(
cascade="all, delete") "UserAttribute", collection_class=attribute_mapped_collection("name"), cascade="all, delete"
)
def set_attribute(self, name, value): def set_attribute(self, name, value):
if name in self.attributes: if name in self.attributes:
@ -50,16 +53,16 @@ class User(db.Model):
def update_data(self, data): def update_data(self, data):
logger.debug("update data of user") logger.debug("update data of user")
if 'uid' in data: if "uid" in data:
self.uid = data['uid'] self.uid = data["uid"]
if 'firstname' in data: if "firstname" in data:
self.firstname = data['firstname'] self.firstname = data["firstname"]
if 'lastname' in data: if "lastname" in data:
self.lastname = data['lastname'] self.lastname = data["lastname"]
if 'mail' in data: if "mail" in data:
self.mail = data['mail'] self.mail = data["mail"]
if 'display_name' in data: if "display_name" in data:
self.display_name = data['display_name'] self.display_name = data["display_name"]
def get_permissions(self): def get_permissions(self):
return ["user"] + [permission.name for role in self.roles for permission in role.permissions] return ["user"] + [permission.name for role in self.roles for permission in role.permissions]
@ -78,26 +81,27 @@ class User(db.Model):
"firstname": self.firstname, "firstname": self.firstname,
"lastname": self.lastname, "lastname": self.lastname,
"mail": self.mail, "mail": self.mail,
"roles": [r.name for r in self.roles] "roles": [r.name for r in self.roles],
} }
class UserAttribute(db.Model): class UserAttribute(db.Model):
__tablename__ = 'user_attribute' __tablename__ = "user_attribute"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
user = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) user = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
name = db.Column(db.String(30)) name = db.Column(db.String(30))
value = db.Column(db.String(192)) value = db.Column(db.String(192))
role_permission_association_table = db.Table('role_x_permission', role_permission_association_table = db.Table(
db.Column('role_id', db.Integer, db.ForeignKey('role.id')), "role_x_permission",
db.Column('permission_id', db.Integer, db.ForeignKey('permission.id')) db.Column("role_id", db.Integer, db.ForeignKey("role.id")),
) db.Column("permission_id", db.Integer, db.ForeignKey("permission.id")),
)
class Role(db.Model): class Role(db.Model):
__tablename__ = 'role' __tablename__ = "role"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30), unique=True) name = db.Column(db.String(30), unique=True)
permissions = db.relationship("Permission", secondary=role_permission_association_table, cascade="all, delete") permissions = db.relationship("Permission", secondary=role_permission_association_table, cascade="all, delete")
@ -107,7 +111,7 @@ class Role(db.Model):
class Permission(db.Model): class Permission(db.Model):
__tablename__ = 'permission' __tablename__ = "permission"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30), unique=True) name = db.Column(db.String(30), unique=True)

View File

@ -19,7 +19,7 @@ setup(
"werkzeug", "werkzeug",
"bjoern", "bjoern",
"python-dateutil", "python-dateutil",
"pyhooks" "pyhooks",
], ],
extras_require={"ldap": ["flask_ldapconn", "ldap3"]}, extras_require={"ldap": ["flask_ldapconn", "ldap3"]},
entry_points={ entry_points={
@ -29,7 +29,6 @@ setup(
"roles = flaschengeist.modules.roles:RolesPlugin", "roles = flaschengeist.modules.roles:RolesPlugin",
"schedule = flaschengeist.modules.schedule:SchedulePlugin", "schedule = flaschengeist.modules.schedule:SchedulePlugin",
"mail = flaschengeist.modules.message_mail:MailMessagePlugin", "mail = flaschengeist.modules.message_mail:MailMessagePlugin",
"auth_plain = flaschengeist.modules.auth_plain:AuthPlain", "auth_plain = flaschengeist.modules.auth_plain:AuthPlain",
"auth_ldap = flaschengeist.modules.auth_ldap:AuthLDAP [ldap]", "auth_ldap = flaschengeist.modules.auth_ldap:AuthLDAP [ldap]",
], ],