Пример #1
0
 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()
Пример #2
0
 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)
Пример #3
0
 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()
Пример #4
0
 def delete(self, folio_id, export_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())
         # Get the single portfolio-export
         folio_export = data_engine.get_object(FolioExport,
                                               export_id,
                                               _db_session=db_session)
         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))
         # Delete it and the export files
         delete_portfolio_export(folio_export,
                                 get_session_user(),
                                 'Deleted: ' + folio_export.describe(True),
                                 _db_session=db_session)
         return make_api_success_response()
     finally:
         db_session.close()
Пример #5
0
 def delete(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())
         # Add history first so that we only commit once at the end
         folio = folio_image.portfolio
         data_engine.add_portfolio_history(folio,
                                           get_session_user(),
                                           FolioHistory.ACTION_IMAGE_CHANGE,
                                           '%s removed' %
                                           folio_image.image.src,
                                           _db_session=db_session,
                                           _commit=False)
         # Flag that exported zips will be out of date
         folio.last_updated = datetime.utcnow()
         # Delete the image from the portfolio and commit changes
         data_engine.delete_object(folio_image,
                                   _db_session=db_session,
                                   _commit=True)
         return make_api_success_response()
     finally:
         db_session.close()
Пример #6
0
    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))
Пример #7
0
    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)))
Пример #8
0
 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()
Пример #9
0
 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()
Пример #10
0
 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()
Пример #11
0
 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))
Пример #12
0
 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))
Пример #13
0
    def post(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))

        # Check permissions! The current user must have user admin to be here.
        # But if they don't also have permissions admin or superuser then we
        # must block the change if the new group would grant one of the same.
        if group.permissions.admin_permissions or group.permissions.admin_all:
            if not permissions_engine.is_permitted(
                SystemPermissions.PERMIT_ADMIN_PERMISSIONS, get_session_user()
            ):
                raise SecurityError(
                    'You cannot add users to a group that ' +
                    'grants permissions administration, because you do not ' +
                    'have permissions administration access yourself.'
                )

        user = data_engine.get_user(user_id=params['user_id'])
        if user is not None:
            if user not in group.users:
                group.users.append(user)
                data_engine.save_object(group)
                permissions_engine.reset()
        return make_api_success_response()
Пример #14
0
Файл: views.py Проект: quru/qis
def portfolio_download(human_id, filename):
    logger.debug('GET ' + request.url)
    try:
        # Find the portfolio
        folio = data_engine.get_portfolio(human_id=human_id)
        if not folio:
            raise DoesNotExistError('Portfolio \'%s\' does not exist' %
                                    human_id)

        # Ensure that the user has permission to download the portfolio
        user = get_session_user()
        permissions_engine.ensure_portfolio_permitted(
            folio, FolioPermission.ACCESS_DOWNLOAD, user)

        # Check that the filename is valid (note: assumes folio.downloads is eager loaded)
        if not filename:
            raise DoesNotExistError('No filename specified')
        folio_exports = [
            dl for dl in folio.downloads if dl.filename == filename
        ]
        if not folio_exports:
            raise DoesNotExistError('Download \'%s\' is not available' %
                                    filename)
        folio_export = folio_exports[0]
        # The physical file should always exist when the data+filename exists
        # This also checks that the file path lies inside IMAGES_BASE_DIR
        zip_path = get_portfolio_export_file_path(folio_export)
        ensure_path_exists(zip_path, require_file=True)

        # Prepare to serve the file
        response = send_file(
            get_abs_path(zip_path),
            mimetype='application/zip',
            as_attachment=True,
            conditional=True,
            cache_timeout=31536000  # zips never change once created
        )

        # Lastly write an audit record
        data_engine.add_portfolio_history(folio, user,
                                          FolioHistory.ACTION_DOWNLOADED,
                                          folio_export.filename)
        return response

    except httpexc.HTTPException:
        # Pass through HTTP 4xx and 5xx
        raise
    except SecurityError as e:
        if app.config['DEBUG']:
            raise
        log_security_error(e, request)
        raise httpexc.Forbidden()
    except DoesNotExistError as e:
        logger.warning('404 Not found: ' + str(e))
        raise httpexc.NotFound(safe_error_str(e))
    except Exception as e:
        if app.config['DEBUG']:
            raise
        logger.error('500 Error for ' + request.url + '\n' + str(e))
        raise httpexc.InternalServerError(safe_error_str(e))
