From 0a9f0ebddb90cb43680c587451f486eae4123b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 11 Jul 2017 12:40:13 +0200 Subject: [PATCH] Roles '{flamenco,attract}-user' are now linked to 'subscriber' All users who get 'subscriber' role automatically get 'flamenco-user' and 'attract-user', and when 'subscriber' is revoked, so are '{flamenco,attract}-user'. --- cloud/__init__.py | 40 ++++++++++++++++++++++++++++++++++++ setup.cfg | 5 +++++ tests/abstract_cloud_test.py | 24 ++++++++++++++++++++++ tests/test_linked_roles.py | 17 +++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 setup.cfg create mode 100644 tests/abstract_cloud_test.py create mode 100644 tests/test_linked_roles.py diff --git a/cloud/__init__.py b/cloud/__init__.py index 3fe2ff2..773a578 100644 --- a/cloud/__init__.py +++ b/cloud/__init__.py @@ -75,6 +75,46 @@ class CloudExtension(PillarExtension): 'current_user_is_subscriber': authorization.user_matches_roles(ROLES_TO_BE_SUBSCRIBER) } + def setup_app(self, app): + """Links certain roles to the subscriber role. + + This means that users who get the subscriber role also get this linked + role, and when the subscriber role is revoked, the linked role is also + revoked. + """ + + from pillar.api.service import signal_user_changed_role + + signal_user_changed_role.connect(self._user_changed_role) + + def _user_changed_role(self, sender, user: dict): + from pillar.api import service + + linked_roles = {'flamenco-user', 'attract-user'} + link_to = 'subscriber' + user_roles = set(user.get('roles', [])) + + # Determine what to do + has_linked_roles = not (linked_roles - user_roles) + action = '' + if link_to in user_roles and not has_linked_roles: + self._log.info('Granting roles %s to user %s', linked_roles, user['_id']) + action = 'grant' + elif link_to not in user_roles and has_linked_roles: + self._log.info('Revoking roles %s from user %s', linked_roles, user['_id']) + action = 'revoke' + + if not action: + return + + # Avoid infinite loops while we're changing the user's roles. + service.signal_user_changed_role.disconnect(self._user_changed_role) + try: + for role in linked_roles: + service.do_badger(action, role, user_id=user['_id']) + finally: + service.signal_user_changed_role.connect(self._user_changed_role) + def _get_current_cloud(): """Returns the Cloud extension of the current application.""" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..1c4c495 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[tool:pytest] +addopts = -v --cov cloud --cov-report term-missing --ignore node_modules --ignore docker + +[pep8] +max-line-length = 100 diff --git a/tests/abstract_cloud_test.py b/tests/abstract_cloud_test.py new file mode 100644 index 0000000..beee4f7 --- /dev/null +++ b/tests/abstract_cloud_test.py @@ -0,0 +1,24 @@ +from pillar.tests import PillarTestServer, AbstractPillarTest + + +class CloudTestServer(PillarTestServer): + def __init__(self, *args, **kwargs): + PillarTestServer.__init__(self, *args, **kwargs) + + from flamenco import FlamencoExtension + from attract import AttractExtension + from cloud import CloudExtension + + self.load_extension(FlamencoExtension(), '/flamenco') + self.load_extension(AttractExtension(), '/attract') + self.load_extension(CloudExtension(), None) + + +class AbstractCloudTest(AbstractPillarTest): + pillar_server_class = CloudTestServer + + def tearDown(self): + self.unload_modules('cloud') + self.unload_modules('attract') + self.unload_modules('flamenco') + AbstractPillarTest.tearDown(self) diff --git a/tests/test_linked_roles.py b/tests/test_linked_roles.py new file mode 100644 index 0000000..8994c6a --- /dev/null +++ b/tests/test_linked_roles.py @@ -0,0 +1,17 @@ +from abstract_cloud_test import AbstractCloudTest + + +class LinkedRolesTest(AbstractCloudTest): + def test_linked_roles(self): + user_id = self.create_user(roles=[]) + db_user = self.fetch_user_from_db(user_id) + + self.badger(db_user['email'], {'subscriber'}, 'grant') + db_user = self.fetch_user_from_db(user_id) + self.assertEqual({'subscriber', 'flamenco-user', 'attract-user'}, + set(db_user['roles'])) + + self.badger(db_user['email'], {'subscriber'}, 'revoke') + db_user = self.fetch_user_from_db(user_id) + self.assertEqual(set(), + set(db_user.get('roles', [])))