async def apply_rendering(view, request, view_result): for ct in get_acceptable_content_types(request): renderer = query_multi_adapter( (view, request), IRenderer, name=ct) if renderer is not None: break else: # default to application/json renderer = query_multi_adapter( (view, request), IRenderer, name='application/json') return await renderer(view_result)
def get_object_url(ob: IResource, request: IRequest=None, **kwargs) -> typing.Optional[str]: ''' Generate full url of object. :param ob: object to get url for :param request: relative path to object you want to retrieve ''' if request is None: request = get_current_request() url_adapter = query_multi_adapter((ob, request), IAbsoluteURL) if url_adapter is not None: return url_adapter(**kwargs) return None
def get_service_definition(self, resource, tail): if tail and len(tail) > 0: # convert match lookups view_name = routes.path_to_view_name(tail) elif not tail: view_name = '' else: return None request = get_mocked_request() method = (self.options.get('method') or 'get').upper() method = app_settings['http_methods'][method] # container registry lookup try: view = query_multi_adapter( (resource, request), method, name=view_name) return view.__route__.service_configuration except AttributeError: pass
async def real_resolve(self, request: IRequest) -> Optional[MatchInfo]: """Main function to resolve a request.""" if request.method not in app_settings["http_methods"]: raise HTTPMethodNotAllowed( method=request.method, allowed_methods=[ k for k in app_settings["http_methods"].keys() ]) method = app_settings["http_methods"][request.method] try: resource, tail = await self.traverse(request) except (ConflictError, asyncio.CancelledError): # can also happen from connection errors so we bubble this... raise except Exception as _exc: logger.error("Unhandled exception occurred", exc_info=True) request.resource = request.tail = None request.exc = _exc data = { "success": False, "exception_message": str(_exc), "exception_type": getattr(type(_exc), "__name__", str(type(_exc))), # noqa } if app_settings.get("debug"): data["traceback"] = traceback.format_exc() raise HTTPBadRequest(content={"reason": data}) request.record("traversed") request.resource = resource request.tail = tail if tail and len(tail) > 0: # convert match lookups view_name = routes.path_to_view_name(tail) elif not tail: view_name = "" request.record("beforeauthentication") authenticated = await authenticate_request(request) # Add anonymous participation if authenticated is None: authenticated = AnonymousUser() set_authenticated_user(authenticated) request.record("authentication") policy = get_security_policy(authenticated) for language in get_acceptable_languages(request): translator = query_adapter((resource, request), ILanguage, name=language) if translator is not None: resource = translator.translate() break # container registry lookup try: view = query_multi_adapter((resource, request), method, name=view_name) except AttributeError: view = None if view is None and method == IOPTIONS: view = DefaultOPTIONS(resource, request) # Check security on context to AccessContent unless # is view allows explicit or its OPTIONS permission = get_utility(IPermission, name="guillotina.AccessContent") if not policy.check_permission(permission.id, resource): # Check if its a CORS call: if IOPTIONS != method: # Check if the view has permissions explicit if view is None or not view.__allow_access__: logger.info( "No access content {content} with {auth}".format( content=resource, auth=authenticated.id), request=request, ) raise HTTPUnauthorized( content={ "reason": "You are not authorized to access content", "content": str(resource), "auth": authenticated.id, }) if not view and len(tail) > 0: # we should have a view in this case because we are matching routes await notify(TraversalViewMissEvent(request, tail)) raise HTTPNotFound( content={"reason": "object and/or route not found"}) request.found_view = view request.view_name = view_name request.record("viewfound") ViewClass = view.__class__ view_permission = get_view_permission(ViewClass) if view_permission is None: # use default view permission view_permission = app_settings["default_permission"] if not policy.check_permission(view_permission, view): if IOPTIONS != method: raise HTTPUnauthorized( content={ "reason": "You are not authorized to view", "content": str(resource), "auth": authenticated.id, }) try: view.__route__.matches(request, tail or []) except (KeyError, IndexError): await notify(TraversalRouteMissEvent(request, tail)) return None except AttributeError: pass await notify(ObjectLoadedEvent(resource)) if hasattr(view, "prepare"): view = (await view.prepare()) or view request.record("authorization") return MatchInfo(resource, request, view)
async def move(context, request): try: data = await request.json() except Exception: data = {} destination = data.get('destination') if destination is None: destination_ob = context.__parent__ else: try: destination_ob = await navigate_to(request.container, destination) except KeyError: destination_ob = None if destination_ob is None: raise PreconditionFailed(context, 'Could not find destination object') old_id = context.id if 'new_id' in data: new_id = data['new_id'] context.id = context.__name__ = new_id else: new_id = context.id security = IInteraction(request) if not security.check_permission('guillotina.AddContent', destination_ob): raise PreconditionFailed( context, 'You do not have permission to add content to the ' 'destination object') if await destination_ob.async_contains(new_id): raise PreconditionFailed( context, f'Destination already has object with the id {new_id}') original_parent = context.__parent__ txn = get_transaction(request) cache_keys = txn._cache.get_cache_keys(context, 'deleted') data['id'] = new_id await notify( BeforeObjectMovedEvent(context, original_parent, old_id, destination_ob, new_id, payload=data)) context.__parent__ = destination_ob context._p_register() await notify( ObjectMovedEvent(context, original_parent, old_id, destination_ob, new_id, payload=data)) cache_keys += txn._cache.get_cache_keys(context, 'added') await txn._cache.delete_all(cache_keys) absolute_url = query_multi_adapter((context, request), IAbsoluteURL) return {'@url': absolute_url()}
async def __call__(self): """To create a content.""" data = await self.get_data() type_ = data.get('@type', None) id_ = data.get('id', None) behaviors = data.get('@behaviors', None) if not type_: raise ErrorResponse('RequiredParam', _("Property '@type' is required"), reason=error_reasons.REQUIRED_PARAM_MISSING, status=412) # Generate a temporary id if the id is not given new_id = None if not id_: generator = query_adapter(self.request, IIDGenerator) if generator is not None: new_id = generator(data) else: if not isinstance(id_, str) or not valid_id(id_): raise ErrorResponse('PreconditionFailed', str('Invalid id'), status=412, reason=error_reasons.INVALID_ID) new_id = id_ user = get_authenticated_user_id(self.request) options = {'creators': (user, ), 'contributors': (user, )} if 'uid' in data: options['_p_oid'] = data.pop('uid') # Create object try: obj = await create_content_in_container(self.context, type_, new_id, **options) except ValueError as e: return ErrorResponse('CreatingObject', str(e), status=412) for behavior in behaviors or (): obj.add_behavior(behavior) # Update fields deserializer = query_multi_adapter((obj, self.request), IResourceDeserializeFromJson) if deserializer is None: return ErrorResponse('DeserializationError', 'Cannot deserialize type {}'.format( obj.type_name), status=412, reason=error_reasons.DESERIALIZATION_FAILED) await deserializer(data, validate_all=True, create=True) # Local Roles assign owner as the creator user get_owner = get_utility(IGetOwner) roleperm = IPrincipalRoleManager(obj) owner = await get_owner(obj, user) if owner is not None: roleperm.assign_role_to_principal('guillotina.Owner', owner) data['id'] = obj.id await notify(ObjectAddedEvent(obj, self.context, obj.id, payload=data)) absolute_url = query_multi_adapter((obj, self.request), IAbsoluteURL) headers = { 'Access-Control-Expose-Headers': 'Location', 'Location': absolute_url() } serializer = query_multi_adapter((obj, self.request), IResourceSerializeToJsonSummary) response = await serializer() return Response(content=response, status=201, headers=headers)
async def test_view_registered_for_sub_path_matching(dummy_guillotina, dummy_request): view = query_multi_adapter((dummy_guillotina.root, dummy_request), name='@match//') assert view is not None
async def handle_ws_request(self, ws, message): method = app_settings["http_methods"]["GET"] try: frame_id = message["id"] except KeyError: frame_id = "0" parsed = parse.urlparse(message.get("path", message.get("value"))) path = tuple(p for p in parsed.path.split("/") if p) from guillotina.traversal import traverse obj, tail = await traverse(self.request, task_vars.container.get(), path) if tail and len(tail) > 0: # convert match lookups view_name = routes.path_to_view_name(tail) elif not tail: view_name = "" else: raise permission = get_utility(IPermission, name="guillotina.AccessContent") security = get_security_policy() allowed = security.check_permission(permission.id, obj) if not allowed: return await ws.send_str(ujson.dumps({"error": "Not allowed"})) try: view = query_multi_adapter((obj, self.request), method, name=view_name) except AttributeError: view = None try: view.__route__.matches(self.request, tail or []) except (KeyError, IndexError): view = None if view is None: return await ws.send_str(ujson.dumps({"error": "Not found", "id": frame_id})) ViewClass = view.__class__ view_permission = get_view_permission(ViewClass) if not security.check_permission(view_permission, view): return await ws.send_str(ujson.dumps({"error": "No view access", "id": frame_id})) if hasattr(view, "prepare"): view = (await view.prepare()) or view view_result = await view() if IAioHTTPResponse.providedBy(view_result): raise Exception("Do not accept raw aiohttp exceptions in ws") else: from guillotina.traversal import apply_rendering resp = await apply_rendering(view, self.request, view_result) # Return the value, body is always encoded response_object = ujson.dumps({"data": resp.body.decode("utf-8"), "id": frame_id}) await ws.send_str(response_object) # Wait for possible value self.request.execute_futures()
async def handle_ws_request(self, ws, message): method = app_settings['http_methods']['GET'] try: frame_id = message['id'] except KeyError: frame_id = '0' parsed = parse.urlparse(message.get('path', message.get('value'))) path = tuple(p for p in parsed.path.split('/') if p) from guillotina.traversal import traverse obj, tail = await traverse(self.request, self.request.container, path) if tail and len(tail) > 0: # convert match lookups view_name = routes.path_to_view_name(tail) elif not tail: view_name = '' else: raise permission = get_utility( IPermission, name='guillotina.AccessContent') security = get_adapter(self.request, IInteraction) allowed = security.check_permission(permission.id, obj) if not allowed: return await ws.send_str(ujson.dumps({ 'error': 'Not allowed' })) try: view = query_multi_adapter( (obj, self.request), method, name=view_name) except AttributeError: view = None try: view.__route__.matches(self.request, tail or []) except (KeyError, IndexError): view = None if view is None: return await ws.send_str(ujson.dumps({ 'error': 'Not found', 'id': frame_id })) ViewClass = view.__class__ view_permission = get_view_permission(ViewClass) if not security.check_permission(view_permission, view): return await ws.send_str(ujson.dumps({ 'error': 'No view access', 'id': frame_id })) if hasattr(view, 'prepare'): view = (await view.prepare()) or view view_result = await view() if IAioHTTPResponse.providedBy(view_result): raise Exception('Do not accept raw aiohttp exceptions in ws') else: from guillotina.traversal import apply_rendering resp = await apply_rendering(view, self.request, view_result) # Return the value, body is always encoded response_object = ujson.dumps({ 'data': resp.body.decode('utf-8'), 'id': frame_id }) await ws.send_str(response_object) # Wait for possible value self.request.execute_futures()
async def __call__(self): """To create a content.""" data = await self.get_data() type_ = data.get("@type", None) id_ = data.get("id", None) behaviors = data.get("@behaviors", None) if not type_: raise ErrorResponse( "RequiredParam", _("Property '@type' is required"), reason=error_reasons.REQUIRED_PARAM_MISSING, status=412, ) id_checker = get_adapter(self.context, IIDChecker) # Generate a temporary id if the id is not given new_id = None if not id_: generator = query_adapter(self.request, IIDGenerator) if generator is not None: new_id = await apply_coroutine(generator, data) if isinstance(new_id, str) and not await id_checker(new_id, type_): raise ErrorResponse( "PreconditionFailed", "Invalid id: {}".format(new_id), status=412, reason=error_reasons.INVALID_ID, ) else: if not isinstance(id_, str) or not await id_checker(id_, type_): raise ErrorResponse( "PreconditionFailed", "Invalid id: {}".format(id_), status=412, reason=error_reasons.INVALID_ID, ) new_id = id_ user = get_authenticated_user_id() options = {"creators": (user,), "contributors": (user,)} if "uid" in data: options["__uuid__"] = data.pop("uid") # Create object try: obj = await create_content_in_container( self.context, type_, new_id, check_constraints=True, **options ) except ValueError as e: return ErrorResponse("CreatingObject", str(e), status=412) for behavior in behaviors or (): obj.add_behavior(behavior) # Update fields deserializer = query_multi_adapter((obj, self.request), IResourceDeserializeFromJson) if deserializer is None: return ErrorResponse( "DeserializationError", "Cannot deserialize type {}".format(obj.type_name), status=412, reason=error_reasons.DESERIALIZATION_FAILED, ) await deserializer(data, validate_all=True, create=True) # Local Roles assign owner as the creator user get_owner = get_utility(IGetOwner) roleperm = IPrincipalRoleManager(obj) owner = await get_owner(obj, user) if owner is not None: roleperm.assign_role_to_principal("guillotina.Owner", owner) data["id"] = obj.id await notify(ObjectAddedEvent(obj, self.context, obj.id, payload=data)) headers = {"Access-Control-Expose-Headers": "Location", "Location": get_object_url(obj, self.request)} serializer = query_multi_adapter((obj, self.request), IResourceSerializeToJsonSummary) response = await serializer() return Response(content=response, status=201, headers=headers)
def _callFUT(self, *args, **kw): from guillotina.component import query_multi_adapter return query_multi_adapter(*args, **kw)
async def real_resolve(self, request: IRequest) -> Optional[MatchInfo]: """Main function to resolve a request.""" security = get_adapter(request, IInteraction) if request.method not in app_settings['http_methods']: raise HTTPMethodNotAllowed( method=request.method, allowed_methods=[k for k in app_settings['http_methods'].keys()]) method = app_settings['http_methods'][request.method] try: resource, tail = await self.traverse(request) except ConflictError: # can also happen from connection errors so we bubble this... raise except Exception as _exc: logger.error('Unhandled exception occurred', exc_info=True) request.resource = request.tail = None request.exc = _exc data = { 'success': False, 'exception_message': str(_exc), 'exception_type': getattr(type(_exc), '__name__', str(type(_exc))), # noqa } if app_settings.get('debug'): data['traceback'] = traceback.format_exc() raise HTTPBadRequest(content={ 'reason': data }) request.record('traversed') await notify(ObjectLoadedEvent(resource)) request.resource = resource request.tail = tail if request.resource is None: await notify(TraversalResourceMissEvent(request, tail)) raise HTTPNotFound(content={ "reason": 'Resource not found' }) if tail and len(tail) > 0: # convert match lookups view_name = routes.path_to_view_name(tail) elif not tail: view_name = '' request.record('beforeauthentication') await self.apply_authorization(request) request.record('authentication') for language in get_acceptable_languages(request): translator = query_adapter((resource, request), ILanguage, name=language) if translator is not None: resource = translator.translate() break # Add anonymous participation if len(security.participations) == 0: security.add(AnonymousParticipation(request)) # container registry lookup try: view = query_multi_adapter( (resource, request), method, name=view_name) except AttributeError: view = None if view is None and method == IOPTIONS: view = DefaultOPTIONS(resource, request) # Check security on context to AccessContent unless # is view allows explicit or its OPTIONS permission = get_utility(IPermission, name='guillotina.AccessContent') if not security.check_permission(permission.id, resource): # Check if its a CORS call: if IOPTIONS != method: # Check if the view has permissions explicit if view is None or not view.__allow_access__: logger.info( "No access content {content} with {auths}".format( content=resource, auths=str([x.principal.id for x in security.participations])), request=request) raise HTTPUnauthorized( content={ "reason": "You are not authorized to access content", "content": str(resource), "auths": [x.principal.id for x in security.participations] } ) if not view and len(tail) > 0: # we should have a view in this case because we are matching routes await notify(TraversalViewMissEvent(request, tail)) return None request.found_view = view request.view_name = view_name request.record('viewfound') ViewClass = view.__class__ view_permission = get_view_permission(ViewClass) if not security.check_permission(view_permission, view): if IOPTIONS != method: raise HTTPUnauthorized( content={ "reason": "You are not authorized to view", "content": str(resource), "auths": [x.principal.id for x in security.participations] } ) try: view.__route__.matches(request, tail or []) except (KeyError, IndexError): await notify(TraversalRouteMissEvent(request, tail)) return None except AttributeError: pass if hasattr(view, 'prepare'): view = (await view.prepare()) or view request.record('authorization') return MatchInfo(resource, request, view)