Merge "Authenticate via oauth"
This commit is contained in:
@@ -14,13 +14,17 @@
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import httpretty
|
import httpretty
|
||||||
|
import mock
|
||||||
import six
|
import six
|
||||||
from testtools import matchers
|
from testtools import matchers
|
||||||
|
|
||||||
from keystoneclient.openstack.common import jsonutils
|
from keystoneclient.openstack.common import jsonutils
|
||||||
from keystoneclient.openstack.common import timeutils
|
from keystoneclient.openstack.common import timeutils
|
||||||
|
from keystoneclient import session
|
||||||
|
from keystoneclient.tests.v3 import client_fixtures
|
||||||
from keystoneclient.tests.v3 import utils
|
from keystoneclient.tests.v3 import utils
|
||||||
from keystoneclient.v3.contrib.oauth1 import access_tokens
|
from keystoneclient.v3.contrib.oauth1 import access_tokens
|
||||||
|
from keystoneclient.v3.contrib.oauth1 import auth
|
||||||
from keystoneclient.v3.contrib.oauth1 import consumers
|
from keystoneclient.v3.contrib.oauth1 import consumers
|
||||||
from keystoneclient.v3.contrib.oauth1 import request_tokens
|
from keystoneclient.v3.contrib.oauth1 import request_tokens
|
||||||
|
|
||||||
@@ -227,3 +231,70 @@ class AccessTokenTests(TokenTests):
|
|||||||
timestamp=expires_at)
|
timestamp=expires_at)
|
||||||
self._validate_oauth_headers(req_headers['Authorization'],
|
self._validate_oauth_headers(req_headers['Authorization'],
|
||||||
oauth_client)
|
oauth_client)
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticateWithOAuthTests(TokenTests):
|
||||||
|
def setUp(self):
|
||||||
|
super(AuthenticateWithOAuthTests, self).setUp()
|
||||||
|
if oauth1 is None:
|
||||||
|
self.skipTest('optional package oauthlib is not installed')
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_oauth_authenticate_success(self):
|
||||||
|
consumer_key = uuid.uuid4().hex
|
||||||
|
consumer_secret = uuid.uuid4().hex
|
||||||
|
access_key = uuid.uuid4().hex
|
||||||
|
access_secret = uuid.uuid4().hex
|
||||||
|
|
||||||
|
# Just use an existing project scoped token and change
|
||||||
|
# the methods to oauth1, and add an OS-OAUTH1 section.
|
||||||
|
oauth_token = client_fixtures.project_scoped_token()
|
||||||
|
oauth_token['methods'] = ["oauth1"]
|
||||||
|
oauth_token['OS-OAUTH1'] = {"consumer_id": consumer_key,
|
||||||
|
"access_token_id": access_key}
|
||||||
|
self.stub_auth(json=oauth_token)
|
||||||
|
|
||||||
|
a = auth.OAuth(self.TEST_URL, consumer_key=consumer_key,
|
||||||
|
consumer_secret=consumer_secret,
|
||||||
|
access_key=access_key,
|
||||||
|
access_secret=access_secret)
|
||||||
|
s = session.Session(auth=a)
|
||||||
|
t = s.get_token()
|
||||||
|
self.assertEqual(t, self.TEST_TOKEN)
|
||||||
|
|
||||||
|
OAUTH_REQUEST_BODY = {
|
||||||
|
"auth": {
|
||||||
|
"identity": {
|
||||||
|
"methods": ["oauth1"],
|
||||||
|
"oauth1": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.assertRequestBodyIs(json=OAUTH_REQUEST_BODY)
|
||||||
|
|
||||||
|
# Assert that the headers have the same oauthlib data
|
||||||
|
req_headers = httpretty.last_request().headers
|
||||||
|
oauth_client = oauth1.Client(consumer_key,
|
||||||
|
client_secret=consumer_secret,
|
||||||
|
resource_owner_key=access_key,
|
||||||
|
resource_owner_secret=access_secret,
|
||||||
|
signature_method=oauth1.SIGNATURE_HMAC)
|
||||||
|
self._validate_oauth_headers(req_headers['Authorization'],
|
||||||
|
oauth_client)
|
||||||
|
|
||||||
|
|
||||||
|
class TestOAuthLibModule(utils.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestOAuthLibModule, self).setUp()
|
||||||
|
|
||||||
|
def test_no_oauthlib_installed(self):
|
||||||
|
with mock.patch.object(auth, 'oauth1', None):
|
||||||
|
self.assertRaises(NotImplementedError,
|
||||||
|
auth.OAuth,
|
||||||
|
self.TEST_URL,
|
||||||
|
consumer_key=uuid.uuid4().hex,
|
||||||
|
consumer_secret=uuid.uuid4().hex,
|
||||||
|
access_key=uuid.uuid4().hex,
|
||||||
|
access_secret=uuid.uuid4().hex)
|
||||||
|
58
keystoneclient/v3/contrib/oauth1/auth.py
Normal file
58
keystoneclient/v3/contrib/oauth1/auth.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from keystoneclient.auth.identity import v3
|
||||||
|
|
||||||
|
try:
|
||||||
|
from oauthlib import oauth1
|
||||||
|
except ImportError:
|
||||||
|
oauth1 = None
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthMethod(v3.AuthMethod):
|
||||||
|
|
||||||
|
_method_parameters = ['consumer_key', 'consumer_secret',
|
||||||
|
'access_key', 'access_secret']
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
"""Construct an OAuth based authentication method.
|
||||||
|
|
||||||
|
:param string consumer_key: Consumer key.
|
||||||
|
:param string consumer_secret: Consumer secret.
|
||||||
|
:param string access_key: Access token key.
|
||||||
|
:param string access_secret: Access token secret.
|
||||||
|
"""
|
||||||
|
super(OAuthMethod, self).__init__(**kwargs)
|
||||||
|
if oauth1 is None:
|
||||||
|
raise NotImplementedError('optional package oauthlib'
|
||||||
|
' is not installed')
|
||||||
|
|
||||||
|
def get_auth_data(self, session, auth, headers, **kwargs):
|
||||||
|
# Add the oauth specific content into the headers
|
||||||
|
oauth_client = oauth1.Client(self.consumer_key,
|
||||||
|
client_secret=self.consumer_secret,
|
||||||
|
resource_owner_key=self.access_key,
|
||||||
|
resource_owner_secret=self.access_secret,
|
||||||
|
signature_method=oauth1.SIGNATURE_HMAC)
|
||||||
|
o_url, o_headers, o_body = oauth_client.sign(auth.token_url,
|
||||||
|
http_method='POST')
|
||||||
|
|
||||||
|
headers.update(o_headers)
|
||||||
|
return 'oauth1', {}
|
||||||
|
|
||||||
|
|
||||||
|
class OAuth(v3._AuthConstructor):
|
||||||
|
_auth_method_class = OAuthMethod
|
||||||
|
|
||||||
|
def __init__(self, auth_url, **kwargs):
|
||||||
|
super(OAuth, self).__init__(auth_url, **kwargs)
|
Reference in New Issue
Block a user