From 778a52e66aeab883b31db729e04944eeecfec6a2 Mon Sep 17 00:00:00 2001 From: Rodrigo Barbieri Date: Wed, 7 Sep 2022 10:52:48 -0300 Subject: Fix app cred create without project_id for domain admins Users with domain admin role that are not cloud admins are not able to get scoped context and create an application credential with project_id, so this change forces the scoped context in that particular case. Closes-bug: #1827120 Change-Id: I076a97a6f943ab74a2db8bc5179a7db194009db4 (cherry picked from commit 6eeaf9852478e25ff77c21117664ac126c5357a4) --- openstack_dashboard/api/keystone.py | 17 +++++++-- openstack_dashboard/test/unit/api/test_keystone.py | 44 ++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/openstack_dashboard/api/keystone.py b/openstack_dashboard/api/keystone.py index d8aca8e71..cfaebfeab 100644 --- a/openstack_dashboard/api/keystone.py +++ b/openstack_dashboard/api/keystone.py @@ -120,7 +120,7 @@ def _get_endpoint_url(request, endpoint_type, catalog=None): return url -def keystoneclient(request, admin=False): +def keystoneclient(request, admin=False, force_scoped=False): """Returns a client connected to the Keystone backend. Several forms of authentication are supported: @@ -152,7 +152,8 @@ def keystoneclient(request, admin=False): # If user is Cloud Admin, Domain Admin or Mixed Domain Admin and there # is no domain context specified, use domain scoped token - if is_domain_admin(request) and not is_domain_context_specified: + if (is_domain_admin(request) and not is_domain_context_specified and + not force_scoped): domain_token = request.session.get('domain_token') if domain_token: token_id = getattr(domain_token, 'auth_token', None) @@ -995,7 +996,17 @@ def application_credential_create(request, name, secret=None, roles=None, unrestricted=False, access_rules=None): user = request.user.id - manager = keystoneclient(request).application_credentials + # NOTE(ganso): users with domain admin role that are not cloud admins are + # not able to get scoped context and create an application credential with + # project_id, so only in this particular case we force a scoped context + force_scoped = False + if (request.user.project_id and request.session.get("domain_token") and + not policy.check( + (("identity", "identity:update_domain"),), request)): + force_scoped = True + + manager = keystoneclient( + request, force_scoped=force_scoped).application_credentials try: return manager.create(name=name, user=user, secret=secret, description=description, expires_at=expires_at, diff --git a/openstack_dashboard/test/unit/api/test_keystone.py b/openstack_dashboard/test/unit/api/test_keystone.py index 4598c2d50..71e8a6314 100644 --- a/openstack_dashboard/test/unit/api/test_keystone.py +++ b/openstack_dashboard/test/unit/api/test_keystone.py @@ -21,6 +21,7 @@ from unittest import mock from django.test.utils import override_settings from openstack_dashboard import api +from openstack_dashboard import policy from openstack_dashboard.test import helpers as test @@ -164,3 +165,46 @@ class APIVersionTests(test.APIMockTestCase): keystoneclient.session.get_endpoint_data.assert_called_once_with( service_type='identity') self.assertEqual((3, 10), api_version) + + +class ApplicationCredentialsAPITests(test.APIMockTestCase): + + @mock.patch.object(policy, 'check') + @mock.patch.object(api.keystone, 'keystoneclient') + def test_application_credential_create_domain_token_removed( + self, mock_keystoneclient, mock_policy): + self.request.session['domain_token'] = 'some_token' + mock_policy.return_value = False + api.keystone.application_credential_create(self.request, None) + mock_keystoneclient.assert_called_once_with( + self.request, force_scoped=True) + + @mock.patch.object(policy, 'check') + @mock.patch.object(api.keystone, 'keystoneclient') + def test_application_credential_create_domain_token_not_removed_policy_true( + self, mock_keystoneclient, mock_policy): + self.request.session['domain_token'] = 'some_token' + mock_policy.return_value = True + api.keystone.application_credential_create(self.request, None) + mock_keystoneclient.assert_called_once_with( + self.request, force_scoped=False) + + @mock.patch.object(policy, 'check') + @mock.patch.object(api.keystone, 'keystoneclient') + def test_application_credential_create_domain_token_not_removed_no_token( + self, mock_keystoneclient, mock_policy): + mock_policy.return_value = True + api.keystone.application_credential_create(self.request, None) + mock_keystoneclient.assert_called_once_with( + self.request, force_scoped=False) + + @mock.patch.object(policy, 'check') + @mock.patch.object(api.keystone, 'keystoneclient') + def test_application_credential_create_domain_token_not_removed_no_project( + self, mock_keystoneclient, mock_policy): + self.request.session['domain_token'] = 'some_token' + mock_policy.return_value = True + self.request.user.project_id = None + api.keystone.application_credential_create(self.request, None) + mock_keystoneclient.assert_called_once_with( + self.request, force_scoped=False) -- cgit v1.2.1