Merge "Use built-in oslo context de/serialization" into stable/xena

This commit is contained in:
Zuul 2022-05-05 17:44:51 +00:00 committed by Gerrit Code Review
commit 2a34a6f6a2
14 changed files with 153 additions and 136 deletions

View File

@ -27,18 +27,5 @@ def ctx_from_headers(headers):
except TypeError: except TypeError:
raise exceptions.WrongFormat() raise exceptions.WrongFormat()
kwargs = {"user_id": headers['X-User-Id'], return context.BlazarContext.from_environ(headers.environ,
"project_id": headers['X-Project-Id'], service_catalog=service_catalog)
"auth_token": headers['X-Auth-Token'],
"service_catalog": service_catalog,
"user_name": headers['X-User-Name'],
"project_name": headers['X-Project-Name'],
"roles": list(
map(str.strip, headers['X-Roles'].split(',')))}
# For v1 only, request_id and global_request_id will be available.
if headers.environ['PATH_INFO'].startswith('/v1'):
kwargs['request_id'] = headers.environ['openstack.request_id']
kwargs['global_request_id'] = headers.environ.get(
'openstack.global_request_id')
return context.BlazarContext(**kwargs)

View File

@ -15,31 +15,29 @@
import threading import threading
from oslo_config import cfg
from oslo_context import context from oslo_context import context
CONF = cfg.CONF
class BlazarContext(context.RequestContext): class BlazarContext(context.RequestContext):
# service_catalog is not by default read from a dict
# when deserializing a context.
FROM_DICT_EXTRA_KEYS = ['service_catalog']
_context_stack = threading.local() _context_stack = threading.local()
def __init__(self, user_id=None, project_id=None, project_name=None, def __init__(self, service_catalog=None, **kwargs):
service_catalog=None, user_name=None, **kwargs):
# NOTE(neha-alhat): During serializing/deserializing context object # NOTE(neha-alhat): During serializing/deserializing context object
# over the RPC layer, below extra parameters which are passed by # over the RPC layer, below extra parameters which are passed by
# `oslo.messaging` are popped as these parameters are not required. # `oslo.messaging` are popped as these parameters are not required.
kwargs.pop('client_timeout', None) kwargs.pop('client_timeout', None)
kwargs.pop('user_identity', None) kwargs.pop('user_identity', None)
kwargs.pop('project', None)
if user_id:
kwargs['user_id'] = user_id
if project_id:
kwargs['project_id'] = project_id
super(BlazarContext, self).__init__(**kwargs) super(BlazarContext, self).__init__(**kwargs)
self.project_name = project_name
self.user_name = user_name
self.service_catalog = service_catalog or [] self.service_catalog = service_catalog or []
if self.is_admin and 'admin' not in self.roles: if self.is_admin and 'admin' not in self.roles:
@ -65,28 +63,35 @@ class BlazarContext(context.RequestContext):
except (AttributeError, IndexError): except (AttributeError, IndexError):
raise RuntimeError("Context isn't available here") raise RuntimeError("Context isn't available here")
# NOTE(yorik-sar): as long as oslo.rpc requires this
def to_dict(self): def to_dict(self):
result = super(BlazarContext, self).to_dict() result = super(BlazarContext, self).to_dict()
result['user_id'] = self.user_id
result['user_name'] = self.user_name
result['project_id'] = self.project_id
result['project_name'] = self.project_name
result['service_catalog'] = self.service_catalog result['service_catalog'] = self.service_catalog
return result return result
@classmethod @classmethod
def elevated(cls): def admin(cls):
try: try:
ctx = cls.current() cur = cls.current()
request_id = cur.request_id
global_request_id = cur.global_request_id
service_catalog = cur.service_catalog
except RuntimeError: except RuntimeError:
ctx = None request_id = global_request_id = service_catalog = None
return cls(ctx, is_admin=True) return cls(
user_name=CONF.os_admin_username,
user_domain_name=CONF.os_admin_user_domain_name,
project_name=CONF.os_admin_project_name,
project_domain_name=CONF.os_admin_project_domain_name,
is_admin=True,
service_catalog=service_catalog,
request_id=request_id,
global_request_id=global_request_id
)
def current(): def current():
return BlazarContext.current() return BlazarContext.current()
def elevated(): def admin():
return BlazarContext.elevated() return BlazarContext.admin()

