Skip to content

Commit 48150a6

Browse files
committed
Enable use of service user token with admin context
When the [service_user] section is configured in nova.conf, nova will have the ability to send a service user token alongside the user's token. The service user token is sent when nova calls other services' REST APIs to authenticate as a service, and service calls can sometimes have elevated privileges. Currently, nova does not however have the ability to send a service user token with an admin context. This means that when nova makes REST API calls to other services with an anonymous admin RequestContext (such as in nova-manage or periodic tasks), it will not be authenticated as a service. This adds a keyword argument to service_auth.get_auth_plugin() to enable callers to provide a user_auth object instead of attempting to extract the user_auth from the RequestContext. The cinder and neutron client modules are also adjusted to make use of the new user_auth keyword argument so that nova calls made with anonymous admin request contexts can authenticate as a service when configured. Related-Bug: #2004555 Change-Id: I14df2d55f4b2f0be58f1a6ad3f19e48f7a6bfcb4 (cherry picked from commit 41c64b9) (cherry picked from commit 1f78142) (cherry picked from commit 0d6dd6c) (cherry picked from commit 98c3e37) (cherry picked from commit 6cc4e7f)
1 parent 5b4cb92 commit 48150a6

File tree

6 files changed

+51
-8
lines changed

6 files changed

+51
-8
lines changed

nova/network/neutron.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,15 @@ def _get_auth_plugin(context, admin=False):
208208
# support some services (metadata API) where an admin context is used
209209
# without an auth token.
210210
global _ADMIN_AUTH
211+
user_auth = None
211212
if admin or (context.is_admin and not context.auth_token):
212213
if not _ADMIN_AUTH:
213214
_ADMIN_AUTH = _load_auth_plugin(CONF)
214-
return _ADMIN_AUTH
215+
user_auth = _ADMIN_AUTH
215216

216-
if context.auth_token:
217-
return service_auth.get_auth_plugin(context)
217+
if context.auth_token or user_auth:
218+
# When user_auth = None, user_auth will be extracted from the context.
219+
return service_auth.get_auth_plugin(context, user_auth=user_auth)
218220

219221
# We did not get a user token and we should not be using
220222
# an admin token so log an error

nova/service_auth.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ def reset_globals():
3030
_SERVICE_AUTH = None
3131

3232

33-
def get_auth_plugin(context):
34-
user_auth = context.get_auth_plugin()
33+
def get_auth_plugin(context, user_auth=None):
34+
# user_auth may be passed in when the RequestContext is anonymous, such as
35+
# when get_admin_context() is used for API calls by nova-manage.
36+
user_auth = user_auth or context.get_auth_plugin()
3537

3638
if CONF.service_user.send_service_user_token:
3739
global _SERVICE_AUTH

nova/tests/unit/network/test_neutron.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,22 @@ def test_non_admin_with_service_token(self, mock_load):
143143
self.assertIsInstance(cl.httpclient.auth,
144144
service_token.ServiceTokenAuthWrapper)
145145

146+
@mock.patch('nova.service_auth._SERVICE_AUTH')
147+
@mock.patch('nova.network.neutron._ADMIN_AUTH')
148+
@mock.patch.object(ks_loading, 'load_auth_from_conf_options')
149+
def test_admin_with_service_token(
150+
self, mock_load, mock_admin_auth, mock_service_auth
151+
):
152+
self.flags(send_service_user_token=True, group='service_user')
153+
154+
admin_context = context.get_admin_context()
155+
156+
cl = neutronapi.get_client(admin_context)
157+
self.assertIsInstance(cl.httpclient.auth,
158+
service_token.ServiceTokenAuthWrapper)
159+
self.assertEqual(mock_admin_auth, cl.httpclient.auth.user_auth)
160+
self.assertEqual(mock_service_auth, cl.httpclient.auth.service_auth)
161+
146162
@mock.patch.object(client.Client, "list_networks",
147163
side_effect=exceptions.Unauthorized())
148164
def test_Unauthorized_user(self, mock_list_networks):

nova/tests/unit/test_service_auth.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,13 @@ def test_get_auth_plugin_wraps_bad_config(self, mock_load):
5555
result = service_auth.get_auth_plugin(self.ctx)
5656
self.assertEqual(1, mock_load.call_count)
5757
self.assertNotIsInstance(result, service_token.ServiceTokenAuthWrapper)
58+
59+
@mock.patch.object(ks_loading, 'load_auth_from_conf_options',
60+
new=mock.Mock())
61+
def test_get_auth_plugin_user_auth(self):
62+
self.flags(send_service_user_token=True, group='service_user')
63+
user_auth = mock.Mock()
64+
65+
result = service_auth.get_auth_plugin(self.ctx, user_auth=user_auth)
66+
67+
self.assertEqual(user_auth, result.user_auth)

nova/tests/unit/volume/test_cinder.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,3 +1264,14 @@ def test_admin_context_without_token(self,
12641264
admin_ctx = context.get_admin_context()
12651265
params = cinder._get_cinderclient_parameters(admin_ctx)
12661266
self.assertEqual(params[0], mock_admin_auth)
1267+
1268+
@mock.patch('nova.service_auth._SERVICE_AUTH')
1269+
@mock.patch('nova.volume.cinder._ADMIN_AUTH')
1270+
def test_admin_context_without_user_token_but_with_service_token(
1271+
self, mock_admin_auth, mock_service_auth
1272+
):
1273+
self.flags(send_service_user_token=True, group='service_user')
1274+
admin_ctx = context.get_admin_context()
1275+
params = cinder._get_cinderclient_parameters(admin_ctx)
1276+
self.assertEqual(mock_admin_auth, params[0].user_auth)
1277+
self.assertEqual(mock_service_auth, params[0].service_auth)

nova/volume/cinder.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,14 @@ def _get_auth(context):
9191
# from them generated from 'context.get_admin_context'
9292
# which only set is_admin=True but is without token.
9393
# So add load_auth_plugin when this condition appear.
94+
user_auth = None
9495
if context.is_admin and not context.auth_token:
9596
if not _ADMIN_AUTH:
9697
_ADMIN_AUTH = _load_auth_plugin(CONF)
97-
return _ADMIN_AUTH
98-
else:
99-
return service_auth.get_auth_plugin(context)
98+
user_auth = _ADMIN_AUTH
99+
100+
# When user_auth = None, user_auth will be extracted from the context.
101+
return service_auth.get_auth_plugin(context, user_auth=user_auth)
100102

101103

102104
# NOTE(efried): Bug #1752152

0 commit comments

Comments
 (0)