Add the service token details to context

In the case of communications that include service tokens we need to add
that information to the context so that we can also enforce policy on
it. Add this information and load this information in the from_environ
method. Add these details to to_policy_values so that we can start to
enforce policy based on these attributes.

Change-Id: Id90f32795905112de804a18ddc8a69c038c829bb
This commit is contained in:
Jamie Lennox 2016-10-26 21:18:28 +11:00
parent c4621f04ff
commit 2eafb0eb6b
2 changed files with 134 additions and 34 deletions

View File

@ -40,22 +40,34 @@ _request_store = threading.local()
# These arguments will be passed to a new context from the first available
# header to support backwards compatibility.
_ENVIRON_HEADERS = {'auth_token': ['HTTP_X_AUTH_TOKEN',
'HTTP_X_STORAGE_TOKEN'],
'user': ['HTTP_X_USER_ID',
'HTTP_X_USER'],
'tenant': ['HTTP_X_PROJECT_ID',
'HTTP_X_TENANT_ID',
'HTTP_X_TENANT'],
'user_domain': ['HTTP_X_USER_DOMAIN_ID'],
'project_domain': ['HTTP_X_PROJECT_DOMAIN_ID'],
'user_name': ['HTTP_X_USER_NAME'],
'project_name': ['HTTP_X_PROJECT_NAME',
'HTTP_X_TENANT_NAME'],
'user_domain_name': ['HTTP_X_USER_DOMAIN_NAME'],
'project_domain_name': ['HTTP_X_PROJECT_DOMAIN_NAME'],
'request_id': ['openstack.request_id'],
}
_ENVIRON_HEADERS = {
'auth_token': ['HTTP_X_AUTH_TOKEN',
'HTTP_X_STORAGE_TOKEN'],
'user': ['HTTP_X_USER_ID',
'HTTP_X_USER'],
'tenant': ['HTTP_X_PROJECT_ID',
'HTTP_X_TENANT_ID',
'HTTP_X_TENANT'],
'user_domain': ['HTTP_X_USER_DOMAIN_ID'],
'project_domain': ['HTTP_X_PROJECT_DOMAIN_ID'],
'user_name': ['HTTP_X_USER_NAME'],
'project_name': ['HTTP_X_PROJECT_NAME',
'HTTP_X_TENANT_NAME'],
'user_domain_name': ['HTTP_X_USER_DOMAIN_NAME'],
'project_domain_name': ['HTTP_X_PROJECT_DOMAIN_NAME'],
'request_id': ['openstack.request_id'],
'service_token': ['HTTP_X_SERVICE_TOKEN'],
'service_user_id': ['HTTP_X_SERVICE_USER_ID'],
'service_user_name': ['HTTP_X_SERVICE_USER_NAME'],
'service_user_domain_id': ['HTTP_X_SERVICE_USER_DOMAIN_ID'],
'service_user_domain_name': ['HTTP_X_SERVICE_USER_DOMAIN_NAME'],
'service_project_id': ['HTTP_X_SERVICE_PROJECT_ID'],
'service_project_name': ['HTTP_X_SERVICE_PROJECT_NAME'],
'service_project_domain_id': ['HTTP_X_SERVICE_PROJECT_DOMAIN_ID'],
'service_project_domain_name': ['HTTP_X_SERVICE_PROJECT_DOMAIN_NAME'],
}
def generate_request_id():
@ -181,7 +193,17 @@ class RequestContext(object):
domain_name=None,
user_domain_name=None,
project_domain_name=None,
is_admin_project=True):
is_admin_project=True,
service_token=None,
service_user_id=None,
service_user_name=None,
service_user_domain_id=None,
service_user_domain_name=None,
service_project_id=None,
service_project_name=None,
service_project_domain_id=None,
service_project_domain_name=None,
service_roles=None):
"""Initialize the RequestContext
:param overwrite: Set to False to ensure that the greenthread local
@ -210,6 +232,18 @@ class RequestContext(object):
self.show_deleted = show_deleted
self.resource_uuid = resource_uuid
self.roles = roles or []
self.service_token = service_token
self.service_user_id = service_user_id
self.service_user_name = service_user_name
self.service_user_domain_id = service_user_domain_id
self.service_user_domain_name = service_user_domain_name
self.service_project_id = service_project_id
self.service_project_name = service_project_name
self.service_project_domain_id = service_project_domain_id
self.service_project_domain_name = service_project_domain_name
self.service_roles = service_roles or []
if not request_id:
request_id = generate_request_id()
self.request_id = request_id
@ -261,7 +295,12 @@ class RequestContext(object):
'project_id': self.project_id,
'project_domain_id': self.project_domain_id,
'roles': self.roles,
'is_admin_project': self.is_admin_project})
'is_admin_project': self.is_admin_project,
'service_user_id': self.service_user_id,
'service_user_domain_id': self.service_user_domain_id,
'service_project_id': self.service_project_id,
'service_project_domain_id': self.service_project_domain_id,
'service_roles': self.service_roles})
def to_dict(self):
"""Return a dictionary of context attributes."""
@ -351,6 +390,11 @@ class RequestContext(object):
roles = [r.strip() for r in roles.split(',')] if roles else []
kwargs['roles'] = roles
if 'service_roles' not in kwargs:
roles = environ.get('HTTP_X_SERVICE_ROLES')
roles = [r.strip() for r in roles.split(',')] if roles else []
kwargs['service_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

View File

@ -200,18 +200,40 @@ class ContextTest(test_base.BaseTestCase):
project_domain_id = generate_id(project_domain_name)
roles = [uuid.uuid4().hex, uuid.uuid4().hex, uuid.uuid4().hex]
request_id = uuid.uuid4().hex
service_token = uuid.uuid4().hex
service_user_id = uuid.uuid4().hex
service_user_name = uuid.uuid4().hex
service_user_domain_id = uuid.uuid4().hex
service_user_domain_name = uuid.uuid4().hex
service_project_id = uuid.uuid4().hex
service_project_name = uuid.uuid4().hex
service_project_domain_id = uuid.uuid4().hex
service_project_domain_name = uuid.uuid4().hex
service_roles = [uuid.uuid4().hex, uuid.uuid4().hex, uuid.uuid4().hex]
environ = {'HTTP_X_AUTH_TOKEN': auth_token,
'HTTP_X_USER_ID': user_id,
'HTTP_X_PROJECT_ID': project_id,
'HTTP_X_USER_DOMAIN_ID': user_domain_id,
'HTTP_X_PROJECT_DOMAIN_ID': project_domain_id,
'HTTP_X_ROLES': ','.join(roles),
'HTTP_X_USER_NAME': user_name,
'HTTP_X_PROJECT_NAME': project_name,
'HTTP_X_USER_DOMAIN_NAME': user_domain_name,
'HTTP_X_PROJECT_DOMAIN_NAME': project_domain_name,
'openstack.request_id': request_id}
environ = {
'HTTP_X_AUTH_TOKEN': auth_token,
'HTTP_X_USER_ID': user_id,
'HTTP_X_PROJECT_ID': project_id,
'HTTP_X_USER_DOMAIN_ID': user_domain_id,
'HTTP_X_PROJECT_DOMAIN_ID': project_domain_id,
'HTTP_X_ROLES': ','.join(roles),
'HTTP_X_USER_NAME': user_name,
'HTTP_X_PROJECT_NAME': project_name,
'HTTP_X_USER_DOMAIN_NAME': user_domain_name,
'HTTP_X_PROJECT_DOMAIN_NAME': project_domain_name,
'HTTP_X_SERVICE_TOKEN': service_token,
'HTTP_X_SERVICE_USER_ID': service_user_id,
'HTTP_X_SERVICE_USER_NAME': service_user_name,
'HTTP_X_SERVICE_USER_DOMAIN_ID': service_user_domain_id,
'HTTP_X_SERVICE_USER_DOMAIN_NAME': service_user_domain_name,
'HTTP_X_SERVICE_PROJECT_ID': service_project_id,
'HTTP_X_SERVICE_PROJECT_NAME': service_project_name,
'HTTP_X_SERVICE_PROJECT_DOMAIN_ID': service_project_domain_id,
'HTTP_X_SERVICE_PROJECT_DOMAIN_NAME': service_project_domain_name,
'HTTP_X_SERVICE_ROLES': ','.join(service_roles),
'openstack.request_id': request_id,
}
ctx = context.RequestContext.from_environ(environ)
@ -226,6 +248,19 @@ class ContextTest(test_base.BaseTestCase):
self.assertEqual(project_domain_name, ctx.project_domain_name)
self.assertEqual(roles, ctx.roles)
self.assertEqual(request_id, ctx.request_id)
self.assertEqual(service_token, ctx.service_token)
self.assertEqual(service_user_id, ctx.service_user_id)
self.assertEqual(service_user_name, ctx.service_user_name)
self.assertEqual(service_user_domain_id, ctx.service_user_domain_id)
self.assertEqual(service_user_domain_name,
ctx.service_user_domain_name)
self.assertEqual(service_project_id, ctx.service_project_id)
self.assertEqual(service_project_name, ctx.service_project_name)
self.assertEqual(service_project_domain_id,
ctx.service_project_domain_id)
self.assertEqual(service_project_domain_name,
ctx.service_project_domain_name)
self.assertEqual(service_roles, ctx.service_roles)
def test_from_environ_no_roles(self):
ctx = context.RequestContext.from_environ(environ={})
@ -293,9 +328,11 @@ class ContextTest(test_base.BaseTestCase):
self.assertEqual(new, ctx.project_name)
def test_from_environ_strip_roles(self):
environ = {'HTTP_X_ROLES': ' abc\t,\ndef\n,ghi\n\n'}
environ = {'HTTP_X_ROLES': ' abc\t,\ndef\n,ghi\n\n',
'HTTP_X_SERVICE_ROLES': ' jkl\t,\nmno\n,pqr\n\n'}
ctx = context.RequestContext.from_environ(environ=environ)
self.assertEqual(['abc', 'def', 'ghi'], ctx.roles)
self.assertEqual(['jkl', 'mno', 'pqr'], ctx.service_roles)
def test_environ_admin_project(self):
environ = {}
@ -461,20 +498,31 @@ class ContextTest(test_base.BaseTestCase):
tenant = uuid.uuid4().hex
project_domain = uuid.uuid4().hex
roles = [uuid.uuid4().hex, uuid.uuid4().hex, uuid.uuid4().hex]
service_user_id = uuid.uuid4().hex
service_project_id = uuid.uuid4().hex
service_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,
project_domain=project_domain,
roles=roles)
roles=roles,
service_user_id=service_user_id,
service_project_id=service_project_id,
service_roles=service_roles)
self.assertEqual({'user_id': user,
'user_domain_id': user_domain,
'project_id': tenant,
'project_domain_id': project_domain,
'roles': roles,
'is_admin_project': True},
'is_admin_project': True,
'service_user_id': service_user_id,
'service_user_domain_id': None,
'service_project_id': service_project_id,
'service_project_domain_id': None,
'service_roles': service_roles},
ctx.to_policy_values())
ctx = context.RequestContext(user=user,
@ -482,14 +530,22 @@ class ContextTest(test_base.BaseTestCase):
tenant=tenant,
project_domain=project_domain,
roles=roles,
is_admin_project=False)
is_admin_project=False,
service_user_id=service_user_id,
service_project_id=service_project_id,
service_roles=service_roles)
self.assertEqual({'user_id': user,
'user_domain_id': user_domain,
'project_id': tenant,
'project_domain_id': project_domain,
'roles': roles,
'is_admin_project': False},
'is_admin_project': False,
'service_user_id': service_user_id,
'service_user_domain_id': None,
'service_project_id': service_project_id,
'service_project_domain_id': None,
'service_roles': service_roles},
ctx.to_policy_values())
def test_positional_args(self):