示例#1
0
    def _syncItems(image, cli_dict, user):
        folderModel = Folder()
        itemModel = Item()

        children = folderModel.childItems(
            image.tagFolder, filters={'meta.slicerCLIType': 'task'})
        existingItems = {item['name']: item for item in children}

        for cli, desc in six.iteritems(cli_dict):
            item = itemModel.createItem(
                cli,
                user,
                image.tagFolder,
                'Slicer CLI generated CLI command item',
                reuseExisting=True)
            meta_data = dict(slicerCLIType='task',
                             type=desc.get('type', 'Unknown'))

            # copy some things from the image to be independent
            meta_data['image'] = image.name
            meta_data[
                'digest'] = image.digest if image.name != image.digest else None

            desc_type = desc.get('desc-type', 'xml')

            if desc_type == 'xml':
                meta_data.update(parse_xml_desc(item, desc, user))
            elif desc_type == 'yaml':
                meta_data.update(parse_yaml_desc(item, desc, user))
            elif desc_type == 'json':
                meta_data.update(parse_json_desc(item, desc, user))

            itemModel.setMetadata(item, meta_data)

            if cli in existingItems:
                del existingItems[cli]

        # delete superfluous items
        for item in six.itervalues(existingItems):
            itemModel.remove(item)
示例#2
0
class Item(Resource):

    def __init__(self):
        super(Item, self).__init__()
        self.resourceName = 'item'
        self._model = ItemModel()

        self.route('DELETE', (':id',), self.deleteItem)
        self.route('GET', (), self.find)
        self.route('GET', (':id',), self.getItem)
        self.route('GET', (':id', 'files'), self.getFiles)
        self.route('GET', (':id', 'download'), self.download)
        self.route('GET', (':id', 'rootpath'), self.rootpath)
        self.route('POST', (), self.createItem)
        self.route('PUT', (':id',), self.updateItem)
        self.route('POST', (':id', 'copy'), self.copyItem)
        self.route('PUT', (':id', 'metadata'), self.setMetadata)
        self.route('DELETE', (':id', 'metadata'), self.deleteMetadata)

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('List or search for items.')
        .notes('You must pass either a "itemId" or "text" field '
               'to specify how you are searching for items.  '
               'If you omit one of these parameters the request will fail and respond : '
               '"Invalid search mode."')
        .responseClass('Item', array=True)
        .param('folderId', 'Pass this to list all items in a folder.',
               required=False)
        .param('text', 'Pass this to perform a full text search for items.',
               required=False)
        .param('name', 'Pass to lookup an item by exact name match. Must '
               'pass folderId as well when using this.', required=False)
        .pagingParams(defaultSort='lowerName')
        .errorResponse()
        .errorResponse('Read access was denied on the parent folder.', 403)
    )
    def find(self, folderId, text, name, limit, offset, sort):
        """
        Get a list of items with given search parameters. Currently accepted
        search modes are:

        1. Searching by folderId, with optional additional filtering by the name
           field (exact match) or using full text search within a single parent
           folder. Pass a "name" parameter or "text" parameter to invoke these
           additional filters.
        2. Searching with full text search across all items in the system.
           Simply pass a "text" parameter for this mode.
        """
        user = self.getCurrentUser()

        if folderId:
            folder = Folder().load(
                id=folderId, user=user, level=AccessType.READ, exc=True)
            filters = {}
            if text:
                filters['$text'] = {
                    '$search': text
                }
            if name:
                filters['name'] = name

            return Folder().childItems(
                folder=folder, limit=limit, offset=offset, sort=sort, filters=filters)
        elif text is not None:
            return self._model.textSearch(
                text, user=user, limit=limit, offset=offset, sort=sort)
        else:
            raise RestException('Invalid search mode.')

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Get an item by ID.')
        .responseClass('Item')
        .modelParam('id', model=ItemModel, level=AccessType.READ)
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the item.', 403)
    )
    def getItem(self, item):
        return item

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Create a new item.')
        .responseClass('Item')
        .modelParam('folderId', 'The ID of the parent folder.', model=Folder,
                    level=AccessType.WRITE, paramType='query')
        .param('name', 'Name for the item.', strip=True)
        .param('description', 'Description for the item.', required=False,
               default='', strip=True)
        .param('reuseExisting', 'Return existing item (by name) if it exists.',
               required=False, dataType='boolean', default=False)
        .jsonParam('metadata', 'A JSON object containing the metadata keys to add',
                   paramType='form', requireObject=True, required=False)
        .errorResponse()
        .errorResponse('Write access was denied on the parent folder.', 403)
    )
    def createItem(self, folder, name, description, reuseExisting, metadata):
        newItem = self._model.createItem(
            folder=folder, name=name, creator=self.getCurrentUser(), description=description,
            reuseExisting=reuseExisting)
        if metadata:
            newItem = self._model.setMetadata(newItem, metadata)
        return newItem

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Edit an item or move it to another folder.')
        .responseClass('Item')
        .modelParam('id', model=ItemModel, level=AccessType.WRITE)
        .param('name', 'Name for the item.', required=False, strip=True)
        .param('description', 'Description for the item.', required=False)
        .modelParam('folderId', 'Pass this to move the item to a new folder.', model=Folder,
                    required=False, paramType='query', level=AccessType.WRITE)
        .jsonParam('metadata', 'A JSON object containing the metadata keys to add',
                   paramType='form', requireObject=True, required=False)
        .errorResponse('ID was invalid.')
        .errorResponse('Write access was denied for the item or folder.', 403)
    )
    def updateItem(self, item, name, description, folder, metadata):
        if name is not None:
            item['name'] = name
        if description is not None:
            item['description'] = description

        self._model.updateItem(item)

        if folder and folder['_id'] != item['folderId']:
            self._model.move(item, folder)

        if metadata:
            item = self._model.setMetadata(item, metadata)

        return item

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Set metadata fields on an item.')
        .responseClass('Item')
        .notes('Set metadata fields to null in order to delete them.')
        .modelParam('id', model=ItemModel, level=AccessType.WRITE)
        .jsonParam('metadata', 'A JSON object containing the metadata keys to add',
                   paramType='body', requireObject=True)
        .param('allowNull', 'Whether "null" is allowed as a metadata value.', required=False,
               dataType='boolean', default=False)
        .errorResponse(('ID was invalid.',
                        'Invalid JSON passed in request body.',
                        'Metadata key name was invalid.'))
        .errorResponse('Write access was denied for the item.', 403)
    )
    def setMetadata(self, item, metadata, allowNull):
        return self._model.setMetadata(item, metadata, allowNull=allowNull)

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(ItemModel)
    @autoDescribeRoute(
        Description('Delete metadata fields on an item.')
        .responseClass('Item')
        .modelParam('id', model=ItemModel, level=AccessType.WRITE)
        .jsonParam(
            'fields', 'A JSON list containing the metadata fields to delete',
            paramType='body', schema={
                'type': 'array',
                'items': {
                    'type': 'string'
                }
            }
        )
        .errorResponse(('ID was invalid.',
                        'Invalid JSON passed in request body.',
                        'Metadata key name was invalid.'))
        .errorResponse('Write access was denied for the item.', 403)
    )
    def deleteMetadata(self, item, fields):
        return self._model.deleteMetadata(item, fields)

    def _downloadMultifileItem(self, item, user):
        setResponseHeader('Content-Type', 'application/zip')
        setContentDisposition(item['name'] + '.zip')

        def stream():
            zip = ziputil.ZipGenerator(item['name'])
            for (path, file) in self._model.fileList(item, subpath=False):
                for data in zip.addFile(file, path):
                    yield data
            yield zip.footer()
        return stream

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=File)
    @autoDescribeRoute(
        Description('Get the files within an item.')
        .responseClass('File', array=True)
        .modelParam('id', model=ItemModel, level=AccessType.READ)
        .pagingParams(defaultSort='name')
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the item.', 403)
    )
    def getFiles(self, item, limit, offset, sort):
        return self._model.childFiles(item=item, limit=limit, offset=offset, sort=sort)

    @access.cookie
    @access.public(scope=TokenScope.DATA_READ)
    @autoDescribeRoute(
        Description('Download the contents of an item.')
        .modelParam('id', model=ItemModel, level=AccessType.READ)
        .param('offset', 'Byte offset into the file.', dataType='int',
               required=False, default=0)
        .param('format', 'If unspecified, items with one file are downloaded '
               'as that file, and other items are downloaded as a zip '
               'archive.  If \'zip\', a zip archive is always sent.',
               required=False)
        .param('contentDisposition', 'Specify the Content-Disposition response '
               'header disposition-type value, only applied for single file '
               'items.', required=False, enum=['inline', 'attachment'],
               default='attachment')
        .param('extraParameters', 'Arbitrary data to send along with the '
               'download request, only applied for single file '
               'items.', required=False)
        # single file items could produce other types, too.
        .produces(['application/zip', 'application/octet-stream'])
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the item.', 403)
    )
    def download(self, item, offset, format, contentDisposition, extraParameters):
        user = self.getCurrentUser()
        files = list(self._model.childFiles(item=item, limit=2))
        if format not in (None, '', 'zip'):
            raise RestException('Unsupported format: %s.' % format)
        if len(files) == 1 and format != 'zip':
            if contentDisposition not in {None, 'inline', 'attachment'}:
                raise RestException('Unallowed contentDisposition type "%s".' % contentDisposition)
            return File().download(
                files[0], offset, contentDisposition=contentDisposition,
                extraParameters=extraParameters)
        else:
            return self._downloadMultifileItem(item, user)

    @access.user(scope=TokenScope.DATA_WRITE)
    @autoDescribeRoute(
        Description('Delete an item by ID.')
        .modelParam('id', model=ItemModel, level=AccessType.WRITE)
        .errorResponse('ID was invalid.')
        .errorResponse('Write access was denied for the item.', 403)
    )
    def deleteItem(self, item):
        self._model.remove(item)
        return {'message': 'Deleted item %s.' % item['name']}

    @access.public(scope=TokenScope.DATA_READ)
    @autoDescribeRoute(
        Description('Get the path to the root of the item\'s hierarchy.')
        .modelParam('id', model=ItemModel, level=AccessType.READ)
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the item.', 403)
    )
    def rootpath(self, item):
        return self._model.parentsToRoot(item, self.getCurrentUser())

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Copy an item.')
        .notes('If no folderId parameter is specified, creates a copy of the item in '
               'its current containing folder.')
        .responseClass('Item')
        .modelParam('id', 'The ID of the original item.', model=ItemModel, level=AccessType.READ)
        .modelParam('folderId', 'The ID of the parent folder.', required=False, model=Folder,
                    level=AccessType.WRITE, paramType='query')
        .param('name', 'Name for the new item.', required=False, strip=True)
        .param('description', 'Description for the new item.', required=False, strip=True)
        .errorResponse(('A parameter was invalid.',
                        'ID was invalid.'))
        .errorResponse('Read access was denied on the original item.\n\n'
                       'Write access was denied on the parent folder.', 403)
    )
    def copyItem(self, item, folder, name, description):
        user = self.getCurrentUser()

        if folder is None:
            folder = Folder().load(
                id=item['folderId'], user=user, level=AccessType.WRITE, exc=True)
        return self._model.copyItem(
            item, creator=user, name=name, folder=folder, description=description)
