Fixed Hooks, use own implementation. Fixed routes.

This commit is contained in:
Ferdinand Thiessen 2020-10-16 00:37:57 +02:00
parent 287cc91947
commit ec05cde746
10 changed files with 405 additions and 330 deletions

View File

@ -1,6 +1,6 @@
from pyhooks import precall_register from flaschengeist.system.hook import HookCall
send_message_hook = precall_register("send_message") send_message_hook = HookCall("send_message")
class Plugin: class Plugin:

View File

@ -11,7 +11,7 @@ from werkzeug.local import LocalProxy
from flaschengeist import logger from flaschengeist import logger
from flaschengeist.modules import Plugin from flaschengeist.modules import Plugin
from flaschengeist.system.decorator import login_required from flaschengeist.system.decorator import login_required
from flaschengeist.system.controller import accessTokenController, userController from flaschengeist.system.controller import accessTokenController, userController, messageController
access_controller = LocalProxy(lambda: accessTokenController.AccessTokenController()) access_controller = LocalProxy(lambda: accessTokenController.AccessTokenController())
auth_bp = Blueprint("auth", __name__) auth_bp = Blueprint("auth", __name__)
@ -21,18 +21,19 @@ 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"])
def _create_token(): @auth_bp.route("/auth", methods=["POST"])
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.
@ -61,15 +62,19 @@ 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"])
@login_required() @auth_bp.route("/auth", methods=["GET"])
def _get_tokens(access_token, **kwargs): @login_required()
def _get_tokens(access_token, **kwargs):
tokens = access_controller.get_users_tokens(access_token.user) tokens = access_controller.get_users_tokens(access_token.user)
a = messageController.Message(access_token.user, "Go", "Bar")
messageController.send_message(a)
return jsonify(tokens) return jsonify(tokens)
@auth_bp.route("/auth/<token>", methods=["DELETE"])
@login_required() @login_required()
def _delete_token(token, access_token, **kwargs): @auth_bp.route("/auth/<token>", methods=["DELETE"])
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))
token = access_controller.get_token(token, access_token.user) token = access_controller.get_token(token, access_token.user)
if not token: if not token:
@ -81,9 +86,10 @@ 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"])
@login_required() @login_required()
def _get_token(token, access_token, **kwargs): @auth_bp.route("/auth/<token>", methods=["GET"])
def _get_token(token, access_token, **kwargs):
logger.debug("get token {{ {} }}".format(token)) logger.debug("get token {{ {} }}".format(token))
token = access_controller.get_token(token, access_token.user) token = access_controller.get_token(token, access_token.user)
if not token: if not token:
@ -92,9 +98,10 @@ class AuthRoutePlugin(Plugin):
raise Forbidden raise Forbidden
return jsonify(token) return jsonify(token)
@auth_bp.route("/auth/<token>", methods=["PUT"])
@login_required() @login_required()
def _set_lifetime(token, access_token, **kwargs): @auth_bp.route("/auth/<token>", methods=["PUT"])
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)
if not token: if not token:
# Return 403 error, so that users can not bruteforce tokens # Return 403 error, so that users can not bruteforce tokens

View File

