Added some permissions, reworked permission system.

This commit is contained in:
Ferdinand Thiessen 2020-10-23 02:29:55 +02:00
parent ba0c76a727
commit d3a2b40834
8 changed files with 92 additions and 49 deletions

View File

@ -18,10 +18,7 @@ class Plugin:
pass pass
def serialize(self): def serialize(self):
return { return {"version": self.version, "permissions": self.permissions}
"version": self.version,
"permissions": self.permissions
}
class AuthPlugin(Plugin): class AuthPlugin(Plugin):
@ -38,9 +35,9 @@ class AuthPlugin(Plugin):
def update_user(self, user): def update_user(self, user):
"""If backend is using external data, then update this user instance with external data """If backend is using external data, then update this user instance with external data
)
Args: Args:
user: User object user: User object
""" """
pass pass
@ -57,3 +54,12 @@ class AuthPlugin(Plugin):
Error: Other errors if backend went mad (are not handled and will result in a 500 error) Error: Other errors if backend went mad (are not handled and will result in a 500 error)
""" """
raise NotImplemented raise NotImplemented
def delete_user(self, user):
"""If backend is using (writeable) external data, then delete the user from external database.
Args:
user: User object
"""
pass

View File

@ -6,12 +6,13 @@ from flaschengeist.system.decorator import login_required
from flaschengeist.system.controller import roleController from flaschengeist.system.controller import roleController
roles_bp = Blueprint("roles", __name__) roles_bp = Blueprint("roles", __name__)
roles_permission = "roles_edit" _permission_edit = "roles_edit"
_permission_delete = "roles_delete"
class RolesPlugin(Plugin): class RolesPlugin(Plugin):
def __init__(self, config): def __init__(self, config):
super().__init__(config, roles_bp, permissions=[roles_permission]) super().__init__(config, roles_bp, permissions=[_permission_edit, _permission_delete])
###################################################### ######################################################
@ -27,7 +28,7 @@ class RolesPlugin(Plugin):
@roles_bp.route("/roles", methods=["POST"]) @roles_bp.route("/roles", methods=["POST"])
@login_required(permissions=[roles_permission]) @login_required(permission=_permission_edit)
def add_role(**kwargs): def add_role(**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:
@ -41,7 +42,7 @@ def add_role(**kwargs):
@roles_bp.route("/roles", methods=["GET"]) @roles_bp.route("/roles", methods=["GET"])
@login_required() @login_required()
def list_roles(**kwargs): def list_roles(**kwargs):
roles = roleController.get_roles() roles = roleController.get_all()
return jsonify(roles) return jsonify(roles)
@ -55,18 +56,16 @@ def list_permissions(**kwargs):
@roles_bp.route("/roles/<rid>", methods=["GET"]) @roles_bp.route("/roles/<rid>", methods=["GET"])
@login_required() @login_required()
def __get_role(rid, **kwargs): def __get_role(rid, **kwargs):
role = roleController.get_role(rid) role = roleController.get(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"]) @roles_bp.route("/roles/<rid>", methods=["PUT"])
@login_required(permissions=[roles_permission]) @login_required(permission=_permission_edit)
def __edit_role(rid, **kwargs): def __edit_role(rid, **kwargs):
role = roleController.get_role(rid) role = roleController.get(rid)
if not role:
raise NotFound
data = request.get_json() data = request.get_json()
if "name" in data: if "name" in data:
@ -78,9 +77,9 @@ def __edit_role(rid, **kwargs):
@roles_bp.route("/roles/<rid>", methods=["DELETE"]) @roles_bp.route("/roles/<rid>", methods=["DELETE"])
@login_required(permissions=[roles_permission]) @login_required(permission=_permission_delete)
def __delete_role(rid, **kwargs): def __delete_role(rid, **kwargs):
if not roleController.delete_role(rid): role = roleController.get(rid)
raise NotFound roleController.delete(role)
return jsonify({"ok": "ok"}) return jsonify({"ok": "ok"})

View File

@ -10,13 +10,25 @@ from flaschengeist.system.decorator import login_required
from flaschengeist.system.controller import eventController from flaschengeist.system.controller import eventController
schedule_bp = Blueprint("schedule", __name__, url_prefix="/schedule") schedule_bp = Blueprint("schedule", __name__, url_prefix="/schedule")
schedule_perms = {"EDIT_EVENT": "schedule_edit_event", _permission_edit_type = "schedule_edit_type"
"NEW_EVENT": "schedule_create_event"} _permission_edit = "schedule_edit"
_permission_create = "schedule_create"
_permission_delete = "schedule_delete"
_permission_assign = "schedule_assign_other"
class SchedulePlugin(Plugin): class SchedulePlugin(Plugin):
def __init__(self, config): def __init__(self, config):
super().__init__(blueprint=schedule_bp, permissions=schedule_perms.values()) super().__init__(
blueprint=schedule_bp,
permissions=[
_permission_create,
_permission_edit,
_permission_edit_type,
_permission_delete,
_permission_assign,
],
)
#################################################################################### ####################################################################################
@ -41,10 +53,10 @@ class SchedulePlugin(Plugin):
#################################################################################### ####################################################################################
@schedule_bp.route("/events/<int:id>", methods=["GET"]) @schedule_bp.route("/events/<int:eid>", methods=["GET"])
@login_required() @login_required()
def __get_event(id, **kwargs): def __get_event(eid, **kwargs):
event = eventController.get_event(id) event = eventController.get_event(eid)
if not event: if not event:
raise NotFound raise NotFound
return jsonify(event) return jsonify(event)
@ -86,7 +98,7 @@ def __get_events(year=datetime.now().year, month=datetime.now().month, day=None,
@schedule_bp.route("/eventKinds", methods=["POST"]) @schedule_bp.route("/eventKinds", methods=["POST"])
@login_required() @login_required(permission=_permission_edit_type)
def __new_event_kind(**kwargs): def __new_event_kind(**kwargs):
data = request.get_json() data = request.get_json()
if "name" not in data: if "name" not in data:
@ -96,7 +108,7 @@ def __new_event_kind(**kwargs):
@schedule_bp.route("/slotKinds", methods=["POST"]) @schedule_bp.route("/slotKinds", methods=["POST"])
@login_required() @login_required(permission=_permission_edit_type)
def __new_slot_kind(**kwargs): 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:
@ -106,7 +118,7 @@ def __new_slot_kind(**kwargs):
@schedule_bp.route("/events", methods=["POST"]) @schedule_bp.route("/events", methods=["POST"])
@login_required(permissions=[schedule_perms["NEW_EVENT"]]) @login_required(permission=_permission_create)
def __new_event(**kwargs): def __new_event(**kwargs):
data = request.get_json() data = request.get_json()
event = eventController.create_event( event = eventController.create_event(
@ -119,7 +131,7 @@ def __new_event(**kwargs):
@schedule_bp.route("/events/<int:event_id>", methods=["DELETE"]) @schedule_bp.route("/events/<int:event_id>", methods=["DELETE"])
@login_required(permissions=[schedule_perms["EDIT_EVENT"]]) @login_required(permission=_permission_delete)
def __delete_event(event_id, **kwargs): def __delete_event(event_id, **kwargs):
if not eventController.delete_event(event_id): if not eventController.delete_event(event_id):
raise NotFound raise NotFound
@ -128,7 +140,7 @@ def __delete_event(event_id, **kwargs):
@schedule_bp.route("/eventKinds/<int:event_id>", methods=["PUT"]) @schedule_bp.route("/eventKinds/<int:event_id>", methods=["PUT"])
@login_required() @login_required(permission=_permission_edit_type)
def __edit_event_kind(event_id, **kwargs): 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:
@ -156,7 +168,7 @@ def __get_slot(event_id, slot_id, **kwargs):
@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(permission=_permission_delete)
def __delete_slot(event_id, slot_id, **kwargs): 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"})
@ -164,7 +176,7 @@ def __delete_slot(event_id, slot_id, **kwargs):
@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(permission=_permission_edit)
def __update_slot(event_id, slot_id, **kwargs): def __update_slot(event_id, slot_id, **kwargs):
data = request.get_json() data = request.get_json()
if not data: if not data:
@ -178,7 +190,7 @@ def __update_slot(event_id, slot_id, **kwargs):
@schedule_bp.route("/events/<int:event_id>/slots", methods=["POST"]) @schedule_bp.route("/events/<int:event_id>/slots", methods=["POST"])
@login_required() @login_required(permission=_permission_edit)
def __add_slot(event_id, **kwargs): def __add_slot(event_id, **kwargs):
event = eventController.get_event(event_id) event = eventController.get_event(event_id)
if not event: if not event:

View File

@ -7,12 +7,13 @@ 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__)
users_perm = "users_edit_other" _permission_edit = "users_edit_other"
_permission_delete = "users_delete_other"
class UsersPlugin(Plugin): class UsersPlugin(Plugin):
def __init__(self, config): def __init__(self, config):
super().__init__(blueprint=users_bp, permissions=[users_perm]) super().__init__(blueprint=users_bp, permissions=[_permission_edit, _permission_delete])
################################################# #################################################
# Routes # # Routes #
@ -49,15 +50,22 @@ def __get_user(uid, **kwargs):
raise NotFound raise NotFound
@users_bp.route("/users/<uid>", methods=["DELETE"])
@login_required(permission=_permission_delete)
def __delete_user(uid, **kwargs):
logger.debug("Delete user {{ {} }}".format(uid))
user = userController.get_user(uid)
userController.delete(user)
return jsonify({"ok": "ok"})
@users_bp.route("/users/<uid>", methods=["PUT"]) @users_bp.route("/users/<uid>", methods=["PUT"])
@login_required() @login_required()
def __edit_user(uid, **kwargs): 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:
raise NotFound
if uid != kwargs["access_token"].user.userid and user.has_permissions([users_perm]): if uid != kwargs["access_token"].user.userid and user.has_permission(_permission_edit):
return Forbidden return Forbidden
data = request.get_json() data = request.get_json()

View File

@ -1,17 +1,19 @@
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from werkzeug.exceptions import BadRequest from werkzeug.exceptions import BadRequest, NotFound
from flaschengeist.system.models.user import Role, Permission from flaschengeist.system.models.user import Role, Permission
from flaschengeist.system.database import db from flaschengeist.system.database import db
from flaschengeist import logger from flaschengeist import logger
def get_roles(): def get_all():
return Role.query.all() return Role.query.all()
def get_role(rid): def get(rid):
return Role.query.get(rid) role = Role.query.get(rid).one_or_none()
if not role:
raise NotFound
def get_permissions(): def get_permissions():
@ -47,9 +49,9 @@ def create_role(name, permissions=[]):
return role.id return role.id
def delete_role(id): def delete(role):
try: try:
num = Role.query.filter(Role.id == id).delete() num = Role.query.filter(Role.id == role.id).delete()
except IntegrityError: except IntegrityError:
logger.debug("IntegrityError: Role might still be in use", exc_info=True) logger.debug("IntegrityError: Role might still be in use", exc_info=True)
raise BadRequest("Role still in use") raise BadRequest("Role still in use")

View File

@ -1,4 +1,5 @@
from flask import current_app from flask import current_app
from werkzeug.exceptions import NotFound
from flaschengeist.system.models.user import User, Role from flaschengeist.system.models.user import User, Role
from flaschengeist.system.database import db from flaschengeist.system.database import db
@ -51,4 +52,13 @@ def get_user_by_role(role: Role):
def get_user(uid): def get_user(uid):
return User.query.filter(User.userid == uid).one_or_none() user = User.query.filter(User.userid == uid).one_or_none()
if not user:
raise NotFound
return user
def delete(user):
current_app.config["FG_AUTH_BACKEND"].delete_user(user)
db.session.delete(user)
db.session.commit()

View File

@ -6,12 +6,12 @@ from flaschengeist import logger
from flaschengeist.system.controller import sessionController from flaschengeist.system.controller import sessionController
def login_required(permissions=None): def login_required(permission=None):
def wrap(func): def wrap(func):
@wraps(func) @wraps(func)
def wrapped_f(*args, **kwargs): def wrapped_f(*args, **kwargs):
token = list(filter(None, request.headers.get("Authorization").split(" ")))[-1] token = list(filter(None, request.headers.get("Authorization").split(" ")))[-1]
access_token = sessionController.validate_token(token, request.user_agent, permissions) access_token = sessionController.validate_token(token, request.user_agent, permission)
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))
@ -19,5 +19,7 @@ def login_required(permissions=None):
else: else:
logger.info("token {{ {} }} is not valid".format(token)) logger.info("token {{ {} }} is not valid".format(token))
raise Unauthorized raise Unauthorized
return wrapped_f return wrapped_f
return wrap return wrap

View File

@ -45,15 +45,19 @@ class Event(db.Model, ModelSerializeMixin):
class Job(db.Model, ModelSerializeMixin): class Job(db.Model, ModelSerializeMixin):
__tablename__ = "job" __tablename__ = "job"
_user: User = db.relationship("User") user: str = None
# user: str = column_property(_user.userid)
value: float = db.Column(db.Numeric(precision=3, scale=2)) value: float = db.Column(db.Numeric(precision=3, scale=2))
_id = db.Column("id", db.Integer, primary_key=True) _id = db.Column("id", db.Integer, primary_key=True)
_user_id = db.Column("user_id", db.Integer, db.ForeignKey("user.id")) _user_id = db.Column("user_id", db.Integer, db.ForeignKey("user.id"))
_user: User = db.relationship("User")
_slot_id = db.Column("slot_id", db.Integer, db.ForeignKey("job_slot.id")) _slot_id = db.Column("slot_id", db.Integer, db.ForeignKey("job_slot.id"))
_slot = db.relationship("JobSlot") _slot = db.relationship("JobSlot")
@property
def user(self):
return self._user.userid
class JobKind(db.Model, ModelSerializeMixin): class JobKind(db.Model, ModelSerializeMixin):
__tablename__ = "job_kind" __tablename__ = "job_kind"