def page_not_found(response, request): """Return a JSON 404 error response.""" config_key = 'trailing_slash_redirect_enabled' redirect_enabled = request.registry.settings[config_key] trailing_slash_redirection_enabled = asbool(redirect_enabled) querystring = request.url[(request.url.rindex(request.path) + len(request.path)):] errno = ERRORS.MISSING_RESOURCE error_msg = 'The resource you are looking for could not be found.' if not request.path.startswith('/{}'.format(request.registry.route_prefix)): errno = ERRORS.VERSION_NOT_AVAILABLE error_msg = ('The requested API version is not available ' 'on this server.') elif trailing_slash_redirection_enabled: redirect = None if request.path.endswith('/'): path = request.path.rstrip('/') redirect = '{}{}'.format(path, querystring) elif request.path == '/{}'.format(request.registry.route_prefix): # Case for /v0 -> /v0/ redirect = '/{}/{}'.format(request.registry.route_prefix, querystring) if redirect: return reapply_cors(request, HTTPTemporaryRedirect(redirect)) if response.content_type != 'application/json': response = http_error(httpexceptions.HTTPNotFound(), errno=errno, message=error_msg) return reapply_cors(request, response)
def page_not_found(response, request): """Return a JSON 404 error response.""" config_key = "trailing_slash_redirect_enabled" redirect_enabled = request.registry.settings[config_key] trailing_slash_redirection_enabled = asbool(redirect_enabled) querystring = request.url[(request.url.rindex(request.path) + len(request.path)) :] errno = ERRORS.MISSING_RESOURCE error_msg = "The resource you are looking for could not be found." if not request.path.startswith(f"/{request.registry.route_prefix}"): errno = ERRORS.VERSION_NOT_AVAILABLE error_msg = "The requested API version is not available " "on this server." elif trailing_slash_redirection_enabled: redirect = None if request.path.endswith("/"): path = request.path.rstrip("/") redirect = f"{path}{querystring}" elif request.path == f"/{request.registry.route_prefix}": # Case for /v0 -> /v0/ redirect = f"/{request.registry.route_prefix}/{querystring}" if redirect: return reapply_cors(request, HTTPTemporaryRedirect(redirect)) if response.content_type != "application/json": response = http_error(httpexceptions.HTTPNotFound(), errno=errno, message=error_msg) return reapply_cors(request, response)
def _redirect_to_version_view(request): if request.method.lower() == "options": # CORS responses should always have status 200. return utils.reapply_cors(request, Response()) querystring = request.url[(request.url.rindex(request.path) + len(request.path)) :] redirect = "/{}{}{}".format(route_prefix, request.path, querystring) raise HTTPTemporaryRedirect(redirect)
def method_not_allowed(context, request): if context.content_type == 'application/json': return context response = http_error(context, errno=ERRORS.METHOD_NOT_ALLOWED, message='Method not allowed on this endpoint.') return reapply_cors(request, response)
def _redirect_to_version_view(request): if request.method.lower() == 'options': # CORS responses should always have status 200. return utils.reapply_cors(request, Response()) querystring = request.url[(request.url.rindex(request.path) + len(request.path)):] redirect = '/{}{}{}'.format(route_prefix, request.path, querystring) raise HTTPTemporaryRedirect(redirect)
def error(context, request): """Catch server errors and trace them.""" if isinstance(context, httpexceptions.Response): return reapply_cors(request, context) if isinstance(context, storage_exceptions.IntegrityError): error_msg = 'Integrity constraint violated, please retry.' response = http_error(httpexceptions.HTTPConflict(), errno=ERRORS.CONSTRAINT_VIOLATED, message=error_msg) retry_after = request.registry.settings['retry_after_seconds'] response.headers['Retry-After'] = str(retry_after) return reapply_cors(request, response) # Log some information about current request. extra = { 'path': request.path, 'method': request.method, } qs = dict(request_GET(request)) if qs: extra['querystring'] = qs # Take errno from original exception, or undefined if unknown/unhandled. try: extra['errno'] = context.errno.value except AttributeError: extra['errno'] = ERRORS.UNDEFINED.value if isinstance(context, storage_exceptions.BackendError): logger.critical(context.original, extra=extra, exc_info=context) response = httpexceptions.HTTPServiceUnavailable() return service_unavailable(response, request) # Within the exception view, sys.exc_info() will return null. # see https://github.com/python/cpython/blob/ce9e62544/Lib/logging/__init__.py#L1460-L1462 logger.error(context, extra=extra, exc_info=context) error_msg = 'A programmatic error occured, developers have been informed.' info = request.registry.settings['error_info_link'] response = http_error(httpexceptions.HTTPInternalServerError(), message=error_msg, info=info) return reapply_cors(request, response)
def _redirect_to_version_view(request): if request.method.lower() == 'options': # CORS responses should always have status 200. return utils.reapply_cors(request, Response()) path = request.matchdict['path'] querystring = request.url[(request.url.rindex(request.path) + len(request.path)):] redirect = '/%s/%s%s' % (route_prefix, path, querystring) raise HTTPTemporaryRedirect(redirect)
def error(context, request): """Catch server errors and trace them.""" if isinstance(context, httpexceptions.Response): return reapply_cors(request, context) if isinstance(context, storage_exceptions.BackendError): logger.critical(context.original, exc_info=True) response = httpexceptions.HTTPServiceUnavailable() return service_unavailable(response, request) logger.error(context, exc_info=True) error_msg = "A programmatic error occured, developers have been informed." info = request.registry.settings['error_info_link'] response = http_error(httpexceptions.HTTPInternalServerError(), message=error_msg, info=info) return reapply_cors(request, response)
def service_unavailable(response, request): if response.content_type != 'application/json': error_msg = ('Service temporary unavailable ' 'due to overloading or maintenance, please retry later.') response = http_error(response, errno=ERRORS.BACKEND, message=error_msg) retry_after = request.registry.settings['retry_after_seconds'] response.headers['Retry-After'] = str(retry_after) return reapply_cors(request, response)
def _redirect_to_version_view(request): if request.method.lower() == "options": # CORS responses should always have status 200. return utils.reapply_cors(request, Response()) querystring = request.url[(request.url.rindex(request.path) + len(request.path)) :] redirect = f"/{route_prefix}{request.path}{querystring}" resp = HTTPTemporaryRedirect(redirect) if cache_seconds >= 0: resp.cache_expires(cache_seconds) raise resp
def default_bucket(request): if request.method.lower() == 'options': path = request.path.replace('default', 'unknown') subrequest = build_request(request, { 'method': 'OPTIONS', 'path': path }) return request.invoke_subrequest(subrequest) if Authenticated not in request.effective_principals: # Pass through the forbidden_view_config raise httpexceptions.HTTPForbidden() settings = request.registry.settings if asbool(settings['readonly']): raise httpexceptions.HTTPMethodNotAllowed() bucket_id = request.default_bucket_id path = request.path.replace('/buckets/default', '/buckets/%s' % bucket_id) querystring = request.url[(request.url.index(request.path) + len(request.path)):] try: # If 'id' is provided as 'default', replace with actual bucket id. body = request.json body['data']['id'] = body['data']['id'].replace('default', bucket_id) except: body = request.body # Make sure bucket exists create_bucket(request, bucket_id) # Make sure the collection exists create_collection(request, bucket_id) subrequest = build_request(request, { 'method': request.method, 'path': path + querystring, 'body': body, }) subrequest.bound_data = request.bound_data try: response = request.invoke_subrequest(subrequest) except httpexceptions.HTTPException as error: is_redirect = error.status_code < 400 if error.content_type == 'application/json' or is_redirect: response = reapply_cors(subrequest, error) else: # Ask the upper level to format the error. raise error return response
def default_bucket(request): if request.method.lower() == 'options': path = request.path.replace('default', 'unknown') subrequest = build_request(request, { 'method': 'OPTIONS', 'path': path }) return request.invoke_subrequest(subrequest) if Authenticated not in request.effective_principals: # Pass through the forbidden_view_config raise httpexceptions.HTTPForbidden() settings = request.registry.settings if asbool(settings['readonly']): raise httpexceptions.HTTPMethodNotAllowed() bucket_id = request.default_bucket_id # Implicit object creations. # Make sure bucket exists create_bucket(request, bucket_id) # Make sure the collection exists create_collection(request, bucket_id) path = request.path.replace('/buckets/default', '/buckets/{}'.format(bucket_id)) querystring = request.url[(request.url.index(request.path) + len(request.path)):] try: # If 'id' is provided as 'default', replace with actual bucket id. body = request.json body['data']['id'] = body['data']['id'].replace('default', bucket_id) except: body = request.body or {"data": {}} subrequest = build_request(request, { 'method': request.method, 'path': path + querystring, 'body': body, }) subrequest.bound_data = request.bound_data try: response = request.invoke_subrequest(subrequest) except httpexceptions.HTTPException as error: is_redirect = error.status_code < 400 if error.content_type == 'application/json' or is_redirect: response = reapply_cors(subrequest, error) else: # Ask the upper level to format the error. raise error return response
def default_bucket(request): if request.method.lower() == "options": path = request.path.replace("default", "unknown") subrequest = build_request(request, { "method": "OPTIONS", "path": path }) return request.invoke_subrequest(subrequest) if Authenticated not in request.effective_principals: # Pass through the forbidden_view_config raise httpexceptions.HTTPForbidden() settings = request.registry.settings if asbool(settings["readonly"]): raise httpexceptions.HTTPMethodNotAllowed() bucket_id = request.default_bucket_id # Implicit object creations. # Make sure bucket exists create_bucket(request, bucket_id) # Make sure the collection exists create_collection(request, bucket_id) path = request.path.replace("/buckets/default", f"/buckets/{bucket_id}") querystring = request.url[(request.url.index(request.path) + len(request.path)):] try: # If 'id' is provided as 'default', replace with actual bucket id. body = request.json body["data"]["id"] = body["data"]["id"].replace("default", bucket_id) except Exception: body = request.body or {"data": {}} subrequest = build_request(request, { "method": request.method, "path": path + querystring, "body": body }) subrequest.bound_data = request.bound_data try: response = request.invoke_subrequest(subrequest) except httpexceptions.HTTPException as error: is_redirect = error.status_code < 400 if error.content_type == "application/json" or is_redirect: response = reapply_cors(subrequest, error) else: # Ask the upper level to format the error. raise error return response
def default_bucket(request): if request.method.lower() == "options": path = request.path.replace("default", "unknown") subrequest = build_request(request, {"method": "OPTIONS", "path": path}) return request.invoke_subrequest(subrequest) if Authenticated not in request.effective_principals: # Pass through the forbidden_view_config raise httpexceptions.HTTPForbidden() settings = request.registry.settings if asbool(settings["readonly"]): raise httpexceptions.HTTPMethodNotAllowed() bucket_id = request.default_bucket_id # Implicit object creations. # Make sure bucket exists create_bucket(request, bucket_id) # Make sure the collection exists create_collection(request, bucket_id) path = request.path.replace("/buckets/default", "/buckets/{}".format(bucket_id)) querystring = request.url[(request.url.index(request.path) + len(request.path)) :] try: # If 'id' is provided as 'default', replace with actual bucket id. body = request.json body["data"]["id"] = body["data"]["id"].replace("default", bucket_id) except Exception: body = request.body or {"data": {}} subrequest = build_request( request, {"method": request.method, "path": path + querystring, "body": body} ) subrequest.bound_data = request.bound_data try: response = request.invoke_subrequest(subrequest) except httpexceptions.HTTPException as error: is_redirect = error.status_code < 400 if error.content_type == "application/json" or is_redirect: response = reapply_cors(subrequest, error) else: # Ask the upper level to format the error. raise error return response
def json_error_handler(request): """Cornice JSON error handler, returning consistant JSON formatted errors from schema validation errors. This is meant to be used is custom services in your applications. .. code-block:: python upload = Service(name="upload", path='/upload', error_handler=errors.json_error_handler) .. warning:: Only the first error of the list is formatted in the response. (c.f. HTTP API). """ errors = request.errors sorted_errors = sorted(errors, key=lambda x: str(x["name"])) # In Cornice, we call error handler if at least one error was set. error = sorted_errors[0] name = error["name"] description = error["description"] if isinstance(description, bytes): description = error["description"].decode("utf-8") if name is not None: if str(name) in description: message = description else: message = "{name} in {location}: {description}".format_map(error) else: message = "{location}: {description}".format_map(error) response = http_error( httpexceptions.HTTPBadRequest(), code=errors.status, errno=ERRORS.INVALID_PARAMETERS.value, error="Invalid parameters", message=message, details=errors, ) response.status = errors.status response = reapply_cors(request, response) return response
def json_error_handler(request): """Cornice JSON error handler, returning consistant JSON formatted errors from schema validation errors. This is meant to be used is custom services in your applications. .. code-block:: python upload = Service(name="upload", path='/upload', error_handler=errors.json_error_handler) .. warning:: Only the first error of the list is formatted in the response. (c.f. HTTP API). """ errors = request.errors sorted_errors = sorted(errors, key=lambda x: str(x["name"])) # In Cornice, we call error handler if at least one error was set. error = sorted_errors[0] name = error["name"] description = error["description"] if isinstance(description, bytes): description = error["description"].decode("utf-8") if name is not None: if name in description: message = description else: message = "{name} in {location}: {description}".format_map(error) else: message = "{location}: {description}".format_map(error) response = http_error( httpexceptions.HTTPBadRequest(), code=errors.status, errno=ERRORS.INVALID_PARAMETERS.value, error="Invalid parameters", message=message, details=errors, ) response.status = errors.status response = reapply_cors(request, response) return response
def authorization_required(response, request): """Distinguish authentication required (``401 Unauthorized``) from not allowed (``403 Forbidden``). """ if Authenticated not in request.effective_principals: error_msg = "Please authenticate yourself to use this endpoint." response = http_error(httpexceptions.HTTPUnauthorized(), errno=ERRORS.MISSING_AUTH_TOKEN, message=error_msg) response.headers.extend(forget(request)) return response if response.content_type != "application/json": error_msg = "This user cannot access this resource." response = http_error(httpexceptions.HTTPForbidden(), errno=ERRORS.FORBIDDEN, message=error_msg) return reapply_cors(request, response)
def json_error_handler(request): """Cornice JSON error handler, returning consistant JSON formatted errors from schema validation errors. This is meant to be used is custom services in your applications. .. code-block:: python upload = Service(name="upload", path='/upload', error_handler=errors.json_error_handler) .. warning:: Only the first error of the list is formatted in the response. (c.f. HTTP API). """ errors = request.errors assert len(errors) != 0 sorted_errors = sorted(errors, key=lambda x: six.text_type(x['name'])) error = sorted_errors[0] name = error['name'] description = error['description'] if isinstance(description, six.binary_type): description = error['description'].decode('utf-8') if name is not None: if name in description: message = description else: message = '%(name)s in %(location)s: %(description)s' % error else: message = '%(location)s: %(description)s' % error response = http_error(httpexceptions.HTTPBadRequest(), code=errors.status, errno=ERRORS.INVALID_PARAMETERS.value, error='Invalid parameters', message=message, details=errors) response.status = errors.status response = reapply_cors(request, response) return response
def json_error_handler(errors): """Cornice JSON error handler, returning consistant JSON formatted errors from schema validation errors. This is meant to be used is custom services in your applications. .. code-block:: python upload = Service(name="upload", path='/upload', error_handler=errors.json_error_handler) .. warning:: Only the first error of the list is formatted in the response. (c.f. protocol). """ assert len(errors) != 0 sorted_errors = sorted(errors, key=lambda x: six.text_type(x['name'])) error = sorted_errors[0] name = error['name'] description = error['description'] if isinstance(description, six.binary_type): description = error['description'].decode('utf-8') if name is not None: if name in description: message = description else: message = '%(name)s in %(location)s: %(description)s' % error else: message = '%(location)s: %(description)s' % error response = http_error(httpexceptions.HTTPBadRequest(), code=errors.status, errno=ERRORS.INVALID_PARAMETERS.value, error='Invalid parameters', message=message, details=errors) response.status = errors.status response = reapply_cors(errors.request, response) return response
def eos_tween(request): eos_date = registry.settings["eos"] eos_url = registry.settings["eos_url"] eos_message = registry.settings["eos_message"] if not eos_date: return handler(request) eos_date = dateparser.parse(eos_date) if eos_date > datetime.now(): code = "soft-eol" request.response = handler(request) else: code = "hard-eol" request.response = errors.http_error( HTTPGone(), errno=errors.ERRORS.SERVICE_DEPRECATED, message=deprecation_msg ) errors.send_alert(request, eos_message, url=eos_url, code=code) return utils.reapply_cors(request, request.response)
def wrap_view(request, *args, **kwargs): response = view(request, *args, **kwargs) # We need to re-apply the CORS checks done by Cornice, since we're # recreating the response from scratch. return reapply_cors(request, response)