From 83b3d0d39cb8072682fac74f6a40877030e91c18 Mon Sep 17 00:00:00 2001 From: Juan Antonio Osorio Robles Date: Fri, 17 Mar 2017 18:03:29 +0200 Subject: [PATCH] Accept keystone session in client This already has auth configured, and allows us to re-use the session even with other clients. Currently this is not used by the CLI; however this is useful when instantiating the mistral client from a python script or from another service. Change-Id: I4fccb4117db11c966ac3f17ec6d2a53c657378e8 --- mistralclient/api/httpclient.py | 52 +++++++++++++-------- mistralclient/api/v2/client.py | 8 +++- mistralclient/auth/keycloak.py | 4 +- mistralclient/auth/keystone.py | 10 ++-- mistralclient/tests/unit/test_httpclient.py | 8 ++-- 5 files changed, 51 insertions(+), 31 deletions(-) diff --git a/mistralclient/api/httpclient.py b/mistralclient/api/httpclient.py index 70923c4e..2acc5fe2 100644 --- a/mistralclient/api/httpclient.py +++ b/mistralclient/api/httpclient.py @@ -58,6 +58,7 @@ def log_request(func): class HTTPClient(object): def __init__(self, base_url, **kwargs): self.base_url = base_url + self.session = kwargs.pop('session', None) self.auth_token = kwargs.get(AUTH_TOKEN) self.project_id = kwargs.get(PROJECT_ID) self.user_id = kwargs.get(USER_ID) @@ -77,6 +78,11 @@ class HTTPClient(object): TARGET_PROJECT_DOMAIN_NAME ) + if self.session: + self.crud_provider = self.session + else: + self.crud_provider = requests + if self.base_url.startswith('https'): if self.cacert and not os.path.exists(self.cacert): raise ValueError('Unable to locate cacert file ' @@ -86,42 +92,47 @@ class HTTPClient(object): LOG.warning('Client is set to not verify even though ' 'cacert is provided.') - if self.insecure: - self.ssl_options['verify'] = False - else: - if self.cacert: - self.ssl_options['verify'] = self.cacert + # These are already set by the session, so it's not needed + if not self.session: + if self.insecure: + self.ssl_options['verify'] = False else: - self.ssl_options['verify'] = True + if self.cacert: + self.ssl_options['verify'] = self.cacert + else: + self.ssl_options['verify'] = True - self.ssl_options['cert'] = ( - kwargs.get(CERT_FILE), - kwargs.get(CERT_KEY) - ) + self.ssl_options['cert'] = ( + kwargs.get(CERT_FILE), + kwargs.get(CERT_KEY) + ) @log_request def get(self, url, headers=None): options = self._get_request_options('get', headers) - return requests.get(self.base_url + url, **options) + return self.crud_provider.get(self.base_url + url, **options) @log_request def post(self, url, body, headers=None): options = self._get_request_options('post', headers) - return requests.post(self.base_url + url, body, **options) + return self.crud_provider.post(self.base_url + url, + data=body, **options) @log_request def put(self, url, body, headers=None): options = self._get_request_options('put', headers) - return requests.put(self.base_url + url, body, **options) + return self.crud_provider.put(self.base_url + url, + data=body, **options) @log_request def delete(self, url, headers=None): options = self._get_request_options('delete', headers) - return requests.delete(self.base_url + url, **options) + return self.crud_provider.delete(self.base_url + url, + **options) def _get_request_options(self, method, headers): headers = self._update_headers(headers) @@ -139,14 +150,15 @@ class HTTPClient(object): if not headers: headers = {} - if self.auth_token: - headers['x-auth-token'] = self.auth_token + if not self.session: + if self.auth_token: + headers['x-auth-token'] = self.auth_token - if self.project_id: - headers['X-Project-Id'] = self.project_id + if self.project_id: + headers['X-Project-Id'] = self.project_id - if self.user_id: - headers['X-User-Id'] = self.user_id + if self.user_id: + headers['X-User-Id'] = self.user_id if self.target_auth_token: headers['X-Target-Auth-Token'] = self.target_auth_token diff --git a/mistralclient/api/v2/client.py b/mistralclient/api/v2/client.py index 67589097..166cbf88 100644 --- a/mistralclient/api/v2/client.py +++ b/mistralclient/api/v2/client.py @@ -39,6 +39,9 @@ _DEFAULT_MISTRAL_URL = "http://localhost:8989/v2" class Client(object): def __init__(self, auth_type='keystone', **kwargs): + # We get the session at this point, as some instances of session + # objects might have mutexes that can't be deep-copied. + session = kwargs.pop('session', None) req = copy.deepcopy(kwargs) mistral_url = req.get('mistral_url') profile = req.get('profile') @@ -47,7 +50,7 @@ class Client(object): raise RuntimeError('Mistral url should be a string.') auth_handler = auth.get_auth_handler(auth_type) - auth_response = auth_handler.authenticate(req) or {} + auth_response = auth_handler.authenticate(req, session=session) or {} req.update(auth_response) @@ -59,7 +62,8 @@ class Client(object): if profile: osprofiler_profiler.init(profile) - http_client = httpclient.HTTPClient(mistral_url, **req) + http_client = httpclient.HTTPClient(mistral_url, session=session, + **req) # Create all resource managers. self.workbooks = workbooks.WorkbookManager(http_client) diff --git a/mistralclient/auth/keycloak.py b/mistralclient/auth/keycloak.py index a3d6a432..8315aa67 100644 --- a/mistralclient/auth/keycloak.py +++ b/mistralclient/auth/keycloak.py @@ -24,7 +24,7 @@ LOG = logging.getLogger(__name__) class KeycloakAuthHandler(auth.AuthHandler): - def authenticate(self, req): + def authenticate(self, req, session=None): """Performs authentication using Keycloak OpenID Protocol. :param req: Request dict containing list of parameters required @@ -46,6 +46,8 @@ class KeycloakAuthHandler(auth.AuthHandler): cacert: SSL certificate file (Optional). insecure: If True, SSL certificate is not verified (Optional). + :param session: Keystone session object. Not used by this plugin. + """ if not isinstance(req, dict): raise TypeError('The input "req" is not typeof dict.') diff --git a/mistralclient/auth/keystone.py b/mistralclient/auth/keystone.py index f9ed1a17..e7f7fb12 100644 --- a/mistralclient/auth/keystone.py +++ b/mistralclient/auth/keystone.py @@ -21,7 +21,7 @@ import mistralclient.api.httpclient as api class KeystoneAuthHandler(auth.AuthHandler): - def authenticate(self, req): + def authenticate(self, req, session=None): """Performs authentication via Keystone. :param req: Request dict containing list of parameters required @@ -31,6 +31,7 @@ class KeystoneAuthHandler(auth.AuthHandler): if not isinstance(req, dict): raise TypeError('The input "req" is not typeof dict.') + session = session mistral_url = req.get('mistral_url') endpoint_type = req.get('endpoint_type', 'publicURL') service_type = req.get('service_type', 'workflow2') @@ -76,7 +77,9 @@ class KeystoneAuthHandler(auth.AuthHandler): auth_response = {} - if auth_url: + if session: + keystone = client.Client(session=session) + elif auth_url: keystone = client.Client( username=username, user_id=user_id, @@ -90,15 +93,14 @@ class KeystoneAuthHandler(auth.AuthHandler): user_domain_name=user_domain_name, project_domain_name=project_domain_name ) - keystone.authenticate() - auth_response.update({ api.AUTH_TOKEN: keystone.auth_token, api.PROJECT_ID: keystone.project_id, api.USER_ID: keystone.user_id, }) + if session or auth_url: if not mistral_url: try: mistral_url = keystone.service_catalog.url_for( diff --git a/mistralclient/tests/unit/test_httpclient.py b/mistralclient/tests/unit/test_httpclient.py index 4744159d..9028264c 100644 --- a/mistralclient/tests/unit/test_httpclient.py +++ b/mistralclient/tests/unit/test_httpclient.py @@ -196,7 +196,7 @@ class HTTPClientTest(base.BaseTestCase): requests.post.assert_called_with( EXPECTED_URL, - EXPECTED_BODY, + data=EXPECTED_BODY, **expected_options ) @@ -216,7 +216,7 @@ class HTTPClientTest(base.BaseTestCase): requests.put.assert_called_with( EXPECTED_URL, - EXPECTED_BODY, + data=EXPECTED_BODY, **expected_options ) @@ -281,7 +281,7 @@ class HTTPClientTest(base.BaseTestCase): requests.post.assert_called_with( EXPECTED_URL, - EXPECTED_BODY, + data=EXPECTED_BODY, **EXPECTED_REQ_OPTIONS ) @@ -305,7 +305,7 @@ class HTTPClientTest(base.BaseTestCase): requests.put.assert_called_with( EXPECTED_URL, - EXPECTED_BODY, + data=EXPECTED_BODY, **EXPECTED_REQ_OPTIONS )