def __init__(self): super(Group, self).__init__() self.resourceName = 'group' self._model = GroupModel() self.route('DELETE', (':id',), self.deleteGroup) self.route('DELETE', (':id', 'member'), self.removeFromGroup) self.route('DELETE', (':id', 'moderator'), self.demote) self.route('DELETE', (':id', 'admin'), self.demote) self.route('GET', (), self.find) self.route('GET', (':id',), self.getGroup) self.route('GET', (':id', 'access'), self.getGroupAccess) self.route('GET', (':id', 'invitation'), self.getGroupInvitations) self.route('GET', (':id', 'member'), self.listMembers) self.route('POST', (), self.createGroup) self.route('POST', (':id', 'invitation'), self.inviteToGroup) self.route('POST', (':id', 'member'), self.joinGroup) self.route('POST', (':id', 'moderator'), self.promoteToModerator) self.route('POST', (':id', 'admin'), self.promoteToAdmin) self.route('PUT', (':id',), self.updateGroup)
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)
class Group(Resource): """API Endpoint for groups.""" def __init__(self): super(Group, self).__init__() self.resourceName = 'group' self._model = GroupModel() self.route('DELETE', (':id',), self.deleteGroup) self.route('DELETE', (':id', 'member'), self.removeFromGroup) self.route('DELETE', (':id', 'moderator'), self.demote) self.route('DELETE', (':id', 'admin'), self.demote) self.route('GET', (), self.find) self.route('GET', (':id',), self.getGroup) self.route('GET', (':id', 'access'), self.getGroupAccess) self.route('GET', (':id', 'invitation'), self.getGroupInvitations) self.route('GET', (':id', 'member'), self.listMembers) self.route('POST', (), self.createGroup) self.route('POST', (':id', 'invitation'), self.inviteToGroup) self.route('POST', (':id', 'member'), self.joinGroup) self.route('POST', (':id', 'moderator'), self.promoteToModerator) self.route('POST', (':id', 'admin'), self.promoteToAdmin) self.route('PUT', (':id',), self.updateGroup) @access.public @filtermodel(model=GroupModel) @autoDescribeRoute( Description('Search for groups or list all groups.') .param('text', 'Pass this to perform a full-text search for groups.', required=False) .param('exact', 'If true, only return exact name matches. This is ' 'case sensitive.', required=False, dataType='boolean', default=False) .pagingParams(defaultSort='name') .errorResponse() ) def find(self, text, exact, limit, offset, sort): user = self.getCurrentUser() if text is not None: if exact: groupList = self._model.find( {'name': text}, offset=offset, limit=limit, sort=sort) else: groupList = self._model.textSearch( text, user=user, offset=offset, limit=limit, sort=sort) else: groupList = self._model.list( user=user, offset=offset, limit=limit, sort=sort) return groupList @access.user @filtermodel(model=GroupModel) @autoDescribeRoute( Description('Create a new group.') .responseClass('Group') .notes('Must be logged in.') .param('name', 'Unique name for the group.', strip=True) .param('description', 'Description of the group.', required=False, default='', strip=True) .param('public', 'Whether the group should be publicly visible.', required=False, dataType='boolean', default=False) .errorResponse() .errorResponse('Write access was denied on the parent', 403) ) def createGroup(self, name, description, public): return self._model.createGroup( name=name, creator=self.getCurrentUser(), description=description, public=public) @access.public @filtermodel(model=GroupModel) @autoDescribeRoute( Description('Get a group by ID.') .responseClass('Group') .modelParam('id', model=GroupModel, level=AccessType.READ) .errorResponse('ID was invalid.') .errorResponse('Read access was denied for the group.', 403) ) def getGroup(self, group): # Add in the current setting for adding to groups group['_addToGroupPolicy'] = Setting().get(SettingKey.ADD_TO_GROUP_POLICY) return group @access.public @filtermodel(model=GroupModel, addFields={'access', 'requests'}) @autoDescribeRoute( Description('Get the access control list for a group.') .responseClass('Group') .modelParam('id', model=GroupModel, level=AccessType.READ) .errorResponse('ID was invalid.') .errorResponse('Read access was denied for the group.', 403) ) def getGroupAccess(self, group): groupModel = self._model group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group @access.public @filtermodel(model=User) @autoDescribeRoute( Description('Show outstanding invitations for a group.') .responseClass('Group') .modelParam('id', model=GroupModel, level=AccessType.READ) .pagingParams(defaultSort='lastName') .errorResponse() .errorResponse('Read access was denied for the group.', 403) ) def getGroupInvitations(self, group, limit, offset, sort): return self._model.getInvites(group, limit, offset, sort) @access.user @filtermodel(model=GroupModel) @autoDescribeRoute( Description('Update a group by ID.') .modelParam('id', model=GroupModel, level=AccessType.WRITE) .param('name', 'The name to set on the group.', required=False, strip=True) .param('description', 'Description for the group.', required=False, strip=True) .param('public', 'Whether the group should be publicly visible', dataType='boolean', required=False) .param('addAllowed', 'Can admins or moderators directly add members ' 'to this group? Only system administrators are allowed to ' 'set this field', required=False, enum=['default', 'no', 'yesmod', 'yesadmin']) .errorResponse() .errorResponse('Write access was denied for the group.', 403) ) def updateGroup(self, group, name, description, public, addAllowed): if public is not None: self._model.setPublic(group, public) if name is not None: group['name'] = name if description is not None: group['description'] = description if addAllowed is not None: self.requireAdmin(self.getCurrentUser()) group['addAllowed'] = addAllowed return self._model.updateGroup(group) @access.user @filtermodel(model=GroupModel, addFields={'access', 'requests'}) @autoDescribeRoute( Description('Request to join a group, or accept an invitation to join.') .responseClass('Group') .modelParam('id', model=GroupModel, level=AccessType.READ) .errorResponse('ID was invalid.') .errorResponse('You were not invited to this group, or do not have ' 'read access to it.', 403) ) def joinGroup(self, group): groupModel = self._model group = groupModel.joinGroup(group, self.getCurrentUser()) group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group @access.public @filtermodel(model=User) @autoDescribeRoute( Description('List members of a group.') .modelParam('id', model=GroupModel, level=AccessType.READ) .pagingParams(defaultSort='lastName') .errorResponse('ID was invalid.') .errorResponse('Read access was denied for the group.', 403) ) def listMembers(self, group, limit, offset, sort): return self._model.listMembers(group, offset=offset, limit=limit, sort=sort) @access.user @filtermodel(model=GroupModel, addFields={'access', 'requests'}) @autoDescribeRoute( Description("Invite a user to join a group, or accept a user's request to join.") .responseClass('Group') .notes('The "force" option to this endpoint is only available to ' 'administrators and can be used to bypass the invitation process' ' and instead add the user directly to the group.') .modelParam('id', model=GroupModel, level=AccessType.WRITE) .modelParam('userId', 'The ID of the user to invite or accept.', model=User, destName='userToInvite', level=AccessType.READ, paramType='form') .param('level', 'The access level the user will be given when they accept the invitation.', required=False, dataType='integer', default=AccessType.READ) .param('quiet', 'If you do not want this action to send an email to ' 'the target user, set this to true.', dataType='boolean', required=False, default=False) .param('force', 'Add user directly rather than sending an invitation ' '(admin-only option).', dataType='boolean', required=False, default=False) .errorResponse() .errorResponse('Write access was denied for the group.', 403) ) def inviteToGroup(self, group, userToInvite, level, quiet, force): groupModel = self._model user = self.getCurrentUser() if force: if not user['admin']: mustBeAdmin = True addPolicy = Setting().get(SettingKey.ADD_TO_GROUP_POLICY) addGroup = group.get('addAllowed', 'default') if addGroup not in ['no', 'yesadmin', 'yesmod']: addGroup = addPolicy if (groupModel.hasAccess( group, user, AccessType.ADMIN) and ('mod' in addPolicy or 'admin' in addPolicy) and addGroup.startswith('yes')): mustBeAdmin = False elif (groupModel.hasAccess( group, user, AccessType.WRITE) and 'mod' in addPolicy and addGroup == 'yesmod'): mustBeAdmin = False if mustBeAdmin: self.requireAdmin(user) groupModel.addUser(group, userToInvite, level=level) else: # Can only invite into access levels that you yourself have groupModel.requireAccess(group, user, level) groupModel.inviteUser(group, userToInvite, level) if not quiet: html = mail_utils.renderTemplate('groupInvite.mako', { 'userToInvite': userToInvite, 'user': user, 'group': group }) mail_utils.sendEmail( to=userToInvite['email'], text=html, subject="%s: You've been invited to a group" % Setting().get(SettingKey.BRAND_NAME) ) group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group @access.user @filtermodel(model=GroupModel, addFields={'access'}) @autoDescribeRoute( Description('Promote a member to be a moderator of the group.') .responseClass('Group') .modelParam('id', model=GroupModel, level=AccessType.ADMIN) .modelParam('userId', 'The ID of the user to promote.', model=User, level=AccessType.READ, paramType='formData') .errorResponse('ID was invalid.') .errorResponse("You don't have permission to promote users.", 403) ) def promoteToModerator(self, group, user): return self._promote(group, user, AccessType.WRITE) @access.user @filtermodel(model=GroupModel, addFields={'access'}) @autoDescribeRoute( Description('Promote a member to be an administrator of the group.') .responseClass('Group') .modelParam('id', model=GroupModel, level=AccessType.ADMIN) .modelParam('userId', 'The ID of the user to promote.', model=User, level=AccessType.READ, paramType='formData') .errorResponse('ID was invalid.') .errorResponse("You don't have permission to promote users.", 403) ) def promoteToAdmin(self, group, user): return self._promote(group, user, AccessType.ADMIN) 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 @access.user @filtermodel(model=GroupModel, addFields={'access', 'requests'}) @autoDescribeRoute( Description('Demote a user to a normal group member.') .responseClass('Group') .modelParam('id', model=GroupModel, level=AccessType.ADMIN) .modelParam('userId', 'The ID of the user to demote.', model=User, level=AccessType.READ, paramType='formData') .errorResponse() .errorResponse("You don't have permission to demote users.", 403) ) def demote(self, group, user): groupModel = self._model group = groupModel.setUserAccess(group, user, level=AccessType.READ, save=True) group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group @access.user @filtermodel(model=GroupModel, addFields={'access', 'requests'}) @autoDescribeRoute( Description('Remove a user from a group, or uninvite them.') .responseClass('Group') .notes('If the specified user is not yet a member of the group, this ' 'will delete any outstanding invitation or membership request for ' 'the user. Passing no userId parameter will assume that the ' 'current user is removing themself.') .modelParam('id', model=GroupModel, level=AccessType.READ) .modelParam('userId', 'The ID of the user to remove. If not passed, will ' 'remove yourself from the group.', required=False, model=User, level=AccessType.READ, destName='userToRemove', paramType='formData') .errorResponse() .errorResponse("You don't have permission to remove that user.", 403) ) def removeFromGroup(self, group, userToRemove): user = self.getCurrentUser() groupModel = self._model if userToRemove is None: # Assume user is removing themself from the group userToRemove = user # If removing someone else, you must have at least as high an # access level as they do, and you must have at least write access # to remove any user other than yourself. if user['_id'] != userToRemove['_id']: if groupModel.hasAccess(group, userToRemove, AccessType.ADMIN): groupModel.requireAccess(group, user, AccessType.ADMIN) else: groupModel.requireAccess(group, user, AccessType.WRITE) group = groupModel.removeUser(group, userToRemove) group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group @access.user @autoDescribeRoute( Description('Delete a group by ID.') .modelParam('id', model=GroupModel, level=AccessType.ADMIN) .errorResponse('ID was invalid.') .errorResponse('Admin access was denied for the group.', 403) ) def deleteGroup(self, group): self._model.remove(group) return {'message': 'Deleted the group %s.' % group['name']}
def testYAMLConfigFile(server, admin, user, fsAssetstore): # Create some resources to use in the tests collection = Collection().createCollection('collection A', admin) colFolderA = Folder().createFolder(collection, 'folder A', parentType='collection', creator=admin) colFolderB = Folder().createFolder(colFolderA, 'folder B', creator=admin) groupA = Group().createGroup('Group A', admin) resp = server.request(path='/folder/%s/yaml_config/sample.json' % str(colFolderB['_id']), method='GET') assert utilities.respStatus(resp) == 200 assert resp.json is None colFolderConfig = Folder().createFolder(collection, '.config', parentType='collection', creator=admin) utilities.uploadText(json.dumps({'keyA': 'value1'}), admin, fsAssetstore, colFolderConfig, 'sample.json') resp = server.request(path='/folder/%s/yaml_config/sample.json' % str(colFolderB['_id'])) assert utilities.respStatus(resp) == 200 assert resp.json['keyA'] == 'value1' utilities.uploadText(json.dumps({'keyA': 'value2'}), admin, fsAssetstore, colFolderA, 'sample.json') resp = server.request(path='/folder/%s/yaml_config/sample.json' % str(colFolderB['_id'])) assert utilities.respStatus(resp) == 200 assert resp.json['keyA'] == 'value2' utilities.uploadText( json.dumps({ 'keyA': 'value3', 'groups': { 'Group A': { 'access': { 'user': { 'keyA': 'value4' } } } }, 'access': { 'user': { 'keyA': 'value5' }, 'admin': { 'keyA': 'value6' } } }), admin, fsAssetstore, colFolderB, 'sample.json') resp = server.request(path='/folder/%s/yaml_config/sample.json' % str(colFolderB['_id'])) assert utilities.respStatus(resp) == 200 assert resp.json['keyA'] == 'value3' resp = server.request(path='/folder/%s/yaml_config/sample.json' % str(colFolderB['_id']), user=user) assert utilities.respStatus(resp) == 200 assert resp.json['keyA'] == 'value5' resp = server.request(path='/folder/%s/yaml_config/sample.json' % str(colFolderB['_id']), user=admin) assert utilities.respStatus(resp) == 200 assert resp.json['keyA'] == 'value6' Group().addUser(groupA, user) resp = server.request(path='/folder/%s/yaml_config/sample.json' % str(colFolderB['_id']), user=user) assert utilities.respStatus(resp) == 200 assert resp.json['keyA'] == 'value4' resp = server.request(path='/folder/%s/yaml_config/sample.json' % str(colFolderB['_id']), user=admin) assert utilities.respStatus(resp) == 200 assert resp.json['keyA'] == 'value6'
def cliHandler(self, **hargs): # print 'in cliHandler hargs is ' # print hargs user = self.getCurrentUser() token = self.getCurrentToken()['_id'] # create job jobModel = self.model('job', 'jobs') jobTitle = '.'.join((restResource.resourceName, cliName)) # User Group access control, # register group into particular job so that this user can access this job groups = list(Group().list(user=user)) groupsAccess = [] for eachGroup in groups: eachGroupAccess = {'id': eachGroup['_id'], 'level': 0} groupsAccess.append(eachGroupAccess) job = jobModel.createJob(title=jobTitle, type=jobTitle, handler='worker_handler', user=user, otherFields={'access': {'groups': groupsAccess}}) kwargs = { 'validate': False, 'auto_convert': True, 'cleanup': True, 'inputs': dict(), 'outputs': dict() } # create job info jobToken = jobModel.createJobToken(job) kwargs['jobInfo'] = wutils.jobInfoSpec(job, jobToken) # initialize task spec taskSpec = {'name': cliName, 'mode': 'docker', 'docker_image': dockerImage, 'pull_image': False, 'inputs': [], 'outputs': []} _addIndexedInputParamsToTaskSpec(index_input_params, taskSpec) _addIndexedOutputParamsToTaskSpec(index_output_params, taskSpec, hargs) _addOptionalInputParamsToTaskSpec(opt_input_params, taskSpec) _addOptionalOutputParamsToTaskSpec(opt_output_params, taskSpec, hargs) if len(simple_out_params) > 0: _addReturnParameterFileParamToTaskSpec(taskSpec, hargs) kwargs['task'] = taskSpec # add input/output parameter bindings _addIndexedInputParamBindings(index_input_params, kwargs['inputs'], hargs, token) _addIndexedOutputParamBindings(index_output_params, kwargs['outputs'], hargs, user, token) _addOptionalInputParamBindings(opt_input_params, kwargs['inputs'], hargs, user, token) _addOptionalOutputParamBindings(opt_output_params, kwargs['outputs'], hargs, user, token) if len(simple_out_params) > 0: _addReturnParameterFileBinding(kwargs['outputs'], hargs, user, token) # construct container arguments containerArgs = [cliRelPath] _addOptionalInputParamsToContainerArgs(opt_input_params, containerArgs, hargs) _addOptionalOutputParamsToContainerArgs(opt_output_params, containerArgs, kwargs, hargs) _addReturnParameterFileToContainerArgs(containerArgs, kwargs, hargs) # print 'index_params' # print index_params _addIndexedParamsToContainerArgs(index_params, containerArgs, hargs) taskSpec['container_args'] = containerArgs # schedule job job['kwargs'] = kwargs # print '-------job is-------' # print job job = jobModel.save(job) jobModel.scheduleJob(job) # return result return jobModel.filter(job, user)
def group(user): g = Group().createGroup(name='agroup', creator=user) yield g
class Group(Resource): """API Endpoint for groups.""" def __init__(self): super(Group, self).__init__() self.resourceName = 'group' self._model = GroupModel() self.route('DELETE', (':id', ), self.deleteGroup) self.route('DELETE', (':id', 'member'), self.removeFromGroup) self.route('DELETE', (':id', 'moderator'), self.demote) self.route('DELETE', (':id', 'admin'), self.demote) self.route('GET', (), self.find) self.route('GET', (':id', ), self.getGroup) self.route('GET', (':id', 'access'), self.getGroupAccess) self.route('GET', (':id', 'invitation'), self.getGroupInvitations) self.route('GET', (':id', 'member'), self.listMembers) self.route('POST', (), self.createGroup) self.route('POST', (':id', 'invitation'), self.inviteToGroup) self.route('POST', (':id', 'member'), self.joinGroup) self.route('POST', (':id', 'moderator'), self.promoteToModerator) self.route('POST', (':id', 'admin'), self.promoteToAdmin) self.route('PUT', (':id', ), self.updateGroup) @access.public @filtermodel(model=GroupModel) @autoDescribeRoute( Description('Search for groups or list all groups.').param( 'text', 'Pass this to perform a full-text search for groups.', required=False).param( 'exact', 'If true, only return exact name matches. This is ' 'case sensitive.', required=False, dataType='boolean', default=False).pagingParams( defaultSort='name').errorResponse()) def find(self, text, exact, limit, offset, sort): user = self.getCurrentUser() if text is not None: if exact: groupList = self._model.find({'name': text}, offset=offset, limit=limit, sort=sort) else: groupList = self._model.textSearch(text, user=user, offset=offset, limit=limit, sort=sort) else: groupList = self._model.list(user=user, offset=offset, limit=limit, sort=sort) return groupList @access.user @filtermodel(model=GroupModel) @autoDescribeRoute( Description('Create a new group.').responseClass('Group').notes( 'Must be logged in.').param( 'name', 'Unique name for the group.', strip=True).param( 'description', 'Description of the group.', required=False, default='', strip=True).param( 'public', 'Whether the group should be publicly visible.', required=False, dataType='boolean', default=False).errorResponse().errorResponse( 'Write access was denied on the parent', 403)) def createGroup(self, name, description, public): return self._model.createGroup(name=name, creator=self.getCurrentUser(), description=description, public=public) @access.public @filtermodel(model=GroupModel) @autoDescribeRoute( Description('Get a group by ID.').responseClass('Group').modelParam( 'id', model=GroupModel, level=AccessType.READ).errorResponse( 'ID was invalid.').errorResponse( 'Read access was denied for the group.', 403)) def getGroup(self, group): # Add in the current setting for adding to groups group['_addToGroupPolicy'] = Setting().get( SettingKey.ADD_TO_GROUP_POLICY) return group @access.public @filtermodel(model=GroupModel, addFields={'access', 'requests'}) @autoDescribeRoute( Description('Get the access control list for a group.').responseClass( 'Group').modelParam( 'id', model=GroupModel, level=AccessType.READ).errorResponse( 'ID was invalid.').errorResponse( 'Read access was denied for the group.', 403)) def getGroupAccess(self, group): groupModel = self._model group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group @access.public @filtermodel(model=User) @autoDescribeRoute( Description('Show outstanding invitations for a group.').responseClass( 'Group').modelParam( 'id', model=GroupModel, level=AccessType.READ).pagingParams( defaultSort='lastName').errorResponse().errorResponse( 'Read access was denied for the group.', 403)) def getGroupInvitations(self, group, limit, offset, sort): return self._model.getInvites(group, limit, offset, sort) @access.user @filtermodel(model=GroupModel) @autoDescribeRoute( Description('Update a group by ID.').modelParam( 'id', model=GroupModel, level=AccessType.WRITE).param( 'name', 'The name to set on the group.', required=False, strip=True).param('description', 'Description for the group.', required=False, strip=True). param('public', 'Whether the group should be publicly visible', dataType='boolean', required=False).param( 'addAllowed', 'Can admins or moderators directly add members ' 'to this group? Only system administrators are allowed to ' 'set this field', required=False, enum=['default', 'no', 'yesmod', 'yesadmin']).errorResponse().errorResponse( 'Write access was denied for the group.', 403)) def updateGroup(self, group, name, description, public, addAllowed): if public is not None: self._model.setPublic(group, public) if name is not None: group['name'] = name if description is not None: group['description'] = description if addAllowed is not None: self.requireAdmin(self.getCurrentUser()) group['addAllowed'] = addAllowed return self._model.updateGroup(group) @access.user @filtermodel(model=GroupModel, addFields={'access', 'requests'}) @autoDescribeRoute( Description('Request to join a group, or accept an invitation to join.' ).responseClass('Group').modelParam('id', model=GroupModel, level=AccessType.READ). errorResponse('ID was invalid.').errorResponse( 'You were not invited to this group, or do not have ' 'read access to it.', 403)) def joinGroup(self, group): groupModel = self._model group = groupModel.joinGroup(group, self.getCurrentUser()) group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group @access.public @filtermodel(model=User) @autoDescribeRoute( Description('List members of a group.').modelParam( 'id', model=GroupModel, level=AccessType.READ).pagingParams( defaultSort='lastName').errorResponse( 'ID was invalid.').errorResponse( 'Read access was denied for the group.', 403)) def listMembers(self, group, limit, offset, sort): return self._model.listMembers(group, offset=offset, limit=limit, sort=sort) @access.user @filtermodel(model=GroupModel, addFields={'access', 'requests'}) @autoDescribeRoute( Description( "Invite a user to join a group, or accept a user's request to join." ).responseClass('Group').notes( 'The "force" option to this endpoint is only available to ' 'administrators and can be used to bypass the invitation process' ' and instead add the user directly to the group.').modelParam( 'id', model=GroupModel, level=AccessType.WRITE).modelParam( 'userId', 'The ID of the user to invite or accept.', model=User, destName='userToInvite', level=AccessType.READ, paramType='form'). param( 'level', 'The access level the user will be given when they accept the invitation.', required=False, dataType='integer', default=AccessType.READ).param( 'quiet', 'If you do not want this action to send an email to ' 'the target user, set this to true.', dataType='boolean', required=False, default=False).param( 'force', 'Add user directly rather than sending an invitation ' '(admin-only option).', dataType='boolean', required=False, default=False).errorResponse().errorResponse( 'Write access was denied for the group.', 403)) def inviteToGroup(self, group, userToInvite, level, quiet, force): groupModel = self._model user = self.getCurrentUser() if force: if not user['admin']: mustBeAdmin = True addPolicy = Setting().get(SettingKey.ADD_TO_GROUP_POLICY) addGroup = group.get('addAllowed', 'default') if addGroup not in ['no', 'yesadmin', 'yesmod']: addGroup = addPolicy if (groupModel.hasAccess(group, user, AccessType.ADMIN) and ('mod' in addPolicy or 'admin' in addPolicy) and addGroup.startswith('yes')): mustBeAdmin = False elif (groupModel.hasAccess(group, user, AccessType.WRITE) and 'mod' in addPolicy and addGroup == 'yesmod'): mustBeAdmin = False if mustBeAdmin: self.requireAdmin(user) groupModel.addUser(group, userToInvite, level=level) else: # Can only invite into access levels that you yourself have groupModel.requireAccess(group, user, level) groupModel.inviteUser(group, userToInvite, level) if not quiet: html = mail_utils.renderTemplate('groupInvite.mako', { 'userToInvite': userToInvite, 'user': user, 'group': group }) mail_utils.sendEmail( to=userToInvite['email'], text=html, subject="%s: You've been invited to a group" % Setting().get(SettingKey.BRAND_NAME)) group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group @access.user @filtermodel(model=GroupModel, addFields={'access'}) @autoDescribeRoute( Description('Promote a member to be a moderator of the group.'). responseClass('Group').modelParam( 'id', model=GroupModel, level=AccessType.ADMIN).modelParam( 'userId', 'The ID of the user to promote.', model=User, level=AccessType.READ, paramType='formData').errorResponse( 'ID was invalid.').errorResponse( "You don't have permission to promote users.", 403)) def promoteToModerator(self, group, user): return self._promote(group, user, AccessType.WRITE) @access.user @filtermodel(model=GroupModel, addFields={'access'}) @autoDescribeRoute( Description('Promote a member to be an administrator of the group.'). responseClass('Group').modelParam( 'id', model=GroupModel, level=AccessType.ADMIN).modelParam( 'userId', 'The ID of the user to promote.', model=User, level=AccessType.READ, paramType='formData').errorResponse( 'ID was invalid.').errorResponse( "You don't have permission to promote users.", 403)) def promoteToAdmin(self, group, user): return self._promote(group, user, AccessType.ADMIN) 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 @access.user @filtermodel(model=GroupModel, addFields={'access', 'requests'}) @autoDescribeRoute( Description('Demote a user to a normal group member.').responseClass( 'Group').modelParam( 'id', model=GroupModel, level=AccessType.ADMIN).modelParam( 'userId', 'The ID of the user to demote.', model=User, level=AccessType.READ, paramType='formData').errorResponse().errorResponse( "You don't have permission to demote users.", 403)) def demote(self, group, user): groupModel = self._model group = groupModel.setUserAccess(group, user, level=AccessType.READ, save=True) group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group @access.user @filtermodel(model=GroupModel, addFields={'access', 'requests'}) @autoDescribeRoute( Description('Remove a user from a group, or uninvite them.'). responseClass('Group').notes( 'If the specified user is not yet a member of the group, this ' 'will delete any outstanding invitation or membership request for ' 'the user. Passing no userId parameter will assume that the ' 'current user is removing themself.').modelParam( 'id', model=GroupModel, level=AccessType.READ).modelParam( 'userId', 'The ID of the user to remove. If not passed, will ' 'remove yourself from the group.', required=False, model=User, level=AccessType.READ, destName='userToRemove', paramType='formData').errorResponse().errorResponse( "You don't have permission to remove that user.", 403)) def removeFromGroup(self, group, userToRemove): user = self.getCurrentUser() groupModel = self._model if userToRemove is None: # Assume user is removing themself from the group userToRemove = user # If removing someone else, you must have at least as high an # access level as they do, and you must have at least write access # to remove any user other than yourself. if user['_id'] != userToRemove['_id']: if groupModel.hasAccess(group, userToRemove, AccessType.ADMIN): groupModel.requireAccess(group, user, AccessType.ADMIN) else: groupModel.requireAccess(group, user, AccessType.WRITE) group = groupModel.removeUser(group, userToRemove) group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group @access.user @autoDescribeRoute( Description('Delete a group by ID.').modelParam( 'id', model=GroupModel, level=AccessType.ADMIN).errorResponse( 'ID was invalid.').errorResponse( 'Admin access was denied for the group.', 403)) def deleteGroup(self, group): self._model.remove(group) return {'message': 'Deleted the group %s.' % group['name']}
def testCollectionCreatePolicy(self): # With default settings, non-admin users cannot create collections resp = self.request(path='/collection', method='POST', params={'name': 'user collection'}, user=self.user) self.assertStatus(resp, 403) # Allow any user to create collections Setting().set(SettingKey.COLLECTION_CREATE_POLICY, {'open': True}) resp = self.request(path='/collection', method='POST', params={'name': 'open collection'}, user=self.user) self.assertStatusOk(resp) self.assertTrue('_id' in resp.json) # Anonymous users still shouldn't be able to resp = self.request(path='/collection', method='POST', params={'name': 'open collection'}, user=None) self.assertStatus(resp, 401) # Add a group that has collection create permission group = Group().createGroup(name='coll. creators', creator=self.admin) Setting().set(SettingKey.COLLECTION_CREATE_POLICY, { 'open': False, 'groups': [str(group['_id'])] }) # Group membership should allow creation now Group().addUser(group=group, user=self.user) resp = self.request(path='/collection', method='POST', params={'name': 'group collection'}, user=self.user) self.assertStatusOk(resp) self.assertTrue('_id' in resp.json) # Test individual user access Group().removeUser(group=group, user=self.user) resp = self.request(path='/collection', method='POST', params={'name': 'group collection'}, user=self.user) self.assertStatus(resp, 403) Setting().set(SettingKey.COLLECTION_CREATE_POLICY, { 'open': False, 'users': [str(self.user['_id'])] }) resp = self.request(path='/collection', method='POST', params={'name': 'user collection'}, user=self.user) self.assertStatusOk(resp) self.assertTrue('_id' in resp.json)
def testCuration(self): admin, user = self.users # create some groups g1 = Group().createGroup('g1', admin) g2 = Group().createGroup('g2', admin) g3 = Group().createGroup('g3', admin) # set auto join rules rules = [ { 'pattern': '@test.com', 'groupId': str(g1['_id']), 'level': AccessType.ADMIN }, { 'pattern': '@example.com', 'groupId': str(g2['_id']), 'level': AccessType.READ }, { 'pattern': '@example.com', 'groupId': str(g3['_id']), 'level': AccessType.WRITE }, ] params = {'list': json.dumps([{'key': 'autojoin', 'value': rules}])} resp = self.request('/system/setting', user=admin, method='PUT', params=params) self.assertStatusOk(resp) # create users user1 = User().createUser('user1', 'password', 'John', 'Doe', '*****@*****.**') user2 = User().createUser('user2', 'password', 'John', 'Doe', '*****@*****.**') user3 = User().createUser('user3', 'password', 'John', 'Doe', '*****@*****.**') # check correct groups were joined self.assertEqual(user1['groups'], [g2['_id'], g3['_id']]) self.assertEqual(user2['groups'], [g1['_id']]) self.assertEqual(user3['groups'], []) # check correct access levels g1 = Group().load(g1['_id'], force=True) g3 = Group().load(g3['_id'], force=True) self.assertIn( { 'id': user2['_id'], 'level': AccessType.ADMIN, 'flags': [] }, g1['access']['users']) self.assertIn( { 'id': user1['_id'], 'level': AccessType.WRITE, 'flags': [] }, g3['access']['users'])
def testGroupAddAllow(self): """ Test letting group admins and mods ability to add users directly to groups. """ policy = { 'never': { 'default': [0], 'no': [0], 'yesadmin': [0], 'yesmod': [0], }, 'noadmin': { 'default': [0], 'no': [0], 'yesadmin': [0, 1], 'yesmod': [0, 1], }, 'nomod': { 'default': [0], 'no': [0], 'yesadmin': [0, 1], 'yesmod': [0, 1, 2], }, 'yesadmin': { 'default': [0, 1], 'no': [0], 'yesadmin': [0, 1], 'yesmod': [0, 1], }, 'yesmod': { 'default': [0, 1, 2], 'no': [0], 'yesadmin': [0, 1], 'yesmod': [0, 1, 2], }, } # We want the group to have user 1 as a group admin, user 2 as a group # moderator, and user 3 as a group member. user 0 is a sys admin, and # user 4 is not part of the group. User 5 is the user who is added to # the group. group = Group().createGroup('public ', self.users[0], public=True) for user in [1, 2, 3]: resp = self.request(path='/group/%s/invitation' % group['_id'], params={ 'force': 'true', 'userId': self.users[user]['_id'], 'level': 3 - user }, method='POST', user=self.users[0]) self.assertStatusOk(resp) resp = self.request(path='/group/%s/member' % group['_id'], user=self.users[0], method='DELETE', params={'userId': self.users[0]['_id']}) self.assertStatusOk(resp) for systemSetting in policy: Setting().set(SettingKey.ADD_TO_GROUP_POLICY, systemSetting) for groupSetting in policy[systemSetting]: resp = self.request(path='/group/%s' % group['_id'], user=self.users[0], method='PUT', params={'addAllowed': groupSetting}) self.assertStatusOk(resp) for user in range(5): resp = self.request(path='/group/%s/invitation' % group['_id'], params={ 'force': 'true', 'userId': self.users[5]['_id'], }, method='POST', user=self.users[user]) if user in policy[systemSetting][groupSetting]: self.assertStatusOk(resp) else: self.assertStatus(resp, 403) Setting().unset(SettingKey.ADD_TO_GROUP_POLICY)
def testCollectionAccess(self): # Asking to change to an invalid access list should fail resp = self.request(path='/collection/%s/access' % self.collection['_id'], method='PUT', params={ 'access': 'not an access list', 'public': False }, user=self.admin) self.assertStatus(resp, 400) # Create some folders underneath the collection folder1 = Folder().createFolder(parentType='collection', parent=self.collection, creator=self.admin, public=False, name='top level') folder2 = Folder().createFolder(parentType='folder', parent=folder1, creator=self.admin, public=False, name='subfolder') Folder().createFolder(parentType='collection', parent=self.collection, creator=self.admin, public=False, name='another top level folder') # Admin should see two top level folders resp = self.request(path='/collection/%s/details' % self.collection['_id'], user=self.admin) self.assertStatusOk(resp) self.assertEqual(resp.json['nFolders'], 2) self.assertNotIn('nItems', resp.json) # Normal user should see 0 folders resp = self.request(path='/collection/%s/details' % self.collection['_id'], user=self.user) self.assertStatusOk(resp) self.assertEqual(resp.json['nFolders'], 0) # Add read access on one of the folders Folder().setUserAccess(folder1, self.user, AccessType.READ, save=True) # Normal user should see one folder now resp = self.request(path='/collection/%s/details' % self.collection['_id'], user=self.user) self.assertStatusOk(resp) self.assertEqual(resp.json['nFolders'], 1) # Change the access to allow just the user obj = { 'users': [{ 'id': str(self.user['_id']), 'level': AccessType.WRITE }] } resp = self.request(path='/collection/%s/access' % self.collection['_id'], method='PUT', params={ 'access': json.dumps(obj), 'public': True }, user=self.admin) self.assertStatusOk(resp) # Request the collection access resp = self.request(path='/collection/%s/access' % self.collection['_id'], user=self.admin) self.assertStatusOk(resp) access = resp.json self.assertEqual(access['users'][0]['id'], str(self.user['_id'])) self.assertEqual(access['users'][0]['level'], AccessType.WRITE) coll = Collection().load(self.collection['_id'], force=True) folder1 = Folder().load(folder1['_id'], force=True) folder2 = Folder().load(folder2['_id'], force=True) self.assertEqual(coll['public'], True) self.assertEqual(folder1['public'], False) # Update the collection recursively to public resp = self.request(path='/collection/%s/access' % coll['_id'], method='PUT', params={ 'access': json.dumps(obj), 'public': True, 'recurse': True, 'progress': True }, user=self.admin) self.assertStatusOk(resp) coll = Collection().load(coll['_id'], force=True) folder1 = Folder().load(folder1['_id'], force=True) folder2 = Folder().load(folder2['_id'], force=True) self.assertEqual(coll['public'], True) self.assertEqual(folder1['public'], True) self.assertEqual(folder2['public'], True) self.assertEqual(folder1['access'], coll['access']) self.assertEqual(folder1['access'], folder2['access']) self.assertEqual( folder2['access'], { 'users': [{ 'id': self.user['_id'], 'level': AccessType.WRITE, 'flags': [] }], 'groups': [] }) # Recursively drop the user's access level to READ obj['users'][0]['level'] = AccessType.READ resp = self.request(path='/collection/%s/access' % coll['_id'], method='PUT', params={ 'access': json.dumps(obj), 'public': True, 'recurse': True, 'progress': True }, user=self.admin) self.assertStatusOk(resp) coll = Collection().load(coll['_id'], force=True) folder1 = Folder().load(folder1['_id'], force=True) folder2 = Folder().load(folder2['_id'], force=True) self.assertEqual(coll['public'], True) self.assertEqual(folder1['public'], True) self.assertEqual(folder2['public'], True) self.assertEqual(folder1['access'], coll['access']) self.assertEqual(folder1['access'], folder2['access']) self.assertEqual( folder2['access'], { 'users': [{ 'id': self.user['_id'], 'level': AccessType.READ, 'flags': [] }], 'groups': [] }) # Recursively remove the user's access altogether, also make sure that # passing no "public" param just retains the current flag state obj['users'] = () resp = self.request(path='/collection/%s/access' % coll['_id'], method='PUT', params={ 'access': json.dumps(obj), 'recurse': True }, user=self.admin) self.assertStatusOk(resp) coll = Collection().load(coll['_id'], force=True) folder1 = Folder().load(folder1['_id'], force=True) folder2 = Folder().load(folder2['_id'], force=True) self.assertEqual(coll['public'], True) self.assertEqual(folder1['public'], True) self.assertEqual(folder2['public'], True) self.assertEqual(folder1['access'], coll['access']) self.assertEqual(folder1['access'], folder2['access']) self.assertEqual(folder2['access'], {'users': [], 'groups': []}) # Add group access to the collection group = Group().createGroup('test', self.admin) obj = { 'groups': [{ 'id': str(group['_id']), 'level': AccessType.WRITE }] } resp = self.request(path='/collection/%s/access' % coll['_id'], method='PUT', params={ 'access': json.dumps(obj), 'recurse': False }, user=self.admin) self.assertStatusOk(resp) # Create a new top-level folder, it should inherit the collection ACL. resp = self.request(path='/folder', method='POST', params={ 'name': 'top level 2', 'parentId': coll['_id'], 'parentType': 'collection' }, user=self.admin) self.assertStatusOk(resp) folder = Folder().load(resp.json['_id'], force=True) coll = Collection().load(coll['_id'], force=True) self.assertEqual(coll['access']['users'], []) self.assertEqual(folder['access']['users'], [{ 'id': self.admin['_id'], 'level': AccessType.ADMIN, 'flags': [] }]) self.assertEqual(folder['access']['groups'], [{ 'id': group['_id'], 'level': AccessType.WRITE, 'flags': [] }]) self.assertEqual(folder['access']['groups'], coll['access']['groups'])
def testGroupAccess(self): """ Test creation, invitation, joining, and removing of users from groups. """ self.ensureRequiredParams(path='/group', method='POST', required=['name'], user=self.users[0]) params = { 'name': ' A group name ', 'description': 'my description ', 'public': 'true' } # Anonymous users can't make groups resp = self.request(path='/group', method='POST', params=params) self.assertStatus(resp, 401) self.assertEqual('You must be logged in.', resp.json['message']) # Have user 0 make a public group resp = self.request(path='/group', method='POST', params=params, user=self.users[0]) self.assertStatusOk(resp) self.assertEqual(resp.json['name'], params['name'].strip()) self.assertEqual(resp.json['description'], params['description'].strip()) publicGroup = Group().load(resp.json['_id'], force=True) # User 1 should be able to see the public group self.assertTrue(Group().hasAccess(publicGroup, self.users[0], AccessType.ADMIN)) self.assertTrue(Group().hasAccess(publicGroup, self.users[1], AccessType.READ)) # Try to make group with same name; should fail resp = self.request(path='/group', method='POST', params=params, user=self.users[0]) self.assertValidationError(resp, 'name') # Make a private group now params['public'] = 'false' params['name'] = 'different name' resp = self.request(path='/group', method='POST', params=params, user=self.users[0]) self.assertStatusOk(resp) self.assertEqual(resp.json['name'], params['name'].strip()) self.assertEqual(resp.json['description'], params['description'].strip()) privateGroup = Group().load(resp.json['_id'], force=True) # User 1 should not be able to see it self.assertTrue(Group().hasAccess(privateGroup, self.users[0], AccessType.ADMIN)) self.assertFalse(Group().hasAccess(privateGroup, self.users[1], AccessType.READ)) self.assertEqual(len(list(Group().getMembers(privateGroup))), 1) # Invite user 1 to join the private group as member self.assertTrue(base.mockSmtp.isMailQueueEmpty()) params = {'userId': self.users[1]['_id'], 'level': AccessType.READ} resp = self.request(path='/group/%s/invitation' % privateGroup['_id'], user=self.users[0], method='POST', params=params) self.assertStatusOk(resp) # We should see the invitation in the list resp = self.request(path='/group/%s/invitation' % privateGroup['_id'], user=self.users[0], method='GET') self.assertStatusOk(resp) self.assertEqual(len(resp.json), 1) self.assertEqual(resp.json[0]['_id'], str(self.users[1]['_id'])) # An email should have been sent self.assertTrue(base.mockSmtp.waitForMail()) # Reload user and group since they've changed in the database self.users[1] = User().load(self.users[1]['_id'], force=True) privateGroup = Group().load(privateGroup['_id'], force=True) # User object should now have an invitation set on it self.assertTrue( privateGroup['_id'] in [invite['groupId'] for invite in self.users[1]['groupInvites']]) # User 1 should now be able to see the group, but should not be in it. self.assertTrue(Group().hasAccess(privateGroup, self.users[1], AccessType.READ)) self.assertEqual(len(list(Group().getMembers(privateGroup))), 1) # Removing user 1 from the group before they join it should remove the # invitation. resp = self.request(path='/group/%s/member' % privateGroup['_id'], user=self.users[0], method='DELETE', params={'userId': self.users[1]['_id']}) self.assertStatusOk(resp) self.users[1] = User().load(self.users[1]['_id'], force=True) self.assertEqual(0, len(self.users[1]['groupInvites'])) # Now re-invite user 1 resp = self.request(path='/group/%s/invitation' % privateGroup['_id'], user=self.users[0], method='POST', params=params) self.assertStatusOk(resp) # User 1 should not yet be in the member list resp = self.request(path='/group/%s/member' % privateGroup['_id'], user=self.users[0], method='GET') self.assertStatusOk(resp) self.assertEqual(len(resp.json), 1) self.assertEqual(resp.json[0]['_id'], str(self.users[0]['_id'])) # Have user 1 join the group resp = self.request(path='/group/%s/member' % privateGroup['_id'], method='POST', user=self.users[1]) self.assertStatusOk(resp) # User 1 should now be in the member list resp = self.request(path='/group/%s/member' % privateGroup['_id'], user=self.users[0], method='GET') self.assertStatusOk(resp) self.assertEqual(len(resp.json), 2) self.assertEqual(resp.json[1]['_id'], str(self.users[1]['_id'])) # Reload user and group since they've changed in the database self.users[1] = User().load(self.users[1]['_id'], force=True) privateGroup = Group().load(privateGroup['_id'], force=True) # Invitation should be gone from user 1 self.assertFalse( privateGroup['_id'] in [invite['groupId'] for invite in self.users[1]['groupInvites']]) # User 1 should not be able to invite other members params = {'userId': self.users[2]['_id'], 'level': AccessType.READ} resp = self.request(path='/group/%s/invitation' % privateGroup['_id'], user=self.users[1], method='POST', params=params) self.assertStatus(resp, 403) # We promote user 1 to moderator resp = self.request(path='/group/%s/moderator' % privateGroup['_id'], user=self.users[0], method='POST', params={'userId': self.users[1]['_id']}) self.assertStatusOk(resp) # User 1 should not be able to invite anyone as admin params['level'] = AccessType.ADMIN resp = self.request(path='/group/%s/invitation' % privateGroup['_id'], user=self.users[1], method='POST', params=params) self.assertStatus(resp, 403) # User 1 should be able to invite someone with write access params['level'] = AccessType.WRITE resp = self.request(path='/group/%s/invitation' % privateGroup['_id'], user=self.users[1], method='POST', params=params) self.assertStatusOk(resp) # Have user 2 join the group resp = self.request(path='/group/%s/member' % privateGroup['_id'], method='POST', user=self.users[2]) self.assertStatusOk(resp) # User 1 should not be able to remove a group admin params['userId'] = self.users[0]['_id'] resp = self.request(path='/group/%s/member' % privateGroup['_id'], user=self.users[1], method='DELETE', params=params) self.assertStatus(resp, 403) self.assertTrue( resp.json['message'].startswith('Admin access denied for group')) # User 1 should not be able to delete the group resp = self.request(path='/group/%s' % privateGroup['_id'], user=self.users[1], method='DELETE') self.assertStatus(resp, 403) self.assertTrue( resp.json['message'].startswith('Admin access denied for group')) # We promote user 1 to admin resp = self.request(path='/group/%s/admin' % privateGroup['_id'], user=self.users[0], method='POST', params={'userId': self.users[1]['_id']}) self.assertStatusOk(resp) privateGroup = Group().load(privateGroup['_id'], force=True) self.assertTrue(Group().hasAccess(privateGroup, self.users[1], AccessType.ADMIN)) # User 1 should now be able to promote and demote members resp = self.request(path='/group/%s/admin' % privateGroup['_id'], user=self.users[1], method='POST', params={'userId': self.users[2]['_id']}) self.assertStatusOk(resp) privateGroup = Group().load(privateGroup['_id'], force=True) self.assertTrue(Group().hasAccess(privateGroup, self.users[2], AccessType.ADMIN)) resp = self.request(path='/group/%s/admin' % privateGroup['_id'], user=self.users[1], method='DELETE', params={'userId': self.users[2]['_id']}) self.assertStatusOk(resp) privateGroup = Group().load(privateGroup['_id'], force=True) self.assertFalse(Group().hasAccess(privateGroup, self.users[2], AccessType.ADMIN)) # User 2 should be able to leave the group self.users[2] = User().load(self.users[2]['_id'], force=True) self.assertTrue(privateGroup['_id'] in self.users[2]['groups']) resp = self.request(path='/group/%s/member' % privateGroup['_id'], user=self.users[2], method='DELETE') self.assertStatusOk(resp) self.users[2] = User().load(self.users[2]['_id'], force=True) self.assertFalse(privateGroup['_id'] in self.users[2]['groups']) # User 0 should be able to remove user 1 params['userId'] = self.users[1]['_id'] self.assertEqual(len(list(Group().getMembers(privateGroup))), 2) resp = self.request(path='/group/%s/member' % privateGroup['_id'], user=self.users[0], method='DELETE', params=params) self.assertStatusOk(resp) self.assertEqual(len(list(Group().getMembers(privateGroup))), 1) # User 0 should be able to delete the group self.users[0] = User().load(self.users[0]['_id'], force=True) self.assertEqual(len(self.users[0]['groups']), 2) resp = self.request(path='/group/%s' % privateGroup['_id'], user=self.users[0], method='DELETE') self.assertStatusOk(resp) self.assertEqual(resp.json['message'], 'Deleted the group %s.' % privateGroup['name']) privateGroup = Group().load(privateGroup['_id'], force=True) self.assertTrue(privateGroup is None) # Make sure group reference was removed from user 0 self.users[0] = User().load(self.users[0]['_id'], force=True) self.assertEqual(len(self.users[0]['groups']), 1)
def testGetGroups(self): """ Test the GET endpoints for groups, including getting a single group and getting the list of all groups. """ privateGroup = Group().createGroup('private ', self.users[0], public=False) publicGroup = Group().createGroup('public ', self.users[0], public=True) # Error: Invalid GET URL resp = self.request(path='/group/%s/foo' % publicGroup['_id'], method='GET') self.assertStatus(resp, 400) # Anonymous user should be able to see the public group resp = self.request(path='/group/%s' % publicGroup['_id'], method='GET') self.assertStatusOk(resp) self.assertEqual(resp.json['name'], 'public') # Anonymous user should not be able to see the private group resp = self.request(path='/group/%s' % privateGroup['_id'], method='GET') self.assertStatus(resp, 401) # User 0 should be able to see the private group resp = self.request(path='/group/%s' % privateGroup['_id'], method='GET', user=self.users[0]) self.assertStatusOk(resp) self.assertEqual(resp.json['name'], 'private') # The same rules apply to the group access points resp = self.request(path='/group/%s/access' % publicGroup['_id'], method='GET') self.assertStatusOk(resp) resp = self.request(path='/group/%s/access' % privateGroup['_id'], method='GET') self.assertStatus(resp, 401) resp = self.request(path='/group/%s/access' % privateGroup['_id'], method='GET', user=self.users[0]) self.assertStatusOk(resp) # Test listing all groups privateGroup2 = Group().createGroup('private group', self.users[0], public=False) resp = self.request(path='/group', method='GET', user=self.users[0]) self.assertStatusOk(resp) self.assertIsInstance(resp.json, list) self.assertEqual(len(resp.json), 3) resp = self.request(path='/group', method='GET', user=self.users[1]) self.assertStatusOk(resp) self.assertIsInstance(resp.json, list) self.assertEqual(len(resp.json), 1) self.assertEqual(resp.json[0]['_id'], str(publicGroup['_id'])) # Test searching by name resp = self.request(path='/group', method='GET', user=self.users[0], params={'text': 'private'}) self.assertStatusOk(resp) self.assertIsInstance(resp.json, list) self.assertEqual(len(resp.json), 2) self.assertEqual(resp.json[0]['_id'], str(privateGroup['_id'])) self.assertEqual(resp.json[1]['_id'], str(privateGroup2['_id'])) resp = self.request(path='/group', method='GET', user=self.users[0], params={ 'text': 'private', 'exact': True }) self.assertStatusOk(resp) self.assertIsInstance(resp.json, list) self.assertEqual(len(resp.json), 1) self.assertEqual(resp.json[0]['_id'], str(privateGroup['_id']))
def makeResources(self, admin): self.user2 = User().createUser( email='*****@*****.**', login='******', firstName='user2', lastName='user2', password='******', admin=False) self.group = self.group = Group().createGroup('test group', creator=self.user2) Group().addUser(self.group, self.user2)
def testUploaderUser(provisionedServer, smtp): # Create an uploader admin resp = provisionedServer.request(path='/user', method='POST', params={ 'email': '*****@*****.**', 'login': '******', 'firstName': 'uploader', 'lastName': 'admin', 'password': '******' }) assertStatusOk(resp) uploaderAdmin = User().findOne({'login': '******'}) assert uploaderAdmin is not None contributorsGroup = Group().findOne({'name': 'Dataset Contributors'}) assert contributorsGroup is not None Group().addUser(contributorsGroup, uploaderAdmin, level=AccessType.WRITE) # Create an uploader user resp = provisionedServer.request(path='/user', method='POST', params={ 'email': '*****@*****.**', 'login': '******', 'firstName': 'uploader', 'lastName': 'user', 'password': '******' }) assertStatusOk(resp) uploaderUser = User().findOne({'login': '******'}) assert uploaderUser is not None # TODO: check if a user can upload without agreeing to terms # Ensure request create dataset permission works resp = provisionedServer.request( path='/user/requestCreateDatasetPermission', method='POST', user=uploaderUser) assertStatusOk(resp) assert smtp.waitForMail() assert smtp.getMail( ) # pop off the queue for later assertion that the queue is empty # Ensure that the user can't create datasets yet resp = provisionedServer.request(path='/user/me', method='GET', user=uploaderUser) assertStatusOk(resp) assert not resp.json['permissions']['createDataset'] # Ensure that a join request is pending contributorsGroup = Group().findOne({'name': 'Dataset Contributors'}) joinRequestUserIds = [ user['id'] for user in Group().getFullRequestList(contributorsGroup) ] assert uploaderUser['_id'] in joinRequestUserIds assert smtp.isMailQueueEmpty() # Add the user, then ensure they can create datasets Group().inviteUser(contributorsGroup, uploaderUser, level=AccessType.READ) resp = provisionedServer.request(path='/user/me', method='GET', user=uploaderUser) assertStatusOk(resp) assert resp.json['permissions']['createDataset']
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'