diff --git a/api/auth/__init__.py b/api/auth/__init__.py index d2e185b95..ba9a0d651 100644 --- a/api/auth/__init__.py +++ b/api/auth/__init__.py @@ -28,6 +28,13 @@ def _get_access(uid, container): def has_access(uid, container, perm): return _get_access(uid, container) >= INTEGER_PERMISSIONS[perm] +# Returns true if user has phi access +def check_phi(uid, container): + permissions_list = container.get('permissions', []) + for perm in permissions_list: + if perm['_id'] == uid and perm.get('phi-access'): + return has_access(uid, container, 'ro') + return False def always_ok(exec_op): """ diff --git a/api/auth/containerauth.py b/api/auth/containerauth.py index b6265bc2b..05a8e6c0c 100644 --- a/api/auth/containerauth.py +++ b/api/auth/containerauth.py @@ -1,8 +1,10 @@ """ Purpose of this module is to define all the permissions checker decorators for the ContainerHandler classes. """ - -from . import _get_access, INTEGER_PERMISSIONS +from copy import deepcopy +from . import _get_access, check_phi, INTEGER_PERMISSIONS +from .. import config +log = config.log def default_container(handler, container=None, target_parent_container=None): @@ -12,8 +14,7 @@ def default_container(handler, container=None, target_parent_container=None): on the container before actually executing this method. """ def g(exec_op): - def f(method, _id=None, payload=None, unset_payload=None, recursive=False, r_payload=None, replace_metadata=False): - projection = None + def f(method, _id=None, payload=None, unset_payload=None, recursive=False, r_payload=None, projection=None, replace_metadata=False, phi=False): additional_error_msg = None if method == 'GET' and container.get('public', False): has_access = True @@ -46,6 +47,10 @@ def f(method, _id=None, payload=None, unset_payload=None, recursive=False, r_pay else: has_access = False + if method == 'GET' and phi: + if not check_phi(handler.uid, container): + handler.abort(403, "User not authorized to view PHI fields on the container.") + if has_access: return exec_op(method, _id=_id, payload=payload, unset_payload=unset_payload, recursive=recursive, r_payload=r_payload, replace_metadata=replace_metadata, projection=projection) else: @@ -63,7 +68,7 @@ def collection_permissions(handler, container=None, _=None): Permissions are checked on the collection itself or not at all if the collection is new. """ def g(exec_op): - def f(method, _id=None, payload = None): + def f(method, _id=None, payload = None, projection=None, phi=False): if method == 'GET' and container.get('public', False): has_access = True elif method == 'GET': @@ -77,8 +82,12 @@ def f(method, _id=None, payload = None): else: has_access = False + if method == 'GET' and phi: + if not check_phi(handler.uid, container): + handler.abort(403, "User not authorized to view PHI fields.") + if has_access: - return exec_op(method, _id=_id, payload=payload) + return exec_op(method, _id=_id, payload=payload, projection=projection) else: handler.abort(403, 'user not authorized to perform a {} operation on the container'.format(method)) return f @@ -87,7 +96,7 @@ def f(method, _id=None, payload = None): def default_referer(handler, parent_container=None): def g(exec_op): - def f(method, _id=None, payload=None): + def f(method, _id=None, payload=None, projection = None, phi=False): access = _get_access(handler.uid, parent_container) if method == 'GET' and parent_container.get('public', False): has_access = True @@ -96,10 +105,14 @@ def f(method, _id=None, payload=None): elif method in ['POST', 'PUT', 'DELETE']: has_access = access >= INTEGER_PERMISSIONS['rw'] else: - has_access = False + has_access = False + + if method == 'GET' and phi: + if not check_phi(handler.uid, parent_container): + handler.abort(403, "User not authorized to view PHI fields.") if has_access: - return exec_op(method, _id=_id, payload=payload) + return exec_op(method, _id=_id, payload=payload, projection=projection) else: handler.abort(403, 'user not authorized to perform a {} operation on parent container'.format(method)) return f @@ -111,9 +124,11 @@ def public_request(handler, container=None): For public requests we allow only GET operations on containers marked as public. """ def g(exec_op): - def f(method, _id=None, payload = None): + def f(method, _id=None, payload = None, phi=False, projection=None): + if phi: + handler.abort(403, "Must be logged in to view PHI fields.") if method == 'GET' and container.get('public', False): - return exec_op(method, _id, payload) + return exec_op(method, _id, payload=payload, projection=projection) else: handler.abort(403, 'not authorized to perform a {} operation on this container'.format(method)) return f @@ -121,20 +136,30 @@ def f(method, _id=None, payload = None): def list_permission_checker(handler): def g(exec_op): - def f(method, query=None, user=None, public=False, projection=None): + def f(method, query=None, user=None, public=False, projection=None, phi=False): if user and (user['_id'] != handler.uid): handler.abort(403, 'User ' + handler.uid + ' may not see the Projects of User ' + user['_id']) query['permissions'] = {'$elemMatch': {'_id': handler.uid}} if handler.is_true('public'): query['$or'] = [{'public': True}, {'permissions': query.pop('permissions')}] + if phi: + temp_query = deepcopy(query) + temp_query['permissions'] = {'$elemMatch': {'_id': handler.uid, 'phi-access': False}} + log.debug(temp_query) + log.debug(query) + not_allowed = exec_op(method, query=temp_query, user=user, public=public, projection=projection) + if not_allowed: + handler.abort(403, "User does not have PHI access to one or more elements") + return exec_op(method, query=query, user=user, public=public, projection=projection) return f return g def list_public_request(exec_op): - def f(method, query=None, user=None, public=False, projection=None): + def f(method, query=None, user=None, public=False, projection=None, phi=False): # pylint: disable=unused-argument if public: query['public'] = True return exec_op(method, query=query, user=user, public=public, projection=projection) return f + diff --git a/api/dao/basecontainerstorage.py b/api/dao/basecontainerstorage.py index 3f6a610b1..0785fbcb7 100644 --- a/api/dao/basecontainerstorage.py +++ b/api/dao/basecontainerstorage.py @@ -129,11 +129,10 @@ def _to_mongo(self, payload): def exec_op(self, action, _id=None, payload=None, query=None, user=None, public=False, projection=None, recursive=False, r_payload=None, # pylint: disable=unused-argument - replace_metadata=False, unset_payload=None): + replace_metadata=False, unset_payload=None, phi=False): # pylint: disable=unused-argument """ Generic method to exec a CRUD operation from a REST verb. """ - check = consistencychecker.get_container_storage_checker(action, self.cont_name) data_op = payload or {'_id': _id} check(data_op) diff --git a/api/dao/containerstorage.py b/api/dao/containerstorage.py index 3c4961b8d..7566fb32a 100644 --- a/api/dao/containerstorage.py +++ b/api/dao/containerstorage.py @@ -315,10 +315,10 @@ def get_parent(self, parent_type, parent_id): return parent_storage.get_container(parent_id) - def get_analyses(self, parent_type, parent_id, inflate_job_info=False): + def get_analyses(self, parent_type, parent_id, inflate_job_info=False, projection=None): parent_type = containerutil.singularize(parent_type) parent_id = bson.ObjectId(parent_id) - analyses = self.get_all_el({'parent.type': parent_type, 'parent.id': parent_id}, None, None) + analyses = self.get_all_el({'parent.type': parent_type, 'parent.id': parent_id}, None, projection) if inflate_job_info: for analysis in analyses: self.inflate_job_info(analysis) diff --git a/api/handlers/collectionshandler.py b/api/handlers/collectionshandler.py index 39b07a08f..7bafdba59 100644 --- a/api/handlers/collectionshandler.py +++ b/api/handlers/collectionshandler.py @@ -6,6 +6,7 @@ from ..dao import containerstorage, containerutil, noop from ..web.errors import APIStorageException from ..validators import verify_payload_exists +from ..web.request import AccessType from .containerhandler import ContainerHandler @@ -31,7 +32,7 @@ def __init__(self, request=None, response=None): self.storage = self.container_handler_configurations['collections']['storage'] def get(self, **kwargs): - return super(CollectionsHandler, self).get('collections', **kwargs) + return super(CollectionsHandler, self).get(cont_name='collections', **kwargs) def post(self): mongo_validator, payload_validator = self._get_validators() @@ -40,7 +41,8 @@ def post(self): payload_validator(payload, 'POST') payload['permissions'] = [{ '_id': self.uid, - 'access': 'admin' + 'access': 'admin', + 'phi-access': True }] payload['curator'] = self.uid payload['created'] = payload['modified'] = datetime.datetime.utcnow() @@ -101,7 +103,7 @@ def delete(self, **kwargs): config.db.acquisitions.update_many({'collections': bson.ObjectId(_id)}, {'$pull': {'collections': bson.ObjectId(_id)}}) def get_all(self): - projection = self.container_handler_configurations['collections']['list_projection'] + projection = None if self.superuser_request: permchecker = always_ok elif self.public_request: @@ -109,7 +111,14 @@ def get_all(self): else: permchecker = containerauth.list_permission_checker(self) query = {} - results = permchecker(self.storage.exec_op)('GET', query=query, public=self.public_request, projection=projection) + if self.is_true('phi'): + projection = None + phi = True + else: + phi = False + projection = {'info': 0, 'tags': 0, 'files.info':0} + results = permchecker(self.storage.exec_op)('GET', query=query, public=self.public_request, projection=projection, phi=phi) + log.debug(results) if not self.superuser_request and not self.is_true('join_avatars'): self._filter_all_permissions(results, self.uid) if self.is_true('join_avatars'): @@ -117,6 +126,8 @@ def get_all(self): for result in results: if self.is_true('stats'): result = containerutil.get_stats(result, 'collections') + if phi: + self.log_user_access(AccessType.view_container, cont_name='collections', cont_id=result.get('_id')) return results def curators(self): @@ -148,13 +159,23 @@ def get_sessions(self, cid): if not self.is_true('archived'): query['archived'] = {'$ne': True} - if not self.superuser_request: - query['permissions._id'] = self.uid - projection = self.container_handler_configurations['sessions']['list_projection'] + if self.superuser_request: + permchecker = always_ok + elif self.public_request: + permchecker = containerauth.list_public_request + else: + permchecker = containerauth.list_permission_checker(self) - sessions = list(containerstorage.SessionStorage().get_all_el(query, None, projection)) + projection = self.PHI_FIELDS.copy() + if self.is_true('phi'): + projection = None + phi = True + else: + phi = False + sessions = permchecker(containerstorage.SessionStorage().exec_op)('GET', query=query, public=self.public_request, projection=projection, phi=phi) + log.debug(sessions) self._filter_all_permissions(sessions, self.uid) if self.is_true('measurements'): self._add_session_measurements(sessions) @@ -183,11 +204,21 @@ def get_acquisitions(self, cid): if not self.superuser_request: query['permissions._id'] = self.uid + if self.superuser_request: + permchecker = always_ok + elif self.public_request: + permchecker = containerauth.list_public_request + else: + permchecker = containerauth.list_permission_checker(self) - projection = self.container_handler_configurations['acquisitions']['list_projection'] - - acquisitions = list(containerstorage.AcquisitionStorage().get_all_el(query, None, projection)) + projection = self.PHI_FIELDS.copy() + if self.is_true('phi'): + projection = None + phi = True + else: + phi = False + acquisitions = permchecker(containerstorage.AcquisitionStorage().exec_op)('GET', query=query, public=self.public_request, projection=projection, phi=phi) self._filter_all_permissions(acquisitions, self.uid) for acquisition in acquisitions: diff --git a/api/handlers/containerhandler.py b/api/handlers/containerhandler.py index 22392b247..4825dcb09 100644 --- a/api/handlers/containerhandler.py +++ b/api/handlers/containerhandler.py @@ -5,7 +5,7 @@ from .. import config from .. import util from .. import validators -from ..auth import containerauth, always_ok +from ..auth import containerauth, always_ok, check_phi from ..dao import containerstorage, containerutil, noop from ..dao.containerstorage import AnalysisStorage from ..jobs.gears import get_gear @@ -42,7 +42,10 @@ class ContainerHandler(base.RequestHandler): 'sessions': True, 'acquisitions': True } - default_list_projection = ['files', 'notes', 'timestamp', 'timezone', 'public'] + + # Hard-coded PHI fields, will be changed to user set PHI fields + PHI_FIELDS = {'info': 0, 'subject.firstname':0, 'subject.lastname': 0, 'subject.sex': 0, + 'subject.age': 0, 'subject.race': 0, 'subject.ethnicity': 0, 'subject.info': 0, 'tags': 0, 'files.info':0} # This configurations are used by the ContainerHandler class to load the storage, # the permissions checker and the json schema validators used to handle a request. @@ -57,7 +60,6 @@ class ContainerHandler(base.RequestHandler): 'parent_storage': containerstorage.GroupStorage(), 'storage_schema_file': 'project.json', 'payload_schema_file': 'project.json', - 'list_projection': {'info': 0}, 'propagated_properties': ['archived', 'public'], 'children_cont': 'sessions' }, @@ -68,10 +70,6 @@ class ContainerHandler(base.RequestHandler): 'storage_schema_file': 'session.json', 'payload_schema_file': 'session.json', # Remove subject first/last from list view to better log access to this information - 'list_projection': {'info': 0, 'analyses': 0, 'subject.firstname': 0, - 'subject.lastname': 0, 'subject.sex': 0, 'subject.age': 0, - 'subject.race': 0, 'subject.ethnicity': 0, 'subject.info': 0, - 'files.info': 0, 'tags': 0}, 'propagated_properties': ['archived'], 'children_cont': 'acquisitions' }, @@ -80,27 +78,37 @@ class ContainerHandler(base.RequestHandler): 'permchecker': containerauth.default_container, 'parent_storage': containerstorage.SessionStorage(), 'storage_schema_file': 'acquisition.json', - 'payload_schema_file': 'acquisition.json', - 'list_projection': {'info': 0, 'collections': 0, 'files.info': 0, 'tags': 0} + 'payload_schema_file': 'acquisition.json' } } + def __init__(self, request=None, response=None): super(ContainerHandler, self).__init__(request, response) self.storage = None self.config = None + self.phi = False @log_access(AccessType.view_container) def get(self, cont_name, **kwargs): _id = kwargs.get('cid') + projection = self.PHI_FIELDS.copy() + log.debug(self.PHI_FIELDS) + log.debug(projection) self.config = self.container_handler_configurations[cont_name] self.storage = self.config['storage'] container = self._get_container(_id) + log.debug(container) + if check_phi(self.uid, container) or self.superuser_request: + log.debug("PHI") + log.debug(self.superuser_request) + self.phi = True + projection = None permchecker = self._get_permchecker(container) try: # This line exec the actual get checking permissions using the decorator permchecker - result = permchecker(self.storage.exec_op)('GET', _id) + result = permchecker(self.storage.exec_op)('GET', _id, projection=projection, phi=self.phi) except APIStorageException as e: self.abort(400, e.message) if result is None: @@ -115,7 +123,7 @@ def get(self, cont_name, **kwargs): fileinfo['path'] = util.path_from_hash(fileinfo['hash']) inflate_job_info = cont_name == 'sessions' - result['analyses'] = AnalysisStorage().get_analyses(cont_name, _id, inflate_job_info) + result['analyses'] = AnalysisStorage().get_analyses(cont_name, _id, inflate_job_info, self.PHI_FIELDS.copy()) return self.handle_origin(result) def handle_origin(self, result): @@ -311,14 +319,18 @@ def match_ids(x): def get_all(self, cont_name, par_cont_name=None, par_id=None): self.config = self.container_handler_configurations[cont_name] self.storage = self.config['storage'] - - projection = self.config['list_projection'].copy() - if self.is_true('info'): - projection.pop('info') + projection = None if self.is_true('permissions'): if not projection: projection = None - + if self.is_true('phi'): + phi = True + else: + phi = False + if projection == None: + projection = self.PHI_FIELDS.copy() + else: + projection.update(self.PHI_FIELDS) # select which permission filter will be applied to the list of results. if self.superuser_request: permchecker = always_ok @@ -340,8 +352,9 @@ def get_all(self, cont_name, par_cont_name=None, par_id=None): query = {} if not self.is_true('archived'): query['archived'] = {'$ne': True} + # this request executes the actual reqeust filtering containers based on the user permissions - results = permchecker(self.storage.exec_op)('GET', query=query, public=self.public_request, projection=projection) + results = permchecker(self.storage.exec_op)('GET', query=query, public=self.public_request, projection=projection, phi=phi) if results is None: self.abort(404, 'No elements found in container {}'.format(self.storage.cont_name)) # return only permissions of the current user unless superuser or getting avatars @@ -361,6 +374,8 @@ def get_all(self, cont_name, par_cont_name=None, par_id=None): result = containerutil.get_stats(result, cont_name) result = self.handle_origin(result) modified_results.append(result) + if phi: + self.log_user_access(AccessType.view_container, cont_name, result.get('_id')) if self.is_true('join_avatars'): modified_results = self.join_user_info(modified_results) @@ -388,7 +403,13 @@ def _add_results_counts(self, results, cont_name): def get_all_for_user(self, cont_name, uid): self.config = self.container_handler_configurations[cont_name] self.storage = self.config['storage'] - projection = self.config['list_projection'] + projection = None + phi = False + if self.is_true('phi'): + phi = True + else: + projection = self.PHI_FIELDS.copy() + # select which permission filter will be applied to the list of results. if self.superuser_request or self.user_is_admin: permchecker = always_ok @@ -401,11 +422,14 @@ def get_all_for_user(self, cont_name, uid): '_id': uid } try: - results = permchecker(self.storage.exec_op)('GET', query=query, user=user, projection=projection) + results = permchecker(self.storage.exec_op)('GET', query=query, user=user, projection=projection, phi=phi) except APIStorageException as e: self.abort(400, e.message) if results is None: self.abort(404, 'Element not found in container {} {}'.format(self.storage.cont_name, uid)) + if phi: + for result in results: + self.log_user_access(AccessType.view_container, cont_name, result.get('_id')) self._filter_all_permissions(results, uid) return results @@ -432,7 +456,7 @@ def post(self, cont_name): if self.is_true('inherit') and cont_name == 'projects': payload['permissions'] = parent_container.get('permissions') elif cont_name =='projects': - payload['permissions'] = [{'_id': self.uid, 'access': 'admin'}] if self.uid else [] + payload['permissions'] = [{'_id': self.uid, 'access': 'admin', 'phi-access':True}] if self.uid else [] else: payload['permissions'] = parent_container.get('permissions', []) # Created and modified timestamps are added here to the payload diff --git a/api/handlers/grouphandler.py b/api/handlers/grouphandler.py index a006b1209..52d57b000 100644 --- a/api/handlers/grouphandler.py +++ b/api/handlers/grouphandler.py @@ -83,7 +83,7 @@ def post(self): payload_validator = validators.from_schema_path(payload_schema_uri) payload_validator(payload, 'POST') payload['created'] = payload['modified'] = datetime.datetime.utcnow() - payload['permissions'] = [{'_id': self.uid, 'access': 'admin'}] if self.uid else [] + payload['permissions'] = [{'_id': self.uid, 'access': 'admin', 'phi-access':True}] if self.uid else [] result = mongo_validator(permchecker(self.storage.exec_op))('POST', payload=payload) if result.acknowledged: if result.upserted_id: diff --git a/api/handlers/listhandler.py b/api/handlers/listhandler.py index c6cea1c40..984d2034f 100644 --- a/api/handlers/listhandler.py +++ b/api/handlers/listhandler.py @@ -215,7 +215,7 @@ def post(self, cont_name, list_name, **kwargs): 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' : payload['_id']}, update={'$set': {'permissions.$.access': payload['access'], 'permissions.$.phi-access': payload['phi-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) @@ -228,8 +228,15 @@ def put(self, cont_name, list_name, **kwargs): 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']}}) - self._propagate_permissions(cont_name, _id, query={'permissions._id': {'$ne': payload['_id']}}, update={'$addToSet': {'permissions': payload}}) + group = self.get("groups","permissions",**kwargs) + update = {'$set': {'permissions.$.access': payload.get('access'), 'permissions.$.phi-access': payload.get('phi-access')}} + if not update['$set']['permissions.$.access']: + update['$set'].pop('permissions.$.access') + elif not update['$set']['permissions.$.phi-access']: + update['$set'].pop('permissions.$.phi-access') + self._propagate_permissions(cont_name, _id, query={'permissions._id' : payload['_id']}, update=update) + + self._propagate_permissions(cont_name, _id, query={'permissions._id': {'$ne': payload['_id']}}, update={'$addToSet': {'permissions': group}}) elif cont_name != 'groups': self._propagate_permissions(cont_name, _id) return result diff --git a/api/handlers/refererhandler.py b/api/handlers/refererhandler.py index 628300935..c747ffd96 100644 --- a/api/handlers/refererhandler.py +++ b/api/handlers/refererhandler.py @@ -16,7 +16,7 @@ from .. import upload from .. import util from .. import validators -from ..auth import containerauth, always_ok +from ..auth import containerauth, always_ok, check_phi from ..dao import containerstorage, noop from ..dao.basecontainerstorage import ContainerStorage from ..dao.containerutil import singularize @@ -63,6 +63,12 @@ class AnalysesHandler(RefererHandler): payload_schema_file = 'analysis.json' update_payload_schema_file = 'analysis-update.json' + # Hard-coded PHI fields, will be changed to user set PHI fields + PHI_FIELDS = {'info': 0, 'tags': 0, 'files.info':0} + + def __init__(self, request=None, response=None): + super(AnalysesHandler, self).__init__(request, response) + self.phi = False def post(self, cont_name, cid): """ @@ -130,8 +136,13 @@ def get(self, **kwargs): _id = kwargs.get('_id') analysis = self.storage.get_container(_id) parent = self.storage.get_parent(analysis['parent']['type'], analysis['parent']['id']) + projection = self.PHI_FIELDS.copy() + if check_phi(self.uid, parent)or self.superuser_request: + self.phi = True + projection = None permchecker = self.get_permchecker(parent) - permchecker(noop)('GET') + permchecker(noop)('GET',phi=self.phi) + analysis = self.storage.get_container(_id, projection=projection) if self.is_true('inflate_job'): self.storage.inflate_job_info(analysis) diff --git a/api/web/request.py b/api/web/request.py index 7421eef65..16b1a3270 100644 --- a/api/web/request.py +++ b/api/web/request.py @@ -61,7 +61,7 @@ def log_user_access_from_request(self, *args, **kwargs): cont_id = kwargs.get(cont_id_kwarg) # Only log view_container events when the container is a session - if access_type is AccessType.view_container and cont_name not in ['sessions', 'session']: + if access_type is AccessType.view_container and not self.phi: return result self.log_user_access(access_type, cont_name, cont_id) diff --git a/bin/database.py b/bin/database.py index 57672b556..13e980a54 100755 --- a/bin/database.py +++ b/bin/database.py @@ -1219,6 +1219,22 @@ def upgrade_to_37(): cursor = config.db[coll].find({'permissions.site': {'$exists': True}}) process_cursor(cursor, upgrade_to_32_closure, context = coll) +def upgrade_to_38_closure(coll_item, coll): + permissions = coll_item.get('permissions', []) + for permission_ in permissions: + permission_['phi-access'] = True + result = config.db[coll].update_one({'_id': coll_item['_id']}, {'$set': {'permissions' : permissions}}) + if result.modified_count == 0: + return "Failed to add phi access to permissions in {} container {}".format(coll_item.get("_id"), coll) + return True + +def upgrade_to_38(): + """ + permissions now have a mandatory phi-access field + """ + for coll in ['acquisitions', 'groups', 'projects', 'sessions', 'analyses']: + cursor = config.db[coll].find({'permissions.site': {'$exists': True}}) + process_cursor(cursor, upgrade_to_38_closure, context = coll) def upgrade_to_38_closure(user): diff --git a/raml/examples/input/permission-update.json b/raml/examples/input/permission-update.json index 9c8a26f20..70b1c7b63 100644 --- a/raml/examples/input/permission-update.json +++ b/raml/examples/input/permission-update.json @@ -1,4 +1,5 @@ { - "access": "ro", - "_id": "coltonlw@flywheel.io" + "access": "ro", + "_id": "coltonlw@flywheel.io", + "phi-access": true } diff --git a/raml/examples/input/permission.json b/raml/examples/input/permission.json index 7a6ecbf7a..2bbe48159 100644 --- a/raml/examples/input/permission.json +++ b/raml/examples/input/permission.json @@ -1,4 +1,5 @@ { "_id":"coltonlw@flywheel.io", - "access":"admin" + "access":"admin", + "phi-access": true } diff --git a/raml/examples/output/acquisition-list.json b/raml/examples/output/acquisition-list.json index 1b29d562c..248b0ab53 100644 --- a/raml/examples/output/acquisition-list.json +++ b/raml/examples/output/acquisition-list.json @@ -34,7 +34,8 @@ "permissions": [ { "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true } ] }, @@ -73,7 +74,8 @@ "permissions": [ { "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true } ] }, @@ -112,7 +114,8 @@ "permissions": [ { "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true } ] } diff --git a/raml/examples/output/acquisition.json b/raml/examples/output/acquisition.json index fef6ba738..f7b61f7fb 100644 --- a/raml/examples/output/acquisition.json +++ b/raml/examples/output/acquisition.json @@ -33,7 +33,8 @@ "permissions": [ { "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true } ] } diff --git a/raml/examples/output/collection-list.json b/raml/examples/output/collection-list.json index e766d50c0..5d22f5211 100644 --- a/raml/examples/output/collection-list.json +++ b/raml/examples/output/collection-list.json @@ -6,6 +6,7 @@ "_id": "57c5c0bd9e512c606dd3df09", "permissions": [{ "access": "admin", - "_id": "user@test.com" + "_id": "user@test.com", + "phi-access": true }] }] diff --git a/raml/examples/output/collection.json b/raml/examples/output/collection.json index 445be806b..6a6c999e0 100644 --- a/raml/examples/output/collection.json +++ b/raml/examples/output/collection.json @@ -7,6 +7,7 @@ "_id": "57c5c0bd9e512c606dd3df09", "permissions": [{ "access": "admin", - "_id": "user@test.com" + "_id": "user@test.com", + "phi-access": true }] } diff --git a/raml/examples/output/groups-list.json b/raml/examples/output/groups-list.json index 5f89d59f5..8901c3471 100644 --- a/raml/examples/output/groups-list.json +++ b/raml/examples/output/groups-list.json @@ -4,15 +4,18 @@ "permissions": [ { "access": "admin", - "_id": "group_admin@fakeuser.com" + "_id": "group_admin@fakeuser.com", + "phi-access": true }, { "access": "rw", - "_id": "group_member_read-write@fakeuser.com" + "_id": "group_member_read-write@fakeuser.com", + "phi-access": true }, { "access": "ro", - "_id": "group_member_read-only@fakeuser.com" + "_id": "group_member_read-only@fakeuser.com", + "phi-access": true } ], "created": "2016-08-19T11:41:15.360000+00:00", diff --git a/raml/examples/output/permission.json b/raml/examples/output/permission.json index ae383f550..b353de43e 100644 --- a/raml/examples/output/permission.json +++ b/raml/examples/output/permission.json @@ -1,4 +1,5 @@ { "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true } diff --git a/raml/examples/output/project-list.json b/raml/examples/output/project-list.json index a597be608..925aa28ed 100644 --- a/raml/examples/output/project-list.json +++ b/raml/examples/output/project-list.json @@ -7,7 +7,8 @@ "public": false, "permissions": [{ "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true }] }, { "group": "scitran", @@ -18,7 +19,8 @@ "public": false, "permissions": [{ "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true }] }, { "group": "scitran", @@ -29,7 +31,8 @@ "public": false, "permissions": [{ "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true }] }, { "group": "test-group", @@ -41,7 +44,8 @@ "public": false, "permissions": [{ "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true }] }, { "group": "scitran", @@ -52,6 +56,7 @@ "_id": "57ed6312466d8e01c91ee427", "permissions": [{ "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true }] }] diff --git a/raml/examples/output/project.json b/raml/examples/output/project.json index ea610b4d1..af8d5fb5e 100644 --- a/raml/examples/output/project.json +++ b/raml/examples/output/project.json @@ -8,6 +8,7 @@ "public": false, "permissions": [{ "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true }] } diff --git a/raml/examples/output/session-list.json b/raml/examples/output/session-list.json index 9f6cbc64f..de4e7842b 100644 --- a/raml/examples/output/session-list.json +++ b/raml/examples/output/session-list.json @@ -12,7 +12,8 @@ "public": false, "permissions": [{ "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true }] }, { "group": "scitran", @@ -28,7 +29,8 @@ "public": false, "permissions": [{ "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true }] }, { "group": "scitran", @@ -44,6 +46,7 @@ "public": false, "permissions": [{ "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true }] }] diff --git a/raml/examples/output/session.json b/raml/examples/output/session.json index 74dc0a6f5..0d2304663 100644 --- a/raml/examples/output/session.json +++ b/raml/examples/output/session.json @@ -12,6 +12,7 @@ "public": false, "permissions": [{ "access": "admin", - "_id": "coltonlw@flywheel.io" + "_id": "coltonlw@flywheel.io", + "phi-access": true }] } diff --git a/raml/resources/collections.raml b/raml/resources/collections.raml index 53fc43235..a156d6aa3 100644 --- a/raml/resources/collections.raml +++ b/raml/resources/collections.raml @@ -1,4 +1,7 @@ get: + queryParameters: + phi: + description: To return phi description: List all collections responses: 200: diff --git a/raml/schemas/definitions/analysis.json b/raml/schemas/definitions/analysis.json index 1a8da4353..963b022e2 100644 --- a/raml/schemas/definitions/analysis.json +++ b/raml/schemas/definitions/analysis.json @@ -59,6 +59,7 @@ "measurements": {"$ref":"../definitions/file.json#/definitions/measurements"}, "tags": {"$ref":"../definitions/file.json#/definitions/tags"}, "info": {"$ref":"../definitions/file.json#/definitions/info"}, + "info_exists":{"type":"boolean"}, "origin":{"$ref":"../definitions/file.json#/definitions/origin"}, "hash":{"$ref":"../definitions/file.json#/definitions/hash"}, "created":{"$ref":"../definitions/created-modified.json#/definitions/created"}, @@ -78,6 +79,13 @@ }, "notes":{"$ref":"#/definitions/notes"}, "description":{"$ref":"#/definitions/description"}, + "parent":{ + "type": "object", + "properties": { + "type": "string", + "id": {"$ref":"../definitions/objectid.json#"} + } + }, "label":{"$ref":"#/definitions/label"}, "user":{"$ref":"#/definitions/user"}, "created":{"$ref":"../definitions/created-modified.json#/definitions/created"}, diff --git a/raml/schemas/definitions/permission.json b/raml/schemas/definitions/permission.json index 490aafa51..c195b31e8 100644 --- a/raml/schemas/definitions/permission.json +++ b/raml/schemas/definitions/permission.json @@ -4,11 +4,13 @@ "definitions": { "_id": { "type": "string" }, "access": { "enum": ["ro", "rw", "admin"] }, + "phi-access": {"type": "boolean"}, "permission":{ "type":"object", "properties":{ "_id":{"$ref":"#/definitions/_id"}, - "access":{"$ref":"#/definitions/access"} + "access":{"$ref":"#/definitions/access"}, + "phi-access":{"$ref":"#/definitions/phi-access"} }, "additionalProperties": false }, diff --git a/raml/schemas/input/permission.json b/raml/schemas/input/permission.json index 85172151f..2ccfe500f 100644 --- a/raml/schemas/input/permission.json +++ b/raml/schemas/input/permission.json @@ -3,5 +3,5 @@ "type": "object", "allOf":[{"$ref":"../definitions/permission.json#/definitions/permission"}], "key_fields": ["_id"], - "required": ["_id", "access"] + "required": ["_id", "access", "phi-access"] } diff --git a/tests/integration_tests/abao/abao_test_hooks.js b/tests/integration_tests/abao/abao_test_hooks.js index 270c43aae..1ad38fad5 100644 --- a/tests/integration_tests/abao/abao_test_hooks.js +++ b/tests/integration_tests/abao/abao_test_hooks.js @@ -218,7 +218,8 @@ hooks.before("POST /groups/{GroupId}/permissions -> 200", function(test, done) { }; test.request.body = { _id: "test@user.com", - access: "ro" + access: "ro", + "phi-access": true } done(); }); @@ -246,7 +247,8 @@ hooks.before("PUT /groups/{GroupId}/permissions/{UserId} -> 200", function(test, }; test.request.body = { _id: "test@user.com", - access: "admin" + access: "admin", + "phi-access": true }; done(); }); @@ -259,6 +261,7 @@ hooks.before("PUT /groups/{GroupId}/permissions/{UserId} -> 400", function(test, test.request.body = { _id: "test@user.com", access: "rw", + "phi-access": true, not_a_real_property: "foo" }; done(); @@ -341,11 +344,18 @@ hooks.before("GET /groups/{GroupId}/projects -> 200", function(test, done) { }); +hooks.before("GET /collections -> 200", function(test, done) { + test.request.query = { + "phi": true + }; + done(); +}); // set initial test_collection_1 hooks.after("GET /collections -> 200", function(test, done) { test_collection_1 = test.response.body[0]; collection_id = test.response.body[0]._id; delete_collection_id = test.response.body[1]._id; + assert.equal(test_collection_1.label, "test-collection-1"); done(); }); @@ -459,7 +469,8 @@ hooks.before("POST /collections/{CollectionId}/permissions -> 200", function(tes }; test.request.body = { "_id":"test@user.com", - "access":"ro" + "access":"ro", + "phi-access": true }; done(); }); @@ -489,7 +500,8 @@ hooks.before("PUT /collections/{CollectionId}/permissions/{UserId} -> 200", func }; test.request.body = { "access":"rw", - "_id":"test@user.com" + "_id":"test@user.com", + "phi-access": true }; done(); }); @@ -1259,7 +1271,8 @@ hooks.before("POST /projects/{ProjectId}/permissions -> 200", function(test, don }; test.request.body = { "_id":"test@user.com", - "access":"ro" + "access":"ro", + "phi-access": true }; done(); }); @@ -1289,7 +1302,8 @@ hooks.before("PUT /projects/{ProjectId}/permissions/{UserId} -> 200", function(t }; test.request.body = { "access":"rw", - "_id":"test@user.com" + "_id":"test@user.com", + "phi-access": true }; done(); }); diff --git a/tests/integration_tests/python/conftest.py b/tests/integration_tests/python/conftest.py index 7cd2593d5..e84f98ea5 100644 --- a/tests/integration_tests/python/conftest.py +++ b/tests/integration_tests/python/conftest.py @@ -219,6 +219,7 @@ class DataBuilder(object): 'session': 'project', 'acquisition': 'session', } + parent_to_child = {parent: child for child, parent in child_to_parent.items()} def __init__(self, session, randstr=lambda: binascii.hexlify(os.urandom(10))): @@ -254,8 +255,8 @@ def create(self, resource, **kwargs): payload['gear']['name'] = self.randstr() # add missing label fields using randstr - # such fields are: [project.label, session.label, acquisition.label] - if resource in self.child_to_parent and 'label' not in payload: + # such fields are: [project.label, session.label, acquisition.label, group.label] + if resource in ['project', 'group', 'groups', 'session', 'acquisition'] and 'label' not in payload: payload['label'] = self.randstr() # add missing parent container when creating child container diff --git a/tests/integration_tests/python/test_access_log.py b/tests/integration_tests/python/test_access_log.py index 083f5f791..29d66841a 100644 --- a/tests/integration_tests/python/test_access_log.py +++ b/tests/integration_tests/python/test_access_log.py @@ -265,8 +265,8 @@ def test_access_log_fails(data_builder, as_admin, log_db): r = as_admin.delete('/projects/' + project + '/files/' + file_name) assert r.status_code == 500 + log_db.command('collMod', 'access_log', validator={}, validationLevel='strict') + r = as_admin.get('/projects/' + project) assert r.ok assert r.json()['files'] - - log_db.command('collMod', 'access_log', validator={}, validationLevel='strict') diff --git a/tests/integration_tests/python/test_collection.py b/tests/integration_tests/python/test_collection.py index c88d5fb37..15c90636d 100644 --- a/tests/integration_tests/python/test_collection.py +++ b/tests/integration_tests/python/test_collection.py @@ -72,7 +72,7 @@ def test_collections(data_builder, as_admin, as_user): assert r.ok uid = r.json()['_id'] - r = as_admin.post('/collections/' + collection + '/permissions', json={'_id': uid, 'access': 'ro'}) + r = as_admin.post('/collections/' + collection + '/permissions', json={'_id': uid, 'access': 'ro', 'phi-access':True}) assert r.ok # test user cannot see sessions or acquisitions @@ -85,7 +85,7 @@ def test_collections(data_builder, as_admin, as_user): assert r.json() == [] # add user to project - r = as_admin.post('/projects/' + project2 + '/permissions', json={'_id': uid, 'access': 'ro'}) + r = as_admin.post('/projects/' + project2 + '/permissions', json={'_id': uid, 'access': 'ro', 'phi-access':True}) assert r.ok # test user can now see some of sessions and acquisitions @@ -113,3 +113,98 @@ def test_collections(data_builder, as_admin, as_user): # test if collection is listed at acquisition r = as_admin.get('/acquisitions/' + acquisition) assert collection not in r.json()['collections'] + +def test_collections_phi(data_builder, as_admin, as_user, log_db, as_root,file_form): + session = data_builder.create_session() + acquisition = data_builder.create_acquisition() + + # create collection + r = as_admin.post('/collections', json={ + 'label': 'SciTran/Testing', + 'public': True + }) + assert r.ok + collection = r.json()['_id'] + + file_name = 'test_file.txt' + + assert as_admin.post('/collections/' + collection + '/files', files=file_form(file_name)).ok + + # Attempt full replace of info + file_info = { + 'a': 'b', + 'test': 123, + 'map': { + 'a': 'b' + }, + 'list': [1,2,3] + } + + + r = as_admin.post('/collections/' + collection + '/files/' + file_name + '/info', json={ + 'replace': file_info + }) + assert r.ok + + + # Test phi access for list returns with phi access level + pre_log = log_db.access_log.count({}) + r = as_admin.get('/collections', params={"phi":False}) + assert r.ok + for collection_ in r.json(): + assert collection_.get('files',[{}])[0].get('info') == None + assert pre_log == log_db.access_log.count({}) + r = as_admin.get('/collections', params={'phi':True}) + assert r.ok + for collection_ in r.json(): + assert collection_.get('files',[{}])[0].get('info').get('a') == "b" + assert pre_log == log_db.access_log.count({}) - len(r.json()) + + # Test phi access for individual elements with phi access level + pre_log = log_db.access_log.count({}) + r = as_admin.get('/collections/' + collection) + assert r.ok + assert r.json().get('files',[{}])[0].get('info').get('a') == "b" + assert pre_log == log_db.access_log.count({}) - 1 + pre_log = log_db.access_log.count({}) + + r = as_admin.get('/collections/' + collection, params={'phi':True}) + assert r.ok + assert r.json().get('files',[{}])[0].get('info').get('a') == "b" + assert pre_log == log_db.access_log.count({}) - 1 + + + # Test phi access without phi access + r = as_admin.put("/collections/" + collection + "/permissions/admin@user.com", json={"phi-access":False}) + assert r.ok + r = as_admin.get("/collections/" + collection + "/permissions/admin@user.com") + assert r.ok + assert r.json().get("phi-access") == False + + pre_log = log_db.access_log.count({}) + r = as_admin.get('/collections', params={"phi":False}) + assert r.ok + for collection_ in r.json(): + assert collection_.get('files',[{}])[0].get('info') == None + assert pre_log == log_db.access_log.count({}) + r = as_admin.get('/collections', params={'phi':True}) + assert r.status_code == 403 + + # Test phi access for individual elements without phi access level + pre_log = log_db.access_log.count({}) + r = as_admin.get('/collections/' + collection) + assert r.ok + assert r.json().get('files',[{}])[0].get('info') == None + assert pre_log == log_db.access_log.count({}) + pre_log = log_db.access_log.count({}) + + + # Test phi access for individual elements without phi access level but with root + r = as_root.get('/collections/' + collection) + assert r.ok + assert r.json().get('files',[{}])[0].get('info').get('a') == "b" + assert pre_log == log_db.access_log.count({}) - 1 + + + r = as_admin.delete('/collections/'+ collection) + assert r.ok diff --git a/tests/integration_tests/python/test_containers.py b/tests/integration_tests/python/test_containers.py index f0fc8ff4a..429f0e464 100644 --- a/tests/integration_tests/python/test_containers.py +++ b/tests/integration_tests/python/test_containers.py @@ -262,6 +262,82 @@ def test_get_all_for_user(as_admin, as_public): r = as_admin.get('/users/' + user_id + '/sessions') assert r.ok +def test_phi_access(as_user, as_admin, as_root, data_builder, log_db): + group = data_builder.create_group() + project = data_builder.create_project() + session = data_builder.create_session() + r = as_admin.put('/sessions/' + session, json={"subject":{"firstname":"FirstName", "code":"Subject_Code"}}, params={'replace_metadata':True}) + assert r.ok + + # Test phi access for list returns with phi access level + pre_log = log_db.access_log.count({}) + r = as_admin.get('/sessions') + assert r.ok + for session_ in r.json(): + assert session_.get('subject').get('firstname') == None + assert pre_log == log_db.access_log.count({}) + r = as_admin.get('/sessions', params={'phi':True}) + assert r.ok + for session_ in r.json(): + assert session_.get('subject').get('firstname') == "FirstName" + assert pre_log == log_db.access_log.count({}) - len(r.json()) + + # Test phi access for individual elements with phi access level + pre_log = log_db.access_log.count({}) + r = as_admin.get('/sessions/' + session) + assert r.ok + assert r.json().get('subject').get('firstname') == 'FirstName' + assert pre_log == log_db.access_log.count({}) - 1 + + r = as_admin.get('/sessions/' + session, params={'phi':True}) + assert r.ok + assert r.json().get('subject').get('firstname') == 'FirstName' + assert r.json().get('subject').get('code') == 'Subject_Code' + assert pre_log == log_db.access_log.count({}) - 2 + + # Set no-phi flag to true + r = as_admin.put('/projects/' + project + '/permissions/admin@user.com', json={'phi-access': False}) + assert r.ok + r = as_admin.post('/projects/' + project + '/permissions', json={'access': 'ro', 'phi-access': False, '_id': 'user@user.com'}) + assert r.ok + + # Test phi access for list returns without phi access level + pre_log = log_db.access_log.count({}) + r = as_admin.get('/sessions') + assert r.ok + for session_ in r.json(): + assert session_.get('subject').get('firstname') == None + assert session_.get('subject').get('code') == 'Subject_Code' + assert pre_log == log_db.access_log.count({}) + r = as_admin.get('/sessions', params={'phi':True}) + assert r.status_code == 403 + + # Test phi access for individual elements without phi access level but with super_user + pre_log = log_db.access_log.count({}) + r = as_root.get('/sessions/' + session) + assert r.ok + assert r.json().get('subject').get('firstname') == "FirstName" + assert r.json().get('subject').get('code') == 'Subject_Code' + assert pre_log == log_db.access_log.count({}) - 1 + + r = as_admin.get('/sessions/' + session, params={'phi':True}) + assert r.status_code == 200 + + # Test phi access for individual elements without phi access level and w/o super_user + as_admin + pre_log = log_db.access_log.count({}) + r = as_user.get('/sessions/' + session) + assert r.ok + assert r.json().get('subject').get('firstname') == None + assert r.json().get('subject').get('code') == 'Subject_Code' + assert pre_log == log_db.access_log.count({}) + + r = as_user.get('/sessions/' + session, params={'phi':True}) + assert r.status_code == 200 + + r = as_admin.delete('/sessions/' + session) + assert r.ok + def test_get_container(data_builder, default_payload, file_form, as_drone, as_admin, as_public, api_db): project = data_builder.create_project() @@ -403,7 +479,8 @@ def test_post_container(data_builder, as_admin, as_user): r = as_admin.post('/groups/' + group + '/permissions', json={ '_id': uid, - 'access': 'rw' + 'access': 'rw', + 'phi-access': True }) assert r.ok @@ -421,7 +498,8 @@ def test_post_container(data_builder, as_admin, as_user): r = as_admin.post('/projects/' + project + '/permissions', json={ '_id': uid, - 'access': 'rw' + 'access': 'rw', + 'phi-access': True }) assert r.ok diff --git a/tests/integration_tests/python/test_groups.py b/tests/integration_tests/python/test_groups.py index 9ebcddf1f..8ba485782 100644 --- a/tests/integration_tests/python/test_groups.py +++ b/tests/integration_tests/python/test_groups.py @@ -10,6 +10,7 @@ def test_groups(as_user, as_admin, data_builder): # Able to find new group r = as_admin.get('/groups/' + group) assert r.ok + assert r.json()['label'] != None first_modified = r.json()['modified'] # Test to make sure that list of roles nor name exists in a newly created group @@ -66,7 +67,7 @@ def test_groups(as_user, as_admin, data_builder): assert d5 > d4 # Add a permission to the group - user = {'access': 'rw', '_id': 'newUser@fakeuser.com'} + user = {'access': 'rw', '_id': 'newUser@fakeuser.com', 'phi-access': True} r = as_admin.post('/groups/' + group + '/permissions', json=user) assert r.ok @@ -106,7 +107,7 @@ def test_groups(as_user, as_admin, data_builder): assert d8 > d7 group2 = data_builder.create_group() - r = as_admin.post('/groups/' + group2 + '/permissions', json={'access':'admin','_id':'user@user.com'}) + r = as_admin.post('/groups/' + group2 + '/permissions', json={'access':'admin','_id':'user@user.com', 'phi-access': True}) assert r.ok r = as_user.get('/groups') assert r.ok diff --git a/tests/integration_tests/python/test_jobs.py b/tests/integration_tests/python/test_jobs.py index 16a0a04d3..20e934ba4 100644 --- a/tests/integration_tests/python/test_jobs.py +++ b/tests/integration_tests/python/test_jobs.py @@ -183,7 +183,8 @@ def test_jobs(data_builder, default_payload, as_public, as_user, as_admin, as_ro r = as_admin.post('/projects/' + project + '/permissions', json={ '_id': uid, - 'access': 'ro' + 'access': 'ro', + 'phi-access': True }) assert r.ok diff --git a/tests/integration_tests/python/test_permissions.py b/tests/integration_tests/python/test_permissions.py index 3eab504be..f3743ff57 100644 --- a/tests/integration_tests/python/test_permissions.py +++ b/tests/integration_tests/python/test_permissions.py @@ -14,7 +14,8 @@ def test_permissions(data_builder, as_admin): # Add permissions for user 1 r = as_admin.post(permissions_path, json={ '_id': user_1, - 'access': 'ro' + 'access': 'ro', + 'phi-access': True }) assert r.ok @@ -32,7 +33,8 @@ def test_permissions(data_builder, as_admin): # Add user 2 to have ro access r = as_admin.post(permissions_path, json={ '_id': user_2, - 'access': 'ro' + 'access': 'ro', + 'phi-access': True }) assert r.ok @@ -66,7 +68,7 @@ def test_group_permissions(data_builder, as_admin, as_public): assert r.status_code == 404 # Create permission for user - r = as_admin.post(permissions_path, json={'_id': user, 'access': 'rw'}) + r = as_admin.post(permissions_path, json={'_id': user, 'access': 'rw', 'phi-access': True}) assert r.ok # Verify new user permission diff --git a/tests/integration_tests/python/test_propagation.py b/tests/integration_tests/python/test_propagation.py index 81c8cb580..994f6b595 100644 --- a/tests/integration_tests/python/test_propagation.py +++ b/tests/integration_tests/python/test_propagation.py @@ -127,7 +127,7 @@ def get_user_in_perms(perms, uid): user_id = 'propagation@user.com' # Add user to project permissions - payload = {'_id': user_id, 'access': 'admin'} + payload = {'_id': user_id, 'access': 'admin', 'phi-access': True} r = as_admin.post('/projects/' + project + '/permissions', json=payload) assert r.ok @@ -147,7 +147,7 @@ def get_user_in_perms(perms, uid): assert r.ok and user # Modify user permissions - payload = {'access': 'rw', '_id': user_id} + payload = {'access': 'rw', '_id': user_id, 'phi-access': True} r = as_admin.put('/projects/' + project + '/permissions/' + user_id, json=payload) assert r.ok @@ -208,7 +208,7 @@ def get_user_in_perms(perms, uid): user_id = 'propagation@user.com' # Add user to group permissions - payload = {'_id': user_id, 'access': 'admin'} + payload = {'_id': user_id, 'access': 'admin', 'phi-access': True} r = as_admin.post('/groups/' + group + '/permissions', json=payload, params={'propagate': 'true'}) assert r.ok @@ -240,7 +240,7 @@ def get_user_in_perms(perms, uid): assert r.ok and user # Modify user permissions - payload = {'access': 'rw', '_id': user_id} + payload = {'access': 'rw', '_id': user_id, 'phi-access':True} r = as_admin.put('/groups/' + group + '/permissions/' + user_id, json=payload, params={'propagate': 'true'}) assert r.ok diff --git a/tests/integration_tests/python/test_rules.py b/tests/integration_tests/python/test_rules.py index a69c7900b..78a6a8f1b 100644 --- a/tests/integration_tests/python/test_rules.py +++ b/tests/integration_tests/python/test_rules.py @@ -265,7 +265,7 @@ def test_rules(randstr, data_builder, file_form, as_root, as_admin, with_user, a # add read-only perms for user r = as_admin.post('/projects/' + project + '/permissions', json={ - '_id': with_user.user, 'access': 'ro'}) + '_id': with_user.user, 'access': 'ro', 'phi-access': True}) assert r.ok # try to add rule w/ read-only project perms diff --git a/tests/integration_tests/python/test_users.py b/tests/integration_tests/python/test_users.py index a646d4adf..ee427b1c9 100644 --- a/tests/integration_tests/python/test_users.py +++ b/tests/integration_tests/python/test_users.py @@ -95,7 +95,8 @@ def test_users(data_builder, as_root, as_admin, as_user, as_public): project = data_builder.create_project() r = as_admin.post('/projects/' + project + '/permissions', json={ '_id': new_user_id_admin, - 'access': 'ro' + 'access': 'ro', + 'phi-access': True }) assert r.ok