示例#3
0
    def testAccessFlags(self):
        resp = self.request('/system/access_flag')
        self.assertStatusOk(resp)
        self.assertEqual(resp.json, {})

        registerAccessFlag('my_key', name='hello', description='a custom flag')

        resp = self.request('/system/access_flag')
        self.assertStatusOk(resp)
        self.assertEqual(
            resp.json, {
                'my_key': {
                    'name': 'hello',
                    'description': 'a custom flag',
                    'admin': False
                }
            })

        self.users[1] = User().load(self.users[1]['_id'], force=True)
        user = self.users[1]

        # Manage custom access flags on an access controlled resource
        self.assertFalse(User().hasAccessFlags(user, user, flags=['my_key']))

        # Admin should always have permission
        self.assertTrue(User().hasAccessFlags(user,
                                              self.users[0],
                                              flags=['my_key']))

        # Test the requireAccessFlags method
        with self.assertRaises(AccessException):
            User().requireAccessFlags(user, user=user, flags='my_key')

        User().requireAccessFlags(user, user=self.users[0], flags='my_key')

        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], [])

        # Test loadmodel requiredFlags argument via REST endpoint
        resp = self.request('/test_endpoints/loadmodel_with_flags/%s' %
                            user['_id'],
                            user=self.users[1])
        self.assertStatus(resp, 403)

        user = User().setAccessList(
            self.users[0],
            access={
                'users': [{
                    'id': self.users[1]['_id'],
                    'level': AccessType.ADMIN,
                    'flags': ['my_key', 'not a registered flag']
                }],
                'groups': [{
                    'id': self.group['_id'],
                    'level': AccessType.ADMIN,
                    'flags': ['my_key']
                }]
            },
            save=True)

        resp = self.request('/test_endpoints/loadmodel_with_flags/%s' %
                            user['_id'],
                            user=self.users[1])
        self.assertStatusOk(resp)
        self.assertEqual(resp.json, 'success')

        # Only registered flags should be stored
        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], ['my_key'])
        self.assertTrue(User().hasAccessFlags(user, user, flags=['my_key']))

        # Create an admin-only access flag
        registerAccessFlag('admin_flag', name='admin flag', admin=True)

        # Non-admin shouldn't be able to set it
        user = User().setAccessList(self.users[0],
                                    access={
                                        'users': [{
                                            'id': self.users[1]['_id'],
                                            'level': AccessType.ADMIN,
                                            'flags': ['admin_flag']
                                        }],
                                        'groups': []
                                    },
                                    save=True,
                                    user=self.users[1])

        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], [])

        # Admin user should be able to set it
        user = User().setAccessList(self.users[1],
                                    access={
                                        'users': [{
                                            'id': self.users[1]['_id'],
                                            'level': AccessType.ADMIN,
                                            'flags': ['admin_flag']
                                        }],
                                        'groups': [{
                                            'id': self.group['_id'],
                                            'level': AccessType.ADMIN,
                                            'flags': ['admin_flag']
                                        }]
                                    },
                                    save=True,
                                    user=self.users[0])

        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], ['admin_flag'])

        # An already-enabled admin-only flag should stay enabled for non-admin user
        user = User().setAccessList(self.users[1],
                                    access={
                                        'users': [{
                                            'id':
                                            self.users[1]['_id'],
                                            'level':
                                            AccessType.ADMIN,
                                            'flags': ['my_key', 'admin_flag']
                                        }],
                                        'groups': [{
                                            'id': self.group['_id'],
                                            'level': AccessType.ADMIN,
                                            'flags': ['admin_flag']
                                        }]
                                    },
                                    save=True,
                                    user=self.users[1])

        acl = User().getFullAccessList(user)
        self.assertEqual(set(acl['users'][0]['flags']),
                         {'my_key', 'admin_flag'})
        self.assertEqual(acl['groups'][0]['flags'], ['admin_flag'])

        # Test setting public flags on a collection and folder
        collectionModel = Collection()
        folderModel = Folder()
        itemModel = Item()
        collection = collectionModel.createCollection('coll',
                                                      creator=self.users[0],
                                                      public=True)
        folder = folderModel.createFolder(collection,
                                          'folder',
                                          parentType='collection',
                                          creator=self.users[0])

        # Add an item to the folder so we can test AclMixin flag behavior
        item = itemModel.createItem(folder=folder,
                                    name='test',
                                    creator=self.users[0])

        folder = folderModel.setUserAccess(folder,
                                           self.users[1],
                                           level=AccessType.ADMIN,
                                           save=True,
                                           currentUser=self.users[0])

        with self.assertRaises(AccessException):
            collectionModel.requireAccessFlags(collection,
                                               user=None,
                                               flags='my_key')

        # Test AclMixin flag behavior
        with self.assertRaises(AccessException):
            itemModel.requireAccessFlags(item, user=None, flags='my_key')

        self.assertFalse(
            itemModel.hasAccessFlags(item, user=None, flags='my_key'))

        collection = collectionModel.setAccessList(collection,
                                                   access=collection['access'],
                                                   save=True,
                                                   recurse=True,
                                                   user=self.users[0],
                                                   publicFlags=['my_key'])
        collectionModel.requireAccessFlags(collection,
                                           user=None,
                                           flags='my_key')

        # Make sure recursive setting of public flags worked
        folder = folderModel.load(folder['_id'], force=True)
        self.assertEqual(folder['publicFlags'], ['my_key'])

        itemModel.requireAccessFlags(item, user=None, flags='my_key')

        # Non-admin shouldn't be able to set admin-only public flags
        folder = folderModel.setPublicFlags(folder,
                                            flags=['admin_flag'],
                                            user=self.users[1],
                                            save=True)
        self.assertEqual(folder['publicFlags'], [])

        # Admin users should be able to set admin-only public flags
        folder = folderModel.setPublicFlags(folder,
                                            flags=['admin_flag'],
                                            user=self.users[0],
                                            save=True,
                                            append=True)
        self.assertEqual(folder['publicFlags'], ['admin_flag'])

        # Non-admin users can set admin-only public flags if they are already enabled
        folder = folderModel.setPublicFlags(folder,
                                            flags=['admin_flag', 'my_key'],
                                            user=self.users[1],
                                            save=True)
        self.assertEqual(set(folder['publicFlags']), {'admin_flag', 'my_key'})

        # Test "force" options
        folder = folderModel.setPublicFlags(folder,
                                            flags='admin_flag',
                                            force=True,
                                            save=True)
        self.assertEqual(folder['publicFlags'], ['admin_flag'])

        folder = folderModel.setAccessList(folder,
                                           access={
                                               'users': [{
                                                   'id':
                                                   self.users[1]['_id'],
                                                   'level':
                                                   AccessType.ADMIN,
                                                   'flags':
                                                   ['my_key', 'admin_flag']
                                               }],
                                               'groups': []
                                           },
                                           save=True,
                                           force=True)
        folderModel.requireAccessFlags(folder,
                                       user=self.users[1],
                                       flags='my_key')

        folder = folderModel.setUserAccess(folder,
                                           self.users[1],
                                           level=AccessType.READ,
                                           save=True,
                                           force=True,
                                           flags=[])
        self.assertFalse(
            folderModel.hasAccessFlags(folder, self.users[1], flags='my_key'))

        folder = folderModel.setGroupAccess(folder,
                                            self.group,
                                            level=AccessType.READ,
                                            save=True,
                                            force=True,
                                            flags='my_key')
        folderModel.requireAccessFlags(folder,
                                       user=self.users[1],
                                       flags='my_key')

        # Testing with flags=None should give sensible behavior
        folderModel.requireAccessFlags(folder, user=None, flags=None)

        # Test filtering results by access flags (both ACModel and AclMixin)
        for model, doc in ((folderModel, folder), (itemModel, item)):
            cursor = model.find({})
            self.assertGreater(len(list(cursor)), 0)

            cursor = model.find({})
            filtered = list(
                model.filterResultsByPermission(cursor,
                                                user=None,
                                                level=AccessType.READ,
                                                flags='my_key'))
            self.assertEqual(len(filtered), 0)

            cursor = model.find({})
            filtered = list(
                model.filterResultsByPermission(cursor,
                                                user=self.users[1],
                                                level=AccessType.READ,
                                                flags=('my_key',
                                                       'admin_flag')))
            self.assertEqual(len(filtered), 1)
            self.assertEqual(filtered[0]['_id'], doc['_id'])
