diff options
-rw-r--r-- | heatclient/common/http.py | 13 | ||||
-rw-r--r-- | heatclient/shell.py | 11 | ||||
-rw-r--r-- | heatclient/tests/test_common_http.py | 75 |
3 files changed, 96 insertions, 3 deletions
diff --git a/heatclient/common/http.py b/heatclient/common/http.py index 66ab996..6e801bd 100644 --- a/heatclient/common/http.py +++ b/heatclient/common/http.py @@ -53,6 +53,7 @@ class HTTPClient(object): self.username = kwargs.get('username') self.password = kwargs.get('password') self.region_name = kwargs.get('region_name') + self.include_pass = kwargs.get('include_pass') self.connection_params = self.get_connection_params(endpoint, **kwargs) @staticmethod @@ -137,6 +138,8 @@ class HTTPClient(object): kwargs['headers'].setdefault('X-Auth-Url', self.auth_url) if self.region_name: kwargs['headers'].setdefault('X-Region-Name', self.region_name) + if self.include_pass and not 'X-Auth-Key' in kwargs['headers']: + kwargs['headers'].update(self.credentials_headers()) self.log_curl_request(method, url, kwargs) conn = self.get_connection() @@ -160,7 +163,15 @@ class HTTPClient(object): body_str = ''.join([chunk for chunk in body_iter]) self.log_http_response(resp, body_str) - if 400 <= resp.status < 600: + if not 'X-Auth-Key' in kwargs['headers'] and \ + (resp.status == 401 or + (resp.status == 500 and "(HTTP 401)" in body_str)): + raise exc.HTTPUnauthorized("Authentication failed. Please try" + " again with option " + "--include-password or export " + "HEAT_INCLUDE_PASSWORD=1\n%s" + % body_str) + elif 400 <= resp.status < 600: raise exc.from_response(resp, body_str) elif resp.status in (301, 302, 305): # Redirected. Reissue the request to the new location. diff --git a/heatclient/shell.py b/heatclient/shell.py index 434fc9e..d04a9d1 100644 --- a/heatclient/shell.py +++ b/heatclient/shell.py @@ -176,6 +176,11 @@ class HeatShell(object): parser.add_argument('--os_endpoint_type', help=argparse.SUPPRESS) + parser.add_argument('--include-password', + default=bool(utils.env('HEAT_INCLUDE_PASSWORD')), + action='store_true', + help='Send os-username and os-password to heat') + return parser def get_subcommand_parser(self, version): @@ -328,7 +333,8 @@ class HeatShell(object): 'auth_url': args.os_auth_url, 'service_type': args.os_service_type, 'endpoint_type': args.os_endpoint_type, - 'insecure': args.insecure + 'insecure': args.insecure, + 'include_pass': args.include_password } endpoint = args.heat_url @@ -346,7 +352,8 @@ class HeatShell(object): 'key_file': args.key_file, 'username': args.os_username, 'password': args.os_password, - 'endpoint_type': args.os_endpoint_type + 'endpoint_type': args.os_endpoint_type, + 'include_pass': args.include_password } if args.os_region_name: diff --git a/heatclient/tests/test_common_http.py b/heatclient/tests/test_common_http.py index 9398347..9a8a157 100644 --- a/heatclient/tests/test_common_http.py +++ b/heatclient/tests/test_common_http.py @@ -100,6 +100,81 @@ class HttpClientTest(testtools.TestCase): self.assertEqual(resp.status, 200) self.m.VerifyAll() + def test_include_pass(self): + # Record a 200 + fake200 = fakes.FakeHTTPResponse( + 200, 'OK', + {'content-type': 'application/octet-stream'}, + '') + + # no token or credentials + mock_conn = http.httplib.HTTPConnection('example.com', 8004, + timeout=600.0) + mock_conn.request('GET', '/', + headers={'Content-Type': 'application/octet-stream', + 'User-Agent': 'python-heatclient'}) + mock_conn.getresponse().AndReturn(fake200) + + # credentials + mock_conn = http.httplib.HTTPConnection('example.com', 8004, + timeout=600.0) + mock_conn.request('GET', '/', + headers={'Content-Type': 'application/octet-stream', + 'User-Agent': 'python-heatclient', + 'X-Auth-Key': 'pass', + 'X-Auth-User': 'user'}) + mock_conn.getresponse().AndReturn(fake200) + + # token suppresses credentials + mock_conn = http.httplib.HTTPConnection('example.com', 8004, + timeout=600.0) + mock_conn.request('GET', '/', + headers={'Content-Type': 'application/octet-stream', + 'User-Agent': 'python-heatclient', + 'X-Auth-Token': 'abcd1234', + 'X-Auth-Key': 'pass', + 'X-Auth-User': 'user'}) + mock_conn.getresponse().AndReturn(fake200) + + # Replay, create client, assert + self.m.ReplayAll() + client = http.HTTPClient('http://example.com:8004') + resp, body = client.raw_request('GET', '') + self.assertEqual(200, resp.status) + + client.username = 'user' + client.password = 'pass' + client.include_pass = True + resp, body = client.raw_request('GET', '') + self.assertEqual(200, resp.status) + + client.auth_token = 'abcd1234' + resp, body = client.raw_request('GET', '') + self.assertEqual(200, resp.status) + self.m.VerifyAll() + + def test_not_include_pass(self): + # Record a 200 + fake500 = fakes.FakeHTTPResponse( + 500, 'ERROR', + {'content-type': 'application/octet-stream'}, + '(HTTP 401)') + + # no token or credentials + mock_conn = http.httplib.HTTPConnection('example.com', 8004, + timeout=600.0) + mock_conn.request('GET', '/', + headers={'Content-Type': 'application/octet-stream', + 'User-Agent': 'python-heatclient'}) + mock_conn.getresponse().AndReturn(fake500) + + # Replay, create client, assert + self.m.ReplayAll() + client = http.HTTPClient('http://example.com:8004') + e = self.assertRaises(exc.HTTPUnauthorized, + client.raw_request, 'GET', '') + self.assertIn('include-password', str(e)) + def test_region_name(self): # Record a 200 fake200 = fakes.FakeHTTPResponse( |