From 123537cd1699b35c311a47e763408f1a158c75a3 Mon Sep 17 00:00:00 2001
From: Patrick Jentsch
Date: Fri, 3 Dec 2021 14:07:03 +0100
Subject: [PATCH] Add new roles and a new permission
---
app/__init__.py | 3 ++-
app/decorators.py | 39 ++++++++++++++++++++++++++++++----
app/models.py | 13 +++++++++---
app/templates/_sidenav.html.j2 | 6 ++++--
app/utils.py | 5 +++++
5 files changed, 56 insertions(+), 10 deletions(-)
diff --git a/app/__init__.py b/app/__init__.py
index c6df4293..0b14dc23 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -39,8 +39,9 @@ def create_app(config_class=Config):
message_queue=app.config.get('NOPAQUE_SOCKETIO_MESSAGE_QUEUE_URI')
)
- from .utils import HashidConverter
+ from .utils import HashidConverter, permission_context_processor
app.url_map.converters['hashid'] = HashidConverter
+ app.context_processor(permission_context_processor)
from .events import socketio as socketio_events
from .events import sqlalchemy as sqlalchemy_events
diff --git a/app/decorators.py b/app/decorators.py
index 98809122..74b37a66 100644
--- a/app/decorators.py
+++ b/app/decorators.py
@@ -2,16 +2,47 @@ from flask import abort, current_app
from flask_login import current_user
from functools import wraps
from threading import Thread
+from .models import Permission
+
+
+def permission_required(permission):
+ def decorator(f):
+ @wraps(f)
+ def decorated_function(*args, **kwargs):
+ if not current_user.can(permission):
+ abort(403)
+ return f(*args, **kwargs)
+ return decorated_function
+ return decorator
def admin_required(f):
+ return permission_required(Permission.ADMINISTRATE)(f)
+
+
+def socketio_login_required(f):
@wraps(f)
- def wrapped(*args, **kwargs):
- if current_user.is_administrator:
+ def decorated_function(*args, **kwargs):
+ if current_user.is_authenticated:
return f(*args, **kwargs)
else:
- abort(403)
- return wrapped
+ return {'code': 401, 'msg': 'Unauthorized'}
+ return decorated_function
+
+
+def socketio_permission_required(permission):
+ def decorator(f):
+ @wraps(f)
+ def decorated_function(*args, **kwargs):
+ if not current_user.can(permission):
+ return {'code': 403, 'msg': 'Forbidden'}
+ return f(*args, **kwargs)
+ return decorated_function
+ return decorator
+
+
+def socketio_admin_required(f):
+ return socketio_permission_required(Permission.ADMINISTRATE)(f)
def background(f):
diff --git a/app/models.py b/app/models.py
index 1572a144..a00db66d 100644
--- a/app/models.py
+++ b/app/models.py
@@ -38,8 +38,9 @@ class Permission(enum.IntEnum):
Defines User permissions as integers by the power of 2. User permission
can be evaluated using the bitwise operator &.
'''
- ADMINISTRATE = 1
- USE_API = 2
+ ADMINISTRATE = 4
+ CONTRIBUTE = 2
+ USE_API = 1
class Role(HashidMixin, db.Model):
@@ -93,7 +94,13 @@ class Role(HashidMixin, db.Model):
def insert_roles():
roles = {
'User': [],
- 'Administrator': [Permission.USE_API, Permission.ADMINISTRATE]
+ 'API user': [Permission.USE_API],
+ 'Contributor': [Permission.CONTRIBUTE],
+ 'Administrator': [
+ Permission.ADMINISTRATE,
+ Permission.CONTRIBUTE,
+ Permission.USE_API
+ ]
}
default_role_name = 'User'
for role_name, permissions in roles.items():
diff --git a/app/templates/_sidenav.html.j2 b/app/templates/_sidenav.html.j2
index aaa71647..e50e345a 100644
--- a/app/templates/_sidenav.html.j2
+++ b/app/templates/_sidenav.html.j2
@@ -22,10 +22,12 @@
settingsSettings
Log out
- {% if current_user.is_administrator() %}
+ {% if current_user.can(Permission.ADMINISTRATE) %}
-
+
admin_panel_settingsAdministration
+ {% endif %}
+ {% if current_user.can(Permission.USE_API) %}
apiAPI
{% endif %}
diff --git a/app/utils.py b/app/utils.py
index a320d4bb..04ca4a45 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -1,5 +1,6 @@
from app import hashids
from werkzeug.routing import BaseConverter
+from .models import Permission
class HashidConverter(BaseConverter):
@@ -8,3 +9,7 @@ class HashidConverter(BaseConverter):
def to_url(self, value):
return hashids.encode(value)
+
+
+def permission_context_processor():
+ return {'Permission': Permission}