Change url passed to oauth signature verifier to request url

OAUTH signature verification should happen with the same URL used for signing.
Typically at the user end it should be signed with the request URL and hence it
should be verified with the same.
Currently keystone uses public endpoint URL for signature verification.

Modified the URL passed to oauth signature verification to request URL.

Change-Id: I28059a43cb0088c2952c19f696042ebec54d26c9
Partial-Bug: #1687593
(cherry picked from commit 926685c5a4)
This commit is contained in:
Hemanth Nakkina 2017-05-02 16:44:21 +05:30 committed by Lance Bragstad
parent 76bdca6498
commit 59e4b4923f
2 changed files with 36 additions and 14 deletions

View File

@ -233,15 +233,13 @@ class OAuthControllerV3(controller.V3Controller):
self.resource_api.get_project(requested_project_id)
self.oauth_api.get_consumer(consumer_id)
url = self.base_url(request.context_dict, request.context_dict['path'])
req_headers = {'Requested-Project-Id': requested_project_id}
req_headers.update(request.headers)
request_verifier = oauth1.RequestTokenEndpoint(
request_validator=validator.OAuthValidator(),
token_generator=oauth1.token_generator)
h, b, s = request_verifier.create_request_token_response(
url,
request.url,
http_method='POST',
body=request.params,
headers=req_headers)
@ -301,14 +299,12 @@ class OAuthControllerV3(controller.V3Controller):
if now > expires:
raise exception.Unauthorized(_('Request token is expired'))
url = self.base_url(request.context_dict, request.context_dict['path'])
access_verifier = oauth1.AccessTokenEndpoint(
request_validator=validator.OAuthValidator(),
token_generator=oauth1.token_generator)
try:
h, b, s = access_verifier.create_access_token_response(
url,
request.url,
http_method='POST',
body=request.params,
headers=request.headers)

View File

@ -60,19 +60,21 @@ class OAuth1Tests(test_v3.RestfulTestCase):
body={'consumer': ref})
return resp.result['consumer']
def _create_request_token(self, consumer, project_id):
def _create_request_token(self, consumer, project_id, base_url=None):
endpoint = '/OS-OAUTH1/request_token'
client = oauth1.Client(consumer['key'],
client_secret=consumer['secret'],
signature_method=oauth1.SIG_HMAC,
callback_uri="oob")
headers = {'requested_project_id': project_id}
url, headers, body = client.sign(self.base_url + endpoint,
if not base_url:
base_url = self.base_url
url, headers, body = client.sign(base_url + endpoint,
http_method='POST',
headers=headers)
return endpoint, headers
def _create_access_token(self, consumer, token):
def _create_access_token(self, consumer, token, base_url=None):
endpoint = '/OS-OAUTH1/access_token'
client = oauth1.Client(consumer['key'],
client_secret=consumer['secret'],
@ -80,7 +82,9 @@ class OAuth1Tests(test_v3.RestfulTestCase):
resource_owner_secret=token.secret,
signature_method=oauth1.SIG_HMAC,
verifier=token.verifier)
url, headers, body = client.sign(self.base_url + endpoint,
if not base_url:
base_url = self.base_url
url, headers, body = client.sign(base_url + endpoint,
http_method='POST')
headers.update({'Content-Type': 'application/json'})
return endpoint, headers
@ -638,6 +642,17 @@ class MaliciousOAuth1Tests(OAuth1Tests):
self.post(url, headers=headers,
expected_status=http_client.UNAUTHORIZED)
def test_bad_request_url(self):
consumer = self._create_single_consumer()
consumer_id = consumer['id']
consumer_secret = consumer['secret']
consumer = {'key': consumer_id, 'secret': consumer_secret}
bad_base_url = 'http://localhost/identity_admin/v3'
url, headers = self._create_request_token(consumer, self.project_id,
base_url=bad_base_url)
self.post(url, headers=headers,
expected_status=http_client.UNAUTHORIZED)
def test_bad_request_token_key(self):
consumer = self._create_single_consumer()
consumer_id = consumer['id']
@ -720,7 +735,18 @@ class MaliciousOAuth1Tests(OAuth1Tests):
verifier = resp.result['token']['oauth_verifier']
request_token.set_verifier(verifier)
# 1. Invalid signature.
# 1. Invalid base url.
# Update the base url, so it will fail to validate the signature.
base_url = 'http://localhost/identity_admin/v3'
url, headers = self._create_access_token(consumer, request_token,
base_url=base_url)
resp = self.post(url, headers=headers,
expected_status=http_client.UNAUTHORIZED)
resp_data = jsonutils.loads(resp.body)
self.assertIn('Invalid signature',
resp_data.get('error', {}).get('message'))
# 2. Invalid signature.
# Update the secret, so it will fail to validate the signature.
consumer.update({'secret': uuid.uuid4().hex})
url, headers = self._create_access_token(consumer, request_token)
@ -730,7 +756,7 @@ class MaliciousOAuth1Tests(OAuth1Tests):
self.assertIn('Invalid signature',
resp_data.get('error', {}).get('message'))
# 2. Invalid verifier.
# 3. Invalid verifier.
# Even though the verifier is well formatted, it is not verifier
# that is stored in the backend, this is different with the testcase
# above `test_bad_verifier` where it test that `verifier` is not
@ -745,7 +771,7 @@ class MaliciousOAuth1Tests(OAuth1Tests):
self.assertIn('Provided verifier',
resp_data.get('error', {}).get('message'))
# 3. The provided consumer does not exist.
# 4. The provided consumer does not exist.
consumer.update({'key': uuid.uuid4().hex})
url, headers = self._create_access_token(consumer, request_token)
resp = self.post(url, headers=headers,
@ -754,7 +780,7 @@ class MaliciousOAuth1Tests(OAuth1Tests):
self.assertIn('Provided consumer does not exist',
resp_data.get('error', {}).get('message'))
# 4. The consumer key provided does not match stored consumer key.
# 5. The consumer key provided does not match stored consumer key.
consumer2 = self._create_single_consumer()
consumer.update({'key': consumer2['id']})
url, headers = self._create_access_token(consumer, request_token)