From 66dcfa80b10002c3dab97072ad620dfad120e0f2 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Tue, 1 Sep 2020 21:36:25 +0200 Subject: [PATCH] Fixed Typo in accessController, added Roles for access controll --- flaschengeist/__init__.py | 15 +++++-------- ...Controller.py => accessTokenController.py} | 22 ++++++++++--------- flaschengeist/system/decorator.py | 17 +++++--------- flaschengeist/system/models/user.py | 22 ++++++++++++++----- 4 files changed, 40 insertions(+), 36 deletions(-) rename flaschengeist/system/controller/{accesTokenController.py => accessTokenController.py} (84%) diff --git a/flaschengeist/__init__.py b/flaschengeist/__init__.py index 441e331..2d79f16 100644 --- a/flaschengeist/__init__.py +++ b/flaschengeist/__init__.py @@ -45,16 +45,11 @@ def create_app(): logger.error('No authentification plugin configured or authentification plugin not found') logger.info('Search for plugins') - discovered_plugins = { - entry_point.name: entry_point.load() - for entry_point in pkg_resources.iter_entry_points('flaschengeist.plugin') - } - - for name in discovered_plugins: - logger.debug("Found plugin: %s", name) - if config.get(name, 'enabled', fallback=False): - logger.info('Loaded plugin > %s <', name) - app.register_blueprint(discovered_plugins[name]()) + for entry_point in pkg_resources.iter_entry_points('flaschengeist.plugin'): + logger.debug("Found plugin: %s", entry_point.name) + if config.get(entry_point.name, 'enabled', fallback=False): + logger.info('Loaded plugin > %s <', entry_point.name) + app.register_blueprint(entry_point.load()()) return app #app.register_blueprint(baruser) diff --git a/flaschengeist/system/controller/accesTokenController.py b/flaschengeist/system/controller/accessTokenController.py similarity index 84% rename from flaschengeist/system/controller/accesTokenController.py rename to flaschengeist/system/controller/accessTokenController.py index c89b478..15b503d 100644 --- a/flaschengeist/system/controller/accesTokenController.py +++ b/flaschengeist/system/controller/accessTokenController.py @@ -4,12 +4,12 @@ from flaschengeist.system.database import db from datetime import datetime, timedelta import secrets from . import Singleton -from flask import Blueprint, request, jsonify import logging logger = logging.getLogger("flaschenpost") -class AccesTokenController(metaclass=Singleton): + +class AccessTokenController(metaclass=Singleton): """ Control all createt AccesToken This Class create, delete, find and manage AccesToken. @@ -29,7 +29,7 @@ class AccesTokenController(metaclass=Singleton): logger.debug("init accesstoken controller") self.lifetime = lifetime - def validateAccessToken(self, token, group): + def validateAccessToken(self, token, roles): """ Verify Accestoken Verify an Accestoken and Group so if the User has permission or not. @@ -37,7 +37,7 @@ class AccesTokenController(metaclass=Singleton): Args: token: Token to verify. - group: Group like 'moneymaster', 'gastro', 'user' or 'bar' + roles: Roles needed to access restricted routes Returns: An the AccesToken for this given Token or False. """ @@ -48,14 +48,9 @@ class AccesTokenController(metaclass=Singleton): logger.debug("now is {{ {} }}, endtime is {{ {} }}".format(now, endTime)) if now <= endTime: logger.debug("check if token {{ {} }} is same as {{ {} }}".format(token, accToken)) - if accToken == token: - # if not self.checkBar(accToken.user): - # accToken.lock_bar = False - # logger.debug("check if accestoken {{ {} }} has group {{ {} }}".format(accToken, group)) - # if self.isSameGroup(accToken, group): + if not roles or (roles and self.userHasRole(accToken.user, roles)): accToken.updateTimestamp() db.session.commit() - # logger.debug("found accesstoken {{ {} }} with token: {{ {} }} and group: {{ {} }}".format(accToken, token, group)) return accToken else: logger.debug("accesstoken is {{ {} }} out of date".format(accToken)) @@ -64,6 +59,13 @@ class AccesTokenController(metaclass=Singleton): logger.debug("no valid accesstoken with token: {{ {} }} and group: {{ {} }}".format(token, group)) return False + def userHasRole(self, user, roles): + for group in user.groups: + for role in group.roles: + if role.name in roles: + return True + return False + def createAccesToken(self, user, user_agent=None): """ Create an AccessToken diff --git a/flaschengeist/system/decorator.py b/flaschengeist/system/decorator.py index 4c67c0f..26a1e96 100644 --- a/flaschengeist/system/decorator.py +++ b/flaschengeist/system/decorator.py @@ -3,27 +3,22 @@ from flask import current_app, request, jsonify from flaschengeist import logger def login_required(**kwargs): - from .controller.accesTokenController import AccesTokenController - accessController = AccesTokenController() - #if "groups" in kwargs: - # groups = kwargs["groups"] - #if "bar" in kwargs: - # bar = kwargs["bar"] - #logger.debug("groups are {{ {} }}".format(groups)) + from .controller.accessTokenController import AccessTokenController + accessController = AccessTokenController() + roles = None + if "roles" in kwargs: + roles = kwargs["roles"] def real_decorator(func): @wraps(func) def wrapper(*args, **kwargs): token = request.headers.get('Token') logger.debug("token is {{ {} }}".format(token)) - accToken = accessController.validateAccessToken(token, None) + accToken = accessController.validateAccessToken(token, roles) logger.debug("accToken is {{ {} }}".format(accToken)) kwargs['accToken'] = accToken if accToken: logger.debug("token {{ {} }} is valid".format(token)) - # if accToken.lock_bar and not bar: - # return jsonify({"error": "error", - # "message": "permission forbidden"}), 403 return func(*args, **kwargs) else: logger.warning("token {{ {} }} is not valid".format(token)) diff --git a/flaschengeist/system/models/user.py b/flaschengeist/system/models/user.py index f517ae4..f462595 100644 --- a/flaschengeist/system/models/user.py +++ b/flaschengeist/system/models/user.py @@ -18,7 +18,7 @@ class User(db.Model): Attributes: id: Id in Database as Primary Key. - uid: User ID used by authentification provider + uid: User ID used by authentication provider displayname: Name to show firstname: Firstname of the User lastname: Lastname of the User @@ -31,7 +31,7 @@ class User(db.Model): firstname = db.Column(db.String(30)) lastname = db.Column(db.String(30)) mail = db.Column(db.String(30)) - groups = db.relationship("UserGroup", secondary=association_table) + groups = db.relationship("Group", secondary=association_table) sessions = db.relationship("AccessToken", back_populates="user") attributes = db.relationship("UserAttribute", collection_class=attribute_mapped_collection('name'), cascade="all, delete") @@ -42,9 +42,9 @@ class User(db.Model): self.attributes[name] = UserAttribute(name=name, value=value) def addGroup(self, name): - r = UserGroup.query.filter_by(name=name).first() + r = Group.query.filter_by(name=name).first() if not r: - r = UserGroup(name=name) + r = Group(name=name) self.groups.append(r) def updateData(self, data): @@ -79,13 +79,25 @@ class UserAttribute(db.Model): name = db.Column(db.String(30)) value = db.Column(db.String(192)) -class UserGroup(db.Model): +group_permission_association_table = db.Table('group_permission', + db.Column('group_id', db.Integer, db.ForeignKey('group.id')), + db.Column('permission_id', db.Integer, db.ForeignKey('permission.id')) +) + + +class Group(db.Model): __tablename__ = 'group' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(30)) + permissions = db.relationship("Permission", secondary=group_permission_association_table) def toJSON(self): return { 'name': self.name } + +class Permission(db.Model): + __tablename__ = 'permission' + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(30))