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

@ -14,7 +14,7 @@ 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):
@ -31,7 +31,7 @@ class AuthRoutePlugin(Plugin):
# 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
@ -43,8 +43,8 @@ class AuthRoutePlugin(Plugin):
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 [
("firstname", "givenName"),
("lastname", "sn"), ("lastname", "sn"),
("mail", "mail"), ("mail", "mail"),
("display_name", "displayName")]: ("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

@ -23,7 +23,7 @@ class RolesPlugin(Plugin):
# 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

@ -37,7 +37,7 @@ class SchedulePlugin(Plugin):
# 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),
@ -79,8 +79,7 @@ class SchedulePlugin(Plugin):
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(
begin=parser.isoparse(data["begin"]),
end=parser.isoparse(data["end"]), end=parser.isoparse(data["end"]),
description=data["description"], description=data["description"],
kind=EventKind.query.get(data["kind"])) 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,7 +7,7 @@ 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):
@ -24,19 +24,19 @@ class UsersPlugin(Plugin):
# 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,16 +4,12 @@ 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:
@ -24,31 +20,21 @@ for loc in paths:
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

@ -37,8 +37,8 @@ class AccessTokenController(metaclass=Singleton):
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()
@ -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

@ -12,9 +12,10 @@ class AccessToken(db.Model):
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)
@ -42,7 +43,7 @@ class AccessToken(db.Model):
"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,9 +5,10 @@ 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")),
) )
@ -24,7 +25,8 @@ class User(db.Model):
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]",
], ],