示例#4
0
class LabelResource(Resource):

    def __init__(self):
        super().__init__()
        self.resourceName = 'label'

        self.ann_file_name = "annotation.json"

        self.coll_m = Collection()
        self.file_m = File()
        self.folder_m = Folder()
        self.item_m = Item()
        self.upload_m = Upload()
        self.asset_m = Assetstore()

        self.setupRoutes()

    def setupRoutes(self):
        self.route('GET', (), handler=self.getLabelList)
        self.route('GET', (':label_id',), self.getLabel)
        self.route('GET', ('meta',), self.getLabelMeta)
        self.route('GET', ('create',), self.createLabelFile)
        self.route('GET', ('by_name',), self.getLabelByName)
        self.route('POST', (), self.postLabel)

    # ############# PUBLIC METHODS ##################

    @access.public
    @autoDescribeRoute(
        Description('Get label list').param('assign_id', 'assignment folder id'))
    @rest.rawResponse
    @trace
    def getLabelList(self, assign_id):
        files = self.folder_m.fileList(assign_id, user=self.getCurrentUser(), data=False,
                                       includeMetadata=True, mimeFilter=['application/json'])
        files = list(files)
        cherrypy.response.headers["Content-Type"] = "application/json"
        return dumps(files)

    @access.public
    @autoDescribeRoute(
        Description('Create a new label file inside the label folder if it doesnt exist')
            .param('assign_id', 'the parent folder id')
            .param('name', 'image name for which we are creating this label file'))
    @rest.rawResponse
    @trace
    def createLabelFile(self, assign_id, name):
        p_folder = self.folder_m.load(id=assign_id,
                                      user=self.getCurrentUser(),
                                      level=AccessType.WRITE)

        label_folder = find_folder(p_folder=p_folder,
                                   name=name,
                                   user=self.getCurrentUser(),
                                   desc="Label Folder",
                                   create=True)

        file = find_file(p_folder=label_folder,
                         name=self.ann_file_name,
                         user=self.getCurrentUser(),
                         assetstore=self.asset_m.getCurrent(),
                         create=False)

        if not file:
            file = create_new_file(p_folder=label_folder,
                                   name=self.ann_file_name,
                                   user=self.getCurrentUser(),
                                   assetstore=self.asset_m.getCurrent())

            config_file = self.__find_config(assign_id)

            if not config_file:
                printFail("No config file found")
                return errorMessage("No config file found")
            else:
                res = copy_file(src_file=config_file,
                                dest_file=file,
                                user=self.getCurrentUser())
                return dumps({
                    "label_id": res['fileId']
                })

        return dumps({
            "label_id": file['_id']
        })

    @access.public
    @autoDescribeRoute(
        Description('Get labels by file_name')
            .param('name', 'label file name')
            .param('assign_id', 'the assignment id'))
    @rest.rawResponse
    @trace
    def getLabelByName(self, name, assign_id):
        p_folder = self.folder_m.load(assign_id,
                                      user=self.getCurrentUser(),
                                      level=AccessType.READ)

        label_folder = find_folder(p_folder=p_folder,
                                   name=name,
                                   user=self.getCurrentUser(),
                                   desc="Label Folder",
                                   create=True)

        # this file is created in <assign_folder>/<label_folder>/assignment.json
        file = find_file(p_folder=label_folder,
                         name=self.ann_file_name,
                         user=self.getCurrentUser(),
                         assetstore=self.asset_m.getCurrent(),
                         create=False)

        cherrypy.response.headers["Content-Type"] = "application/json"
        if file:
            return self.file_m.download(file)
        else:
            return dumps({})

    @access.public
    @autoDescribeRoute(
        Description('Get label file by id')
            .param('label_id', 'label file id'))
    @rest.rawResponse
    @trace
    def getLabel(self, label_id):
        file = self.file_m.load(label_id, level=AccessType.READ, user=self.getCurrentUser())
        printOk2(file)
        cherrypy.response.headers["Content-Type"] = "application/json"
        return self.file_m.download(file)

    @access.public
    @autoDescribeRoute(
        Description('Get label meta data by id')
            .param('label_id', 'label file id'))
    @trace
    def getLabelMeta(self, label_id):
        file = self.file_m.load(label_id, level=AccessType.READ, user=self.getCurrentUser())
        cherrypy.response.headers["Content-Type"] = "application/json"
        return dumps(file)

    @access.public
    @autoDescribeRoute(
        Description('Post to label file by id')
            .param('label_id', 'label file id')
            .param('labels', 'labels to be updated'))
    @rest.rawResponse
    @trace
    def postLabel(self, label_id, labels):
        file = self.file_m.load(label_id, level=AccessType.WRITE, user=self.getCurrentUser())
        cherrypy.response.headers["Content-Type"] = "application/json"
        params = {'labels': json.loads(labels)}
        data = json.dumps(params, indent=2, sort_keys=True)
        upload = writeData(self.getCurrentUser(), file, data)
        printOk2(file)
        printOk(upload)
        return dumps(upload)

    # ############# PRIVATE METHODS ##################

    def __create_new_file(self, folder, file_name):
        item = self.item_m.createItem(file_name,
                                      creator=self.getCurrentUser(),
                                      folder=folder,
                                      description='label file',
                                      reuseExisting=False)

        file = self.file_m.createFile(size=0,
                                      item=item,
                                      name=file_name,
                                      creator=self.getCurrentUser(),
                                      assetstore=self.asset_m.getCurrent(),
                                      mimeType="application/json")
        return file

    @staticmethod
    def __get_owner_id(folder):
        aclList = Folder().getFullAccessList(folder)
        for acl in aclList['users']:
            if acl['level'] == AccessType.ADMIN:
                return str(acl['id'])
        return None

    def __get_config_folder(self, label_folder_id):
        label_folder = Folder().load(label_folder_id,
                                     user=self.getCurrentUser(),
                                     level=AccessType.READ)
        ownerId = self.__get_owner_id(label_folder)
        config_folder = self.folder_m.load(label_folder['meta'][ownerId], level=AccessType.READ,
                                           user=self.getCurrentUser())
        return config_folder

    def __find_config(self, folder_id):
        folder = self.__get_config_folder(folder_id)
        printOk2("Config folder {}".format(folder))
        files = self.folder_m.fileList(folder, self.getCurrentUser(), data=False)
        for file_path, file in files:
            printOk(file)
            if file['name'] == "config.json":
                return file

    def __findFolder(self, p_folder, name, desc="", create=False):
        """
        Find folder by name. If not found create the folder
        :param p_folder: parent folder
        :param name: name of the folder you want to find inside the parent folder
        :return: folder doc
        """
        folder = list(self.folder_m.find({'folderId': p_folder['_id'], 'name': name}).limit(1))
        if not folder:
            # check if you are allowed to create, else return nothing
            if create:
                folder = self.folder_m.createFolder(parent=p_folder,
                                                    name=name,
                                                    creator=self.getCurrentUser(),
                                                    description=desc,
                                                    reuseExisting=True)
            else:
                return None

        return folder

    def __findFile(self, folder, file_name):
        item = list(self.item_m.find({'folderId': folder['_id'], 'name': file_name}).limit(1))
        if not item:
            return None

        item = item[0]
        file = list(self.file_m.find({'itemId': item['_id']}).limit(1))

        if not file:
            return None

        return file[0]
