def find(tagFolderId, user): folderModel = Folder() tagFolder = folderModel.load(tagFolderId, user=user, level=AccessType.READ) if not tagFolder: return None imageFolder = folderModel.load(tagFolder['parentId'], user=user, level=AccessType.READ) return DockerImageItem(imageFolder, tagFolder, user)
def execute_simulation(self, folderId, targetTime): user, token = self.getCurrentUser(returnToken=True) folder_model = Folder() job_model = Job() folder = folder_model.load(folderId, user=user, level=AccessType.WRITE, exc=True) girder_config = GirderConfig(token=token['_id'], folder=folder['_id']) simulation_config = SimulationConfig() # TODO: This would be better stored as a dict, but it's easier once we change the # config object format. simulation_config_file = StringIO() simulation_config.write(simulation_config_file) job = job_model.createJob( title='NLI Simulation', type='nli_simulation', kwargs={ 'girder_config': attr.asdict(girder_config), 'simulation_config': simulation_config_file.getvalue(), }, ) job = run_simulation.delay( girder_config=girder_config, simulation_config=simulation_config, target_time=targetTime, job=job, ) return job
def _validate_dataset(tale): try: jsonschema.validate(tale["dataSet"], dataSetSchema) except jsonschema.exceptions.ValidationError as exc: raise ValidationException(str(exc)) creator = User().load(tale["creatorId"], force=True) for obj in tale["dataSet"]: if obj["_modelType"] == "folder": model = Folder() else: model = Item() model.load(obj["itemId"], level=AccessType.READ, user=creator, fields={}, exc=True)
def getAdjacentImages(self, currentImage): folderModel = Folder() folder = folderModel.load(currentImage['folderId'], user=self.getCurrentUser(), level=AccessType.READ) allImages = [ item for item in folderModel.childItems(folder) if _isLargeImageItem(item) ] try: index = allImages.index(currentImage) except ValueError: raise RestException('Id is not an image', 404) return { 'previous': allImages[index - 1], 'next': allImages[(index + 1) % len(allImages)] }
def getAdjacentImages(self, currentImage, currentFolder=None): folderModel = Folder() if currentFolder: folder = currentFolder else: folder = folderModel.load( currentImage['folderId'], user=self.getCurrentUser(), level=AccessType.READ) if folder.get('isVirtual'): children = folderModel.childItems(folder, includeVirtual=True) else: children = folderModel.childItems(folder) allImages = [item for item in children if _isLargeImageItem(item)] try: index = allImages.index(currentImage) except ValueError: raise RestException('Id is not an image', 404) return { 'previous': allImages[index - 1], 'next': allImages[(index + 1) % len(allImages)] }
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'])
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]
class LabelImageResource(Resource): def __init__(self): super().__init__() self.resourceName = 'labelImage' 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.label_image_folder_name = "LabelImages" self.setupRoutes() def setupRoutes(self): self.route('GET', (), handler=self.getList) self.route('POST', (), handler=self.post) self.route('GET', ("download", ), handler=self.download) @access.public @autoDescribeRoute(Description('Get label Image list')) @rest.rawResponse @trace def getList(self): printOk2("get label image called") 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/png']) files = list(files) cherrypy.response.headers["Content-Type"] = "application/png" return dumps(files) @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 @access.public @autoDescribeRoute( Description( 'Create a new label image file if it doesnt exist, else update'). param('label_name', 'label name').param( 'image_name', 'The original image that this belongs to').param( 'assign_id', 'the assignment folder id').param('image', 'image in string64')) @rest.rawResponse @trace def post(self, label_name, image_name, assign_id, image): p_folder = self.folder_m.load(assign_id, user=self.getCurrentUser(), level=AccessType.WRITE) label_folder = find_folder(p_folder=p_folder, name=image_name, user=self.getCurrentUser(), create=True) label_image_folder = find_folder(p_folder=label_folder, name=self.label_image_folder_name, user=self.getCurrentUser(), create=True) safe_label_name = label_name.replace("/", "_") file_name = ".".join([safe_label_name, 'png']) file = find_file(p_folder=label_image_folder, name=file_name, user=self.getCurrentUser(), assetstore=self.asset_m.getCurrent(), create=True) # remove data:image/png;base64, image = image.split(',')[1] image = base64.b64decode(image) # image = decode_base64(image) upload = writeBytes(self.getCurrentUser(), file, image) return dumps({"label_image_file": upload['fileId']}) def __downloadFolder(self, folder): pass @access.public @autoDescribeRoute( Description( "download label images using image id. Returns a stream to the zip file. " ).param('assign_id', 'id of the assignment').param( 'image_name', 'name of the images whose label images you want to download')) @rest.rawResponse @trace def downloadAssignment(self, assign_id): assignment = self.folder_m.load(assign_id, user=self.getCurrentUser(), level=AccessType.WRITE) # find the label image folder return self.__downloadFolder(assignment) @access.public @autoDescribeRoute( Description( "download the full collection. Returns a stream to the zip file. " ).param('assign_id', 'id of the assignment').param( 'image_name', 'name of the images whose label images you want to download')) @rest.rawResponse @trace def downloadCollection(self): collection = list( self.coll_m.list(user=self.getCurrentUser(), offset=0, limit=1))[0] # find the label image folder return self.__downloadFolder(collection) @access.cookie @access.public(scope=TokenScope.DATA_READ) @autoDescribeRoute( Description( "download label images using image id. Returns a stream to the zip file. " ).param('assign_id', 'id of the assignment').param( 'image_name', 'name of the images whose label images you want to download'). produces('application/zip')) @rest.rawResponse @trace def download(self, assign_id, image_name): assignment = self.folder_m.load(assign_id, user=self.getCurrentUser(), level=AccessType.READ) label_folder = find_folder(p_folder=assignment, name=image_name, user=self.getCurrentUser(), create=True) folder = find_folder(p_folder=label_folder, name=self.label_image_folder_name, user=self.getCurrentUser(), create=True) printOk(folder) setResponseHeader('Content-Type', 'application/zip') setContentDisposition(label_folder['name'] + '.zip') user = self.getCurrentUser() def stream(): zip = ziputil.ZipGenerator(folder['name']) for (path, file) in self.folder_m.fileList(folder, user=user, subpath=False): for data in zip.addFile(file, path): yield data yield zip.footer() return stream
class AssignmentResource(Resource): def __init__(self): super().__init__() 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.user_m = User() self.setupRoutes() def setupRoutes(self): self.route('GET', (), handler=self.list) self.route('GET', (':a_id',), handler=self.getAssignment) self.route('GET', ('admin_data',), handler=self.getAdminData) def __findName(self, folder): imageFolder = self.__findImageFolder(folder) if isinstance(imageFolder, dict): return imageFolder['name'] return "" def __findImageFolder(self, folder): owner = self.__findOwner(folder) ret = "" if self.folder_m.getAccessLevel(folder, self.getCurrentUser()) == AccessType.ADMIN: # this folder was created by this user, and so it will contain the images # printFail(folder) ret = folder else: meta = folder['meta'] # this is the label file, and so should only have one entry in the metadata assert len(meta) == 1 # that one entry contains link to the image folder, key must be the creator of this folder assert str(owner['_id']) in meta, (str(owner['_id']), meta) ret = self.folder_m.load(meta[str(owner['_id'])], level=AccessType.READ, user=self.getCurrentUser()) return ret def __findLabelFolder(self, folder): owner = self.__findOwner(folder) ret = [] if self.folder_m.getAccessLevel(folder, self.getCurrentUser()) == AccessType.ADMIN: # this folder was created by this user, so it will contain images if 'meta' not in folder: return [] meta = folder['meta'] for k, v in meta.items(): ret.append(self.folder_m.load(v, level=AccessType.READ, user=self.getCurrentUser())) else: # this folder was not created by this user, so it will contain labels ret = [folder] return ret def __findOwner(self, folder): aclList = Folder().getFullAccessList(folder) for acl in aclList['users']: if acl['level'] == AccessType.ADMIN: return self.user_m.load(str(acl['id']), level=AccessType.READ, user=self.getCurrentUser()) return None @access.public @autoDescribeRoute( Description('Get list of all assignments that it owns or is a part of') .param('limit', 'Number of assignments to return') .param('offset', 'offset from 0th assignment to start looking for assignments')) @rest.rawResponse def list(self, limit, offset): try: printOk((limit, offset)) ret = self.__list(int(limit), int(offset)) cherrypy.response.headers["Content-Type"] = "application/json" return dumps(ret) except: printFail(traceback.print_exc) def __list(self, limit=0, offset=0): user = self.getCurrentUser() folders = self.folder_m.find({'parentId': user['_id'], 'parentCollection': 'user'}, limit=limit, offset=offset) ret = [] for folder in folders: if self.__findName(folder): ret.append({'name': self.__findName(folder), 'image_folder': self.__findImageFolder(folder), 'label_folders': self.__findLabelFolder(folder), 'owner': self.__findOwner(folder)}) return ret def __getAssignment(self, _id): assignments = self.__list() for assignment in assignments: # printOk2(assignment) if str(assignment['image_folder']['_id']) == _id: return assignment return None def __getAnnotators(self, assignment): ret = [] for label_folder in assignment['label_folders']: printOk(label_folder) ret.append({ 'user': self.user_m.load(label_folder['parentId'], user=self.getCurrentUser(), level=AccessType.READ), 'expanded': False, }) return ret @access.public @autoDescribeRoute( Description('Get assignment by id').param('a_id', 'folder id that controls the assignment')) @rest.rawResponse def getAssignment(self, a_id): try: assignment = self.__getAssignment(a_id) return dumps(assignment) except: printFail(traceback.print_exc) @access.public @autoDescribeRoute( Description('Get admin data associated with assignment id').param('a_id', 'folder id that controls the assignment')) @rest.rawResponse def getAdminData(self, a_id): try: assignment = self.__getAssignment(a_id) if assignment['owner']['_id'] == self.getCurrentUser()['_id']: # then current user is this assignment's admin return dumps({'annotators': self.__getAnnotators(assignment)}) except: printFail(traceback.print_exc)
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'])
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
class App(Resource): def __init__(self): super(App, self).__init__() self.resourceName = 'app' self._model = Folder() self.route('POST', (), self.initApp) self.route('GET', (), self.listApp) self.route('DELETE', (':app_id', ), self.deleteApp) self.route('POST', (':app_id', 'release'), self.createNewRelease) self.route('GET', (':app_id', 'release'), self.getAllReleases) self.route('GET', (':app_id', 'release', ':release_id_or_name'), self.getReleaseByIdOrName) self.route('DELETE', (':app_id', 'release', ':release_id_or_name'), self.deleteReleaseByIdOrName) self.route('GET', (':app_id', 'extension'), self.getExtensions) self.route('GET', (':app_id', 'extension', ':extension_name'), self.getExtensionByName) self.route('POST', (':app_id', 'extension'), self.createOrUpdateExtension) self.route('DELETE', (':app_id', 'extension', ':ext_id'), self.deleteExtension) @autoDescribeRoute( Description('Create a new application.').responseClass('Folder').notes( 'If collectionId is missing or collectionName does not match an existing ' 'collection, a fresh new collection will be created with the "collection_name" ' 'given in parameters. ' 'By default the name "Applications" will be given to the collection.' ).param('name', 'The name of the application.').param( 'app_description', 'Application description.', required=False).param( 'collection_id', 'The ID of the collection which contain the application', required=False).param( 'collection_name', 'The Name of the collection which be created to contain' ' the application', required=False).param('collection_description', 'Collection description.', required=False). param('public', 'Whether the collection should be publicly visible.', required=False, dataType='boolean', default=True).errorResponse( 'Write permission denied on the application.', 403)) @access.user(scope=TokenScope.DATA_WRITE) def initApp(self, name, app_description, collection_id, collection_name, collection_description, public): """ Create the directory for start a new application. By default, without specifying a ``collection_id``, it will create a new collection name either ``collection_name`` if provided, or **'Applications'**. If the collection 'Applications already exist it will get it. Return the new application (as a folder) that always contain a default sub-folder named 'nightly'. :param name: Name of the new application :param app_description: Description of the new application :param collection_id: Id of the collection within create the application :param collection_name: Name of the collection which will be created :param collection_description: Description of the new collection :param public: Whether the new collection should be publicly visible :return: The new application folder """ creator = self.getCurrentUser() # Load or create the collection that contain the application if collection_id: collection = Collection().load(collection_id, force=True) elif collection_name: collection = list(Collection().find({'name': collection_name}, user=creator)) if not collection: collection = Collection().createCollection( name=collection_name, description=collection_description, public=public, creator=creator) else: collection = collection[0] else: collection = list(Collection().find({'name': 'Applications'}, user=creator)) if not collection: collection = Collection().createCollection( name='Applications', description=collection_description, public=public, creator=creator) else: collection = collection[0] # Create the application if not app_description: app_description = '' app = self._model.createFolder(parent=collection, name=name, description=app_description, parentType='Collection', public=public, creator=creator) # Create the 'nightly' release which will be the default folder when uploading an extension self._model.createFolder( parent=app, name=constants.NIGHTLY_RELEASE_NAME, description='Uploaded each night, always up-to-date', parentType='Folder', public=public, creator=creator) # Set a default template name for extensions in the application, # this can be changed in anytime. return self._model.setMetadata( app, { 'extensionNameTemplate': '{app_revision}_{os}_{arch}_{baseName}_{revision}' }) @autoDescribeRoute( Description('List existing application.').responseClass( 'Folder', array=True).param('app_id', 'The ID of the application.', required=False).param( 'collection_id', 'The ID of the collection.', required=False).param( 'name', 'The name of the application.', required=False). param('text', 'Provide text search of the application.', required=False).pagingParams( defaultSort='name').errorResponse().errorResponse( 'Read permission denied on the application.', 403)) @access.user(scope=TokenScope.DATA_READ) def listApp(self, app_id, collection_id, name, text, limit, offset, sort): """ List existing applications base on some optional parameters: :param app_id: Application ID :param collection_id: Collection ID :param name: Name of the application :param text: Provide text search on the name of the application :return: List of applications """ user = self.getCurrentUser() if ObjectId.is_valid(app_id): return self._model.load(app_id, user=user) else: if collection_id: parent = Collection().load(collection_id, user=user, level=AccessType.READ, exc=True) else: parent = Collection().findOne(query={'name': 'Applications'}, user=user, offset=offset) if parent: filters = {} if text: filters['$text'] = {'$search': text} if name: filters['name'] = name return list( self._model.childFolders(parentType='collection', parent=parent, user=user, offset=offset, limit=limit, sort=sort, filters=filters)) return [] @access.user(scope=TokenScope.DATA_WRITE) @autoDescribeRoute( Description('Delete an Application by ID.').modelParam( 'app_id', model=Folder, level=AccessType.ADMIN).param( 'progress', 'Whether to record progress on this task.', required=False, dataType='boolean', default=False).errorResponse('ID was invalid.').errorResponse( 'Admin access was denied for the application.', 403)) def deleteApp(self, folder, progress): """ Delete the application by ID. :param id: Id of the application :return: Confirmation message with the deleted application name """ return _deleteFolder(folder, progress, self.getCurrentUser()) @autoDescribeRoute( Description('Create a new release.').responseClass('Folder').notes( 'The application\'s revision is stored as metadata of the new release.' ).param('name', 'The release\'s name.').param( 'app_id', 'The application\'s ID which contain the release').param( 'app_revision', 'The application\'s revision which correspond to the release' ).param('description', 'The application\'s description.', required=False).param( 'public', 'Whether the release should be publicly visible.', required=False, dataType='boolean', default=True).errorResponse()) @access.user(scope=TokenScope.DATA_WRITE) def createNewRelease(self, name, app_id, app_revision, description, public): """ Create a new release with the ``name`` within the application. The ``app_revision`` will permit to automatically choose this release when uploading an extension with a matching `app_revision`` metadata. :param name: Name of the new release :param app_id: Application ID :param app_revision: Revision of the application corresponding to this release :param description: Description of the new release :param public: Whether the release should be publicly visible :return: The new release folder """ creator = self.getCurrentUser() application = self._model.load(app_id, user=creator) if not description: description = '' release = self._model.createFolder(parent=application, name=name, description=description, parentType='Folder', public=public, creator=creator) return self._model.setMetadata(release, {'revision': app_revision}) @autoDescribeRoute( Description('Get all the releases from an application.').responseClass( 'Folder').param('app_id', 'The application\'s ID.').pagingParams( defaultSort='name').errorResponse( 'ID was invalid.').errorResponse( 'Read permission denied on the application.', 403)) @access.user(scope=TokenScope.DATA_READ) def getAllReleases(self, app_id, limit, offset, sort): """ Get a list of all the release of an application. :param app_id: Application ID :return: List of all release within the application """ user = self.getCurrentUser() application = self._model.load(app_id, user=user) # It returns the nightly release too return list( self._model.childFolders(application, 'Folder', user=user, limit=limit, offset=offset, sort=sort)) @autoDescribeRoute( Description( 'Get a particular releases by ID or name from an application.'). responseClass('Folder').param( 'app_id', 'The application\'s ID.').param( 'release_id_or_name', 'The release\'s ID or name.').errorResponse( 'ID or name was invalid.').errorResponse( 'Read permission denied on the application.', 403)) @access.user(scope=TokenScope.DATA_READ) def getReleaseByIdOrName(self, app_id, release_id_or_name): """ Get the release folder by ID or by name. :param app_id: Application ID :param release_id_or_name: Could be either the release ID or the release name :return: The release folder """ user = self.getCurrentUser() application = self._model.load(app_id, user=user) if ObjectId.is_valid(release_id_or_name): return self._model.load(release_id_or_name, user=user) release_folder = list( self._model.childFolders( application, 'Folder', filters={'lowerName': release_id_or_name.lower()})) if not release_folder: return None return release_folder[0] @access.user(scope=TokenScope.DATA_WRITE) @autoDescribeRoute( Description('Delete a release by ID or name.').modelParam( 'app_id', model=Folder, level=AccessType.ADMIN).param('release_id_or_name', 'The release\'s ID or name.'). param('progress', 'Whether to record progress on this task.', required=False, dataType='boolean', default=False).errorResponse('ID was invalid.').errorResponse( 'Admin access was denied for the release.', 403)) def deleteReleaseByIdOrName(self, folder, release_id_or_name, progress): """ Delete a release by ID or name. :param app_id: Application ID :param release_id_or_name: Could be either the release ID or the release name :param progress: Whether to record progress on this task :return: Confirmation message with the deleted release name """ user = self.getCurrentUser() if ObjectId.is_valid(release_id_or_name): release = self._model.load(release_id_or_name, user=user) else: release_folder = list( self._model.childFolders( folder, 'Folder', filters={'lowerName': release_id_or_name.lower()})) if not release_folder: raise Exception("Couldn't find release %s" % release_id_or_name) release = release_folder[0] return _deleteFolder(release, progress, self.getCurrentUser()) @autoDescribeRoute( Description('List or search available extensions.').responseClass( 'Extension').param('app_id', 'The ID of the application.').param( 'release_id', 'The release id.', required=False).param( 'extension_id', 'The extension id.', required=False).param( 'os', 'The target operating system of the package.', required=False, enum=['linux', 'win', 'macosx']).param('arch', 'The os chip architecture.', required=False, enum=['i386', 'amd64']). param('app_revision', 'The revision of the package.', required=False).param( 'search', 'Text matched against extension name or description.', required=False).pagingParams( defaultSort='created').errorResponse()) @access.cookie @access.public def getExtensions(self, app_id, release_id, extension_id, os, arch, app_revision, search, limit, offset, sort): """ Get a list of extension which is filtered by some optional parameters: :param app_id: Application ID :param release_id: Release ID :param extension_id: Extension ID :param os: The operation system used for the extension. :param arch: The architecture compatible with the extension. :param app_revision: The revision of the application :param search: Text search on the name of the extension :return: The list of extensions """ filters = { '$and': [{ 'meta.app_id': { '$eq': app_id } }, { 'meta.os': { '$exists': True } }, { 'meta.arch': { '$exists': True } }, { 'meta.revision': { '$exists': True } }] } if ObjectId.is_valid(extension_id): filters['_id'] = ObjectId(extension_id) if ObjectId.is_valid(release_id): filters['folderId'] = ObjectId(release_id) if os: filters['meta.os'] = os if arch: filters['meta.arch'] = arch if app_revision: filters['meta.revision'] = app_revision if search: # Provide a full text search on baseName filters['meta.baseName'] = search return list(ExtensionModel().find(query=filters, limit=limit, offset=offset, sort=sort)) @autoDescribeRoute( Description('Get a particular extension by name from an application.'). responseClass('Item').param('app_id', 'The application\'s ID.').param( 'extension_name', 'The extension\'s name.').errorResponse( 'ID or name was invalid.').errorResponse( 'Read permission denied on the application.', 403)) @access.user(scope=TokenScope.DATA_READ) def getExtensionByName(self, app_id, extension_name): """ Get the extension item by name. :param app_id: Application ID :param extension_name: The extension name :return: The extension item """ user = self.getCurrentUser() application = self._model.load(app_id, user=user) release_folder = list(self._model.childFolders(application, 'Folder')) if not release_folder: raise Exception('The application has no release') for release in release_folder: extensions = list( self._model.childItems( release, filters={'lowerName': extension_name.lower()})) if extensions: return extensions[0] return None @autoDescribeRoute( # noqa: C901 Description('Create or Update an extension package.').param( 'app_id', 'The ID of the App.').param( 'os', 'The target operating system of the package.', enum=['linux', 'win', 'macosx']).param('arch', 'The os chip architecture.', enum=['i386', 'amd64']). param('baseName', 'The baseName of the package (ie installer baseName).').param( 'repository_type', 'The type of the repository (svn, git).').param( 'repository_url', 'The url of the repository.').param( 'revision', 'The svn or git revision of the extension.').param( 'app_revision', 'The revision of the application ' 'that the extension was built against.').param( 'packagetype', 'Installer, data, etc.').param( 'codebase', 'The codebase baseName (Ex: Slicer4).'). param('description', 'Text describing the extension.').param( 'release', 'Release identifier (Ex: 0.0.1, 0.0.2, 0.1).', required=False).param( 'icon_url', 'The url of the icon for the extension.', required=False).param( 'development_status', 'Arbitrary description of the status of the extension ' '(stable, active, etc).', required=False). param( 'category', 'Category under which to place the extension. Subcategories should be ' 'delimited by character. If none is passed, will render under ' 'the Miscellaneous category..', required=False ).param( 'enabled', 'Boolean indicating if the extension should be automatically enabled ' 'after its installation.', required=False).param('homepage', 'The url of the extension homepage.', required=False). param('screenshots', 'Space-separate list of URLs of screenshots for the extension.', required=False).param('contributors', 'List of contributors of the extension', required=False).errorResponse()) @access.cookie @access.public def createOrUpdateExtension(self, app_id, os, arch, baseName, repository_type, repository_url, revision, app_revision, packagetype, codebase, description, release, icon_url, development_status, category, enabled, homepage, screenshots, contributors): """ Upload an extension package in the database, in a specific release with providing ``release_id``. Or by default in the **'Nightly'** folder. :param app_id: The ID of the application. :param os: The operation system used for the extension. :param arch: The architecture compatible with the extension. :param baseName: The base name of the extension. :param repository_type: The type of repository (github, gitlab, ...). :param repository_url: The Url of the repository. :param revision: The revision of the extension. :param app_revision: The revision of the application. :param packagetype: Type of the extension. :param codebase: The codebase baseName. :param description: The description of the extension. :return: The status of the upload. """ creator = self.getCurrentUser() application = self._model.load(app_id, user=creator) release_folder = None # Find the release by metadata revision releases = self._model.childFolders(application, 'Folder', user=creator) for folder in releases: if 'meta' in folder: if folder['meta']['revision'] == app_revision: release_folder = folder break if not release_folder: # Only the nightly folder in the list release_folder = list( self._model.childFolders( application, 'Folder', user=creator, filters={'name': constants.NIGHTLY_RELEASE_NAME})) if not release_folder: raise Exception('The %s folder not found.' % constants.NIGHTLY_RELEASE_NAME) release_folder = release_folder[0] params = { 'app_id': app_id, 'baseName': baseName, 'os': os, 'arch': arch, 'repository_type': repository_type, 'repository_url': repository_url, 'revision': revision, 'app_revision': app_revision, 'packagetype': packagetype, 'codebase': codebase, 'description': description } if release: params['release'] = release if icon_url: params['icon_url'] = icon_url if development_status: params['development_status'] = development_status if category: params['category'] = category if enabled: params['enabled'] = enabled if homepage: params['homepage'] = homepage if screenshots: params['screenshots'] = screenshots if contributors: params['contributors'] = contributors name = application['meta']['extensionNameTemplate'].format(**params) filters = {'name': name} # Only one extensions should be in this list extensions = list(ExtensionModel().get(release_folder, filters=filters)) if not len(extensions): # The extension doesn't exist yet: extension = ExtensionModel().createExtension( name, creator, release_folder, params) elif len(extensions) == 1: extension = extensions[0] else: raise Exception( 'Too many extensions found for the same name :"%s"' % name) # Check the file inside the extension Item files = Item().childFiles(extension) if files.count() == 1: old_file = files.next() # catch the event of upload success and remove the file events.bind('model.file.finalizeUpload.after', 'application', File().remove(old_file)) elif not files.count(): # Extension new or empty pass else: raise Exception("More than 1 binary file in the extension.") old_meta = { 'baseName': extension['meta']['baseName'], 'os': extension['meta']['os'], 'arch': extension['meta']['arch'], 'revision': extension['meta']['revision'], 'app_revision': extension['meta']['app_revision'] } identifier_meta = { 'baseName': baseName, 'os': os, 'arch': arch, 'revision': revision, 'app_revision': app_revision } if identifier_meta == old_meta and len(extensions): # The revision is the same than these before, no need to upload extension = ExtensionModel().setMetadata(extension, params) events.unbind('model.file.finalizeUpload.after', 'application') # Ready to upload the binary file return extension @access.user(scope=TokenScope.DATA_WRITE) @autoDescribeRoute( Description('Delete an Extension by ID.').param( 'app_id', 'The ID of the App.').modelParam( 'ext_id', model=Item, level=AccessType.WRITE).errorResponse( 'ID was invalid.').errorResponse( 'Admin access was denied for the extension.', 403)) def deleteExtension(self, app_id, item): """ Delete the extension by ID. :param app_id: Application ID :param ext_id: Extension ID :return: Confirmation message with the name of the deleted extension """ Item().remove(item) return {'message': 'Deleted extension %s.' % item['name']}