Skip to content

Commit 0d6dd6c

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)
1 parent 8b4b991 commit 0d6dd6c

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
@@ -222,13 +222,15 @@ def _get_auth_plugin(context, admin=False):
222222
# support some services (metadata API) where an admin context is used
223223
# without an auth token.
224224
global _ADMIN_AUTH
225+
user_auth = None
225226
if admin or (context.is_admin and not context.auth_token):
226227
if not _ADMIN_AUTH:
227228
_ADMIN_AUTH = _load_auth_plugin(CONF)
228-
return _ADMIN_AUTH
229+
user_auth = _ADMIN_AUTH
229230

230-
if context.auth_token:
231-
return service_auth.get_auth_plugin(context)
231+
if context.auth_token or user_auth:
232+
# When user_auth = None, user_auth will be extracted from the context.
233+
return service_auth.get_auth_plugin(context, user_auth=user_auth)
232234

233235
# We did not get a user token and we should not be using
234236
# 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
@@ -142,6 +142,22 @@ def test_non_admin_with_service_token(self, mock_load):
142142
self.assertIsInstance(cl.httpclient.auth,
143143
service_token.ServiceTokenAuthWrapper)
144144

145+
@mock.patch('nova.service_auth._SERVICE_AUTH')
146+
@mock.patch('nova.network.neutron._ADMIN_AUTH')
147+
@mock.patch.object(ks_loading, 'load_auth_from_conf_options')
148+
def test_admin_with_service_token(
149+
self, mock_load, mock_admin_auth, mock_service_auth
150+
):
151+
self.flags(send_service_user_token=True, group='service_user')
152+
153+
admin_context = context.get_admin_context()
154+
155+
cl = neutronapi.get_client(admin_context)
156+
self.assertIsInstance(cl.httpclient.auth,
157+
service_token.ServiceTokenAuthWrapper)
158+
self.assertEqual(mock_admin_auth, cl.httpclient.auth.user_auth)
159+
self.assertEqual(mock_service_auth, cl.httpclient.auth.service_auth)
160+
145161
@mock.patch.object(client.Client, "list_networks",
146162
side_effect=exceptions.Unauthorized())
147163
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
@@ -56,3 +56,13 @@ def test_get_auth_plugin_wraps_bad_config(self, mock_load):
5656
result = service_auth.get_auth_plugin(self.ctx)
5757
self.assertEqual(1, mock_load.call_count)
5858
self.assertNotIsInstance(result, service_token.ServiceTokenAuthWrapper)
59+
60+
@mock.patch.object(ks_loading, 'load_auth_from_conf_options',
61+
new=mock.Mock())
62+
def test_get_auth_plugin_user_auth(self):
63+
self.flags(send_service_user_token=True, group='service_user')
64+
user_auth = mock.Mock()
65+
66+
result = service_auth.get_auth_plugin(self.ctx, user_auth=user_auth)
67+
68+
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
@@ -1276,3 +1276,14 @@ def test_admin_context_without_token(self,
12761276
admin_ctx = context.get_admin_context()
12771277
params = cinder._get_cinderclient_parameters(admin_ctx)
12781278
self.assertEqual(params[0], mock_admin_auth)
1279+
1280+
@mock.patch('nova.service_auth._SERVICE_AUTH')
1281+
@mock.patch('nova.volume.cinder._ADMIN_AUTH')
1282+
def test_admin_context_without_user_token_but_with_service_token(
1283+
self, mock_admin_auth, mock_service_auth
1284+
):
1285+
self.flags(send_service_user_token=True, group='service_user')
1286+
admin_ctx = context.get_admin_context()
1287+
params = cinder._get_cinderclient_parameters(admin_ctx)
1288+
self.assertEqual(mock_admin_auth, params[0].user_auth)
1289+
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)