From 3eba764be3dcbafa01e1e4298bdf1810925a6f49 Mon Sep 17 00:00:00 2001 From: Vladyslav Drok Date: Mon, 21 Mar 2016 17:46:55 +0200 Subject: [PATCH] Add user and project domains to ironic context This change also removes most of the logic from ironic's RequestContext to reuse the oslo_context as much as possible. Usage of domain_id and domain_name in policy files is deprecated and their support will be removed in the Pike release. domain_id field was removed from the context class completely, domain_name value now mathces the oslo_context expectations. ContextHook is changed too so as not to duplicate from_environ functional from oslo_context. to_dict method left as is, so that we don't break an older service receiving the context over RPC. It will be changed in Pike release to reuse the base oslo_context class' to_dict. Closes-Bug: #1602081 Closes-Bug: #1627173 Closes-Bug: #1641972 Co-Authored-By: Jamie Lennox Co-Authored-By: Devananda van der Veen Change-Id: I9afe89bc6aee282ee4b7579d661e3fa83cc0ce84 --- etc/ironic/policy.json.sample | 8 +- ironic/api/hooks.py | 87 +++++----- ironic/common/context.py | 69 ++++---- ironic/common/policy.py | 4 +- ironic/tests/unit/api/test_hooks.py | 153 +++++++----------- ironic/tests/unit/common/test_context.py | 92 +++++------ ironic/tests/unit/common/test_policy.py | 21 ++- ironic/tests/unit/common/test_rpc.py | 8 +- ironic/tests/unit/objects/test_objects.py | 4 +- ...-id-name-deprecation-ae6e40718273be8d.yaml | 10 ++ 10 files changed, 221 insertions(+), 235 deletions(-) create mode 100644 releasenotes/notes/context-domain-id-name-deprecation-ae6e40718273be8d.yaml diff --git a/etc/ironic/policy.json.sample b/etc/ironic/policy.json.sample index 177ded5b11..e328eade25 100644 --- a/etc/ironic/policy.json.sample +++ b/etc/ironic/policy.json.sample @@ -2,12 +2,12 @@ "admin_api": "role:admin or role:administrator" # Internal flag for public API routes "public_api": "is_public_api:True" -# Show or mask secrets within driver_info in API responses +# Show or mask secrets within node driver information in API responses "show_password": "!" -# Show or mask secrets within instance_info in API responses +# Show or mask secrets within instance information in API responses "show_instance_secrets": "!" -# May be used to restrict access to specific tenants -"is_member": "tenant:demo or tenant:baremetal" +# May be used to restrict access to specific projects +"is_member": "(project_domain_id:default or project_domain_id:None) and (project_name:demo or project_name:baremetal)" # Read-only API access "is_observer": "rule:is_member and (role:observer or role:baremetal_observer)" # Full read/write API access diff --git a/ironic/api/hooks.py b/ironic/api/hooks.py index d3f3838d0f..65321de6e8 100644 --- a/ironic/api/hooks.py +++ b/ironic/api/hooks.py @@ -14,15 +14,55 @@ # License for the specific language governing permissions and limitations # under the License. +import re + from oslo_config import cfg +from oslo_log import log from pecan import hooks +import six from six.moves import http_client from ironic.common import context +from ironic.common.i18n import _LW from ironic.common import policy from ironic.conductor import rpcapi from ironic.db import api as dbapi +LOG = log.getLogger(__name__) + +CHECKED_DEPRECATED_POLICY_ARGS = False + + +def policy_deprecation_check(): + global CHECKED_DEPRECATED_POLICY_ARGS + if not CHECKED_DEPRECATED_POLICY_ARGS: + enforcer = policy.get_enforcer() + substitution_dict = { + 'user': 'user_id', + 'domain_id': 'user_domain_id', + 'domain_name': 'user_domain_id', + 'tenant': 'project_name', + } + policy_rules = enforcer.file_rules.values() + for rule in policy_rules: + str_rule = six.text_type(rule) + for deprecated, replacement in substitution_dict.items(): + if re.search(r'\b%s\b' % deprecated, str_rule): + LOG.warning(_LW( + "Deprecated argument %(deprecated)s is used in policy " + "file rule (%(rule)s), please use %(replacement)s " + "argument instead. The possibility to use deprecated " + "arguments will be removed in the Pike release."), + {'deprecated': deprecated, 'replacement': replacement, + 'rule': str_rule}) + if deprecated == 'domain_name': + LOG.warning(_LW( + "Please note that user_domain_id is an ID of the " + "user domain, while the deprecated domain_name is " + "its name. The policy rule has to be updated " + "accordingly.")) + CHECKED_DEPRECATED_POLICY_ARGS = True + class ConfigHook(hooks.PecanHook): """Attach the config object to the request so controllers can get to it.""" @@ -39,52 +79,25 @@ class DBHook(hooks.PecanHook): class ContextHook(hooks.PecanHook): - """Configures a request context and attaches it to the request. - - The following HTTP request headers are used: - - X-User-Id or X-User: - Used for context.user_id. - - X-Tenant-Id or X-Tenant: - Used for context.tenant. - - X-Auth-Token: - Used for context.auth_token. - - X-Roles: - Used for setting context.is_admin flag to either True or False. - The flag is set to True, if X-Roles contains either an administrator - or admin substring. Otherwise it is set to False. - - """ + """Configures a request context and attaches it to the request.""" def __init__(self, public_api_routes): self.public_api_routes = public_api_routes super(ContextHook, self).__init__() def before(self, state): - headers = state.request.headers - - # Do not pass any token with context for noauth mode - auth_token = (None if cfg.CONF.auth_strategy == 'noauth' else - headers.get('X-Auth-Token')) is_public_api = state.request.environ.get('is_public_api', False) + ctx = context.RequestContext.from_environ(state.request.environ, + is_public_api=is_public_api) + # Do not pass any token with context for noauth mode + if cfg.CONF.auth_strategy == 'noauth': + ctx.auth_token = None - creds = { - 'user': headers.get('X-User') or headers.get('X-User-Id'), - 'tenant': headers.get('X-Tenant') or headers.get('X-Tenant-Id'), - 'domain_id': headers.get('X-User-Domain-Id'), - 'domain_name': headers.get('X-User-Domain-Name'), - 'auth_token': auth_token, - 'roles': headers.get('X-Roles', '').split(','), - 'is_public_api': is_public_api, - } - + creds = ctx.to_policy_values() is_admin = policy.check('is_admin', creds, creds) + ctx.is_admin = is_admin + policy_deprecation_check() - state.request.context = context.RequestContext( - is_admin=is_admin, - **creds) + state.request.context = ctx def after(self, state): if state.request.context == {}: diff --git a/ironic/common/context.py b/ironic/common/context.py index 6e9c7e34b1..e8c836567e 100644 --- a/ironic/common/context.py +++ b/ironic/common/context.py @@ -13,49 +13,40 @@ # under the License. from oslo_context import context +from oslo_log import log + +LOG = log.getLogger(__name__) class RequestContext(context.RequestContext): """Extends security contexts from the oslo.context library.""" - def __init__(self, auth_token=None, domain_id=None, domain_name=None, - user=None, tenant=None, is_admin=False, is_public_api=False, - read_only=False, show_deleted=False, request_id=None, - roles=None, overwrite=True): + def __init__(self, is_public_api=False, **kwargs): """Initialize the RequestContext - :param auth_token: The authentication token of the current request. - :param domain_id: The ID of the domain. - :param domain_name: The name of the domain. - :param user: The name of the user. - :param tenant: The name of the tenant. - :param is_admin: Indicates if the request context is an administrator - context. :param is_public_api: Specifies whether the request should be processed - without authentication. - :param read_only: unused flag for Ironic. - :param show_deleted: unused flag for Ironic. - :param request_id: The UUID of the request. - :param roles: List of user's roles if any. - :param overwrite: Set to False to ensure that the greenthread local - copy of the index is not overwritten. + without authentication. + :param kwargs: additional arguments passed to oslo.context. """ - super(RequestContext, self).__init__(auth_token=auth_token, - user=user, tenant=tenant, - is_admin=is_admin, - read_only=read_only, - show_deleted=show_deleted, - request_id=request_id, - overwrite=overwrite) + super(RequestContext, self).__init__(**kwargs) self.is_public_api = is_public_api - self.domain_id = domain_id - self.domain_name = domain_name - # NOTE(dims): roles was added in context.RequestContext recently. - # we should pass roles in __init__ above instead of setting the - # value here once the minimum version of oslo.context is updated. - self.roles = roles or [] + + def to_policy_values(self): + policy_values = super(RequestContext, self).to_policy_values() + # TODO(vdrok): remove all of these apart from is_public_api and + # project_name after deprecation period + policy_values.update({ + 'user': self.user, + 'domain_id': self.user_domain, + 'domain_name': self.user_domain_name, + 'tenant': self.tenant, + 'project_name': self.project_name, + 'is_public_api': self.is_public_api, + }) + return policy_values def to_dict(self): + # TODO(vdrok): reuse the base class to_dict in Pike return {'auth_token': self.auth_token, 'user': self.user, 'tenant': self.tenant, @@ -63,16 +54,18 @@ class RequestContext(context.RequestContext): 'read_only': self.read_only, 'show_deleted': self.show_deleted, 'request_id': self.request_id, - 'domain_id': self.domain_id, + 'domain_id': self.user_domain, 'roles': self.roles, - 'domain_name': self.domain_name, + 'domain_name': self.user_domain_name, 'is_public_api': self.is_public_api} @classmethod - def from_dict(cls, values): - values.pop('user', None) - values.pop('tenant', None) - return cls(**values) + def from_dict(cls, values, **kwargs): + kwargs.setdefault('is_public_api', values.get('is_public_api', False)) + if 'domain_id' in values: + kwargs.setdefault('user_domain', values['domain_id']) + return super(RequestContext, RequestContext).from_dict(values, + **kwargs) def ensure_thread_contain_context(self): """Ensure threading contains context @@ -90,7 +83,7 @@ class RequestContext(context.RequestContext): def get_admin_context(): """Create an administrator context.""" - context = RequestContext(None, + context = RequestContext(auth_token=None, tenant=None, is_admin=True, overwrite=False) diff --git a/ironic/common/policy.py b/ironic/common/policy.py index 87199af450..79119736d3 100644 --- a/ironic/common/policy.py +++ b/ironic/common/policy.py @@ -55,8 +55,8 @@ default_policies = [ description='Show or mask secrets within instance information in API responses'), # noqa # Roles likely to be overridden by operator policy.RuleDefault('is_member', - 'tenant:demo or tenant:baremetal', - description='May be used to restrict access to specific tenants'), # noqa + '(project_domain_id:default or project_domain_id:None) and (project_name:demo or project_name:baremetal)', # noqa + description='May be used to restrict access to specific projects'), # noqa policy.RuleDefault('is_observer', 'rule:is_member and (role:observer or role:baremetal_observer)', # noqa description='Read-only API access'), diff --git a/ironic/tests/unit/api/test_hooks.py b/ironic/tests/unit/api/test_hooks.py index 88eaada516..08cdda30a0 100644 --- a/ironic/tests/unit/api/test_hooks.py +++ b/ironic/tests/unit/api/test_hooks.py @@ -25,6 +25,8 @@ from six.moves import http_client from ironic.api.controllers import root from ironic.api import hooks from ironic.common import context +from ironic.common import policy +from ironic.tests import base as tests_base from ironic.tests.unit.api import base @@ -42,24 +44,6 @@ class FakeRequestState(object): self.request = FakeRequest(headers, context, environ) self.response = FakeRequest(headers, context, environ) - def set_context(self): - headers = self.request.headers - creds = { - 'user': headers.get('X-User') or headers.get('X-User-Id'), - 'tenant': headers.get('X-Tenant') or headers.get('X-Tenant-Id'), - 'domain_id': headers.get('X-User-Domain-Id'), - 'domain_name': headers.get('X-User-Domain-Name'), - 'auth_token': headers.get('X-Auth-Token'), - 'roles': headers.get('X-Roles', '').split(','), - } - is_admin = ('admin' in creds['roles'] or - 'administrator' in creds['roles']) - is_public_api = self.request.environ.get('is_public_api', False) - - self.request.context = context.RequestContext( - is_admin=is_admin, is_public_api=is_public_api, - **creds) - def fake_headers(admin=False): headers = { @@ -99,6 +83,14 @@ def fake_headers(admin=False): return headers +def headers_to_environ(headers, **kwargs): + environ = {} + for k, v in headers.items(): + environ['HTTP_%s' % k.replace('-', '_').upper()] = v + environ.update(kwargs) + return environ + + class TestNoExceptionTracebackHook(base.BaseApiTest): TRACE = [u'Traceback (most recent call last):', @@ -212,88 +204,52 @@ class TestNoExceptionTracebackHook(base.BaseApiTest): class TestContextHook(base.BaseApiTest): - @mock.patch.object(context, 'RequestContext') - def test_context_hook_not_admin(self, mock_ctx): - cfg.CONF.set_override('auth_strategy', 'keystone') - headers = fake_headers(admin=False) - reqstate = FakeRequestState(headers=headers) - context_hook = hooks.ContextHook(None) - context_hook.before(reqstate) - mock_ctx.assert_called_with( - auth_token=headers['X-Auth-Token'], - user=headers['X-User'], - tenant=headers['X-Tenant'], - domain_id=headers['X-User-Domain-Id'], - domain_name=headers['X-User-Domain-Name'], - is_public_api=False, - is_admin=False, - roles=headers['X-Roles'].split(',')) @mock.patch.object(context, 'RequestContext') - def test_context_hook_admin(self, mock_ctx): - cfg.CONF.set_override('auth_strategy', 'keystone') - headers = fake_headers(admin=True) - reqstate = FakeRequestState(headers=headers) + @mock.patch.object(policy, 'check') + def _test_context_hook(self, mock_policy, mock_ctx, is_admin=False, + is_public_api=False, auth_strategy='keystone', + request_id=None): + cfg.CONF.set_override('auth_strategy', auth_strategy) + headers = fake_headers(admin=is_admin) + environ = headers_to_environ(headers, is_public_api=is_public_api) + reqstate = FakeRequestState(headers=headers, environ=environ) context_hook = hooks.ContextHook(None) + ctx = mock.Mock() + if request_id: + ctx.request_id = request_id + mock_ctx.from_environ.return_value = ctx + policy_dict = {'user_id': 'foo'} # Lots of other values here + ctx.to_policy_values.return_value = policy_dict + mock_policy.return_value = is_admin context_hook.before(reqstate) - mock_ctx.assert_called_with( - auth_token=headers['X-Auth-Token'], - user=headers['X-User'], - tenant=headers['X-Tenant'], - domain_id=headers['X-User-Domain-Id'], - domain_name=headers['X-User-Domain-Name'], - is_public_api=False, - is_admin=True, - roles=headers['X-Roles'].split(',')) + creds_dict = {'is_public_api': is_public_api} + mock_ctx.from_environ.assert_called_once_with(environ, **creds_dict) + mock_policy.assert_called_once_with('is_admin', policy_dict, + policy_dict) + self.assertIs(is_admin, ctx.is_admin) + if auth_strategy == 'noauth': + self.assertIsNone(ctx.auth_token) + return context_hook, reqstate - @mock.patch.object(context, 'RequestContext') - def test_context_hook_public_api(self, mock_ctx): - cfg.CONF.set_override('auth_strategy', 'keystone') - headers = fake_headers(admin=True) - env = {'is_public_api': True} - reqstate = FakeRequestState(headers=headers, environ=env) - context_hook = hooks.ContextHook(None) - context_hook.before(reqstate) - mock_ctx.assert_called_with( - auth_token=headers['X-Auth-Token'], - user=headers['X-User'], - tenant=headers['X-Tenant'], - domain_id=headers['X-User-Domain-Id'], - domain_name=headers['X-User-Domain-Name'], - is_public_api=True, - is_admin=True, - roles=headers['X-Roles'].split(',')) + def test_context_hook_not_admin(self): + self._test_context_hook() - @mock.patch.object(context, 'RequestContext') - def test_context_hook_noauth_token_removed(self, mock_ctx): - cfg.CONF.set_override('auth_strategy', 'noauth') - headers = fake_headers(admin=False) - reqstate = FakeRequestState(headers=headers) - context_hook = hooks.ContextHook(None) - context_hook.before(reqstate) - mock_ctx.assert_called_with( - auth_token=None, - user=headers['X-User'], - tenant=headers['X-Tenant'], - domain_id=headers['X-User-Domain-Id'], - domain_name=headers['X-User-Domain-Name'], - is_public_api=False, - is_admin=False, - roles=headers['X-Roles'].split(',')) + def test_context_hook_admin(self): + self._test_context_hook(is_admin=True) - @mock.patch.object(context, 'RequestContext') - def test_context_hook_after_add_request_id(self, mock_ctx): - headers = fake_headers(admin=True) - reqstate = FakeRequestState(headers=headers) - reqstate.set_context() - reqstate.request.context.request_id = 'fake-id' - context_hook = hooks.ContextHook(None) + def test_context_hook_public_api(self): + self._test_context_hook(is_admin=True, is_public_api=True) + + def test_context_hook_noauth_token_removed(self): + self._test_context_hook(auth_strategy='noauth') + + def test_context_hook_after_add_request_id(self): + context_hook, reqstate = self._test_context_hook(is_admin=True, + request_id='fake-id') context_hook.after(reqstate) - self.assertIn('Openstack-Request-Id', - reqstate.response.headers) - self.assertEqual( - 'fake-id', - reqstate.response.headers['Openstack-Request-Id']) + self.assertEqual('fake-id', + reqstate.response.headers['Openstack-Request-Id']) def test_context_hook_after_miss_context(self): response = self.get_json('/bad/path', @@ -302,6 +258,19 @@ class TestContextHook(base.BaseApiTest): response.headers) +class TestPolicyDeprecation(tests_base.TestCase): + + @mock.patch.object(hooks, 'CHECKED_DEPRECATED_POLICY_ARGS', False) + @mock.patch.object(hooks.LOG, 'warning') + @mock.patch.object(policy, 'get_enforcer') + def test_policy_deprecation_check(self, enforcer_mock, warning_mock): + rules = {'is_member': 'project_name:demo or tenant:baremetal', + 'is_default_project_domain': 'project_domain_id:default'} + enforcer_mock.return_value = mock.Mock(file_rules=rules, autospec=True) + hooks.policy_deprecation_check() + self.assertEqual(1, warning_mock.call_count) + + class TestPublicUrlHook(base.BaseApiTest): def test_before_host_url(self): diff --git a/ironic/tests/unit/common/test_context.py b/ironic/tests/unit/common/test_context.py index cf9286b66e..f44cfe0bb5 100644 --- a/ironic/tests/unit/common/test_context.py +++ b/ironic/tests/unit/common/test_context.py @@ -20,66 +20,33 @@ from ironic.tests import base as tests_base class RequestContextTestCase(tests_base.TestCase): def setUp(self): super(RequestContextTestCase, self).setUp() - - @mock.patch.object(oslo_context.RequestContext, "__init__") - def test_create_context(self, context_mock): - test_context = context.RequestContext() - context_mock.assert_called_once_with( - auth_token=None, user=None, tenant=None, is_admin=False, - read_only=False, show_deleted=False, request_id=None, - overwrite=True) - self.assertFalse(test_context.is_public_api) - self.assertIsNone(test_context.domain_id) - self.assertIsNone(test_context.domain_name) - self.assertEqual([], test_context.roles) - - def test_from_dict(self): - dict = { - "user": "user1", - "tenant": "tenant1", - "is_public_api": True, - "domain_id": "domain_id1", - "domain_name": "domain_name1", - "roles": None - } - ctx = context.RequestContext.from_dict(dict) - self.assertIsNone(ctx.user) - self.assertIsNone(ctx.tenant) - self.assertTrue(ctx.is_public_api) - self.assertEqual("domain_id1", ctx.domain_id) - self.assertEqual("domain_name1", ctx.domain_name) - self.assertEqual([], ctx.roles) - - def test_to_dict(self): - values = { + self.context_dict = { 'auth_token': 'auth_token1', "user": "user1", "tenant": "tenant1", + "project_name": "somename", 'is_admin': True, 'read_only': True, 'show_deleted': True, 'request_id': 'id1', "is_public_api": True, - "domain_id": "domain_id1", - "domain_name": "domain_name1", + "domain": "domain_id2", + "user_domain": "domain_id3", + "user_domain_name": "TreeDomain", + "project_domain": "domain_id4", "roles": None, "overwrite": True } - ctx = context.RequestContext(**values) - ctx_dict = ctx.to_dict() - self.assertIn('auth_token', ctx_dict) - self.assertIn('user', ctx_dict) - self.assertIn('tenant', ctx_dict) - self.assertIn('is_admin', ctx_dict) - self.assertIn('read_only', ctx_dict) - self.assertIn('show_deleted', ctx_dict) - self.assertIn('request_id', ctx_dict) - self.assertIn('domain_id', ctx_dict) - self.assertIn('roles', ctx_dict) - self.assertIn('domain_name', ctx_dict) - self.assertIn('is_public_api', ctx_dict) - self.assertNotIn('overwrite', ctx_dict) + @mock.patch.object(oslo_context.RequestContext, "__init__") + def test_create_context(self, context_mock): + test_context = context.RequestContext() + context_mock.assert_called_once_with() + self.assertFalse(test_context.is_public_api) + + def test_to_dict(self): + ctx = context.RequestContext(**self.context_dict) + ctx_dict = ctx.to_dict() self.assertEqual('auth_token1', ctx_dict['auth_token']) self.assertEqual('user1', ctx_dict['user']) self.assertEqual('tenant1', ctx_dict['tenant']) @@ -88,8 +55,33 @@ class RequestContextTestCase(tests_base.TestCase): self.assertTrue(ctx_dict['show_deleted']) self.assertEqual('id1', ctx_dict['request_id']) self.assertTrue(ctx_dict['is_public_api']) - self.assertEqual('domain_id1', ctx_dict['domain_id']) - self.assertEqual('domain_name1', ctx_dict['domain_name']) + self.assertEqual('domain_id3', ctx_dict['domain_id']) + self.assertEqual('TreeDomain', ctx_dict['domain_name']) + self.assertEqual([], ctx_dict['roles']) + self.assertNotIn('overwrite', ctx_dict) + + def test_from_dict(self): + test_context = context.RequestContext.from_dict( + {'project_name': 'demo', 'is_public_api': True, + 'domain_id': 'meow'}) + self.assertEqual('demo', test_context.project_name) + self.assertEqual('meow', test_context.user_domain) + self.assertTrue(test_context.is_public_api) + + def test_to_policy_values(self): + ctx = context.RequestContext(**self.context_dict) + ctx_dict = ctx.to_policy_values() + self.assertEqual('user1', ctx_dict['user']) + self.assertEqual('user1', ctx_dict['user_id']) + self.assertEqual('tenant1', ctx_dict['tenant']) + self.assertEqual('tenant1', ctx_dict['project_id']) + self.assertEqual('somename', ctx_dict['project_name']) + self.assertTrue(ctx_dict['is_public_api']) + self.assertTrue(ctx_dict['is_admin_project']) + self.assertEqual('domain_id3', ctx_dict['domain_id']) + self.assertEqual('TreeDomain', ctx_dict['domain_name']) + self.assertEqual('domain_id3', ctx_dict['user_domain_id']) + self.assertEqual('domain_id4', ctx_dict['project_domain_id']) self.assertEqual([], ctx_dict['roles']) def test_get_admin_context(self): diff --git a/ironic/tests/unit/common/test_policy.py b/ironic/tests/unit/common/test_policy.py index db0a3e437c..208fcbbbd1 100644 --- a/ironic/tests/unit/common/test_policy.py +++ b/ironic/tests/unit/common/test_policy.py @@ -42,15 +42,28 @@ class PolicyInCodeTestCase(base.TestCase): self.assertTrue(policy.check('public_api', creds, creds)) def test_show_password(self): - creds = {'roles': [u'admin'], 'tenant': 'admin'} - self.assertTrue(policy.check('show_password', creds, creds)) + creds = {'roles': [u'admin'], 'project_name': 'admin', + 'project_domain_id': 'default'} + self.assertFalse(policy.check('show_password', creds, creds)) + + def test_is_member(self): + creds = [{'project_name': 'demo', 'project_domain_id': 'default'}, + {'project_name': 'baremetal', 'project_domain_id': 'default'}, + {'project_name': 'demo', 'project_domain_id': None}, + {'project_name': 'baremetal', 'project_domain_id': None}] + for c in creds: + self.assertTrue(policy.check('is_member', c, c)) + c = {'project_name': 'demo1', 'project_domain_id': 'default2'} + self.assertFalse(policy.check('is_member', c, c)) def test_node_get(self): - creds = {'roles': ['baremetal_observer'], 'tenant': 'demo'} + creds = {'roles': ['baremetal_observer'], 'project_name': 'demo', + 'project_domain_id': 'default'} self.assertTrue(policy.check('baremetal:node:get', creds, creds)) def test_node_create(self): - creds = {'roles': ['baremetal_admin'], 'tenant': 'demo'} + creds = {'roles': ['baremetal_admin'], 'project_name': 'demo', + 'project_domain_id': 'default'} self.assertTrue(policy.check('baremetal:node:create', creds, creds)) diff --git a/ironic/tests/unit/common/test_rpc.py b/ironic/tests/unit/common/test_rpc.py index 197f553eb8..382a04c3bc 100644 --- a/ironic/tests/unit/common/test_rpc.py +++ b/ironic/tests/unit/common/test_rpc.py @@ -179,11 +179,7 @@ class TestRequestContextSerializer(base.TestCase): self.assertEqual(self.context.to_dict(), serialize_values) def test_deserialize_context(self): - self.context.user = 'fake-user' - self.context.tenant = 'fake-tenant' serialize_values = self.context.to_dict() new_context = self.serializer.deserialize_context(serialize_values) - # Ironic RequestContext from_dict will pop 'user' and 'tenant' and - # initialize to None. - self.assertIsNone(new_context.user) - self.assertIsNone(new_context.tenant) + self.assertEqual(serialize_values, new_context.to_dict()) + self.assertIsInstance(new_context, ironic_context.RequestContext) diff --git a/ironic/tests/unit/objects/test_objects.py b/ironic/tests/unit/objects/test_objects.py index 8fdc583e79..0d499318f3 100644 --- a/ironic/tests/unit/objects/test_objects.py +++ b/ironic/tests/unit/objects/test_objects.py @@ -209,8 +209,8 @@ class _TestObject(object): base.IronicObject.obj_class_from_name, 'foo', '1.0') def test_with_alternate_context(self): - ctxt1 = context.RequestContext('foo', 'foo') - ctxt2 = context.RequestContext('bar', tenant='alternate') + ctxt1 = context.RequestContext(auth_token='foo', tenant='foo') + ctxt2 = context.RequestContext(auth_token='bar', tenant='alternate') obj = MyObj.query(ctxt1) obj.update_test(ctxt2) self.assertEqual('alternate-context', obj.bar) diff --git a/releasenotes/notes/context-domain-id-name-deprecation-ae6e40718273be8d.yaml b/releasenotes/notes/context-domain-id-name-deprecation-ae6e40718273be8d.yaml new file mode 100644 index 0000000000..9a8aa7b655 --- /dev/null +++ b/releasenotes/notes/context-domain-id-name-deprecation-ae6e40718273be8d.yaml @@ -0,0 +1,10 @@ +--- +deprecations: + - | + Usage of the following values was deprecated in the policy files: + + - domain_id and domain_name - user_domain_id should be used + instead of those (note - user_domain is an ID of the domain, + not its name); + - tenant - project_name should be used instead; + - user - user_id should be used instead.