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:
wangxiyuan 2016-11-30 14:36:16 +08:00
parent 4eb63b8ef2
commit e1014faed1
3 changed files with 91 additions and 16 deletions

View File

@ -205,7 +205,8 @@ class HTTPClient(object):
bypass_url=None, retries=None,
http_log_debug=False, cacert=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.password = password
self.projectid = projectid
@ -236,6 +237,8 @@ class HTTPClient(object):
self.proxy_token = proxy_token
self.proxy_tenant_id = proxy_tenant_id
self.timeout = timeout
self.user_domain_name = user_domain_name
self.project_domain_name = project_domain_name
if insecure:
self.verify_cert = False
@ -419,8 +422,8 @@ class HTTPClient(object):
We may get redirected to another site, fail or actually get
back a service catalog with a token and our endpoints.
"""
if resp.status_code == 200: # content must always present
# content must always present
if resp.status_code == 200 or resp.status_code == 201:
try:
self.auth_url = url
self.auth_ref = access.create(resp=resp, body=body)
@ -497,10 +500,10 @@ class HTTPClient(object):
path, query, frag))
auth_url = self.auth_url
if self.version == "v2.0":
if self.version == "v2.0" or self.version == "v3":
while auth_url:
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:
auth_url = self._plugin_auth(auth_url)
@ -526,7 +529,7 @@ class HTTPClient(object):
except exceptions.AuthorizationFailure:
if auth_url.find('v2.0') < 0:
auth_url = auth_url + '/v2.0'
self._v2_auth(auth_url)
self._v2_or_v3_auth(auth_url)
if self.bypass_url:
self.set_management_url(self.bypass_url)
@ -559,23 +562,43 @@ class HTTPClient(object):
def _plugin_auth(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."""
body = {"auth": {
"passwordCredentials": {"username": self.user,
"password": self.password}}}
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
if self.projectid:
body['auth']['tenantName'] = self.projectid
elif self.tenant_id:
body['auth']['tenantId'] = self.tenant_id
body["auth"]["scope"] = scope
else:
body = {"auth": {
"passwordCredentials": {"username": self.user,
"password": self.password}}}
if self.projectid:
body['auth']['tenantName'] = self.projectid
elif self.tenant_id:
body['auth']['tenantId'] = self.tenant_id
self._authenticate(url, body)
def _authenticate(self, url, body):
"""Authenticate and extract the service catalog."""
token_url = url + "/tokens"
if self.version == 'v3':
token_url = url + "/auth/tokens"
else:
token_url = url + "/tokens"
# Make sure we follow redirects when trying to reach Keystone
resp, body = self.request(
token_url,

View File

@ -11,6 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import mock
import requests
@ -25,6 +26,12 @@ fake_response = utils.TestResponse({
})
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({
"status_code": 400,
"text": '[Errno 111] Connection refused',
@ -293,6 +300,47 @@ class ClientTest(utils.TestCase):
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):
cl = get_authed_client(retries=1)

View File

@ -0,0 +1,4 @@
---
features:
- |
Support Keystone V3 authentication for httpClient.