def _assert_if_match(request: web.Request, etag: T.Union[None, bool, str], require: bool): # The If-Match: header is commonly used in non-safe HTTP requests to prevent # lost update problems. etags = _parse_if_header(request, 'If-Match') if etags is None: if require: raise web.HTTPPreconditionRequired(text='If-Match') return if etags is _STAR: if etag is None or etag is False: raise web.HTTPPreconditionFailed() if etag is True or require is False: return raise web.HTTPPreconditionRequired( text='If-Match: * is too generic for this resource.') # From here on, `etags` can only be a set(). if etag is True or etag is False: raise web.HTTPPreconditionFailed( text="Resource doesn't support If-Match header.") # From here on, `etag` can only be `None` or a valid ETag string: if etag is None: raise web.HTTPNotFound() if etag not in etags: raise web.HTTPPreconditionFailed()
def abort(code): if code == 400: return web.HTTPBadRequest() elif code == 401: return web.HTTPUnauthorized() elif code == 402: return web.HTTPPaymentRequired() elif code == 403: return web.HTTPForbidden() elif code == 404: return web.HTTPNotFound() elif code == 405: return web.HTTPMethodNotAllowed() elif code == 406: return web.HTTPNotAcceptable() elif code == 407: return web.HTTPProxyAuthenticationRequired() elif code == 408: return web.HTTPRequestTimeout() elif code == 409: return web.HTTPConflict() elif code == 410: return web.HTTPGone() elif code == 411: return web.HTTPLengthRequired() elif code == 412: return web.HTTPPreconditionFailed() elif code == 413: return web.HTTPRequestEntityTooLarge() elif code == 414: return web.HTTPRequestURITooLong() elif code == 415: return web.HTTPUnsupportedMediaType() elif code == 416: return web.HTTPRequestRangeNotSatisfiable() elif code == 417: return web.HTTPExpectationFailed() elif code == 421: return web.HTTPMisdirectedRequest() elif code == 422: return web.HTTPUnprocessableEntity() elif code == 424: return web.HTTPFailedDependency() elif code == 426: return web.HTTPUpgradeRequired() elif code == 428: return web.HTTPPreconditionRequired() elif code == 429: return web.HTTPTooManyRequests() elif code == 431: return web.HTTPRequestHeaderFieldsTooLarge() elif code == 451: return web.HTTPUnavailableForLegalReasons() else: return web.HTTPBadRequest()
def _assert_if_none_match(request: web.Request, etag: T.Union[None, bool, str], require: bool): # The If-None-Match: header is used in two scenarios: # 1. GET requests by a caching client. In this case, the client will # normally provide a list of (cached) ETags. # 2. PUT requests, where the client intends to create a new resource and # wants to avoid overwriting an existing resource. In this case, the # client will normally provide only the asterisk "*" character. etags = _parse_if_header(request, 'If-None-Match') if require and etags is None: raise web.HTTPPreconditionRequired(text='If-None-Match') if etags is None or etag is False or etag is None: return if etags is _STAR: raise web.HTTPPreconditionFailed() # From here on, we know that etags is a set of strings. if etag is True: raise web.HTTPPreconditionFailed( text="Resource doesn't support ETags.") # From here on, we know that etag is a string: if etag in etags: if request.method in {'GET', 'HEAD'}: raise web.HTTPNotModified() else: raise web.HTTPPreconditionFailed()
async def put(self): if_match = self.request.headers.get('if-match', '') if_none_match = self.request.headers.get('if-none-match', '') if if_match == '' and if_none_match == '': raise web.HTTPPreconditionRequired() assert_preconditions(self.request, await self.etag()) if not re.match(r'application/(?:hal\+)?json(?:$|;)', self.request.content_type): raise web.HTTPUnsupportedMediaType() try: request_body_json = json_loads(await self.request.text()) except: raise web.HTTPBadRequest() # self.request.app['swagger'].validate_definition('Account', request_body_json) existing_roles = set( self.request.app['config']['authz_admin']['roles'].keys()) try: roles = request_body_json['_links']['role'] assert isinstance(roles, list) except: raise web.HTTPBadRequest( text="No '#/_links/role' array in request.") from None new_roles = set() try: for link_object in roles: role = web.URL(link_object['href']).name assert role in existing_roles new_roles.add(role) except: raise web.HTTPBadRequest( text= "Not all roles are valid HALJSON link objects to an existing role." ) from None if await self.data() is None: try: log_id = await database.create_account(self.request, self['account'], new_roles) except database.PreconditionFailed: raise web.HTTPPreconditionFailed() from None status = 201 headers = { 'Location': self.rel_url.raw_path, 'ETag': etag_from_int(log_id) } else: try: log_id = await database.update_account(self.request, self, new_roles) except database.PreconditionFailed: raise web.HTTPPreconditionFailed() from None status = 204 headers = {'ETag': etag_from_int(log_id)} return web.Response(status=status, headers=headers)
async def wrapper(self, *args, **kwargs): etag = await self.etag() request = self.request if_match = _assert_if_match(request=request, etag=etag, deny_asterisk=required, allow_weak=allow_weak) if_none_match = _assert_if_none_match(request=request, etag=etag, allow_weak=allow_weak) safe_methods = (_HTTP_SAFE_METHODS_INCL_POST if post_is_safe else _HTTP_SAFE_METHODS_EXCL_POST) if request.method not in safe_methods and required and \ not if_match and not if_none_match: raise web.HTTPPreconditionRequired() return await f(self, *args, **kwargs)
def error(self, request: web.Request = None, response: dict = {}, exception: Exception = None, state: int = 400, headers: dict = {}, **kwargs) -> web.Response: # TODO: process the exception object response_obj = {"status": "Failed"} if not request: request = self.request if exception: response_obj["reason"] = str(exception) args = {**kwargs} if isinstance(response, dict): response_obj = {**response_obj, **response} args["content_type"] = "application/json" args["text"] = json.dumps(response_obj) else: args["body"] = response # defining the error if state == 400: # bad request obj = web.HTTPBadRequest(**args) elif state == 401: # unauthorized obj = web.HTTPUnauthorized(**args) elif state == 403: # forbidden obj = web.HTTPForbidden(**args) elif state == 404: # not found obj = web.HTTPNotFound(**args) elif state == 406: obj = web.HTTPNotAcceptable(**args) elif state == 412: obj = web.HTTPPreconditionFailed(**args) elif state == 428: obj = web.HTTPPreconditionRequired(**args) else: obj = web.HTTPBadRequest(**args) for header, value in headers.items(): obj.headers[header] = value return obj