Support Keystone V3 with HttpClient
The http way doesn't work with keystone v3. This patch update the HttpClient to support it. Closes-bug: #1546280 Change-Id: Iefa1aafb796609aca076ed6ab73d02c92b9198d0
This commit is contained in:
@@ -205,7 +205,8 @@ class HTTPClient(object):
|
|||||||
bypass_url=None, retries=None,
|
bypass_url=None, retries=None,
|
||||||
http_log_debug=False, cacert=None,
|
http_log_debug=False, cacert=None,
|
||||||
auth_system='keystone', auth_plugin=None, api_version=None,
|
auth_system='keystone', auth_plugin=None, api_version=None,
|
||||||
logger=None):
|
logger=None, user_domain_name='Default',
|
||||||
|
project_domain_name='Default'):
|
||||||
self.user = user
|
self.user = user
|
||||||
self.password = password
|
self.password = password
|
||||||
self.projectid = projectid
|
self.projectid = projectid
|
||||||
@@ -236,6 +237,8 @@ class HTTPClient(object):
|
|||||||
self.proxy_token = proxy_token
|
self.proxy_token = proxy_token
|
||||||
self.proxy_tenant_id = proxy_tenant_id
|
self.proxy_tenant_id = proxy_tenant_id
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
self.user_domain_name = user_domain_name
|
||||||
|
self.project_domain_name = project_domain_name
|
||||||
|
|
||||||
if insecure:
|
if insecure:
|
||||||
self.verify_cert = False
|
self.verify_cert = False
|
||||||
@@ -419,8 +422,8 @@ class HTTPClient(object):
|
|||||||
We may get redirected to another site, fail or actually get
|
We may get redirected to another site, fail or actually get
|
||||||
back a service catalog with a token and our endpoints.
|
back a service catalog with a token and our endpoints.
|
||||||
"""
|
"""
|
||||||
|
# content must always present
|
||||||
if resp.status_code == 200: # content must always present
|
if resp.status_code == 200 or resp.status_code == 201:
|
||||||
try:
|
try:
|
||||||
self.auth_url = url
|
self.auth_url = url
|
||||||
self.auth_ref = access.create(resp=resp, body=body)
|
self.auth_ref = access.create(resp=resp, body=body)
|
||||||
@@ -497,10 +500,10 @@ class HTTPClient(object):
|
|||||||
path, query, frag))
|
path, query, frag))
|
||||||
|
|
||||||
auth_url = self.auth_url
|
auth_url = self.auth_url
|
||||||
if self.version == "v2.0":
|
if self.version == "v2.0" or self.version == "v3":
|
||||||
while auth_url:
|
while auth_url:
|
||||||
if not self.auth_system or self.auth_system == 'keystone':
|
if not self.auth_system or self.auth_system == 'keystone':
|
||||||
auth_url = self._v2_auth(auth_url)
|
auth_url = self._v2_or_v3_auth(auth_url)
|
||||||
else:
|
else:
|
||||||
auth_url = self._plugin_auth(auth_url)
|
auth_url = self._plugin_auth(auth_url)
|
||||||
|
|
||||||
@@ -526,7 +529,7 @@ class HTTPClient(object):
|
|||||||
except exceptions.AuthorizationFailure:
|
except exceptions.AuthorizationFailure:
|
||||||
if auth_url.find('v2.0') < 0:
|
if auth_url.find('v2.0') < 0:
|
||||||
auth_url = auth_url + '/v2.0'
|
auth_url = auth_url + '/v2.0'
|
||||||
self._v2_auth(auth_url)
|
self._v2_or_v3_auth(auth_url)
|
||||||
|
|
||||||
if self.bypass_url:
|
if self.bypass_url:
|
||||||
self.set_management_url(self.bypass_url)
|
self.set_management_url(self.bypass_url)
|
||||||
@@ -559,8 +562,27 @@ class HTTPClient(object):
|
|||||||
def _plugin_auth(self, auth_url):
|
def _plugin_auth(self, auth_url):
|
||||||
return self.auth_plugin.authenticate(self, auth_url)
|
return self.auth_plugin.authenticate(self, auth_url)
|
||||||
|
|
||||||
def _v2_auth(self, url):
|
def _v2_or_v3_auth(self, url):
|
||||||
"""Authenticate against a v2.0 auth service."""
|
"""Authenticate against a v2.0 auth service."""
|
||||||
|
if self.version == "v3":
|
||||||
|
body = {
|
||||||
|
"auth": {
|
||||||
|
"identity": {
|
||||||
|
"methods": ["password"],
|
||||||
|
"password": {"user": {
|
||||||
|
"domain": {"name": self.user_domain_name},
|
||||||
|
"name": self.user,
|
||||||
|
"password": self.password}}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope = {"project": {"domain": {"name": self.project_domain_name}}}
|
||||||
|
if self.projectid:
|
||||||
|
scope['project']['name'] = self.projectid
|
||||||
|
elif self.tenant_id:
|
||||||
|
scope['project']['id'] = self.tenant_id
|
||||||
|
|
||||||
|
body["auth"]["scope"] = scope
|
||||||
|
else:
|
||||||
body = {"auth": {
|
body = {"auth": {
|
||||||
"passwordCredentials": {"username": self.user,
|
"passwordCredentials": {"username": self.user,
|
||||||
"password": self.password}}}
|
"password": self.password}}}
|
||||||
@@ -569,13 +591,14 @@ class HTTPClient(object):
|
|||||||
body['auth']['tenantName'] = self.projectid
|
body['auth']['tenantName'] = self.projectid
|
||||||
elif self.tenant_id:
|
elif self.tenant_id:
|
||||||
body['auth']['tenantId'] = self.tenant_id
|
body['auth']['tenantId'] = self.tenant_id
|
||||||
|
|
||||||
self._authenticate(url, body)
|
self._authenticate(url, body)
|
||||||
|
|
||||||
def _authenticate(self, url, body):
|
def _authenticate(self, url, body):
|
||||||
"""Authenticate and extract the service catalog."""
|
"""Authenticate and extract the service catalog."""
|
||||||
|
if self.version == 'v3':
|
||||||
|
token_url = url + "/auth/tokens"
|
||||||
|
else:
|
||||||
token_url = url + "/tokens"
|
token_url = url + "/tokens"
|
||||||
|
|
||||||
# Make sure we follow redirects when trying to reach Keystone
|
# Make sure we follow redirects when trying to reach Keystone
|
||||||
resp, body = self.request(
|
resp, body = self.request(
|
||||||
token_url,
|
token_url,
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import json
|
||||||
import mock
|
import mock
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@@ -25,6 +26,12 @@ fake_response = utils.TestResponse({
|
|||||||
})
|
})
|
||||||
mock_request = mock.Mock(return_value=(fake_response))
|
mock_request = mock.Mock(return_value=(fake_response))
|
||||||
|
|
||||||
|
fake_201_response = utils.TestResponse({
|
||||||
|
"status_code": 201,
|
||||||
|
"text": '{"hi": "there"}',
|
||||||
|
})
|
||||||
|
mock_201_request = mock.Mock(return_value=(fake_201_response))
|
||||||
|
|
||||||
refused_response = utils.TestResponse({
|
refused_response = utils.TestResponse({
|
||||||
"status_code": 400,
|
"status_code": 400,
|
||||||
"text": '[Errno 111] Connection refused',
|
"text": '[Errno 111] Connection refused',
|
||||||
@@ -293,6 +300,47 @@ class ClientTest(utils.TestCase):
|
|||||||
|
|
||||||
test_auth_call()
|
test_auth_call()
|
||||||
|
|
||||||
|
def test_auth_with_keystone_v3(self):
|
||||||
|
cl = get_authed_client()
|
||||||
|
cl.auth_url = 'http://example.com:5000/v3'
|
||||||
|
|
||||||
|
@mock.patch.object(cl, "_extract_service_catalog", mock.Mock())
|
||||||
|
@mock.patch.object(requests, "request", mock_201_request)
|
||||||
|
def test_auth_call():
|
||||||
|
cl.authenticate()
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
'Accept': 'application/json',
|
||||||
|
"User-Agent": cl.USER_AGENT
|
||||||
|
}
|
||||||
|
data = {
|
||||||
|
"auth": {
|
||||||
|
"scope": {
|
||||||
|
"project": {
|
||||||
|
"domain": {"name": "Default"},
|
||||||
|
"name": "project_id"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"identity": {
|
||||||
|
"methods": ["password"],
|
||||||
|
"password": {
|
||||||
|
"user": {"domain": {"name": "Default"},
|
||||||
|
"password": "password", "name": "username"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mock_201_request.assert_called_with(
|
||||||
|
"POST",
|
||||||
|
"http://example.com:5000/v3/auth/tokens",
|
||||||
|
headers=headers,
|
||||||
|
allow_redirects=True,
|
||||||
|
data=json.dumps(data),
|
||||||
|
**self.TEST_REQUEST_BASE)
|
||||||
|
|
||||||
|
test_auth_call()
|
||||||
|
|
||||||
def test_get_retry_timeout_error(self):
|
def test_get_retry_timeout_error(self):
|
||||||
cl = get_authed_client(retries=1)
|
cl = get_authed_client(retries=1)
|
||||||
|
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Support Keystone V3 authentication for httpClient.
|
Reference in New Issue
Block a user