Change url scheme passed to oauth signature verifier
Change 461736 modifies the url passed to oauth signature verifier to
request url. But in some deployments, https endpoints are terminated
at haproxy and http request is sent to keystone. So request url will
have http as url scheme whereas the endpoint is registered with https
and signature at client is done with https url. This results in OAUTH
signature validation failure.
Update URL sent for OAUTH signature verification with the scheme of
the base url.
Change-Id: Iaba285985b616a35e3dfe33cdd45667174e7c69d
Partial-Bug: #1687593
(cherry picked from commit b7aece57d2
)
This commit is contained in:
parent
59e4b4923f
commit
215788d26d
|
@ -18,6 +18,7 @@ from oslo_log import log
|
|||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import timeutils
|
||||
from six.moves import http_client
|
||||
from six.moves.urllib import parse as urlparse
|
||||
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
|
@ -217,6 +218,15 @@ class OAuthControllerV3(controller.V3Controller):
|
|||
collection_name = 'not_used'
|
||||
member_name = 'not_used'
|
||||
|
||||
def _update_url_scheme(self, request):
|
||||
"""Update request url scheme with base url scheme."""
|
||||
url = self.base_url(request.context_dict, request.context_dict['path'])
|
||||
url_scheme = list(urlparse.urlparse(url))[0]
|
||||
req_url_list = list(urlparse.urlparse(request.url))
|
||||
req_url_list[0] = url_scheme
|
||||
req_url = urlparse.urlunparse(req_url_list)
|
||||
return req_url
|
||||
|
||||
def create_request_token(self, request):
|
||||
oauth_headers = oauth1.get_oauth_headers(request.headers)
|
||||
consumer_id = oauth_headers.get('oauth_consumer_key')
|
||||
|
@ -233,13 +243,14 @@ class OAuthControllerV3(controller.V3Controller):
|
|||
self.resource_api.get_project(requested_project_id)
|
||||
self.oauth_api.get_consumer(consumer_id)
|
||||
|
||||
url = self._update_url_scheme(request)
|
||||
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(
|
||||
request.url,
|
||||
url,
|
||||
http_method='POST',
|
||||
body=request.params,
|
||||
headers=req_headers)
|
||||
|
@ -299,12 +310,13 @@ class OAuthControllerV3(controller.V3Controller):
|
|||
if now > expires:
|
||||
raise exception.Unauthorized(_('Request token is expired'))
|
||||
|
||||
url = self._update_url_scheme(request)
|
||||
access_verifier = oauth1.AccessTokenEndpoint(
|
||||
request_validator=validator.OAuthValidator(),
|
||||
token_generator=oauth1.token_generator)
|
||||
try:
|
||||
h, b, s = access_verifier.create_access_token_response(
|
||||
request.url,
|
||||
url,
|
||||
http_method='POST',
|
||||
body=request.params,
|
||||
headers=request.headers)
|
||||
|
|
|
@ -21,6 +21,7 @@ from oslo_serialization import jsonutils
|
|||
from pycadf import cadftaxonomy
|
||||
from six.moves import http_client
|
||||
from six.moves import urllib
|
||||
from six.moves.urllib import parse as urlparse
|
||||
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
|
@ -634,6 +635,13 @@ class UUIDAuthTokenTests(AuthTokenTests, OAuthFlowTests):
|
|||
|
||||
class MaliciousOAuth1Tests(OAuth1Tests):
|
||||
|
||||
def _switch_baseurl_scheme(self):
|
||||
"""Switch the base url scheme."""
|
||||
base_url_list = list(urlparse.urlparse(self.base_url))
|
||||
base_url_list[0] = 'https' if base_url_list[0] == 'http' else 'http'
|
||||
bad_url = urlparse.urlunparse(base_url_list)
|
||||
return bad_url
|
||||
|
||||
def test_bad_consumer_secret(self):
|
||||
consumer = self._create_single_consumer()
|
||||
consumer_id = consumer['id']
|
||||
|
@ -653,6 +661,17 @@ class MaliciousOAuth1Tests(OAuth1Tests):
|
|||
self.post(url, headers=headers,
|
||||
expected_status=http_client.UNAUTHORIZED)
|
||||
|
||||
def test_bad_request_url_scheme(self):
|
||||
consumer = self._create_single_consumer()
|
||||
consumer_id = consumer['id']
|
||||
consumer_secret = consumer['secret']
|
||||
consumer = {'key': consumer_id, 'secret': consumer_secret}
|
||||
bad_url_scheme = self._switch_baseurl_scheme()
|
||||
url, headers = self._create_request_token(consumer, self.project_id,
|
||||
base_url=bad_url_scheme)
|
||||
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']
|
||||
|
@ -746,7 +765,18 @@ class MaliciousOAuth1Tests(OAuth1Tests):
|
|||
self.assertIn('Invalid signature',
|
||||
resp_data.get('error', {}).get('message'))
|
||||
|
||||
# 2. Invalid signature.
|
||||
# 2. Invalid base url scheme.
|
||||
# Update the base url scheme, so it will fail to validate signature.
|
||||
bad_url_scheme = self._switch_baseurl_scheme()
|
||||
url, headers = self._create_access_token(consumer, request_token,
|
||||
base_url=bad_url_scheme)
|
||||
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'))
|
||||
|
||||
# 3. 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)
|
||||
|
@ -756,7 +786,7 @@ class MaliciousOAuth1Tests(OAuth1Tests):
|
|||
self.assertIn('Invalid signature',
|
||||
resp_data.get('error', {}).get('message'))
|
||||
|
||||
# 3. Invalid verifier.
|
||||
# 4. 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
|
||||
|
@ -771,7 +801,7 @@ class MaliciousOAuth1Tests(OAuth1Tests):
|
|||
self.assertIn('Provided verifier',
|
||||
resp_data.get('error', {}).get('message'))
|
||||
|
||||
# 4. The provided consumer does not exist.
|
||||
# 5. 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,
|
||||
|
@ -780,7 +810,7 @@ class MaliciousOAuth1Tests(OAuth1Tests):
|
|||
self.assertIn('Provided consumer does not exist',
|
||||
resp_data.get('error', {}).get('message'))
|
||||
|
||||
# 5. The consumer key provided does not match stored consumer key.
|
||||
# 6. 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)
|
||||
|
|
Loading…
Reference in New Issue