Merge "Require auth_context middleware in the pipeline"
This commit is contained in:
commit
a6c0b71252
|
@ -29,6 +29,7 @@ class RequestContext(oslo_context.RequestContext):
|
|||
self.username = kwargs.pop('username', None)
|
||||
self.user_domain_name = kwargs.pop('user_domain_name', None)
|
||||
self.project_domain_name = kwargs.pop('project_domain_name', None)
|
||||
self.authenticated = kwargs.pop('authenticated', False)
|
||||
super(RequestContext, self).__init__(**kwargs)
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -79,32 +79,7 @@ def _build_policy_check_credentials(self, action, context, kwargs):
|
|||
'action': action,
|
||||
'kwargs': kwargs_str})
|
||||
|
||||
# see if auth context has already been created. If so use it.
|
||||
if ('environment' in context and
|
||||
authorization.AUTH_CONTEXT_ENV in context['environment']):
|
||||
LOG.debug('RBAC: using auth context from the request environment')
|
||||
return context['environment'].get(authorization.AUTH_CONTEXT_ENV)
|
||||
|
||||
# There is no current auth context, build it from the incoming token.
|
||||
# TODO(morganfainberg): Collapse this logic with AuthContextMiddleware
|
||||
# in a sane manner as this just mirrors the logic in AuthContextMiddleware
|
||||
try:
|
||||
LOG.debug('RBAC: building auth context from the incoming auth token')
|
||||
token_ref = token_model.KeystoneToken(
|
||||
token_id=context['token_id'],
|
||||
token_data=self.token_provider_api.validate_token(
|
||||
context['token_id']))
|
||||
# NOTE(jamielennox): whilst this maybe shouldn't be within this
|
||||
# function it would otherwise need to reload the token_ref from
|
||||
# backing store.
|
||||
wsgi.validate_token_bind(context, token_ref)
|
||||
except exception.TokenNotFound:
|
||||
LOG.warning(_LW('RBAC: Invalid token'))
|
||||
raise exception.Unauthorized()
|
||||
|
||||
auth_context = authorization.token_to_auth_context(token_ref)
|
||||
|
||||
return auth_context
|
||||
return context['environment'].get(authorization.AUTH_CONTEXT_ENV, {})
|
||||
|
||||
|
||||
def protected(callback=None):
|
||||
|
@ -123,6 +98,8 @@ def protected(callback=None):
|
|||
def wrapper(f):
|
||||
@functools.wraps(f)
|
||||
def inner(self, request, *args, **kwargs):
|
||||
request.assert_authenticated()
|
||||
|
||||
if request.context.is_admin:
|
||||
LOG.warning(_LW('RBAC: Bypassing authorization'))
|
||||
elif callback is not None:
|
||||
|
@ -205,6 +182,8 @@ def filterprotected(*filters, **callback):
|
|||
def _filterprotected(f):
|
||||
@functools.wraps(f)
|
||||
def wrapper(self, request, **kwargs):
|
||||
request.assert_authenticated()
|
||||
|
||||
if not request.context.is_admin:
|
||||
# The target dict for the policy check will include:
|
||||
#
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
|
||||
import webob
|
||||
from webob.descriptors import environ_getter
|
||||
|
||||
|
@ -17,13 +19,14 @@ from keystone.common import authorization
|
|||
from keystone.common import context
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
from keystone.i18n import _
|
||||
from keystone.i18n import _, _LW
|
||||
|
||||
|
||||
# Environment variable used to pass the request context
|
||||
CONTEXT_ENV = 'openstack.context'
|
||||
|
||||
CONF = keystone.conf.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Request(webob.Request):
|
||||
|
@ -73,6 +76,20 @@ class Request(webob.Request):
|
|||
def auth_context(self):
|
||||
return self.environ.get(authorization.AUTH_CONTEXT_ENV, {})
|
||||
|
||||
def assert_authenticated(self):
|
||||
"""Ensure that the current request has been authenticated."""
|
||||
if not self.context:
|
||||
LOG.warning(_LW('An authenticated call was made and there is '
|
||||
'no request.context. This means the '
|
||||
'auth_context middleware is not in place. You '
|
||||
'must have this middleware in your pipeline '
|
||||
'to perform authenticated calls'))
|
||||
raise exception.Unauthorized()
|
||||
|
||||
if not self.context.authenticated:
|
||||
# auth_context didn't decode anything we can use
|
||||
raise exception.Unauthorized()
|
||||
|
||||
auth_type = environ_getter('AUTH_TYPE', None)
|
||||
remote_domain = environ_getter('REMOTE_DOMAIN', None)
|
||||
context = environ_getter(context.REQUEST_CONTEXT_ENV, None)
|
||||
|
|
|
@ -154,6 +154,7 @@ class AuthContextMiddleware(auth_token.BaseAuthProtocol):
|
|||
# The request context stores itself in thread-local memory for logging.
|
||||
request_context = context.RequestContext(
|
||||
request_id=request.environ.get('openstack.request_id'),
|
||||
authenticated=False,
|
||||
overwrite=True)
|
||||
request.environ[context.REQUEST_CONTEXT_ENV] = request_context
|
||||
|
||||
|
@ -203,6 +204,9 @@ class AuthContextMiddleware(auth_token.BaseAuthProtocol):
|
|||
'context will be set.')
|
||||
return
|
||||
|
||||
# set authenticated to flag to keystone that a token has been validated
|
||||
request_context.authenticated = True
|
||||
|
||||
# The attributes of request_context are put into the logs. This is a
|
||||
# common pattern for all the OpenStack services. In all the other
|
||||
# projects these are IDs, so set the attributes to IDs here rather than
|
||||
|
|
|
@ -594,7 +594,8 @@ class TestCase(BaseTestCase):
|
|||
|
||||
if not environ.get(context.REQUEST_CONTEXT_ENV):
|
||||
environ[context.REQUEST_CONTEXT_ENV] = context.RequestContext(
|
||||
is_admin=is_admin)
|
||||
is_admin=is_admin,
|
||||
authenticated=kwargs.pop('authenticated', True))
|
||||
|
||||
req = request.Request.blank(path=path, **kwargs)
|
||||
req.context_dict['is_admin'] = is_admin
|
||||
|
|
|
@ -4699,23 +4699,6 @@ class TestTrustChain(test_v3.RestfulTestCase):
|
|||
expected_status=http_client.FORBIDDEN)
|
||||
|
||||
|
||||
class TestAPIProtectionWithoutAuthContextMiddleware(test_v3.RestfulTestCase):
|
||||
def test_api_protection_with_no_auth_context_in_env(self):
|
||||
auth_data = self.build_authentication_request(
|
||||
user_id=self.default_domain_user['id'],
|
||||
password=self.default_domain_user['password'],
|
||||
project_id=self.project['id'])
|
||||
token = self.get_requested_token(auth_data)
|
||||
auth_controller = auth.controllers.Auth()
|
||||
# all we care is that auth context is not in the environment and
|
||||
# 'token_id' is used to build the auth context instead
|
||||
request = self.make_request()
|
||||
request.context_dict['subject_token_id'] = token
|
||||
request.context_dict['token_id'] = token
|
||||
r = auth_controller.validate_token(request)
|
||||
self.assertEqual(http_client.OK, r.status_code)
|
||||
|
||||
|
||||
class TestAuthContext(unit.TestCase):
|
||||
def setUp(self):
|
||||
super(TestAuthContext, self).setUp()
|
||||
|
|
Loading…
Reference in New Issue