def load(info): registerAccessFlag(constants.ACCESS_FLAG_EXECUTE_TASK, name='Execute analyses', admin=True) TokenScope.describeScope( constants.TOKEN_SCOPE_EXECUTE_TASK, name='Execute tasks', description='Execute item tasks.') TokenScope.describeScope( constants.TOKEN_SCOPE_AUTO_CREATE_CLI, 'Item task auto-creation', 'Create new CLIs via automatic introspection.', admin=True) ModelImporter.model('item').ensureIndex(['meta.isItemTask', {'sparse': True}]) ModelImporter.model('item').exposeFields(level=AccessType.READ, fields='createdByJob') ModelImporter.model('job', 'jobs').exposeFields(level=AccessType.READ, fields={ 'itemTaskId', 'itemTaskBindings'}) events.bind('jobs.job.update', info['name'], _onJobSave) events.bind('data.process', info['name'], _onUpload) info['apiRoot'].item_task = ItemTask() info['apiRoot'].item.route('POST', (':id', 'item_task_slicer_cli_description'), runSlicerCliTasksDescriptionForItem) info['apiRoot'].item.route('PUT', (':id', 'item_task_slicer_cli_xml'), configureItemTaskFromSlicerCliXml) info['apiRoot'].item.route('POST', (':id', 'item_task_json_description'), runJsonTasksDescriptionForItem) info['apiRoot'].item.route('PUT', (':id', 'item_task_json_specs'), configureItemTaskFromJson) info['apiRoot'].folder.route('POST', (':id', 'item_task_slicer_cli_description'), runSlicerCliTasksDescriptionForFolder) info['apiRoot'].folder.route('POST', (':id', 'item_task_slicer_cli_xml'), createItemTasksFromSlicerCliXml) info['apiRoot'].folder.route('POST', (':id', 'item_task_json_description'), runJsonTasksDescriptionForFolder) info['apiRoot'].folder.route('POST', (':id', 'item_task_json_specs'), createItemTasksFromJson)
def load(info): registerAccessFlag(constants.ACCESS_FLAG_EXECUTE_TASK, name='Execute analyses', admin=True) TokenScope.describeScope(constants.TOKEN_SCOPE_EXECUTE_TASK, name='Execute tasks', description='Execute item tasks.') TokenScope.describeScope(constants.TOKEN_SCOPE_AUTO_CREATE_CLI, 'Item task auto-creation', 'Create new CLIs via automatic introspection.', admin=True) ModelImporter.model('item').ensureIndex( ['meta.isItemTask', { 'sparse': True }]) ModelImporter.model('item').exposeFields(level=AccessType.READ, fields='createdByJob') ModelImporter.model('job', 'jobs').exposeFields( level=AccessType.READ, fields={'itemTaskId', 'itemTaskBindings'}) events.bind('jobs.job.update', info['name'], _onJobSave) events.bind('data.process', info['name'], _onUpload) info['apiRoot'].item_task = ItemTask()
def registerAccessFlags(self, params): """ Helper that can be used to register access flags in the system. This is used to test the access flags UI since the core does not expose any flags. """ flags = self.getBodyJson() for key, info in six.viewitems(flags): registerAccessFlag(key, info['name'], info['description'], info['admin'])
def load(info): registerAccessFlag(constants.ACCESS_FLAG_EXECUTE_TASK, name='Execute analyses', admin=True) TokenScope.describeScope(constants.TOKEN_SCOPE_EXECUTE_TASK, name='Execute tasks', description='Execute item tasks.') TokenScope.describeScope(constants.TOKEN_SCOPE_AUTO_CREATE_CLI, 'Item task auto-creation', 'Create new CLIs via automatic introspection.', admin=True) ModelImporter.model('item').ensureIndex( ['meta.isItemTask', { 'sparse': True }]) ModelImporter.model('item').exposeFields(level=AccessType.READ, fields='createdByJob') ModelImporter.model('job', 'jobs').exposeFields( level=AccessType.READ, fields={'itemTaskId', 'itemTaskBindings'}) events.bind('jobs.job.update', info['name'], _onJobSave) events.bind('data.process', info['name'], _onUpload) info['apiRoot'].item_task = ItemTask() info['apiRoot'].item.route('POST', (':id', 'item_task_slicer_cli_description'), runSlicerCliTasksDescriptionForItem) info['apiRoot'].item.route('PUT', (':id', 'item_task_slicer_cli_xml'), configureItemTaskFromSlicerCliXml) info['apiRoot'].item.route('POST', (':id', 'item_task_json_description'), runJsonTasksDescriptionForItem) info['apiRoot'].item.route('PUT', (':id', 'item_task_json_specs'), configureItemTaskFromJson) info['apiRoot'].folder.route('POST', (':id', 'item_task_slicer_cli_description'), runSlicerCliTasksDescriptionForFolder) info['apiRoot'].folder.route('POST', (':id', 'item_task_slicer_cli_xml'), createItemTasksFromSlicerCliXml) info['apiRoot'].folder.route('POST', (':id', 'item_task_json_description'), runJsonTasksDescriptionForFolder) info['apiRoot'].folder.route('POST', (':id', 'item_task_json_specs'), createItemTasksFromJson)
def testAutoDescribeRoute(self): testRuns = [] registerAccessFlag('my_flag', name='My flag') class AutoDescribe(Resource): def __init__(self): super(AutoDescribe, self).__init__() self.resourceName = 'auto_describe' self.route('GET', ('test', ), self.test) self.route('POST', ('body', ), self.body) self.route('POST', ('json_body', ), self.jsonBody) self.route('POST', ('json_body_required', ), self.jsonBodyRequired) self.route('GET', ('model_param_flags', ), self.hasModelParamFlags) self.route('GET', ('model_param_query', ), self.hasModelQueryParam) self.route('GET', ('json_schema', ), self.hasJsonSchema) self.route('GET', ('missing_arg', ), self.hasMissingArg) @access.public @describe.autoDescribeRoute( describe.Description('test').param( 'b1', '', dataType='boolean', required=False, default=True).param('b2', '', dataType='boolean', required=False).param( 'float', '', dataType='number', required=False, default=1.0).param( 'integer', '', dataType='integer', required=False).param( 'timestamp', '', dataType='dateTime', required=False). param('datestamp', '', dataType='date', required=False).param( 'string', '', enum=['hello', 'world'], strip=True, lower=True).param('upper', '', required=False, upper=True).jsonParam( 'json1', '', required=False, requireArray=True).jsonParam( 'json2', '', required=False, requireObject=True, default={})) def test(self, b1, b2, string, upper, integer, float, timestamp, datestamp, json1, json2, params): testRuns.append({ 'b1': b1, 'b2': b2, 'string': string, 'upper': upper, 'integer': integer, 'float': float, 'timestamp': timestamp, 'datestamp': datestamp, 'json1': json1, 'json2': json2 }) @access.public @describe.autoDescribeRoute( describe.Description('body').param('body', '', required=False, paramType='body')) def body(self, body): testRuns.append({'body': body}) @access.public @describe.autoDescribeRoute( describe.Description('json_body').jsonParam('json_body', '', required=False, paramType='body')) def jsonBody(self, json_body): testRuns.append({'json_body': json_body}) @access.public @describe.autoDescribeRoute( describe.Description('json_body_required').jsonParam( 'json_body', '', required=True, requireObject=True, paramType='body')) def jsonBodyRequired(self, json_body): testRuns.append({'json_body': json_body}) @access.public @describe.autoDescribeRoute( describe.Description('has_model_param_query').modelParam( 'userId', model='user', level=AccessType.READ, paramType='query')) @filtermodel(model='user') def hasModelQueryParam(self, user): return user @access.public @describe.autoDescribeRoute( describe.Description('has_model_param_flags').modelParam( 'userId', model='user', level=AccessType.READ, paramType='query', requiredFlags='my_flag')) def hasModelParamFlags(self, user): return user @access.public @describe.autoDescribeRoute( describe.Description('has_json_schema').jsonParam( 'obj', '', schema={ 'type': 'object', 'required': ['foo', 'bar'] })) def hasJsonSchema(self, obj): return obj @access.public @describe.autoDescribeRoute( describe.Description('has_missing_arg').param('foo', '')) def hasMissingArg(self, params): return params server.root.api.v1.auto_describe = AutoDescribe() def testBad(inputs, expected): resp = self.request('/auto_describe/test', params=inputs) self.assertStatus(resp, 400) self.assertEqual(testRuns, []) self.assertEqual(resp.json['message'], expected) def testOk(inputs, expected): resp = self.request('/auto_describe/test', params=inputs) self.assertStatusOk(resp) self.assertEqual(len(testRuns), 1) self.assertEqual(testRuns[0], expected) del testRuns[-1] testBad({}, 'Parameter "string" is required.') testBad({ 'string': 'invalid value' }, 'Invalid value for string: "invalid value". Allowed values: hello, world.' ) testBad({ 'string': 'hello', 'float': 'not a float' }, 'Invalid value for numeric parameter float: not a float.') testBad({ 'string': 'hello', 'integer': '7.5' }, 'Invalid value for integer parameter integer: 7.5.') testBad({ 'string': 'hello', 'timestamp': 'hello world' }, 'Invalid date format for parameter timestamp: hello world.') testBad({ 'string': 'hello', 'datestamp': 'not a date' }, 'Invalid date format for parameter datestamp: not a date.') testBad({ 'string': 'hello', 'json1': json.dumps({'hello': 'world'}) }, 'Parameter json1 must be a JSON array.') testBad({ 'string': 'hello', 'json2': json.dumps(['hello', 'world']) }, 'Parameter json2 must be a JSON object.') testOk({'string': ' WoRlD '}, { 'string': 'world', 'upper': None, 'b1': True, 'b2': None, 'integer': None, 'float': 1., 'json1': None, 'json2': {}, 'timestamp': None, 'datestamp': None }) testOk( { 'string': ' hello', 'upper': ' hello', 'b1': 'false', 'b2': 'true', 'integer': '3', 'float': '0.25', 'json1': json.dumps([1, 2, 'abc']), 'json2': json.dumps({'hello': 'world'}), 'timestamp': '2017-01-01T11:35:22', 'datestamp': '2017-02-02T11:33:22' }, { 'string': 'hello', 'upper': ' HELLO', 'b1': False, 'b2': True, 'integer': 3, 'float': 0.25, 'json1': [1, 2, 'abc'], 'json2': { 'hello': 'world' }, 'timestamp': datetime.datetime(2017, 1, 1, 11, 35, 22), 'datestamp': datetime.date(2017, 2, 2) }) # Test request body body = 'torso' resp = self.request('/auto_describe/body', method='POST', body=json.dumps(body), type='application/json') self.assertStatusOk(resp) self.assertEqual(len(testRuns), 1) self.assertTrue('body' in testRuns[0]) self.assertTrue(hasattr(testRuns[0]['body'], 'read')) del testRuns[-1] # Test request JSON body (optional) body = {'emmet': 'otter'} resp = self.request('/auto_describe/json_body', method='POST', body=json.dumps(body), type='application/json') self.assertStatusOk(resp) self.assertEqual(len(testRuns), 1) expected = {'json_body': body} self.assertEqual(testRuns[0], expected) del testRuns[-1] # Test request JSON body (optional), omitting body resp = self.request('/auto_describe/json_body', method='POST') self.assertStatusOk(resp) self.assertEqual(len(testRuns), 1) expected = {'json_body': None} self.assertEqual(testRuns[0], expected) del testRuns[-1] # Test request JSON body (required) body = {'emmet': 'otter'} resp = self.request('/auto_describe/json_body_required', method='POST', body=json.dumps(body), type='application/json') self.assertStatusOk(resp) self.assertEqual(len(testRuns), 1) expected = {'json_body': body} self.assertEqual(testRuns[0], expected) del testRuns[-1] # Test request JSON body (required), omitting body resp = self.request('/auto_describe/json_body_required', method='POST') self.assertStatus(resp, 400) # Test request JSON body (required), pass list body = [{'emmet': 'otter'}] resp = self.request('/auto_describe/json_body_required', method='POST', body=json.dumps(body), type='application/json') self.assertStatus(resp, 400) # Test omission of required modelParam resp = self.request('/auto_describe/model_param_query') self.assertStatus(resp, 400) self.assertEqual(resp.json['message'], 'Parameter "userId" is required.') resp = self.request('/auto_describe/model_param_query', params={'userId': None}) self.assertStatus(resp, 400) self.assertEqual(resp.json['message'], 'Invalid ObjectId: None') user = User().createUser(firstName='admin', lastName='admin', email='*****@*****.**', login='******', password='******') resp = self.request('/auto_describe/model_param_query', user=user, params={'userId': user['_id']}) self.assertStatusOk(resp) # Test requiredFlags in modelParam resp = self.request('/auto_describe/model_param_flags', params={'userId': user['_id']}) self.assertStatus(resp, 401) self.assertRegex(resp.json['message'], '^Access denied for user') resp = self.request('/auto_describe/json_schema', params={'obj': json.dumps([])}) self.assertStatus(resp, 400) self.assertRegex( resp.json['message'], r"^Invalid JSON object for parameter obj: \[\] is not of type 'object'" ) resp = self.request('/auto_describe/json_schema', params={'obj': json.dumps({})}) self.assertStatus(resp, 400) self.assertRegex( resp.json['message'], r"^Invalid JSON object for parameter obj: 'foo' is a required property" ) obj = {'foo': 1, 'bar': 2} resp = self.request('/auto_describe/json_schema', params={'obj': json.dumps(obj)}) self.assertStatusOk(resp) self.assertEqual(resp.json, obj) # Test missing arg in wrapped function, should fall through to params dict resp = self.request('/auto_describe/missing_arg', params={'foo': 'bar'}) self.assertStatusOk(resp) self.assertEqual(resp.json, {'foo': 'bar'})
def testAccessFlags(self): resp = self.request('/system/access_flag') self.assertStatusOk(resp) self.assertEqual(resp.json, {}) registerAccessFlag('my_key', name='hello', description='a custom flag') resp = self.request('/system/access_flag') self.assertStatusOk(resp) self.assertEqual(resp.json, { 'my_key': { 'name': 'hello', 'description': 'a custom flag', 'admin': False } }) self.users[1] = self.model('user').load(self.users[1]['_id'], force=True) user = self.users[1] # Manage custom access flags on an access controlled resource self.assertFalse(self.model('user').hasAccessFlags(user, user, flags=['my_key'])) # Admin should always have permission self.assertTrue(self.model('user').hasAccessFlags(user, self.users[0], flags=['my_key'])) # Test the requireAccessFlags method with self.assertRaises(AccessException): self.model('user').requireAccessFlags(user, user=user, flags='my_key') self.model('user').requireAccessFlags(user, user=self.users[0], flags='my_key') acl = self.model('user').getFullAccessList(user) self.assertEqual(acl['users'][0]['flags'], []) # Test loadmodel requiredFlags argument via REST endpoint resp = self.request( '/test_endpoints/loadmodel_with_flags/%s' % user['_id'], user=self.users[1]) self.assertStatus(resp, 403) user = self.model('user').setAccessList(self.users[0], access={ 'users': [{ 'id': self.users[1]['_id'], 'level': AccessType.ADMIN, 'flags': ['my_key', 'not a registered flag'] }], 'groups': [{ 'id': self.group['_id'], 'level': AccessType.ADMIN, 'flags': ['my_key'] }] }, save=True) resp = self.request( '/test_endpoints/loadmodel_with_flags/%s' % user['_id'], user=self.users[1]) self.assertStatusOk(resp) self.assertEqual(resp.json, 'success') # Only registered flags should be stored acl = self.model('user').getFullAccessList(user) self.assertEqual(acl['users'][0]['flags'], ['my_key']) self.assertTrue(self.model('user').hasAccessFlags(user, user, flags=['my_key'])) # Create an admin-only access flag registerAccessFlag('admin_flag', name='admin flag', admin=True) # Non-admin shouldn't be able to set it user = self.model('user').setAccessList(self.users[0], access={ 'users': [{ 'id': self.users[1]['_id'], 'level': AccessType.ADMIN, 'flags': ['admin_flag'] }], 'groups': [] }, save=True, user=self.users[1]) acl = self.model('user').getFullAccessList(user) self.assertEqual(acl['users'][0]['flags'], []) # Admin user should be able to set it user = self.model('user').setAccessList(self.users[1], access={ 'users': [{ 'id': self.users[1]['_id'], 'level': AccessType.ADMIN, 'flags': ['admin_flag'] }], 'groups': [{ 'id': self.group['_id'], 'level': AccessType.ADMIN, 'flags': ['admin_flag'] }] }, save=True, user=self.users[0]) acl = self.model('user').getFullAccessList(user) self.assertEqual(acl['users'][0]['flags'], ['admin_flag']) # An already-enabled admin-only flag should stay enabled for non-admin user user = self.model('user').setAccessList(self.users[1], access={ 'users': [{ 'id': self.users[1]['_id'], 'level': AccessType.ADMIN, 'flags': ['my_key', 'admin_flag'] }], 'groups': [{ 'id': self.group['_id'], 'level': AccessType.ADMIN, 'flags': ['admin_flag'] }] }, save=True, user=self.users[1]) acl = self.model('user').getFullAccessList(user) self.assertEqual(set(acl['users'][0]['flags']), {'my_key', 'admin_flag'}) self.assertEqual(acl['groups'][0]['flags'], ['admin_flag']) # Test setting public flags on a collection and folder collectionModel = self.model('collection') folderModel = self.model('folder') itemModel = self.model('item') collection = collectionModel.createCollection('coll', creator=self.users[0], public=True) folder = folderModel.createFolder( collection, 'folder', parentType='collection', creator=self.users[0]) # Add an item to the folder so we can test AclMixin flag behavior item = itemModel.createItem(folder=folder, name='test', creator=self.users[0]) folder = folderModel.setUserAccess( folder, self.users[1], level=AccessType.ADMIN, save=True, currentUser=self.users[0]) with self.assertRaises(AccessException): collectionModel.requireAccessFlags(collection, user=None, flags='my_key') # Test AclMixin flag behavior with self.assertRaises(AccessException): itemModel.requireAccessFlags(item, user=None, flags='my_key') self.assertFalse(itemModel.hasAccessFlags(item, user=None, flags='my_key')) collection = collectionModel.setAccessList( collection, access=collection['access'], save=True, recurse=True, user=self.users[0], publicFlags=['my_key']) collectionModel.requireAccessFlags(collection, user=None, flags='my_key') # Make sure recursive setting of public flags worked folder = folderModel.load(folder['_id'], force=True) self.assertEqual(folder['publicFlags'], ['my_key']) itemModel.requireAccessFlags(item, user=None, flags='my_key') # Non-admin shouldn't be able to set admin-only public flags folder = folderModel.setPublicFlags( folder, flags=['admin_flag'], user=self.users[1], save=True) self.assertEqual(folder['publicFlags'], []) # Admin users should be able to set admin-only public flags folder = folderModel.setPublicFlags( folder, flags=['admin_flag'], user=self.users[0], save=True, append=True) self.assertEqual(folder['publicFlags'], ['admin_flag']) # Non-admin users can set admin-only public flags if they are already enabled folder = folderModel.setPublicFlags( folder, flags=['admin_flag', 'my_key'], user=self.users[1], save=True) self.assertEqual(set(folder['publicFlags']), {'admin_flag', 'my_key'}) # Test "force" options folder = folderModel.setPublicFlags(folder, flags='admin_flag', force=True, save=True) self.assertEqual(folder['publicFlags'], ['admin_flag']) folder = folderModel.setAccessList(folder, access={ 'users': [{ 'id': self.users[1]['_id'], 'level': AccessType.ADMIN, 'flags': ['my_key', 'admin_flag'] }], 'groups': [] }, save=True, force=True) folderModel.requireAccessFlags(folder, user=self.users[1], flags='my_key') folder = folderModel.setUserAccess( folder, self.users[1], level=AccessType.READ, save=True, force=True, flags=[]) self.assertFalse(folderModel.hasAccessFlags(folder, self.users[1], flags='my_key')) folder = folderModel.setGroupAccess( folder, self.group, level=AccessType.READ, save=True, force=True, flags='my_key') folderModel.requireAccessFlags(folder, user=self.users[1], flags='my_key') # Testing with flags=None should give sensible behavior folderModel.requireAccessFlags(folder, user=None, flags=None) # Test filtering results by access flags (both ACModel and AclMixin) for model, doc in ((folderModel, folder), (itemModel, item)): cursor = model.find({}) self.assertGreater(len(list(cursor)), 0) cursor = model.find({}) filtered = list(model.filterResultsByPermission( cursor, user=None, level=AccessType.READ, flags='my_key')) self.assertEqual(len(filtered), 0) cursor = model.find({}) filtered = list(model.filterResultsByPermission( cursor, user=self.users[1], level=AccessType.READ, flags=('my_key', 'admin_flag'))) self.assertEqual(len(filtered), 1) self.assertEqual(filtered[0]['_id'], doc['_id'])
def testAccessFlags(self): resp = self.request('/system/access_flag') self.assertStatusOk(resp) self.assertEqual(resp.json, {}) registerAccessFlag('my_key', name='hello', description='a custom flag') resp = self.request('/system/access_flag') self.assertStatusOk(resp) self.assertEqual(resp.json, { 'my_key': { 'name': 'hello', 'description': 'a custom flag', 'admin': False } }) self.users[1] = User().load(self.users[1]['_id'], force=True) user = self.users[1] # Manage custom access flags on an access controlled resource self.assertFalse(User().hasAccessFlags(user, user, flags=['my_key'])) # Admin should always have permission self.assertTrue(User().hasAccessFlags(user, self.users[0], flags=['my_key'])) # Test the requireAccessFlags method with self.assertRaises(AccessException): User().requireAccessFlags(user, user=user, flags='my_key') User().requireAccessFlags(user, user=self.users[0], flags='my_key') acl = User().getFullAccessList(user) self.assertEqual(acl['users'][0]['flags'], []) # Test loadmodel requiredFlags argument via REST endpoint resp = self.request( '/test_endpoints/loadmodel_with_flags/%s' % user['_id'], user=self.users[1]) self.assertStatus(resp, 403) user = User().setAccessList(self.users[0], access={ 'users': [{ 'id': self.users[1]['_id'], 'level': AccessType.ADMIN, 'flags': ['my_key', 'not a registered flag'] }], 'groups': [{ 'id': self.group['_id'], 'level': AccessType.ADMIN, 'flags': ['my_key'] }] }, save=True) resp = self.request( '/test_endpoints/loadmodel_with_flags/%s' % user['_id'], user=self.users[1]) self.assertStatusOk(resp) self.assertEqual(resp.json, 'success') # Only registered flags should be stored acl = User().getFullAccessList(user) self.assertEqual(acl['users'][0]['flags'], ['my_key']) self.assertTrue(User().hasAccessFlags(user, user, flags=['my_key'])) # Create an admin-only access flag registerAccessFlag('admin_flag', name='admin flag', admin=True) # Non-admin shouldn't be able to set it user = User().setAccessList(self.users[0], access={ 'users': [{ 'id': self.users[1]['_id'], 'level': AccessType.ADMIN, 'flags': ['admin_flag'] }], 'groups': [] }, save=True, user=self.users[1]) acl = User().getFullAccessList(user) self.assertEqual(acl['users'][0]['flags'], []) # Admin user should be able to set it user = User().setAccessList(self.users[1], access={ 'users': [{ 'id': self.users[1]['_id'], 'level': AccessType.ADMIN, 'flags': ['admin_flag'] }], 'groups': [{ 'id': self.group['_id'], 'level': AccessType.ADMIN, 'flags': ['admin_flag'] }] }, save=True, user=self.users[0]) acl = User().getFullAccessList(user) self.assertEqual(acl['users'][0]['flags'], ['admin_flag']) # An already-enabled admin-only flag should stay enabled for non-admin user user = User().setAccessList(self.users[1], access={ 'users': [{ 'id': self.users[1]['_id'], 'level': AccessType.ADMIN, 'flags': ['my_key', 'admin_flag'] }], 'groups': [{ 'id': self.group['_id'], 'level': AccessType.ADMIN, 'flags': ['admin_flag'] }] }, save=True, user=self.users[1]) acl = User().getFullAccessList(user) self.assertEqual(set(acl['users'][0]['flags']), {'my_key', 'admin_flag'}) self.assertEqual(acl['groups'][0]['flags'], ['admin_flag']) # Test setting public flags on a collection and folder collectionModel = Collection() folderModel = Folder() itemModel = Item() collection = collectionModel.createCollection('coll', creator=self.users[0], public=True) folder = folderModel.createFolder( collection, 'folder', parentType='collection', creator=self.users[0]) # Add an item to the folder so we can test AclMixin flag behavior item = itemModel.createItem(folder=folder, name='test', creator=self.users[0]) folder = folderModel.setUserAccess( folder, self.users[1], level=AccessType.ADMIN, save=True, currentUser=self.users[0]) with self.assertRaises(AccessException): collectionModel.requireAccessFlags(collection, user=None, flags='my_key') # Test AclMixin flag behavior with self.assertRaises(AccessException): itemModel.requireAccessFlags(item, user=None, flags='my_key') self.assertFalse(itemModel.hasAccessFlags(item, user=None, flags='my_key')) collection = collectionModel.setAccessList( collection, access=collection['access'], save=True, recurse=True, user=self.users[0], publicFlags=['my_key']) collectionModel.requireAccessFlags(collection, user=None, flags='my_key') # Make sure recursive setting of public flags worked folder = folderModel.load(folder['_id'], force=True) self.assertEqual(folder['publicFlags'], ['my_key']) itemModel.requireAccessFlags(item, user=None, flags='my_key') # Non-admin shouldn't be able to set admin-only public flags folder = folderModel.setPublicFlags( folder, flags=['admin_flag'], user=self.users[1], save=True) self.assertEqual(folder['publicFlags'], []) # Admin users should be able to set admin-only public flags folder = folderModel.setPublicFlags( folder, flags=['admin_flag'], user=self.users[0], save=True, append=True) self.assertEqual(folder['publicFlags'], ['admin_flag']) # Non-admin users can set admin-only public flags if they are already enabled folder = folderModel.setPublicFlags( folder, flags=['admin_flag', 'my_key'], user=self.users[1], save=True) self.assertEqual(set(folder['publicFlags']), {'admin_flag', 'my_key'}) # Test "force" options folder = folderModel.setPublicFlags(folder, flags='admin_flag', force=True, save=True) self.assertEqual(folder['publicFlags'], ['admin_flag']) folder = folderModel.setAccessList(folder, access={ 'users': [{ 'id': self.users[1]['_id'], 'level': AccessType.ADMIN, 'flags': ['my_key', 'admin_flag'] }], 'groups': [] }, save=True, force=True) folderModel.requireAccessFlags(folder, user=self.users[1], flags='my_key') folder = folderModel.setUserAccess( folder, self.users[1], level=AccessType.READ, save=True, force=True, flags=[]) self.assertFalse(folderModel.hasAccessFlags(folder, self.users[1], flags='my_key')) folder = folderModel.setGroupAccess( folder, self.group, level=AccessType.READ, save=True, force=True, flags='my_key') folderModel.requireAccessFlags(folder, user=self.users[1], flags='my_key') # Testing with flags=None should give sensible behavior folderModel.requireAccessFlags(folder, user=None, flags=None) # Test filtering results by access flags (both ACModel and AclMixin) for model, doc in ((folderModel, folder), (itemModel, item)): cursor = model.find({}) self.assertGreater(len(list(cursor)), 0) cursor = model.find({}) filtered = list(model.filterResultsByPermission( cursor, user=None, level=AccessType.READ, flags='my_key')) self.assertEqual(len(filtered), 0) cursor = model.find({}) filtered = list(model.filterResultsByPermission( cursor, user=self.users[1], level=AccessType.READ, flags=('my_key', 'admin_flag'))) self.assertEqual(len(filtered), 1) self.assertEqual(filtered[0]['_id'], doc['_id'])
def testAutoDescribeRoute(self): testRuns = [] registerAccessFlag('my_flag', name='My flag') class AutoDescribe(Resource): def __init__(self): super(AutoDescribe, self).__init__() self.resourceName = 'auto_describe' self.route('GET', ('test',), self.test) self.route('POST', ('body',), self.body) self.route('POST', ('json_body',), self.jsonBody) self.route('POST', ('json_body_required',), self.jsonBodyRequired) self.route('GET', ('model_param_flags',), self.hasModelParamFlags) self.route('GET', ('model_param_query',), self.hasModelQueryParam) self.route('GET', ('json_schema',), self.hasJsonSchema) self.route('GET', ('missing_arg',), self.hasMissingArg) @access.public @describe.autoDescribeRoute( describe.Description('test') .param('b1', '', dataType='boolean', required=False, default=True) .param('b2', '', dataType='boolean', required=False) .param('float', '', dataType='number', required=False, default=1.0) .param('integer', '', dataType='integer', required=False) .param('timestamp', '', dataType='dateTime', required=False) .param('datestamp', '', dataType='date', required=False) .param('string', '', enum=['hello', 'world'], strip=True, lower=True) .param('upper', '', required=False, upper=True) .jsonParam('json1', '', required=False, requireArray=True) .jsonParam('json2', '', required=False, requireObject=True, default={}) ) def test(self, b1, b2, string, upper, integer, float, timestamp, datestamp, json1, json2, params): testRuns.append({ 'b1': b1, 'b2': b2, 'string': string, 'upper': upper, 'integer': integer, 'float': float, 'timestamp': timestamp, 'datestamp': datestamp, 'json1': json1, 'json2': json2 }) @access.public @describe.autoDescribeRoute( describe.Description('body') .param('body', '', required=False, paramType='body') ) def body(self, body): testRuns.append({ 'body': body }) @access.public @describe.autoDescribeRoute( describe.Description('json_body') .jsonParam('json_body', '', required=False, paramType='body') ) def jsonBody(self, json_body): testRuns.append({ 'json_body': json_body }) @access.public @describe.autoDescribeRoute( describe.Description('json_body_required') .jsonParam('json_body', '', required=True, requireObject=True, paramType='body') ) def jsonBodyRequired(self, json_body): testRuns.append({ 'json_body': json_body }) @access.public @describe.autoDescribeRoute( describe.Description('has_model_param_query') .modelParam('userId', model='user', level=AccessType.READ, paramType='query') ) @filtermodel(model='user') def hasModelQueryParam(self, user): return user @access.public @describe.autoDescribeRoute( describe.Description('has_model_param_flags') .modelParam('userId', model='user', level=AccessType.READ, paramType='query', requiredFlags='my_flag') ) def hasModelParamFlags(self, user): return user @access.public @describe.autoDescribeRoute( describe.Description('has_json_schema') .jsonParam('obj', '', schema={ 'type': 'object', 'required': ['foo', 'bar'] }) ) def hasJsonSchema(self, obj): return obj @access.public @describe.autoDescribeRoute( describe.Description('has_missing_arg') .param('foo', '') ) def hasMissingArg(self, params): return params server.root.api.v1.auto_describe = AutoDescribe() def testBad(inputs, expected): resp = self.request('/auto_describe/test', params=inputs) self.assertStatus(resp, 400) self.assertEqual(testRuns, []) self.assertEqual(resp.json['message'], expected) def testOk(inputs, expected): resp = self.request('/auto_describe/test', params=inputs) self.assertStatusOk(resp) self.assertEqual(len(testRuns), 1) self.assertEqual(testRuns[0], expected) del testRuns[-1] testBad({}, 'Parameter "string" is required.') testBad({ 'string': 'invalid value' }, 'Invalid value for string: "invalid value". Allowed values: hello, world.') testBad({ 'string': 'hello', 'float': 'not a float' }, 'Invalid value for numeric parameter float: not a float.') testBad({ 'string': 'hello', 'integer': '7.5' }, 'Invalid value for integer parameter integer: 7.5.') testBad({ 'string': 'hello', 'timestamp': 'hello world' }, 'Invalid date format for parameter timestamp: hello world.') testBad({ 'string': 'hello', 'datestamp': 'not a date' }, 'Invalid date format for parameter datestamp: not a date.') testBad({ 'string': 'hello', 'json1': json.dumps({'hello': 'world'}) }, 'Parameter json1 must be a JSON array.') testBad({ 'string': 'hello', 'json2': json.dumps(['hello', 'world']) }, 'Parameter json2 must be a JSON object.') testOk({ 'string': ' WoRlD ' }, { 'string': 'world', 'upper': None, 'b1': True, 'b2': None, 'integer': None, 'float': 1., 'json1': None, 'json2': {}, 'timestamp': None, 'datestamp': None }) testOk({ 'string': ' hello', 'upper': ' hello', 'b1': 'false', 'b2': 'true', 'integer': '3', 'float': '0.25', 'json1': json.dumps([1, 2, 'abc']), 'json2': json.dumps({'hello': 'world'}), 'timestamp': '2017-01-01T11:35:22', 'datestamp': '2017-02-02T11:33:22' }, { 'string': 'hello', 'upper': ' HELLO', 'b1': False, 'b2': True, 'integer': 3, 'float': 0.25, 'json1': [1, 2, 'abc'], 'json2': {'hello': 'world'}, 'timestamp': datetime.datetime(2017, 1, 1, 11, 35, 22), 'datestamp': datetime.date(2017, 2, 2) }) # Test request body body = 'torso' resp = self.request('/auto_describe/body', method='POST', body=json.dumps(body), type='application/json') self.assertStatusOk(resp) self.assertEqual(len(testRuns), 1) self.assertTrue('body' in testRuns[0]) self.assertTrue(hasattr(testRuns[0]['body'], 'read')) del testRuns[-1] # Test request JSON body (optional) body = { 'emmet': 'otter' } resp = self.request('/auto_describe/json_body', method='POST', body=json.dumps(body), type='application/json') self.assertStatusOk(resp) self.assertEqual(len(testRuns), 1) expected = { 'json_body': body } self.assertEqual(testRuns[0], expected) del testRuns[-1] # Test request JSON body (optional), omitting body resp = self.request('/auto_describe/json_body', method='POST') self.assertStatusOk(resp) self.assertEqual(len(testRuns), 1) expected = { 'json_body': None } self.assertEqual(testRuns[0], expected) del testRuns[-1] # Test request JSON body (required) body = { 'emmet': 'otter' } resp = self.request('/auto_describe/json_body_required', method='POST', body=json.dumps(body), type='application/json') self.assertStatusOk(resp) self.assertEqual(len(testRuns), 1) expected = { 'json_body': body } self.assertEqual(testRuns[0], expected) del testRuns[-1] # Test request JSON body (required), omitting body resp = self.request('/auto_describe/json_body_required', method='POST') self.assertStatus(resp, 400) # Test request JSON body (required), pass list body = [{ 'emmet': 'otter' }] resp = self.request('/auto_describe/json_body_required', method='POST', body=json.dumps(body), type='application/json') self.assertStatus(resp, 400) # Test omission of required modelParam resp = self.request('/auto_describe/model_param_query') self.assertStatus(resp, 400) self.assertEqual(resp.json['message'], 'Parameter "userId" is required.') resp = self.request('/auto_describe/model_param_query', params={'userId': None}) self.assertStatus(resp, 400) self.assertEqual(resp.json['message'], 'Invalid ObjectId: None') user = self.model('user').createUser( firstName='admin', lastName='admin', email='*****@*****.**', login='******', password='******') resp = self.request( '/auto_describe/model_param_query', user=user, params={'userId': user['_id']}) self.assertStatusOk(resp) # Test requiredFlags in modelParam resp = self.request('/auto_describe/model_param_flags', params={ 'userId': user['_id'] }) self.assertStatus(resp, 401) six.assertRegex(self, resp.json['message'], '^Access denied for user') resp = self.request('/auto_describe/json_schema', params={ 'obj': json.dumps([]) }) self.assertStatus(resp, 400) self.assertEqual( resp.json['message'], "Invalid JSON object for parameter obj: [] is not of type 'object'") resp = self.request('/auto_describe/json_schema', params={ 'obj': json.dumps({}) }) self.assertStatus(resp, 400) self.assertEqual( resp.json['message'], "Invalid JSON object for parameter obj: 'foo' is a required property") obj = { 'foo': 1, 'bar': 2 } resp = self.request('/auto_describe/json_schema', params={ 'obj': json.dumps(obj) }) self.assertStatusOk(resp) self.assertEqual(resp.json, obj) # Test missing arg in wrapped function, should fall through to params dict resp = self.request('/auto_describe/missing_arg', params={ 'foo': 'bar' }) self.assertStatusOk(resp) self.assertEqual(resp.json, {'foo': 'bar'})
class CatResource(Resource): def __init__(self): super(CatResource, self).__init__() self.resourceName = 'cat' self.route('GET', (), self.findCat) self.route('GET', (':id', ), self.getCat) self.route('POST', (), self.createCat) self.route('PUT', (':id', ), self.updateCat) self.route('DELETE', (':id', ), self.deleteCat) self.route('PUT', (':id', 'feed'), self.feedCat) @access.public @autoDescribeRoute(Description('Find a cat')) def findCat(self, params): print('findCat() was called!') print('params is', params) @access.public @autoDescribeRoute( Description('Get a cat').modelParam('id', 'The cat ID', model=CatModel, level=AccessType.READ)) def getCat(self, params): print('getCat() was called!') print('id is', id) print('params is', params) @access.public @autoDescribeRoute(Description('Create a cat')) def createCat(self, params): document = {} catModel = CatModel().save(document) return catModel @access.public @autoDescribeRoute( Description('Update a cat').modelParam('id', 'The cat ID', model=CatModel, level=AccessType.WRITE)) def updateCat(self, params): print("params is", params) if 'cat_model' not in params: print("Error: no cat model in the parameter!") return catModel = params['cat_model'] if '_id' not in catModel: print("Error: missing id from catModel!") return id = catModel['_id'] print("id is", id) print('updateCat() was called!') @access.public @autoDescribeRoute( Description('Delete a cat').modelParam('id', 'The cat ID', model=CatModel, level=AccessType.WRITE, destName='doc')) def deleteCat(self, doc, params): print('doc is', doc) print('params is', params) print('deleteCat() was called!') CatModel().remove(doc) registerAccessFlag(key='cat.feed', name='Feed a cat', description='Allows users to feed a cat') @access.user @autoDescribeRoute( Description('Feed a cat').modelParam('id', 'The cat ID', model=CatModel, plugin='cats', level=AccessType.WRITE, requiredFlags='cat.feed', destName='doc')) def feedCat(self, doc, params): print('doc is', doc) print('params is', params) # Feed the cat return CatModel().feed(doc)