Merge "Replace context building with a request object"
This commit is contained in:
commit
3afbf86b56
67
keystone/common/request.py
Normal file
67
keystone/common/request.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
import webob
|
||||||
|
|
||||||
|
from keystone import exception
|
||||||
|
from keystone.i18n import _
|
||||||
|
|
||||||
|
# Environment variable used to pass the request context
|
||||||
|
CONTEXT_ENV = 'openstack.context'
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class Request(webob.Request):
|
||||||
|
|
||||||
|
_context_dict = None
|
||||||
|
|
||||||
|
def _get_context_dict(self):
|
||||||
|
# allow middleware up the stack to provide context, params and headers.
|
||||||
|
context = self.environ.get(CONTEXT_ENV, {})
|
||||||
|
|
||||||
|
try:
|
||||||
|
context['query_string'] = dict(self.params.items())
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
# The webob package throws UnicodeError when a request cannot be
|
||||||
|
# decoded. Raise ValidationError instead to avoid an UnknownError.
|
||||||
|
msg = _('Query string is not UTF-8 encoded')
|
||||||
|
raise exception.ValidationError(msg)
|
||||||
|
|
||||||
|
context['headers'] = dict(self.headers.items())
|
||||||
|
context['path'] = self.environ['PATH_INFO']
|
||||||
|
scheme = self.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.
|
||||||
|
self.environ['wsgi.url_scheme'] = scheme
|
||||||
|
context['host_url'] = self.host_url
|
||||||
|
# authentication and authorization attributes are set as environment
|
||||||
|
# values by the container and processed by the pipeline. The complete
|
||||||
|
# set is not yet known.
|
||||||
|
context['environment'] = self.environ
|
||||||
|
context['accept_header'] = self.accept
|
||||||
|
|
||||||
|
context.setdefault('is_admin', False)
|
||||||
|
return context
|
||||||
|
|
||||||
|
@property
|
||||||
|
def context_dict(self):
|
||||||
|
if not self._context_dict:
|
||||||
|
self._context_dict = self._get_context_dict()
|
||||||
|
|
||||||
|
return self._context_dict
|
@ -37,6 +37,7 @@ import webob.exc
|
|||||||
|
|
||||||
from keystone.common import dependency
|
from keystone.common import dependency
|
||||||
from keystone.common import json_home
|
from keystone.common import json_home
|
||||||
|
from keystone.common import request as request_mod
|
||||||
from keystone.common import utils
|
from keystone.common import utils
|
||||||
from keystone import exception
|
from keystone import exception
|
||||||
from keystone.i18n import _
|
from keystone.i18n import _
|
||||||
@ -194,47 +195,16 @@ class BaseApplication(object):
|
|||||||
|
|
||||||
@dependency.requires('assignment_api', 'policy_api', 'token_provider_api')
|
@dependency.requires('assignment_api', 'policy_api', 'token_provider_api')
|
||||||
class Application(BaseApplication):
|
class Application(BaseApplication):
|
||||||
@webob.dec.wsgify()
|
|
||||||
|
@webob.dec.wsgify(RequestClass=request_mod.Request)
|
||||||
def __call__(self, req):
|
def __call__(self, req):
|
||||||
arg_dict = req.environ['wsgiorg.routing_args'][1]
|
arg_dict = req.environ['wsgiorg.routing_args'][1]
|
||||||
action = arg_dict.pop('action')
|
action = arg_dict.pop('action')
|
||||||
del arg_dict['controller']
|
del arg_dict['controller']
|
||||||
|
|
||||||
# allow middleware up the stack to provide context, params and headers.
|
|
||||||
context = req.environ.get(CONTEXT_ENV, {})
|
|
||||||
|
|
||||||
try:
|
|
||||||
context['query_string'] = dict(req.params.items())
|
|
||||||
except UnicodeDecodeError as e:
|
|
||||||
# The webob package throws UnicodeError when a request cannot be
|
|
||||||
# decoded. Raise ValidationError instead to avoid an UnknownError.
|
|
||||||
msg = _('Query string is not UTF-8 encoded')
|
|
||||||
raise exception.ValidationError(msg)
|
|
||||||
|
|
||||||
context['headers'] = dict(req.headers.items())
|
|
||||||
context['path'] = req.environ['PATH_INFO']
|
|
||||||
scheme = 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, {})
|
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 known.
|
|
||||||
context['environment'] = req.environ
|
|
||||||
context['accept_header'] = req.accept
|
|
||||||
req.environ = None
|
|
||||||
|
|
||||||
params.update(arg_dict)
|
params.update(arg_dict)
|
||||||
|
|
||||||
context.setdefault('is_admin', False)
|
|
||||||
|
|
||||||
# TODO(termie): do some basic normalization on methods
|
# TODO(termie): do some basic normalization on methods
|
||||||
method = getattr(self, action)
|
method = getattr(self, action)
|
||||||
|
|
||||||
@ -242,34 +212,36 @@ class Application(BaseApplication):
|
|||||||
# response code between GET and HEAD requests. The HTTP status should
|
# response code between GET and HEAD requests. The HTTP status should
|
||||||
# be the same.
|
# be the same.
|
||||||
LOG.info('%(req_method)s %(uri)s', {
|
LOG.info('%(req_method)s %(uri)s', {
|
||||||
'req_method': req.environ['REQUEST_METHOD'].upper(),
|
'req_method': req.method.upper(),
|
||||||
'uri': wsgiref.util.request_uri(req.environ),
|
'uri': wsgiref.util.request_uri(req.environ),
|
||||||
})
|
})
|
||||||
|
|
||||||
params = self._normalize_dict(params)
|
params = self._normalize_dict(params)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = method(context, **params)
|
result = method(req.context_dict, **params)
|
||||||
except exception.Unauthorized as e:
|
except exception.Unauthorized as e:
|
||||||
LOG.warning(
|
LOG.warning(
|
||||||
_LW("Authorization failed. %(exception)s from "
|
_LW("Authorization failed. %(exception)s from "
|
||||||
"%(remote_addr)s"),
|
"%(remote_addr)s"),
|
||||||
{'exception': e, 'remote_addr': req.environ['REMOTE_ADDR']})
|
{'exception': e, 'remote_addr': req.environ['REMOTE_ADDR']})
|
||||||
return render_exception(e, context=context,
|
return render_exception(e,
|
||||||
|
context=req.context_dict,
|
||||||
user_locale=best_match_language(req))
|
user_locale=best_match_language(req))
|
||||||
except exception.Error as e:
|
except exception.Error as e:
|
||||||
LOG.warning(six.text_type(e))
|
LOG.warning(six.text_type(e))
|
||||||
return render_exception(e, context=context,
|
return render_exception(e,
|
||||||
|
context=req.context_dict,
|
||||||
user_locale=best_match_language(req))
|
user_locale=best_match_language(req))
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
LOG.exception(six.text_type(e))
|
LOG.exception(six.text_type(e))
|
||||||
return render_exception(exception.ValidationError(e),
|
return render_exception(exception.ValidationError(e),
|
||||||
context=context,
|
context=req.context_dict,
|
||||||
user_locale=best_match_language(req))
|
user_locale=best_match_language(req))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception(six.text_type(e))
|
LOG.exception(six.text_type(e))
|
||||||
return render_exception(exception.UnexpectedError(exception=e),
|
return render_exception(exception.UnexpectedError(exception=e),
|
||||||
context=context,
|
context=req.context_dict,
|
||||||
user_locale=best_match_language(req))
|
user_locale=best_match_language(req))
|
||||||
|
|
||||||
if result is None:
|
if result is None:
|
||||||
@ -282,8 +254,9 @@ class Application(BaseApplication):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
response_code = self._get_response_code(req)
|
response_code = self._get_response_code(req)
|
||||||
return render_response(body=result, status=response_code,
|
return render_response(body=result,
|
||||||
method=req.environ['REQUEST_METHOD'])
|
status=response_code,
|
||||||
|
method=req.method)
|
||||||
|
|
||||||
def _get_response_code(self, req):
|
def _get_response_code(self, req):
|
||||||
req_method = req.environ['REQUEST_METHOD']
|
req_method = req.environ['REQUEST_METHOD']
|
||||||
@ -455,7 +428,7 @@ class Middleware(Application):
|
|||||||
"""Do whatever you'd like to the response, based on the request."""
|
"""Do whatever you'd like to the response, based on the request."""
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@webob.dec.wsgify()
|
@webob.dec.wsgify(RequestClass=request_mod.Request)
|
||||||
@middleware_exceptions
|
@middleware_exceptions
|
||||||
def __call__(self, request):
|
def __call__(self, request):
|
||||||
response = self.process_request(request)
|
response = self.process_request(request)
|
||||||
@ -473,7 +446,7 @@ class Debug(Middleware):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@webob.dec.wsgify()
|
@webob.dec.wsgify(RequestClass=request_mod.Request)
|
||||||
def __call__(self, req):
|
def __call__(self, req):
|
||||||
if not hasattr(LOG, 'isEnabledFor') or LOG.isEnabledFor(LOG.debug):
|
if not hasattr(LOG, 'isEnabledFor') or LOG.isEnabledFor(LOG.debug):
|
||||||
LOG.debug('%s %s %s', ('*' * 20), 'REQUEST ENVIRON', ('*' * 20))
|
LOG.debug('%s %s %s', ('*' * 20), 'REQUEST ENVIRON', ('*' * 20))
|
||||||
@ -537,7 +510,7 @@ class Router(object):
|
|||||||
self._router = routes.middleware.RoutesMiddleware(self._dispatch,
|
self._router = routes.middleware.RoutesMiddleware(self._dispatch,
|
||||||
self.map)
|
self.map)
|
||||||
|
|
||||||
@webob.dec.wsgify()
|
@webob.dec.wsgify(RequestClass=request_mod.Request)
|
||||||
def __call__(self, req):
|
def __call__(self, req):
|
||||||
"""Route the incoming request to a controller based on self.map.
|
"""Route the incoming request to a controller based on self.map.
|
||||||
|
|
||||||
@ -547,7 +520,7 @@ class Router(object):
|
|||||||
return self._router
|
return self._router
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@webob.dec.wsgify()
|
@webob.dec.wsgify(RequestClass=request_mod.Request)
|
||||||
def _dispatch(req):
|
def _dispatch(req):
|
||||||
"""Dispatch the request to the appropriate controller.
|
"""Dispatch the request to the appropriate controller.
|
||||||
|
|
||||||
@ -714,7 +687,7 @@ class V3ExtensionRouter(ExtensionRouter, RoutersBase):
|
|||||||
def _update_version_response(self, response_data):
|
def _update_version_response(self, response_data):
|
||||||
response_data['resources'].update(self.v3_resources)
|
response_data['resources'].update(self.v3_resources)
|
||||||
|
|
||||||
@webob.dec.wsgify()
|
@webob.dec.wsgify(RequestClass=request_mod.Request)
|
||||||
def __call__(self, request):
|
def __call__(self, request):
|
||||||
if request.path_info != '/':
|
if request.path_info != '/':
|
||||||
# Not a request for version info so forward to super.
|
# Not a request for version info so forward to super.
|
||||||
|
Loading…
Reference in New Issue
Block a user