View File

@ -91,7 +91,7 @@ def enforce(context, action, target, do_raise=True):
init() init()
credentials = context.to_dict() credentials = context.to_policy_values()
# Add the exceptions arguments if asked to do a raise # Add the exceptions arguments if asked to do a raise
extra = {} extra = {}

View File

@ -27,6 +27,9 @@ from blazar import tests
PATH_PREFIX = '/v2' PATH_PREFIX = '/v2'
FAKE_PROJECT = '981b767265174c108bc5a61185b748ac'
FAKE_USER = 'b4fdb2fff13545ceb751295096cc18ee'
class APITest(tests.TestCase): class APITest(tests.TestCase):
"""Used for unittests tests of Pecan controllers.""" """Used for unittests tests of Pecan controllers."""
@ -40,11 +43,12 @@ class APITest(tests.TestCase):
def fake_ctx_from_headers(headers): def fake_ctx_from_headers(headers):
if not headers: if not headers:
return context.BlazarContext( return context.BlazarContext(
user_id='fake', project_id='fake', roles=['member']) user_id=FAKE_USER, project_id=FAKE_PROJECT,
roles=['member'])
roles = headers.get('X-Roles', str('member')).split(',') roles = headers.get('X-Roles', str('member')).split(',')
return context.BlazarContext( return context.BlazarContext(
user_id=headers.get('X-User-Id', 'fake'), user_id=headers.get('X-User-Id', FAKE_USER),
project_id=headers.get('X-Project-Id', 'fake'), project_id=headers.get('X-Project-Id', FAKE_PROJECT),
auth_token=headers.get('X-Auth-Token', None), auth_token=headers.get('X-Auth-Token', None),
service_catalog=None, service_catalog=None,
user_name=headers.get('X-User-Name', 'fake'), user_name=headers.get('X-User-Name', 'fake'),

View File

@ -19,7 +19,6 @@ import webob
from werkzeug import wrappers from werkzeug import wrappers
from blazar.api import context as api_context from blazar.api import context as api_context
from blazar import context
from blazar import exceptions from blazar import exceptions
from blazar import tests from blazar import tests
@ -33,9 +32,10 @@ class ContextTestCase(tests.TestCase):
'X-Project-Id': uuidsentinel.project_id, 'X-Project-Id': uuidsentinel.project_id,
'X-Auth-Token': '111-111-111', 'X-Auth-Token': '111-111-111',
'X-User-Name': 'user_name', 'X-User-Name': 'user_name',
'X-User-Domain-Name': 'user_domain_name',
'X-Project-Name': 'project_name', 'X-Project-Name': 'project_name',
'X-Project-Domain-Name': 'project_domain_name',
'X-Roles': 'user_name0, user_name1'} 'X-Roles': 'user_name0, user_name1'}
self.context = self.patch(context, 'BlazarContext')
self.catalog = jsonutils.dump_as_bytes({'nova': 'catalog'}) self.catalog = jsonutils.dump_as_bytes({'nova': 'catalog'})
def test_ctx_from_headers_no_catalog(self): def test_ctx_from_headers_no_catalog(self):
@ -64,19 +64,24 @@ class ContextTestCaseV1(ContextTestCase):
'/v1/leases', '/v1/leases',
headers=self.fake_headers, headers=self.fake_headers,
environ_base=environ_base) environ_base=environ_base)
api_context.ctx_from_headers(req.headers) context = api_context.ctx_from_headers(req.headers)
expected = dict(
self.context.assert_called_once_with(
user_id=uuidsentinel.user_id, user_id=uuidsentinel.user_id,
roles=['user_name0', roles=['user_name0',
'user_name1'], 'user_name1'],
project_name='project_name', project_name='project_name',
project_domain_name='project_domain_name',
auth_token='111-111-111', auth_token='111-111-111',
service_catalog={'nova': 'catalog'}, service_catalog={'nova': 'catalog'},
project_id=uuidsentinel.project_id, project_id=uuidsentinel.project_id,
user_name='user_name', user_name='user_name',
user_domain_name='user_domain_name',
request_id='req-' + uuidsentinel.reqid, request_id='req-' + uuidsentinel.reqid,
global_request_id='req-' + uuidsentinel.globalreqid) global_request_id='req-' + uuidsentinel.globalreqid
)
for k, v in expected.items():
self.assertEqual(getattr(context, k, None), v)
class ContextTestCaseV2(ContextTestCase): class ContextTestCaseV2(ContextTestCase):
@ -85,14 +90,19 @@ class ContextTestCaseV2(ContextTestCase):
self.fake_headers['X-Service-Catalog'] = self.catalog self.fake_headers['X-Service-Catalog'] = self.catalog
req = webob.Request.blank('/v2/leases') req = webob.Request.blank('/v2/leases')
req.headers = self.fake_headers req.headers = self.fake_headers
api_context.ctx_from_headers(req.headers) context = api_context.ctx_from_headers(req.headers)
expected = dict(
self.context.assert_called_once_with(
user_id=uuidsentinel.user_id, user_id=uuidsentinel.user_id,
roles=['user_name0', roles=['user_name0',
'user_name1'], 'user_name1'],
project_name='project_name', project_name='project_name',
project_domain_name='project_domain_name',
auth_token='111-111-111', auth_token='111-111-111',
service_catalog={'nova': 'catalog'}, service_catalog={'nova': 'catalog'},
project_id=uuidsentinel.project_id, project_id=uuidsentinel.project_id,
user_name='user_name') user_name='user_name',
user_domain_name='user_domain_name'
)
for k, v in expected.items():
self.assertEqual(getattr(context, k, None), v)

