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
def serialize(self):
return {
"version": self.version,
"permissions": self.permissions
}
return {"version": self.version, "permissions": self.permissions}
class AuthPlugin(Plugin):
@ -38,9 +35,9 @@ class AuthPlugin(Plugin):
def update_user(self, user):
"""If backend is using external data, then update this user instance with external data
Args:
user: User object
)
Args:
user: User object
"""
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)
"""
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
roles_bp = Blueprint("roles", __name__)
roles_permission = "roles_edit"
_permission_edit = "roles_edit"
_permission_delete = "roles_delete"
class RolesPlugin(Plugin):
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"])
@login_required(permissions=[roles_permission])
@login_required(permission=_permission_edit)
def add_role(**kwargs):
data = request.get_json()
if not data or "name" not in data:
@ -41,7 +42,7 @@ def add_role(**kwargs):
@roles_bp.route("/roles", methods=["GET"])
@login_required()
def list_roles(**kwargs):
roles = roleController.get_roles()
roles = roleController.get_all()
return jsonify(roles)
@ -55,18 +56,16 @@ def list_permissions(**kwargs):
@roles_bp.route("/roles/<rid>", methods=["GET"])
@login_required()
def __get_role(rid, **kwargs):
role = roleController.get_role(rid)
role = roleController.get(rid)
if role:
return jsonify({"id": role.id, "name": role, "permissions": role.permissions})
raise NotFound
@roles_bp.route("/roles/<rid>", methods=["PUT"])
@login_required(permissions=[roles_permission])
@login_required(permission=_permission_edit)
def __edit_role(rid, **kwargs):
role = roleController.get_role(rid)
if not role:
raise NotFound
role = roleController.get(rid)
data = request.get_json()
if "name" in data:
@ -78,9 +77,9 @@ def __edit_role(rid, **kwargs):
@roles_bp.route("/roles/<rid>", methods=["DELETE"])
@login_required(permissions=[roles_permission])
@login_required(permission=_permission_delete)
def __delete_role(rid, **kwargs):
if not roleController.delete_role(rid):
raise NotFound
role = roleController.get(rid)
roleController.delete(role)
return jsonify({"ok": "ok"})

View File

@ -10,13 +10,25 @@ from flaschengeist.system.decorator import login_required
from flaschengeist.system.controller import eventController
schedule_bp = Blueprint("schedule", __name__, url_prefix="/schedule")
schedule_perms = {"EDIT_EVENT": "schedule_edit_event",
"NEW_EVENT": "schedule_create_event"}
_permission_edit_type = "schedule_edit_type"
_permission_edit = "schedule_edit"
_permission_create = "schedule_create"
_permission_delete = "schedule_delete"
_permission_assign = "schedule_assign_other"
class SchedulePlugin(Plugin):
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()
def __get_event(id, **kwargs):
event = eventController.get_event(id)
def __get_event(eid, **kwargs):
event = eventController.get_event(eid)
if not event:
raise NotFound
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"])
@login_required()
@login_required(permission=_permission_edit_type)
def __new_event_kind(**kwargs):
data = request.get_json()
if "name" not in data:
@ -96,7 +108,7 @@ def __new_event_kind(**kwargs):
@schedule_bp.route("/slotKinds", methods=["POST"])
@login_required()
@login_required(permission=_permission_edit_type)
def __new_slot_kind(**kwargs):
data = request.get_json()
if not data or "name" not in data:
@ -106,7 +118,7 @@ def __new_slot_kind(**kwargs):
@schedule_bp.route("/events", methods=["POST"])
@login_required(permissions=[schedule_perms["NEW_EVENT"]])
@login_required(permission=_permission_create)
def __new_event(**kwargs):
data = request.get_json()
event = eventController.create_event(
@ -119,7 +131,7 @@ def __new_event(**kwargs):
@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):
if not eventController.delete_event(event_id):
raise NotFound
@ -128,7 +140,7 @@ def __delete_event(event_id, **kwargs):
@schedule_bp.route("/eventKinds/<int:event_id>", methods=["PUT"])
@login_required()
@login_required(permission=_permission_edit_type)
def __edit_event_kind(event_id, **kwargs):
data = request.get_json()
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"])
@login_required()
@login_required(permission=_permission_delete)
def __delete_slot(event_id, slot_id, **kwargs):
if eventController.delete_event_slot(slot_id, event_id):
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"])
@login_required()
@login_required(permission=_permission_edit)
def __update_slot(event_id, slot_id, **kwargs):
data = request.get_json()
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"])
@login_required()
@login_required(permission=_permission_edit)
def __add_slot(event_id, **kwargs):
event = eventController.get_event(event_id)
if not event:

View File

@ -7,12 +7,13 @@ from flaschengeist.system.decorator import login_required
from flaschengeist.system.controller import userController
users_bp = Blueprint("users", __name__)
users_perm = "users_edit_other"
_permission_edit = "users_edit_other"
_permission_delete = "users_delete_other"
class UsersPlugin(Plugin):
def __init__(self, config):
super().__init__(blueprint=users_bp, permissions=[users_perm])
super().__init__(blueprint=users_bp, permissions=[_permission_edit, _permission_delete])
#################################################
# Routes #
@ -49,15 +50,22 @@ def __get_user(uid, **kwargs):
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"])
@login_required()
def __edit_user(uid, **kwargs):
logger.debug("Modify information of user {{ {} }}".format(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
data = request.get_json()

View File

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

View File

@ -1,4 +1,5 @@
from flask import current_app
from werkzeug.exceptions import NotFound
from flaschengeist.system.models.user import User, Role
from flaschengeist.system.database import db
@ -51,4 +52,13 @@ def get_user_by_role(role: Role):
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
def login_required(permissions=None):
def login_required(permission=None):
def wrap(func):
@wraps(func)
def wrapped_f(*args, **kwargs):
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:
kwargs["access_token"] = access_token
logger.debug("token {{ {} }} is valid".format(token))
@ -19,5 +19,7 @@ def login_required(permissions=None):
else:
logger.info("token {{ {} }} is not valid".format(token))
raise Unauthorized
return wrapped_f
return wrap

View File

@ -45,15 +45,19 @@ class Event(db.Model, ModelSerializeMixin):
class Job(db.Model, ModelSerializeMixin):
__tablename__ = "job"
_user: User = db.relationship("User")
# user: str = column_property(_user.userid)
user: str = None
value: float = db.Column(db.Numeric(precision=3, scale=2))
_id = db.Column("id", db.Integer, primary_key=True)
_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 = db.relationship("JobSlot")
@property
def user(self):
return self._user.userid
class JobKind(db.Model, ModelSerializeMixin):
__tablename__ = "job_kind"