def test_ingest_without_molecule(server, molecule, user, make_girder_file, fsAssetstore): from girder.plugins.molecules.models.calculation import Calculation # Upload simulation result dir_path = os.path.dirname(os.path.realpath(__file__)) with open(os.path.join(dir_path, 'data', 'ethane.cjson')) as f: file = make_girder_file(fsAssetstore, user, 'ethane.cjson', contents=f.read().encode()) # Now we can test the ingest body = { 'fileId': str(file['_id']), 'format': 'cjson', 'public': True, 'image': { 'repository': 'openchemistry/psi4', 'tag': 'latest' } } r = server.request('/calculations', method='POST', type='application/json', body=json.dumps(body), user=user) assertStatus(r, 201) calculation = Calculation().load(r.json['_id'], force=True) for prop in ['fileId', 'moleculeId', 'notebooks', 'properties', 'cjson']: assert prop in calculation # Molecule should be created assert calculation['moleculeId'] is not None
def testReadingSettingsWithUserToken(server, userSettingToken): # Non-admin user with this token scope should still not work resp = server.request(path='/system/setting', params={'key': SettingKey.SMTP_PORT}, token=userSettingToken) assertStatus(resp, 403) assert resp.json['message'] == 'Administrator access required.'
def test_ingest_with_molecule(server, molecule, user, make_girder_file, fsAssetstore): molecule = molecule(user) from molecules.models.calculation import Calculation # Upload simulation result dir_path = os.path.dirname(os.path.realpath(__file__)) with open(os.path.join(dir_path, 'data', 'ethane.cjson')) as f: file = make_girder_file(fsAssetstore, user, 'ethane.cjson', contents=f.read().encode()) # Now we can test the ingest body = { 'fileId': str(file['_id']), 'format': 'cjson', 'moleculeId': str(molecule['_id']), 'public': True, 'image': { 'repository': 'openchemistry/psi4', 'tag': 'latest' } } r = server.request('/calculations', method='POST', type='application/json', body=json.dumps(body), user=user) assertStatus(r, 201) calculation = Calculation().load(r.json['_id'], force=True) for prop in ['fileId', 'moleculeId', 'notebooks', 'properties', 'cjson']: assert prop in calculation
def test_get(server, project, cycle, batch, make_batch, user, admin, cycletest): r = server.request( '/edp/projects/%s/cycles/%s/batches/%s/tests/%s' % (project['_id'], cycle['_id'], batch['_id'], cycletest['_id']), method='GET', user=user) assertStatusOk(r) assert cycletest.items() <= r.json.items() # Make another project and try to fetch a test that is not associated # with it body = { 'startDate': datetime.datetime.utcnow().timestamp(), 'title': 'another title', 'experimentalDesign': 'I designed the cool experiment.', 'experimentalNotes': 'These are my notes.', 'dataNotes': 'Here are some notes.', 'motivation': 'I have some.' } another_batch = make_batch(user, project, cycle, body) r = server.request( '/edp/projects/%s/cycles/%s/batches/%s/tests/%s' % (project['_id'], cycle['_id'], another_batch['_id'], cycletest['_id']), method='GET', user=user) assertStatus(r, 400)
def _make_project(user, request): r = server.request('/edp/projects', method='POST', body=json.dumps(request), type='application/json', user=user) assertStatus(r, 201) projects.append(r.json) return r.json
def _make_postmortem(user, project, request): r = server.request('/edp/projects/%s/postmortems' % project['_id'], method='POST', body=json.dumps(request), type='application/json', user=user) assertStatus(r, 201) postmortems.append(r.json) return r.json
def testTokenSessionDeletion(server, token): # With the token should succeed resp = server.request(path='/token/session', method='DELETE', token=token) assertStatusOk(resp) # Now the token is gone, so it should fail resp = server.request(path='/token/session', method='DELETE', token=token) assertStatus(resp, 401)
def testDisableApiKeysSetting(server, user): errMsg = 'API key functionality is disabled on this instance.' resp = server.request('/api_key', method='POST', user=user, params={ 'name': 'test key' }) assertStatusOk(resp) # Disable API keys Setting().set(SettingKey.API_KEYS, False) # Key should still exist key = ApiKey().load(resp.json['_id'], force=True, exc=True) # No longer possible to authenticate with existing key resp = server.request('/api_key/token', method='POST', params={ 'key': key['key'] }) assertStatus(resp, 400) assert resp.json['message'] == errMsg # No longer possible to create new keys resp = server.request('/api_key', method='POST', user=user, params={ 'name': 'should not work' }) assertStatus(resp, 400) assert resp.json['message'] == errMsg # Still possible to delete key resp = server.request('/api_key/%s' % key['_id'], method='DELETE', user=user) assertStatusOk(resp) assert ApiKey().load(key['_id'], force=True) is None
def testUserCannotAccessOtherApiKeys(server, admin, user): # Normal users shouldn't be able to request other users' keys resp = server.request('/api_key', params={'userId': admin['_id']}, user=user) assertStatus(resp, 403) assert resp.json['message'] == 'Administrator access required.'
def testGetResourcePathForUser(server, parentChain, user): # Test access denied response for access 'hidden folder' for user without access rights resp = server.request(path='/resource/%s/path' % parentChain['folder4']['_id'], method='GET', user=user, params={'type': 'folder'}) assertStatus(resp, 403)
def _make_batch(user, project, cycle, request): r = server.request('/edp/projects/%s/cycles/%s/batches' % ( project['_id'], cycle['_id']), method='POST', body=json.dumps(request), type='application/json', user=user) assertStatus(r, 201) batches.append(r.json) return r.json
def test_create_dandiset_no_name(server, request_auth): resp = server.request( path=path, method="POST", params={"description": DESCRIPTION_1}, **request_auth, ) assertStatus(resp, 400)
def testGetResourcePathForNoneUser(server, parentChain): # Test access denied response for access 'hidden folder' for a none User resp = server.request(path='/resource/%s/path' % parentChain['folder4']['_id'], method='GET', user=None, params={'type': 'folder'}) assertStatus(resp, 401)
def testFolderPositionUserNoAccess(server, makeResources, user): resp = server.request( '/folder/%s/position' % str(makeResources['folders'][7]['_id']), user=user, params={ 'parentType': 'folder', 'parentId': str(makeResources['publicFolder']['_id']) }) assertStatus(resp, 403)
def testMoveFolderUnderItselfFails(server, admin, hierarchy): resp = server.request( path='/folder/%s' % hierarchy.folders[0]['_id'], method='PUT', user=admin, params={ 'parentId': hierarchy.folders[1]['_id'], 'parentType': 'folder' }) assertStatus(resp, 400) assert resp.json['message'] == 'You may not move a folder underneath itself.'
def testNoReadme(server, admin, privateFolder): # Verify that the readme endpoint returns 204 No Content when there is no README resp = server.request( path=f'/folder/{privateFolder["_id"]}/readme', method='GET', user=admin, isJson=False, ) assertStatus(resp, 204)
def testLoadModelDecorator(server, user): resp = server.request( path='/accesstest/test_loadmodel_plain/%s' % user['_id'], method='GET') assertStatusOk(resp) assert resp.json['_id'] == str(user['_id']) resp = server.request(path='/accesstest/test_loadmodel_query', params={'userId': None}) assertStatus(resp, 400) assert resp.json['message'] == 'Invalid ObjectId: None'
def testDuplicateParameters(server, method): # In addition to a dict, the urllib.parse.urlencode used by server.request can accept a list # of tuples params = [('foo', 'bar'), ('foo', 'baz')] # Use /system/setting because it has both GET and PUT endpoints path = '/system/version' resp = server.request(path=path, method=method, params=params) assertStatus(resp, 400) assert resp.json['message'] == 'Parameter "foo" must not be specified multiple times.'
def testServerInfoInErrorPage(server): # For security, we want to ensure cherrypy does not appear in server info resp = server.request('/girder/api/v1', prefix='', isJson=False) assertStatus(resp, 404) body = getResponseBody(resp).lower() server = resp.headers['Server'].lower() assert 'cherrypy' not in body + server assert 'girder' in body assert 'girder' in server
def test_create_dandiset_empty_description(server, request_auth): resp = server.request(path=path, method="POST", params={ "name": NAME_1, "description": "" }, **request_auth) assertStatus(resp, 400)
def testExceptionHandlingBasedOnServerMode(exceptionServer, uuidMock, mode, msg, hasTrace): with serverMode(mode): resp = exceptionServer.request('/item/exception', exception=True) assertStatus(resp, 500) assert resp.json['message'] == msg assert resp.json['type'] == 'internal' assert resp.json['uid'] == uuidMock assert ('trace' in resp.json) is hasTrace assert resp.headers['Girder-Request-Uid'] == uuidMock
def deleteImage(self, name, responseCodeOK, deleteDockerImage=False, status=4): """ Delete docker image data and test whether a docker image can be deleted off the local machine """ job_status = [JobStatus.SUCCESS] if deleteDockerImage: event = threading.Event() def tempListener(self, girderEvent): job = girderEvent.info['job'] if (job['type'] == 'slicer_cli_web_job' and job['status'] in (JobStatus.SUCCESS, JobStatus.ERROR)): assert job[ 'status'] == status, 'The status of the job should match' events.unbind('jobs.job.update.after', 'slicer_cli_web_del') job_status[0] = job['status'] event.set() self.delHandler = types.MethodType(tempListener, self) events.bind('jobs.job.update.after', 'slicer_cli_web_del', self.delHandler) resp = self.server.request(path='/slicer_cli_web/docker_image', user=self.admin, method='DELETE', params={ 'name': json.dumps(name), 'delete_from_local_repo': deleteDockerImage }, isJson=False) if responseCodeOK: assertStatus(resp, 200) else: assertStatus(resp, 400) # A status ok or code 200 should not have been recieved for # deleting the image %s' % str(name)) if deleteDockerImage: if not event.wait(TIMEOUT): del self.delHandler raise AssertionError('deleting the docker image is taking ' 'longer than %d seconds' % TIMEOUT) else: del self.delHandler assert job_status[ 0] == status, 'The status of the job should match '
def testWorkerDifferentTask(server, models): # Test the settings resp = server.request('/system/setting', method='PUT', params={ 'key': PluginSettings.API_URL, 'value': 'bad value' }, user=models['admin']) assertStatus(resp, 400) assert resp.json['message'] == 'API URL must start with http:// or https://.' resp = server.request('/system/setting', method='PUT', params={ 'list': json.dumps([{ 'key': PluginSettings.BROKER, 'value': 'amqp://[email protected]' }, { 'key': PluginSettings.BACKEND, 'value': 'amqp://[email protected]' }]) }, user=models['admin']) assertStatusOk(resp) # Create a job to be handled by the worker plugin jobModel = Job() job = jobModel.createJob( title='title', type='foo', handler='worker_handler', user=models['admin'], public=False, args=(), kwargs={}, otherFields={ 'celeryTaskName': 'some_other.task', 'celeryQueue': 'my_other_q' }) job['kwargs'] = { 'jobInfo': utils.jobInfoSpec(job), 'inputs': [ utils.girderInputSpec(models['adminFolder'], resourceType='folder') ], 'outputs': [ utils.girderOutputSpec(models['adminFolder'], token=models['adminToken']) ] } job = jobModel.save(job) # Schedule the job, make sure it is sent to celery app = celery.getCeleryApp() with mock.patch.object(app, 'send_task') as sendTask: sendTask.return_value = FakeAsyncResult() jobModel.scheduleJob(job) sendTaskCalls = sendTask.mock_calls assert len(sendTaskCalls) == 1 assert sendTaskCalls[0][1] == ( 'some_other.task', job['args'], job['kwargs']) assert 'queue' in sendTaskCalls[0][2] assert sendTaskCalls[0][2]['queue'] == 'my_other_q'
def addImage(self, name, status, initialStatus=200): """ Test the put endpoint. :param name: a string or a list of strings :param status: either JobStatus.SUCCESS or JobStatus.ERROR. :param initialStatus: 200 if the job should run, otherwise a HTTP error code expected if the job will fail. """ event = threading.Event() job_status = [JobStatus.SUCCESS] def tempListener(self, girderEvent): job = girderEvent.info['job'] if (job['type'] == 'slicer_cli_web_job' and job['status'] in (JobStatus.SUCCESS, JobStatus.ERROR)): assert job[ 'status'] == status, 'The status of the job should match' job_status[0] = job['status'] events.unbind('jobs.job.update.after', 'slicer_cli_web_add') # wait 10sec before continue threading.Timer(5, lambda: event.set()).start() if initialStatus == 200: self.addHandler = types.MethodType(tempListener, self) events.bind('jobs.job.update.after', 'slicer_cli_web_add', self.addHandler) resp = self.server.request(path='/slicer_cli_web/docker_image', user=self.admin, method='PUT', params={ 'name': json.dumps(name), 'folder': self.folder['_id'] }, isJson=initialStatus == 200) assertStatus(resp, initialStatus) if initialStatus != 200: return # We should have a job ID assert resp.json.get('_id') is not None if not event.wait(TIMEOUT): del self.addHandler raise AssertionError('adding the docker image is taking ' 'longer than %d seconds' % TIMEOUT) else: del self.addHandler job_status[0] == status, 'The status of the job should match '
def testMoveFolderUnderItselfFails(server, admin, hierarchy): resp = server.request(path='/folder/%s' % hierarchy.folders[0]['_id'], method='PUT', user=admin, params={ 'parentId': hierarchy.folders[1]['_id'], 'parentType': 'folder' }) assertStatus(resp, 400) assert resp.json[ 'message'] == 'You may not move a folder underneath itself.'
def testLoadModelDecorator(server, user): resp = server.request(path='/accesstest/test_loadmodel_plain/%s' % user['_id'], method='GET') assertStatusOk(resp) assert resp.json['_id'] == str(user['_id']) resp = server.request(path='/accesstest/test_loadmodel_query', params={'userId': None}) assertStatus(resp, 400) assert resp.json['message'] == 'Invalid ObjectId: None'
def cycletest(server, user, project, cycle, batch, create_cycletest_request): from girder.plugins.edp.models.cycletest import CycleTest r = server.request('/edp/projects/%s/cycles/%s/batches/%s/tests' % ( project['_id'], cycle['_id'], batch['_id']), method='POST', body=json.dumps(create_cycletest_request), type='application/json', user=user) assertStatus(r, 201) yield r.json CycleTest().remove(r.json, user)
def test_update_non_existent(server, user, project): from girder.plugins.edp.models.project import Project updates = { 'title': 'Nothing to see here.' } non_existent = '5ae71e1ff657102b11ce2233' r = server.request('/edp/projects/%s' % non_existent, method='PATCH', body=json.dumps(updates), type='application/json', user=user) assertStatus(r, 400)
def postmortemtest(server, user, project, postmortem, create_postmortemtest_request): from girder.plugins.edp.models.postmortemtest import PostmortemTest r = server.request('/edp/projects/%s/postmortems/%s/tests' % ( project['_id'], postmortem['_id']), method='POST', body=json.dumps(create_postmortemtest_request), type='application/json', user=user) assertStatus(r, 201) yield r.json PostmortemTest().remove(r.json, user)
def test_create_public(server, user, project_request): from girder.plugins.edp.models.project import Project r = server.request('/edp/projects', method='POST', body=json.dumps(project_request), type='application/json', user=user) assertStatus(r, 201) assert '_id' in r.json project = Project().load(r.json['_id'], force=True) assert project['owner'] == user['_id'] assert project_request.items() <= project.items()
def testGetResourceByPathForUser(server, parentChain, admin, user): # Test access denied response for access 'hidden folder' for user without access rights resp = server.request(path='/resource/lookup', method='GET', user=user, params={ 'path': '/user/%s/%s/%s/%s/%s' % ( admin['login'], parentChain['folder1']['name'], parentChain['folder2']['name'], parentChain['privateFolder']['name'], parentChain['folder4']['name']) }) assertStatus(resp, 400)
def testDirectPathSettingValidation(server, models): # Test the setting resp = server.request('/system/setting', method='PUT', params={ 'key': PluginSettings.DIRECT_PATH, 'value': 'bad value' }, user=models['admin']) assertStatus(resp, 400) assert resp.json['message'] == 'The direct path setting must be true or false.' resp = server.request('/system/setting', method='PUT', params={ 'key': PluginSettings.DIRECT_PATH, 'value': 'false' }, user=models['admin']) assertStatusOk(resp)
def test_update_non_existent(server, user, project, cycle, batch): from girder.plugins.edp.models.batch import Batch updates = {'title': 'Nothing to see here.', 'dataNotes': 'Notes'} non_existent = '5ae71e1ff657102b11ce2233' r = server.request('/edp/projects/%s/cycles/%s/batches/%s' % (project['_id'], cycle['_id'], non_existent), method='PATCH', body=json.dumps(updates), type='application/json', user=user) assertStatus(r, 400)
def testDockerAddBadParam(server, admin, folder): # test sending bad parameters to the PUT endpoint kwargs = { 'path': '/slicer_cli_web/docker_image', 'user': admin, 'method': 'PUT', 'params': { 'name': json.dumps(6), 'folder': folder['_id'] } } resp = server.request(**kwargs) assertStatus(resp, 400) assert 'A valid string' in resp.json['message'] kwargs['params']['name'] = json.dumps({'abc': 'def'}) resp = server.request(**kwargs) assertStatus(resp, 400) assert 'A valid string' in resp.json['message'] kwargs['params']['name'] = json.dumps([6]) resp = server.request(**kwargs) assertStatus(resp, 400) assert 'is not a valid string' in resp.json['message'] kwargs['params']['name'] = '"not json' resp = server.request(**kwargs) assertStatus(resp, 400) assert 'does not have a tag' in resp.json['message']
def testInactiveKeysCannotCreateTokens(server, user, apiKey): newScopes = [TokenScope.DATA_READ, TokenScope.DATA_WRITE] resp = server.request('/api_key/%s' % apiKey['_id'], params={ 'active': False, 'tokenDuration': 10, 'scope': json.dumps(newScopes) }, method='PUT', user=user) assertStatusOk(resp) # We should not be able to create tokens for this key anymore resp = server.request('/api_key/token', method='POST', params={ 'key': apiKey['key'] }) assertStatus(resp, 400) assert resp.json['message'] == 'Invalid API key.'
def test_create_calc(server, molecule, user): from girder.plugins.molecules.models.calculation import Calculation from girder.constants import AccessType assert '_id' in molecule dir_path = os.path.dirname(os.path.realpath(__file__)) # This cjson should match the molecule with open(dir_path + '/data/ethane.cjson', 'r') as rf: ethane_cjson = rf.read() # Let's make some properties properties = { 'molecular mass': 30.0690, 'melting point': -172, 'boiling point': -88, 'code': 'nwchem' } body = { 'cjson': ethane_cjson, 'properties': properties, 'moleculeId': molecule['_id'] } r = server.request('/calculations', method='POST', body=json.dumps(body), type='application/json', user=user) assertStatus(r, 201) calc = r.json assert '_id' in calc assert 'moleculeId' in calc calc_id = str(calc['_id']) molecule_id = calc['moleculeId'] calc2 = Calculation().load(calc_id, level=AccessType.READ, user=user) # It should have an _id and a molecule id, and it should match assert '_id' in calc2 assert 'moleculeId' in calc2 assert str(calc2['_id']) == calc_id assert str(calc2['moleculeId']) == molecule_id
def test_ingest_pending(server, molecule, user, make_girder_file, fsAssetstore): body = { 'moleculeId': molecule['_id'], 'cjson': None, 'public': True, 'properties': { 'calculationTypes': 'energy;', 'basisSet': { 'name': '3-21g' }, 'theory': 'b3lyp', 'pending': True, 'code': 'nwchem' } } # First create pending calculation r = server.request('/calculations', method='POST', type='application/json', body=json.dumps(body), user=user) assertStatus(r, 201) calculation = r.json # Upload simulation result dir_path = os.path.dirname(os.path.realpath(__file__)) with open(os.path.join(dir_path, 'data', 'ethane.cjson')) as f: file = make_girder_file(fsAssetstore, user, 'ethane.cjson', contents=f.read().encode()) # Now we can test the ingest body = { 'fileId': str(file['_id']), 'format': 'cjson', 'public': True } r = server.request('/calculations/%s' % calculation['_id'], method='PUT', type='application/json', body=json.dumps(body), user=user) assertStatusOk(r) calculation = r.json assert 'pending' not in calculation['properties']
def testRequiredScopeExists(server, user): token = Token().createToken(scope=CUSTOM_SCOPE) resp = server.request(path='/accesstest/test_required_scope_exists') # If not given a user or a valid auth token the status should be 401 assertStatus(resp, 401) resp2 = server.request(path='/accesstest/test_required_scope_exists', user=user) # If the token does not have the CUSTOM_SCOPE the status should be 403 assertStatus(resp2, 403) # If user is not given but the token has the correct scope # the status should be 200 resp3 = server.request(path='/accesstest/test_required_scope_exists', token=token) assertStatus(resp3, 200)
def testCookieAuthFailsOnPost(server, user, cookie): resp = server.request(path='/accesstest/cookie_auth', method='POST', cookie=cookie) assertStatus(resp, 401)
def testOtpApiWorkflow(server, user): # Try to finalize OTP before it's been initialized resp = server.request( path='/user/%s/otp' % user['_id'], method='PUT', user=user, additionalHeaders=[('Girder-OTP', '123456')]) # This should fail cleanly assertStatus(resp, 400) assert 'not initialized' in resp.json['message'] # Try to disable OTP before it's been enabled resp = server.request(path='/user/%s/otp' % user['_id'], method='DELETE', user=user) # This should fail cleanly assertStatus(resp, 400) assert 'not enabled' in resp.json['message'] # Initialize OTP resp = server.request(path='/user/%s/otp' % user['_id'], method='POST', user=user) assertStatusOk(resp) # Save the URI totpUri = resp.json['totpUri'] # Test the logic for server hostname as OTP URI issuer assert 'issuer=127.0.0.1' in totpUri # Login without an OTP resp = server.request(path='/user/authentication', method='GET', basicAuth='user:password') # Since OTP has not been finalized, this should still succeed assertStatusOk(resp) # Finalize without an OTP resp = server.request( path='/user/%s/otp' % user['_id'], method='PUT', user=user) assertStatus(resp, 400) assert 'Girder-OTP' in resp.json['message'] # Finalize with an invalid OTP resp = server.request( path='/user/%s/otp' % user['_id'], method='PUT', user=user, additionalHeaders=[('Girder-OTP', _tokenFromTotpUri(totpUri, False))]) assertStatus(resp, 403) assert 'validation failed' in resp.json['message'] # Finalize with a valid OTP resp = server.request( path='/user/%s/otp' % user['_id'], method='PUT', user=user, additionalHeaders=[('Girder-OTP', _tokenFromTotpUri(totpUri))]) assertStatusOk(resp) # The valid token from this time period was used to finalize OTP; to prevent having to wait for # the next time period, flush the rateLimitBuffer from girder.utility._cache import rateLimitBuffer rateLimitBuffer.invalidate() # Login without an OTP resp = server.request(path='/user/authentication', method='GET', basicAuth='user:password') assertStatus(resp, 401) assert 'Girder-OTP' in resp.json['message'] # Login with an invalid OTP resp = server.request( path='/user/authentication', method='GET', basicAuth='user:password', additionalHeaders=[('Girder-OTP', _tokenFromTotpUri(totpUri, False))]) assertStatus(resp, 401) assert 'Token did not match' in resp.json['message'] # Login with a valid OTP resp = server.request( path='/user/authentication', method='GET', basicAuth='user:password', additionalHeaders=[('Girder-OTP', _tokenFromTotpUri(totpUri))]) assertStatusOk(resp) # Disable OTP resp = server.request(path='/user/%s/otp' % user['_id'], method='DELETE', user=user) assertStatusOk(resp)
def testInviteNewUser(provisionedServer, smtp): # Create a study admin user resp = provisionedServer.request(path='/user', method='POST', params={ 'email': '*****@*****.**', 'login': '******', 'firstName': 'study admin', 'lastName': 'user', 'password': '******' }) assertStatusOk(resp) studyAdminUser = User().findOne({'login': '******'}) assert studyAdminUser is not None # Ensure that user doesn't have permission to invite a new user, yet resp = provisionedServer.request(path='/user/invite', method='POST', params={ 'login': '******', 'email': '*****@*****.**', 'firstName': 'invited', 'lastName': 'user' }, user=studyAdminUser) assertStatus(resp, 403) # Add the user to the study admins group studyAdminsGroup = Group().findOne({'name': 'Study Administrators'}) assert studyAdminsGroup is not None Group().addUser(studyAdminsGroup, studyAdminUser, level=AccessType.READ) # Ensure that user can invite a new user resp = provisionedServer.request(path='/user/invite', method='POST', params={ 'login': '******', 'email': '*****@*****.**', 'firstName': 'invited', 'lastName': 'user' }, user=studyAdminUser) assertStatusOk(resp) assert 'newUser' in resp.json assert 'inviteUrl' in resp.json for key in ('login', 'firstName', 'lastName', 'name'): assert key in resp.json['newUser'] assert resp.json['newUser']['login'] == 'invited-user' assert resp.json['newUser']['firstName'] == 'invited' assert resp.json['newUser']['lastName'] == 'user' assert resp.json['newUser']['name'] assert resp.json['inviteUrl'] assert smtp.waitForMail() assert smtp.getMail() # pop off the queue for later assertion that the queue is empty # Ensure that user can invite a new user and specify the validity period resp = provisionedServer.request(path='/user/invite', method='POST', params={ 'login': '******', 'email': '*****@*****.**', 'firstName': 'invited', 'lastName': 'user2', 'validityPeriod': 15.0 }, user=studyAdminUser) assertStatusOk(resp) assert 'newUser' in resp.json assert 'inviteUrl' in resp.json for key in ('login', 'firstName', 'lastName', 'name'): assert key in resp.json['newUser'] assert resp.json['newUser']['login'] == 'invited-user2' assert resp.json['newUser']['firstName'] == 'invited' assert resp.json['newUser']['lastName'] == 'user2' assert resp.json['newUser']['name'] assert resp.json['inviteUrl'] assert smtp.waitForMail() assert smtp.getMail() # pop off the queue for later assertion that the queue is empty # Test sending an invalid value for the validity period resp = provisionedServer.request(path='/user/invite', method='POST', params={ 'login': '******', 'email': '*****@*****.**', 'firstName': 'invited', 'lastName': 'user3', 'validityPeriod': 'invalid' }, user=studyAdminUser) assertStatus(resp, 400) assert resp.json['type'] == 'validation' assert resp.json.get('field') == 'validityPeriod'
def testBasicUser(provisionedServer): # Create a basic user resp = provisionedServer.request(path='/user', method='POST', params={ 'email': '*****@*****.**', 'login': '******', 'firstName': 'test', 'lastName': 'user', 'password': '******' }) assertStatusOk(resp) testUser = User().findOne({'login': '******'}) assert testUser is not None # Ensure creation returns permissions negativePermissions = { 'acceptTerms': False, 'createDataset': False, 'reviewDataset': False, 'segmentationSkill': None, 'adminStudy': False } assert resp.json.get('permissions') == negativePermissions # Ensure login returns permissions resp = provisionedServer.request(path='/user/authentication', method='GET', basicAuth='test-user:password') assertStatusOk(resp) assert resp.json['user'].get('permissions') == negativePermissions # Ensure get user returns permissions resp = provisionedServer.request(path='/user/me', method='GET', user=testUser) assertStatusOk(resp) assert resp.json.get('permissions') == negativePermissions # Ensure get user for anonymous still succeeds resp = provisionedServer.request(path='/user/me', method='GET') assertStatusOk(resp) assert resp.json is None # Ensure user is private resp = provisionedServer.request(path='/user/%s' % testUser['_id'], method='GET') assertStatus(resp, 401) # Ensure accept terms works resp = provisionedServer.request(path='/user/acceptTerms', method='POST', user=testUser) assertStatusOk(resp) assert resp.json.get('extra') == 'hasPermission' resp = provisionedServer.request(path='/user/me', method='GET', user=testUser) assertStatusOk(resp) acceptedTermsPermissions = negativePermissions.copy() acceptedTermsPermissions['acceptTerms'] = True assert resp.json.get('permissions') == acceptedTermsPermissions # Ensure accepting terms twice is idempotent testUser = User().findOne({'login': '******'}) uploaderUserAcceptTermsTime = testUser['acceptTerms'] resp = provisionedServer.request(path='/user/acceptTerms', method='POST', user=testUser) assertStatusOk(resp) assert resp.json.get('extra') == 'hasPermission' testUser = User().findOne({'login': '******'}) assert testUser['acceptTerms'] == uploaderUserAcceptTermsTime
def testUserCannotAccessAdminEndpoints(server, user, endpoint): resp = server.request(path=endpoint, method='GET', user=user) assertStatus(resp, 403)
def testTokenSessionDeletionFailsWithoutToken(server): # Trying to delete a token without specifying one results in an error resp = server.request(path='/token/session', method='DELETE') assertStatus(resp, 401)
def testUserAccessToken(server, userDataReadToken): resp = server.request(path='/accesstest/user_access', token=userDataReadToken) assertStatus(resp, 401)
def testAdminRawDecoratorIsEquivalentToReturnedDecorator(server, adminSettingToken, endpoint): resp = server.request(path=endpoint, token=adminSettingToken) assertStatus(resp, 401)
def testReadingAssetstoreWithSettingScopedToken(server, adminSettingToken): # The setting-scope token should not grant access to other endpoints resp = server.request(path='/assetstore', token=adminSettingToken) assertStatus(resp, 401)
def testReadingSettingsWithUserToken(server, userSettingToken): # Non-admin user with this token scope should still not work resp = server.request(path='/system/setting', params={ 'key': SettingKey.SMTP_PORT}, token=userSettingToken) assertStatus(resp, 403) assert resp.json['message'] == 'Administrator access required.'
def testReadingSettingsWithAdminEmailToken(server, adminEmailToken): # Reading settings with an improperly scoped token should fail resp = server.request(path='/system/setting', params={ 'key': SettingKey.SMTP_PORT}, token=adminEmailToken) assertStatus(resp, 401)
def testCookieAuthFailsWithNoAuth(server, endpoint, method): resp = server.request(path='/accesstest/%s' % endpoint, method=method) assertStatus(resp, 401)
def testPublicCannotAccessNonPublicEndpoints(server, endpoint): resp = server.request(path=endpoint, method='GET') assertStatus(resp, 401)
def testListNotificationsAuthError(server): resp = server.request(path='/notification') assertStatus(resp, 401)
def testApiRedirect(server): resp = server.request('/api', prefix='', isJson=False) assertStatus(resp, 303) assert urlparse(resp.headers['Location']).path == '/api/v1'
def testReadingSettingsAsUserShouldFail(server, user): # Reading setting as non-admin should fail resp = server.request(path='/system/setting', params={ 'key': SettingKey.SMTP_PORT}, user=user) assertStatus(resp, 403)
def testCookieAuthFailsWithNoAuth(server, method): resp = server.request(path='/accesstest/cookie_auth', method=method) assertStatus(resp, 401)