Пример #15
0
    def post(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))

        # Check permissions! The current user must have user admin to be here.
        # But if they don't also have permissions admin or superuser then we
        # must block the change if the new group would grant one of the same.
        if group.permissions.admin_permissions or group.permissions.admin_all:
            if not permissions_engine.is_permitted(
                    SystemPermissions.PERMIT_ADMIN_PERMISSIONS,
                    get_session_user()):
                raise SecurityError(
                    'You cannot add users to a group that ' +
                    'grants permissions administration, because you do not ' +
                    'have permissions administration access yourself.')

        user = data_engine.get_user(user_id=params['user_id'])
        if user is not None:
            if user not in group.users:
                group.users.append(user)
                data_engine.save_object(group)
                reset_user_sessions(user)
                permissions_engine.reset()
        return make_api_success_response()
Пример #16
0
def _prep_image_object(image, can_download=None, **url_params):
    """
    Modifies an Image object to add calculated fields.
    This provides the common data dictionary for the file admin, image admin,
    image details, upload, and directory listing (with detail) APIs.

    If the download permission is None, it is calculated for the image's folder
    and the current user. The permissions engine returns this from cache when
    possible, but it is more efficient to pass in the permission if it is already
    known, or when handling many images in the same folder.

    If any url_params are provided (as kwargs), these are included in the
    generated image 'url' attribute.
    """
    if can_download is None:
        can_download = permissions_engine.is_folder_permitted(
            image.folder, FolderPermission.ACCESS_DOWNLOAD, get_session_user())
    image.url = external_url_for('image', src=image.src, **url_params)
    image.download = can_download
    image.filename = filepath_filename(image.src)
    # Unsupported files shouldn't be in the database but it can happen if
    # support is removed for a file type that was once enabled
    image.supported = (get_file_extension(image.filename)
                       in image_engine.get_image_formats(supported_only=True))
    return image
Пример #17
0
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
    ))
Пример #18
0
 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()
Пример #19
0
 def post(self):
     permissions_engine.ensure_permitted(
         SystemPermissions.PERMIT_SUPER_USER, get_session_user())
     params = self._get_validated_object_parameters(request.form)
     template = ImageTemplate(params['name'], params['description'],
                              params['template'])
     template = data_engine.save_object(template, refresh=True)
     image_engine.reset_templates()
     return self.get(template.id)
Пример #20
0
 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)))
Пример #21
0
def token_login():
    status = API_CODES.SUCCESS
    err_msg = ''
    token = request.args.get('token', '')
    next_url = request.args.get('next', '')
    try:
        if token:
            token_auth_class = app.config['API_AUTHENTICATION_CLASS']
            auth_cls = getattr(flask_ext, token_auth_class, None)
            if not auth_cls:
                raise ValueError('Class flask_ext.%s was not found' % token_auth_class)
            auth_module = auth_cls(app)
            auth_object = auth_module.decode_auth_token(token)
            if auth_object:
                # The token is valid - set as logged in on the API
                auth_module.set_authenticated(auth_object)
                # Now set as logged in on the web session too
                auth_user = session_manager.get_session_user()
                if not auth_user:
                    raise ValueError(
                        'Internal error - no session user returned - has BaseHttpAuthentication '
                        'or session_manager been changed?'
                    )
                session_manager.log_in(auth_user)
            else:
                status = API_CODES.UNAUTHORISED
                err_msg = 'Invalid or expired token'
        else:
            status = API_CODES.INVALID_PARAM
            err_msg = 'No token value supplied'

    except SecurityError as se:
        if app.config['DEBUG']:
            raise
        log_security_error(se, request)
        status = API_CODES.UNAUTHORISED
        err_msg = str(se)
    except Exception as e:
        if app.config['DEBUG']:
            raise
        logger.error('Error performing API token to web login: '******'Sorry, an error occurred. Please try again later.'
    finally:
        if status != API_CODES.SUCCESS:
            session_manager.log_out()

    if next_url and status == API_CODES.SUCCESS:
        return redirect(next_url)
    else:
        return render_template(
            'token_login.html',
            err_msg=safe_error_str(err_msg)
        ), status
Пример #22
0
 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))
