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))
def template_edit(template_id): embed = request.args.get('embed', '') fields = None field_values = None db_template = None err_msg = None try: if template_id > 0: db_template = data_engine.get_image_template(template_id) # See also views_pages.publish fields = ImageAttrs.validators().copy() fields.update(TemplateAttrs.validators().copy()) # ...but here we use the template values as field values if db_template: template = TemplateAttrs(db_template.name, db_template.template) field_values = template.get_values_dict() else: # New template defaults field_values = { 'record_stats': True, 'expiry_secs': image_engine.DEFAULT_EXPIRY_SECS } except Exception as e: log_security_error(e, request) err_msg = safe_error_str(e) return render_template( 'admin_template_edit.html', fields=fields, field_values=field_values, supported_fields=image_engine.get_supported_operations(), embed=embed, template=db_template, err_msg=err_msg)
def trace_permissions(): embed = request.args.get('embed', '') user_id = request.args.get('user', '') folder_path = request.args.get('path', '') if folder_path == '': folder_path = os.path.sep folder = None user = None users = [] user_has_admin = False trace = None err_msg = None db_session = data_engine.db_get_session() try: # Get folder and selected user info # User can be None for an anonymous user user_id = parse_int(user_id) if user_id != 0: user = data_engine.get_user(user_id, _db_session=db_session) if user is None: raise DoesNotExistError('This user no longer exists') folder = data_engine.get_folder(folder_path=folder_path, _db_session=db_session) if folder is None or folder.status == Folder.STATUS_DELETED: raise DoesNotExistError('This folder no longer exists') # Get users list users = data_engine.list_users(status=User.STATUS_ACTIVE, order_field=User.username, _db_session=db_session) # Get the folder+user traced permissions trace = permissions_engine._trace_folder_permissions(folder, user) # Flag on the UI if the user has admin for gdict in trace['groups']: gperms = gdict['group'].permissions if gperms.admin_files or gperms.admin_all: user_has_admin = True break except Exception as e: log_security_error(e, request) err_msg = safe_error_str(e) finally: try: return render_template( 'admin_trace_permissions.html', embed=embed, folder=folder, folder_is_root=folder.is_root() if folder else False, user=user, user_list=users, trace=trace, user_has_admin=user_has_admin, err_msg=err_msg, GROUP_ID_PUBLIC=Group.ID_PUBLIC) finally: db_session.close()
def image_stats(): # Get parameters image_id = request.args.get('id', '') time_from = request.args.get('from', '') time_to = request.args.get('to', '') data_type = request.args.get('data_type', '1') embed = request.args.get('embed', '') try: # Validate params, get iso and datetime versions if image_id == '': raise ValueError('No image was specified.') image_id = parse_long(image_id) (iso_time_from, iso_time_to) = process_time_parameters(time_from, time_to) dt_time_from = parse_iso_datetime(iso_time_from) dt_time_to = parse_iso_datetime(iso_time_to) embed = parse_boolean(embed) return render_template( 'reports_image_stats.html', timezone=get_timezone_code(), timezone_seconds=get_timezone_offset(), time_from=dt_time_from, time_to=dt_time_to, data_type=data_type, db_image=data_engine.get_image(image_id=image_id), embed=embed) except Exception as e: log_security_error(e, request) if app.config['DEBUG']: raise raise InternalServerError(safe_error_str(e))
def system_stats(): # Get parameters time_from = request.args.get('from', '') time_to = request.args.get('to', '') data_type = request.args.get('data_type', '1') embed = request.args.get('embed', '') try: # Validate params, get iso and datetime versions (iso_time_from, iso_time_to) = process_time_parameters(time_from, time_to) dt_time_from = parse_iso_datetime(iso_time_from) dt_time_to = parse_iso_datetime(iso_time_to) embed = parse_boolean(embed) return render_template('reports_system_stats.html', timezone=get_timezone_code(), timezone_seconds=get_timezone_offset(), time_from=dt_time_from, time_to=dt_time_to, data_type=data_type, embed=embed) except Exception as e: log_security_error(e, request) if app.config['DEBUG']: raise raise InternalServerError(safe_error_str(e))
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
def folder_permissions(): folder_path = request.args.get('path', '') if folder_path == '': folder_path = os.path.sep group_id = request.args.get('group', '') if group_id == '': group_id = Group.ID_PUBLIC group = None folder = None current_perms = None groups = [] err_msg = None db_session = data_engine.db_get_session() try: # Get folder and group info group = data_engine.get_group(group_id, _db_session=db_session) if group is None: raise DoesNotExistError('This group no longer exists') folder = data_engine.get_folder(folder_path=folder_path, _db_session=db_session) if folder is None or folder.status == Folder.STATUS_DELETED: raise DoesNotExistError('This folder no longer exists') # Get groups list groups = data_engine.list_objects(Group, Group.name, _db_session=db_session) # Get the current permissions for the folder+group, which can be None. # Note that permissions_manager might fall back to the Public group if # this is None, but to keep the admin manageable we're going to deal # only with folder inheritance, not group inheritance too. current_perms = data_engine.get_nearest_folder_permission( folder, group, _load_nearest_folder=True, _db_session=db_session) except Exception as e: log_security_error(e, request) err_msg = safe_error_str(e) finally: try: return render_template( 'admin_folder_permissions.html', group=group, folder=folder, folder_is_root=folder.is_root() if folder else False, current_permissions=current_perms, group_list=groups, err_msg=err_msg, GROUP_ID_PUBLIC=Group.ID_PUBLIC, GROUP_ID_EVERYONE=Group.ID_EVERYONE) finally: db_session.close()
def group_edit(group_id): embed = request.args.get('embed', '') group = None users = [] err_msg = None try: users = data_engine.list_users(status=User.STATUS_ACTIVE, order_field=User.username) if group_id > 0: group = data_engine.get_group(group_id=group_id, load_users=True) except Exception as e: log_security_error(e, request) err_msg = safe_error_str(e) return render_template('admin_group_edit.html', embed=embed, users=users, group=group, err_msg=err_msg, GROUP_ID_PUBLIC=Group.ID_PUBLIC, GROUP_TYPE_LOCAL=Group.GROUP_TYPE_LOCAL, GROUP_TYPE_SYSTEM=Group.GROUP_TYPE_SYSTEM, STATUS_ACTIVE=User.STATUS_ACTIVE)
def portfolio_view(human_id): try: # Find the portfolio folio = data_engine.get_portfolio(human_id=human_id, load_images=True) if not folio: raise DoesNotExistError('Portfolio \'%s\' does not exist' % human_id) # Ensure that the user has permission to view the portfolio user = get_session_user() permissions_engine.ensure_portfolio_permitted( folio, FolioPermission.ACCESS_VIEW, user ) # Filter out images that the user can't view # so that we don't get broken images in the UI checked_folders = {} # cache folders already checked folio_images_1 = folio.images folio_images_2 = [] for fol_img in folio_images_1: folder_path = fol_img.image.folder.path if folder_path in checked_folders: folio_images_2.append(fol_img) elif permissions_engine.is_folder_permitted( folder_path, FolderPermission.ACCESS_VIEW, user, folder_must_exist=False # though it should exist! ): checked_folders[folder_path] = True folio_images_2.append(fol_img) # Replace the original image list with the filtered one folio.images = folio_images_2 # Generate the image viewing URLs, including any portfolio-specific changes web_view_params = { 'format': 'jpg', 'colorspace': 'srgb' } sizing_view_params = { 'width': 800, 'height': 800, 'size_fit': True } pre_sized_images = [ fol_img for fol_img in folio.images if fol_img.parameters and ( ('width' in fol_img.parameters and fol_img.parameters['width']['value']) or ('height' in fol_img.parameters and fol_img.parameters['height']['value']) ) ] for fol_img in folio.images: image_attrs = get_portfolio_image_attrs(fol_img, False, False, False) image_attrs.apply_dict(web_view_params, True, False, False) if len(pre_sized_images) == 0: image_attrs.apply_dict(sizing_view_params, True, False, False) # Here we normalise the attrs only after everything has been applied image_attrs.normalise_values() fol_img.url = url_for_image_attrs(image_attrs) return render_template( 'portfolio_view.html', title=folio.name, folio=folio, removed_count=(len(folio_images_1) - len(folio_images_2)) ) except Exception as e: # Although this isn't a JSON API, we're still using it like a viewing API, # so get the correct HTTP status code to return. create_api_error_dict() also # logs security errors so we don't need to do that separately here. error_dict = create_api_error_dict(e, logger) if app.config['DEBUG']: raise return render_template( 'portfolio_view.html', title='Portfolio', err_msg='This portfolio cannot be viewed: ' + safe_error_str(e) ), error_dict['status']
def topten(): # Get parameters days = request.args.get('days', '1') limit = request.args.get('number', '10') data_type = request.args.get('data_type', '2') try: results = [] db_session = data_engine.db_get_session() try: # Convert params to ints days = parse_int(days) limit = parse_int(limit) data_type = parse_int(data_type) # Set options if days < 1: days = 1 if days > 30: days = 30 if limit < 10: limit = 10 if limit > 100: limit = 100 if data_type == 1: order = '-total_requests' elif data_type == 2: order = '-total_views' elif data_type == 3: order = '-total_cached_views' elif data_type == 4: order = '-total_downloads' elif data_type == 5: order = '-total_bytes' elif data_type == 6: order = '-total_seconds' elif data_type == 7: order = '-max_seconds' else: raise ValueError('Invalid data_type %d' % data_type) # Get initial stats top_stats = data_engine.summarise_image_stats( datetime.utcnow() - timedelta(days=days), datetime.utcnow(), limit=limit, order_by=order, _db_session=db_session) # Convert stats list to an image list for result in top_stats: db_image = data_engine.get_image(image_id=result[0], _db_session=db_session) if db_image: results.append({ 'id': db_image.id, 'src': db_image.src, 'requests': result[1], 'views': result[2], 'cached_views': result[3], 'downloads': result[4], 'bytes': result[5], 'seconds': result[6], 'max_seconds': result[7] }) finally: db_session.close() return render_template('reports_topten.html', days=days, data_type=data_type, number=limit, results=results) except Exception as e: log_security_error(e, request) if app.config['DEBUG']: raise raise InternalServerError(safe_error_str(e))