def _task_response(self, task, timeout_secs): task_completed = False try: # Wait for task to complete task = task_engine.wait_for_task(task.id, timeout_secs) if task is None: # Someone else deleted it? Shouldn't normally get here. raise TimeoutError() task_completed = True # Return the updated folder (or raise the exception) if isinstance(task.result, Exception): raise task.result return make_api_success_response(object_to_dict(task.result)) except TimeoutError: # Return a 202 "task ongoing" response task_dict = object_to_dict(task) if task is not None else None # Decode the params before returning if task_dict and task_dict.get('params'): task_dict['params'] = pickle.loads(task_dict['params']) return make_api_success_response(task_dict, task_accepted=True) finally: if task and task_completed: try: # Delete the task so another API call can be made immediately data_engine.delete_object(task) except Exception: pass
def post(self): params = self._get_validated_object_parameters(request.form) db_session = data_engine.db_get_session() db_commit = False try: db_group = data_engine.get_group(params['group_id'], _db_session=db_session) if db_group is None: raise DoesNotExistError(str(params['group_id'])) db_folder = data_engine.get_folder(params['folder_id'], _db_session=db_session) if db_folder is None: raise DoesNotExistError(str(params['folder_id'])) # This commits (needed for refresh to get the new ID) fp = FolderPermission(db_folder, db_group, params['access']) fp = data_engine.save_object( fp, refresh=True, _db_session=db_session, _commit=True ) db_commit = True return make_api_success_response(object_to_dict(fp)) finally: try: if db_commit: db_session.commit() permissions_engine.reset() else: db_session.rollback() finally: db_session.close()
def imagedetails(): # Get/check parameters try: src = request.args.get('src', '') validate_string(src, 1, 1024) except ValueError as e: raise ParameterError(e) # v2.6.4 Don't allow this call to populate the database with unsupported files supported_file = ( get_file_extension(src) in image_engine.get_image_formats(supported_only=True) ) if not supported_file and path_exists(src, require_file=True): raise ImageError('The file is not a supported image format') # Get the image database entry db_image = auto_sync_file(src, data_engine, task_engine) if not db_image or db_image.status == Image.STATUS_DELETED: raise DoesNotExistError(src) # Require view permission or file admin permissions_engine.ensure_folder_permitted( db_image.folder, FolderPermission.ACCESS_VIEW, get_session_user() ) return make_api_success_response(object_to_dict( _prep_image_object(db_image), _omit_fields ))
def post(self, function_name): """ Launches a system task """ # Validate function name if getattr(tasks, function_name, None) is None: raise DoesNotExistError(function_name) # Requires super user permissions_engine.ensure_permitted(SystemPermissions.PERMIT_SUPER_USER, get_session_user()) # API parameters depend on the function params = self._get_validated_parameters(function_name, request.form) # Set remaining parameters for the task (description, task_params, priority, log_level, error_log_level, keep_secs) = self._get_task_data( function_name, params ) # Queue the task db_task = task_engine.add_task( get_session_user(), description, function_name, task_params, priority, log_level, error_log_level, keep_secs ) if db_task is None: raise AlreadyExistsError("Task is already running") # Decode the params before returning db_task.params = cPickle.loads(db_task.params) tdict = object_to_dict(db_task) if tdict.get("user") is not None: # Do not give out anything password related del tdict["user"]["password"] return make_api_success_response(tdict)
def get(self, folder_id=None): """ Gets a folder by path or ID, returning 1 level of children (sub-folders) """ if folder_id is None: # Get folder from path, using auto_sync to pick up new and deleted disk folders path = self._get_validated_path_arg(request) db_folder = auto_sync_folder(path, data_engine, task_engine) if db_folder is None: raise DoesNotExistError(path) else: # Get folder from ID db_folder = data_engine.get_folder(folder_id) if db_folder is None: raise DoesNotExistError(str(folder_id)) # View permission is required (ignoring view permission on parent+children) permissions_engine.ensure_folder_permitted( db_folder, FolderPermission.ACCESS_VIEW, get_session_user()) # Get the folder again, this time with parent and children # (children possibly faked - see the get_folder() docs - which is why # we can't use db_folder mk2 normally, only serialize it and exit) status_filter = self._get_validated_status_arg(request) db_folder = data_engine.get_folder(db_folder.id, load_parent=True, load_children=True, children_status=status_filter) if db_folder is None: raise DoesNotExistError(str(folder_id)) return make_api_success_response(object_to_dict(db_folder))
def post(self): # Require folios or admin_folios permission to create a portfolio permissions_engine.ensure_permitted(SystemPermissions.PERMIT_FOLIOS, get_session_user()) db_session = data_engine.db_get_session() try: params = self._get_validated_object_parameters(request.form) folio = Folio(params['human_id'] or Folio.create_human_id(), params['name'], params['description'], get_session_user()) self._set_permissions(folio, params, db_session) data_engine.create_portfolio( folio, get_session_user(), _db_session=db_session, _commit=True # fail here if human_id not unique ) # Return a clean object the same as for get(id) folio = data_engine.get_portfolio(folio.id, load_images=True, load_history=True) folio = _prep_folio_object(folio) return make_api_success_response( object_to_dict(folio, _omit_fields)) finally: db_session.close()
def get(self, folio_id, image_id=None): if image_id is None: # List images in the portfolio folio = data_engine.get_portfolio(folio_id, load_images=True) if folio is None: raise DoesNotExistError(str(folio_id)) # Check permissions permissions_engine.ensure_portfolio_permitted( folio, FolioPermission.ACCESS_VIEW, get_session_user()) image_list = [_prep_folioimage_object(fi) for fi in folio.images] return make_api_success_response( object_to_dict_list(image_list, _omit_fields)) else: # Get a single portfolio-image db_session = data_engine.db_get_session() try: folio_image = data_engine.get_portfolio_image( AttrObject(id=folio_id), AttrObject(id=image_id), _db_session=db_session) if folio_image is None: raise DoesNotExistError( str(folio_id) + '/' + str(image_id)) # Check permissions permissions_engine.ensure_portfolio_permitted( folio_image.portfolio, FolioPermission.ACCESS_VIEW, get_session_user()) return make_api_success_response( object_to_dict(_prep_folioimage_object(folio_image), _omit_fields + ['portfolio'])) finally: db_session.close()
def get(self, folio_id, export_id=None): # Get the portfolio folio = data_engine.get_portfolio(folio_id) if folio is None: raise DoesNotExistError(str(folio_id)) # Check permissions permissions_engine.ensure_portfolio_permitted( folio, FolioPermission.ACCESS_VIEW, get_session_user()) if export_id is None: # List portfolio exports exports_list = [ _prep_folioexport_object(folio, fe) for fe in folio.downloads ] return make_api_success_response( object_to_dict_list(exports_list, _omit_fields)) else: # Get a single portfolio-export folio_export = data_engine.get_object(FolioExport, export_id) if folio_export is None: raise DoesNotExistError(str(export_id)) if folio_export.folio_id != folio_id: raise ParameterError( 'export ID %d does not belong to portfolio ID %d' % (export_id, folio_id)) return make_api_success_response( object_to_dict(_prep_folioexport_object(folio, folio_export), _omit_fields))
def post(self): params = self._get_validated_object_parameters(request.form) db_session = data_engine.db_get_session() db_commit = False try: db_group = data_engine.get_group(params['group_id'], _db_session=db_session) if db_group is None: raise DoesNotExistError(str(params['group_id'])) db_folder = data_engine.get_folder(params['folder_id'], _db_session=db_session) if db_folder is None: raise DoesNotExistError(str(params['folder_id'])) # This commits (needed for refresh to get the new ID) fp = FolderPermission(db_folder, db_group, params['access']) fp = data_engine.save_object(fp, refresh=True, _db_session=db_session, _commit=True) db_commit = True return make_api_success_response(object_to_dict(fp)) finally: try: if db_commit: db_session.commit() permissions_engine.reset_folder_permissions() else: db_session.rollback() finally: db_session.close()
def put(self, permission_id): params = self._get_validated_object_parameters(request.form) fp = data_engine.get_object(FolderPermission, permission_id) if fp is None: raise DoesNotExistError(str(permission_id)) fp.access = params['access'] data_engine.save_object(fp) permissions_engine.reset() return make_api_success_response(object_to_dict(fp))
def put(self, permission_id): params = self._get_validated_object_parameters(request.form) fp = data_engine.get_object(FolderPermission, permission_id) if fp is None: raise DoesNotExistError(str(permission_id)) fp.access = params['access'] data_engine.save_object(fp) permissions_engine.reset_folder_permissions() return make_api_success_response(object_to_dict(fp))
def put(self, property_id): params = self._get_validated_object_parameters(request.form) db_prop = data_engine.get_object(Property, property_id) if db_prop is None: raise DoesNotExistError(str(property_id)) db_prop.value = params['value'] data_engine.save_object(db_prop) if property_id == Property.DEFAULT_TEMPLATE: image_engine.reset_templates() return make_api_success_response(object_to_dict(db_prop))
def get(self, image_id): db_img = data_engine.get_image(image_id=image_id) if not db_img: raise DoesNotExistError(str(image_id)) else: # Require view permission or file admin permissions_engine.ensure_folder_permitted( db_img.folder, FolderPermission.ACCESS_VIEW, get_session_user()) return make_api_success_response( object_to_dict(_prep_image_object(db_img)))
def get(self, permission_id=None): if permission_id is None: # List all permissions fp_list = data_engine.list_objects(FolderPermission) return make_api_success_response(object_to_dict_list(fp_list)) else: # Get permission entry fp = data_engine.get_object(FolderPermission, permission_id) if fp is None: raise DoesNotExistError(str(permission_id)) return make_api_success_response(object_to_dict(fp))
def put(self, image_id): params = self._get_validated_object_parameters(request.form) # Get image and update it db_img = data_engine.get_image(image_id=image_id) if not db_img: raise DoesNotExistError(str(image_id)) # Require edit permission or file admin permissions_engine.ensure_folder_permitted( db_img.folder, FolderPermission.ACCESS_EDIT, get_session_user() ) old_title = db_img.title old_description = db_img.description db_img.title = params['title'] db_img.description = params['description'] data_engine.save_object(db_img) # Get text changes. Max info length = # 100 + 200 + len('()' + '()' + 'Title: ' + ' / ' + 'Description: ') ==> 327 title_diff = get_string_changes(old_title, params['title'], char_limit=100).strip() if not title_diff: # Try for deletions from title title_diff = get_string_changes(params['title'], old_title, char_limit=100).strip() if title_diff: title_diff = '(' + title_diff + ')' desc_diff = get_string_changes( old_description, params['description'], char_limit=200 ).strip() if not desc_diff: # Try for deletions from description desc_diff = get_string_changes( params['description'], old_description, char_limit=200 ).strip() if desc_diff: desc_diff = '(' + desc_diff + ')' info = '' if title_diff: info += 'Title: ' + title_diff if info and desc_diff: info += ' / ' if desc_diff: info += 'Description: ' + desc_diff # Add change history data_engine.add_image_history( db_img, get_session_user(), ImageHistory.ACTION_EDITED, info ) return make_api_success_response(object_to_dict(db_img))
def get(self, image_id): db_img = data_engine.get_image(image_id=image_id) if not db_img: raise DoesNotExistError(str(image_id)) else: # Require view permission or file admin permissions_engine.ensure_folder_permitted( db_img.folder, FolderPermission.ACCESS_VIEW, get_session_user() ) return make_api_success_response(object_to_dict(db_img))
def get(self, group_id=None): if group_id is None: # List groups return make_api_success_response( object_to_dict_list(data_engine.list_objects( Group, Group.name))) else: # Get single group group = data_engine.get_group(group_id=group_id, load_users=True) if group is None: raise DoesNotExistError(str(group_id)) return make_api_success_response(object_to_dict(group))
def post(self): params = self._get_validated_object_parameters(request.form, True) # Do not use the password if this is an LDAP user if params['auth_type'] == User.AUTH_TYPE_LDAP: params['password'] = generate_password() # Create the new user user = User(params['first_name'], params['last_name'], params['email'], params['username'], params['password'], params['auth_type'], params['allow_api'], User.STATUS_ACTIVE) data_engine.create_user(user) return make_api_success_response(object_to_dict(user))
def put(self, image_id): params = self._get_validated_object_parameters(request.form) # Get image and update it db_img = data_engine.get_image(image_id=image_id) if not db_img: raise DoesNotExistError(str(image_id)) # Require edit permission or file admin permissions_engine.ensure_folder_permitted( db_img.folder, FolderPermission.ACCESS_EDIT, get_session_user()) old_title = db_img.title old_description = db_img.description db_img.title = params['title'] db_img.description = params['description'] data_engine.save_object(db_img) # Get text changes. Max info length = # 100 + 200 + len('()' + '()' + 'Title: ' + ' / ' + 'Description: ') ==> 327 title_diff = get_string_changes(old_title, params['title'], char_limit=100).strip() if not title_diff: # Try for deletions from title title_diff = get_string_changes(params['title'], old_title, char_limit=100).strip() if title_diff: title_diff = '(' + title_diff + ')' desc_diff = get_string_changes(old_description, params['description'], char_limit=200).strip() if not desc_diff: # Try for deletions from description desc_diff = get_string_changes(params['description'], old_description, char_limit=200).strip() if desc_diff: desc_diff = '(' + desc_diff + ')' info = '' if title_diff: info += 'Title: ' + title_diff if info and desc_diff: info += ' / ' if desc_diff: info += 'Description: ' + desc_diff # Add change history data_engine.add_image_history(db_img, get_session_user(), ImageHistory.ACTION_EDITED, info) return make_api_success_response( object_to_dict(_prep_image_object(db_img)))
def get(self, task_id): db_task = task_engine.get_task(task_id=task_id, decode_attrs=True) if not db_task: raise DoesNotExistError(str(task_id)) else: # Requires super user or task owner if not db_task.user or db_task.user.id != get_session_user_id(): permissions_engine.ensure_permitted(SystemPermissions.PERMIT_SUPER_USER, get_session_user()) tdict = object_to_dict(db_task) if tdict.get("user") is not None: # Do not give out anything password related del tdict["user"]["password"] return make_api_success_response(tdict)
def post(self): # Check permissions! The current user must have permissions admin to create groups. permissions_engine.ensure_permitted( SystemPermissions.PERMIT_ADMIN_PERMISSIONS, get_session_user()) params = self._get_validated_object_parameters(request.form) if params['group_type'] == Group.GROUP_TYPE_SYSTEM: raise ParameterError('System groups cannot be created') group = Group(params['name'], params['description'], params['group_type']) group.users = [] self._set_permissions(group, params) data_engine.create_group(group) return make_api_success_response(object_to_dict(group))
def delete(self, user_id): user = data_engine.get_user(user_id=user_id) if user is None: raise DoesNotExistError(str(user_id)) if user.id == 1: raise ParameterError('The \'admin\' user cannot be deleted') data_engine.delete_user(user) # If this is the current user, log out if get_session_user_id() == user_id: log_out() # Reset session caches reset_user_sessions(user) return make_api_success_response(object_to_dict(user))
def get(self, user_id=None): if user_id is None: # List users status_filter = self._get_validated_status_arg(request) ulist = data_engine.list_users(status=status_filter, order_field=User.username) return make_api_success_response(object_to_dict_list(ulist)) else: # Get single user user = data_engine.get_user(user_id) if user is None: raise DoesNotExistError(str(user_id)) return make_api_success_response(object_to_dict(user))
def post(self, folio_id): db_session = data_engine.db_get_session() try: # Get the portfolio folio = data_engine.get_portfolio(folio_id, _db_session=db_session) if folio is None: raise DoesNotExistError(str(folio_id)) # Check portfolio permissions permissions_engine.ensure_portfolio_permitted( folio, FolioPermission.ACCESS_EDIT, get_session_user()) # Get the image by either ID or src params = self._get_validated_object_parameters(request.form, True) if 'image_id' in params: image = data_engine.get_image(params['image_id'], _db_session=db_session) if image is None: raise DoesNotExistError(str(params['image_id'])) else: image = auto_sync_file(params['image_src'], data_engine, task_engine, anon_history=True, burst_pdf=False, _db_session=db_session) if image is None or image.status == Image.STATUS_DELETED: raise DoesNotExistError(params['image_src']) # Check image permissions permissions_engine.ensure_folder_permitted( image.folder, FolderPermission.ACCESS_VIEW, get_session_user(), False) # Add history first so that we only commit once at the end data_engine.add_portfolio_history(folio, get_session_user(), FolioHistory.ACTION_IMAGE_CHANGE, '%s added' % image.src, _db_session=db_session, _commit=False) # Flag that exported zips are now out of date folio.last_updated = datetime.utcnow() # Add the image and commit changes db_folio_image = data_engine.save_object(FolioImage( folio, image, params['image_parameters'], params['filename'], params['index']), refresh=True, _db_session=db_session, _commit=True) return make_api_success_response( object_to_dict(_prep_folioimage_object(db_folio_image), _omit_fields + ['portfolio'])) finally: db_session.close()
def delete(self, user_id): user = data_engine.get_user(user_id=user_id) if user is None: raise DoesNotExistError(str(user_id)) if user.id == 1: raise ParameterError('The \'admin\' user cannot be deleted') data_engine.delete_user(user) # If this is the current user, log out if get_session_user_id() == user_id: log_out() # Do not give out anything password related udict = object_to_dict(user) del udict['password'] return make_api_success_response(udict)
def post(self): """ Creates a disk folder """ params = self._get_validated_parameters(request.form) try: db_folder = create_folder(params['path'], get_session_user(), data_engine, permissions_engine, logger) # Return a "fresh" object (without relationships loaded) to match PUT, DELETE db_folder = data_engine.get_folder(db_folder.id) return make_api_success_response(object_to_dict(db_folder)) except ValueError as e: if type(e) is ValueError: raise ParameterError(str(e)) else: raise # Sub-classes of ValueError
def put(self, folio_id): db_session = data_engine.db_get_session() try: # Get portfolio folio = data_engine.get_portfolio(folio_id, _db_session=db_session) if folio is None: raise DoesNotExistError(str(folio_id)) # Check permissions permissions_engine.ensure_portfolio_permitted( folio, FolioPermission.ACCESS_EDIT, get_session_user()) # Update the object params = self._get_validated_object_parameters(request.form) permissions_changed = self._set_permissions( folio, params, db_session) changes = [] if params['human_id'] != folio.human_id: changes.append('short URL changed') if params['name'] != folio.name: changes.append('name changed') if params['description'] != folio.description: changes.append('description changed') if permissions_changed: changes.append('permissions changed') folio.human_id = params['human_id'] or Folio.create_human_id() folio.name = params['name'] folio.description = params['description'] # Note: folio.last_updated is only for image changes # (to know when to invalidate the exported zips) data_engine.add_portfolio_history(folio, get_session_user(), FolioHistory.ACTION_EDITED, ', '.join(changes).capitalize(), _db_session=db_session, _commit=False) data_engine.save_object( folio, _db_session=db_session, _commit=True # fail here if human_id not unique ) if permissions_changed: permissions_engine.reset_portfolio_permissions() # Return a clean object the same as for get(id) folio = data_engine.get_portfolio(folio.id, load_images=True, load_history=True) folio = _prep_folio_object(folio) return make_api_success_response( object_to_dict(folio, _omit_fields)) finally: db_session.close()
def put(self, folio_id, image_id): db_session = data_engine.db_get_session() try: folio_image = data_engine.get_portfolio_image( AttrObject(id=folio_id), AttrObject(id=image_id), _db_session=db_session) if folio_image is None: raise DoesNotExistError(str(folio_id) + '/' + str(image_id)) # Check permissions permissions_engine.ensure_portfolio_permitted( folio_image.portfolio, FolioPermission.ACCESS_EDIT, get_session_user()) # Update the object with any/all parameters that were passed in params = self._get_validated_object_parameters(request.form, False) changes = [] affects_zips = False if (params['image_parameters'] is not None and params['image_parameters'] != folio_image.parameters): folio_image.parameters = params['image_parameters'] changes.append('image attributes changed') affects_zips = True if (params['filename'] is not None and params['filename'] != folio_image.filename): folio_image.filename = params['filename'] changes.append('filename changed') affects_zips = True if (params['index'] is not None and params['index'] != folio_image.order_num): folio_image.order_num = params['index'] changes.append('set as position %d' % (params['index'] + 1)) if changes: # Flag if exported zips will be out of date if affects_zips: folio_image.portfolio.last_updated = datetime.utcnow() # Add history and commit changes data_engine.add_portfolio_history( folio_image.portfolio, get_session_user(), FolioHistory.ACTION_IMAGE_CHANGE, '%s updated: %s' % (folio_image.image.src, ', '.join(changes)), _db_session=db_session, _commit=True) return make_api_success_response( object_to_dict(_prep_folioimage_object(folio_image), _omit_fields + ['portfolio'])) finally: db_session.close()
def get(self, group_id=None): if group_id is None: # List groups return make_api_success_response( object_to_dict_list(data_engine.list_objects(Group, Group.name)) ) else: # Get single group group = data_engine.get_group(group_id=group_id, load_users=True) if group is None: raise DoesNotExistError(str(group_id)) # Do not give out anything password related gdict = object_to_dict(group) for udict in gdict['users']: del udict['password'] return make_api_success_response(gdict)
def get(self, template_id=None): if template_id is None: # List templates tlist = data_engine.list_objects(ImageTemplate, ImageTemplate.name) tdictlist = object_to_dict_list(tlist) for tdict in tdictlist: self._del_keys(tdict['template'], TemplateAPI.HIDE_FIELDS) return make_api_success_response(tdictlist) else: # Get single template template_info = data_engine.get_image_template(template_id) if template_info is None: raise DoesNotExistError(str(template_id)) tdict = object_to_dict(template_info) self._del_keys(tdict['template'], TemplateAPI.HIDE_FIELDS) return make_api_success_response(tdict)
def post(self, folio_id): db_session = data_engine.db_get_session() try: # Get the portfolio folio = data_engine.get_portfolio(folio_id, _db_session=db_session) if folio is None: raise DoesNotExistError(str(folio_id)) # Check permissions permissions_engine.ensure_portfolio_permitted( folio, FolioPermission.ACCESS_EDIT, get_session_user()) # Block the export now if it would create an empty zip file if len(folio.images) == 0: raise ParameterError( 'this portfolio is empty and cannot be published') # Create a folio-export record and start the export as a background task params = self._get_validated_object_parameters(request.form) folio_export = FolioExport(folio, params['description'], params['originals'], params['image_parameters'], params['expiry_time']) data_engine.add_portfolio_history(folio, get_session_user(), FolioHistory.ACTION_PUBLISHED, folio_export.describe(True), _db_session=db_session, _commit=False) folio_export = data_engine.save_object(folio_export, refresh=True, _db_session=db_session, _commit=True) export_task = task_engine.add_task( get_session_user(), 'Export portfolio %d / export %d' % (folio.id, folio_export.id), 'export_portfolio', { 'export_id': folio_export.id, 'ignore_errors': False }, Task.PRIORITY_NORMAL, 'info', 'error', 60) # Update and return the folio-export record with the task ID folio_export.task_id = export_task.id data_engine.save_object(folio_export, _db_session=db_session, _commit=True) return make_api_success_response(object_to_dict( _prep_folioexport_object(folio, folio_export), _omit_fields + ['portfolio']), task_accepted=True) finally: db_session.close()
def post(self): # Check permissions! The current user must have permissions admin to create groups. permissions_engine.ensure_permitted( SystemPermissions.PERMIT_ADMIN_PERMISSIONS, get_session_user() ) params = self._get_validated_object_parameters(request.form) if params['group_type'] == Group.GROUP_TYPE_SYSTEM: raise ParameterError('System groups cannot be created') group = Group( params['name'], params['description'], params['group_type'] ) group.users = [] self._set_permissions(group, params) data_engine.create_group(group) return make_api_success_response(object_to_dict(group))
def get(self, user_id=None): if user_id is None: # List users ulist = data_engine.list_users(order_field=User.username) # Do not give out anything password related udictlist = object_to_dict_list(ulist) for user in udictlist: del user['password'] return make_api_success_response(udictlist) else: # Get single user user = data_engine.get_user(user_id) if user is None: raise DoesNotExistError(str(user_id)) # Do not give out anything password related udict = object_to_dict(user) del udict['password'] return make_api_success_response(udict)
def post(self): params = self._get_validated_object_parameters(request.form, True) # Do not use the password if this is an LDAP user if params['auth_type'] == User.AUTH_TYPE_LDAP: params['password'] = generate_password() # Create the new user user = User( params['first_name'], params['last_name'], params['email'], params['username'], params['password'], params['auth_type'], params['allow_api'], User.STATUS_ACTIVE ) data_engine.create_user(user) # Do not give out anything password related udict = object_to_dict(user) del udict['password'] return make_api_success_response(udict)
def put(self, image_id): """ Moves or renames a file on disk """ params = self._get_validated_parameters(request.form) # Get image data db_img = data_engine.get_image(image_id=image_id) if not db_img: raise DoesNotExistError(str(image_id)) # Move try: db_img = move_file(db_img, params['path'], get_session_user(), data_engine, permissions_engine) except ValueError as e: if type(e) is ValueError: raise ParameterError(str(e)) else: raise # Sub-classes of ValueError # Remove cached images for the old path image_engine._uncache_image_id(db_img.id) # Return updated image return make_api_success_response( object_to_dict(_prep_image_object(db_img)))
def delete(self, image_id): """ Deletes a file from disk """ # Get image data db_img = data_engine.get_image(image_id=image_id) if not db_img: raise DoesNotExistError(str(image_id)) # v4.1 #10 delete_file() doesn't care whether the file exists, but we # want the API to return a "not found" if the file doesn't exist # (and as long as the database is already in sync with that) if not path_exists( db_img.src, require_file=True) and db_img.status == Image.STATUS_DELETED: raise DoesNotExistError(db_img.src) # Delete db_img = delete_file(db_img, get_session_user(), data_engine, permissions_engine) # Remove cached images for old path image_engine._uncache_image_id(db_img.id) # Return updated image return make_api_success_response( object_to_dict(_prep_image_object(db_img)))
def put(self, user_id): params = self._get_validated_object_parameters(request.form, False) user = data_engine.get_user(user_id=user_id) if user is None: raise DoesNotExistError(str(user_id)) user.first_name = params['first_name'] user.last_name = params['last_name'] user.email = params['email'] user.auth_type = params['auth_type'] user.allow_api = params['allow_api'] # Don't update the status field with this method # Update username only if non-LDAP if user.auth_type != User.AUTH_TYPE_LDAP: user.username = params['username'] # Update password only if non-LDAP and a new one was passed in if user.auth_type != User.AUTH_TYPE_LDAP and params['password']: user.set_password(params['password']) data_engine.save_object(user) # Reset session caches reset_user_sessions(user) return make_api_success_response(object_to_dict(user))
def put(self, group_id): params = self._get_validated_object_parameters(request.form) group = data_engine.get_group(group_id=group_id, load_users=True) if group is None: raise DoesNotExistError(str(group_id)) # Back up the object in case we need to restore it backup_group = copy.deepcopy(group) # Update group group.description = params['description'] if group.group_type != Group.GROUP_TYPE_SYSTEM: group.group_type = params['group_type'] if group.group_type == Group.GROUP_TYPE_LOCAL: group.name = params['name'] permissions_changed = self._set_permissions(group, params) data_engine.save_object(group) # Reset permissions and session caches if permissions_changed: reset_user_sessions(group.users) permissions_engine.reset() _check_for_user_lockout(backup_group) return make_api_success_response(object_to_dict(group))
def put(self, user_id): params = self._get_validated_object_parameters(request.form, False) user = data_engine.get_user(user_id=user_id) if user is None: raise DoesNotExistError(str(user_id)) user.first_name = params['first_name'] user.last_name = params['last_name'] user.email = params['email'] user.auth_type = params['auth_type'] user.allow_api = params['allow_api'] # Don't update the status field with this method # Update username only if non-LDAP if user.auth_type != User.AUTH_TYPE_LDAP: user.username = params['username'] # Update password only if non-LDAP and a new one was passed in if user.auth_type != User.AUTH_TYPE_LDAP and params['password']: user.set_password(params['password']) data_engine.save_object(user) # Do not give out anything password related udict = object_to_dict(user) del udict['password'] return make_api_success_response(udict)
def get(self, folio_id=None): # v4.1 Added support for /api/portfolios/?human_id=<human id> human_id = request.args.get('human_id', '') if not folio_id and not human_id: # List portfolios that the user can view folio_list = data_engine.list_portfolios( get_session_user(), FolioPermission.ACCESS_VIEW) folio_list = [_prep_folio_object(f) for f in folio_list] return make_api_success_response( object_to_dict_list(folio_list, _omit_fields)) else: # Get single portfolio by either ID or v4.1 by human ID folio = data_engine.get_portfolio(folio_id, human_id, load_images=True, load_history=True) if folio is None: raise DoesNotExistError(human_id or str(folio_id)) # Check permissions permissions_engine.ensure_portfolio_permitted( folio, FolioPermission.ACCESS_VIEW, get_session_user()) folio = _prep_folio_object(folio) return make_api_success_response( object_to_dict(folio, _omit_fields))
def put(self, group_id): params = self._get_validated_object_parameters(request.form) group = data_engine.get_group(group_id=group_id, load_users=True) if group is None: raise DoesNotExistError(str(group_id)) # Back up the object in case we need to restore it backup_group = copy.deepcopy(group) # Update group group.description = params['description'] if group.group_type != Group.GROUP_TYPE_SYSTEM: group.group_type = params['group_type'] if group.group_type == Group.GROUP_TYPE_LOCAL: group.name = params['name'] permissions_changed = self._set_permissions(group, params) data_engine.save_object(group) # Reset permissions cache if permissions_changed: permissions_engine.reset() _check_for_user_lockout(backup_group) # Do not give out anything password related gdict = object_to_dict(group) for udict in gdict['users']: del udict['password'] return make_api_success_response(gdict)
def upload(): # Get URL parameters for the upload file_list = request.files.getlist('files') path_index = request.form.get('path_index', '-1') # Index into IMAGE_UPLOAD_DIRS or -1 path = request.form.get('path', '') # Manual path when path_index is -1 overwrite = request.form.get('overwrite') ret_dict = {} try: current_user = get_session_user() assert current_user is not None # Check params path_index = parse_int(path_index) if overwrite != 'rename': overwrite = parse_boolean(overwrite) validate_string(path, 0, 1024) if not path and path_index < 0: raise ValueError('Either path or path_index is required') if len(file_list) < 1: raise ValueError('No files have been attached') if path_index >= 0: # Get a "trusted" pre-defined upload folder # image_engine.put_image() will create it if it doesn't exist _, path = get_upload_directory(path_index) else: # A manually specified folder is "untrusted" and has to exist already if not path_exists(path): raise DoesNotExistError('Path \'' + path + '\' does not exist') # Loop over the upload files put_image_exception = None can_download = None saved_files = [] for wkfile in file_list: original_filepath = wkfile.filename original_filename = filepath_filename(original_filepath) # v2.7.1 added if original_filename: db_image = None try: # Don't allow filenames like "../../../etc/passwd" safe_filename = secure_filename( original_filename, app.config['ALLOW_UNICODE_FILENAMES'] ) # v2.7.1 If we already saved a file as safe_filename during this upload, # override this one to have overwrite=rename overwrite_flag = 'rename' if safe_filename in saved_files else overwrite # Save (this also checks user-folder permissions) _, db_image = image_engine.put_image( current_user, wkfile, path, safe_filename, overwrite_flag ) # v2.7.1 Keep a record of what filenames we used during this upload saved_files.append(safe_filename) except Exception as e: # Save the error to use as our overall return value if put_image_exception is None: put_image_exception = e # This loop failure, add the error info to our return data ret_dict[original_filepath] = { 'error': create_api_error_dict(e, logger) } if db_image: # Calculate download permission once (all files are going to same folder) if can_download is None: can_download = permissions_engine.is_folder_permitted( db_image.folder, FolderPermission.ACCESS_DOWNLOAD, get_session_user() ) # This loop success ret_dict[original_filepath] = object_to_dict( _prep_image_object(db_image, can_download), _omit_fields ) else: logger.warning('Upload received blank filename, ignoring file') # Loop complete. If we had an exception, raise it now. if put_image_exception is not None: raise put_image_exception except Exception as e: # put_image returns ValueError for parameter errors if type(e) is ValueError: e = ParameterError(str(e)) # Attach whatever data we have to return with the error # Caller can then decide whether to continue if some files worked e.api_data = ret_dict raise e finally: # Store the result for the upload_complete page cache_engine.raw_put( 'UPLOAD_API:' + str(current_user.id), ret_dict, expiry_secs=(60 * 60 * 24 * 7) ) # If here, all files were uploaded successfully return make_api_success_response(ret_dict)
def imagelist(): # Check parameters try: from_path = request.args.get('path', '') want_info = parse_boolean(request.args.get('attributes', '')) start = parse_int(request.args.get('start', '0')) limit = parse_int(request.args.get('limit', '1000')) validate_string(from_path, 1, 1024) validate_number(start, 0, 999999999) validate_number(limit, 1, 1000) except ValueError as e: raise ParameterError(e) # Get extra parameters for image URL construction, remove API parameters image_params = request.args.to_dict() image_params.pop('path', None) image_params.pop('attributes', None) image_params.pop('start', None) image_params.pop('limit', None) # Get directory listing directory_info = get_directory_listing(from_path, False, 2, start, limit) if not directory_info.exists(): raise DoesNotExistError('Invalid path') ret_list = [] db_session = data_engine.db_get_session() db_commit = False try: # Auto-populate the folders database db_folder = auto_sync_folder( from_path, data_engine, task_engine, _db_session=db_session ) db_session.commit() # Require view permission or file admin permissions_engine.ensure_folder_permitted( db_folder, FolderPermission.ACCESS_VIEW, get_session_user() ) # Get download permission in case we need to return it later can_download = permissions_engine.is_folder_permitted( db_folder, FolderPermission.ACCESS_DOWNLOAD, get_session_user() ) # Create the response file_list = directory_info.contents() supported_img_types = image_engine.get_image_formats(supported_only=True) base_folder = add_sep(directory_info.name()) for f in file_list: # v2.6.4 Return unsupported files too. If you want to reverse this change, # the filtering needs to be elsewhere for 'start' and 'limit' to work properly supported_file = get_file_extension(f['filename']) in supported_img_types file_path = base_folder + f['filename'] if want_info: # Need to return the database fields too if supported_file: db_entry = auto_sync_existing_file( file_path, data_engine, task_engine, burst_pdf=False, # Don't burst a PDF just by finding it here _db_session=db_session ) db_entry = _prep_image_object(db_entry, can_download, **image_params) else: db_entry = _prep_blank_image_object() db_entry.filename = f['filename'] db_entry.supported = False # Return images in full (standard) image dict format entry = object_to_dict(db_entry, _omit_fields) else: # Return images in short dict format entry = { 'filename': f['filename'], 'supported': supported_file, 'url': (external_url_for('image', src=file_path, **image_params) if supported_file else '') } ret_list.append(entry) db_commit = True finally: try: if db_commit: db_session.commit() else: db_session.rollback() finally: db_session.close() return make_api_success_response(ret_list)