def dispatch(self, obj, action, *args, **kwargs): """Find action-specific method on self and call it.""" try: method = getattr(obj, action) if isinstance(args[0], Request): ser_name = "%s[%s]" % (method.__module__, method.__name__) tomograph.start_http(ser_name, method.__name__, args[0]) tomograph.stop(method.__name__) except AttributeError: method = getattr(obj, 'default') return method(*args, **kwargs)
def process_request(self, request): """Process request. Evaluate the headers in a request and attempt to authenticate the request against the identity server. If authenticated then additional headers are added to the request for use by applications. If not authenticated the request will be rejected or marked unauthenticated depending on configuration. """ # pdb.set_trace() tomograph.start_http( "keystonemiddleware.auth_token.AuthProtocol[process_request]", "process_request", request) tomograph.add_trace_info_header(request.headers) self._token_cache.initialize(request.environ) resp = super(AuthProtocol, self).process_request(request) tomograph.stop("process_request") if resp: return resp if not request.user_token: # if no user token is present then that's an invalid request request.user_token_valid = False # NOTE(jamielennox): The service status is allowed to be missing if a # service token is not passed. If the service status is missing that's # a valid request. We should find a better way to expose this from the # request object. user_status = request.user_token and request.user_token_valid service_status = request.headers.get('X-Service-Identity-Status', 'Confirmed') if not (user_status and service_status == 'Confirmed'): if self._delay_auth_decision: self.log.info(_LI('Deferring reject downstream')) else: self.log.info(_LI('Rejecting request')) self._reject_request() if request.user_token_valid: request.set_user_headers(request.token_auth._user_auth_ref, self._include_service_catalog) if request.service_token and request.service_token_valid: request.set_service_headers(request.token_auth._serv_auth_ref) if self.log.isEnabledFor(logging.DEBUG): self.log.debug('Received request from %s', request.token_auth._log_format)
def process_request(self, request): """Process request. Evaluate the headers in a request and attempt to authenticate the request against the identity server. If authenticated then additional headers are added to the request for use by applications. If not authenticated the request will be rejected or marked unauthenticated depending on configuration. """ # pdb.set_trace() tomograph.start_http("keystonemiddleware.auth_token.AuthProtocol[process_request]", "process_request", request) tomograph.add_trace_info_header(request.headers) self._token_cache.initialize(request.environ) resp = super(AuthProtocol, self).process_request(request) tomograph.stop("process_request") if resp: return resp if not request.user_token: # if no user token is present then that's an invalid request request.user_token_valid = False # NOTE(jamielennox): The service status is allowed to be missing if a # service token is not passed. If the service status is missing that's # a valid request. We should find a better way to expose this from the # request object. user_status = request.user_token and request.user_token_valid service_status = request.headers.get("X-Service-Identity-Status", "Confirmed") if not (user_status and service_status == "Confirmed"): if self._delay_auth_decision: self.log.info(_LI("Deferring reject downstream")) else: self.log.info(_LI("Rejecting request")) self._reject_request() if request.user_token_valid: request.set_user_headers(request.token_auth._user_auth_ref, self._include_service_catalog) if request.service_token and request.service_token_valid: request.set_service_headers(request.token_auth._serv_auth_ref) if self.log.isEnabledFor(logging.DEBUG): self.log.debug("Received request from %s", request.token_auth._log_format)
def __call__(self, req): arg_dict = req.environ['wsgiorg.routing_args'][1] action = arg_dict.pop('action') del arg_dict['controller'] # allow middleware up the stack to provide context, params and headers. context = req.environ.get(CONTEXT_ENV, {}) context['query_string'] = dict(req.params.items()) context['headers'] = dict(req.headers.items()) context['path'] = req.environ['PATH_INFO'] scheme = (None if not CONF.secure_proxy_ssl_header else req.environ.get(CONF.secure_proxy_ssl_header)) if scheme: # NOTE(andrey-mp): "wsgi.url_scheme" contains the protocol used # before the proxy removed it ('https' usually). So if # the webob.Request instance is modified in order to use this # scheme instead of the one defined by API, the call to # webob.Request.relative_url() will return a URL with the correct # scheme. req.environ['wsgi.url_scheme'] = scheme context['host_url'] = req.host_url params = req.environ.get(PARAMS_ENV, {}) # authentication and authorization attributes are set as environment # values by the container and processed by the pipeline. the complete # set is not yet know. context['environment'] = req.environ context['accept_header'] = req.accept req.environ = None params.update(arg_dict) context.setdefault('is_admin', False) # TODO(termie): do some basic normalization on methods method = getattr(self, action) # NOTE(morganfainberg): use the request method to normalize the # response code between GET and HEAD requests. The HTTP status should # be the same. LOG.info( '%(req_method)s %(uri)s', { 'req_method': req.environ['REQUEST_METHOD'].upper(), 'uri': wsgiref.util.request_uri(req.environ), }) params = self._normalize_dict(params) try: ser_name = "%s[%s]" % (method.__module__, method.__name__) tomograph.start_http(ser_name, method.__name__, req) result = method(context, **params) tomograph.stop(method.__name__) except exception.Unauthorized as e: LOG.warning( _LW("Authorization failed. %(exception)s from " "%(remote_addr)s"), { 'exception': e, 'remote_addr': req.environ['REMOTE_ADDR'] }) return render_exception(e, context=context, user_locale=best_match_language(req)) except exception.Error as e: LOG.warning(six.text_type(e)) return render_exception(e, context=context, user_locale=best_match_language(req)) except TypeError as e: LOG.exception(six.text_type(e)) return render_exception(exception.ValidationError(e), context=context, user_locale=best_match_language(req)) except Exception as e: LOG.exception(six.text_type(e)) return render_exception(exception.UnexpectedError(exception=e), context=context, user_locale=best_match_language(req)) if result is None: return render_response(status=(204, 'No Content')) elif isinstance(result, six.string_types): return result elif isinstance(result, webob.Response): return result elif isinstance(result, webob.exc.WSGIHTTPException): return result response_code = self._get_response_code(req) return render_response(body=result, status=response_code, method=req.environ['REQUEST_METHOD'])
def _process_stack(self, request, action, action_args, content_type, body, accept): """Implement the processing stack.""" # Get the implementing method try: meth, extensions = self.get_method(request, action, content_type, body) except (AttributeError, TypeError): return Fault(webob.exc.HTTPNotFound()) except KeyError as ex: msg = _("There is no such action: %s") % ex.args[0] return Fault(webob.exc.HTTPBadRequest(explanation=msg)) except exception.MalformedRequestBody: msg = _("Malformed request body") return Fault(webob.exc.HTTPBadRequest(explanation=msg)) ser_name = "%s[%s]" % (meth.__module__, meth.__name__) tomograph.start_http(ser_name, meth.__name__, request) if body: msg = _("Action: '%(action)s', calling method: %(meth)s, body: " "%(body)s") % {'action': action, 'body': six.text_type(body, 'utf-8'), 'meth': str(meth)} LOG.debug(strutils.mask_password(msg)) else: LOG.debug("Calling method '%(meth)s'", {'meth': str(meth)}) # Now, deserialize the request body... try: contents = {} if self._should_have_body(request): # allow empty body with PUT and POST if request.content_length == 0: contents = {'body': None} else: contents = self.deserialize(meth, content_type, body) except exception.InvalidContentType: msg = _("Unsupported Content-Type") tomograph.tag("Exception", "Unsupported Content-Type") tomograph.stop(meth.__name__) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) except exception.MalformedRequestBody: msg = _("Malformed request body") tomograph.tag("Exception", "Malformed request body") tomograph.stop(meth.__name__) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) # Update the action args action_args.update(contents) project_id = action_args.pop("project_id", None) context = request.environ.get('nova.context') if (context and project_id and (project_id != context.project_id)): msg = _("Malformed request URL: URL's project_id '%(project_id)s'" " doesn't match Context's project_id" " '%(context_project_id)s'") % \ {'project_id': project_id, 'context_project_id': context.project_id} tomograph.tag("Exception", "HTTPBadRequest") tomograph.stop(meth.__name__) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) # Run pre-processing extensions response, post = self.pre_process_extensions(extensions, request, action_args) if not response: try: with ResourceExceptionHandler(): action_result = self.dispatch(meth, request, action_args) except Fault as ex: response = ex if not response: # No exceptions; convert action_result into a # ResponseObject resp_obj = None if type(action_result) is dict or action_result is None: resp_obj = ResponseObject(action_result) elif isinstance(action_result, ResponseObject): resp_obj = action_result else: response = action_result # Run post-processing extensions if resp_obj: # Do a preserialize to set up the response object serializers = getattr(meth, 'wsgi_serializers', {}) resp_obj._bind_method_serializers(serializers) if hasattr(meth, 'wsgi_code'): resp_obj._default_code = meth.wsgi_code resp_obj.preserialize(accept, self.default_serializers) # Process post-processing extensions response = self.post_process_extensions(post, resp_obj, request, action_args) if resp_obj and not response: response = resp_obj.serialize(request, accept, self.default_serializers) if hasattr(response, 'headers'): for hdr, val in response.headers.items(): # Headers must be utf-8 strings response.headers[hdr] = utils.utf8(str(val)) if not request.api_version_request.is_null(): response.headers[API_VERSION_REQUEST_HEADER] = \ request.api_version_request.get_string() response.headers['Vary'] = API_VERSION_REQUEST_HEADER tomograph.stop(meth.__name__) return response
def __call__(self, req): arg_dict = req.environ['wsgiorg.routing_args'][1] action = arg_dict.pop('action') del arg_dict['controller'] # allow middleware up the stack to provide context, params and headers. context = req.environ.get(CONTEXT_ENV, {}) context['query_string'] = dict(req.params.items()) context['headers'] = dict(req.headers.items()) context['path'] = req.environ['PATH_INFO'] scheme = (None if not CONF.secure_proxy_ssl_header else req.environ.get(CONF.secure_proxy_ssl_header)) if scheme: # NOTE(andrey-mp): "wsgi.url_scheme" contains the protocol used # before the proxy removed it ('https' usually). So if # the webob.Request instance is modified in order to use this # scheme instead of the one defined by API, the call to # webob.Request.relative_url() will return a URL with the correct # scheme. req.environ['wsgi.url_scheme'] = scheme context['host_url'] = req.host_url params = req.environ.get(PARAMS_ENV, {}) # authentication and authorization attributes are set as environment # values by the container and processed by the pipeline. the complete # set is not yet know. context['environment'] = req.environ context['accept_header'] = req.accept req.environ = None params.update(arg_dict) context.setdefault('is_admin', False) # TODO(termie): do some basic normalization on methods method = getattr(self, action) # NOTE(morganfainberg): use the request method to normalize the # response code between GET and HEAD requests. The HTTP status should # be the same. LOG.info('%(req_method)s %(uri)s', { 'req_method': req.environ['REQUEST_METHOD'].upper(), 'uri': wsgiref.util.request_uri(req.environ), }) params = self._normalize_dict(params) try: ser_name = "%s[%s]" % (method.__module__, method.__name__) tomograph.start_http(ser_name, method.__name__, req) result = method(context, **params) tomograph.stop(method.__name__) except exception.Unauthorized as e: LOG.warning( _LW("Authorization failed. %(exception)s from " "%(remote_addr)s"), {'exception': e, 'remote_addr': req.environ['REMOTE_ADDR']}) return render_exception(e, context=context, user_locale=best_match_language(req)) except exception.Error as e: LOG.warning(six.text_type(e)) return render_exception(e, context=context, user_locale=best_match_language(req)) except TypeError as e: LOG.exception(six.text_type(e)) return render_exception(exception.ValidationError(e), context=context, user_locale=best_match_language(req)) except Exception as e: LOG.exception(six.text_type(e)) return render_exception(exception.UnexpectedError(exception=e), context=context, user_locale=best_match_language(req)) if result is None: return render_response(status=(204, 'No Content')) elif isinstance(result, six.string_types): return result elif isinstance(result, webob.Response): return result elif isinstance(result, webob.exc.WSGIHTTPException): return result response_code = self._get_response_code(req) return render_response(body=result, status=response_code, method=req.environ['REQUEST_METHOD'])
def _process_stack(self, request, action, action_args, content_type, body, accept): """Implement the processing stack.""" # Get the implementing method try: meth, extensions = self.get_method(request, action, content_type, body) except (AttributeError, TypeError): return Fault(webob.exc.HTTPNotFound()) except KeyError as ex: msg = _("There is no such action: %s") % ex.args[0] return Fault(webob.exc.HTTPBadRequest(explanation=msg)) except exception.MalformedRequestBody: msg = _("Malformed request body") return Fault(webob.exc.HTTPBadRequest(explanation=msg)) ser_name = "%s[%s]" % (meth.__module__, meth.__name__) tomograph.start_http(ser_name, meth.__name__, request) if body: msg = _("Action: '%(action)s', calling method: %(meth)s, body: " "%(body)s") % { 'action': action, 'body': six.text_type(body, 'utf-8'), 'meth': str(meth) } LOG.debug(strutils.mask_password(msg)) else: LOG.debug("Calling method '%(meth)s'", {'meth': str(meth)}) # Now, deserialize the request body... try: contents = {} if self._should_have_body(request): # allow empty body with PUT and POST if request.content_length == 0: contents = {'body': None} else: contents = self.deserialize(meth, content_type, body) except exception.InvalidContentType: msg = _("Unsupported Content-Type") tomograph.tag("Exception", "Unsupported Content-Type") tomograph.stop(meth.__name__) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) except exception.MalformedRequestBody: msg = _("Malformed request body") tomograph.tag("Exception", "Malformed request body") tomograph.stop(meth.__name__) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) # Update the action args action_args.update(contents) project_id = action_args.pop("project_id", None) context = request.environ.get('nova.context') if (context and project_id and (project_id != context.project_id)): msg = _("Malformed request URL: URL's project_id '%(project_id)s'" " doesn't match Context's project_id" " '%(context_project_id)s'") % \ {'project_id': project_id, 'context_project_id': context.project_id} tomograph.tag("Exception", "HTTPBadRequest") tomograph.stop(meth.__name__) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) # Run pre-processing extensions response, post = self.pre_process_extensions(extensions, request, action_args) if not response: try: with ResourceExceptionHandler(): action_result = self.dispatch(meth, request, action_args) except Fault as ex: response = ex if not response: # No exceptions; convert action_result into a # ResponseObject resp_obj = None if type(action_result) is dict or action_result is None: resp_obj = ResponseObject(action_result) elif isinstance(action_result, ResponseObject): resp_obj = action_result else: response = action_result # Run post-processing extensions if resp_obj: # Do a preserialize to set up the response object serializers = getattr(meth, 'wsgi_serializers', {}) resp_obj._bind_method_serializers(serializers) if hasattr(meth, 'wsgi_code'): resp_obj._default_code = meth.wsgi_code resp_obj.preserialize(accept, self.default_serializers) # Process post-processing extensions response = self.post_process_extensions( post, resp_obj, request, action_args) if resp_obj and not response: response = resp_obj.serialize(request, accept, self.default_serializers) if hasattr(response, 'headers'): for hdr, val in response.headers.items(): # Headers must be utf-8 strings response.headers[hdr] = utils.utf8(str(val)) if not request.api_version_request.is_null(): response.headers[API_VERSION_REQUEST_HEADER] = \ request.api_version_request.get_string() response.headers['Vary'] = API_VERSION_REQUEST_HEADER tomograph.stop(meth.__name__) return response