View File

@ -28,9 +28,8 @@ def fake_lease(**kw):
'end_date': kw.get('end_date', '2014-02-01 13:37'), 'end_date': kw.get('end_date', '2014-02-01 13:37'),
'trust_id': kw.get('trust_id', 'trust_id': kw.get('trust_id',
'35b17138b3644e6aa1318f3099c5be68'), '35b17138b3644e6aa1318f3099c5be68'),
'user_id': kw.get('user_id', 'efd8780712d24b389c705f5c2ac427ff'), 'user_id': kw.get('user_id', api.FAKE_USER),
'project_id': kw.get('project_id', 'project_id': kw.get('project_id', api.FAKE_PROJECT),
'bd9431c18d694ad3803a8d4a6b89fd36'),
'reservations': kw.get('reservations', [ 'reservations': kw.get('reservations', [
{ {
'resource_id': '1234', 'resource_id': '1234',

View File

@ -13,14 +13,27 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from oslo_config import cfg
from oslo_config import fixture as conf_fixture
from oslo_utils.fixture import uuidsentinel from oslo_utils.fixture import uuidsentinel
from blazar import context from blazar import context
from blazar import tests from blazar import tests
CONF = cfg.CONF
class TestBlazarContext(tests.TestCase): class TestBlazarContext(tests.TestCase):
def setUp(self):
super(TestBlazarContext, self).setUp()
self.cfg = self.useFixture(conf_fixture.Config(CONF))
self.cfg.config(os_admin_username='fake-admin')
self.cfg.config(os_admin_user_domain_name='fake-admin-domain')
self.cfg.config(os_admin_project_name='fake-admin-project')
self.cfg.config(os_admin_project_domain_name='fake-admin-domain')
def test_to_dict(self): def test_to_dict(self):
ctx = context.BlazarContext( ctx = context.BlazarContext(
user_id=111, project_id=222, user_id=111, project_id=222,
@ -33,8 +46,6 @@ class TestBlazarContext(tests.TestCase):
'is_admin_project': True, 'is_admin_project': True,
'project': 222, 'project': 222,
'project_domain': None, 'project_domain': None,
'project_id': 222,
'project_name': None,
'read_only': False, 'read_only': False,
'request_id': 'req-679033b7-1755-4929-bf85-eb3bfaef7e0b', 'request_id': 'req-679033b7-1755-4929-bf85-eb3bfaef7e0b',
'resource_uuid': None, 'resource_uuid': None,
@ -45,15 +56,9 @@ class TestBlazarContext(tests.TestCase):
'tenant': 222, 'tenant': 222,
'user': 111, 'user': 111,
'user_domain': None, 'user_domain': None,
'user_id': 111, 'user_identity': '111 222 - - -'}
'user_identity': '111 222 - - -',
'user_name': None}
self.assertEqual(expected, ctx.to_dict()) self.assertEqual(expected, ctx.to_dict())
def test_elevated_empty(self):
ctx = context.BlazarContext.elevated()
self.assertTrue(ctx.is_admin)
def test_service_catalog_default(self): def test_service_catalog_default(self):
ctxt = context.BlazarContext(user_id=uuidsentinel.user_id, ctxt = context.BlazarContext(user_id=uuidsentinel.user_id,
project_id=uuidsentinel.project_id) project_id=uuidsentinel.project_id)
@ -69,14 +74,30 @@ class TestBlazarContext(tests.TestCase):
service_catalog=None) service_catalog=None)
self.assertEqual([], ctxt.service_catalog) self.assertEqual([], ctxt.service_catalog)
def test_blazar_context_elevated(self): def test_admin(self):
user_context = context.BlazarContext( ctx = context.admin()
user_id=uuidsentinel.user_id, self.assertEqual(ctx.user_name, 'fake-admin')
project_id=uuidsentinel.project_id, is_admin=False) self.assertEqual(ctx.user_domain_name, 'fake-admin-domain')
self.assertFalse(user_context.is_admin) self.assertEqual(ctx.project_name, 'fake-admin-project')
self.assertEqual(ctx.project_domain_name, 'fake-admin-domain')
self.assertEqual(ctx.is_admin, True)
admin_context = user_context.elevated() def test_admin_nested(self):
self.assertFalse(user_context.is_admin) """Test that admin properties take priority over current context."""
self.assertTrue(admin_context.is_admin) request_id = 'req-679033b7-1755-4929-bf85-eb3bfaef7e0b'
self.assertNotIn('admin', user_context.roles) service_catalog = ['foo']
self.assertIn('admin', admin_context.roles) ctx = context.BlazarContext(
user_name='fake-user', user_domain_name='fake-user-domain',
project_name='fake-project',
project_domain_name='fake-user-domain',
service_catalog=service_catalog, request_id=request_id)
with ctx:
admin_ctx = context.admin()
self.assertEqual(admin_ctx.user_name, 'fake-admin')
self.assertEqual(admin_ctx.user_domain_name, 'fake-admin-domain')
self.assertEqual(admin_ctx.project_name, 'fake-admin-project')
self.assertEqual(admin_ctx.project_domain_name,
'fake-admin-domain')
self.assertEqual(admin_ctx.is_admin, True)
self.assertEqual(admin_ctx.request_id, request_id)
self.assertEqual(admin_ctx.service_catalog, service_catalog)

View File

@ -52,15 +52,6 @@ class BlazarPolicyTestCase(tests.TestCase):
self.assertRaises(exceptions.PolicyNotAuthorized, policy.enforce, self.assertRaises(exceptions.PolicyNotAuthorized, policy.enforce,
self.context, action, target) self.context, action, target)
def test_elevatedpolicy(self):
target = {'user_id': self.context.user_id,
'project_id': self.context.project_id}
action = "blazar:oshosts:get"
self.assertRaises(exceptions.PolicyNotAuthorized, policy.enforce,
self.context, action, target)
elevated_context = self.context.elevated()
self.assertTrue(policy.enforce(elevated_context, action, target))
def test_authorize(self): def test_authorize(self):
@policy.authorize('leases', 'get', ctx=self.context) @policy.authorize('leases', 'get', ctx=self.context)

View File

@ -38,56 +38,59 @@ class TestCKClient(tests.TestCase):
self.version = '3' self.version = '3'
self.username = 'fake_user' self.username = 'fake_user'
self.user_domain_name = 'fake_user_domain'
self.token = 'fake_token' self.token = 'fake_token'
self.password = 'fake_pass' self.password = 'fake_pass'
self.tenant_name = 'fake_project' self.project_name = 'fake_project'
self.project_domain_name = 'fake_project_domain'
self.auth_url = 'fake_url' self.auth_url = 'fake_url'
self.trust_id = 'fake_trust' self.trust_id = 'fake_trust'
def test_client_from_kwargs(self): def test_client_from_kwargs(self):
self.ctx.side_effect = RuntimeError self.ctx.side_effect = RuntimeError
self.keystone.BlazarKeystoneClient(
self.keystone.BlazarKeystoneClient(version=self.version, version=self.version,
username=self.username, username=self.username,
password=self.password, password=self.password,
tenant_name=self.tenant_name, project_name=self.project_name,
trust_id=self.trust_id, trust_id=self.trust_id,
auth_url=self.auth_url) auth_url=self.auth_url)
self.client.assert_called_once_with(
self.client.assert_called_once_with(version=self.version, version=self.version,
trust_id=self.trust_id, trust_id=self.trust_id,
username=self.username, username=self.username,
password=self.password, password=self.password,
auth_url=self.auth_url) auth_url=self.auth_url)
def test_client_from_kwargs_and_ctx(self): def test_client_from_kwargs_and_ctx(self):
self.keystone.BlazarKeystoneClient(
self.keystone.BlazarKeystoneClient(version=self.version, version=self.version,
username=self.username, username=self.username,
password=self.password, user_domain_name=self.user_domain_name,
tenant_name=self.tenant_name, password=self.password,
auth_url=self.auth_url) project_name=self.project_name,
project_domain_name=self.project_domain_name,
self.client.assert_called_once_with(version=self.version, auth_url=self.auth_url)
tenant_name=self.tenant_name, self.client.assert_called_once_with(
endpoint='http://fake.com/', version=self.version,
username=self.username, username=self.username,
password=self.password, user_domain_name=self.user_domain_name,
auth_url=self.auth_url, project_name=self.project_name,
global_request_id=self. project_domain_name=self.project_domain_name,
context.current(). endpoint='http://fake.com/',
global_request_id) password=self.password,
auth_url=self.auth_url,
global_request_id=self.context.current().global_request_id)
def test_client_from_ctx(self): def test_client_from_ctx(self):
self.keystone.BlazarKeystoneClient() self.keystone.BlazarKeystoneClient()
self.client.assert_called_once_with( self.client.assert_called_once_with(
version='3', version='3',
username=self.ctx().user_name, username=self.ctx().user_name,
user_domain_name=self.ctx().user_domain_name,
token=self.ctx().auth_token, token=self.ctx().auth_token,
tenant_name=self.ctx().project_name, project_name=self.ctx().project_name,
project_domain_name=self.ctx().project_domain_name,
auth_url='http://fake.com/', auth_url='http://fake.com/',
endpoint='http://fake.com/', endpoint='http://fake.com/',
global_request_id=self.context.current().global_request_id) global_request_id=self.context.current().global_request_id)

View File

@ -65,10 +65,8 @@ class TestTrusts(tests.TestCase):
'global_request_id': self.context.current().global_request_id, 'global_request_id': self.context.current().global_request_id,
'is_admin': False, 'is_admin': False,
'is_admin_project': True, 'is_admin_project': True,
'project': self.client().tenant_id, 'project': self.client().project_id,
'project_domain': None, 'project_domain': None,
'project_id': self.client().tenant_id,
'project_name': 'admin',
'read_only': False, 'read_only': False,
'request_id': ctx.request_id, 'request_id': ctx.request_id,
'resource_uuid': None, 'resource_uuid': None,
@ -76,10 +74,8 @@ class TestTrusts(tests.TestCase):
'service_catalog': ctx.service_catalog, 'service_catalog': ctx.service_catalog,
'show_deleted': False, 'show_deleted': False,
'system_scope': None, 'system_scope': None,
'tenant': self.client().tenant_id,
'user': None, 'user': None,
'user_domain': None, 'user_domain': None}
'user_id': None}
self.assertDictContainsSubset(fake_ctx_dict, ctx.to_dict()) self.assertDictContainsSubset(fake_ctx_dict, ctx.to_dict())
def test_use_trust_auth_dict(self): def test_use_trust_auth_dict(self):

View File

@ -101,7 +101,9 @@ class BlazarKeystoneClient(object):
kwargs.setdefault('version', cfg.CONF.keystone_client_version) kwargs.setdefault('version', cfg.CONF.keystone_client_version)
if ctx is not None: if ctx is not None:
kwargs.setdefault('username', ctx.user_name) kwargs.setdefault('username', ctx.user_name)
kwargs.setdefault('tenant_name', ctx.project_name) kwargs.setdefault('user_domain_name', ctx.user_domain_name)
kwargs.setdefault('project_name', ctx.project_name)
kwargs.setdefault('project_domain_name', ctx.project_domain_name)
kwargs.setdefault('global_request_id', ctx.global_request_id) kwargs.setdefault('global_request_id', ctx.global_request_id)
if not kwargs.get('auth_url'): if not kwargs.get('auth_url'):
kwargs['auth_url'] = base.url_for( kwargs['auth_url'] = base.url_for(
@ -121,8 +123,8 @@ class BlazarKeystoneClient(object):
# NOTE(dbelova): we need this checking to support current # NOTE(dbelova): we need this checking to support current
# keystoneclient: token can only be scoped now to either # keystoneclient: token can only be scoped now to either
# a trust or project, not both. # a trust or project, not both.
if kwargs.get('trust_id') and kwargs.get('tenant_name'): if kwargs.get('trust_id') and kwargs.get('project_name'):
kwargs.pop('tenant_name') kwargs.pop('project_name')
try: try:
# NOTE(n.s.): we shall remove this try: except: clause when # NOTE(n.s.): we shall remove this try: except: clause when

View File

@ -74,7 +74,7 @@ class ContextEndpointHandler(object):
method = getattr(self.__endpoint, name) method = getattr(self.__endpoint, name)
def run_method(__ctx, **kwargs): def run_method(__ctx, **kwargs):
with context.BlazarContext(**__ctx): with context.BlazarContext.from_dict(__ctx):
return method(**kwargs) return method(**kwargs)
return run_method return run_method

View File

@ -29,9 +29,8 @@ def create_trust():
client = keystone.BlazarKeystoneClient() client = keystone.BlazarKeystoneClient()
trustee_id = keystone.BlazarKeystoneClient( trustee_id = keystone.BlazarKeystoneClient(
username=CONF.os_admin_username,
password=CONF.os_admin_password, password=CONF.os_admin_password,
tenant_name=CONF.os_admin_project_name).user_id ctx=context.admin()).user_id
ctx = context.current() ctx = context.current()
trust = client.trusts.create(trustor_user=ctx.user_id, trust = client.trusts.create(trustor_user=ctx.user_id,
@ -53,31 +52,26 @@ def create_ctx_from_trust(trust_id):
"""Return context built from given trust.""" """Return context built from given trust."""
ctx = context.current() ctx = context.current()
ctx = context.BlazarContext(
user_name=CONF.os_admin_username,
project_name=CONF.os_admin_project_name,
request_id=ctx.request_id,
global_request_id=ctx.global_request_id
)
auth_url = "%s://%s:%s" % (CONF.os_auth_protocol, auth_url = "%s://%s:%s" % (CONF.os_auth_protocol,
base.get_os_auth_host(CONF), base.get_os_auth_host(CONF),
CONF.os_auth_port) CONF.os_auth_port)
if CONF.os_auth_prefix: if CONF.os_auth_prefix:
auth_url += "/%s" % CONF.os_auth_prefix auth_url += "/%s" % CONF.os_auth_prefix
client = keystone.BlazarKeystoneClient( client = keystone.BlazarKeystoneClient(
password=CONF.os_admin_password, password=CONF.os_admin_password,
trust_id=trust_id, trust_id=trust_id,
auth_url=auth_url, auth_url=auth_url,
ctx=ctx, ctx=context.admin(),
) )
# use 'with ctx' statement in the place you need context from trust # use 'with ctx' statement in the place you need context from trust
return context.BlazarContext( return context.BlazarContext(
user_name=ctx.user_name, user_name=ctx.user_name,
project_name=ctx.project_name, user_domain_name=ctx.user_domain_name,
auth_token=client.auth_token, auth_token=client.auth_token,
service_catalog=client.service_catalog.catalog['catalog'], service_catalog=client.service_catalog.catalog['catalog'],
project_id=client.tenant_id, project_id=client.project_id,
request_id=ctx.request_id, request_id=ctx.request_id,
global_request_id=ctx.global_request_id global_request_id=ctx.global_request_id
) )

View File

@ -0,0 +1,5 @@
---
fixes:
- |
Allows users of multiple Keystone domains to create leases; previously only users
and projects in the default domain could use Blazar.