def readChunk(self, params): """ After the temporary upload record has been created (see initUpload), the bytes themselves should be passed up in ordered chunks. The user must remain logged in when passing each chunk, to authenticate that the writer of the chunk is the same as the person who initiated the upload. The passed offset is a verification mechanism for ensuring the server and client agree on the number of bytes sent/received. """ self.requireParams(('offset', 'uploadId', 'chunk'), params) user = self.getCurrentUser() if not user: raise AccessException('You must be logged in to upload.') upload = self.model('upload').load(params['uploadId'], exc=True) offset = int(params['offset']) chunk = params['chunk'] if upload['userId'] != user['_id']: raise AccessException('You did not initiate this upload.') if upload['received'] != offset: raise RestException( 'Server has received {} bytes, but client sent offset {}.'. format(upload['received'], offset)) if type(chunk) == cherrypy._cpreqbody.Part: return self.model('upload').handleChunk(upload, chunk.file) else: return self.model('upload').handleChunk(upload, chunk)
def authenticate(self, username, password): entry = self.findOne({'userName': username}) if entry is None: self._authenticationFailed() raise AccessException('Invalid username/password') self._checkLocked(entry) if not pdigest.verify(password, entry['hash']): self._authenticationFailed(entry) raise AccessException('Invalid username/password')
def getReviewImages(self, dataset, params): user = self.getCurrentUser() User().requireReviewDataset(user) prereviewFolder = Dataset().prereviewFolder(dataset) if not (prereviewFolder and Folder().hasAccess(prereviewFolder, user=user, level=AccessType.READ)): raise AccessException( 'User does not have access to any Pre-review images for this dataset.') limit = int(params.get('limit', 50)) output = [ { field: image[field] for field in ['_id', 'name', 'updated', 'description', 'meta'] } for image in Image().find( {'folderId': prereviewFolder['_id']}, limit=limit, sort=[('name', SortDir.ASCENDING)] ) ] return output
def submit(self, params): url = params.get('url') content = params.get('content') user = self.getCurrentUser() # check permissions group = ModelImporter().model('group').find({'name': config['group']}) if group.count(): # the group must exist group = group[0] # the user must have read access to the group ModelImporter().model('group').requireAccess( group, user, AccessType.READ) else: raise AccessException('Invalid group name configured') # Create the diagnosis task statusMethod = server_support.handleDiagnosis(content=content, url=url) # Get the initial status status = statusMethod() # Get the maximum number of times to poll the task maxLoops = config['maxTaskWait'] / config['pollingInterval'] # Loop until the task is finished iloop = 0 while status['status'] == 'pending' and iloop < maxLoops: iloop += 1 time.sleep(config['pollingInterval']) status = statusMethod() # Get status and report errors if status['status'] == 'pending': raise RestException("Task timed out.", code=408) if status['status'] == 'failure': raise RestException(status['message'], code=400) # check access to private data group = ModelImporter().model('group').find( {'name': config['privateGroup']}) hasAccess = False if group.count(): group = group[0] try: ModelImporter().model('group').requireAccess( group, user, AccessType.READ) hasAccess = True except AccessException: pass # Append content data if the user has access if hasAccess: status["result"]["scrapedData"] = status["content"] return status["result"]
def _promote(self, group, params, level): """ Promote a user to moderator or administrator. :param group: The group to promote within. :param params: Request parameters. :param level: Either WRITE or ADMIN, for moderator or administrator. :type level: AccessType :returns: The updated group document. """ self.requireParams('userId', params) user = self.getCurrentUser() userToPromote = self.model('user').load(id=params['userId'], user=user, level=AccessType.READ, exc=True) if not group['_id'] in userToPromote.get('groups', []): raise AccessException('That user is not a group member.') group = self.model('group').setUserAccess(group, userToPromote, level=level, save=True) group['access'] = self.model('group').getFullAccessList(group) return group
def romanescoRun(self, item, params): # Make sure that we have permission to perform this analysis. user = self.getCurrentUser() settings = ModelImporter.model('setting') requireAuth = settings.get(PluginSettings.REQUIRE_AUTH, True) if requireAuth: safeFolders = settings.get(PluginSettings.SAFE_FOLDERS, ()) fullAccessUsers = settings.get(PluginSettings.FULL_ACCESS_USERS, ()) fullAccessGrps = settings.get(PluginSettings.FULL_ACCESS_GROUPS, ()) userGrps = {str(id) for id in user.get('groups', ())} if (str(item['folderId']) not in safeFolders and ( not user or user['login'] not in fullAccessUsers) and not userGrps & set(fullAccessGrps)): raise AccessException('Unauthorized user.') analysis = item.get('meta', {}).get('analysis') if type(analysis) is not dict: raise rest.RestException( 'Must specify a valid JSON object as the "analysis" metadata ' 'field on the input item.') # Get the analysis parameters (includes inputs & outputs). try: kwargs = json.load(cherrypy.request.body) except ValueError: raise rest.RestException( 'You must pass a valid JSON object in the request body.') return runAnalysis(user, analysis, kwargs, item)
def readChunk(self, upload, offset, chunk, params): """ After the temporary upload record has been created (see initUpload), the bytes themselves should be passed up in ordered chunks. The user must remain logged in when passing each chunk, to authenticate that the writer of the chunk is the same as the person who initiated the upload. The passed offset is a verification mechanism for ensuring the server and client agree on the number of bytes sent/received. """ user = self.getCurrentUser() if upload['userId'] != user['_id']: raise AccessException('You did not initiate this upload.') if upload['received'] != offset: raise RestException( 'Server has received %s bytes, but client sent offset %s.' % (upload['received'], offset)) try: if isinstance(chunk, cherrypy._cpreqbody.Part): return self.model('upload').handleChunk(upload, chunk.file, filter=True, user=user) else: return self.model('upload').handleChunk(upload, chunk, filter=True, user=user) except IOError as exc: if exc.errno == errno.EACCES: raise Exception('Failed to store upload.') raise
def nextSegmentationTask(self, params): self.requireParams(['datasetId'], params) user = self.getCurrentUser() dataset = Dataset().load(params['datasetId'], user=user, level=AccessType.READ, exc=True) userSkill = User().getSegmentationSkill(user) if userSkill is None: raise AccessException( 'You are not authorized to perform segmentations.') pipeline = list( itertools.chain( self._pipeline1ImagesFromDataset(dataset), self._pipeline2ImagesWithSegmentations(), ( # TODO: prefer an image with a novice segmentation to one with # no segmentations self._pipeline3NoExpertSegmentations() if userSkill == Segmentation().Skill.EXPERT else self._pipeline3MissingSegmentations()), self._pipeline4RandomImage())) results = list(Image().collection.aggregate(pipeline)) if not results: raise RestException( 'No segmentations are needed for this dataset.') nextImage = results[0] return Image().filterSummary(nextImage, user)
def wrapped(*args, **kwargs): if not rest.getCurrentToken(): raise AccessException( 'You must be logged in or have a valid auth token.') if required: Token().requireScope(rest.getCurrentToken(), scope) return fun(*args, **kwargs)
def cancelUpload(self, upload, params): user = self.getCurrentUser() if upload['userId'] != user['_id'] and not user['admin']: raise AccessException('You did not initiate this upload.') self.model('upload').cancelUpload(upload) return {'message': 'Upload canceled.'}
def createCollection(self, name, description, public): user = self.getCurrentUser() if not self.model('collection').hasCreatePrivilege(user): raise AccessException('You are not authorized to create collections.') return self.model('collection').createCollection( name=name, description=description, public=public, creator=user)
def _checkLocked(self, entry): now = datetime.datetime.now() if 'resetOn' in entry and now > entry['resetOn']: del entry['resetOn'] entry['failedCount'] = 0 self.save(entry) if now < entry['lockedUntil']: raise AccessException('Too many authentication failures. Wait some time until trying ' 'again.')
def finalizeUpload(self, params): self.requireParams('uploadId', params) user = self.getCurrentUser() upload = self.model('upload').load(params['uploadId'], exc=True) if upload['userId'] != user['_id']: raise AccessException('You did not initiate this upload.') return self.model('upload').finalizeUpload(upload)
def checkOwnership(self, user, session): if user['admin']: # admin owns everything return if 'ownerId' in session: ownerId = session['ownerId'] else: ownerId = session['userId'] if ownerId != user['_id']: raise AccessException('Current user is not the session owner')
def removeContainer(self, user, container): if container['ownerId'] != user['_id']: raise AccessException("This container is not yours") try: self._stopContainer(container) except: # some of these things need to be properly synchronized logger.info("Could not stop container") self.remove(container)
def requireAdmin(user): """ Calling this on a user will ensure that they have admin rights. If not, raises an AccessException. :param user: The user to check admin flag on. :type user: dict. :raises AccessException: If the user is not an administrator. """ if user is None or user.get('admin', False) is not True: raise AccessException('Administrator access required.')
def requireScope(self, token, scope): """ Raise an error if given set of scopes are not included. :param token: The token object. :type token: dict :param scope: A scope or set of scopes that will be tested as a subset of the given token's allowed scopes. :type scope: str or list of str """ if not self.hasScope(token, scope): raise AccessException('Invalid token scope, required: %s.' % (scope))
def requireAdmin(user, message=None): """ Calling this on a user will ensure that they have admin rights. If not, raises an AccessException. :param user: The user to check admin flag on. :type user: dict. :param message: The exception message. :type message: str or None :raises AccessException: If the user is not an administrator. """ if user is None or not user['admin']: raise AccessException(message or 'Administrator access required.')
def createCollection(self, params): self.requireParams('name', params) user = self.getCurrentUser() if not self.model('collection').hasCreatePrivilege(user): raise AccessException( 'You are not authorized to create collections.') public = self.boolParam('public', params, default=False) return self.model('collection').createCollection( name=params['name'], description=params.get('description'), public=public, creator=user)
def _promote(self, group, user, level): """ Promote a user to moderator or administrator. :param group: The group to promote within. :param user: The user to promote. :param level: Either WRITE or ADMIN, for moderator or administrator. :type level: AccessType :returns: The updated group document. """ if not group['_id'] in user.get('groups', []): raise AccessException('That user is not a group member.') group = self._model.setUserAccess(group, user, level=level, save=True) group['access'] = self._model.getFullAccessList(group) return group
def stopContainer(self, user, container): """ Stops a container. :param user: The current user. :param container: The container to stop. """ if container['ownerId'] != user['_id']: raise AccessException("This container is not yours") container['status'] = 'Stopping' self.save(container) event = events.trigger('container.stop', info = container) self._stopContainer(container) return container
def finalizeUpload(self, upload, params): user = self.getCurrentUser() if upload['userId'] != user['_id']: raise AccessException('You did not initiate this upload.') # If we don't have as much data as we were told would be uploaded and # the upload hasn't specified it has an alternate behavior, refuse to # complete the upload. if upload['received'] != upload['size'] and 'behavior' not in upload: raise RestException( 'Server has only received %s bytes, but the file should be %s bytes.' % (upload['received'], upload['size'])) file = self.model('upload').finalizeUpload(upload) extraKeys = file.get('additionalFinalizeKeys', ()) return self.model('file').filter(file, user, additionalKeys=extraKeys)
def finalizeUpload(self, params): self.requireParams('uploadId', params) user = self.getCurrentUser() upload = self.model('upload').load(params['uploadId'], exc=True) if upload['userId'] != user['_id']: raise AccessException('You did not initiate this upload.') # If we don't have as much data as we were told would be uploaded and # the upload hasn't specified it has an alternate behavior, refuse to # complete the upload. if upload['received'] != upload['size'] and 'behavior' not in upload: raise RestException( 'Server has only received {} bytes, but the file should be {} ' 'bytes.'.format(upload['received'], upload['size'])) return self.model('upload').finalizeUpload(upload)
def _checkUser(event): if event.info['provider'] == github.GitHub: requiredGitHubOrg = ModelImporter.model('setting').get( _REQUIRED_GITHUB_ORG) if requiredGitHubOrg: token = event.info['token'] headers = { 'Authorization': 'token %s' % token['access_token'], 'Accept': 'application/json' } resp = requests.get('https://api.github.com/user/orgs', headers=headers) orgs = [org['login'] for org in resp.json()] if requiredGitHubOrg not in orgs: raise AccessException( 'This user is not a member of the required GitHub org.')
def readChunk(self, upload, offset, params): """ After the temporary upload record has been created (see initUpload), the bytes themselves should be passed up in ordered chunks. The user must remain logged in when passing each chunk, to authenticate that the writer of the chunk is the same as the person who initiated the upload. The passed offset is a verification mechanism for ensuring the server and client agree on the number of bytes sent/received. This method accepts both the legacy multipart content encoding, as well as passing offset and uploadId as query parameters and passing the chunk as the body, which is the recommended method. .. deprecated :: 2.2.0 """ if 'chunk' in params: chunk = params['chunk'] if isinstance(chunk, cherrypy._cpreqbody.Part): # Seek is the only obvious way to get the length of the part chunk.file.seek(0, os.SEEK_END) size = chunk.file.tell() chunk.file.seek(0, os.SEEK_SET) chunk = RequestBodyStream(chunk.file, size=size) else: chunk = RequestBodyStream(cherrypy.request.body) user = self.getCurrentUser() if upload['userId'] != user['_id']: raise AccessException('You did not initiate this upload.') if upload['received'] != offset: raise RestException( 'Server has received %s bytes, but client sent offset %s.' % (upload['received'], offset)) try: return self.model('upload').handleChunk(upload, chunk, filter=True, user=user) except IOError as exc: if exc.errno == errno.EACCES: raise Exception('Failed to store upload.') raise
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 = ModelImporter.model('token') if not tokenModel.hasScope(token, scope): setattr(cherrypy.request, 'girderUser', None) if isinstance(scope, six.string_types): scope = (scope, ) raise AccessException( 'Invalid token scope.\nRequired: {}.\nAllowed: {}'.format( ' '.join(scope), ' '.join(tokenModel.getAllowedScopes(token))))
def getSegmentationTasks(self, params): details = self.boolParam('details', params, False) user = self.getCurrentUser() userSkill = User().getSegmentationSkill(user) if userSkill is None: raise AccessException( 'You are not authorized to perform segmentations.') pipeline = list( itertools.chain(self._pipeline1AllImages(user), self._pipeline2ImagesWithSegmentations(), (self._pipeline3NoExpertSegmentations() if userSkill == Segmentation().Skill.EXPERT else self._pipeline3MissingSegmentations()), (self._pipeline4ListImages() if details else self._pipeline4CountImages()), self._pipeline5JoinDataset())) results = list(Image().collection.aggregate(pipeline)) return results
def redirectReviewTask(self, params): self.requireParams(['datasetId'], params) user = self.getCurrentUser() User().requireReviewDataset(user) dataset = Dataset().load(params['datasetId'], user=user, level=AccessType.READ, exc=True) prereviewFolder = Dataset().prereviewFolder(dataset) if not (prereviewFolder and Folder().hasAccess( prereviewFolder, user=user, level=AccessType.READ)): raise AccessException( 'User does not have access to any Pre-review images for this dataset.' ) if not Image().find({'folderId': prereviewFolder['_id']}).count(): raise RestException( 'No Pre-review images are available for this dataset.') reviewUrl = '/markup/gallery#/qc/%s' % dataset['_id'] self._doRedirect(reviewUrl)
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 = ModelImporter.model('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 accessDecorator(*args, **kwargs): token = rest.getCurrentToken() if not token: raise AccessException('You must be logged in or supply a valid ' 'session token.') return fun(*args, **kwargs)