From 1b261e8bec31a6de3905557abde0da93d5c0437a Mon Sep 17 00:00:00 2001 From: Guang Yee Date: Fri, 8 Feb 2019 16:56:40 -0800 Subject: [PATCH] populate request context with X.509 tokenless cred information Fixes X.509 tokenless auth by properly populating the request context with the necessary credential information. Since Stein release, RBAC has been using the credential information from the Keystone request context instead of the authentication context. Therefore, we'll need to populate the request context with the necessary credential information from the X.509 tokenless authentication context. Closes-Bug: 1811605 Change-Id: I170a91e9ac36990d1e7ec4165dd0337b8f06a938 --- .../middleware/auth_context.py | 24 ++++++++++++- keystone/tests/unit/test_middleware.py | 34 +++++++++++++++++++ .../notes/bug-1811605-9d23080d7e949c25.yaml | 10 ++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/bug-1811605-9d23080d7e949c25.yaml diff --git a/keystone/server/flask/request_processing/middleware/auth_context.py b/keystone/server/flask/request_processing/middleware/auth_context.py index 92d86ee127..7961416d3e 100644 --- a/keystone/server/flask/request_processing/middleware/auth_context.py +++ b/keystone/server/flask/request_processing/middleware/auth_context.py @@ -435,7 +435,29 @@ class AuthContextMiddleware(provider_api.ProviderAPIMixin, elif self._validate_trusted_issuer(request): auth_context = self._build_tokenless_auth_context(request) - + # NOTE(gyee): we are no longer using auth_context when formulating + # the credentials for RBAC. Instead, we are using the (Oslo) + # request context. So we'll need to set all the necessary + # credential attributes in the request context here. + token_attributes = frozenset(( + 'user_id', 'project_id', + 'domain_id', 'user_domain_id', + 'project_domain_id', 'user_domain_name', + 'project_domain_name', 'roles', 'is_admin', + 'project_name', 'domain_name', 'system_scope', + 'is_admin_project', 'service_user_id', + 'service_user_name', 'service_project_id', + 'service_project_name', 'service_user_domain_id' + 'service_user_domain_name', 'service_project_domain_id', + 'service_project_domain_name', 'service_roles')) + for attr in token_attributes: + if attr in auth_context: + setattr(request_context, attr, auth_context[attr]) + # NOTE(gyee): request_context.token_reference is always + # expecting a 'token' key regardless. But in the case of X.509 + # tokenless auth, we don't need a token. So setting it to None + # should be suffice. + request_context.token_reference = {'token': None} else: # There is either no auth token in the request or the certificate # issuer is not trusted. No auth context will be set. This diff --git a/keystone/tests/unit/test_middleware.py b/keystone/tests/unit/test_middleware.py index 2c4717fe0d..8537c25a6e 100644 --- a/keystone/tests/unit/test_middleware.py +++ b/keystone/tests/unit/test_middleware.py @@ -21,6 +21,7 @@ from six.moves import http_client import webtest from keystone.common import authorization +from keystone.common import context as keystone_context from keystone.common import provider_api from keystone.common import tokenless_auth import keystone.conf @@ -223,6 +224,14 @@ class AuthContextMiddlewareTest(test_backend_sql.SqlTests, else: self.assertEqual(self.user['id'], context['user_id']) + def _assert_tokenless_request_context(self, request_context, + ephemeral_user=False): + self.assertIsNotNone(request_context) + self.assertEqual(self.project_id, request_context.project_id) + self.assertIn(self.role_name, request_context.roles) + if not ephemeral_user: + self.assertEqual(self.user['id'], request_context.user_id) + def test_context_already_exists(self): stub_value = uuid.uuid4().hex env = {authorization.AUTH_CONTEXT_ENV: stub_value} @@ -317,6 +326,8 @@ class AuthContextMiddlewareTest(test_backend_sql.SqlTests, req = self._do_middleware_request(extra_environ=env) context = req.environ.get(authorization.AUTH_CONTEXT_ENV) self._assert_tokenless_auth_context(context) + request_context = req.environ.get(keystone_context.REQUEST_CONTEXT_ENV) + self._assert_tokenless_request_context(request_context) def test_proj_scope_with_proj_id_only_success(self): env = {} @@ -331,6 +342,8 @@ class AuthContextMiddlewareTest(test_backend_sql.SqlTests, req = self._do_middleware_request(extra_environ=env) context = req.environ.get(authorization.AUTH_CONTEXT_ENV) self._assert_tokenless_auth_context(context) + request_context = req.environ.get(keystone_context.REQUEST_CONTEXT_ENV) + self._assert_tokenless_request_context(request_context) def test_proj_scope_with_proj_name_and_proj_dom_id_success(self): env = {} @@ -346,6 +359,8 @@ class AuthContextMiddlewareTest(test_backend_sql.SqlTests, req = self._do_middleware_request(extra_environ=env) context = req.environ.get(authorization.AUTH_CONTEXT_ENV) self._assert_tokenless_auth_context(context) + request_context = req.environ.get(keystone_context.REQUEST_CONTEXT_ENV) + self._assert_tokenless_request_context(request_context) def test_proj_scope_with_proj_name_and_proj_dom_name_success(self): env = {} @@ -361,6 +376,8 @@ class AuthContextMiddlewareTest(test_backend_sql.SqlTests, req = self._do_middleware_request(extra_environ=env) context = req.environ.get(authorization.AUTH_CONTEXT_ENV) self._assert_tokenless_auth_context(context) + request_context = req.environ.get(keystone_context.REQUEST_CONTEXT_ENV) + self._assert_tokenless_request_context(request_context) def test_proj_scope_with_proj_name_only_fail(self): env = {} @@ -390,6 +407,8 @@ class AuthContextMiddlewareTest(test_backend_sql.SqlTests, req = self._do_middleware_request(extra_environ=env) context = req.environ.get(authorization.AUTH_CONTEXT_ENV) self._assert_tokenless_auth_context(context) + request_context = req.environ.get(keystone_context.REQUEST_CONTEXT_ENV) + self._assert_tokenless_request_context(request_context) def test_mapping_with_userid_and_domainname_success(self): env = {} @@ -405,6 +424,8 @@ class AuthContextMiddlewareTest(test_backend_sql.SqlTests, req = self._do_middleware_request(extra_environ=env) context = req.environ.get(authorization.AUTH_CONTEXT_ENV) self._assert_tokenless_auth_context(context) + request_context = req.environ.get(keystone_context.REQUEST_CONTEXT_ENV) + self._assert_tokenless_request_context(request_context) def test_mapping_with_username_and_domainid_success(self): env = {} @@ -420,6 +441,8 @@ class AuthContextMiddlewareTest(test_backend_sql.SqlTests, req = self._do_middleware_request(extra_environ=env) context = req.environ.get(authorization.AUTH_CONTEXT_ENV) self._assert_tokenless_auth_context(context) + request_context = req.environ.get(keystone_context.REQUEST_CONTEXT_ENV) + self._assert_tokenless_request_context(request_context) def test_only_domain_name_fail(self): env = {} @@ -474,6 +497,8 @@ class AuthContextMiddlewareTest(test_backend_sql.SqlTests, req = self._do_middleware_request(extra_environ=env) context = req.environ.get(authorization.AUTH_CONTEXT_ENV) self._assert_tokenless_auth_context(context) + request_context = req.environ.get(keystone_context.REQUEST_CONTEXT_ENV) + self._assert_tokenless_request_context(request_context) def test_domain_disable_fail(self): env = {} @@ -543,6 +568,9 @@ class AuthContextMiddlewareTest(test_backend_sql.SqlTests, req = self._do_middleware_request(extra_environ=env) context = req.environ.get(authorization.AUTH_CONTEXT_ENV) self._assert_tokenless_auth_context(context, ephemeral_user=True) + request_context = req.environ.get(keystone_context.REQUEST_CONTEXT_ENV) + self._assert_tokenless_request_context(request_context, + ephemeral_user=True) def test_ephemeral_with_default_user_type_success(self): env = {} @@ -564,6 +592,9 @@ class AuthContextMiddlewareTest(test_backend_sql.SqlTests, req = self._do_middleware_request(extra_environ=env) context = req.environ.get(authorization.AUTH_CONTEXT_ENV) self._assert_tokenless_auth_context(context, ephemeral_user=True) + request_context = req.environ.get(keystone_context.REQUEST_CONTEXT_ENV) + self._assert_tokenless_request_context(request_context, + ephemeral_user=True) def test_ephemeral_any_user_success(self): """Verify ephemeral user does not need a specified user. @@ -585,6 +616,9 @@ class AuthContextMiddlewareTest(test_backend_sql.SqlTests, req = self._do_middleware_request(extra_environ=env) context = req.environ.get(authorization.AUTH_CONTEXT_ENV) self._assert_tokenless_auth_context(context, ephemeral_user=True) + request_context = req.environ.get(keystone_context.REQUEST_CONTEXT_ENV) + self._assert_tokenless_request_context(request_context, + ephemeral_user=True) def test_ephemeral_invalid_scope_fail(self): env = {} diff --git a/releasenotes/notes/bug-1811605-9d23080d7e949c25.yaml b/releasenotes/notes/bug-1811605-9d23080d7e949c25.yaml new file mode 100644 index 0000000000..9b536ba2a2 --- /dev/null +++ b/releasenotes/notes/bug-1811605-9d23080d7e949c25.yaml @@ -0,0 +1,10 @@ +--- +fixes: + - | + [`bug 1811605 `_] + Fixes X.509 tokenless auth by properly populating the request context + with the necessary credential information. Since Stein release, RBAC + has been using the credential information from the Keystone request + context instead of the authentication context. Therefore, we'll need + to populate the request context with the necessary credential + information from the X.509 tokenless authentication context.