def _onUpload(event): """ Look at uploads containing references related to this plugin. If found, they are used to link item task outputs back to a job document. """ try: ref = json.loads(event.info.get('reference', '')) except ValueError: return if isinstance(ref, dict) and ref.get('type') == 'item_tasks.output': jobModel = Job() tokenModel = Token() token = event.info['currentToken'] if tokenModel.hasScope(token, 'item_tasks.job_write:%s' % ref['jobId']): job = jobModel.load(ref['jobId'], force=True, exc=True) else: job = jobModel.load( ref['jobId'], level=AccessType.WRITE, user=event.info['currentUser'], exc=True) file = event.info['file'] item = Item().load(file['itemId'], force=True) # Add link to job model to the output item jobModel.updateJob(job, otherFields={ 'itemTaskBindings.outputs.%s.itemId' % ref['id']: item['_id'] }) # Also a link in the item to the job that created it item['createdByJob'] = job['_id'] Item().save(item)
def ensureTokenScopes(token, scope): """ Call this to validate a token scope for endpoints that require tokens other than a user authentication token. Raises an AccessException if the required scopes are not allowed by the given token. :param token: The token object used in the request. :type token: dict :param scope: The required scope or set of scopes. :type scope: `str or list of str` """ tokenModel = Token() if tokenModel.hasScope(token, TokenScope.USER_AUTH): return if not tokenModel.hasScope(token, scope): setCurrentUser(None) if isinstance(scope, six.string_types): scope = (scope,) raise AccessException( 'Invalid token scope.\n' 'Required: %s.\n' 'Allowed: %s' % ( ' '.join(scope), '' if token is None else ' '.join(tokenModel.getAllowedScopes(token))))
def verifyEmail(self, user, token): token = Token().load( token, user=user, level=AccessType.ADMIN, objectId=False, exc=True) delta = (token['expires'] - datetime.datetime.utcnow()).total_seconds() hasScope = Token().hasScope(token, TokenScope.EMAIL_VERIFICATION) if token.get('userId') != user['_id'] or delta <= 0 or not hasScope: raise AccessException('The token is invalid or expired.') user['emailVerified'] = True Token().remove(token) user = self._model.save(user) if self._model.canLogin(user): setCurrentUser(user) authToken = self.sendAuthTokenCookie(user) return { 'user': self._model.filter(user, user), 'authToken': { 'token': authToken['_id'], 'expires': authToken['expires'], 'scope': authToken['scope'] }, 'message': 'Email verification succeeded.' } else: return { 'user': self._model.filter(user, user), 'message': 'Email verification succeeded.' }
class Token(Resource): """API Endpoint for non-user tokens in the system.""" def __init__(self): super(Token, self).__init__() self.resourceName = 'token' self._model = TokenModel() self.route('DELETE', ('session',), self.deleteSession) self.route('GET', ('session',), self.getSession) self.route('GET', ('current',), self.currentSession) self.route('GET', ('scopes',), self.listScopes) @access.public @autoDescribeRoute( Description('Retrieve the current session information.') .responseClass('Token') ) def currentSession(self): return self.getCurrentToken() @access.public @autoDescribeRoute( Description('Get an anonymous session token for the system.') .notes('If you are logged in, this will return a token associated with that login.') .responseClass('Token') ) def getSession(self): token = self.getCurrentToken() # Only create and send new cookie if token isn't valid or will expire soon if not token: token = self.sendAuthTokenCookie(None, scope=TokenScope.ANONYMOUS_SESSION) return { 'token': token['_id'], 'expires': token['expires'] } @access.token @autoDescribeRoute( Description('Remove a session from the system.') .responseClass('Token') .notes('Attempts to delete your authentication cookie.') ) def deleteSession(self): token = self.getCurrentToken() if token: self._model.remove(token) self.deleteAuthTokenCookie() return {'message': 'Session deleted.'} @access.public @autoDescribeRoute( Description('List all token scopes available in the system.') ) def listScopes(self): return TokenScope.listScopes()
def testRequireScope(db): scope = TokenScope.DATA_OWN anotherScope = TokenScope.SETTINGS_READ tokenModel = Token() token = tokenModel.createToken(scope=scope) # If specified scope does not exist raise an error with pytest.raises(AccessException): tokenModel.requireScope(token, anotherScope)
def testHasScope(db): scope = TokenScope.DATA_READ tokenModel = Token() token = tokenModel.createToken(scope=scope) # If token is None should return False assert not tokenModel.hasScope(None, scope) # If scope is None should return True assert tokenModel.hasScope(token, None)
def executeTask(self, item, jobTitle, includeJobInfo, inputs, outputs): user = self.getCurrentUser() if jobTitle is None: jobTitle = item['name'] task, handler = self._validateTask(item) if task.get('mode') == 'girder_worker': return runCeleryTask(item['meta']['itemTaskImport'], inputs) jobModel = self.model('job', 'jobs') jobModel = Job() job = jobModel.createJob( title=jobTitle, type='item_task', handler=handler, user=user) # If this is a user auth token, we make an IO-enabled token token = self.getCurrentToken() tokenModel = Token() if tokenModel.hasScope(token, TokenScope.USER_AUTH): token = tokenModel.createToken( user=user, days=7, scope=(TokenScope.DATA_READ, TokenScope.DATA_WRITE)) job['itemTaskTempToken'] = token['_id'] token = tokenModel.addScope(token, 'item_tasks.job_write:%s' % job['_id']) job.update({ 'itemTaskId': item['_id'], 'itemTaskBindings': { 'inputs': inputs, 'outputs': outputs }, 'kwargs': { 'task': task, 'inputs': self._transformInputs(inputs, token), 'outputs': self._transformOutputs(outputs, token, job, task, item['_id']), 'validate': False, 'auto_convert': False, 'cleanup': True } }) if includeJobInfo: job['kwargs']['jobInfo'] = utils.jobInfoSpec(job) if 'itemTaskCeleryQueue' in item.get('meta', {}): job['celeryQueue'] = item['meta']['itemTaskCeleryQueue'] job = jobModel.save(job) jobModel.scheduleJob(job) return job
def _authorizeInitUpload(event): """ Called when initializing an upload, prior to the default handler. Checks if the user is passing an authorized upload token, and if so, sets the current request-thread user to be whoever created the token. """ token = getCurrentToken() params = event.info['params'] tokenModel = Token() parentType = params.get('parentType') parentId = params.get('parentId', '') requiredScopes = {TOKEN_SCOPE_AUTHORIZED_UPLOAD, 'authorized_upload_folder_%s' % parentId} if parentType == 'folder' and tokenModel.hasScope(token=token, scope=requiredScopes): user = User().load(token['userId'], force=True) setCurrentUser(user)
def _storeUploadId(event): """ Called after an upload is first initialized successfully. Sets the authorized upload ID in the token, ensuring it can be used for only this upload. """ returnVal = event.info['returnVal'] token = getCurrentToken() tokenModel = Token() isAuthorizedUpload = tokenModel.hasScope(token, TOKEN_SCOPE_AUTHORIZED_UPLOAD) if isAuthorizedUpload and returnVal.get('_modelType', 'upload') == 'upload': params = event.info['params'] token['scope'].remove(TOKEN_SCOPE_AUTHORIZED_UPLOAD) token['authorizedUploadId'] = returnVal['_id'] token['authorizedUploadDescription'] = params.get('authorizedUploadDescription', '') token['authorizedUploadEmail'] = params.get('authorizedUploadEmail') tokenModel.save(token)
def __init__(self): super(Token, self).__init__() self.resourceName = 'token' self._model = TokenModel() self.route('DELETE', ('session',), self.deleteSession) self.route('GET', ('session',), self.getSession) self.route('GET', ('current',), self.currentSession) self.route('GET', ('scopes',), self.listScopes)
def changePassword(self, old, new): user = self.getCurrentUser() token = None if not old: raise RestException('Old password must not be empty.') if not Password().hasPassword(user) or not Password().authenticate(user, old): # If not the user's actual password, check for temp access token token = Token().load(old, force=True, objectId=False, exc=False) if (not token or not token.get('userId') or token['userId'] != user['_id'] or not Token().hasScope(token, TokenScope.TEMPORARY_USER_AUTH)): raise AccessException('Old password is incorrect.') self._model.setPassword(user, new) if token: # Remove the temporary access token if one was used Token().remove(token) return {'message': 'Password changed.'}
def checkTemporaryPassword(self, user, token): token = Token().load( token, user=user, level=AccessType.ADMIN, objectId=False, exc=True) delta = (token['expires'] - datetime.datetime.utcnow()).total_seconds() hasScope = Token().hasScope(token, TokenScope.TEMPORARY_USER_AUTH) if token.get('userId') != user['_id'] or delta <= 0 or not hasScope: raise AccessException('The token does not grant temporary access to this user.') # Temp auth is verified, send an actual auth token now. We keep the # temp token around since it can still be used on a subsequent request # to change the password authToken = self.sendAuthTokenCookie(user) return { 'user': self._model.filter(user, user), 'authToken': { 'token': authToken['_id'], 'expires': authToken['expires'], 'temporary': True }, 'message': 'Temporary access token is valid.' }
def ensureTokenScopes(token, scope): """ Call this to validate a token scope for endpoints that require tokens other than a user authentication token. Raises an AccessException if the required scopes are not allowed by the given token. :param token: The token object used in the request. :type token: dict :param scope: The required scope or set of scopes. :type scope: `str or list of str` """ tokenModel = Token() if tokenModel.hasScope(token, TokenScope.USER_AUTH): return if not tokenModel.hasScope(token, scope): setCurrentUser(None) if isinstance(scope, six.string_types): scope = (scope, ) raise AccessException( 'Invalid token scope.\n' 'Required: %s.\n' 'Allowed: %s' % (' '.join(scope), ' '.join(tokenModel.getAllowedScopes(token))))
def testTokenCreationDuration(server, user, apiKey): defaultDuration = Setting().get(SettingKey.COOKIE_LIFETIME) # We should be able to request a duration shorter than default resp = server.request('/api_key/token', method='POST', params={ 'key': apiKey['key'], 'duration': defaultDuration - 1 }) assertStatusOk(resp) token = Token().load(resp.json['authToken']['token'], force=True, objectId=False) duration = token['expires'] - token['created'] assert duration == datetime.timedelta(days=defaultDuration - 1)
def models(fsAssetstore, admin, user): adminFolder = six.next(Folder().childFolders( parent=admin, parentType='user', user=admin)) adminToken = Token().createToken(admin) sampleData = b'Hello world' sampleFile = Upload().uploadFromFile( obj=six.BytesIO(sampleData), size=len(sampleData), name='Sample', parentType='folder', parent=adminFolder, user=admin) return { 'user': user, 'admin': admin, 'adminFolder': adminFolder, 'adminToken': adminToken, 'sampleFile': sampleFile }
def createAuthorizedUpload(self, folder, params): try: if params.get('duration'): days = int(params.get('duration')) else: days = Setting().get(SettingKey.COOKIE_LIFETIME) except ValueError: raise ValidationException('Token duration must be an integer, or leave it empty.') token = Token().createToken(days=days, user=self.getCurrentUser(), scope=( TOKEN_SCOPE_AUTHORIZED_UPLOAD, 'authorized_upload_folder_%s' % folder['_id'])) url = '%s#authorized_upload/%s/%s' % ( mail_utils.getEmailUrlPrefix(), folder['_id'], token['_id']) return {'url': url}
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 cliHandler(resource, params): from .girder_worker_plugin.direct_docker_run import run user = resource.getCurrentUser() currentItem = CLIItem.find(itemId, user) if not currentItem: raise RestException('Invalid CLI Item id (%s).' % (itemId)) # Create a new token for this job; otherwise, the user could log out # and the job would fail to finish. We may want to override the # duration of this token (it defaults to the setting for cookie # lifetime). token = Token().createToken(user=user) if hasattr(getCurrentToken, 'set'): getCurrentToken.set(token) container_args = [currentItem.name] reference = { 'slicer_cli_web': { 'title': cliTitle, 'image': currentItem.image, 'name': currentItem.name, } } args, result_hooks, primary_input_name = prepare_task( params, user, token, index_params, opt_params, has_simple_return_file, reference) container_args.extend(args) jobType = '%s#%s' % (currentItem.image, currentItem.name) if primary_input_name: jobTitle = '%s on %s' % (cliTitle, primary_input_name) else: jobTitle = cliTitle job = run.delay(girder_user=user, girder_job_type=jobType, girder_job_title=jobTitle, girder_result_hooks=result_hooks, image=currentItem.digest, pull_image='if-not-present', container_args=container_args) return job.job
def sendAuthTokenCookie(self, user=None, scope=None, token=None, days=None): """ Helper method to send the authentication cookie """ if days is None: days = float(Setting().get(SettingKey.COOKIE_LIFETIME)) if token is None: token = Token().createToken(user, days=days, scope=scope) cookie = cherrypy.response.cookie cookie['girderToken'] = str(token['_id']) cookie['girderToken']['path'] = '/' cookie['girderToken']['expires'] = int(days * 3600 * 24) if Setting().get(SettingKey.SECURE_COOKIE): cookie['girderToken']['secure'] = True return token
def run_pipeline_task(self, folder, pipeline): user = self.getCurrentUser() token = Token().createToken(user=user, days=1) move_existing_result_to_auxiliary_folder(folder, user) input_type = folder["meta"]["type"] return run_pipeline.apply_async( queue="pipelines", kwargs=dict( input_path=GetPathFromFolderId(str(folder["_id"])), output_folder=str(folder["_id"]), pipeline=pipeline, input_type=input_type, girder_job_title=( "Running {} on {}".format(pipeline, str(folder["name"])) ), girder_client_token=str(token["_id"]), girder_job_type="pipelines", ), )
def setUp(self): base.TestCase.setUp(self) self.users = [ User().createUser('usr' + str(n), 'passwd', 'tst', 'usr', '*****@*****.**' % n) for n in range(2) ] self.admin = self.users[0] self.adminFolder = six.next(Folder().childFolders(parent=self.admin, parentType='user', user=self.admin)) self.adminToken = Token().createToken(self.admin) sampleData = b'Hello world' self.sampleFile = Upload().uploadFromFile(obj=six.BytesIO(sampleData), size=len(sampleData), name='Sample', parentType='folder', parent=self.adminFolder, user=self.admin)
def generateTemporaryPassword(self, email): user = self._model.findOne({'email': email.lower()}) if not user: raise RestException('That email is not registered.') token = Token().createToken(user, days=1, scope=TokenScope.TEMPORARY_USER_AUTH) url = '%s#useraccount/%s/token/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id']), str(token['_id'])) html = mail_utils.renderTemplate('temporaryAccess.mako', { 'url': url, 'token': str(token['_id']) }) mail_utils.sendEmail( to=email, subject='%s: Temporary access' % Setting().get(SettingKey.BRAND_NAME), text=html ) return {'message': 'Sent temporary access email.'}
def testArtificialScopedAccess(server, admin, user, userDataReadToken, userToken): # Test public access for route in ('public_access', 'fn_public', 'scoped_public'): path = '/accesstest/%s' % route for t in (userDataReadToken, None): resp = server.request(path=path, token=t) assertStatusOk(resp) assert resp.json is None resp = server.request(path=path, token=userToken) assertStatusOk(resp) assert resp.json['_id'] == str(user['_id']) # Make a correctly scoped token, should work. token = Token().createToken(user=user, scope=TokenScope.SETTINGS_READ) resp = server.request(path=path, token=token) assertStatusOk(resp) assert resp.json['_id'] == str(user['_id'])
def buildHeaders(headers, cookie, user, token, basicAuth, authHeader): from girder.models.token import Token headers = headers[:] if cookie is not None: headers.append(('Cookie', cookie)) if user is not None: token = Token().createToken(user) headers.append(('Girder-Token', str(token['_id']))) elif token is not None: if isinstance(token, dict): headers.append(('Girder-Token', token['_id'])) else: headers.append(('Girder-Token', token)) if basicAuth is not None: auth = base64.b64encode(basicAuth.encode('utf8')) headers.append((authHeader, 'Basic %s' % auth.decode())) return headers
def testDeactivatingKeyDeletesAssociatedTokens(server, user, apiKey): resp = server.request('/api_key/token', method='POST', params={'key': apiKey['key']}) assertStatusOk(resp) 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) # This should have deleted all corresponding tokens q = {'userId': user['_id'], 'apiKeyId': apiKey['_id']} count = Token().find(q).count() assert count == 0
def inviteUser(self, params): params = self._decodeParams(params) self.requireParams(['login', 'email', 'firstName', 'lastName'], params) if 'validityPeriod' in params: try: validityPeriod = float(params['validityPeriod']) except ValueError: raise ValidationException('Validity period must be a number.', 'validityPeriod') else: validityPeriod = 60.0 currentUser = self.getCurrentUser() User().requireAdminStudy(currentUser) newUser = User().createUser(login=params['login'], password=None, email=params['email'], firstName=params['firstName'], lastName=params['lastName']) token = Token().createToken(newUser, days=validityPeriod, scope=[TokenScope.TEMPORARY_USER_AUTH]) inviteUrl = '%s/#user/%s/rsvp/%s' % (mail_utils.getEmailUrlPrefix(), newUser['_id'], token['_id']) html = mail_utils.renderTemplate('inviteUser.mako', { 'newUser': newUser, 'inviteUrl': inviteUrl, }) mail_utils.sendEmail(to=newUser['email'], subject='ISIC Archive: Invitation', text=html) return { 'newUser': User().filterSummary(newUser, currentUser), 'inviteUrl': inviteUrl }
def testCreateJobRest(self): resp = self.request('/job', method='POST', user=self.users[0], params={ 'title': 'job', 'type': 'job' }) # If user does not have the necessary token status is 403 self.assertStatus(resp, 403) token = Token().createToken(scope=REST_CREATE_JOB_TOKEN_SCOPE) resp2 = self.request('/job', method='POST', token=token, params={ 'title': 'job', 'type': 'job' }) # If user has the necessary token status is 200 self.assertStatusOk(resp2)
def testTokenCreation(server, user, apiKey): defaultDuration = Setting().get(SettingKey.COOKIE_LIFETIME) # Create a token using the key resp = server.request('/api_key/token', method='POST', params={ 'key': apiKey['key'], 'duration': defaultDuration + 1000 }) assertStatusOk(resp) token = Token().load(resp.json['authToken']['token'], force=True, objectId=False) # Make sure token has full user auth access assert token['userId'] == user['_id'] assert token['scope'] == [TokenScope.USER_AUTH] # Make sure the token references the API key used to create it assert token['apiKeyId'] == apiKey['_id'] # Make sure the token duration is not longer than the default duration = token['expires'] - token['created'] assert duration == datetime.timedelta(days=defaultDuration)
def test_docker_run_transfer_encoding_stream(self, params): item_id = params.get('itemId') file_id = params.get('fileId') delimiter = params.get('delimiter') headers = { 'Girder-Token': str(Token().createToken(getCurrentUser())['_id']) } url = '%s/%s?itemId=%s&delimiter=%s' % ( getApiUrl(), 'integration_tests/docker/input_stream', item_id, delimiter) container_args = [ 'read_write', '-i', GirderFileIdToVolume(file_id), '-o', Connect(NamedOutputPipe('out'), ChunkedTransferEncodingStream(url, headers)) ] result = docker_run.delay( TEST_IMAGE, pull_image=True, container_args=container_args, remove_container=True) return result.job
def __init__(self, *args, **kwargs): gc = kwargs.pop('gc', None) try: if gc is None: # We need to resolve Girder's API URL, but girder_worker can # specify a different value than what Girder gets from a rest # request. # Girder 3 try: from girder_worker.girder_plugin.utils import getWorkerApiUrl except ImportError: # Girder 2 try: from girder.plugins.worker.utils import getWorkerApiUrl # Fall back if the worker plugin is unavailble except ImportError: from girder.api.rest import getApiUrl as getWorkerApiUrl self.gc = GirderClient(apiUrl=getWorkerApiUrl()) from girder.api.rest import getCurrentUser if getCurrentUser(): from girder.constants import TokenScope from girder.models.token import Token token = Token().createToken( days=7, scope=[TokenScope.DATA_READ, TokenScope.DATA_WRITE], user=getCurrentUser(), )['_id'] else: from girder.api.rest import getCurrentToken token = getCurrentToken()['_id'] self.gc.token = token else: self.gc = gc except ImportError: self.gc = None
def configureItemTaskFromSlicerCliXml(self, item, xml, setName, setDescription, params): Token().requireScope(self.getCurrentToken(), 'item_task.set_task_spec.%s' % item['_id']) args = item.get('meta', {}).get('itemTaskSlicerCliArgs') or [] cliSpec = cli_parser.parseSlicerCliXml(xml) itemTaskSpec = item.get('meta', {}).get('itemTaskSpec', {}) itemTaskSpec.update({ 'container_args': args + cliSpec['args'], 'inputs': cliSpec['inputs'], 'outputs': cliSpec['outputs'] }) if setName: item['name'] = cliSpec['title'] if setDescription: item['description'] = cliSpec['description'] Item().setMetadata(item, { 'itemTaskSpec': itemTaskSpec, 'isItemTask': True })
def publishTale(self, tale, repository): user = self.getCurrentUser() publishers = { entry["repository"]: entry["auth_provider"] for entry in Setting().get(PluginSettings.PUBLISHER_REPOS) } try: publisher = publishers[repository] except KeyError: raise RestException( "Unknown publisher repository ({})".format(repository)) if publisher.startswith("dataone"): key = "provider" # Dataone value = publisher else: key = "resource_server" value = repository token = next( (_ for _ in user.get("otherTokens", []) if _.get(key) == value), None) if not token: raise RestException( "Missing a token for publisher ({}).".format(publisher)) girder_token = Token().createToken(user=user, days=0.5) publishTask = publish.delay( str(tale["_id"]), token, repository=repository, girder_client_token=str(girder_token["_id"]), ) return publishTask.job
def testTemporaryPassword(self): User().createUser('user1', 'passwd', 'tst', 'usr', '*****@*****.**') # Temporary password should require email param self.ensureRequiredParams(path='/user/password/temporary', method='PUT', required={'email'}) # Temporary password with an incorrect email resp = self.request(path='/user/password/temporary', method='PUT', params={'email': '*****@*****.**'}) self.assertStatus(resp, 400) self.assertEqual(resp.json['message'], "That email is not registered.") # Actually generate temporary access token self.assertTrue(base.mockSmtp.isMailQueueEmpty()) resp = self.request(path='/user/password/temporary', method='PUT', params={'email': '*****@*****.**'}) self.assertStatusOk(resp) self.assertEqual(resp.json['message'], "Sent temporary access email.") self.assertTrue(base.mockSmtp.waitForMail()) msg = base.mockSmtp.getMail(parse=True) # Pull out the auto-generated token from the email body = msg.get_payload(decode=True).decode('utf8') search = re.search('<a href="(.*)">', body) link = search.group(1) linkParts = link.split('/') userId = linkParts[-3] tokenId = linkParts[-1] # Checking if a token is a valid temporary token should fail if the # token is missing or doesn't match the user ID path = '/user/password/temporary/' + userId self.ensureRequiredParams(path=path, required={'token'}) resp = self.request(path=path, method='GET', params={'token': 'not valid'}) self.assertStatus(resp, 400) resp = self.request(path=path, method='GET', params={'token': tokenId}) self.assertStatusOk(resp) user = resp.json['user'] # We should have a real auth token now self.assertTrue('girderToken' in resp.cookie) authToken = resp.cookie['girderToken'].value token = Token().load(authToken, force=True, objectId=False) self.assertEqual(str(token['userId']), userId) self.assertFalse(Token().hasScope(token, [TokenScope.TEMPORARY_USER_AUTH])) self.assertTrue(Token().hasScope(token, [TokenScope.USER_AUTH])) # Artificially adjust the token to have expired. token = Token().load(tokenId, force=True, objectId=False) token['expires'] = (datetime.datetime.utcnow() - datetime.timedelta(days=1)) Token().save(token) resp = self.request(path=path, method='GET', params={'token': tokenId}) self.assertStatus(resp, 401) # We should now be able to change the password resp = self.request(path='/user/password', method='PUT', params={ 'old': tokenId, 'new': 'another_password' }, user=user) self.assertStatusOk(resp) # The token should have been deleted token = Token().load(tokenId, force=True, objectId=False) self.assertEqual(token, None)
def userToken(db, user): yield Token().createToken(user=user)
def logout(self): token = self.getCurrentToken() if token: Token().remove(token) self.deleteAuthTokenCookie() return {'message': 'Logged out.'}
def userDataReadToken(db, user): yield Token().createToken(user=user, scope=TokenScope.DATA_READ)
def userSettingToken(db, user): yield Token().createToken(user=user, scope=TokenScope.SETTINGS_READ)
def adminEmailToken(db, admin): yield Token().createToken(user=admin, scope=TokenScope.DATA_READ)
def adminSettingToken(db, admin): yield Token().createToken(user=admin, scope=TokenScope.SETTINGS_READ)
def cookie(user): yield 'girderToken=%s' % str(Token().createToken(user)['_id'])
def testAddItemTasksToFolderFromJson(self): """ Test adding item tasks to a folder from a JSON spec. """ # Create a new folder that will contain the tasks folder = Folder().createFolder(name='placeholder', creator=self.admin, parent=self.admin, parentType='user') # Create task to introspect container with mock.patch('girder.plugins.jobs.models.job.Job.scheduleJob' ) as scheduleMock: resp = self.request('/folder/%s/item_task_json_description' % folder['_id'], method='POST', params={'image': 'johndoe/foo:v5'}, user=self.admin) self.assertStatusOk(resp) self.assertEqual(resp.json['_modelType'], 'job') self.assertEqual(len(scheduleMock.mock_calls), 1) job = scheduleMock.mock_calls[0][1][0] self.assertEqual(job['handler'], 'worker_handler') self.assertEqual(job['itemTaskId'], folder['_id']) self.assertEqual(job['kwargs']['outputs']['_stdout']['method'], 'POST') self.assertTrue( job['kwargs']['outputs']['_stdout']['url'].endswith( 'folder/%s/item_task_json_specs' % folder['_id'])) params = job['kwargs']['outputs']['_stdout']['params'] self.assertEqual(params['image'], 'johndoe/foo:v5') self.assertEqual(params['pullImage'], True) token = job['kwargs']['outputs']['_stdout']['headers'][ 'Girder-Token'] # Task should not be registered until we get the callback resp = self.request('/item_task', user=self.admin) self.assertStatusOk(resp) self.assertEqual(resp.json, []) # Simulate callback from introspection job with open(os.path.join(os.path.dirname(__file__), 'specs.json')) as f: specs = f.read() parsedSpecs = json.loads(specs) resp = self.request('/folder/%s/item_task_json_specs' % folder['_id'], method='POST', params={ 'image': 'johndoe/foo:v5', 'pullImage': False }, token=token, body=specs, type='application/json') self.assertStatusOk(resp) items = list(Folder().childItems(folder, user=self.admin)) self.assertEqual(len(items), 2) # Image name and item task flag should be stored in the item metadata for itemIndex, item in enumerate(items): item = Item().load(item['_id'], force=True) self.assertEqual(item['name'], 'johndoe/foo:v5 %s' % (str(itemIndex))) self.assertEqual(item['description'], parsedSpecs[itemIndex]['description']) self.assertTrue(item['meta']['isItemTask']) parsedSpecs[itemIndex]['pull_image'] = False parsedSpecs[itemIndex]['docker_image'] = 'johndoe/foo:v5' self.assertEqual(item['meta']['itemTaskSpec'], parsedSpecs[itemIndex]) self.assertEqual(item['meta']['itemTaskName'], '') # We should only be able to see tasks we have read access on resp = self.request('/item_task') self.assertStatusOk(resp) self.assertEqual(resp.json, []) resp = self.request('/item_task', user=self.admin) self.assertStatusOk(resp) self.assertEqual(len(resp.json), 2) # Test adding single task spec folder2 = Folder().createFolder(name='placeholder2', creator=self.admin, parent=self.admin, parentType='user') with open(os.path.join(os.path.dirname(__file__), 'spec.json')) as f: spec = f.read() parsedSpec = json.loads(spec) token = Token().createToken(user=self.admin, scope='item_task.set_task_spec.%s' % folder2['_id']) resp = self.request('/folder/%s/item_task_json_specs' % folder2['_id'], method='POST', params={ 'image': 'johndoe/foo:v5', 'pullImage': False }, token=token, body=spec, type='application/json') self.assertStatusOk(resp) items = list(Folder().childItems(folder2, user=self.admin)) self.assertEqual(len(items), 1) # Check that the single item has the correct metadata item = Item().load(items[0]['_id'], force=True) self.assertEqual(item['name'], 'johndoe/foo:v5') self.assertEqual(item['description'], parsedSpec['description']) self.assertTrue(item['meta']['isItemTask']) parsedSpec['pull_image'] = False parsedSpec['docker_image'] = 'johndoe/foo:v5' self.assertEqual(item['meta']['itemTaskSpec'], parsedSpec) self.assertEqual(item['meta']['itemTaskName'], '')
def testDeleteUser(self): """ Test the behavior of deleting users. """ # Create a couple of users users = [ User().createUser('usr%s' % num, 'passwd', 'tst', 'usr', '*****@*****.**' % num) for num in [0, 1] ] # Create a folder and give both users some access on it folder = Folder().createFolder(parent=users[0], name='x', parentType='user', public=False, creator=users[0]) Folder().setUserAccess(folder, users[0], AccessType.WRITE) Folder().setUserAccess(folder, users[1], AccessType.READ) folder = Folder().save(folder) self.assertEqual(len(folder['access']['users']), 2) # Create a token for user 1 token = Token().createToken(users[1]) # Create a group, and have user 1 request to join it group = Group().createGroup('test', users[0], public=True) resp = self.request(path='/group/%s/member' % group['_id'], method='POST', user=users[1]) self.assertStatusOk(resp) # Make sure non-admin users can't delete other users resp = self.request(path='/user/%s' % users[0]['_id'], method='DELETE', user=users[1]) self.assertStatus(resp, 403) # Delete user 1 as admin, should work resp = self.request(path='/user/%s' % users[1]['_id'], method='DELETE', user=users[0]) self.assertStatusOk(resp) self.assertEqual(resp.json['message'], 'Deleted user %s.' % users[1]['login']) users[1] = User().load(users[1]['_id'], force=True) folder = Folder().load(folder['_id'], force=True) token = Token().load(token['_id'], force=True, objectId=False) group = Group().load(group['_id'], force=True) # Make sure user and token were deleted self.assertEqual(users[1], None) self.assertEqual(token, None) # Make sure pending invite to group was deleted self.assertEqual(len(list(Group().getFullRequestList(group))), 0) # Make sure access control references for the user were deleted self.assertEqual(len(folder['access']['users']), 1) # Delete user 0 resp = self.request(path='/user/%s' % users[0]['_id'], method='DELETE', user=users[0]) self.assertStatusOk(resp) # Make sure the user's folder was deleted folder = Folder().load(folder['_id'], force=True) self.assertEqual(folder, None)