diff --git a/api/dao/hierarchy.py b/api/dao/hierarchy.py index 44dd38865..2ba1b7499 100644 --- a/api/dao/hierarchy.py +++ b/api/dao/hierarchy.py @@ -139,6 +139,7 @@ def propagate_changes(cont_name, _id, query, update): cont_name and _id refer to top level container (which will not be modified here) """ + if cont_name == 'groups': project_ids = [p['_id'] for p in config.db.projects.find({'group': _id}, [])] session_ids = [s['_id'] for s in config.db.sessions.find({'project': {'$in': project_ids}}, [])] @@ -154,6 +155,8 @@ def propagate_changes(cont_name, _id, query, update): config.db.sessions.update_many(session_q, update) config.db.acquisitions.update_many(acquisition_q, update) + + # Apply change to projects elif cont_name == 'projects': session_ids = [s['_id'] for s in config.db.sessions.find({'project': _id}, [])] diff --git a/api/handlers/listhandler.py b/api/handlers/listhandler.py index f488dae3f..282fb347b 100644 --- a/api/handlers/listhandler.py +++ b/api/handlers/listhandler.py @@ -185,7 +185,7 @@ def _initialize_request(self, cont_name, list_name, _id, query_params=None): query_params = None container = storage.get_container(_id, query_params) if container is not None: - if self.superuser_request: + if self.superuser_request or self.user_is_admin: permchecker = always_ok elif self.public_request: permchecker = listauth.public_request(self, container) @@ -209,35 +209,57 @@ class PermissionsListHandler(ListHandler): def post(self, cont_name, list_name, **kwargs): _id = kwargs.get('cid') result = super(PermissionsListHandler, self).post(cont_name, list_name, **kwargs) - self._propagate_project_permissions(cont_name, _id) + payload = self.request.json_body + + if cont_name == 'groups' and self.request.params.get('propagate') =='true': + self._propagate_permissions(cont_name, _id, query={'permissions._id' : payload['_id']}, update={'$set': {'permissions.$.access': payload['access']}}) + self._propagate_permissions(cont_name, _id, query={'permissions._id': {'$ne': payload['_id']}}, update={'$addToSet': {'permissions': payload}}) + elif cont_name != 'groups': + self._propagate_permissions(cont_name, _id) return result def put(self, cont_name, list_name, **kwargs): _id = kwargs.get('cid') result = super(PermissionsListHandler, self).put(cont_name, list_name, **kwargs) - self._propagate_project_permissions(cont_name, _id) + payload = self.request.json_body + payload['_id'] = kwargs.get('_id') + if cont_name == 'groups' and self.request.params.get('propagate') =='true': + self._propagate_permissions(cont_name, _id, query={'permissions._id' : payload['_id']}, update={'$set': {'permissions.$.access': payload['access']}}) + elif cont_name != 'groups': + self._propagate_permissions(cont_name, _id) return result def delete(self, cont_name, list_name, **kwargs): _id = kwargs.get('cid') result = super(PermissionsListHandler, self).delete(cont_name, list_name, **kwargs) - self._propagate_project_permissions(cont_name, _id) + + if cont_name == 'groups' and self.request.params.get('propagate') =='true': + self._propagate_permissions(cont_name, _id, query={'permissions._id' : kwargs.get('_id')}, update={'$pull' : {'permissions': {'_id': kwargs.get('_id')}}}) + elif cont_name != 'groups': + self._propagate_permissions(cont_name, _id) return result - def _propagate_project_permissions(self, cont_name, _id): + def _propagate_permissions(self, cont_name, _id, query=None, update=None): """ - method to propagate permissions from a project to its sessions and acquisitions + method to propagate permissions from a container/group to its sessions and acquisitions """ - if cont_name == 'projects': + if query is None: + query = {} + if cont_name == 'groups': + try: + hierarchy.propagate_changes(cont_name, _id, query, update) + except APIStorageException as e: + self.abort(400, e.message) + elif cont_name == 'projects': try: oid = bson.ObjectId(_id) update = {'$set': { - 'permissions': config.db.projects.find_one({'_id': oid},{'permissions': 1})['permissions'] + 'permissions': config.db[cont_name].find_one({'_id': oid},{'permissions': 1})['permissions'] }} hierarchy.propagate_changes(cont_name, oid, {}, update) except APIStorageException: - self.abort(500, 'permissions not propagated from project {} to sessions'.format(_id)) + self.abort(500, 'permissions not propagated from {} {} down hierarchy'.format(cont_name, _id)) class NotesListHandler(ListHandler): diff --git a/test/integration_tests/python/test_propagation.py b/test/integration_tests/python/test_propagation.py index 67fa27715..735cdb9bb 100644 --- a/test/integration_tests/python/test_propagation.py +++ b/test/integration_tests/python/test_propagation.py @@ -185,6 +185,102 @@ def get_user_in_perms(perms, uid): user = get_user_in_perms(perms, user_id) assert r.ok and user is None +# Test group permission propagation +def test_add_and_remove_user_group_permission(data_builder, as_admin): + """ + Tests: + - changing permissions at a group level with flag triggers propagation + - additive change to list propagates properly + - change to list propagates properly + - removal from list propagates properly + """ + def get_user_in_perms(perms, uid): + for perm in perms: + if perm['_id'] == uid: + return perm + return None + + group = data_builder.create_group() + project = data_builder.create_project() + session = data_builder.create_session() + acquisition = data_builder.create_acquisition() + + user_id = 'propagation@user.com' + + # Add user to group permissions + payload = {'_id': user_id, 'access': 'admin'} + r = as_admin.post('/groups/' + group + '/permissions', json=payload, params={'propagate': 'true'}) + assert r.ok + + r = as_admin.get('/groups/' + group) + perms = r.json()['permissions'] + user = get_user_in_perms(perms, user_id) + assert r.ok and user + + r = as_admin.get('/projects/' + project) + perms = r.json()['permissions'] + user = get_user_in_perms(perms, user_id) + assert r.json()['group'] == group + assert r.ok and user + + r = as_admin.get('/sessions/' + session) + perms = r.json()['permissions'] + user = get_user_in_perms(perms, user_id) + assert r.ok and user + + r = as_admin.get('/acquisitions/' + acquisition) + perms = r.json()['permissions'] + user = get_user_in_perms(perms, user_id) + assert r.ok and user + + # Modify user permissions + payload = {'access': 'rw', '_id': user_id} + r = as_admin.put('/groups/' + group + '/permissions/' + user_id, json=payload, params={'propagate': 'true'}) + assert r.ok + + r = as_admin.get('/groups/' + group) + perms = r.json()['permissions'] + user = get_user_in_perms(perms, user_id) + assert r.ok and user and user['access'] == 'rw' + + r = as_admin.get('/projects/' + project) + perms = r.json()['permissions'] + user = get_user_in_perms(perms, user_id) + assert r.ok and user and user['access'] == 'rw' + + r = as_admin.get('/sessions/' + session) + perms = r.json()['permissions'] + user = get_user_in_perms(perms, user_id) + assert r.ok and user and user['access'] == 'rw' + + r = as_admin.get('/acquisitions/' + acquisition) + perms = r.json()['permissions'] + user = get_user_in_perms(perms, user_id) + assert r.ok and user and user['access'] == 'rw' + + # Remove user from project permissions + r = as_admin.delete('/groups/' + group + '/permissions/' + user_id, json=payload, params={'propagate': 'true'}) + assert r.ok + + r = as_admin.get('/groups/' + group) + perms = r.json()['permissions'] + user = get_user_in_perms(perms, user_id) + assert r.ok and user is None + + r = as_admin.get('/projects/' + project) + perms = r.json()['permissions'] + user = get_user_in_perms(perms, user_id) + assert r.ok and user is None + + r = as_admin.get('/sessions/' + session) + perms = r.json()['permissions'] + user = get_user_in_perms(perms, user_id) + assert r.ok and user is None + + r = as_admin.get('/acquisitions/' + acquisition) + perms = r.json()['permissions'] + user = get_user_in_perms(perms, user_id) + assert r.ok and user is None # Test tag pool renaming and deletion def test_add_rename_remove_group_tag(data_builder, as_admin):