diff --git a/pillar/api/organizations/__init__.py b/pillar/api/organizations/__init__.py index 2b6ffaf7..1bbe8e2e 100644 --- a/pillar/api/organizations/__init__.py +++ b/pillar/api/organizations/__init__.py @@ -4,7 +4,6 @@ Assumes role names that are given to users by organization membership start with the string "org-". """ -import enum import logging import typing @@ -231,6 +230,20 @@ class OrgManager: raise ValueError(f'Organization {org_id} not found') return org + def refresh_all_user_roles(self, org_id: bson.ObjectId): + """Refreshes the roles of all members.""" + + assert isinstance(org_id, bson.ObjectId) + + org = self._get_org(org_id, projection={'members': 1}) + members = org.get('members') + if not members: + self._log.info('Organization %s has no members, nothing to refresh.', org_id) + return + + for uid in members: + self.refresh_roles(uid) + def refresh_roles(self, user_id: bson.ObjectId): """Refreshes the user's roles to own roles + organizations' roles.""" @@ -238,6 +251,8 @@ class OrgManager: from pillar.api.service import do_badger + self._log.info('Refreshing roles for user %s', user_id) + org_coll = current_app.db('organizations') # Aggregate all org-given roles for this user. diff --git a/pillar/api/organizations/patch.py b/pillar/api/organizations/patch.py index 27fd790e..100a00b8 100644 --- a/pillar/api/organizations/patch.py +++ b/pillar/api/organizations/patch.py @@ -136,6 +136,7 @@ class OrganizationPatchHandler(patch_handler.AbstractPatchHandler): 'location': patch.get('location', '').strip(), } + refresh_user_roles = False if user.has_cap('admin'): if 'seat_count' in patch: update['seat_count'] = int(patch['seat_count']) @@ -147,6 +148,7 @@ class OrganizationPatchHandler(patch_handler.AbstractPatchHandler): 'Invalid role given, all roles must start with "org-"') update['org_roles'] = org_roles + refresh_user_roles = True self.log.info('User %s edits Organization %s: %s', current_user_id, org_id, update) @@ -171,6 +173,10 @@ class OrganizationPatchHandler(patch_handler.AbstractPatchHandler): current_user_id, org_id, result.matched_count) raise wz_exceptions.BadRequest() + if refresh_user_roles: + self.log.info('Organization roles set for org %s, refreshing users', org_id) + current_app.org_manager.refresh_all_user_roles(org_id) + return '', 204 diff --git a/tests/test_api/test_organizations.py b/tests/test_api/test_organizations.py index 178ee830..748099ae 100644 --- a/tests/test_api/test_organizations.py +++ b/tests/test_api/test_organizations.py @@ -357,6 +357,36 @@ class OrganizationPatchTest(AbstractPillarTest): self.assertEqual('Open Source animation studio', db_org['description']) self.assertEqual('https://blender.institute/', db_org['website']) + def test_change_roles(self): + self.enter_app_context() + om = self.app.org_manager + + self.create_user(24 * '1', roles={'admin'}, token='uberadmin') + + admin_uid = self.create_user(24 * 'a', token='admin-token') + org_doc = om.create_new_org('Хакеры', admin_uid, 25, org_roles={'org-subscriber'}) + org_id = org_doc['_id'] + + # First assign the user, then change the organization's roles. + # This should refresh the roles on all its members. + member1_uid = self.create_user(24 * 'b', email='member1@example.com', roles={'betatester'}) + om.assign_single_user(org_id, user_id=member1_uid) + + # Try the PATCH to change the roles + self.patch(f'/api/organizations/{org_id}', + json={ + 'op': 'edit-from-web', + 'name': ' Blender Institute ', + 'description': '\nOpen Source animation studio ', + 'website': ' https://blender.institute/ ', + 'org_roles': ['org-subscriber', 'org-flamenco'], + }, + auth_token='uberadmin', + expected_status=204) + + db_user = self.fetch_user_from_db(member1_uid) + self.assertEqual({'betatester', 'org-subscriber', 'org-flamenco'}, set(db_user['roles'])) + def test_assign_admin(self): self.enter_app_context() om = self.app.org_manager