Pass X_IS_ADMIN_PROJECT header from auth_token

To do policy enforcement around admin projects we need for auth_token
middleware to pass this information down to context objects.

Closes-Bug: #1577996
Change-Id: Ic680e6eaa683926914cf4b2152ec3bb67c6601ff
This commit is contained in:
Jamie Lennox 2016-06-18 09:14:26 +10:00
parent 627ec923b9
commit 0562670d4e
6 changed files with 60 additions and 0 deletions

View File

@ -118,6 +118,12 @@ HTTP_X_USER_DOMAIN_NAME, HTTP_X_SERVICE_USER_DOMAIN_NAME
HTTP_X_ROLES, HTTP_X_SERVICE_ROLES
Comma delimited list of case-sensitive role names.
HTTP_X_IS_ADMIN_PROJECT
The string value 'True' or 'False' representing whether the user's token is
scoped to the admin project. As historically there was no admin project
this will default to True for tokens without this information to be
backwards with existing policy files.
HTTP_X_SERVICE_CATALOG
service catalog (optional, JSON string).

View File

@ -54,6 +54,15 @@ def _v3_to_v2_catalog(catalog):
return v2_services
def _is_admin_project(auth_ref):
"""Return an appropriate header value for X-Is-Admin-Project.
Headers must be strings so we can't simply pass a boolean value through so
return a True or False string to signal the admin project.
"""
return 'True' if auth_ref.is_admin_project else 'False'
# NOTE(jamielennox): this should probably be moved into its own file, but at
# the moment there's no real logic here so just keep it locally.
class _AuthTokenResponse(webob.Response):
@ -86,6 +95,8 @@ class _AuthTokenRequest(webob.Request):
_USER_STATUS_HEADER = 'X-Identity-Status'
_SERVICE_STATUS_HEADER = 'X-Service-Identity-Status'
_ADMIN_PROJECT_HEADER = 'X-Is-Admin-Project'
_SERVICE_CATALOG_HEADER = 'X-Service-Catalog'
_TOKEN_AUTH = 'keystone.token_auth'
_TOKEN_INFO = 'keystone.token_info'
@ -155,6 +166,7 @@ class _AuthTokenRequest(webob.Request):
doc info at start of __init__ file for details of headers to be defined
"""
self._set_auth_headers(auth_ref, self._USER_HEADER_PREFIX)
self.headers[self._ADMIN_PROJECT_HEADER] = _is_admin_project(auth_ref)
for k, v in six.iteritems(self._DEPRECATED_HEADER_MAP):
self.headers[k] = self.headers[v]
@ -192,6 +204,7 @@ class _AuthTokenRequest(webob.Request):
yield self._SERVICE_CATALOG_HEADER
yield self._USER_STATUS_HEADER
yield self._SERVICE_STATUS_HEADER
yield self._ADMIN_PROJECT_HEADER
for header in self._DEPRECATED_HEADER_MAP:
yield header

View File

@ -133,6 +133,14 @@ class _TokenData(object):
"""
return frozenset(self._stored_auth_ref.role_names or [])
@property
def is_admin_project(self):
"""Return true if the current project scope is the admin project.
:rtype: bool
"""
return self._stored_auth_ref.is_admin_project
@property
def _log_format(self):
roles = ','.join(self.role_names)

View File

@ -56,6 +56,7 @@ EXPECTED_V2_DEFAULT_ENV_RESPONSE = {
'HTTP_X_USER_ID': 'user_id1',
'HTTP_X_USER_NAME': 'user_name1',
'HTTP_X_ROLES': 'role1,role2',
'HTTP_X_IS_ADMIN_PROJECT': 'True',
'HTTP_X_USER': 'user_name1', # deprecated (diablo-compat)
'HTTP_X_TENANT': 'tenant_name1', # deprecated (diablo-compat)
'HTTP_X_ROLE': 'role1,role2', # deprecated (diablo-compat)
@ -75,6 +76,7 @@ EXPECTED_V3_DEFAULT_ENV_ADDITIONS = {
'HTTP_X_PROJECT_DOMAIN_NAME': 'domain_name1',
'HTTP_X_USER_DOMAIN_ID': 'domain_id1',
'HTTP_X_USER_DOMAIN_NAME': 'domain_name1',
'HTTP_X_IS_ADMIN_PROJECT': 'True'
}
EXPECTED_V3_DEFAULT_SERVICE_ENV_ADDITIONS = {
@ -1848,6 +1850,13 @@ class v3AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest,
new_data = self.middleware.fetch_token(token)
self.assertEqual(data, new_data)
def test_not_is_admin_project(self):
token = self.examples.v3_NOT_IS_ADMIN_PROJECT
self.set_middleware(expected_env={'HTTP_X_IS_ADMIN_PROJECT': 'False'})
req = self.assert_valid_request_200(token)
self.assertIs(False,
req.environ['keystone.token_auth'].user.is_admin_project)
class DelayedAuthTests(BaseAuthTokenMiddlewareTest):

View File

@ -13,6 +13,7 @@
# under the License.
import os
import uuid
import fixtures
from keystoneauth1 import fixture
@ -131,6 +132,7 @@ class Examples(fixtures.Fixture):
self.UUID_SERVICE_TOKEN_BIND = '5e43439613d34a13a7e03b2762bd08ab'
self.v3_UUID_SERVICE_TOKEN_DEFAULT = 'g431071bbc2f492748596c1b53cb229'
self.v3_UUID_SERVICE_TOKEN_BIND = 'be705e4426d0449a89e35ae21c380a05'
self.v3_NOT_IS_ADMIN_PROJECT = uuid.uuid4().hex
revoked_token = self.REVOKED_TOKEN
if isinstance(revoked_token, six.text_type):
@ -465,6 +467,21 @@ class Examples(fixtures.Fixture):
svc.add_endpoint('public', self.SERVICE_URL)
self.TOKEN_RESPONSES[self.v3_UUID_SERVICE_TOKEN_DEFAULT] = token
token = fixture.V3Token(user_id=USER_ID,
user_name=USER_NAME,
user_domain_id=DOMAIN_ID,
user_domain_name=DOMAIN_NAME,
project_id=PROJECT_ID,
project_name=PROJECT_NAME,
project_domain_id=DOMAIN_ID,
project_domain_name=DOMAIN_NAME,
is_admin_project=False)
token.add_role(name=ROLE_NAME1)
token.add_role(name=ROLE_NAME2)
svc = token.add_service(self.SERVICE_TYPE)
svc.add_endpoint('public', self.SERVICE_URL)
self.TOKEN_RESPONSES[self.v3_NOT_IS_ADMIN_PROJECT] = token
# PKIZ tokens generally link to above tokens
self.TOKEN_RESPONSES[self.SIGNED_TOKEN_SCOPED_PKIZ_KEY] = (

View File

@ -0,0 +1,7 @@
---
prelude: >
- Add the `X_IS_ADMIN_PROJECT` header.
features:
- Added the `X_IS_ADMIN_PROJECT` header to authenticated headers. This has
the string value of 'True' or 'False' and can be used to enforce admin
project policies.