示例#5
0
class Item(Resource):

    def __init__(self):
        super(Item, self).__init__()
        self.resourceName = 'item'
        self._model = ItemModel()

        self.route('DELETE', (':id',), self.deleteItem)
        self.route('GET', (), self.find)
        self.route('GET', (':id',), self.getItem)
        self.route('GET', (':id', 'files'), self.getFiles)
        self.route('GET', (':id', 'download'), self.download)
        self.route('GET', (':id', 'rootpath'), self.rootpath)
        self.route('POST', (), self.createItem)
        self.route('PUT', (':id',), self.updateItem)
        self.route('POST', (':id', 'copy'), self.copyItem)
        self.route('PUT', (':id', 'metadata'), self.setMetadata)
        self.route('DELETE', (':id', 'metadata'), self.deleteMetadata)

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('List or search for items.')
        .notes('You must pass either a "itemId" or "text" field '
               'to specify how you are searching for items.  '
               'If you omit one of these parameters the request will fail and respond : '
               '"Invalid search mode."')
        .responseClass('Item', array=True)
        .param('folderId', 'Pass this to list all items in a folder.',
               required=False)
        .param('text', 'Pass this to perform a full text search for items.',
               required=False)
        .param('name', 'Pass to lookup an item by exact name match. Must '
               'pass folderId as well when using this.', required=False)
        .pagingParams(defaultSort='lowerName')
        .errorResponse()
        .errorResponse('Read access was denied on the parent folder.', 403)
    )
    def find(self, folderId, text, name, limit, offset, sort):
        """
        Get a list of items with given search parameters. Currently accepted
        search modes are:

        1. Searching by folderId, with optional additional filtering by the name
           field (exact match) or using full text search within a single parent
           folder. Pass a "name" parameter or "text" parameter to invoke these
           additional filters.
        2. Searching with full text search across all items in the system.
           Simply pass a "text" parameter for this mode.
        """
        user = self.getCurrentUser()

        if folderId:
            folder = Folder().load(
                id=folderId, user=user, level=AccessType.READ, exc=True)
            filters = {}
            if text:
                filters['$text'] = {
                    '$search': text
                }
            if name:
                filters['name'] = name

            return Folder().childItems(
                folder=folder, limit=limit, offset=offset, sort=sort, filters=filters)
        elif text is not None:
            return self._model.textSearch(
                text, user=user, limit=limit, offset=offset, sort=sort)
        else:
            raise RestException('Invalid search mode.')

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Get an item by ID.')
        .responseClass('Item')
        .modelParam('id', model=ItemModel, level=AccessType.READ)
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the item.', 403)
    )
    def getItem(self, item):
        return item

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Create a new item.')
        .responseClass('Item')
        .modelParam('folderId', 'The ID of the parent folder.', model=Folder,
                    level=AccessType.WRITE, paramType='query')
        .param('name', 'Name for the item.', strip=True)
        .param('description', 'Description for the item.', required=False,
               default='', strip=True)
        .param('reuseExisting', 'Return existing item (by name) if it exists.',
               required=False, dataType='boolean', default=False)
        .jsonParam('metadata', 'A JSON object containing the metadata keys to add',
                   paramType='form', requireObject=True, required=False)
        .errorResponse()
        .errorResponse('Write access was denied on the parent folder.', 403)
    )
    def createItem(self, folder, name, description, reuseExisting, metadata):
        newItem = self._model.createItem(
            folder=folder, name=name, creator=self.getCurrentUser(), description=description,
            reuseExisting=reuseExisting)
        if metadata:
            newItem = self._model.setMetadata(newItem, metadata)
        return newItem

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Edit an item or move it to another folder.')
        .responseClass('Item')
        .modelParam('id', model=ItemModel, level=AccessType.WRITE)
        .param('name', 'Name for the item.', required=False, strip=True)
        .param('description', 'Description for the item.', required=False)
        .modelParam('folderId', 'Pass this to move the item to a new folder.', model=Folder,
                    required=False, paramType='query', level=AccessType.WRITE)
        .jsonParam('metadata', 'A JSON object containing the metadata keys to add',
                   paramType='form', requireObject=True, required=False)
        .errorResponse('ID was invalid.')
        .errorResponse('Write access was denied for the item or folder.', 403)
    )
    def updateItem(self, item, name, description, folder, metadata):
        if name is not None:
            item['name'] = name
        if description is not None:
            item['description'] = description

        self._model.updateItem(item)

        if folder and folder['_id'] != item['folderId']:
            self._model.move(item, folder)

        if metadata:
            item = self._model.setMetadata(item, metadata)

        return item

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Set metadata fields on an item.')
        .responseClass('Item')
        .notes('Set metadata fields to null in order to delete them.')
        .modelParam('id', model=ItemModel, level=AccessType.WRITE)
        .jsonParam('metadata', 'A JSON object containing the metadata keys to add',
                   paramType='body', requireObject=True)
        .param('allowNull', 'Whether "null" is allowed as a metadata value.', required=False,
               dataType='boolean', default=False)
        .errorResponse(('ID was invalid.',
                        'Invalid JSON passed in request body.',
                        'Metadata key name was invalid.'))
        .errorResponse('Write access was denied for the item.', 403)
    )
    def setMetadata(self, item, metadata, allowNull):
        return self._model.setMetadata(item, metadata, allowNull=allowNull)

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(ItemModel)
    @autoDescribeRoute(
        Description('Delete metadata fields on an item.')
        .responseClass('Item')
        .modelParam('id', model=ItemModel, level=AccessType.WRITE)
        .jsonParam(
            'fields', 'A JSON list containing the metadata fields to delete',
            paramType='body', schema={
                'type': 'array',
                'items': {
                    'type': 'string'
                }
            }
        )
        .errorResponse(('ID was invalid.',
                        'Invalid JSON passed in request body.',
                        'Metadata key name was invalid.'))
        .errorResponse('Write access was denied for the item.', 403)
    )
    def deleteMetadata(self, item, fields):
        return self._model.deleteMetadata(item, fields)

    def _downloadMultifileItem(self, item, user):
        setResponseHeader('Content-Type', 'application/zip')
        setContentDisposition(item['name'] + '.zip')

        def stream():
            zip = ziputil.ZipGenerator(item['name'])
            for (path, file) in self._model.fileList(item, subpath=False):
                for data in zip.addFile(file, path):
                    yield data
            yield zip.footer()
        return stream

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=File)
    @autoDescribeRoute(
        Description('Get the files within an item.')
        .responseClass('File', array=True)
        .modelParam('id', model=ItemModel, level=AccessType.READ)
        .pagingParams(defaultSort='name')
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the item.', 403)
    )
    def getFiles(self, item, limit, offset, sort):
        return self._model.childFiles(item=item, limit=limit, offset=offset, sort=sort)

    @access.cookie
    @access.public(scope=TokenScope.DATA_READ)
    @autoDescribeRoute(
        Description('Download the contents of an item.')
        .modelParam('id', model=ItemModel, level=AccessType.READ)
        .param('offset', 'Byte offset into the file.', dataType='int',
               required=False, default=0)
        .param('format', 'If unspecified, items with one file are downloaded '
               'as that file, and other items are downloaded as a zip '
               'archive.  If \'zip\', a zip archive is always sent.',
               required=False)
        .param('contentDisposition', 'Specify the Content-Disposition response '
               'header disposition-type value, only applied for single file '
               'items.', required=False, enum=['inline', 'attachment'],
               default='attachment')
        .param('extraParameters', 'Arbitrary data to send along with the '
               'download request, only applied for single file '
               'items.', required=False)
        # single file items could produce other types, too.
        .produces(['application/zip', 'application/octet-stream'])
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the item.', 403)
    )
    def download(self, item, offset, format, contentDisposition, extraParameters):
        user = self.getCurrentUser()
        files = list(self._model.childFiles(item=item, limit=2))
        if format not in (None, '', 'zip'):
            raise RestException('Unsupported format: %s.' % format)
        if len(files) == 1 and format != 'zip':
            if contentDisposition not in {None, 'inline', 'attachment'}:
                raise RestException('Unallowed contentDisposition type "%s".' % contentDisposition)
            return File().download(
                files[0], offset, contentDisposition=contentDisposition,
                extraParameters=extraParameters)
        else:
            return self._downloadMultifileItem(item, user)

    @access.user(scope=TokenScope.DATA_WRITE)
    @autoDescribeRoute(
        Description('Delete an item by ID.')
        .modelParam('id', model=ItemModel, level=AccessType.WRITE)
        .errorResponse('ID was invalid.')
        .errorResponse('Write access was denied for the item.', 403)
    )
    def deleteItem(self, item):
        self._model.remove(item)
        return {'message': 'Deleted item %s.' % item['name']}

    @access.public(scope=TokenScope.DATA_READ)
    @autoDescribeRoute(
        Description('Get the path to the root of the item\'s hierarchy.')
        .modelParam('id', model=ItemModel, level=AccessType.READ)
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the item.', 403)
    )
    def rootpath(self, item):
        return self._model.parentsToRoot(item, self.getCurrentUser())

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Copy an item.')
        .notes('If no folderId parameter is specified, creates a copy of the item in '
               'its current containing folder.')
        .responseClass('Item')
        .modelParam('id', 'The ID of the original item.', model=ItemModel, level=AccessType.READ)
        .modelParam('folderId', 'The ID of the parent folder.', required=False, model=Folder,
                    level=AccessType.WRITE)
        .param('name', 'Name for the new item.', required=False, strip=True)
        .param('description', 'Description for the new item.', required=False, strip=True)
        .errorResponse(('A parameter was invalid.',
                        'ID was invalid.'))
        .errorResponse('Read access was denied on the original item.\n\n'
                       'Write access was denied on the parent folder.', 403)
    )
    def copyItem(self, item, folder, name, description):
        user = self.getCurrentUser()

        if folder is None:
            folder = Folder().load(
                id=item['folderId'], user=user, level=AccessType.WRITE, exc=True)
        return self._model.copyItem(
            item, creator=user, name=name, folder=folder, description=description)
