async def __call__(self): registry = await get_registry() settings = registry.for_interface(IImagingSettings) scale_name = self.request.matchdict["scale"] allowed_sizes = settings["allowed_sizes"] if scale_name not in allowed_sizes: raise HTTPNotFound( content={"reason": f"{scale_name} is not supported"}) file = self.field.get(self.field.context or self.context) if file is None: raise HTTPNotFound( content={ "message": "File or custom filename required to download" }) if hasattr( file, "previews" ) and file.previews is not None and scale_name in file.previews: new_field = CloudPreviewImageFileField( __name__=scale_name, file=file).bind(self.behavior or self.context) adapter = get_multi_adapter( (self.context, self.request, new_field), IFileManager) return await adapter.download() else: return HTTPNoContent()
async def cancel_task(context, request): task_prefix = get_task_id_prefix() if not request.matchdict["task_id"].startswith(task_prefix): return HTTPNotFound(content={"reason": "Task not found"}) task = TaskState(request.matchdict["task_id"]) try: return await task.cancel() except TaskNotFoundException: return HTTPNotFound(content={"reason": "Task not found"})
async def info_task(context, request): task_prefix = get_task_id_prefix() if not request.matchdict['task_id'].startswith(task_prefix): return HTTPNotFound(content={'reason': 'Task not found'}) try: task = TaskState(request.matchdict['task_id']) return await task.get_state() except TaskNotFoundException: return HTTPNotFound(content={'reason': 'Task not found'})
async def preflight(self): """We need to check if there is cors enabled and is valid.""" headers = {} renderer = app_settings['cors_renderer'](self.request) settings = await renderer.get_settings() if not settings: return {} origin = self.request.headers.get('Origin', None) if not origin: raise HTTPNotFound(content={ 'message': 'Origin this header is mandatory' }) requested_method = self.getRequestMethod() if not requested_method: raise HTTPNotFound(content={ 'text': 'Access-Control-Request-Method this header is mandatory' }) requested_headers = ( self.request.headers.get('Access-Control-Request-Headers', ())) if requested_headers: requested_headers = map(str.strip, requested_headers.split(', ')) requested_method = requested_method.upper() allowed_methods = settings['allow_methods'] if requested_method not in allowed_methods: raise HTTPMethodNotAllowed( requested_method, allowed_methods, content={ 'message': 'Access-Control-Request-Method Method not allowed' }) supported_headers = settings['allow_headers'] if '*' not in supported_headers and requested_headers: supported_headers = [s.lower() for s in supported_headers] for h in requested_headers: if not h.lower() in supported_headers: raise HTTPUnauthorized(content={ 'text': 'Access-Control-Request-Headers Header %s not allowed' % h }) supported_headers = [] if supported_headers is None else supported_headers requested_headers = [] if requested_headers is None else requested_headers supported_headers = set(supported_headers) | set(requested_headers) headers['Access-Control-Allow-Headers'] = ','.join(supported_headers) headers['Access-Control-Allow-Methods'] = ','.join(settings['allow_methods']) headers['Access-Control-Max-Age'] = str(settings['max_age']) return headers
async def get_field_value(context, request): field_name = request.matchdict["dotted_name"] if "." in field_name: # behavior field lookup iface_dotted = ".".join(field_name.split(".")[:-1]) field_name = field_name.split(".")[-1] try: schema = resolve_dotted_name(iface_dotted) except ModuleNotFoundError: return HTTPNotFound( content={"reason": f"Could resolve: {iface_dotted}"}) try: field = schema[field_name] except KeyError: return HTTPNotFound(content={"reason": f"No field: {field_name}"}) try: behavior = await get_behavior(context, schema) except AttributeError: return HTTPNotFound( content={"reason": f"Could not load behavior: {iface_dotted}"}) if behavior is None: return HTTPNotFound( content={"reason": f"Not valid behavior: {iface_dotted}"}) field = field.bind(behavior) field_context = behavior else: # main object field factory = get_cached_factory(context.type_name) schema = factory.schema try: field = schema[field_name] except KeyError: return HTTPNotFound(content={"reason": f"No field: {field_name}"}) field = field.bind(context) field_context = context # check permission read_permissions = merged_tagged_value_dict(schema, read_permission.key) serializer = get_multi_adapter((context, request), IResourceSerializeToJson) if not serializer.check_permission(read_permissions.get(field_name)): return HTTPUnauthorized( content={"reason": "You are not authorized to render this field"}) field_renderer = query_multi_adapter((context, request, field), IFieldValueRenderer) if field_renderer is None: return await serializer.serialize_field(field_context, field) else: return await field_renderer()
async def resolve_uid(context, request): uid = request.matchdict['uid'] ob = await get_object_by_oid(uid) if ob is None: return HTTPNotFound(content={'reason': f'Could not find uid: {uid}'}) interaction = IInteraction(request) if interaction.check_permission('guillotina.AccessContent', ob): return HTTPMovedPermanently(get_object_url(ob, request)) else: # if a user doesn't have access to it, they shouldn't know anything about it return HTTPNotFound(content={'reason': f'Could not find uid: {uid}'})
async def resolve_uid(context, request): uid = request.matchdict["uid"] try: ob = await get_object_by_uid(uid) except KeyError: return HTTPNotFound(content={"reason": f"Could not find uid: {uid}"}) policy = get_security_policy() if policy.check_permission("guillotina.AccessContent", ob): return HTTPMovedPermanently(get_object_url(ob, request)) else: # if a user doesn't have access to it, they shouldn't know anything about it return HTTPNotFound(content={"reason": f"Could not find uid: {uid}"})
async def info_task(context, request): task_prefix = get_task_id_prefix() if not request.matchdict["task_id"].startswith(task_prefix): return HTTPNotFound(content={"reason": "Task not found"}) try: task = TaskState(request.matchdict["task_id"]) state = await task.get_state() if not can_debug_amqp(context): state.pop("job_data", None) return state except TaskNotFoundException: return HTTPNotFound(content={"reason": "Task not found"})
async def prepare(self): # we want have the field name = self.request.matchdict["field_name"] fti = query_utility(IFactory, name=self.context.type_name) schema = fti.schema field = None self.behavior = None if name in schema: field = schema[name] else: # TODO : We need to optimize and move to content.py iterSchema for behavior_schema in fti.behaviors or (): if name in behavior_schema: field = behavior_schema[name] self.behavior = behavior_schema(self.context) break for behavior_name in self.context.__behaviors__ or (): behavior_schema = BEHAVIOR_CACHE[behavior_name] if name in behavior_schema: field = behavior_schema[name] self.behavior = behavior_schema(self.context) break # Check that its a File Field if field is None: raise HTTPNotFound(content={"reason": "No valid name"}) if self.behavior is not None: ctx = self.behavior else: ctx = self.context if self.behavior is not None and IAsyncBehavior.implementedBy( self.behavior.__class__): # providedBy not working here? await self.behavior.load() if IDict.providedBy(field) and ICloudFileField.providedBy( field.value_type): key = self.request.matchdict.get("file_key") if key is not None: self.field = CloudFileField(__name__=name).bind( DictFieldProxy(key, ctx, name)) elif ICloudFileField.providedBy(field): self.field = field.bind(ctx) if self.field is None: raise HTTPNotFound(content={"reason": "No valid name"}) return self
async def preflight(self): """We need to check if there is cors enabled and is valid.""" headers = {} renderer = app_settings["cors_renderer"](self.request) settings = await renderer.get_settings() if not settings: return {} origin = self.request.headers.get("Origin", None) if not origin: raise HTTPNotFound(content={"message": "Origin this header is mandatory"}) requested_method = self.getRequestMethod() if not requested_method: raise HTTPNotFound(content={"text": "Access-Control-Request-Method this header is mandatory"}) requested_headers = self.request.headers.get("Access-Control-Request-Headers", ()) if requested_headers: requested_headers = map(str.strip, requested_headers.split(", ")) requested_method = requested_method.upper() allowed_methods = settings["allow_methods"] if requested_method not in allowed_methods: raise HTTPMethodNotAllowed( requested_method, allowed_methods, content={"message": "Access-Control-Request-Method Method not allowed"}, ) supported_headers = settings["allow_headers"] if "*" not in supported_headers and requested_headers: supported_headers = [s.lower() for s in supported_headers] for h in requested_headers: if not h.lower() in supported_headers: raise HTTPUnauthorized( content={"text": "Access-Control-Request-Headers Header %s not allowed" % h} ) supported_headers = [] if supported_headers is None else supported_headers requested_headers = [] if requested_headers is None else requested_headers supported_headers = set(supported_headers) | set(requested_headers) headers["Access-Control-Allow-Headers"] = ",".join(supported_headers) headers["Access-Control-Allow-Methods"] = ",".join(settings["allow_methods"]) headers["Access-Control-Max-Age"] = str(settings["max_age"]) return headers
async def __call__(self): registry = await get_registry() settings = registry.for_interface(IImagingSettings) scale_name = self.request.matchdict["scale"] allowed_sizes = settings["allowed_sizes"] if scale_name not in allowed_sizes: raise HTTPNotFound( content={"reason": f"{scale_name} is not supported"}) file = self.field.get(self.field.context or self.context) if file is None: raise HTTPNotFound( content={ "message": "File or custom filename required to download" }) adapter = get_multi_adapter((self.context, self.request, self.field), IFileManager) data = b"" async for chunk in adapter.iter_data(): data += chunk width, _, height = allowed_sizes[scale_name].partition(":") result, format_, size = scaleImage( data, int(width), int(height), quality=settings["quality"], direction="thumbnail", ) cors_renderer = app_settings["cors_renderer"](self.request) headers = await cors_renderer.get_headers() headers.update({ "CONTENT-DISPOSITION": 'attachment; filename="{}"'.format(file.filename) }) download_resp = Response( status=200, headers=headers, content_type=f"image/{format_}", content_length=len(result), ) await download_resp.prepare(self.request) await download_resp.write(result) await download_resp.write(eof=True) return download_resp
async def get_block_schema(context, request): key = request.matchdict["key"] vocabulary_registry = getVocabularyRegistry() try: vocab = vocabulary_registry.get(context, key) except VocabularyRegistryError: return HTTPNotFound() title_filter = request.query.get("title") if title_filter: title_filter = title_filter.lower() token_filter = request.query.get("token") if token_filter: token_filter = token_filter.lower() result = {} result["@id"] = join(IAbsoluteURL(context)(), "@vocabularies", key) result["items"] = [] for term in vocab.keys(): if token_filter and token_filter not in str(term).lower(): continue new_title = vocab.getTerm(term) if title_filter and title_filter not in str(new_title).lower(): continue result["items"].append({"title": new_title, "token": term}) result["items_total"] = len(result["items"]) return result
async def __call__(self): if app_settings.get("graphql", {}).get("enable_playground") is True: return Response( content=PLAYGROUND_HTML, headers={"content-type": "text/html"}, ) raise HTTPNotFound()
async def download(self, disposition=None, filename=None, content_type=None, size=None, **kwargs): if disposition is None: disposition = self.request.query.get('disposition', 'attachment') file = self.field.get(self.field.context or self.context) if file is None and filename is None: raise HTTPNotFound( content={ 'message': 'File or custom filename required to download' }) cors_renderer = app_settings['cors_renderer'](self.request) headers = await cors_renderer.get_headers() headers.update({ 'CONTENT-DISPOSITION': '{}; filename="{}"'.format(disposition, filename or file.filename) }) download_resp = StreamResponse(headers=headers) download_resp.content_type = content_type or file.guess_content_type() if size or file.size: download_resp.content_length = size or file.size await download_resp.prepare(self.request) async for chunk in self.file_storage_manager.iter_data(**kwargs): await download_resp.write(chunk) await download_resp.drain() await download_resp.write_eof() return download_resp
async def prepare_download(self, disposition=None, filename=None, content_type=None, size=None, **kwargs): if disposition is None: disposition = self.request.query.get('disposition', 'attachment') try: file = self.field.get(self.field.context or self.context) except AttributeError: file = None if file is None and filename is None: raise HTTPNotFound(content={ 'message': 'File or custom filename required to download' }) cors_renderer = app_settings['cors_renderer'](self.request) headers = await cors_renderer.get_headers() headers.update({ 'Content-Disposition': '{}; filename="{}"'.format( disposition, filename or file.filename) }) download_resp = StreamResponse(headers=headers) download_resp.content_type = content_type or file.guess_content_type() if size or file.size: download_resp.content_length = size or file.size await download_resp.prepare(self.request) return download_resp
async def head(self, disposition=None, filename=None, content_type=None, size=None, extra_headers=None, **kwargs): try: if hasattr(self.file_storage_manager, "exists"): # does not need to implement but can be a way to verify # file exists on cloud platform still if not await apply_coroutine(self.file_storage_manager.exists): raise HTTPNotFound( content={"message": "File object does not exist"}) download_resp = await self.prepare_download( disposition, filename, content_type, size, extra_headers, **kwargs) await download_resp.write(eof=True) except (ConnectionRefusedError, ConnectionResetError): # pragma: no cover logger.info(f"Head cancelled: {self.request}") # when supporting range headers, the browser will # cancel downloads. This is fine. raise HTTPClientClosedRequest() return download_resp
async def prepare(self): # we want have the key of the registry self.key = self.request.matchdict['key'] self.value = self.request.container_settings.get(self.key, _marker) if self.value is _marker: raise HTTPNotFound( content={'message': f'{self.key} not in settings'})
async def __call__(self): data = await self.request.json() if '@type' not in data or data['@type'] not in app_settings[ 'container_types']: raise HTTPNotFound( content={ 'message': 'can not create this type %s' % data['@type'] }) if 'id' not in data: raise HTTPPreconditionFailed(content={'message': 'We need an id'}) if not data.get('title'): data['title'] = data['id'] if 'description' not in data: data['description'] = '' value = await self.context.async_contains(data['id']) if value: # Already exist raise HTTPConflict( content={'message': 'Container with id already exists'}) install_addons = data.pop('@addons', None) or [] for addon in install_addons: # validate addon list if addon not in app_settings['available_addons']: return ErrorResponse( 'RequiredParam', "Property '@addons' must refer to a valid addon", status=412, reason=error_reasons.INVALID_ID) owner_id = get_authenticated_user_id(self.request) container = await create_container(self.context, data.pop('id'), container_type=data.pop('@type'), owner_id=owner_id, **data) self.request._container_id = container.__name__ self.request.container = container annotations_container = get_adapter(container, IAnnotations) self.request.container_settings = await annotations_container.async_get( REGISTRY_DATA_KEY) for addon in install_addons: await addons.install(container, addon) resp = { '@type': container.type_name, 'id': container.id, 'title': data['title'] } headers = {'Location': posixpath.join(self.request.path, container.id)} return Response(content=resp, headers=headers)
async def prepare(self): type_id = self.request.matchdict["type_id"] self.value = queryUtility(IResourceFactory, name=type_id) if self.value is None: raise HTTPNotFound( content={"reason": f"Could not find type {type_id}", "type": type_id} )
async def __call__(self): data = await self.request.json() if "@type" not in data or data["@type"] not in app_settings[ "container_types"]: raise HTTPNotFound( content={ "message": "can not create this type %s" % data["@type"] }) if "id" not in data: raise HTTPPreconditionFailed(content={"message": "We need an id"}) if not data.get("title"): data["title"] = data["id"] if "description" not in data: data["description"] = "" value = await self.context.async_contains(data["id"]) if value: # Already exist raise HTTPConflict( content={"message": "Container with id already exists"}) install_addons = data.pop("@addons", None) or [] for addon in install_addons: # validate addon list if addon not in app_settings["available_addons"]: return ErrorResponse( "RequiredParam", "Property '@addons' must refer to a valid addon", status=412, reason=error_reasons.INVALID_ID, ) owner_id = get_authenticated_user_id() container = await create_container(self.context, data.pop("id"), container_type=data.pop("@type"), owner_id=owner_id, **data) task_vars.container.set(container) annotations_container = get_adapter(container, IAnnotations) task_vars.registry.set( await annotations_container.async_get(REGISTRY_DATA_KEY)) for addon in install_addons: await addons.install(container, addon) resp = { "@type": container.type_name, "id": container.id, "title": data["title"] } headers = {"Location": posixpath.join(self.request.path, container.id)} return Response(content=resp, headers=headers)
async def __call__(self): data = await self.request.json() if '@type' not in data or data['@type'] not in app_settings[ 'container_types']: raise HTTPNotFound( content={ 'message': 'can not create this type %s' % data['@type'] }) if 'id' not in data: raise HTTPPreconditionFailed(content={'message': 'We need an id'}) if not data.get('title'): data['title'] = data['id'] if 'description' not in data: data['description'] = '' value = await self.context.async_contains(data['id']) if value: # Already exist raise HTTPConflict( content={'message': 'Container with id already exists'}) container = await create_content(data['@type'], id=data['id'], title=data['title'], description=data['description']) # Special case we don't want the parent pointer container.__name__ = data['id'] await self.context.async_set(data['id'], container) await container.install() self.request._container_id = container.__name__ self.request.container = container user = get_authenticated_user_id(self.request) # Local Roles assign owner as the creator user roleperm = IPrincipalRoleManager(container) roleperm.assign_role_to_principal('guillotina.Owner', user) await notify( ObjectAddedEvent(container, self.context, container.__name__, payload=data)) resp = { '@type': data['@type'], 'id': data['id'], 'title': data['title'] } headers = {'Location': posixpath.join(self.request.path, data['id'])} return Response(content=resp, headers=headers)
async def prepare(self): # we want have the key of the registry self.key = self.request.matchdict["key"] registry = await get_registry(self.context) self.value = registry.get(self.key, _marker) if self.value is _marker: raise HTTPNotFound( content={"message": f"{self.key} not in settings"})
async def prepare(self): type_name = self.request.matchdict['type_name'] self.value = query_utility(IResourceFactory, name=type_name) if self.value is None: raise HTTPNotFound(content={ 'reason': f'Could not find type {type_name}', 'type_name': type_name })
async def get_block_schema(context, request): key = request.matchdict['key'] if key not in app_settings['available_blocks'].keys(): return HTTPNotFound() block = app_settings['available_blocks'][key] schema = resolve_dotted_name(block['schema']) serializer = get_multi_adapter((schema, request), ISchemaSerializeToJson) return await serializer()
async def storage_get(context, request): storage_id = request.matchdict["storage_id"] config = _get_storage_config(storage_id) if config is None: raise HTTPNotFound(content={"reason": f"Storage {storage_id}"}) manager = config.get("type", config["storage"]) factory = get_adapter(context, IDatabaseManager, name=manager, args=[config]) return {"id": storage_id, "type": config["storage"], "databases": await factory.get_names()}
async def __call__(self): settings = self.request.container_settings.for_interface( IImagingSettings) scale_name = self.request.matchdict['scale'] allowed_sizes = settings['allowed_sizes'] if scale_name not in allowed_sizes: raise HTTPNotFound( content={'reason': f'{scale_name} is not supported'}) file = self.field.get(self.field.context or self.context) if file is None: raise HTTPNotFound( content={ 'message': 'File or custom filename required to download' }) adapter = get_multi_adapter((self.context, self.request, self.field), IFileManager) data = b'' async for chunk in adapter.iter_data(): data += chunk width, _, height = allowed_sizes[scale_name].partition(':') result, format_, size = scaleImage(data, int(width), int(height), quality=settings['quality'], direction='thumbnail') cors_renderer = app_settings['cors_renderer'](self.request) headers = await cors_renderer.get_headers() headers.update({ 'CONTENT-DISPOSITION': 'attachment; filename="{}"'.format(file.filename) }) download_resp = StreamResponse(headers=headers) download_resp.content_type = f'image/{format_}' if file.size: download_resp.content_length = len(result) await download_resp.prepare(self.request) await download_resp.write(result) await download_resp.drain() await download_resp.write_eof() return download_resp
async def delete_db(context, request): storage_id = request.matchdict["storage_id"] config = _get_storage_config(storage_id) if config is None: raise HTTPNotFound(content={"reason": f"Storage {storage_id}"}) manager = config.get("type", config["storage"]) factory = get_adapter(context, IDatabaseManager, name=manager, args=[config]) assert request.matchdict["db_id"] in await factory.get_names() await factory.delete(request.matchdict["db_id"])
async def iter_data(self, uri=None, headers=None): if uri is None: file = self.field.get(self.field.context or self.context) if not _is_uploaded_file(file): raise FileNotFoundException( "Trying to iterate data with no file") else: uri = file.uri if headers is None: headers = {} util = get_utility(IGCloudBlobStore) url = "{}/{}/o/{}".format(OBJECT_BASE_URL, await util.get_bucket_name(), quote_plus(uri)) headers["AUTHORIZATION"] = "Bearer {}".format(await util.get_access_token()) async with util.session.get(url, headers=headers, params={"alt": "media"}, timeout=-1) as api_resp: if api_resp.status not in (200, 206): text = await api_resp.text() if api_resp.status == 404: raise HTTPNotFound( content={ "reason": "Google cloud file not found", "response": text, }) elif api_resp.status == 401: log.warning( f"Invalid google cloud credentials error: {text}") raise HTTPNotFound( content={ "reason": "Google cloud invalid credentials", "response": text, }) raise GoogleCloudException(f"{api_resp.status}: {text}") while True: chunk = await api_resp.content.read(1024 * 1024) if len(chunk) > 0: yield chunk else: break
async def copy(self, to_storage_manager, to_dm): file = self.field.get(self.field.context or self.context) if not _is_uploaded_file(file): raise HTTPNotFound( content={"reason": "To copy a uri must be set on the object"}) generator = get_multi_adapter((self.context, self.field), IFileNameGenerator) new_uri = await apply_coroutine(generator) util = get_utility(IGCloudBlobStore) bucket_name = await util.get_bucket_name() url = "{}/{}/o/{}/copyTo/b/{}/o/{}".format( OBJECT_BASE_URL, bucket_name, quote_plus(file.uri), bucket_name, quote_plus(new_uri), ) async with util.session.post( url, headers={ "AUTHORIZATION": "Bearer {}".format(await util.get_access_token()), "Content-Type": "application/json", }, ) as resp: if resp.status == 404: text = await resp.text() reason = ( f"Could not copy file: {file.uri} to {new_uri}:404: {text}" # noqa ) log.warning(reason) raise HTTPNotFound(content={"reason": reason}) else: data = await resp.json() assert data["name"] == new_uri await to_dm.finish( values={ "content_type": data["contentType"], "size": int(data["size"]), "uri": new_uri, "filename": file.filename or "unknown", })
async def get_user(self) -> User: user_id: str = self.request.matchdict["user"] try: user = await navigate_to(self.context, "users/{}".format(user_id)) except KeyError: user = None if user is None: raise HTTPNotFound(content={"reason": f"User {user_id} not found"}) return user