diff --git a/mistralclient/api/client.py b/mistralclient/api/client.py index eda4b750..b65ed93d 100644 --- a/mistralclient/api/client.py +++ b/mistralclient/api/client.py @@ -23,7 +23,11 @@ def client(mistral_url=None, username=None, api_key=None, endpoint_type='publicURL', service_type='workflow', auth_token=None, user_id=None, cacert=None, insecure=False, profile=None, auth_type=auth_types.KEYSTONE, client_id=None, - client_secret=None, **kwargs): + client_secret=None, target_username=None, target_api_key=None, + target_project_name=None, target_auth_url=None, + target_project_id=None, target_auth_token=None, + target_user_id=None, target_cacert=None, target_insecure=False, + **kwargs): if mistral_url and not isinstance(mistral_url, six.string_types): raise RuntimeError('Mistral url should be a string.') @@ -45,6 +49,15 @@ def client(mistral_url=None, username=None, api_key=None, auth_type=auth_type, client_id=client_id, client_secret=client_secret, + target_username=target_username, + target_api_key=target_api_key, + target_project_name=target_project_name, + target_auth_url=target_auth_url, + target_project_id=target_project_id, + target_auth_token=target_auth_token, + target_user_id=target_user_id, + target_cacert=target_cacert, + target_insecure=target_insecure, **kwargs ) diff --git a/mistralclient/api/httpclient.py b/mistralclient/api/httpclient.py index b64b639a..2b52907d 100644 --- a/mistralclient/api/httpclient.py +++ b/mistralclient/api/httpclient.py @@ -37,11 +37,14 @@ def log_request(func): class HTTPClient(object): def __init__(self, base_url, token=None, project_id=None, user_id=None, - cacert=None, insecure=False, **kwargs): + cacert=None, insecure=False, target_token=None, + target_auth_uri=None, **kwargs): self.base_url = base_url self.token = token self.project_id = project_id self.user_id = user_id + self.target_token = target_token + self.target_auth_uri = target_auth_uri self.ssl_options = {} if self.base_url.startswith('https'): @@ -115,6 +118,15 @@ class HTTPClient(object): if user_id: headers['X-User-Id'] = user_id + target_token = headers.get('X-Target-Auth-Token', self.target_token) + if target_token: + headers['X-Target-Auth-Token'] = target_token + + target_auth_uri = headers.get('X-Target-Auth-Uri', + self.target_auth_uri) + if target_auth_uri: + headers['X-Target-Auth-Uri'] = target_auth_uri + # Add headers for osprofiler. headers.update(osprofiler.web.get_trace_id_headers()) diff --git a/mistralclient/api/v2/client.py b/mistralclient/api/v2/client.py index d528fee0..206087b7 100644 --- a/mistralclient/api/v2/client.py +++ b/mistralclient/api/v2/client.py @@ -42,7 +42,11 @@ class Client(object): endpoint_type='publicURL', service_type='workflowv2', auth_token=None, user_id=None, cacert=None, insecure=False, profile=None, auth_type=auth_types.KEYSTONE, client_id=None, - client_secret=None, **kwargs): + client_secret=None, target_username=None, target_api_key=None, + target_project_name=None, target_auth_url=None, + target_project_id=None, target_auth_token=None, + target_user_id=None, target_cacert=None, + target_insecure=False, **kwargs): if mistral_url and not isinstance(mistral_url, six.string_types): raise RuntimeError('Mistral url should be a string.') @@ -96,6 +100,22 @@ class Client(object): if profile: osprofiler.profiler.init(profile) + if target_auth_url: + keystone.authenticate( + mistral_url, + target_username, + target_api_key, + target_project_name, + target_auth_url, + target_project_id, + endpoint_type, + service_type, + target_auth_token, + target_user_id, + target_cacert, + target_insecure + ) + self.http_client = httpclient.HTTPClient( mistral_url, auth_token, @@ -103,6 +123,8 @@ class Client(object): user_id, cacert=cacert, insecure=insecure, + target_token=target_auth_token, + target_auth_uri=target_auth_url, **kwargs ) diff --git a/mistralclient/shell.py b/mistralclient/shell.py index 9abbda60..3109018c 100644 --- a/mistralclient/shell.py +++ b/mistralclient/shell.py @@ -341,6 +341,78 @@ class MistralShell(app.App): ' (Env: OPENID_CLIENT_SECRET)' ) + parser.add_argument( + '--os-target-username', + action='store', + dest='target_username', + default=c.env('OS_TARGET_USERNAME', default='admin'), + help='Authentication username for target cloud' + ' (Env: OS_TARGET_USERNAME)' + ) + + parser.add_argument( + '--os-target-password', + action='store', + dest='target_password', + default=c.env('OS_TARGET_PASSWORD'), + help='Authentication password for target cloud' + ' (Env: OS_TARGET_PASSWORD)' + ) + + parser.add_argument( + '--os-target-tenant-id', + action='store', + dest='target_tenant_id', + default=c.env('OS_TARGET_TENANT_ID'), + help='Authentication tenant identifier for target cloud' + ' (Env: OS_TARGET_TENANT_ID)' + ) + + parser.add_argument( + '--os-target-tenant-name', + action='store', + dest='target_tenant_name', + default=c.env('OS_TARGET_TENANT_NAME', 'Default'), + help='Authentication tenant name for target cloud' + ' (Env: OS_TARGET_TENANT_NAME)' + ) + + parser.add_argument( + '--os-target-auth-token', + action='store', + dest='target_token', + default=c.env('OS_TARGET_AUTH_TOKEN'), + help='Authentication token for target cloud' + ' (Env: OS_TARGET_AUTH_TOKEN)' + ) + + parser.add_argument( + '--os-target-auth-url', + action='store', + dest='target_auth_url', + default=c.env('OS_TARGET_AUTH_URL'), + help='Authentication URL for target cloud' + ' (Env: OS_TARGET_AUTH_URL)' + ) + + parser.add_argument( + '--os-target_cacert', + action='store', + dest='target_cacert', + default=c.env('OS_TARGET_CACERT'), + help='Authentication CA Certificate for target cloud' + ' (Env: OS_TARGET_CACERT)' + ) + + parser.add_argument( + '--target_insecure', + action='store_true', + dest='target_insecure', + default=c.env('TARGET_MISTRALCLIENT_INSECURE', default=False), + help='Disables SSL/TLS certificate verification for target cloud ' + '(Env: TARGET_MISTRALCLIENT_INSECURE)' + ) + parser.add_argument( '--profile', dest='profile', @@ -397,6 +469,14 @@ class MistralShell(app.App): auth_type=self.options.auth_type, client_id=self.options.client_id, client_secret=self.options.client_secret, + target_username=self.options.target_username, + target_api_key=self.options.target_password, + target_project_name=self.options.target_tenant_name, + target_auth_url=self.options.target_auth_url, + target_project_id=self.options.target_tenant_id, + target_auth_token=self.options.target_token, + target_cacert=self.options.target_cacert, + target_insecure=self.options.target_insecure, **kwargs ) diff --git a/mistralclient/tests/unit/test_client.py b/mistralclient/tests/unit/test_client.py index e0809d34..34eab994 100644 --- a/mistralclient/tests/unit/test_client.py +++ b/mistralclient/tests/unit/test_client.py @@ -50,7 +50,9 @@ class BaseClientTests(testtools.TestCase): expected_kwargs = { 'cacert': None, - 'insecure': False + 'insecure': False, + 'target_auth_uri': None, + 'target_token': None } client.client( @@ -80,7 +82,9 @@ class BaseClientTests(testtools.TestCase): expected_kwargs = { 'cacert': None, - 'insecure': True + 'insecure': True, + 'target_auth_uri': None, + 'target_token': None } client.client( @@ -115,7 +119,9 @@ class BaseClientTests(testtools.TestCase): expected_kwargs = { 'cacert': path, - 'insecure': False + 'insecure': False, + 'target_auth_uri': None, + 'target_token': None } try: @@ -196,7 +202,9 @@ class BaseClientTests(testtools.TestCase): expected_kwargs = { 'cacert': None, - 'insecure': False + 'insecure': False, + 'target_auth_uri': None, + 'target_token': None } client.client( diff --git a/mistralclient/tests/unit/test_httpclient.py b/mistralclient/tests/unit/test_httpclient.py index 43623e46..df74bfc1 100644 --- a/mistralclient/tests/unit/test_httpclient.py +++ b/mistralclient/tests/unit/test_httpclient.py @@ -91,24 +91,6 @@ class HTTPClientTest(testtools.TestCase): **EXPECTED_REQ_OPTIONS ) - @mock.patch.object( - requests, - 'get', - mock.MagicMock(return_value=FakeResponse('get', EXPECTED_URL, 200)) - ) - def test_get_request_options_with_headers_for_get(self): - headers = {'foo': 'bar'} - - self.client.get(API_URL, headers=headers) - - expected_options = copy.deepcopy(EXPECTED_REQ_OPTIONS) - expected_options['headers'].update(headers) - - requests.get.assert_called_with( - EXPECTED_URL, - **expected_options - ) - @mock.patch.object( osprofiler.profiler._Profiler, 'get_base_id', @@ -145,6 +127,35 @@ class HTTPClientTest(testtools.TestCase): **expected_options ) + @mock.patch.object( + requests, + 'get', + mock.MagicMock(return_value=FakeResponse('get', EXPECTED_URL, 200)) + ) + def test_get_request_options_with_headers_for_get(self): + target_auth_uri = str(uuid.uuid4()) + target_token = str(uuid.uuid4()) + + target_client = httpclient.HTTPClient( + API_BASE_URL, + AUTH_TOKEN, + PROJECT_ID, + USER_ID, + target_auth_uri=target_auth_uri, + target_token=target_token + ) + + target_client.get(API_URL) + + expected_options = copy.deepcopy(EXPECTED_REQ_OPTIONS) + expected_options["headers"]["X-Target-Auth-Uri"] = target_auth_uri + expected_options["headers"]["X-Target-Auth-Token"] = target_token + + requests.get.assert_called_with( + EXPECTED_URL, + **expected_options + ) + @mock.patch.object( requests, 'post',