示例#6
0
    def testAccessFlags(self):
        resp = self.request('/system/access_flag')
        self.assertStatusOk(resp)
        self.assertEqual(resp.json, {})

        registerAccessFlag('my_key', name='hello', description='a custom flag')

        resp = self.request('/system/access_flag')
        self.assertStatusOk(resp)
        self.assertEqual(resp.json, {
            'my_key': {
                'name': 'hello',
                'description': 'a custom flag',
                'admin': False
            }
        })

        self.users[1] = User().load(self.users[1]['_id'], force=True)
        user = self.users[1]

        # Manage custom access flags on an access controlled resource
        self.assertFalse(User().hasAccessFlags(user, user, flags=['my_key']))

        # Admin should always have permission
        self.assertTrue(User().hasAccessFlags(user, self.users[0], flags=['my_key']))

        # Test the requireAccessFlags method
        with self.assertRaises(AccessException):
            User().requireAccessFlags(user, user=user, flags='my_key')

        User().requireAccessFlags(user, user=self.users[0], flags='my_key')

        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], [])

        # Test loadmodel requiredFlags argument via REST endpoint
        resp = self.request(
            '/test_endpoints/loadmodel_with_flags/%s' % user['_id'], user=self.users[1])
        self.assertStatus(resp, 403)

        user = User().setAccessList(self.users[0], access={
            'users': [{
                'id': self.users[1]['_id'],
                'level': AccessType.ADMIN,
                'flags': ['my_key', 'not a registered flag']
            }],
            'groups': [{
                'id': self.group['_id'],
                'level': AccessType.ADMIN,
                'flags': ['my_key']
            }]
        }, save=True)

        resp = self.request(
            '/test_endpoints/loadmodel_with_flags/%s' % user['_id'], user=self.users[1])
        self.assertStatusOk(resp)
        self.assertEqual(resp.json, 'success')

        # Only registered flags should be stored
        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], ['my_key'])
        self.assertTrue(User().hasAccessFlags(user, user, flags=['my_key']))

        # Create an admin-only access flag
        registerAccessFlag('admin_flag', name='admin flag', admin=True)

        # Non-admin shouldn't be able to set it
        user = User().setAccessList(self.users[0], access={
            'users': [{
                'id': self.users[1]['_id'],
                'level': AccessType.ADMIN,
                'flags': ['admin_flag']
            }],
            'groups': []
        }, save=True, user=self.users[1])

        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], [])

        # Admin user should be able to set it
        user = User().setAccessList(self.users[1], access={
            'users': [{
                'id': self.users[1]['_id'],
                'level': AccessType.ADMIN,
                'flags': ['admin_flag']
            }],
            'groups': [{
                'id': self.group['_id'],
                'level': AccessType.ADMIN,
                'flags': ['admin_flag']
            }]
        }, save=True, user=self.users[0])

        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], ['admin_flag'])

        # An already-enabled admin-only flag should stay enabled for non-admin user
        user = User().setAccessList(self.users[1], access={
            'users': [{
                'id': self.users[1]['_id'],
                'level': AccessType.ADMIN,
                'flags': ['my_key', 'admin_flag']
            }],
            'groups': [{
                'id': self.group['_id'],
                'level': AccessType.ADMIN,
                'flags': ['admin_flag']
            }]
        }, save=True, user=self.users[1])

        acl = User().getFullAccessList(user)
        self.assertEqual(set(acl['users'][0]['flags']), {'my_key', 'admin_flag'})
        self.assertEqual(acl['groups'][0]['flags'], ['admin_flag'])

        # Test setting public flags on a collection and folder
        collectionModel = Collection()
        folderModel = Folder()
        itemModel = Item()
        collection = collectionModel.createCollection('coll', creator=self.users[0], public=True)
        folder = folderModel.createFolder(
            collection, 'folder', parentType='collection', creator=self.users[0])

        # Add an item to the folder so we can test AclMixin flag behavior
        item = itemModel.createItem(folder=folder, name='test', creator=self.users[0])

        folder = folderModel.setUserAccess(
            folder, self.users[1], level=AccessType.ADMIN, save=True, currentUser=self.users[0])

        with self.assertRaises(AccessException):
            collectionModel.requireAccessFlags(collection, user=None, flags='my_key')

        # Test AclMixin flag behavior
        with self.assertRaises(AccessException):
            itemModel.requireAccessFlags(item, user=None, flags='my_key')

        self.assertFalse(itemModel.hasAccessFlags(item, user=None, flags='my_key'))

        collection = collectionModel.setAccessList(
            collection, access=collection['access'], save=True, recurse=True, user=self.users[0],
            publicFlags=['my_key'])
        collectionModel.requireAccessFlags(collection, user=None, flags='my_key')

        # Make sure recursive setting of public flags worked
        folder = folderModel.load(folder['_id'], force=True)
        self.assertEqual(folder['publicFlags'], ['my_key'])

        itemModel.requireAccessFlags(item, user=None, flags='my_key')

        # Non-admin shouldn't be able to set admin-only public flags
        folder = folderModel.setPublicFlags(
            folder, flags=['admin_flag'], user=self.users[1], save=True)
        self.assertEqual(folder['publicFlags'], [])

        # Admin users should be able to set admin-only public flags
        folder = folderModel.setPublicFlags(
            folder, flags=['admin_flag'], user=self.users[0], save=True, append=True)
        self.assertEqual(folder['publicFlags'], ['admin_flag'])

        # Non-admin users can set admin-only public flags if they are already enabled
        folder = folderModel.setPublicFlags(
            folder, flags=['admin_flag', 'my_key'], user=self.users[1], save=True)
        self.assertEqual(set(folder['publicFlags']), {'admin_flag', 'my_key'})

        # Test "force" options
        folder = folderModel.setPublicFlags(folder, flags='admin_flag', force=True, save=True)
        self.assertEqual(folder['publicFlags'], ['admin_flag'])

        folder = folderModel.setAccessList(folder, access={
            'users': [{
                'id': self.users[1]['_id'],
                'level': AccessType.ADMIN,
                'flags': ['my_key', 'admin_flag']
            }],
            'groups': []
        }, save=True, force=True)
        folderModel.requireAccessFlags(folder, user=self.users[1], flags='my_key')

        folder = folderModel.setUserAccess(
            folder, self.users[1], level=AccessType.READ, save=True, force=True, flags=[])
        self.assertFalse(folderModel.hasAccessFlags(folder, self.users[1], flags='my_key'))

        folder = folderModel.setGroupAccess(
            folder, self.group, level=AccessType.READ, save=True, force=True, flags='my_key')
        folderModel.requireAccessFlags(folder, user=self.users[1], flags='my_key')

        # Testing with flags=None should give sensible behavior
        folderModel.requireAccessFlags(folder, user=None, flags=None)

        # Test filtering results by access flags (both ACModel and AclMixin)
        for model, doc in ((folderModel, folder), (itemModel, item)):
            cursor = model.find({})
            self.assertGreater(len(list(cursor)), 0)

            cursor = model.find({})
            filtered = list(model.filterResultsByPermission(
                cursor, user=None, level=AccessType.READ, flags='my_key'))
            self.assertEqual(len(filtered), 0)

            cursor = model.find({})
            filtered = list(model.filterResultsByPermission(
                cursor, user=self.users[1], level=AccessType.READ, flags=('my_key', 'admin_flag')))
            self.assertEqual(len(filtered), 1)
            self.assertEqual(filtered[0]['_id'], doc['_id'])