Пример #23
0
 def put(self, template_id):
     permissions_engine.ensure_permitted(
         SystemPermissions.PERMIT_SUPER_USER, get_session_user())
     params = self._get_validated_object_parameters(request.form)
     template = data_engine.get_image_template(template_id)
     if template is None:
         raise DoesNotExistError(str(template_id))
     template.name = params['name']
     template.description = params['description']
     template.template = params['template']
     data_engine.save_object(template)
     image_engine.reset_templates()
     return self.get(template.id)
Пример #24
0
 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))
Пример #25
0
 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
Пример #26
0
 def put(self, folder_id):
     """ Moves or renames a disk folder """
     params = self._get_validated_parameters(request.form)
     # Run this as a background task in case it takes a long time
     task = task_engine.add_task(get_session_user(),
                                 'Move disk folder %d' % folder_id,
                                 'move_folder', {
                                     'folder_id': folder_id,
                                     'path': params['path']
                                 }, Task.PRIORITY_HIGH, 'info', 'error', 10)
     if task is None:  # Task already submitted
         return make_api_success_response(task_accepted=True)
     else:
         return self._task_response(task, 30)
Пример #27
0
 def delete(self, template_id):
     permissions_engine.ensure_permitted(
         SystemPermissions.PERMIT_SUPER_USER, get_session_user())
     template_info = data_engine.get_image_template(template_id)
     if template_info is None:
         raise DoesNotExistError(str(template_id))
     db_default_template = data_engine.get_object(Property,
                                                  Property.DEFAULT_TEMPLATE)
     if template_info.name.lower() == db_default_template.value.lower():
         raise ParameterError(
             'The system default template cannot be deleted')
     data_engine.delete_object(template_info)
     image_engine.reset_templates()
     return make_api_success_response()
Пример #28
0
 def put(self, folio_id, image_id):
     db_session = data_engine.db_get_session()
     try:
         # Get data
         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 portfolio
         params = self._get_validated_object_parameters(request.form)
         chd_folio_image = data_engine.reorder_portfolio(
             folio_image, params['index'])
         data_engine.add_portfolio_history(
             folio_image.portfolio,
             get_session_user(),
             FolioHistory.ACTION_IMAGE_CHANGE,
             '%s moved to position %d' %
             (folio_image.image.src, chd_folio_image.order_num + 1),
             _db_session=db_session,
             _commit=True)
         # Return the updated image list
         db_session.expire(folio_image)
         folio = data_engine.get_portfolio(folio_id,
                                           load_images=True,
                                           _db_session=db_session)
         image_list = [_prep_folioimage_object(fi) for fi in folio.images]
         return make_api_success_response(
             object_to_dict_list(image_list, _omit_fields + ['portfolio']))
     finally:
         db_session.close()
Пример #29
0
 def delete(self, group_id):
     # Check permissions! The current user must have permissions admin to delete groups.
     permissions_engine.ensure_permitted(
         SystemPermissions.PERMIT_ADMIN_PERMISSIONS, get_session_user()
     )
     group = data_engine.get_group(group_id=group_id)
     if group is None:
         raise DoesNotExistError(str(group_id))
     try:
         data_engine.delete_group(group)
     except ValueError as e:
         raise ParameterError(str(e))
     # Reset permissions cache
     permissions_engine.reset()
     return make_api_success_response()
