diff --git a/attract/__init__.py b/attract/__init__.py index 3fb6f7a..6fe0ef0 100644 --- a/attract/__init__.py +++ b/attract/__init__.py @@ -15,15 +15,20 @@ import attract.shots_and_assets EXTENSION_NAME = 'attract' -# Roles required to view task or shot details. -ROLES_REQUIRED_TO_VIEW_ITEMS = {'demo', 'subscriber', 'admin'} - class AttractExtension(PillarExtension): has_project_settings = True user_roles = {'attract-user'} user_roles_indexable = {'attract-user'} + user_caps = { + 'attract-user': {'attract-view', 'attract-use'}, + 'org-attract': {'attract-view', 'attract-use'}, + 'subscriber': {'attract-view'}, + 'demo': {'attract-view'}, + 'admin': {'attract-view', 'attract-use'}, + } + def __init__(self): self._log = logging.getLogger('%s.AttractExtension' % __name__) self.task_manager = attract.tasks.TaskManager() diff --git a/attract/auth.py b/attract/auth.py index 5b1fdfc..24a017f 100644 --- a/attract/auth.py +++ b/attract/auth.py @@ -6,10 +6,6 @@ import bson from pillar import attrs_extra -# Having either of these roles is minimum requirement for using Attract. -ROLES_REQUIRED_TO_USE_ATTRACT = {'attract-user', 'admin'} -ROLES_REQUIRED_TO_VIEW_ATTRACT = {'admin', 'subscriber', 'demo'} - # Having any of these methods on a project means you can use Attract. # Prerequisite: the project is set up for Attract and has a Manager assigned to it. PROJECT_METHODS_TO_USE_ATTRACT = {'PUT'} @@ -20,10 +16,10 @@ class Actions(enum.Enum): USE = 'use' -# Required roles for a given action. -req_roles = { - Actions.VIEW: ROLES_REQUIRED_TO_VIEW_ATTRACT, - Actions.USE: ROLES_REQUIRED_TO_USE_ATTRACT, +# Required capability for a given action. +req_cap = { + Actions.VIEW: 'attract-view', + Actions.USE: 'attract-use', } @@ -45,23 +41,21 @@ class Auth(object): """Returns True iff the user has Attract User role.""" from pillar import current_app + from pillar.auth import UserClass assert isinstance(user_id, bson.ObjectId) # TODO: move role checking code to Pillar. users_coll = current_app.db('users') - user = users_coll.find_one({'_id': user_id}, {'roles': 1}) - if not user: + db_user = users_coll.find_one({'_id': user_id}, {'roles': 1}) + if not db_user: self._log.debug('user_is_attract_user: User %s not found', user_id) return False - user_roles = set(user.get('roles', [])) - require_roles = set(ROLES_REQUIRED_TO_USE_ATTRACT) + user = UserClass.construct('', db_user) + return user.has_cap('attract-use') - intersection = require_roles.intersection(user_roles) - return bool(intersection) - - def current_user_may(self, action: Actions, project_id: bson.ObjectId=None) -> bool: + def current_user_may(self, action: Actions, project_id: bson.ObjectId = None) -> bool: """Returns True iff the user is authorised to use/view Attract on the current project. Requires that determine_user_rights() was called before. @@ -86,20 +80,18 @@ class Auth(object): g.attract_rights is a frozenset that contains zero or more Actions. """ - from pillar.api.utils.authorization import user_matches_roles - from pillar.api.utils.authentication import current_user_id + from pillar.auth import current_user from pillar.api.projects.utils import user_rights_in_project - user_id = current_user_id() - if not user_id: + if current_user.is_anonymous: self._log.debug('Anonymous user never has access to Attract.') flask.g.attract_rights = frozenset() return rights = set() for action in Actions: - roles = req_roles[action] - if user_matches_roles(roles): + cap = req_cap[action] + if current_user.has_cap(cap): rights.add(action) # TODO Sybren: possibly split this up into a manager-fetching func + authorisation func. diff --git a/attract/routes.py b/attract/routes.py index 08de4e0..7b6517a 100644 --- a/attract/routes.py +++ b/attract/routes.py @@ -5,7 +5,7 @@ from flask import Blueprint, render_template, redirect, url_for, request, jsonif import flask_login import werkzeug.exceptions as wz_exceptions -from pillar.auth import current_web_user as current_user +from pillar.auth import current_user as current_user from pillar.api.utils import str2id from pillar.web.utils import attach_project_pictures import pillar.web.subquery diff --git a/attract/shots_and_assets/routes_common.py b/attract/shots_and_assets/routes_common.py index 9f37377..68b812f 100644 --- a/attract/shots_and_assets/routes_common.py +++ b/attract/shots_and_assets/routes_common.py @@ -1,14 +1,14 @@ import logging import flask -import flask_login import werkzeug.exceptions as wz_exceptions import pillarsdk from pillar.web.system_util import pillar_api from pillar.web.utils import get_file +from pillar.auth import current_user -from attract import current_attract, ROLES_REQUIRED_TO_VIEW_ITEMS +from attract import current_attract log = logging.getLogger(__name__) @@ -53,12 +53,10 @@ def for_project(node_type_name, task_types_for_nt, project, attract_props, def view_node(project, node_id, node_type_name): """Returns the node if the user has access. - - Uses attract.ROLES_REQUIRED_TO_VIEW_ITEMS to check permissions. """ # asset list is public, asset details are not. - if not flask_login.current_user.has_role(*ROLES_REQUIRED_TO_VIEW_ITEMS): + if not current_user.has_cap('attract-view'): raise wz_exceptions.Forbidden() api = pillar_api() diff --git a/attract/tasks/routes.py b/attract/tasks/routes.py index 801b4c4..d675658 100644 --- a/attract/tasks/routes.py +++ b/attract/tasks/routes.py @@ -10,11 +10,12 @@ import pillarsdk from pillar.web.system_util import pillar_api import pillar.api.utils import pillar.web.subquery +from pillar.auth import current_user from attract.routes import attract_project_view from attract.node_types.task import node_type_task from attract.node_types.shot import node_type_shot -from attract import current_attract, ROLES_REQUIRED_TO_VIEW_ITEMS, EXTENSION_NAME +from attract import current_attract, EXTENSION_NAME blueprint = Blueprint('attract.tasks', __name__, url_prefix='/tasks') perproject_blueprint = Blueprint('attract.tasks.perproject', __name__, @@ -65,7 +66,7 @@ def view_task(project, attract_props, task_id): return for_project(project, task_id=task_id) # Task list is public, task details are not. - if not flask_login.current_user.has_role(*ROLES_REQUIRED_TO_VIEW_ITEMS): + if not current_user.has_cap('attract-view'): raise wz_exceptions.Forbidden() api = pillar_api()