示例#7
0
class HdfsAssetstoreResource(Resource):
    def __init__(self):
        super(HdfsAssetstoreResource, self).__init__()
        self.resourceName = 'hdfs_assetstore'
        self.route('PUT', (':id', 'import'), self.importData)

        self.folderModel = Folder()  # Save to avoid many lookups
        self.itemModel = Item()
        self.fileModel = File()

    def _importFile(self, parent, name, user, assetstore, node):
        item = self.itemModel.findOne({
            'folderId': parent['_id'],
            'name': name
        })
        if item is None:
            item = self.itemModel.createItem(
                name=name, creator=user, folder=parent)

        file = self.fileModel.findOne({
            'name': name,
            'itemId': item['_id']
        })
        if file is None:
            file = self.fileModel.createFile(
                creator=user, item=item, name=name, size=node['length'],
                assetstore=assetstore, mimeType=None, saveFile=False)

        file['hdfs'] = {
            'imported': True,
            'path': node['path']
        }
        self.fileModel.save(file)

    def _importData(self, parentType, parent, assetstore, client, path, ctx,
                    user):
        for node in client.ls([path]):
            ctx.update(message='Importing ' + node['path'])
            name = posixpath.basename(node['path'])

            if node['file_type'] == 'd':
                folder = self.folderModel.findOne({
                    'parentId': parent['_id'],
                    'name': name,
                    'parentCollection': parentType
                })
                if folder is None:
                    folder = self.folderModel.createFolder(
                        parent, name, parentType=parentType, creator=user)

                self._importData('folder', folder, assetstore, client,
                                 node['path'], ctx, user)
            elif node['file_type'] == 'f' and parentType == 'folder':
                self._importFile(parent, name, user, assetstore, node)

    @access.admin
    @loadmodel(model='assetstore')
    @describeRoute(
        Description('Import a data hierarchy from an HDFS instance.')
        .notes('Only site administrators may use this endpoint.')
        .param('id', 'The ID of the assetstore representing the HDFS instance.',
               paramType='path')
        .param('parentId', 'The ID of the parent object in the Girder data '
               'hierarchy under which to import the files.')
        .param('parentType', 'The type of the parent object to import into.',
               enum=('folder', 'user', 'collection'), required=False)
        .param('path', 'Root of the directory structure (relative to the root '
               'of the HDFS) to import.')
        .param('progress', 'Whether to record progress on this operation ('
               'default=False)', required=False, dataType='boolean')
        .errorResponse()
        .errorResponse('You are not an administrator.', 403)
    )
    def importData(self, assetstore, params):
        self.requireParams(('parentId', 'path'), params)

        user = self.getCurrentUser()
        parentType = params.get('parentType', 'folder')
        if parentType not in ('user', 'collection', 'folder'):
            raise RestException('Invalid parentType.')

        parent = self.model(parentType).load(params['parentId'], force=True, exc=True)

        progress = self.boolParam('progress', params, default=False)
        client = HdfsClient(
            host=assetstore['hdfs']['host'], port=assetstore['hdfs']['port'], use_trash=False)
        path = params['path']

        with ProgressContext(progress, user=user, title='Importing data from HDFS') as ctx:
            try:
                self._importData(parentType, parent, assetstore, client, path, ctx, user)
            except FileNotFoundException:
                raise RestException('File not found: %s.' % path)