@ -1,6 +1,8 @@
import smtplib import smtplib
from email.mime.multipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from flaschengeist import logger
from flaschengeist.system.models.user import User from flaschengeist.system.models.user import User
from flaschengeist.system.controller import userController from flaschengeist.system.controller import userController
from flaschengeist.system.controller.messageController import Message from flaschengeist.system.controller.messageController import Message
@ -19,8 +21,14 @@ class MailMessagePlugin(Plugin):
self.mail = config["MAIL"] self.mail = config["MAIL"]
@send_message_hook @send_message_hook
def dummy_send(msg):
self.send_mail(msg)
def send_mail(self, msg: Message): def send_mail(self, msg: Message):
if isinstance(msg.receiver, User): if isinstance(msg.receiver, User):
if not msg.receiver.mail:
logger.warn("Could not send Mail, mail missing: {}".format(msg.receiver))
return
recipients = [msg.receiver.mail] recipients = [msg.receiver.mail]
else: else:
recipients = userController.get_user_by_role(msg.receiver) recipients = userController.get_user_by_role(msg.receiver)
@ -29,15 +37,17 @@ class MailMessagePlugin(Plugin):
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) mail.attach(MIMEText(msg.message))
if not self.smtp: if not hasattr(self, "smtp"):
self.__connect() self.__connect()
self.smtp.sendmail(self.mail, recipients, msg.as_string()) self.smtp.sendmail(self.mail, recipients, mail.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": elif self.crypt == "STARTTLS":
self.smtp = smtplib.SMTP(self.smtpServer, self.port) self.smtp = smtplib.SMTP(self.server, self.port)
self.smtp.starttls() self.smtp.starttls()
else:
raise ValueError("Invalid CRYPT given")
self.smtp.login(self.user, self.password) self.smtp.login(self.user, self.password)

View File

@ -12,20 +12,21 @@ 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"])
@login_required() @roles_bp.route("/roles", methods=["POST"])
def add_role(self): @login_required()
def add_role(self):
data = request.get_json() data = request.get_json()
if not data or "name" not in data: if not data or "name" not in data:
raise BadRequest raise BadRequest
@ -34,29 +35,33 @@ 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"])
@login_required() @roles_bp.route("/roles", methods=["GET"])
def list_roles(self, **kwargs): @login_required()
def list_roles(**kwargs):
roles = roleController.get_roles() roles = roleController.get_roles()
return jsonify(roles) return jsonify(roles)
@roles_bp.route("/roles/permissions", methods=["GET"])
@login_required() @roles_bp.route("/roles/permissions", methods=["GET"])
def list_permissions(self, **kwargs): @login_required()
def list_permissions(**kwargs):
permissions = roleController.get_permissions() permissions = roleController.get_permissions()
return jsonify(permissions) return jsonify(permissions)
@roles_bp.route("/roles/<rid>", methods=["GET"])
@login_required() @roles_bp.route("/roles/<rid>", methods=["GET"])
def __get_role(self, rid, **kwargs): @login_required()
def __get_role(rid, **kwargs):
role = roleController.get_role(rid) role = roleController.get_role(rid)
if role: if role:
return jsonify({"id": role.id, "name": role, "permissions": role.permissions}) return jsonify({"id": role.id, "name": role, "permissions": role.permissions})
raise NotFound raise NotFound
@roles_bp.route("/roles/<rid>", methods=["PUT"])
@login_required() @roles_bp.route("/roles/<rid>", methods=["PUT"])
def __edit_role(self, rid, **kwargs): @login_required()
def __edit_role(rid, **kwargs):
role = roleController.get_role(rid) role = roleController.get_role(rid)
if not role: if not role:
raise NotFound raise NotFound
@ -69,9 +74,10 @@ class RolesPlugin(Plugin):
roleController.update_role(role) roleController.update_role(role)
return jsonify({"ok": "ok"}) return jsonify({"ok": "ok"})
@roles_bp.route("/roles/<rid>", methods=["DELETE"])
@login_required() @roles_bp.route("/roles/<rid>", methods=["DELETE"])
def __delete_role(self, rid, **kwargs): @login_required()
def __delete_role(rid, **kwargs):
if not roleController.delete_role(rid): if not roleController.delete_role(rid):
raise NotFound raise NotFound

View File

@ -16,40 +16,42 @@ 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"])
@login_required() # roles=['schedule_read']) @schedule_bp.route("/events/<int:id>", methods=["GET"])
def __get_event(self, id, **kwargs): @login_required() # roles=['schedule_read'])
def __get_event(self, id, **kwargs):
event = eventController.get_event(id) event = eventController.get_event(id)
if not event: if not event:
raise NotFound raise NotFound
return jsonify(event) return jsonify(event)
@schedule_bp.route("/events", methods=["GET"])
@schedule_bp.route("/events/<int:year>/<int:month>", methods=["GET"]) @schedule_bp.route("/events", methods=["GET"])
@schedule_bp.route("/events/<int:year>/<int:month>/<int:day>", methods=["GET"]) @schedule_bp.route("/events/<int:year>/<int:month>", methods=["GET"])
@login_required() # roles=['schedule_read']) @schedule_bp.route("/events/<int:year>/<int:month>/<int:day>", methods=["GET"])
def __get_events(self, year=datetime.now().year, month=datetime.now().month, day=None, **kwrags): @login_required() # roles=['schedule_read'])
def __get_events(year=datetime.now().year, month=datetime.now().month, day=None, **kwargs):
"""Get Event objects for specified date (or month or year), """Get Event objects for specified date (or month or year),
if nothing set then events for current month are returned if nothing set then events for current month are returned
@ -57,7 +59,7 @@ class SchedulePlugin(Plugin):
year (int, optional): year to query, defaults to current year year (int, optional): year to query, defaults to current year
month (int, optional): month to query (if set), defaults to current month month (int, optional): month to query (if set), defaults to current month
day (int, optional): day to query events for (if set) day (int, optional): day to query events for (if set)
**kwrags: contains at least access_token (see flaschengeist.decorator) **kwargs: contains at least access_token (see flaschengeist.decorator)
Returns: Returns:
JSON list containing events found JSON list containing events found
Raises: Raises:
@ -79,27 +81,30 @@ class SchedulePlugin(Plugin):
except ValueError: except ValueError:
raise BadRequest("Invalid date given") raise BadRequest("Invalid date given")
@schedule_bp.route("/eventKinds", methods=["POST"])
@login_required() @schedule_bp.route("/eventKinds", methods=["POST"])
def __new_event_kind(self, **kwargs): @login_required()
def __new_event_kind(**kwargs):
data = request.get_json() data = request.get_json()
if "name" not in data: if "name" not in data:
raise BadRequest raise BadRequest
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"])
@login_required() @schedule_bp.route("/slotKinds", methods=["POST"])
def __new_slot_kind(self, **kwargs): @login_required()
def __new_slot_kind(**kwargs):
data = request.get_json() data = request.get_json()
if not data or "name" not in data: if not data or "name" not in data:
raise BadRequest raise BadRequest
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"])
@login_required() @schedule_bp.route("/events", methods=["POST"])
def __new_event(self, **kwargs): @login_required()
def __new_event(**kwargs):
data = request.get_json() data = request.get_json()
event = eventController.create_event( event = eventController.create_event(
begin=parser.isoparse(data["begin"]), begin=parser.isoparse(data["begin"]),
@ -109,49 +114,55 @@ class SchedulePlugin(Plugin):
) )
return jsonify({"ok": "ok", "id": event.id}) return jsonify({"ok": "ok", "id": event.id})
@schedule_bp.route("/events/<int:id>", methods=["DELETE"])
@login_required() @schedule_bp.route("/events/<int:event_id>", methods=["DELETE"])
def __delete_event(self, id, **kwargs): @login_required()
if not eventController.delete_event(id): def __delete_event(event_id, **kwargs):
if not eventController.delete_event(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"])
@login_required() @schedule_bp.route("/eventKinds/<int:event_id>", methods=["PUT"])
def __edit_event_kind(self, id, **kwargs): @login_required()
def __edit_event_kind(event_id, **kwargs):
data = request.get_json() data = request.get_json()
if not data or "name" not in data: if not data or "name" not in data:
raise BadRequest raise BadRequest
eventController.rename_event_kind(id, data["name"]) eventController.rename_event_kind(event_id, data["name"])
return jsonify({"ok": "ok"}) return jsonify({"ok": "ok"})
@schedule_bp.route("/events/<int:event_id>/slots", methods=["GET"])
@login_required() @schedule_bp.route("/events/<int:event_id>/slots", methods=["GET"])
def __get_slots(self, event_id, **kwargs): @login_required()
def __get_slots(event_id, **kwargs):
event = eventController.get_event(event_id) event = eventController.get_event(event_id)
if not event: if not event:
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"])
@login_required() @schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["GET"])
def __get_slot(self, event_id, slot_id, **kwargs): @login_required()
def __get_slot(event_id, slot_id, **kwargs):
slot = eventController.get_event_slot(slot_id, event_id) slot = eventController.get_event_slot(slot_id, event_id)
if slot: if slot:
return jsonify(slot) return jsonify(slot)
raise NotFound raise NotFound
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["DELETE"])
@login_required() @schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["DELETE"])
def __delete_slot(self, event_id, slot_id, **kwargs): @login_required()
def __delete_slot(event_id, slot_id, **kwargs):
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/<int:slot_id>", methods=["PUT"])
@login_required() @schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["PUT"])
def __update_slot(self, event_id, slot_id, **kwargs): @login_required()
def __update_slot(event_id, slot_id, **kwargs):
data = request.get_json() data = request.get_json()
if not data: if not data:
raise BadRequest raise BadRequest
@ -162,9 +173,10 @@ class SchedulePlugin(Plugin):
return jsonify({"ok": "ok"}) return jsonify({"ok": "ok"})
raise NotFound raise NotFound
@schedule_bp.route("/events/<int:event_id>/slots", methods=["POST"])
@login_required() @schedule_bp.route("/events/<int:event_id>/slots", methods=["POST"])
def __add_slot(self, event_id, **kwargs): @login_required()
def __add_slot(event_id, **kwargs):
event = eventController.get_event(event_id) event = eventController.get_event(event_id)
if not event: if not event:
raise NotFound raise NotFound
@ -182,5 +194,6 @@ 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():
... ...

