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 @@