示例#8
0
class Item(Resource):

    def __init__(self):
        super().__init__()
        self.resourceName = 'item'
        self._model = ItemModel()

        self.route('DELETE', (':id',), self.deleteItem)
        self.route('GET', (), self.find)
        self.route('GET', (':id',), self.getItem)
        self.route('GET', (':id', 'files'), self.getFiles)
        self.route('GET', (':id', 'download'), self.download)
        self.route('GET', (':id', 'rootpath'), self.rootpath)
        self.route('GET', (':id', 'position'), self.findPosition)
        self.route('POST', (), self.createItem)
        self.route('PUT', (':id',), self.updateItem)
        self.route('POST', (':id', 'copy'), self.copyItem)
        self.route('PUT', (':id', 'metadata'), self.setMetadata)
        self.route('DELETE', (':id', 'metadata'), self.deleteMetadata)
        #added by Shubhang on 23rd nov 
        self.route('PUT', ('moveItems',), self.moveItems)

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('List or search for items.')
        .notes('You must pass either a "folderId" or "text" field '
               'to specify how you are searching for items.  '
               'If you omit one of these parameters the request will fail and respond : '
               '"Invalid search mode."')
        .responseClass('Item', array=True)
        .param('folderId', 'Pass this to list all items in a folder.',
               required=False)
        .param('text', 'Pass this to perform a full text search for items.',
               required=False)
        .param('name', 'Pass to lookup an item by exact name match. Must '
               'pass folderId as well when using this.', required=False)
        .pagingParams(defaultSort='lowerName')
        .errorResponse()
        .errorResponse('Read access was denied on the parent folder.', 403)
    )
    def find(self, folderId, text, name, limit, offset, sort):
        """
        Get a list of items with given search parameters. Currently accepted
        search modes are:

        1. Searching by folderId, with optional additional filtering by the name
           field (exact match) or using full text search within a single parent
           folder. Pass a "name" parameter or "text" parameter to invoke these
           additional filters.
        2. Searching with full text search across all items in the system.
           Simply pass a "text" parameter for this mode.
        """
        return self._find(folderId, text, name, limit, offset, sort)

    def _find(self, folderId, text, name, limit, offset, sort, filters=None):
        user = self.getCurrentUser()

        filters = (filters.copy() if filters else {})
        if folderId:
            folder = Folder().load(
                id=folderId, user=user, level=AccessType.READ, exc=True)
            if text:
                filters['$text'] = {
                    '$search': text
                }
            if name:
                filters['name'] = name

            return Folder().childItems(
                folder=folder, limit=limit, offset=offset, sort=sort, filters=filters)
        elif text is not None:
            return self._model.textSearch(
                text, user=user, limit=limit, offset=offset, sort=sort, filters=filters)
        else:
            raise RestException('Invalid search mode.')

    @access.public(scope=TokenScope.DATA_READ)
    @autoDescribeRoute(
        Description('Report the offset of an item in a list or search.')
        .notes('You must pass either a "folderId" or "text" field '
               'to specify how you are searching for items.  '
               'If you omit one of these parameters the request will fail and respond : '
               '"Invalid search mode."')
        .modelParam('id', model=ItemModel, level=AccessType.READ)
        .param('folderId', 'Pass this to list all items in a folder.',
               required=False)
        .param('text', 'Pass this to perform a full text search for items.',
               required=False)
        .param('name', 'Pass to lookup an item by exact name match. Must '
               'pass folderId as well when using this.', required=False)
        .param('sort', 'Field to sort the result set by.', default='lowerName',
               required=False, strip=True)
        .param('sortdir', 'Sort order: 1 for ascending, -1 for descending.',
               required=False, dataType='integer',
               enum=[SortDir.ASCENDING, SortDir.DESCENDING],
               default=SortDir.ASCENDING)
        .errorResponse()
        .errorResponse('Read access was denied on the parent folder.', 403)
    )
    def findPosition(self, item, folderId, text, name, params):
        limit, offset, sort = self.getPagingParameters(params, 'lowerName')
        if len(sort) != 1 or sort[0][0] not in item:
            raise RestException('Invalid sort mode.')
        sortField, sortDir = sort[0]
        dir = '$lt' if sortDir == SortDir.ASCENDING else '$gt'
        filters = {'$or': [
            {sortField: {dir: item.get(sortField)}},
            # For items that have the same sort value, sort by _id.
            # Mongo may fall back to this as the final sort, but this isn't
            # documented.
            {sortField: item.get(sortField), '_id': {dir: item['_id']}}
        ]}
        # limit and offset don't affect the results.
        cursor = self._find(folderId, text, name, 1, 0, sort, filters)
        return cursor.count()

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Get an item by ID.')
        .responseClass('Item')
        .modelParam('id', model=ItemModel, level=AccessType.READ)
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the item.', 403)
    )
    def getItem(self, item):
        return item

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Create a new item.')
        .responseClass('Item')
        .modelParam('folderId', 'The ID of the parent folder.', model=Folder,
                    level=AccessType.WRITE, paramType='query')
        .param('name', 'Name for the item.', strip=True)
        .param('description', 'Description for the item.', required=False,
               default='', strip=True)
        .param('reuseExisting', 'Return existing item (by name) if it exists.',
               required=False, dataType='boolean', default=False)
        .jsonParam('metadata', 'A JSON object containing the metadata keys to add',
                   paramType='form', requireObject=True, required=False)
        .errorResponse()
        .errorResponse('Write access was denied on the parent folder.', 403)
    )
    def createItem(self, folder, name, description, reuseExisting, metadata):
        newItem = self._model.createItem(
            folder=folder, name=name, creator=self.getCurrentUser(), description=description,
            reuseExisting=reuseExisting)
        if metadata:
            newItem = self._model.setMetadata(newItem, metadata)
        return newItem

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Edit an item or move it to another folder.')
        .responseClass('Item')
        .modelParam('id', model=ItemModel, level=AccessType.WRITE)
        .param('name', 'Name for the item.', required=False, strip=True)
        .param('description', 'Description for the item.', required=False)
        .modelParam('folderId', 'Pass this to move the item to a new folder.', model=Folder,
                    required=False, paramType='query', level=AccessType.WRITE)
        .jsonParam('metadata', 'A JSON object containing the metadata keys to add',
                   paramType='form', requireObject=True, required=False)
        .errorResponse('ID was invalid.')
        .errorResponse('Write access was denied for the item or folder.', 403)
    )
    def updateItem(self, item, name, description, folder, metadata):
        #logging.error(Description.modelParam(self,name='5fb637c698dc0d7acfe89190', model=ItemModel, level=AccessType.WRITE))
        # logging.error(item)
        if name is not None:
            item['name'] = name
        if description is not None:
            item['description'] = description

        self._model.updateItem(item)

        if folder and folder['_id'] != item['folderId']:
            self._model.move(item, folder)

        if metadata:
            item = self._model.setMetadata(item, metadata)

        return item

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Set metadata fields on an item.')
        .responseClass('Item')
        .notes('Set metadata fields to null in order to delete them.')
        .modelParam('id', model=ItemModel, level=AccessType.WRITE)
        .jsonParam('metadata', 'A JSON object containing the metadata keys to add',
                   paramType='body', requireObject=True)
        .param('allowNull', 'Whether "null" is allowed as a metadata value.', required=False,
               dataType='boolean', default=False)
        .errorResponse(('ID was invalid.',
                        'Invalid JSON passed in request body.',
                        'Metadata key name was invalid.'))
        .errorResponse('Write access was denied for the item.', 403)
    )
    def setMetadata(self, item, metadata, allowNull):
        return self._model.setMetadata(item, metadata, allowNull=allowNull)

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(ItemModel)
    @autoDescribeRoute(
        Description('Delete metadata fields on an item.')
        .responseClass('Item')
        .modelParam('id', model=ItemModel, level=AccessType.WRITE)
        .jsonParam(
            'fields', 'A JSON list containing the metadata fields to delete',
            paramType='body', schema={
                'type': 'array',
                'items': {
                    'type': 'string'
                }
            }
        )
        .errorResponse(('ID was invalid.',
                        'Invalid JSON passed in request body.',
                        'Metadata key name was invalid.'))
        .errorResponse('Write access was denied for the item.', 403)
    )
    def deleteMetadata(self, item, fields):
        return self._model.deleteMetadata(item, fields)

    def _downloadMultifileItem(self, item, user):
        setResponseHeader('Content-Type', 'application/zip')
        setContentDisposition(item['name'] + '.zip')

        def stream():
            zip = ziputil.ZipGenerator(item['name'])
            for (path, file) in self._model.fileList(item, subpath=False):
                for data in zip.addFile(file, path):
                    yield data
            yield zip.footer()
        return stream

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=File)
    @autoDescribeRoute(
        Description('Get the files within an item.')
        .responseClass('File', array=True)
        .modelParam('id', model=ItemModel, level=AccessType.READ)
        .pagingParams(defaultSort='name')
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the item.', 403)
    )
    def getFiles(self, item, limit, offset, sort):
        return self._model.childFiles(item=item, limit=limit, offset=offset, sort=sort)

    @access.public(scope=TokenScope.DATA_READ, cookie=True)
    @autoDescribeRoute(
        Description('Download the contents of an item.')
        .modelParam('id', model=ItemModel, level=AccessType.READ)
        .param('offset', 'Byte offset into the file.', dataType='int',
               required=False, default=0)
        .param('format', 'If unspecified, items with one file are downloaded '
               'as that file, and other items are downloaded as a zip '
               "archive.  If 'zip', a zip archive is always sent.",
               required=False)
        .param('contentDisposition', 'Specify the Content-Disposition response '
               'header disposition-type value, only applied for single file '
               'items.', required=False, enum=['inline', 'attachment'],
               default='attachment')
        .param('extraParameters', 'Arbitrary data to send along with the '
               'download request, only applied for single file '
               'items.', required=False)
        # single file items could produce other types, too.
        .produces(['application/zip', 'application/octet-stream'])
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the item.', 403)
    )
    def download(self, item, offset, format, contentDisposition, extraParameters):
        user = self.getCurrentUser()
        files = list(self._model.childFiles(item=item, limit=2))
        if format not in (None, '', 'zip'):
            raise RestException('Unsupported format: %s.' % format)
        if len(files) == 1 and format != 'zip':
            if contentDisposition not in {None, 'inline', 'attachment'}:
                raise RestException('Unallowed contentDisposition type "%s".' % contentDisposition)
            return File().download(
                files[0], offset, contentDisposition=contentDisposition,
                extraParameters=extraParameters)
        else:
            return self._downloadMultifileItem(item, user)

    @access.user(scope=TokenScope.DATA_WRITE)
    @autoDescribeRoute(
        Description('Delete an item by ID.')
        .modelParam('id', model=ItemModel, level=AccessType.WRITE)
        .errorResponse('ID was invalid.')
        .errorResponse('Write access was denied for the item.', 403)
    )
    def deleteItem(self, item):
        self._model.remove(item)
        return {'message': 'Deleted item %s.' % item['name']}

    @access.public(scope=TokenScope.DATA_READ)
    @autoDescribeRoute(
        Description("Get the path to the root of the item's hierarchy.")
        .modelParam('id', model=ItemModel, level=AccessType.READ)
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the item.', 403)
    )
    def rootpath(self, item):
        return self._model.parentsToRoot(item, self.getCurrentUser())

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Copy an item.')
        .notes('If no folderId parameter is specified, creates a copy of the item in '
               'its current containing folder.')
        .responseClass('Item')
        .modelParam('id', 'The ID of the original item.', model=ItemModel, level=AccessType.READ)
        .modelParam('folderId', 'The ID of the parent folder.', required=False, model=Folder,
                    level=AccessType.WRITE, paramType='query')
        .param('name', 'Name for the new item.', required=False, strip=True)
        .param('description', 'Description for the new item.', required=False, strip=True)
        .errorResponse(('A parameter was invalid.',
                        'ID was invalid.'))
        .errorResponse('Read access was denied on the original item.\n\n'
                       'Write access was denied on the parent folder.', 403)
    )
    def copyItem(self, item, folder, name, description):
        user = self.getCurrentUser()

        if folder is None:
            folder = Folder().load(
                id=item['folderId'], user=user, level=AccessType.WRITE, exc=True)
        return self._model.copyItem(
            item, creator=user, name=name, folder=folder, description=description)


    # For moving multiple images.
    
    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=ItemModel)
    @autoDescribeRoute(
        Description('Edit an item or move it to another folder.')
        .param('folder','enter id', paramType='formData')
        .param('itemArray', "Ids of item array.", paramType='formData')                                                                                                                                                                                                                                                                                                                                                                                              
        .errorResponse('Write access was denied for the item or folder.', 403)
    )
    def moveItems(self, folder, itemArray):
        
        # for item1 in itemarray:
        #     logging.error(item1)
        #     # self.updateItem(item1,None,None,folder,None)                                                    
        # logging.error(folder)
        
        item_dict = json.loads(itemArray)
        folder_dict = json.loads(folder)

        
        x = folder_dict['_id']
        folder_dict['_id'] = ObjectId(x)
        x = folder_dict['baseParentId']
        folder_dict['baseParentId'] = ObjectId(x)
        x = folder_dict['parentId']
        folder_dict['parentId'] = ObjectId(x)
        x = folder_dict['creatorId']
        folder_dict['creatorId'] = ObjectId(x)

        for oneItem in item_dict:
            # logging.error('---------INDIVIDUAL ITEM-----------')
            # logging.error(oneItem)

            x = oneItem['_id']
            oneItem['_id'] = ObjectId(x)
            x = oneItem['folderId']
            oneItem['folderId'] = ObjectId(x)
            x = oneItem['creatorId']
            oneItem['creatorId'] = ObjectId(x)
            x = oneItem['baseParentId']
            oneItem['baseParentId'] = ObjectId(x)

            self._model.move(oneItem, folder_dict)

        return json.loads(itemArray)                                                                                                                                                                    
