Add is_admin_project to context

is_admin_project is provided by keystonemiddleware and used by
oslo.policy to enforce that a project scoped token exists in the admin
project.

To make this usable we add the ability to read the X-Is-Admin-Project
header from the environment, and add it to the outputted policy values.

Note the value is added to keystonemiddleware in the depend review
however it must work even with older auth_token middlewares so is fine
to merge prior to a middleware release.

Closes-Bug: #1577996
Depends-On: Ic680e6eaa683926914cf4b2152ec3bb67c6601ff
Change-Id: Ie48fedb8092e33e9645a37ea3fe44b88d34ad3b8
This commit is contained in:
Jamie Lennox 2016-06-21 12:26:25 +10:00
parent ae6f152447
commit d3af1d06b4
2 changed files with 54 additions and 4 deletions

View File

@ -71,11 +71,16 @@ class RequestContext(object):
read_only=False, show_deleted=False, request_id=None,
resource_uuid=None, overwrite=True, roles=None,
user_name=None, project_name=None, domain_name=None,
user_domain_name=None, project_domain_name=None):
user_domain_name=None, project_domain_name=None,
is_admin_project=True):
"""Initialize the RequestContext
:param overwrite: Set to False to ensure that the greenthread local
copy of the index is not overwritten.
:param is_admin_project: Whether the specified project is specified in
the token as the admin project. Defaults to
True for backwards compatibility.
:type is_admin_project: bool
"""
self.auth_token = auth_token
self.user = user
@ -91,6 +96,7 @@ class RequestContext(object):
self.project_domain = project_domain
self.project_domain_name = project_domain_name
self.is_admin = is_admin
self.is_admin_project = is_admin_project
self.read_only = read_only
self.show_deleted = show_deleted
self.resource_uuid = resource_uuid
@ -121,7 +127,8 @@ class RequestContext(object):
'user_domain_id': self.user_domain,
'project_id': self.tenant,
'project_domain_id': self.project_domain,
'roles': self.roles}
'roles': self.roles,
'is_admin_project': self.is_admin_project}
def to_dict(self):
"""Return a dictionary of context attributes."""
@ -144,7 +151,8 @@ class RequestContext(object):
'request_id': self.request_id,
'resource_uuid': self.resource_uuid,
'roles': self.roles,
'user_identity': user_idt}
'user_identity': user_idt,
'is_admin_project': self.is_admin_project}
def get_logging_values(self):
"""Return a dictionary of logging specific context attributes."""
@ -194,6 +202,13 @@ class RequestContext(object):
roles = [r.strip() for r in roles.split(',')] if roles else []
kwargs['roles'] = roles
if 'is_admin_project' not in kwargs:
# NOTE(jamielennox): we default is_admin_project to true because if
# nothing is provided we have to assume it is the admin project to
# make old policy continue to work.
is_admin_proj_str = environ.get('HTTP_X_IS_ADMIN_PROJECT', 'true')
kwargs['is_admin_project'] = is_admin_proj_str.lower() == 'true'
return cls(**kwargs)

View File

@ -241,6 +241,22 @@ class ContextTest(test_base.BaseTestCase):
ctx = context.RequestContext.from_environ(environ=environ)
self.assertEqual(['abc', 'def', 'ghi'], ctx.roles)
def test_environ_admin_project(self):
environ = {}
ctx = context.RequestContext.from_environ(environ=environ)
self.assertIs(True, ctx.is_admin_project)
self.assertIs(True, ctx.to_policy_values()['is_admin_project'])
environ = {'HTTP_X_IS_ADMIN_PROJECT': 'True'}
ctx = context.RequestContext.from_environ(environ=environ)
self.assertIs(True, ctx.is_admin_project)
self.assertIs(True, ctx.to_policy_values()['is_admin_project'])
environ = {'HTTP_X_IS_ADMIN_PROJECT': 'False'}
ctx = context.RequestContext.from_environ(environ=environ)
self.assertIs(False, ctx.is_admin_project)
self.assertIs(False, ctx.to_policy_values()['is_admin_project'])
def test_from_function_and_args(self):
ctx = context.RequestContext(user="user1")
arg = []
@ -390,6 +406,7 @@ class ContextTest(test_base.BaseTestCase):
project_domain = uuid.uuid4().hex
roles = [uuid.uuid4().hex, uuid.uuid4().hex, uuid.uuid4().hex]
# default is_admin_project is True
ctx = context.RequestContext(user=user,
user_domain=user_domain,
tenant=tenant,
@ -400,4 +417,22 @@ class ContextTest(test_base.BaseTestCase):
'user_domain_id': user_domain,
'project_id': tenant,
'project_domain_id': project_domain,
'roles': roles}, ctx.to_policy_values())
'roles': roles,
'is_admin_project': True},
ctx.to_policy_values())
# is_admin_project False gets passed through
ctx = context.RequestContext(user=user,
user_domain=user_domain,
tenant=tenant,
project_domain=project_domain,
roles=roles,
is_admin_project=False)
self.assertEqual({'user_id': user,
'user_domain_id': user_domain,
'project_id': tenant,
'project_domain_id': project_domain,
'roles': roles,
'is_admin_project': False},
ctx.to_policy_values())