diff --git a/keystone/auth/plugins/oauth1.py b/keystone/auth/plugins/oauth1.py index 5ccc335274..91863453c3 100644 --- a/keystone/auth/plugins/oauth1.py +++ b/keystone/auth/plugins/oauth1.py @@ -18,6 +18,7 @@ from keystone import auth from keystone.common import dependency from keystone.contrib import oauth1 from keystone.contrib.oauth1 import core as oauth +from keystone.contrib.oauth1 import validator from keystone import exception from keystone.openstack.common import log from keystone.openstack.common import timeutils @@ -36,7 +37,6 @@ class OAuth(auth.AuthMethodHandler): """Turn a signed request with an access key into a keystone token.""" headers = context['headers'] oauth_headers = oauth.get_oauth_headers(headers) - consumer_id = oauth_headers.get('oauth_consumer_key') access_token_id = oauth_headers.get('oauth_token') if not access_token_id: @@ -44,7 +44,6 @@ class OAuth(auth.AuthMethodHandler): attribute='oauth_token', target='request') acc_token = self.oauth_api.get_access_token(access_token_id) - consumer = self.oauth_api.get_consumer_with_secret(consumer_id) expires_at = acc_token['expires_at'] if expires_at: @@ -54,27 +53,20 @@ class OAuth(auth.AuthMethodHandler): if now > expires: raise exception.Unauthorized(_('Access token is expired')) - consumer_obj = oauth1.Consumer(key=consumer['id'], - secret=consumer['secret']) - acc_token_obj = oauth1.Token(key=acc_token['id'], - secret=acc_token['access_secret']) - url = oauth.rebuild_url(context['path']) - oauth_request = oauth1.Request.from_request( + access_verifier = oauth.ResourceEndpoint( + request_validator=validator.OAuthValidator(), + token_generator=oauth.token_generator) + result, request = access_verifier.validate_protected_resource_request( + url, http_method='POST', - http_url=url, - headers=context['headers'], - query_string=context['query_string']) - oauth_server = oauth1.Server() - oauth_server.add_signature_method(oauth1.SignatureMethod_HMAC_SHA1()) - params = oauth_server.verify_request(oauth_request, - consumer_obj, - token=acc_token_obj) - - if params: - msg = _('There should not be any non-oauth parameters') - raise exception.Unauthorized(message=msg) - + body=context['query_string'], + headers=headers, + realms=None + ) + if not result: + msg = _('Could not validate the access token') + raise exception.Unauthorized(msg) auth_context['user_id'] = acc_token['authorizing_user_id'] auth_context['access_token_id'] = access_token_id auth_context['project_id'] = acc_token['project_id'] diff --git a/keystone/contrib/oauth1/backends/sql.py b/keystone/contrib/oauth1/backends/sql.py index e840e4e1e5..cde5c087f5 100644 --- a/keystone/contrib/oauth1/backends/sql.py +++ b/keystone/contrib/oauth1/backends/sql.py @@ -162,7 +162,12 @@ class OAuth1(sql.Base): consumer_ref.extra = new_consumer.extra return core.filter_consumer(consumer_ref.to_dict()) - def create_request_token(self, consumer_id, project_id, token_duration): + def create_request_token(self, consumer_id, project_id, token_duration, + request_token_id=None, request_token_secret=None): + if request_token_id is None: + request_token_id = uuid.uuid4().hex + if request_token_secret is None: + request_token_secret = uuid.uuid4().hex expiry_date = None if token_duration: now = timeutils.utcnow() @@ -170,9 +175,8 @@ class OAuth1(sql.Base): expiry_date = timeutils.isotime(future, subsecond=True) ref = {} - request_token_id = uuid.uuid4().hex ref['id'] = request_token_id - ref['request_secret'] = uuid.uuid4().hex + ref['request_secret'] = request_token_secret ref['verifier'] = None ref['authorizing_user_id'] = None ref['requested_project_id'] = project_id @@ -214,7 +218,12 @@ class OAuth1(sql.Base): return token_ref.to_dict() - def create_access_token(self, request_token_id, token_duration): + def create_access_token(self, request_token_id, token_duration, + access_token_id=None, access_token_secret=None): + if access_token_id is None: + access_token_id = uuid.uuid4().hex + if access_token_secret is None: + access_token_secret = uuid.uuid4().hex session = self.get_session() with session.begin(): req_token_ref = self._get_request_token(session, request_token_id) @@ -228,9 +237,8 @@ class OAuth1(sql.Base): # add Access Token ref = {} - access_token_id = uuid.uuid4().hex ref['id'] = access_token_id - ref['access_secret'] = uuid.uuid4().hex + ref['access_secret'] = access_token_secret ref['authorizing_user_id'] = token_dict['authorizing_user_id'] ref['project_id'] = token_dict['requested_project_id'] ref['role_ids'] = token_dict['role_ids'] diff --git a/keystone/contrib/oauth1/controllers.py b/keystone/contrib/oauth1/controllers.py index c3bd21db42..85cfec8b36 100644 --- a/keystone/contrib/oauth1/controllers.py +++ b/keystone/contrib/oauth1/controllers.py @@ -21,6 +21,7 @@ from keystone.common import dependency from keystone.common import wsgi from keystone import config from keystone.contrib.oauth1 import core as oauth1 +from keystone.contrib.oauth1 import validator from keystone import exception from keystone.openstack.common import jsonutils from keystone.openstack.common import timeutils @@ -176,26 +177,21 @@ class OAuthControllerV3(controller.V3Controller): raise exception.ValidationError( attribute='requested_project_id', target='request') - consumer_ref = self.oauth_api.get_consumer_with_secret(consumer_id) - consumer = oauth1.Consumer(key=consumer_ref['id'], - secret=consumer_ref['secret']) - url = oauth1.rebuild_url(context['path']) - oauth_request = oauth1.Request.from_request( - http_method='POST', - http_url=url, - headers=context['headers'], - query_string=context['query_string'], - parameters={'requested_project_id': requested_project_id}) - oauth_server = oauth1.Server() - oauth_server.add_signature_method(oauth1.SignatureMethod_HMAC_SHA1()) - params = oauth_server.verify_request(oauth_request, - consumer, - token=None) - project_params = params['requested_project_id'] - if project_params != requested_project_id: - msg = _('Non-oauth parameter - project, do not match') + req_headers = {'Requested-Project-Id': requested_project_id} + req_headers.update(headers) + request_verifier = oauth1.RequestTokenEndpoint( + request_validator=validator.OAuthValidator(), + token_generator=oauth1.token_generator) + h, b, s = request_verifier.create_request_token_response( + url, + http_method='POST', + body=context['query_string'], + headers=req_headers) + + if (not b) or int(s) > 399: + msg = _('Invalid signature') raise exception.Unauthorized(message=msg) request_token_duration = CONF.oauth1.request_token_duration @@ -235,7 +231,6 @@ class OAuthControllerV3(controller.V3Controller): raise exception.ValidationError( attribute='oauth_verifier', target='request') - consumer = self.oauth_api.get_consumer_with_secret(consumer_id) req_token = self.oauth_api.get_request_token( request_token_id) @@ -247,25 +242,18 @@ class OAuthControllerV3(controller.V3Controller): if now > expires: raise exception.Unauthorized(_('Request token is expired')) - consumer_obj = oauth1.Consumer(key=consumer['id'], - secret=consumer['secret']) - req_token_obj = oauth1.Token(key=req_token['id'], - secret=req_token['request_secret']) - req_token_obj.set_verifier(oauth_verifier) - url = oauth1.rebuild_url(context['path']) - oauth_request = oauth1.Request.from_request( - http_method='POST', - http_url=url, - headers=context['headers'], - query_string=context['query_string']) - oauth_server = oauth1.Server() - oauth_server.add_signature_method(oauth1.SignatureMethod_HMAC_SHA1()) - params = oauth_server.verify_request(oauth_request, - consumer_obj, - token=req_token_obj) - if params: + access_verifier = oauth1.AccessTokenEndpoint( + request_validator=validator.OAuthValidator(), + token_generator=oauth1.token_generator) + h, b, s = access_verifier.create_access_token_response( + url, + http_method='POST', + body=context['query_string'], + headers=headers) + params = oauth1.extract_non_oauth_params(b) + if len(params) != 0: msg = _('There should not be any non-oauth parameters') raise exception.Unauthorized(message=msg) diff --git a/keystone/contrib/oauth1/core.py b/keystone/contrib/oauth1/core.py index 857a4ad845..d88a828121 100644 --- a/keystone/contrib/oauth1/core.py +++ b/keystone/contrib/oauth1/core.py @@ -20,8 +20,10 @@ from __future__ import absolute_import import abc -import oauth2 as oauth +import oauthlib.common +from oauthlib import oauth1 import six +import uuid from keystone.common import dependency from keystone.common import extension @@ -30,19 +32,33 @@ from keystone import config from keystone import exception -Consumer = oauth.Consumer -Request = oauth.Request -Server = oauth.Server -SignatureMethod = oauth.SignatureMethod -SignatureMethod_HMAC_SHA1 = oauth.SignatureMethod_HMAC_SHA1 -SignatureMethod_PLAINTEXT = oauth.SignatureMethod_PLAINTEXT -Token = oauth.Token -Client = oauth.Client +RequestValidator = oauth1.RequestValidator +Client = oauth1.Client +AccessTokenEndpoint = oauth1.AccessTokenEndpoint +ResourceEndpoint = oauth1.ResourceEndpoint +AuthorizationEndpoint = oauth1.AuthorizationEndpoint +SIG_HMAC = oauth1.SIGNATURE_HMAC +RequestTokenEndpoint = oauth1.RequestTokenEndpoint +oRequest = oauthlib.common.Request + + +class Token(object): + def __init__(self, key, secret): + self.key = key + self.secret = secret + self.verifier = None + + def set_verifier(self, verifier): + self.verifier = verifier CONF = config.CONF +def token_generator(*args, **kwargs): + return uuid.uuid4().hex + + EXTENSION_DATA = { 'name': 'OpenStack OAUTH1 API', 'namespace': 'http://docs.openstack.org/identity/api/ext/' @@ -116,13 +132,14 @@ def get_oauth_headers(headers): # to split the rest of the headers. auth_header = headers['Authorization'] - # Check that the authorization header is OAuth. - if auth_header[:6] == 'OAuth ': - auth_header = auth_header[6:] - # Get the parameters from the header. - header_params = oauth.Request._split_header(auth_header) - parameters.update(header_params) - return parameters + params = oauth1.rfc5849.utils.parse_authorization_header(auth_header) + parameters.update(dict(params)) + return parameters + + +def extract_non_oauth_params(query_string): + params = oauthlib.common.extract_params(query_string) + return dict([(k, v) for k, v in params if not k.startswith('oauth_')]) @dependency.provider('oauth_api') diff --git a/keystone/contrib/oauth1/validator.py b/keystone/contrib/oauth1/validator.py new file mode 100644 index 0000000000..37a5ef0188 --- /dev/null +++ b/keystone/contrib/oauth1/validator.py @@ -0,0 +1,184 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2014 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""oAuthlib request validator.""" + +from keystone.common import dependency +from keystone.contrib.oauth1 import core as oauth1 +from keystone import exception +from keystone.openstack.common import log + + +METHOD_NAME = 'oauth_validator' +LOG = log.getLogger(__name__) + + +@dependency.requires('oauth_api') +class OAuthValidator(oauth1.RequestValidator): + + def __init__(self): + self.oauth_api = oauth1.Manager() + + #TODO(mhu) set as option probably ? + @property + def enforce_ssl(self): + return False + + @property + def safe_characters(self): + #oauth tokens are generated from a uuid hex value + return set("abcdef0123456789") + + def _check_token(self, token): + # generic token verification when they're obtained from a uuid hex + return (set(token) <= self.safe_characters and + len(token) == 32) + + def check_client_key(self, client_key): + return self._check_token(client_key) + + def check_request_token(self, request_token): + return self._check_token(request_token) + + def check_access_token(self, access_token): + return self._check_token(access_token) + + def check_nonce(self, nonce): + # Assuming length is not a concern + return set(nonce) <= self.safe_characters + + def check_verifier(self, verifier): + try: + return 1000 <= int(verifier) <= 9999 + except ValueError: + return False + + def get_client_secret(self, client_key, request): + client = self.oauth_api.get_consumer_with_secret(client_key) + return client['secret'] + + def get_request_token_secret(self, client_key, token, request): + token_ref = self.oauth_api.get_request_token(token) + return token_ref['request_secret'] + + def get_access_token_secret(self, client_key, token, request): + access_token = self.oauth_api.get_access_token(token) + return access_token['access_secret'] + + def get_default_realms(self, client_key, request): + # realms weren't implemented with the previous library + return [] + + def get_realms(self, token, request): + return [] + + def get_redirect_uri(self, token, request): + # OOB (out of band) is supposed to be the default value to use + return 'oob' + + def get_rsa_key(self, client_key, request): + # HMAC signing is used, so return a dummy value + return '' + + def invalidate_request_token(self, client_key, request_token, request): + # this method is invoked when an access token is generated out of a + # request token, to make sure that request token cannot be consumed + # anymore. This is done in the backend, so we do nothing here. + pass + + def validate_client_key(self, client_key, request): + try: + return self.oauth_api.get_consumer(client_key) is not None + except exception.NotFound: + return False + + def validate_request_token(self, client_key, token, request): + try: + return self.oauth_api.get_request_token(token) is not None + except exception.NotFound: + return False + + def validate_access_token(self, client_key, token, request): + try: + return self.oauth_api.get_access_token(token) is not None + except exception.NotFound: + return False + + def validate_timestamp_and_nonce(self, + client_key, + timestamp, + nonce, + request, + request_token=None, + access_token=None): + return True + + def validate_redirect_uri(self, client_key, redirect_uri, request): + # we expect OOB, we don't really care + return True + + def validate_requested_realms(self, client_key, realms, request): + # realms are not used + return True + + def validate_realms(self, + client_key, + token, + request, + uri=None, + realms=None): + return True + + def validate_verifier(self, client_key, token, verifier, request): + try: + req_token = self.oauth_api.get_request_token(token) + return req_token['verifier'] == verifier + except exception.NotFound: + return False + + def verify_request_token(self, token, request): + # there aren't strong expectations on the request token format + return isinstance(token, basestring) + + def verify_realms(self, token, realms, request): + return True + + # The following save_XXX methods are called to create tokens. I chose to + # keep the original logic, but the comments below show how that could be + # implemented. The real implementation logic is in the backend. + def save_access_token(self, token, request): + pass +# token_duration = CONF.oauth1.request_token_duration +# request_token_id = request.client_key +# self.oauth_api.create_access_token(request_token_id, +# token_duration, +# token["oauth_token"], +# token["oauth_token_secret"]) + + def save_request_token(self, token, request): + pass +# project_id = request.headers.get('Requested-Project-Id') +# token_duration = CONF.oauth1.request_token_duration +# self.oauth_api.create_request_token(request.client_key, +# project_id, +# token_duration, +# token["oauth_token"], +# token["oauth_token_secret"]) + + def save_verifier(self, token, verifier, request): + # keep the old logic for this, as it is done in two steps and requires + # information that the request validator has no access to + pass diff --git a/keystone/tests/test_v3_oauth1.py b/keystone/tests/test_v3_oauth1.py index 3aecdb3d59..a5ca5d1352 100644 --- a/keystone/tests/test_v3_oauth1.py +++ b/keystone/tests/test_v3_oauth1.py @@ -60,50 +60,43 @@ class OAuth1Tests(test_v3.RestfulTestCase): body={'consumer': ref}) return resp.result.get('consumer') - def _oauth_request(self, consumer, token=None, **kw): - return oauth1.Request.from_consumer_and_token(consumer=consumer, - token=token, - **kw) - def _create_request_token(self, consumer, project_id): - params = {'requested_project_id': project_id} - headers = {'Content-Type': 'application/json'} - url = '/OS-OAUTH1/request_token' - oreq = self._oauth_request( - consumer=consumer, - http_url=self.base_url + url, - http_method='POST', - parameters=params) - hmac = oauth1.SignatureMethod_HMAC_SHA1() - oreq.sign_request(hmac, consumer, None) - headers.update(oreq.to_header()) - headers.update(params) - return url, headers + 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, + http_method='POST', + headers=headers) + return endpoint, headers def _create_access_token(self, consumer, token): - headers = {'Content-Type': 'application/json'} - url = '/OS-OAUTH1/access_token' - oreq = self._oauth_request( - consumer=consumer, token=token, - http_method='POST', - http_url=self.base_url + url) - hmac = oauth1.SignatureMethod_HMAC_SHA1() - oreq.sign_request(hmac, consumer, token) - headers.update(oreq.to_header()) - return url, headers + endpoint = '/OS-OAUTH1/access_token' + client = oauth1.Client(consumer['key'], + client_secret=consumer['secret'], + resource_owner_key=token.key, + resource_owner_secret=token.secret, + signature_method=oauth1.SIG_HMAC, + verifier=token.verifier) + url, headers, body = client.sign(self.base_url + endpoint, + http_method='POST') + headers.update({'Content-Type': 'application/json'}) + return endpoint, headers def _get_oauth_token(self, consumer, token): - headers = {'Content-Type': 'application/json'} - body = {'auth': {'identity': {'methods': ['oauth1'], 'oauth1': {}}}} - url = '/auth/tokens' - oreq = self._oauth_request( - consumer=consumer, token=token, - http_method='POST', - http_url=self.base_url + url) - hmac = oauth1.SignatureMethod_HMAC_SHA1() - oreq.sign_request(hmac, consumer, token) - headers.update(oreq.to_header()) - return url, headers, body + client = oauth1.Client(consumer['key'], + client_secret=consumer['secret'], + resource_owner_key=token.key, + resource_owner_secret=token.secret, + signature_method=oauth1.SIG_HMAC) + endpoint = '/auth/tokens' + url, headers, body = client.sign(self.base_url + endpoint, + http_method='POST') + headers.update({'Content-Type': 'application/json'}) + ref = {'auth': {'identity': {'oauth1': {}, 'methods': ['oauth1']}}} + return endpoint, headers, ref def _authorize_request_token(self, request_id): return '/OS-OAUTH1/authorize/%s' % (request_id) @@ -214,8 +207,8 @@ class OAuthFlowTests(OAuth1Tests): consumer = self._create_single_consumer() consumer_id = consumer.get('id') consumer_secret = consumer.get('secret') - self.consumer = oauth1.Consumer(consumer_id, consumer_secret) - self.assertIsNotNone(self.consumer.key) + self.consumer = {'key': consumer_id, 'secret': consumer_secret} + self.assertIsNotNone(self.consumer['secret']) url, headers = self._create_request_token(self.consumer, self.project_id) @@ -270,7 +263,7 @@ class AccessTokenCRUDTests(OAuthFlowTests): 'key': self.access_token.key}) entity = resp.result.get('access_token') self.assertEqual(entity['id'], self.access_token.key) - self.assertEqual(entity['consumer_id'], self.consumer.key) + self.assertEqual(entity['consumer_id'], self.consumer['key']) def test_get_access_token_dne(self): self.get('/users/%(user_id)s/OS-OAUTH1/access_tokens/%(key)s' @@ -339,7 +332,7 @@ class AuthTokenTests(OAuthFlowTests): oauth_section = r.result['token']['OS-OAUTH1'] self.assertEqual(oauth_section['access_token_id'], self.access_token.key) - self.assertEqual(oauth_section['consumer_id'], self.consumer.key) + self.assertEqual(oauth_section['consumer_id'], self.consumer['key']) # verify the roles section roles_list = r.result['token']['roles'] @@ -371,7 +364,7 @@ class AuthTokenTests(OAuthFlowTests): self.test_oauth_flow() # Delete consumer - consumer_id = self.consumer.key + consumer_id = self.consumer['key'] resp = self.delete('/OS-OAUTH1/consumers/%(consumer_id)s' % {'consumer_id': consumer_id}) self.assertResponseStatus(resp, 204) @@ -443,7 +436,7 @@ class AuthTokenTests(OAuthFlowTests): self.test_oauth_flow() self.token_api.get_token(self.keystone_token_id) self.token_api.delete_tokens(self.user_id, - consumer_id=self.consumer.key) + consumer_id=self.consumer['key']) self.assertRaises(exception.TokenNotFound, self.token_api.get_token, self.keystone_token_id) @@ -453,20 +446,18 @@ class MaliciousOAuth1Tests(OAuth1Tests): def test_bad_consumer_secret(self): consumer = self._create_single_consumer() consumer_id = consumer.get('id') - consumer = oauth1.Consumer(consumer_id, "bad_secret") - url, headers = self._create_request_token(consumer, - self.project_id) - self.post(url, headers=headers, expected_status=500) + consumer = {'key': consumer_id, 'secret': uuid.uuid4().hex} + url, headers = self._create_request_token(consumer, self.project_id) + self.post(url, headers=headers, expected_status=401) def test_bad_request_token_key(self): consumer = self._create_single_consumer() consumer_id = consumer.get('id') consumer_secret = consumer.get('secret') - consumer = oauth1.Consumer(consumer_id, consumer_secret) - url, headers = self._create_request_token(consumer, - self.project_id) + consumer = {'key': consumer_id, 'secret': consumer_secret} + url, headers = self._create_request_token(consumer, self.project_id) self.post(url, headers=headers) - url = self._authorize_request_token("bad_key") + url = self._authorize_request_token(uuid.uuid4().hex) body = {'roles': [{'id': self.role_id}]} self.put(url, body=body, expected_status=404) @@ -474,10 +465,9 @@ class MaliciousOAuth1Tests(OAuth1Tests): consumer = self._create_single_consumer() consumer_id = consumer.get('id') consumer_secret = consumer.get('secret') - consumer = oauth1.Consumer(consumer_id, consumer_secret) + consumer = {'key': consumer_id, 'secret': consumer_secret} - url, headers = self._create_request_token(consumer, - self.project_id) + url, headers = self._create_request_token(consumer, self.project_id) content = self.post(url, headers=headers) credentials = urlparse.parse_qs(content.result) request_key = credentials.get('oauth_token')[0] @@ -490,26 +480,23 @@ class MaliciousOAuth1Tests(OAuth1Tests): verifier = resp.result['token']['oauth_verifier'] self.assertIsNotNone(verifier) - request_token.set_verifier("bad verifier") - url, headers = self._create_access_token(consumer, - request_token) + request_token.set_verifier(uuid.uuid4().hex) + url, headers = self._create_access_token(consumer, request_token) self.post(url, headers=headers, expected_status=401) def test_bad_authorizing_roles(self): consumer = self._create_single_consumer() consumer_id = consumer.get('id') consumer_secret = consumer.get('secret') - consumer = oauth1.Consumer(consumer_id, consumer_secret) + consumer = {'key': consumer_id, 'secret': consumer_secret} - url, headers = self._create_request_token(consumer, - self.project_id) + url, headers = self._create_request_token(consumer, self.project_id) content = self.post(url, headers=headers) credentials = urlparse.parse_qs(content.result) request_key = credentials.get('oauth_token')[0] - self.assignment_api.remove_role_from_user_and_project(self.user_id, - self.project_id, - self.role_id) + self.assignment_api.remove_role_from_user_and_project( + self.user_id, self.project_id, self.role_id) url = self._authorize_request_token(request_key) body = {'roles': [{'id': self.role_id}]} self.admin_request(path=url, method='PUT', @@ -521,8 +508,8 @@ class MaliciousOAuth1Tests(OAuth1Tests): consumer = self._create_single_consumer() consumer_id = consumer.get('id') consumer_secret = consumer.get('secret') - self.consumer = oauth1.Consumer(consumer_id, consumer_secret) - self.assertIsNotNone(self.consumer.key) + self.consumer = {'key': consumer_id, 'secret': consumer_secret} + self.assertIsNotNone(self.consumer['key']) url, headers = self._create_request_token(self.consumer, self.project_id) @@ -542,8 +529,8 @@ class MaliciousOAuth1Tests(OAuth1Tests): consumer = self._create_single_consumer() consumer_id = consumer.get('id') consumer_secret = consumer.get('secret') - self.consumer = oauth1.Consumer(consumer_id, consumer_secret) - self.assertIsNotNone(self.consumer.key) + self.consumer = {'key': consumer_id, 'secret': consumer_secret} + self.assertIsNotNone(self.consumer['key']) url, headers = self._create_request_token(self.consumer, self.project_id) diff --git a/requirements.txt b/requirements.txt index e7c7a35abc..472a949abd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,7 @@ iso8601>=0.1.8 python-keystoneclient>=0.4.1 oslo.config>=1.2.0 Babel>=1.3 -oauth2 +oauthlib dogpile.cache>=0.5.0 # KDS exclusive dependencies