View File

@ -24,30 +24,34 @@ class UsersPlugin(Plugin):
# DELETE: remove user # # DELETE: remove user #
################################################# #################################################
@users_bp.route("/users", methods=["POST"])
def __registration(self): @users_bp.route("/users", methods=["POST"])
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"])
@login_required() @users_bp.route("/users", methods=["GET"])
def __list_users(self, **kwargs): @login_required()
def __list_users(**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"])
@login_required() @users_bp.route("/users/<uid>", methods=["GET"])
def __get_user(self, uid, **kwargs): @login_required()
def __get_user(uid, **kwargs):
logger.debug("Get information of user {{ {} }}".format(uid)) logger.debug("Get information of user {{ {} }}".format(uid))
user = userController.get_user(uid) user = userController.get_user(uid)
if user: if user:
return jsonify(user) return jsonify(user)
raise NotFound raise NotFound
@users_bp.route("/users/<uid>", methods=["PUT"])
@login_required() @users_bp.route("/users/<uid>", methods=["PUT"])
def __edit_user(self, uid, **kwargs): @login_required()
def __edit_user(uid, **kwargs):
logger.debug("Modify information of user {{ {} }}".format(uid)) logger.debug("Modify information of user {{ {} }}".format(uid))
user = userController.get_user(uid) user = userController.get_user(uid)
if not user: if not user:

View File

@ -28,7 +28,22 @@ or with debug messages:
run_flaschengeist --debug run_flaschengeist --debug
## Development ## Development
### Code Style
We enforce you to use PEP 8 code style with a line length of 120 as used by Black.
See also [Black Code Style](https://github.com/psf/black/blob/master/docs/the_black_code_style.md).
#### Code formatting
We use [Black](https://github.com/psf/black) as the code formatter.
Installation:
pip install black
Usage:
black -l 120 DIRECTORY_OR_FILE
### Misc ### Misc
#### Git blame
When using `git blame` use this to ignore the code formatting commits: When using `git blame` use this to ignore the code formatting commits:
$ git blame FILE.py --ignore-revs-file .git-blame-ignore-revs $ git blame FILE.py --ignore-revs-file .git-blame-ignore-revs

View File

@ -1,5 +1,6 @@
from flaschengeist import logger
from flaschengeist.system.hook import Hook
from flaschengeist.system.models.user import User, Role from flaschengeist.system.models.user import User, Role
from pyhooks import Hook
class Message: class Message:

View File

@ -0,0 +1,20 @@
_hook_dict = {}
class Hook(object):
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
if self.function.__name__ in _hook_dict:
_hook_dict[self.function.__name__](*args, **kwargs)
self.function(*args, **kwargs)
class HookCall(object):
def __init__(self, name):
self.name = name
def __call__(self, function):
_hook_dict[self.name] = function
return function

View File

@ -19,7 +19,6 @@ setup(
"werkzeug", "werkzeug",
"bjoern", "bjoern",
"python-dateutil", "python-dateutil",
"pyhooks",
], ],
extras_require={"ldap": ["flask_ldapconn", "ldap3"]}, extras_require={"ldap": ["flask_ldapconn", "ldap3"]},
entry_points={ entry_points={