Пример #30
0
 def delete(self, group_id):
     # Check permissions! The current user must have permissions admin to delete groups.
     permissions_engine.ensure_permitted(
         SystemPermissions.PERMIT_ADMIN_PERMISSIONS, get_session_user())
     group = data_engine.get_group(group_id=group_id, load_users=True)
     if group is None:
         raise DoesNotExistError(str(group_id))
     try:
         data_engine.delete_group(group)
     except ValueError as e:
         raise ParameterError(str(e))
     # Reset permissions and session caches
     reset_user_sessions(group.users)
     permissions_engine.reset()
     return make_api_success_response()
Пример #31
0
 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))
Пример #32
0
 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))
Пример #33
0
 def delete(self, folio_id):
     # Get 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_DELETE, get_session_user())
     # Double check the downloads were eager-loaded before we try to use them below
     if not data_engine.attr_is_loaded(folio, 'downloads'):
         raise ValueError('bug: folio.downloads should be present')
     # Delete - cascades to folio images, permissions, history, and exports
     data_engine.delete_object(folio)
     # If we got this far the database delete worked and we now need to
     # delete the exported zip files (if any)
     delete_dir(get_portfolio_directory(folio), recursive=True)
     return make_api_success_response()
Пример #34
0
 def _set_permissions(self, group, params):
     # Apply default permissions if this is a new group
     if not group.permissions:
         group.permissions = SystemPermissions(
             group, False, False, False, False, False, False, False
         )
     # Update permissions only if the current user has permissions admin
     if permissions_engine.is_permitted(
         SystemPermissions.PERMIT_ADMIN_PERMISSIONS, get_session_user()
     ):
         group.permissions.folios = params['access_folios']
         group.permissions.reports = params['access_reports']
         group.permissions.admin_users = params['access_admin_users']
         group.permissions.admin_files = params['access_admin_files']
         group.permissions.admin_folios = params['access_admin_folios']
         group.permissions.admin_permissions = params['access_admin_permissions']
         group.permissions.admin_all = params['access_admin_all']
         return True
     return False
Пример #35
0
 def _set_permissions(self, group, params):
     # Apply default permissions if this is a new group
     if not group.permissions:
         group.permissions = SystemPermissions(group, False, False, False,
                                               False, False, False, False)
     # Update permissions only if the current user has permissions admin
     if permissions_engine.is_permitted(
             SystemPermissions.PERMIT_ADMIN_PERMISSIONS,
             get_session_user()):
         group.permissions.folios = params['access_folios']
         group.permissions.reports = params['access_reports']
         group.permissions.admin_users = params['access_admin_users']
         group.permissions.admin_files = params['access_admin_files']
         group.permissions.admin_folios = params['access_admin_folios']
         group.permissions.admin_permissions = params[
             'access_admin_permissions']
         group.permissions.admin_all = params['access_admin_all']
         return True
     return False
Пример #36
0
 def delete(self, folder_id):
     """ Deletes a disk folder """
     # v4.1 #10 delete_folder() doesn't care whether it exists, but we want the
     #          API to return a "not found" if the folder doesn't exist on disk
     #          (and as long as the database is already in sync with that)
     db_folder = data_engine.get_folder(folder_id)
     if db_folder is None:
         raise DoesNotExistError(str(folder_id))
     if not path_exists(db_folder.path, require_directory=True
                        ) and db_folder.status == Folder.STATUS_DELETED:
         raise DoesNotExistError(db_folder.path)
     # Run this as a background task in case it takes a long time
     task = task_engine.add_task(get_session_user(),
                                 'Delete disk folder %d' % folder_id,
                                 'delete_folder', {'folder_id': folder_id},
                                 Task.PRIORITY_HIGH, 'info', 'error', 10)
     if task is None:  # Task already submitted
         return make_api_success_response(task_accepted=True)
     else:
         return self._task_response(task, 30)