示例#9
0
class LabelResource(Resource):

    def __init__(self):
        super().__init__()
        self.resourceName = 'label'

        self.coll_m = Collection()
        self.file_m = File()
        self.folder_m = Folder()
        self.item_m = Item()
        self.upload_m = Upload()
        self.asset_m = Assetstore()

        self.setupRoutes()

    def setupRoutes(self):
        self.route('GET', (), handler=self.getLabelList)
        self.route('GET', (':label_id',), self.getLabel)
        self.route('GET', ('meta',), self.getLabelMeta)
        self.route('GET', ('create',), self.createLabelFile)
        self.route('GET', ('by_name',), self.getLabelByName)
        self.route('POST', (), self.postLabel)

    def createNewFile(self, folder, file_name):
        item = self.item_m.createItem(file_name,
                                      creator=self.getCurrentUser(),
                                      folder=folder,
                                      description='label file',
                                      reuseExisting=False)

        file = self.file_m.createFile(size=0,
                                      item=item,
                                      name=file_name,
                                      creator=self.getCurrentUser(),
                                      assetstore=self.asset_m.getCurrent(),
                                      mimeType="application/json")
        return file

    def copy(self, srcFile, destFile):
        upload = self.upload_m.createUploadToFile(destFile, self.getCurrentUser(), srcFile['size'])
        self.upload_m.handleChunk(upload=upload,
                                  chunk=RequestBodyStream(self.file_m.open(srcFile), size=destFile['size']),
                                  user=self.getCurrentUser())
        return upload

    @access.public
    @autoDescribeRoute(
        Description('Get label list'))
    @rest.rawResponse
    def getLabelList(self):
        printOk('getLabelsList() was called!')

        try:
            collection = list(self.coll_m.list(user=self.getCurrentUser(), offset=0, limit=1))[0]
            files = self.coll_m.fileList(collection, user=self.getCurrentUser(), data=False,
                                         includeMetadata=True, mimeFilter=['application/json'])
            files = list(files)
            cherrypy.response.headers["Content-Type"] = "application/json"
            return dumps(files)

        except:
            printFail(traceback.print_exc)

    @staticmethod
    def getOwnerId(folder):
        aclList = Folder().getFullAccessList(folder)
        for acl in aclList['users']:
            if acl['level'] == AccessType.ADMIN:
                return str(acl['id'])
        return None

    def getConfigFolder(self, label_folder_id):
        label_folder = Folder().load(label_folder_id,
                                     user=self.getCurrentUser(),
                                     level=AccessType.READ)
        ownerId = self.getOwnerId(label_folder)
        config_folder = self.folder_m.load(label_folder['meta'][ownerId], level=AccessType.READ,
                                           user=self.getCurrentUser())
        return config_folder

    def findConfig(self, folder_id):
        folder = self.getConfigFolder(folder_id)
        printOk2("Config folder {}".format(folder))
        files = self.folder_m.fileList(folder, self.getCurrentUser(), data=False)
        for file_path, file in files:
            printOk(file)
            if file['name'] == "config.json":
                return file

    def __findFile(self, folder, file_name):
        item = list(self.item_m.find({'folderId': folder['_id'], 'name': file_name}).limit(1))
        if not item:
            return None

        item = item[0]
        file = list(self.file_m.find({'itemId': item['_id']}).limit(1))

        if not file:
            return None

        return file[0]

    @access.public
    @autoDescribeRoute(
        Description('Create a new label file if it doesnt exist')
            .param('file_name', 'label file name').param('folder_id', 'the parent folder id'))
    @rest.rawResponse
    def createLabelFile(self, file_name, folder_id):
        try:
            folder = self.folder_m.load(folder_id, user=self.getCurrentUser(), level=AccessType.WRITE)
            file = self.__findFile(folder, file_name)
            if not file:
                file = self.createNewFile(folder, file_name)
                config_file = self.findConfig(folder_id)
                if not config_file:
                    printFail("No config file found")
                    return errorMessage("No config file found")
                else:
                    res = self.copy(config_file, file)
                    return dumps({
                        "label_id": res['fileId']
                    })

            return dumps({
                "label_id": file['_id']
            })
        except:
            printFail(traceback.print_exc)
            cherrypy.response.status = 500

    @access.public
    @autoDescribeRoute(
        Description('Get labels by file_name')
            .param('file_name', 'label file name').param('folder_id', 'the parent folder id'))
    @rest.rawResponse
    def getLabelByName(self, file_name, folder_id):
        try:
            folder = self.folder_m.load(folder_id, user=self.getCurrentUser(), level=AccessType.READ)
            file = self.__findFile(folder, file_name)
            cherrypy.response.headers["Content-Type"] = "application/json"
            if file:
                return self.file_m.download(file)
            else:
                return dumps({})
        except:
            printFail(traceback.print_exc)
            cherrypy.response.status = 500

    @access.public
    @autoDescribeRoute(
        Description('Get label by id')
            .param('label_id', 'label file id'))
    @rest.rawResponse
    def getLabel(self, label_id):
        try:
            file = self.file_m.load(label_id, level=AccessType.READ, user=self.getCurrentUser())
            printOk2(file)
            cherrypy.response.headers["Content-Type"] = "application/json"
            return self.file_m.download(file)
        except:
            # Unknown slug
            printFail(traceback.print_exc)
            cherrypy.response.status = 404

    @access.public
    @autoDescribeRoute(
        Description('Get label by id')
            .param('label_id', 'label file id'))
    def getLabelMeta(self, label_id):
        try:
            file = self.file_m.load(label_id, level=AccessType.READ, user=self.getCurrentUser())
            cherrypy.response.headers["Content-Type"] = "application/json"
            return dumps(file)
        except:
            # Unknown slug
            printFail(traceback.print_exc)
            cherrypy.response.status = 404

    @access.public
    @autoDescribeRoute(
        Description('Post label by id')
            .param('label_id', 'label file id'))
    @rest.rawResponse
    def postLabel(self, label_id, params):
        try:
            file = self.file_m.load(label_id, level=AccessType.WRITE, user=self.getCurrentUser())
            cherrypy.response.headers["Content-Type"] = "application/json"
            params['labels'] = json.loads(params['labels'])
            data = json.dumps(params, indent=2, sort_keys=True)
            upload = writeData(self.getCurrentUser(), file, data)
            printOk2(file)
            printOk(upload)
            return dumps(upload)

        except:
            # Unknown slug
            printFail(traceback.print_exc)
            cherrypy.response.status = 404

    @access.public
    @autoDescribeRoute(
        Description('Post label by id')
            .param('label_id', 'label file id'))
    @rest.rawResponse
    def strokeToOutline(self, strokes):
        pass