Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
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']}
Ejemplo n.º 4
0
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'
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
def group(user):
    g = Group().createGroup(name='agroup', creator=user)
    yield g
Ejemplo n.º 7
0
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']}
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
    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'])
Ejemplo n.º 10
0
 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)
Ejemplo n.º 11
0
    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'])
Ejemplo n.º 12
0
    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)
Ejemplo n.º 13
0
    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']))
Ejemplo n.º 14
0
 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)
Ejemplo n.º 15
0
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']
Ejemplo n.º 16
0
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'