Only use one plugin system, load auth and "normal" plugins at once.
* Added Plugin class, where to inheritate from
This commit is contained in:
parent
60c2637784
commit
2c55edf6a8
|
@ -6,6 +6,7 @@ from flask.json import JSONEncoder, jsonify
|
|||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
from . import logger
|
||||
from .modules import AuthPlugin
|
||||
from .system.config import config, configure_app
|
||||
from .system.controller import roleController
|
||||
|
||||
|
@ -30,19 +31,6 @@ class CustomJSONEncoder(JSONEncoder):
|
|||
return JSONEncoder.default(self, o)
|
||||
|
||||
|
||||
def __load_auth(app):
|
||||
for entry_point in pkg_resources.iter_entry_points('flaschengeist.auth'):
|
||||
logger.debug('Found authentication plugin: %s', entry_point.name)
|
||||
if entry_point.name == config['FLASCHENGEIST']['AUTH']:
|
||||
app.config['FG_AUTH_BACKEND'] = entry_point.load()()
|
||||
app.config['FG_AUTH_BACKEND'].configure(
|
||||
config[entry_point.name] if config.has_section(entry_point.name) else {})
|
||||
logger.info('Loaded authentication plugin > %s <', entry_point.name)
|
||||
break
|
||||
if not app.config['FG_AUTH_BACKEND']:
|
||||
logger.error('No authentication plugin configured or authentication plugin not found')
|
||||
|
||||
|
||||
def __load_plugins(app):
|
||||
logger.info('Search for plugins')
|
||||
app.config['FG_PLUGINS'] = {}
|
||||
|
@ -50,10 +38,20 @@ def __load_plugins(app):
|
|||
logger.debug("Found plugin: >{}<".format(entry_point.name))
|
||||
plugin = None
|
||||
if config.get(entry_point.name, 'enabled', fallback=False):
|
||||
plugin = entry_point.load()()
|
||||
app.register_blueprint(plugin.blueprint)
|
||||
logger.info("Loaded plugin >{}<".format(entry_point.name))
|
||||
app.config["FG_PLUGINS"][entry_point.name] = plugin
|
||||
plugin = entry_point.load()(config[entry_point.name] if config.has_section(entry_point.name) else {})
|
||||
if plugin.blueprint:
|
||||
app.register_blueprint(plugin.blueprint)
|
||||
logger.info("Load plugin >{}<".format(entry_point.name))
|
||||
if isinstance(plugin, AuthPlugin):
|
||||
logger.debug('Found authentication plugin: %s', entry_point.name)
|
||||
if entry_point.name == config['FLASCHENGEIST']['AUTH']:
|
||||
app.config['FG_AUTH_BACKEND'] = plugin
|
||||
else:
|
||||
del plugin
|
||||
else:
|
||||
app.config["FG_PLUGINS"][entry_point.name] = plugin
|
||||
if 'FG_AUTH_BACKEND' not in app.config:
|
||||
logger.error('No authentication plugin configured or authentication plugin not found')
|
||||
|
||||
|
||||
def install_all():
|
||||
|
@ -77,7 +75,6 @@ def create_app():
|
|||
from .system.database import db
|
||||
configure_app(app)
|
||||
db.init_app(app)
|
||||
__load_auth(app)
|
||||
__load_plugins(app)
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
|
|
|
@ -12,15 +12,21 @@ HOST =
|
|||
PASSWORD =
|
||||
DATABASE =
|
||||
|
||||
[MAIL]
|
||||
URL =
|
||||
PORT =
|
||||
USER =
|
||||
PASSWD =
|
||||
MAIL =
|
||||
CRYPT = SSL/STARTLS
|
||||
[auth_plain]
|
||||
enabled = true
|
||||
|
||||
#[mail]
|
||||
# enabled = true
|
||||
# SERVER =
|
||||
# PORT =
|
||||
# USER =
|
||||
# PASSWORD =
|
||||
# MAIL =
|
||||
# SSL or STARTLS
|
||||
# CRYPT = SSL
|
||||
|
||||
#[auth_ldap]
|
||||
# enabled = true
|
||||
# URL =
|
||||
# PORT =
|
||||
# BINDDN =
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
from pyhooks import precall_register
|
||||
|
||||
send_message_hook = precall_register("send_message")
|
||||
|
||||
|
||||
class Plugin:
|
||||
def __init__(self, blueprint, permissions = {}):
|
||||
def __init__(self, config=None, blueprint=None, permissions={}):
|
||||
self.blueprint = blueprint
|
||||
self.permissions = permissions
|
||||
|
||||
|
@ -10,10 +15,7 @@ class Plugin:
|
|||
pass
|
||||
|
||||
|
||||
class Auth:
|
||||
def configure(self, config):
|
||||
pass
|
||||
|
||||
class AuthPlugin(Plugin):
|
||||
def login(self, user, pw):
|
||||
""" Login routine, MUST BE IMPLEMENTED!
|
||||
|
||||
|
|
|
@ -14,12 +14,12 @@ from flaschengeist.system.decorator import login_required
|
|||
from flaschengeist.system.controller import accessTokenController, userController
|
||||
|
||||
access_controller = LocalProxy(lambda: accessTokenController.AccessTokenController())
|
||||
|
||||
auth_bp = Blueprint('auth', __name__)
|
||||
|
||||
|
||||
def register():
|
||||
return Plugin(auth_bp)
|
||||
class AuthRoutePlugin(Plugin):
|
||||
def __init__(self, conf):
|
||||
super().__init__(blueprint=auth_bp)
|
||||
|
||||
#################################################
|
||||
# Routes #
|
||||
|
@ -31,84 +31,83 @@ def register():
|
|||
# DELETE: logout / delete token #
|
||||
#################################################
|
||||
|
||||
@auth_bp.route("/auth", methods=['POST'])
|
||||
def _create_token():
|
||||
""" Login User
|
||||
|
||||
@auth_bp.route("/auth", methods=['POST'])
|
||||
def _create_token():
|
||||
""" Login User
|
||||
Login in User and create an AccessToken for the User.
|
||||
Requires POST data {'userid': string, 'password': string}
|
||||
Returns:
|
||||
A JSON-File with user information and created token or errors
|
||||
"""
|
||||
logger.debug("Start log in.")
|
||||
data = request.get_json()
|
||||
try:
|
||||
userid = data['userid']
|
||||
password = data['password']
|
||||
except KeyError:
|
||||
raise BadRequest("Missing parameter(s)")
|
||||
|
||||
Login in User and create an AccessToken for the User.
|
||||
Requires POST data {'userid': string, 'password': string}
|
||||
Returns:
|
||||
A JSON-File with user information and created token or errors
|
||||
"""
|
||||
logger.debug("Start log in.")
|
||||
data = request.get_json()
|
||||
try:
|
||||
userid = data['userid']
|
||||
password = data['password']
|
||||
except KeyError:
|
||||
raise BadRequest("Missing parameter(s)")
|
||||
logger.debug("search user {{ {} }} in database".format(userid))
|
||||
user = userController.login_user(userid, password)
|
||||
if not user:
|
||||
raise Unauthorized
|
||||
logger.debug("user is {{ {} }}".format(user))
|
||||
token = access_controller.create(user, user_agent=request.user_agent)
|
||||
logger.debug("access token is {{ {} }}".format(token))
|
||||
logger.info("User {{ {} }} success login.".format(userid))
|
||||
|
||||
logger.debug("search user {{ {} }} in database".format(userid))
|
||||
user = userController.login_user(userid, password)
|
||||
if not user:
|
||||
raise Unauthorized
|
||||
logger.debug("user is {{ {} }}".format(user))
|
||||
token = access_controller.create(user, user_agent=request.user_agent)
|
||||
logger.debug("access token is {{ {} }}".format(token))
|
||||
logger.info("User {{ {} }} success login.".format(userid))
|
||||
|
||||
# Lets cleanup the DB
|
||||
access_controller.clear_expired()
|
||||
return jsonify({"user": user, "token": token, "permissions": user.get_permissions()})
|
||||
# Lets cleanup the DB
|
||||
access_controller.clear_expired()
|
||||
return jsonify({"user": user, "token": token, "permissions": user.get_permissions()})
|
||||
|
||||
|
||||
@auth_bp.route("/auth", methods=['GET'])
|
||||
@login_required()
|
||||
def _get_tokens(access_token, **kwargs):
|
||||
tokens = access_controller.get_users_tokens(access_token.user)
|
||||
return jsonify(tokens)
|
||||
@auth_bp.route("/auth", methods=['GET'])
|
||||
@login_required()
|
||||
def _get_tokens(access_token, **kwargs):
|
||||
tokens = access_controller.get_users_tokens(access_token.user)
|
||||
return jsonify(tokens)
|
||||
|
||||
|
||||
@auth_bp.route("/auth/<token>", methods=['DELETE'])
|
||||
@login_required()
|
||||
def _delete_token(token, access_token, **kwargs):
|
||||
logger.debug("Try to delete access token {{ {} }}".format(token))
|
||||
token = access_controller.get_token(token, access_token.user)
|
||||
if not token:
|
||||
logger.debug("Token not found in database!")
|
||||
# Return 403 error, so that users can not bruteforce tokens
|
||||
# Valid tokens from other users and invalid tokens now are looking the same
|
||||
raise Forbidden
|
||||
access_controller.delete_token(token)
|
||||
access_controller.clear_expired()
|
||||
return jsonify({"ok": "ok"})
|
||||
|
||||
|
||||
@auth_bp.route("/auth/<token>", methods=['GET'])
|
||||
@login_required()
|
||||
def _get_token(token, access_token, **kwargs):
|
||||
logger.debug("get token {{ {} }}".format(token))
|
||||
token = access_controller.get_token(token, access_token.user)
|
||||
if not token:
|
||||
# Return 403 error, so that users can not bruteforce tokens
|
||||
# Valid tokens from other users and invalid tokens now are looking the same
|
||||
raise Forbidden
|
||||
return jsonify(token)
|
||||
|
||||
|
||||
@auth_bp.route("/auth/<token>", methods=['PUT'])
|
||||
@login_required()
|
||||
def _set_lifetime(token, access_token, **kwargs):
|
||||
token = access_controller.get_token(token, access_token.user)
|
||||
if not token:
|
||||
# Return 403 error, so that users can not bruteforce tokens
|
||||
# Valid tokens from other users and invalid tokens now are looking the same
|
||||
raise Forbidden
|
||||
try:
|
||||
lifetime = request.get_json()['value']
|
||||
logger.debug("set lifetime {{ {} }} to access token {{ {} }}".format(lifetime, token))
|
||||
access_controller.set_lifetime(token, lifetime)
|
||||
@auth_bp.route("/auth/<token>", methods=['DELETE'])
|
||||
@login_required()
|
||||
def _delete_token(token, access_token, **kwargs):
|
||||
logger.debug("Try to delete access token {{ {} }}".format(token))
|
||||
token = access_controller.get_token(token, access_token.user)
|
||||
if not token:
|
||||
logger.debug("Token not found in database!")
|
||||
# Return 403 error, so that users can not bruteforce tokens
|
||||
# Valid tokens from other users and invalid tokens now are looking the same
|
||||
raise Forbidden
|
||||
access_controller.delete_token(token)
|
||||
access_controller.clear_expired()
|
||||
return jsonify({"ok": "ok"})
|
||||
except (KeyError, TypeError):
|
||||
raise BadRequest
|
||||
|
||||
|
||||
@auth_bp.route("/auth/<token>", methods=['GET'])
|
||||
@login_required()
|
||||
def _get_token(token, access_token, **kwargs):
|
||||
logger.debug("get token {{ {} }}".format(token))
|
||||
token = access_controller.get_token(token, access_token.user)
|
||||
if not token:
|
||||
# Return 403 error, so that users can not bruteforce tokens
|
||||
# Valid tokens from other users and invalid tokens now are looking the same
|
||||
raise Forbidden
|
||||
return jsonify(token)
|
||||
|
||||
|
||||
@auth_bp.route("/auth/<token>", methods=['PUT'])
|
||||
@login_required()
|
||||
def _set_lifetime(token, access_token, **kwargs):
|
||||
token = access_controller.get_token(token, access_token.user)
|
||||
if not token:
|
||||
# Return 403 error, so that users can not bruteforce tokens
|
||||
# Valid tokens from other users and invalid tokens now are looking the same
|
||||
raise Forbidden
|
||||
try:
|
||||
lifetime = request.get_json()['value']
|
||||
logger.debug("set lifetime {{ {} }} to access token {{ {} }}".format(lifetime, token))
|
||||
access_controller.set_lifetime(token, lifetime)
|
||||
return jsonify({"ok": "ok"})
|
||||
except (KeyError, TypeError):
|
||||
raise BadRequest
|
||||
|
|
|
@ -1,29 +1,26 @@
|
|||
from ldap3.core.exceptions import LDAPPasswordIsMandatoryError, LDAPBindError
|
||||
import ssl
|
||||
from ldap3.utils.hashed import hashed
|
||||
from werkzeug.exceptions import BadRequest
|
||||
|
||||
import flaschengeist.modules as modules
|
||||
from ldap3 import SUBTREE, MODIFY_REPLACE, HASHED_SALTED_SHA512
|
||||
from ldap3.core.exceptions import LDAPPasswordIsMandatoryError, LDAPBindError
|
||||
from flask import current_app as app
|
||||
from flask_ldapconn import LDAPConn
|
||||
from ldap3 import SUBTREE, MODIFY_REPLACE, HASHED_SALTED_SHA512
|
||||
import ssl
|
||||
from werkzeug.exceptions import BadRequest
|
||||
|
||||
from flaschengeist.modules import AuthPlugin
|
||||
from flaschengeist.system.models.user import User
|
||||
from flaschengeist import logger
|
||||
|
||||
|
||||
class AuthLDAP(modules.Auth):
|
||||
_default = {
|
||||
'PORT': '389',
|
||||
'USE_SSL': 'False'
|
||||
}
|
||||
ldap = None
|
||||
dn = None
|
||||
class AuthLDAP(AuthPlugin):
|
||||
def __init__(self, config):
|
||||
super().__init__()
|
||||
|
||||
def configure(self, config):
|
||||
for name in self._default:
|
||||
defaults = {
|
||||
'PORT': '389',
|
||||
'USE_SSL': 'False'
|
||||
}
|
||||
for name in defaults:
|
||||
if name not in config:
|
||||
config[name] = self._default[name]
|
||||
config[name] = defaults[name]
|
||||
|
||||
app.config.update(
|
||||
LDAP_SERVER=config['URL'],
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import smtplib
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
|
||||
from flaschengeist.system.models.user import User
|
||||
from flaschengeist.system.controller import userController
|
||||
from flaschengeist.system.controller.messageController import Message
|
||||
|
||||
from . import Plugin, send_message_hook
|
||||
|
||||
|
||||
class MailMessagePlugin(Plugin):
|
||||
def __init__(self, config):
|
||||
super().__init__()
|
||||
self.server = config['SERVER']
|
||||
self.port = config['PORT']
|
||||
self.user = config['USER']
|
||||
self.password = config['PASSWORD']
|
||||
self.crypt = config['CRYPT']
|
||||
self.mail = config['MAIL']
|
||||
|
||||
@send_message_hook
|
||||
def send_mail(self, msg: Message):
|
||||
if isinstance(msg.receiver, User):
|
||||
recipients = [msg.receiver.mail]
|
||||
else:
|
||||
recipients = userController.get_user_by_role(msg.receiver)
|
||||
|
||||
mail = MIMEMultipart()
|
||||
mail['From'] = self.mail
|
||||
mail['To'] = ", ".join(recipients)
|
||||
mail['Subject'] = msg.subject
|
||||
msg.attach(msg.message)
|
||||
if not self.smtp:
|
||||
self.__connect()
|
||||
self.smtp.sendmail(self.mail, recipients, msg.as_string())
|
||||
|
||||
def __connect(self):
|
||||
if self.crypt == 'SSL':
|
||||
self.smtp = smtplib.SMTP_SSL(self.server, self.port)
|
||||
if self.crypt == 'STARTTLS':
|
||||
self.smtp = smtplib.SMTP(self.smtpServer, self.port)
|
||||
self.smtp.starttls()
|
||||
self.smtp.login(self.user, self.password)
|
|
@ -1,16 +1,16 @@
|
|||
from flask import Blueprint, request, jsonify
|
||||
from werkzeug.exceptions import NotFound, BadRequest, Forbidden
|
||||
from werkzeug.exceptions import NotFound, BadRequest
|
||||
|
||||
from flaschengeist.modules import Plugin
|
||||
from flaschengeist.system.decorator import login_required
|
||||
from flaschengeist.system.controller import roleController
|
||||
|
||||
roles_bp = Blueprint("roles", __name__)
|
||||
permissions = {}
|
||||
|
||||
|
||||
def register():
|
||||
return Plugin(roles_bp, permissions)
|
||||
class RolesPlugin(Plugin):
|
||||
def __init__(self, config):
|
||||
super().__init__(config, roles_bp)
|
||||
|
||||
######################################################
|
||||
# Routes #
|
||||
|
@ -23,66 +23,60 @@ def register():
|
|||
# DELETE: remove role #
|
||||
######################################################
|
||||
|
||||
@roles_bp.route("/roles", methods=['POST'])
|
||||
@login_required()
|
||||
def add_role(self):
|
||||
data = request.get_json()
|
||||
if not data or "name" not in data:
|
||||
raise BadRequest
|
||||
if "permissions" in data:
|
||||
permissions = data["permissions"]
|
||||
role = roleController.create_role(data["name"], permissions)
|
||||
return jsonify({"ok": "ok", "id": role.id})
|
||||
|
||||
@roles_bp.route("/roles", methods=['POST'])
|
||||
@login_required()
|
||||
def __add_role():
|
||||
data = request.get_json()
|
||||
if not data or "name" not in data:
|
||||
raise BadRequest
|
||||
if "permissions" in data:
|
||||
permissions = data["permissions"]
|
||||
role = roleController.create_role(data["name"], permissions)
|
||||
return jsonify({"ok": "ok", "id": role.id})
|
||||
@roles_bp.route("/roles", methods=['GET'])
|
||||
@login_required()
|
||||
def list_roles(self, **kwargs):
|
||||
roles = roleController.get_roles()
|
||||
return jsonify(roles)
|
||||
|
||||
@roles_bp.route("/roles/permissions", methods=['GET'])
|
||||
@login_required()
|
||||
def list_permissions(self, **kwargs):
|
||||
permissions = roleController.get_permissions()
|
||||
return jsonify(permissions)
|
||||
|
||||
@roles_bp.route("/roles", methods=['GET'])
|
||||
@login_required()
|
||||
def __list_roles(**kwargs):
|
||||
roles = roleController.get_roles()
|
||||
return jsonify(roles)
|
||||
|
||||
|
||||
@roles_bp.route("/roles/permissions", methods=['GET'])
|
||||
@login_required()
|
||||
def __list_permissions(**kwargs):
|
||||
permissions = roleController.get_permissions()
|
||||
return jsonify(permissions)
|
||||
|
||||
|
||||
@roles_bp.route("/roles/<rid>", methods=['GET'])
|
||||
@login_required()
|
||||
def __get_role(rid, **kwargs):
|
||||
role = roleController.get_role(rid)
|
||||
if role:
|
||||
return jsonify({
|
||||
"id": role.id,
|
||||
"name": role,
|
||||
"permissions": role.permissions
|
||||
})
|
||||
raise NotFound
|
||||
|
||||
|
||||
@roles_bp.route("/roles/<rid>", methods=['PUT'])
|
||||
@login_required()
|
||||
def __edit_role(rid, **kwargs):
|
||||
role = roleController.get_role(rid)
|
||||
if not role:
|
||||
@roles_bp.route("/roles/<rid>", methods=['GET'])
|
||||
@login_required()
|
||||
def __get_role(self, rid, **kwargs):
|
||||
role = roleController.get_role(rid)
|
||||
if role:
|
||||
return jsonify({
|
||||
"id": role.id,
|
||||
"name": role,
|
||||
"permissions": role.permissions
|
||||
})
|
||||
raise NotFound
|
||||
|
||||
data = request.get_json()
|
||||
if 'name' in data:
|
||||
role.name = data["name"]
|
||||
if "permissions" in data:
|
||||
roleController.set_permissions(role, data["permissions"])
|
||||
roleController.update_role(role)
|
||||
return jsonify({"ok": "ok"})
|
||||
@roles_bp.route("/roles/<rid>", methods=['PUT'])
|
||||
@login_required()
|
||||
def __edit_role(self, rid, **kwargs):
|
||||
role = roleController.get_role(rid)
|
||||
if not role:
|
||||
raise NotFound
|
||||
|
||||
data = request.get_json()
|
||||
if 'name' in data:
|
||||
role.name = data["name"]
|
||||
if "permissions" in data:
|
||||
roleController.set_permissions(role, data["permissions"])
|
||||
roleController.update_role(role)
|
||||
return jsonify({"ok": "ok"})
|
||||
|
||||
@roles_bp.route("/roles/<rid>", methods=['DELETE'])
|
||||
@login_required()
|
||||
def __delete_role(rid, **kwargs):
|
||||
if not roleController.delete_role(rid):
|
||||
raise NotFound
|
||||
@roles_bp.route("/roles/<rid>", methods=['DELETE'])
|
||||
@login_required()
|
||||
def __delete_role(self, rid, **kwargs):
|
||||
if not roleController.delete_role(rid):
|
||||
raise NotFound
|
||||
|
||||
return jsonify({"ok": "ok"})
|
||||
return jsonify({"ok": "ok"})
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
from dateutil import parser
|
||||
from datetime import datetime, timedelta
|
||||
from flask import Blueprint, request, jsonify
|
||||
from werkzeug.exceptions import BadRequest, NotFound
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from flaschengeist.modules import Plugin
|
||||
from flaschengeist.system.controller import eventController
|
||||
from flaschengeist.system.database import db
|
||||
from flaschengeist.system.decorator import login_required
|
||||
from flaschengeist.system.models.event import EventKind
|
||||
from flaschengeist.system.decorator import login_required
|
||||
from flaschengeist.system.controller import eventController
|
||||
|
||||
schedule_bp = Blueprint("schedule", __name__, url_prefix="/schedule")
|
||||
permissions = {}
|
||||
|
||||
|
||||
def register():
|
||||
return Plugin(schedule_bp, permissions)
|
||||
|
||||
class SchedulePlugin(Plugin):
|
||||
def __init__(self, config):
|
||||
super().__init__(blueprint=schedule_bp)
|
||||
|
||||
####################################################################################
|
||||
# Routes #
|
||||
|
@ -38,161 +37,159 @@ def register():
|
|||
# DELETE: remove user #
|
||||
####################################################################################
|
||||
|
||||
@schedule_bp.route("/events/<int:id>", methods=['GET'])
|
||||
@login_required() # roles=['schedule_read'])
|
||||
def __get_event(self, id, **kwargs):
|
||||
event = eventController.get_event(id)
|
||||
if not event:
|
||||
raise NotFound
|
||||
return jsonify(event)
|
||||
|
||||
@schedule_bp.route("/events/<int:id>", methods=['GET'])
|
||||
@login_required() # roles=['schedule_read'])
|
||||
def __get_event(id, **kwargs):
|
||||
event = eventController.get_event(id)
|
||||
if not event:
|
||||
raise NotFound
|
||||
return jsonify(event)
|
||||
@schedule_bp.route("/events", methods=['GET'])
|
||||
@schedule_bp.route("/events/<int:year>/<int:month>", methods=['GET'])
|
||||
@schedule_bp.route("/events/<int:year>/<int:month>/<int:day>", methods=['GET'])
|
||||
@login_required() # roles=['schedule_read'])
|
||||
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),
|
||||
if nothing set then events for current month are returned
|
||||
|
||||
|
||||
@schedule_bp.route("/events", methods=['GET'])
|
||||
@schedule_bp.route("/events/<int:year>/<int:month>", methods=['GET'])
|
||||
@schedule_bp.route("/events/<int:year>/<int:month>/<int:day>", methods=['GET'])
|
||||
@login_required() # roles=['schedule_read'])
|
||||
def __get_events(year=datetime.now().year, month=datetime.now().month, day=None, **kwrags):
|
||||
"""Get Event objects for specified date (or month or year),
|
||||
if nothing set then events for current month are returned
|
||||
|
||||
Args:
|
||||
year (int, optional): year to query, defaults to current year
|
||||
month (int, optional): month to query (if set), defaults to current month
|
||||
day (int, optional): day to query events for (if set)
|
||||
**kwrags: contains at least access_token (see flaschengeist.decorator)
|
||||
Returns:
|
||||
JSON list containing events found
|
||||
Raises:
|
||||
BadRequest: If date is invalid
|
||||
"""
|
||||
try:
|
||||
begin = datetime(year=year, month=month, day=1)
|
||||
if day:
|
||||
begin += timedelta(days=day - 1)
|
||||
end = begin + timedelta(days=1)
|
||||
else:
|
||||
if month == 12:
|
||||
end = datetime(year=year + 1, month=1, day=1)
|
||||
Args:
|
||||
year (int, optional): year to query, defaults to current year
|
||||
month (int, optional): month to query (if set), defaults to current month
|
||||
day (int, optional): day to query events for (if set)
|
||||
**kwrags: contains at least access_token (see flaschengeist.decorator)
|
||||
Returns:
|
||||
JSON list containing events found
|
||||
Raises:
|
||||
BadRequest: If date is invalid
|
||||
"""
|
||||
try:
|
||||
begin = datetime(year=year, month=month, day=1)
|
||||
if day:
|
||||
begin += timedelta(days=day - 1)
|
||||
end = begin + timedelta(days=1)
|
||||
else:
|
||||
end = datetime(year=year, month=month+1, day=1)
|
||||
if month == 12:
|
||||
end = datetime(year=year + 1, month=1, day=1)
|
||||
else:
|
||||
end = datetime(year=year, month=month+1, day=1)
|
||||
|
||||
events = eventController.get_events(begin, end)
|
||||
return jsonify(events)
|
||||
except ValueError:
|
||||
raise BadRequest("Invalid date given")
|
||||
events = eventController.get_events(begin, end)
|
||||
return jsonify(events)
|
||||
except ValueError:
|
||||
raise BadRequest("Invalid date given")
|
||||
|
||||
|
||||
@schedule_bp.route("/eventKinds", methods=['POST'])
|
||||
@login_required()
|
||||
def __new_event_kind(**kwargs):
|
||||
data = request.get_json()
|
||||
if "name" not in data:
|
||||
raise BadRequest
|
||||
kind = eventController.create_event_kind(data["name"])
|
||||
return jsonify({"ok": "ok", "id": kind.id})
|
||||
@schedule_bp.route("/eventKinds", methods=['POST'])
|
||||
@login_required()
|
||||
def __new_event_kind(self, **kwargs):
|
||||
data = request.get_json()
|
||||
if "name" not in data:
|
||||
raise BadRequest
|
||||
kind = eventController.create_event_kind(data["name"])
|
||||
return jsonify({"ok": "ok", "id": kind.id})
|
||||
|
||||
|
||||
@schedule_bp.route("/slotKinds", methods=["POST"])
|
||||
@login_required()
|
||||
def __new_slot_kind(**kwargs):
|
||||
data = request.get_json()
|
||||
if not data or "name" not in data:
|
||||
raise BadRequest
|
||||
kind = eventController.create_job_kind(data["name"])
|
||||
return jsonify({"ok": "ok", "id": kind.id})
|
||||
@schedule_bp.route("/slotKinds", methods=["POST"])
|
||||
@login_required()
|
||||
def __new_slot_kind(self, **kwargs):
|
||||
data = request.get_json()
|
||||
if not data or "name" not in data:
|
||||
raise BadRequest
|
||||
kind = eventController.create_job_kind(data["name"])
|
||||
return jsonify({"ok": "ok", "id": kind.id})
|
||||
|
||||
|
||||
@schedule_bp.route("/events", methods=['POST'])
|
||||
@login_required()
|
||||
def __new_event(**kwargs):
|
||||
data = request.get_json()
|
||||
event = eventController.create_event(begin=parser.isoparse(data["begin"]),
|
||||
end=parser.isoparse(data["end"]),
|
||||
description=data["description"],
|
||||
kind=EventKind.query.get(data["kind"]))
|
||||
return jsonify({"ok": "ok", "id": event.id})
|
||||
@schedule_bp.route("/events", methods=['POST'])
|
||||
@login_required()
|
||||
def __new_event(self, **kwargs):
|
||||
data = request.get_json()
|
||||
event = eventController.create_event(begin=parser.isoparse(data["begin"]),
|
||||
end=parser.isoparse(data["end"]),
|
||||
description=data["description"],
|
||||
kind=EventKind.query.get(data["kind"]))
|
||||
return jsonify({"ok": "ok", "id": event.id})
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:id>", methods=["DELETE"])
|
||||
@login_required()
|
||||
def __delete_event(id, **kwargs):
|
||||
if not eventController.delete_event(id):
|
||||
raise NotFound
|
||||
db.session.commit()
|
||||
return jsonify({'ok': 'ok'})
|
||||
@schedule_bp.route("/events/<int:id>", methods=["DELETE"])
|
||||
@login_required()
|
||||
def __delete_event(self, id, **kwargs):
|
||||
if not eventController.delete_event(id):
|
||||
raise NotFound
|
||||
db.session.commit()
|
||||
return jsonify({'ok': 'ok'})
|
||||
|
||||
|
||||
@schedule_bp.route("/eventKinds/<int:id>", methods=["PUT"])
|
||||
@login_required()
|
||||
def __edit_event_kind(id, **kwargs):
|
||||
data = request.get_json()
|
||||
if not data or "name" not in data:
|
||||
raise BadRequest
|
||||
eventController.rename_event_kind(id, data["name"])
|
||||
return jsonify({"ok": "ok"})
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:event_id>/slots", methods=["GET"])
|
||||
@login_required()
|
||||
def __get_slots(event_id, **kwargs):
|
||||
event = eventController.get_event(event_id)
|
||||
if not event:
|
||||
raise NotFound
|
||||
return jsonify({event.slots})
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["GET"])
|
||||
@login_required()
|
||||
def __get_slot(event_id, slot_id, **kwargs):
|
||||
slot = eventController.get_event_slot(slot_id, event_id)
|
||||
if slot:
|
||||
return jsonify(slot)
|
||||
raise NotFound
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["DELETE"])
|
||||
@login_required()
|
||||
def __delete_slot(event_id, slot_id, **kwargs):
|
||||
if eventController.delete_event_slot(slot_id, event_id):
|
||||
@schedule_bp.route("/eventKinds/<int:id>", methods=["PUT"])
|
||||
@login_required()
|
||||
def __edit_event_kind(self, id, **kwargs):
|
||||
data = request.get_json()
|
||||
if not data or "name" not in data:
|
||||
raise BadRequest
|
||||
eventController.rename_event_kind(id, data["name"])
|
||||
return jsonify({"ok": "ok"})
|
||||
raise NotFound
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["PUT"])
|
||||
@login_required()
|
||||
def __update_slot(event_id, slot_id, **kwargs):
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
raise BadRequest
|
||||
|
||||
for job in data['jobs']:
|
||||
eventController.add_job(job.kind, job.user)
|
||||
if eventController.delete_event_slot(slot_id, event_id):
|
||||
return jsonify({"ok": "ok"})
|
||||
raise NotFound
|
||||
@schedule_bp.route("/events/<int:event_id>/slots", methods=["GET"])
|
||||
@login_required()
|
||||
def __get_slots(self, event_id, **kwargs):
|
||||
event = eventController.get_event(event_id)
|
||||
if not event:
|
||||
raise NotFound
|
||||
return jsonify({event.slots})
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:event_id>/slots", methods=["POST"])
|
||||
@login_required()
|
||||
def __add_slot(event_id, **kwargs):
|
||||
event = eventController.get_event(event_id)
|
||||
if not event:
|
||||
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["GET"])
|
||||
@login_required()
|
||||
def __get_slot(self, event_id, slot_id, **kwargs):
|
||||
slot = eventController.get_event_slot(slot_id, event_id)
|
||||
if slot:
|
||||
return jsonify(slot)
|
||||
raise NotFound
|
||||
data = request.get_json()
|
||||
attr = {"job_slots": []}
|
||||
try:
|
||||
if "start" in data:
|
||||
attr["start"] = parser.isoparse(data["start"])
|
||||
if "end" in data:
|
||||
attr["end"] = parser.isoparse(data["end"])
|
||||
for job in data["jobs"]:
|
||||
attr["job_slots"].append({"needed_persons": job["needed_persons"], "kind": job["kind"]})
|
||||
except KeyError:
|
||||
raise BadRequest("Missing data in request")
|
||||
eventController.add_slot(event, **attr)
|
||||
return jsonify({"ok": "ok"})
|
||||
|
||||
|
||||
def __edit_event():
|
||||
...
|
||||
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["DELETE"])
|
||||
@login_required()
|
||||
def __delete_slot(self, event_id, slot_id, **kwargs):
|
||||
if eventController.delete_event_slot(slot_id, event_id):
|
||||
return jsonify({"ok": "ok"})
|
||||
raise NotFound
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["PUT"])
|
||||
@login_required()
|
||||
def __update_slot(self, event_id, slot_id, **kwargs):
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
raise BadRequest
|
||||
|
||||
for job in data['jobs']:
|
||||
eventController.add_job(job.kind, job.user)
|
||||
if eventController.delete_event_slot(slot_id, event_id):
|
||||
return jsonify({"ok": "ok"})
|
||||
raise NotFound
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:event_id>/slots", methods=["POST"])
|
||||
@login_required()
|
||||
def __add_slot(self, event_id, **kwargs):
|
||||
event = eventController.get_event(event_id)
|
||||
if not event:
|
||||
raise NotFound
|
||||
data = request.get_json()
|
||||
attr = {"job_slots": []}
|
||||
try:
|
||||
if "start" in data:
|
||||
attr["start"] = parser.isoparse(data["start"])
|
||||
if "end" in data:
|
||||
attr["end"] = parser.isoparse(data["end"])
|
||||
for job in data["jobs"]:
|
||||
attr["job_slots"].append({"needed_persons": job["needed_persons"], "kind": job["kind"]})
|
||||
except KeyError:
|
||||
raise BadRequest("Missing data in request")
|
||||
eventController.add_slot(event, **attr)
|
||||
return jsonify({"ok": "ok"})
|
||||
|
||||
|
||||
def __edit_event(self):
|
||||
...
|
||||
|
|
|
@ -10,8 +10,9 @@ users_bp = Blueprint("users", __name__)
|
|||
permissions = {'EDIT_USER': 'edit_user'}
|
||||
|
||||
|
||||
def register():
|
||||
return Plugin(users_bp, permissions)
|
||||
class UsersPlugin(Plugin):
|
||||
def __init__(self, config):
|
||||
super().__init__(blueprint=users_bp, permissions=permissions)
|
||||
|
||||
#################################################
|
||||
# Routes #
|
||||
|
@ -23,49 +24,45 @@ def register():
|
|||
# DELETE: remove user #
|
||||
#################################################
|
||||
|
||||
@users_bp.route("/users", methods=['POST'])
|
||||
def __registration(self):
|
||||
logger.debug("Register new User...")
|
||||
return jsonify({"ok": "ok... well not implemented"})
|
||||
|
||||
@users_bp.route("/users", methods=['POST'])
|
||||
def __registration():
|
||||
logger.debug("Register new User...")
|
||||
return jsonify({"ok": "ok... well not implemented"})
|
||||
@users_bp.route("/users", methods=['GET'])
|
||||
@login_required()
|
||||
def __list_users(self, **kwargs):
|
||||
logger.debug("Retrieve list of all users")
|
||||
users = userController.get_users()
|
||||
return jsonify(users)
|
||||
|
||||
|
||||
@users_bp.route("/users", methods=['GET'])
|
||||
@login_required()
|
||||
def __list_users(**kwargs):
|
||||
logger.debug("Retrieve list of all users")
|
||||
users = userController.get_users()
|
||||
return jsonify(users)
|
||||
|
||||
|
||||
@users_bp.route("/users/<uid>", methods=['GET'])
|
||||
@login_required()
|
||||
def __get_user(uid, **kwargs):
|
||||
logger.debug("Get information of user {{ {} }}".format(uid))
|
||||
user = userController.get_user(uid)
|
||||
if user:
|
||||
return jsonify(user)
|
||||
raise NotFound
|
||||
|
||||
|
||||
@users_bp.route("/users/<uid>", methods=['PUT'])
|
||||
@login_required()
|
||||
def __edit_user(uid, **kwargs):
|
||||
logger.debug("Modify information of user {{ {} }}".format(uid))
|
||||
user = userController.get_user(uid)
|
||||
if not user:
|
||||
@users_bp.route("/users/<uid>", methods=['GET'])
|
||||
@login_required()
|
||||
def __get_user(self, uid, **kwargs):
|
||||
logger.debug("Get information of user {{ {} }}".format(uid))
|
||||
user = userController.get_user(uid)
|
||||
if user:
|
||||
return jsonify(user)
|
||||
raise NotFound
|
||||
|
||||
if uid != kwargs['access_token'].user.uid and user.has_permissions(permissions['EDIT_USER']):
|
||||
return Forbidden
|
||||
@users_bp.route("/users/<uid>", methods=['PUT'])
|
||||
@login_required()
|
||||
def __edit_user(self, uid, **kwargs):
|
||||
logger.debug("Modify information of user {{ {} }}".format(uid))
|
||||
user = userController.get_user(uid)
|
||||
if not user:
|
||||
raise NotFound
|
||||
|
||||
data = request.get_json()
|
||||
if 'password' not in data:
|
||||
raise BadRequest("Password is missing")
|
||||
for key in ["firstname", "lastname", "display_name", "mail"]:
|
||||
if key in data:
|
||||
setattr(user, key, data[key])
|
||||
new_password = data['new_password'] if 'new_password' in data else None
|
||||
userController.modify_user(user, data['password'], new_password)
|
||||
userController.update_user(user)
|
||||
return jsonify({"ok": "ok"})
|
||||
if uid != kwargs['access_token'].user.uid and user.has_permissions(permissions['EDIT_USER']):
|
||||
return Forbidden
|
||||
|
||||
data = request.get_json()
|
||||
if 'password' not in data:
|
||||
raise BadRequest("Password is missing")
|
||||
for key in ["firstname", "lastname", "display_name", "mail"]:
|
||||
if key in data:
|
||||
setattr(user, key, data[key])
|
||||
new_password = data['new_password'] if 'new_password' in data else None
|
||||
userController.modify_user(user, data['password'], new_password)
|
||||
userController.update_user(user)
|
||||
return jsonify({"ok": "ok"})
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
import smtplib
|
||||
from datetime import datetime
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.header import Header
|
||||
from geruecht.logger import getDebugLogger
|
||||
from . import mailConfig
|
||||
|
||||
debug = getDebugLogger()
|
||||
|
||||
class EmailController():
|
||||
|
||||
def __init__(self):
|
||||
debug.info("init email controller")
|
||||
self.smtpServer = mailConfig['URL']
|
||||
self.port = mailConfig['port']
|
||||
self.user = mailConfig['user']
|
||||
self.passwd = mailConfig['passwd']
|
||||
self.crypt = mailConfig['crypt']
|
||||
self.email = mailConfig['email']
|
||||
|
||||
debug.debug("smtpServer is {{ {} }}, port is {{ {} }}, user is {{ {} }}, crypt is {{ {} }}, email is {{ {} }}".format(self.smtpServer, self.port, self.user, self.crypt, self.email))
|
||||
|
||||
def __connect__(self):
|
||||
debug.info('connect to email server')
|
||||
if self.crypt == 'SSL':
|
||||
self.smtp = smtplib.SMTP_SSL(self.smtpServer, self.port)
|
||||
log = self.smtp.ehlo()
|
||||
debug.debug("ehlo is {{ {} }}".format(log))
|
||||
if self.crypt == 'STARTTLS':
|
||||
self.smtp = smtplib.SMTP(self.smtpServer, self.port)
|
||||
log = self.smtp.ehlo()
|
||||
debug.debug("ehlo is {{ {} }}".format(log))
|
||||
log = self.smtp.starttls()
|
||||
debug.debug("starttles is {{ {} }}".format(log))
|
||||
log = self.smtp.login(self.user, self.passwd)
|
||||
debug.debug("login is {{ {} }}".format(log))
|
||||
|
||||
def jobTransact(self, user, jobtransact):
|
||||
debug.info("create email jobtransact {{ {} }}for user {{ {} }}".format(jobtransact, user))
|
||||
date = '{}.{}.{}'.format(jobtransact['on_date']['day'], jobtransact['on_date']['month'], jobtransact['on_date']['year'])
|
||||
from_user = '{} {}'.format(jobtransact['from_user']['firstname'], jobtransact['from_user']['lastname'])
|
||||
job_kind = jobtransact['job_kind']
|
||||
subject = 'Dienstanfrage am {}'.format(date)
|
||||
text = MIMEText(
|
||||
"Hallo {} {},\n"
|
||||
"{} fragt, ob du am {} den Dienst {} übernehmen willst.\nBeantworte die Anfrage im Userportal von Flaschengeist.".format(user.firstname, user.lastname, from_user, date, job_kind['name']), 'plain')
|
||||
debug.debug("subject is {{ {} }}, text is {{ {} }}".format(subject, text.as_string()))
|
||||
return (subject, text)
|
||||
|
||||
def jobInvite(self, user, jobtransact):
|
||||
debug.info("create email jobtransact {{ {} }}for user {{ {} }}".format(jobtransact, user))
|
||||
date = '{}.{}.{}'.format(jobtransact['on_date']['day'], jobtransact['on_date']['month'], jobtransact['on_date']['year'])
|
||||
from_user = '{} {}'.format(jobtransact['from_user']['firstname'], jobtransact['from_user']['lastname'])
|
||||
subject = 'Diensteinladung am {}'.format(date)
|
||||
text = MIMEText(
|
||||
"Hallo {} {},\n"
|
||||
"{} fragt, ob du am {} mit Dienst haben willst.\nBeantworte die Anfrage im Userportal von Flaschengeist.".format(user.firstname, user.lastname, from_user, date), 'plain')
|
||||
debug.debug("subject is {{ {} }}, text is {{ {} }}".format(subject, text.as_string()))
|
||||
return (subject, text)
|
||||
|
||||
def credit(self, user):
|
||||
debug.info("create email credit for user {{ {} }}".format(user))
|
||||
subject = Header('Gerücht, bezahle deine Schulden!', 'utf-8')
|
||||
sum = user.getGeruecht(datetime.now().year).getSchulden()
|
||||
if sum < 0:
|
||||
type = 'Schulden'
|
||||
add = 'Bezahle diese umgehend an den Finanzer.'
|
||||
else:
|
||||
type = 'Guthaben'
|
||||
add = ''
|
||||
text = MIMEText(
|
||||
"Hallo {} {},\nDu hast {} im Wert von {:.2f} €. {}\n\nDiese Nachricht wurde automatisch erstellt.".format(
|
||||
user.firstname, user.lastname, type, abs(sum) / 100, add), 'plain')
|
||||
debug.debug("subject is {{ {} }}, text is {{ {} }}".format(subject, text.as_string()))
|
||||
return (subject, text)
|
||||
|
||||
def passwordReset(self, user, data):
|
||||
debug.info("create email passwort reset for user {{ {} }}".format(user))
|
||||
subject = Header("Password vergessen")
|
||||
text = MIMEText(
|
||||
"Hallo {} {},\nDu hast dein Password vergessen!\nDies wurde nun mit Flaschengeist zurückgesetzt.\nDein neues Passwort lautet:\n{}\n\nBitte ändere es sofort in deinem Flaschengeistprolif in https://flaschengeist.wu5.de.".format(
|
||||
user.firstname, user.lastname, data['password']
|
||||
), 'plain'
|
||||
)
|
||||
debug.debug("subject is {{ {} }}, text is {{ {} }}".format(subject, text.as_string()))
|
||||
return (subject, text)
|
||||
|
||||
def sendMail(self, user, type='credit', jobtransact=None, **kwargs):
|
||||
debug.info("send email to user {{ {} }}".format(user))
|
||||
try:
|
||||
if user.mail == 'None' or not user.mail:
|
||||
debug.warning("user {{ {} }} has no email-address".format(user))
|
||||
raise Exception("no valid Email")
|
||||
msg = MIMEMultipart()
|
||||
msg['From'] = self.email
|
||||
msg['To'] = user.mail
|
||||
|
||||
if type == 'credit':
|
||||
subject, text = self.credit(user)
|
||||
elif type == 'jobtransact':
|
||||
subject, text = self.jobTransact(user, jobtransact)
|
||||
elif type == 'jobinvite':
|
||||
subject, text = self.jobInvite(user, jobtransact)
|
||||
elif type == 'passwordReset':
|
||||
subject, text = self.passwordReset(user, kwargs)
|
||||
else:
|
||||
raise Exception("Fail to send Email. No type is set. user={}, type={} , jobtransact={}".format(user, type, jobtransact))
|
||||
|
||||
msg['Subject'] = subject
|
||||
msg.attach(text)
|
||||
|
||||
debug.debug("send email {{ {} }} to user {{ {} }}".format(msg.as_string(), user))
|
||||
self.__connect__()
|
||||
self.smtp.sendmail(self.email, user.mail, msg.as_string())
|
||||
return {'error': False, 'user': {'userId': user.uid, 'firstname': user.firstname, 'lastname': user.lastname}}
|
||||
except Exception:
|
||||
debug.warning("exception in send email", exc_info=True)
|
||||
return {'error': True, 'user': {'userId': user.uid, 'firstname': user.firstname, 'lastname': user.lastname}}
|
|
@ -1,10 +1,9 @@
|
|||
from werkzeug.exceptions import BadRequest, NotFound
|
||||
|
||||
from flaschengeist.system.models.event import EventKind, Event, EventSlot, JobSlot, JobKind
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
from flaschengeist import logger
|
||||
from flaschengeist.system.database import db
|
||||
from flaschengeist.system.models.event import EventKind, Event, EventSlot, JobSlot, JobKind
|
||||
|
||||
|
||||
def get_event(id):
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
from flaschengeist.system.models.user import User, Role
|
||||
from pyhooks import Hook
|
||||
|
||||
|
||||
class Message:
|
||||
def __init__(self, receiver: User or Role, message: str, subject: str):
|
||||
self.message = message
|
||||
self.subject = subject
|
||||
self.receiver = receiver
|
||||
|
||||
|
||||
@Hook
|
||||
def send_message(message: Message):
|
||||
pass
|
|
@ -1,6 +1,6 @@
|
|||
from flask import current_app
|
||||
|
||||
from flaschengeist.system.models.user import User
|
||||
from flaschengeist.system.models.user import User, Role
|
||||
from flaschengeist.system.database import db
|
||||
from flaschengeist import logger
|
||||
|
||||
|
@ -44,5 +44,9 @@ def get_users():
|
|||
return User.query.all()
|
||||
|
||||
|
||||
def get_user_by_role(role: Role):
|
||||
return User.query.join(User.roles).filter_by(role_id=role.id).all()
|
||||
|
||||
|
||||
def get_user(uid):
|
||||
return User.query.filter(User.uid == uid).one_or_none()
|
||||
|
|
15
setup.py
15
setup.py
|
@ -18,17 +18,18 @@ setup(
|
|||
"flask_cors",
|
||||
"werkzeug",
|
||||
"bjoern",
|
||||
"python-dateutil"
|
||||
"python-dateutil",
|
||||
"pyhooks"
|
||||
],
|
||||
extras_require={"ldap": ["flask_ldapconn", "ldap3"]},
|
||||
entry_points={
|
||||
"flaschengeist.plugin": [
|
||||
"auth = flaschengeist.modules.auth:register",
|
||||
"users = flaschengeist.modules.users:register",
|
||||
"roles = flaschengeist.modules.roles:register",
|
||||
"schedule = flaschengeist.modules.schedule:register",
|
||||
],
|
||||
"flaschengeist.auth": [
|
||||
"auth = flaschengeist.modules.auth:AuthRoutePlugin",
|
||||
"users = flaschengeist.modules.users:UsersPlugin",
|
||||
"roles = flaschengeist.modules.roles:RolesPlugin",
|
||||
"schedule = flaschengeist.modules.schedule:SchedulePlugin",
|
||||
"mail = flaschengeist.modules.message_mail:MailMessagePlugin",
|
||||
|
||||
"auth_plain = flaschengeist.modules.auth_plain:AuthPlain",
|
||||
"auth_ldap = flaschengeist.modules.auth_ldap:AuthLDAP [ldap]",
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue