def view_node(project_url, node_id): """Entry point to view a node in the context of a project""" # Some browsers mangle URLs and URL-encode /p/{p-url}/#node-id if node_id.startswith('#'): return redirect(url_for('projects.view_node', project_url=project_url, node_id=node_id[1:]), code=301) # permanent redirect theatre_mode = 't' in request.args api = system_util.pillar_api() # First we check if it's a simple string, in which case we are looking for # a static page. Maybe we could use bson.objectid.ObjectId.is_valid(node_id) if not utils.is_valid_id(node_id): # raise wz_exceptions.NotFound('No such node') project, node = render_node_page(project_url, node_id, api) else: # Fetch the node before the project. If this user has access to the # node, we should be able to get the project URL too. try: node = Node.find(node_id, api=api) except ForbiddenAccess: return render_template('errors/403.html'), 403 except ResourceNotFound: raise wz_exceptions.NotFound('No such node') try: project = Project.find_one({'where': {"url": project_url, '_id': node.project}}, api=api) except ResourceNotFound: # In theatre mode, we don't need access to the project at all. if theatre_mode: project = None else: raise wz_exceptions.NotFound('No such project') og_picture = node.picture = utils.get_file(node.picture, api=api) if project: if not node.picture: og_picture = utils.get_file(project.picture_header, api=api) project.picture_square = utils.get_file(project.picture_square, api=api) # Append _theatre to load the proper template theatre = '_theatre' if theatre_mode else '' extension_sidebar_links = current_app.extension_sidebar_links(project) return render_template('projects/view{}.html'.format(theatre), api=api, project=project, node=node, show_node=True, show_project=False, og_picture=og_picture, extension_sidebar_links=extension_sidebar_links)
def test_valid(self): # 24-byte hex strings self.assertTrue(utils.is_valid_id(24 * 'a')) self.assertTrue(utils.is_valid_id(24 * 'a')) self.assertTrue(utils.is_valid_id('deadbeefbeefcacedeadcace')) self.assertTrue(utils.is_valid_id('deadbeefbeefcacedeadcace')) # 12-byte arbitrary ASCII bytes self.assertTrue(utils.is_valid_id(b'DeadBeefCake')) self.assertTrue(utils.is_valid_id(b'DeadBeefCake')) # 12-byte object self.assertTrue(utils.is_valid_id('beef€67890'.encode()))
def view_task_log(project, task_id): """Shows a limited number of task log entries. Pass page=N (N≥1) to request further entries. """ from pillarsdk import ResourceNotFound from pillar.web.utils import is_valid_id, last_page_index from flamenco.tasks.sdk import TaskLog if not is_valid_id(task_id): raise wz_exceptions.UnprocessableEntity() page_idx = int(request.args.get('page', 1)) api = pillar_api() try: logs = TaskLog.all( { 'where': { 'task': task_id }, 'page': page_idx, 'max_results': TASK_LOG_PAGE_SIZE }, api=api) except ResourceNotFound: logs = { '_items': [], '_meta': { 'total': 0, 'page': page_idx, 'max_results': TASK_LOG_PAGE_SIZE } } last_page_idx = last_page_index(logs['_meta']) has_next_page = page_idx < last_page_idx has_prev_page = page_idx > 1 return render_template('flamenco/tasks/view_task_log_embed.html', page_idx=page_idx, logs=logs, has_prev_page=has_prev_page, has_next_page=has_next_page, last_page_idx=last_page_idx, project=project, task_id=task_id)
def download_task_log(project, task_id): """Shows the entire task log as text/plain""" from flask import Response, current_app from pillar.web.utils import is_valid_id, last_page_index from flamenco.tasks.sdk import TaskLog if not is_valid_id(task_id): raise wz_exceptions.UnprocessableEntity() # Required because the stream_log() generator will run outside the app context. app = current_app._get_current_object() api = pillar_api() def stream_log(): page_idx = 1 while True: with app.app_context(): logs = TaskLog.all( { 'where': { 'task': task_id }, 'page': page_idx, 'max_results': TASK_LOG_PAGE_SIZE }, api=api) for tasklog in logs['_items']: yield tasklog.log + '\n' if page_idx >= last_page_index(logs['_meta']): break page_idx += 1 return Response(stream_log(), mimetype='text/plain')
def download_task_log(project, task_id): """Show the entire task log as text/plain. This endpoint is for obtaining the task log stored in MongoDB. This approach is deprecated in favour of having the Manager store & serve the logs and upload them to the Server on demand (see flamenco.managers.api). """ if not is_valid_id(task_id): raise wz_exceptions.UnprocessableEntity() # Required because the stream_log() generator will run outside the app context. app = current_app.real_app api = pillar_api() def stream_log(): page_idx = 1 while True: with app.app_context(): logs = TaskLog.all( { 'where': { 'task': task_id }, 'page': page_idx, 'max_results': TASK_LOG_PAGE_SIZE }, api=api) for tasklog in logs['_items']: yield tasklog.log + '\n' if page_idx >= last_page_index(logs['_meta']): break page_idx += 1 return Response(stream_log(), mimetype='text/plain')
def download_task_log_file(project, task_id): """Redirect to the storage backend for the task log. This endpoint is for obtaining the task log sent to us by Flamenco Manager, and stored as a gzipped file in the storage backend. See the Manager API for the endpoint that actually accepts the log file from the Manager. """ from pillar.web.utils import is_valid_id from .sdk import Task if not is_valid_id(task_id): raise wz_exceptions.UnprocessableEntity() api = pillar_api() task = Task.find(task_id, api=api) if not task.log_file: return wz_exceptions.NotFound( f'Task {task_id} has no attached log file') blob = current_flamenco.task_manager.logfile_blob(task.to_dict()) url = blob.get_url(is_public=False) return redirect(url, code=307)
def test_bad_content(self): # 24-character non-hexadecimal string self.assertFalse(utils.is_valid_id('deadbeefbeefcakedeadcake')) # unicode variant of valid 12-byte str object self.assertFalse(utils.is_valid_id('beef€67890'))
def test_non_string(self): self.assertFalse(utils.is_valid_id(None)) self.assertFalse(utils.is_valid_id(1234)) self.assertFalse(utils.is_valid_id([24 * 'a']))
def test_bad_length(self): self.assertFalse(utils.is_valid_id(23 * 'a')) self.assertFalse(utils.is_valid_id(25 * 'a'))