Merge "Add get_communication_params interface to plugins"
This commit is contained in:
		| @@ -168,6 +168,19 @@ class BaseAuthPlugin(object): | ||||
|         """ | ||||
|         return None | ||||
|  | ||||
|     def get_connection_params(self, session, **kwargs): | ||||
|         """Return any additional connection parameters required for the plugin. | ||||
|  | ||||
|         :param session: The session object that the auth_plugin belongs to. | ||||
|         :type session: keystoneclient.session.Session | ||||
|  | ||||
|         :returns: Headers that are set to authenticate a message or None for | ||||
|                   failure. Note that when checking this value that the empty | ||||
|                   dict is a valid, non-failure response. | ||||
|         :rtype: dict | ||||
|         """ | ||||
|         return {} | ||||
|  | ||||
|     def invalidate(self): | ||||
|         """Invalidate the current authentication data. | ||||
|  | ||||
|   | ||||
| @@ -97,6 +97,23 @@ class NoMatchingPlugin(ClientException): | ||||
|         super(NoMatchingPlugin, self).__init__(msg) | ||||
|  | ||||
|  | ||||
| class UnsupportedParameters(ClientException): | ||||
|     """A parameter that was provided or returned is not supported. | ||||
|  | ||||
|     :param list(str) names: Names of the unsupported parameters. | ||||
|  | ||||
|     .. py:attribute:: names | ||||
|  | ||||
|         Names of the unsupported parameters. | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, names): | ||||
|         self.names = names | ||||
|  | ||||
|         m = _('The following parameters were given that are unsupported: %s') | ||||
|         super(UnsupportedParameters, self).__init__(m % ', '.join(self.names)) | ||||
|  | ||||
|  | ||||
| class InvalidResponse(ClientException): | ||||
|     """The response from the server is not valid for this request.""" | ||||
|  | ||||
|   | ||||
| @@ -379,6 +379,19 @@ class Session(object): | ||||
|         send = functools.partial(self._send_request, | ||||
|                                  url, method, redirect, log, logger, | ||||
|                                  connect_retries) | ||||
|  | ||||
|         try: | ||||
|             connection_params = self.get_auth_connection_params(auth=auth) | ||||
|         except exceptions.MissingAuthPlugin: | ||||
|             # NOTE(jamielennox): If we've gotten this far without an auth | ||||
|             # plugin then we should be happy with allowing no additional | ||||
|             # connection params. This will be the typical case for plugins | ||||
|             # anyway. | ||||
|             pass | ||||
|         else: | ||||
|             if connection_params: | ||||
|                 kwargs.update(connection_params) | ||||
|  | ||||
|         resp = send(**kwargs) | ||||
|  | ||||
|         # handle getting a 401 Unauthorized response by invalidating the plugin | ||||
| @@ -635,6 +648,59 @@ class Session(object): | ||||
|         auth = self._auth_required(auth, msg) | ||||
|         return auth.get_endpoint(self, **kwargs) | ||||
|  | ||||
|     def get_auth_connection_params(self, auth=None, **kwargs): | ||||
|         """Return auth connection params as provided by the auth plugin. | ||||
|  | ||||
|         An auth plugin may specify connection parameters to the request like | ||||
|         providing a client certificate for communication. | ||||
|  | ||||
|         We restrict the values that may be returned from this function to | ||||
|         prevent an auth plugin overriding values unrelated to connection | ||||
|         parmeters. The values that are currently accepted are: | ||||
|  | ||||
|         - `cert`: a path to a client certificate, or tuple of client | ||||
|           certificate and key pair that are used with this request. | ||||
|         - `verify`: a boolean value to indicate verifying SSL certificates | ||||
|           against the system CAs or a path to a CA file to verify with. | ||||
|  | ||||
|         These values are passed to the requests library and further information | ||||
|         on accepted values may be found there. | ||||
|  | ||||
|         :param auth: The auth plugin to use for tokens. Overrides the plugin | ||||
|                      on the session. (optional) | ||||
|         :type auth: keystoneclient.auth.base.BaseAuthPlugin | ||||
|  | ||||
|         :raises keystoneclient.exceptions.AuthorizationFailure: if a new token | ||||
|                                                                 fetch fails. | ||||
|         :raises keystoneclient.exceptions.MissingAuthPlugin: if a plugin is not | ||||
|                                                              available. | ||||
|         :raises keystoneclient.exceptions.UnsupportedParameters: if the plugin | ||||
|             returns a parameter that is not supported by this session. | ||||
|  | ||||
|         :returns: Authentication headers or None for failure. | ||||
|         :rtype: dict | ||||
|         """ | ||||
|         msg = _('An auth plugin is required to fetch connection params') | ||||
|         auth = self._auth_required(auth, msg) | ||||
|         params = auth.get_connection_params(self, **kwargs) | ||||
|  | ||||
|         # NOTE(jamielennox): There needs to be some consensus on what | ||||
|         # parameters are allowed to be modified by the auth plugin here. | ||||
|         # Ideally I think it would be only the send() parts of the request | ||||
|         # flow. For now lets just allow certain elements. | ||||
|         params_copy = params.copy() | ||||
|  | ||||
|         for arg in ('cert', 'verify'): | ||||
|             try: | ||||
|                 kwargs[arg] = params_copy.pop(arg) | ||||
|             except KeyError: | ||||
|                 pass | ||||
|  | ||||
|         if params_copy: | ||||
|             raise exceptions.UnsupportedParameters(list(params_copy.keys())) | ||||
|  | ||||
|         return params | ||||
|  | ||||
|     def invalidate(self, auth=None): | ||||
|         """Invalidate an authentication plugin. | ||||
|  | ||||
|   | ||||
| @@ -14,12 +14,14 @@ import abc | ||||
| import datetime | ||||
| import uuid | ||||
|  | ||||
| import mock | ||||
| from oslo_utils import timeutils | ||||
| import six | ||||
|  | ||||
| from keystoneclient import access | ||||
| from keystoneclient.auth import base | ||||
| from keystoneclient.auth import identity | ||||
| from keystoneclient import exceptions | ||||
| from keystoneclient import fixture | ||||
| from keystoneclient import session | ||||
| from keystoneclient.tests.unit import utils | ||||
| @@ -411,6 +413,9 @@ class GenericPlugin(base.BaseAuthPlugin): | ||||
|         self.headers = {'headerA': 'valueA', | ||||
|                         'headerB': 'valueB'} | ||||
|  | ||||
|         self.cert = '/path/to/cert' | ||||
|         self.connection_params = {'cert': self.cert, 'verify': False} | ||||
|  | ||||
|     def url(self, prefix): | ||||
|         return '%s/%s' % (self.endpoint, prefix) | ||||
|  | ||||
| @@ -424,6 +429,9 @@ class GenericPlugin(base.BaseAuthPlugin): | ||||
|     def get_endpoint(self, session, **kwargs): | ||||
|         return self.endpoint | ||||
|  | ||||
|     def get_connection_params(self, session, **kwargs): | ||||
|         return self.connection_params | ||||
|  | ||||
|  | ||||
| class GenericAuthPluginTests(utils.TestCase): | ||||
|  | ||||
| @@ -451,3 +459,37 @@ class GenericAuthPluginTests(utils.TestCase): | ||||
|                          self.session.get_auth_headers()) | ||||
|         self.assertNotIn('X-Auth-Token', | ||||
|                          self.requests_mock.last_request.headers) | ||||
|  | ||||
|     def test_setting_connection_params(self): | ||||
|         text = uuid.uuid4().hex | ||||
|  | ||||
|         with mock.patch.object(self.session.session, 'request') as mocked: | ||||
|             mocked.return_value = utils.TestResponse({'status_code': 200, | ||||
|                                                       'text': text}) | ||||
|             resp = self.session.get('prefix', | ||||
|                                     endpoint_filter=self.ENDPOINT_FILTER) | ||||
|  | ||||
|             self.assertEqual(text, resp.text) | ||||
|  | ||||
|             # the cert and verify values passed to request are those that were | ||||
|             # returned from the auth plugin as connection params. | ||||
|  | ||||
|             mocked.assert_called_once_with('GET', | ||||
|                                            self.auth.url('prefix'), | ||||
|                                            headers=mock.ANY, | ||||
|                                            allow_redirects=False, | ||||
|                                            cert=self.auth.cert, | ||||
|                                            verify=False) | ||||
|  | ||||
|     def test_setting_bad_connection_params(self): | ||||
|         # The uuid name parameter here is unknown and not in the allowed params | ||||
|         # to be returned to the session and so an error will be raised. | ||||
|         name = uuid.uuid4().hex | ||||
|         self.auth.connection_params[name] = uuid.uuid4().hex | ||||
|  | ||||
|         e = self.assertRaises(exceptions.UnsupportedParameters, | ||||
|                               self.session.get, | ||||
|                               'prefix', | ||||
|                               endpoint_filter=self.ENDPOINT_FILTER) | ||||
|  | ||||
|         self.assertIn(name, str(e)) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jenkins
					Jenkins