Пример #37
0
def imagedetails():
    # Get/check parameters
    try:
        src = request.args.get('src', '')
        validate_string(src, 1, 1024)
    except ValueError as e:
        raise ParameterError(e)

    # 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(_image_dict(db_image))
Пример #38
0
 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)))
Пример #39
0
def _image_dict(db_image, can_download=None):
    """
    Returns the common data dictionary for the imagedetails and upload APIs.
    Provide an Image data model object and optionally the pre-calculated
    boolean download permission. If the download permission is None,
    it is calculated for the image's folder and the current user.
    """
    if can_download is None:
        can_download = permissions_engine.is_folder_permitted(
            db_image.folder,
            FolderPermission.ACCESS_DOWNLOAD,
            get_session_user()
        )
    return {
        'src': db_image.src,
        'url': external_url_for('image', src=db_image.src),
        'id': db_image.id,
        'folder_id': db_image.folder_id,
        'title': db_image.title,
        'description': db_image.description,
        'width': db_image.width,
        'height': db_image.height,
        'download': can_download
    }
Пример #40
0
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:
        path_index = parse_int(path_index)
        overwrite = parse_boolean(overwrite)
        validate_string(path, 0, 1024)

        current_user = get_session_user()
        assert current_user is not None

        put_image_exception = None
        can_download = None
        for wkfile in file_list:
            original_filename = wkfile.filename
            if original_filename:
                db_image = None
                try:
                    # Save (also checks user-folder permissions)
                    _, db_image = image_engine.put_image(
                        current_user,
                        wkfile,
                        secure_filename(
                            original_filename,
                            app.config['ALLOW_UNICODE_FILENAMES']
                        ),
                        path_index,
                        path,
                        overwrite
                    )
                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_filename] = {'error': create_api_error_dict(e)}

                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_filename] = _image_dict(db_image, can_download)

        # 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(unicode(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),
            integrity_check=True
        )

    # If here, all files were uploaded successfully
    return make_api_success_response(ret_dict)
Пример #41
0
 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)
Пример #42
0
def imagelist():
    # Check parameters
    try:
        from_path = request.args.get('path', '')
        want_info = parse_boolean(request.args.get('attributes', ''))
        limit = parse_int(request.args.get('limit', '1000'))
        validate_string(from_path, 1, 1024)
    except ValueError as e:
        raise ParameterError(e)

    # Get extra parameters for image URL construction
    image_params = request.args.to_dict()
    if 'path' in image_params:
        del image_params['path']
    if 'attributes' in image_params:
        del image_params['attributes']
    if 'limit' in image_params:
        del image_params['limit']

    # Get directory listing
    directory_info = get_directory_listing(from_path, False, 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()
        )

        # Create the response
        file_list = directory_info.contents()
        img_types = image_engine.get_image_formats()
        base_folder = add_sep(directory_info.name())
        for f in file_list:
            # Filter out non-images
            if get_file_extension(f['filename']) in img_types:
                entry_path = base_folder + f['filename']
                entry = {
                    'filename': f['filename'],
                    'url': external_url_for('image', src=entry_path, **image_params)
                }
                if want_info:
                    db_entry = auto_sync_existing_file(
                        entry_path,
                        data_engine,
                        task_engine,
                        burst_pdf=False,  # Don't burst a PDF just by finding it here
                        _db_session=db_session
                    )
                    entry['id'] = db_entry.id if db_entry else 0
                    entry['folder_id'] = db_entry.folder_id if db_entry else 0
                    entry['title'] = db_entry.title if db_entry else ''
                    entry['description'] = db_entry.description if db_entry else ''
                    entry['width'] = db_entry.width if db_entry else 0
                    entry['height'] = db_entry.height if db_entry else 0
                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)