Merge "OAuth request/access token and consumer support for oauth client API"
This commit is contained in:
		
							
								
								
									
										229
									
								
								keystoneclient/tests/v3/test_oauth1.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								keystoneclient/tests/v3/test_oauth1.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,229 @@
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
import uuid
 | 
			
		||||
 | 
			
		||||
import httpretty
 | 
			
		||||
import six
 | 
			
		||||
from testtools import matchers
 | 
			
		||||
 | 
			
		||||
from keystoneclient.openstack.common import jsonutils
 | 
			
		||||
from keystoneclient.openstack.common import timeutils
 | 
			
		||||
from keystoneclient.tests.v3 import utils
 | 
			
		||||
from keystoneclient.v3.contrib.oauth1 import access_tokens
 | 
			
		||||
from keystoneclient.v3.contrib.oauth1 import consumers
 | 
			
		||||
from keystoneclient.v3.contrib.oauth1 import request_tokens
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from oauthlib import oauth1
 | 
			
		||||
except ImportError:
 | 
			
		||||
    oauth1 = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseTest(utils.TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(BaseTest, self).setUp()
 | 
			
		||||
        if oauth1 is None:
 | 
			
		||||
            self.skipTest('oauthlib package not available')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConsumerTests(BaseTest, utils.CrudTests):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(ConsumerTests, self).setUp()
 | 
			
		||||
        self.key = 'consumer'
 | 
			
		||||
        self.collection_key = 'consumers'
 | 
			
		||||
        self.model = consumers.Consumer
 | 
			
		||||
        self.manager = self.client.oauth1.consumers
 | 
			
		||||
        self.path_prefix = 'OS-OAUTH1'
 | 
			
		||||
 | 
			
		||||
    def new_ref(self, **kwargs):
 | 
			
		||||
        kwargs = super(ConsumerTests, self).new_ref(**kwargs)
 | 
			
		||||
        kwargs.setdefault('description', uuid.uuid4().hex)
 | 
			
		||||
        return kwargs
 | 
			
		||||
 | 
			
		||||
    @httpretty.activate
 | 
			
		||||
    def test_description_is_optional(self):
 | 
			
		||||
        consumer_id = uuid.uuid4().hex
 | 
			
		||||
        resp_ref = {'consumer': {'description': None,
 | 
			
		||||
                                 'id': consumer_id}}
 | 
			
		||||
 | 
			
		||||
        self.stub_url(httpretty.POST,
 | 
			
		||||
                      [self.path_prefix, self.collection_key],
 | 
			
		||||
                      status=201, json=resp_ref)
 | 
			
		||||
 | 
			
		||||
        consumer = self.manager.create()
 | 
			
		||||
        self.assertEqual(consumer_id, consumer.id)
 | 
			
		||||
        self.assertIsNone(consumer.description)
 | 
			
		||||
 | 
			
		||||
    @httpretty.activate
 | 
			
		||||
    def test_description_not_included(self):
 | 
			
		||||
        consumer_id = uuid.uuid4().hex
 | 
			
		||||
        resp_ref = {'consumer': {'id': consumer_id}}
 | 
			
		||||
 | 
			
		||||
        self.stub_url(httpretty.POST,
 | 
			
		||||
                      [self.path_prefix, self.collection_key],
 | 
			
		||||
                      status=201, json=resp_ref)
 | 
			
		||||
 | 
			
		||||
        consumer = self.manager.create()
 | 
			
		||||
        self.assertEqual(consumer_id, consumer.id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TokenTests(BaseTest):
 | 
			
		||||
    def _new_oauth_token(self):
 | 
			
		||||
        key = uuid.uuid4().hex
 | 
			
		||||
        secret = uuid.uuid4().hex
 | 
			
		||||
        token = 'oauth_token=%s&oauth_token_secret=%s' % (key, secret)
 | 
			
		||||
        return (key, secret, token)
 | 
			
		||||
 | 
			
		||||
    def _new_oauth_token_with_expires_at(self):
 | 
			
		||||
        key, secret, token = self._new_oauth_token()
 | 
			
		||||
        expires_at = timeutils.strtime()
 | 
			
		||||
        token += '&oauth_expires_at=%s' % expires_at
 | 
			
		||||
        return (key, secret, expires_at, token)
 | 
			
		||||
 | 
			
		||||
    def _validate_oauth_headers(self, auth_header, oauth_client):
 | 
			
		||||
        """Assert that the data in the headers matches the data
 | 
			
		||||
        that is produced from oauthlib.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        self.assertThat(auth_header, matchers.StartsWith('OAuth '))
 | 
			
		||||
        auth_header = auth_header[len('OAuth '):]
 | 
			
		||||
        header_params = oauth_client.get_oauth_params()
 | 
			
		||||
        parameters = dict(header_params)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual('HMAC-SHA1', parameters['oauth_signature_method'])
 | 
			
		||||
        self.assertEqual('1.0', parameters['oauth_version'])
 | 
			
		||||
        self.assertIsInstance(parameters['oauth_nonce'], six.string_types)
 | 
			
		||||
        self.assertEqual(oauth_client.client_key,
 | 
			
		||||
                         parameters['oauth_consumer_key'])
 | 
			
		||||
        if oauth_client.resource_owner_key:
 | 
			
		||||
            self.assertEqual(oauth_client.resource_owner_key,
 | 
			
		||||
                             parameters['oauth_token'],)
 | 
			
		||||
        if oauth_client.verifier:
 | 
			
		||||
            self.assertEqual(oauth_client.verifier,
 | 
			
		||||
                             parameters['oauth_verifier'])
 | 
			
		||||
        if oauth_client.callback_uri:
 | 
			
		||||
            self.assertEqual(oauth_client.callback_uri,
 | 
			
		||||
                             parameters['oauth_callback'])
 | 
			
		||||
        if oauth_client.timestamp:
 | 
			
		||||
            self.assertEqual(oauth_client.timestamp,
 | 
			
		||||
                             parameters['oauth_timestamp'])
 | 
			
		||||
        return parameters
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RequestTokenTests(TokenTests):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(RequestTokenTests, self).setUp()
 | 
			
		||||
        self.model = request_tokens.RequestToken
 | 
			
		||||
        self.manager = self.client.oauth1.request_tokens
 | 
			
		||||
        self.path_prefix = 'OS-OAUTH1'
 | 
			
		||||
 | 
			
		||||
    @httpretty.activate
 | 
			
		||||
    def test_authorize_request_token(self):
 | 
			
		||||
        request_key = uuid.uuid4().hex
 | 
			
		||||
        info = {'id': request_key,
 | 
			
		||||
                'key': request_key,
 | 
			
		||||
                'secret': uuid.uuid4().hex}
 | 
			
		||||
        request_token = request_tokens.RequestToken(self.manager, info)
 | 
			
		||||
 | 
			
		||||
        verifier = uuid.uuid4().hex
 | 
			
		||||
        resp_ref = {'token': {'oauth_verifier': verifier}}
 | 
			
		||||
        self.stub_url(httpretty.PUT,
 | 
			
		||||
                      [self.path_prefix, 'authorize', request_key],
 | 
			
		||||
                      status=200, json=resp_ref)
 | 
			
		||||
 | 
			
		||||
        # Assert the manager is returning the expected data
 | 
			
		||||
        role_id = uuid.uuid4().hex
 | 
			
		||||
        token = request_token.authorize([role_id])
 | 
			
		||||
        self.assertEqual(verifier, token.oauth_verifier)
 | 
			
		||||
 | 
			
		||||
        # Assert that the request was sent in the expected structure
 | 
			
		||||
        exp_body = {'roles': [{'id': role_id}]}
 | 
			
		||||
        self.assertRequestBodyIs(json=exp_body)
 | 
			
		||||
 | 
			
		||||
    @httpretty.activate
 | 
			
		||||
    def test_create_request_token(self):
 | 
			
		||||
        project_id = uuid.uuid4().hex
 | 
			
		||||
        consumer_key = uuid.uuid4().hex
 | 
			
		||||
        consumer_secret = uuid.uuid4().hex
 | 
			
		||||
 | 
			
		||||
        request_key, request_secret, resp_ref = self._new_oauth_token()
 | 
			
		||||
 | 
			
		||||
        # NOTE(stevemar) The server expects the body to be JSON. Even though
 | 
			
		||||
        # the resp_ref is a string it is not a JSON string.
 | 
			
		||||
        self.stub_url(httpretty.POST, [self.path_prefix, 'request_token'],
 | 
			
		||||
                      status=201, body=jsonutils.dumps(resp_ref),
 | 
			
		||||
                      content_type='application/x-www-form-urlencoded')
 | 
			
		||||
 | 
			
		||||
        # Assert the manager is returning request token object
 | 
			
		||||
        request_token = self.manager.create(consumer_key, consumer_secret,
 | 
			
		||||
                                            project_id)
 | 
			
		||||
        self.assertIsInstance(request_token, self.model)
 | 
			
		||||
        self.assertEqual(request_key, request_token.key)
 | 
			
		||||
        self.assertEqual(request_secret, request_token.secret)
 | 
			
		||||
 | 
			
		||||
        # Assert that the project id is in the header
 | 
			
		||||
        self.assertRequestHeaderEqual('requested_project_id', project_id)
 | 
			
		||||
        req_headers = httpretty.last_request().headers
 | 
			
		||||
 | 
			
		||||
        oauth_client = oauth1.Client(consumer_key,
 | 
			
		||||
                                     client_secret=consumer_secret,
 | 
			
		||||
                                     signature_method=oauth1.SIGNATURE_HMAC,
 | 
			
		||||
                                     callback_uri="oob")
 | 
			
		||||
        self._validate_oauth_headers(req_headers['Authorization'],
 | 
			
		||||
                                     oauth_client)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AccessTokenTests(TokenTests):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(AccessTokenTests, self).setUp()
 | 
			
		||||
        self.manager = self.client.oauth1.access_tokens
 | 
			
		||||
        self.model = access_tokens.AccessToken
 | 
			
		||||
        self.path_prefix = 'OS-OAUTH1'
 | 
			
		||||
 | 
			
		||||
    @httpretty.activate
 | 
			
		||||
    def test_create_access_token_expires_at(self):
 | 
			
		||||
        verifier = uuid.uuid4().hex
 | 
			
		||||
        consumer_key = uuid.uuid4().hex
 | 
			
		||||
        consumer_secret = uuid.uuid4().hex
 | 
			
		||||
        request_key = uuid.uuid4().hex
 | 
			
		||||
        request_secret = uuid.uuid4().hex
 | 
			
		||||
 | 
			
		||||
        t = self._new_oauth_token_with_expires_at()
 | 
			
		||||
        access_key, access_secret, expires_at, resp_ref = t
 | 
			
		||||
 | 
			
		||||
        # NOTE(stevemar) The server expects the body to be JSON. Even though
 | 
			
		||||
        # the resp_ref is a string it is not a JSON string.
 | 
			
		||||
        self.stub_url(httpretty.POST, [self.path_prefix, 'access_token'],
 | 
			
		||||
                      status=201, body=jsonutils.dumps(resp_ref),
 | 
			
		||||
                      content_type='application/x-www-form-urlencoded')
 | 
			
		||||
 | 
			
		||||
        # Assert that the manager creates an access token object
 | 
			
		||||
        access_token = self.manager.create(consumer_key, consumer_secret,
 | 
			
		||||
                                           request_key, request_secret,
 | 
			
		||||
                                           verifier)
 | 
			
		||||
        self.assertIsInstance(access_token, self.model)
 | 
			
		||||
        self.assertEqual(access_key, access_token.key)
 | 
			
		||||
        self.assertEqual(access_secret, access_token.secret)
 | 
			
		||||
        self.assertEqual(expires_at, access_token.expires)
 | 
			
		||||
 | 
			
		||||
        req_headers = httpretty.last_request().headers
 | 
			
		||||
        oauth_client = oauth1.Client(consumer_key,
 | 
			
		||||
                                     client_secret=consumer_secret,
 | 
			
		||||
                                     resource_owner_key=request_key,
 | 
			
		||||
                                     resource_owner_secret=request_secret,
 | 
			
		||||
                                     signature_method=oauth1.SIGNATURE_HMAC,
 | 
			
		||||
                                     verifier=verifier,
 | 
			
		||||
                                     timestamp=expires_at)
 | 
			
		||||
        self._validate_oauth_headers(req_headers['Authorization'],
 | 
			
		||||
                                     oauth_client)
 | 
			
		||||
@@ -21,6 +21,7 @@ from keystoneclient import httpclient
 | 
			
		||||
from keystoneclient.openstack.common import jsonutils
 | 
			
		||||
from keystoneclient.v3.contrib import endpoint_filter
 | 
			
		||||
from keystoneclient.v3.contrib import federation
 | 
			
		||||
from keystoneclient.v3.contrib import oauth1
 | 
			
		||||
from keystoneclient.v3.contrib import trusts
 | 
			
		||||
from keystoneclient.v3 import credentials
 | 
			
		||||
from keystoneclient.v3 import domains
 | 
			
		||||
@@ -99,6 +100,7 @@ class Client(httpclient.HTTPClient):
 | 
			
		||||
        self.domains = domains.DomainManager(self)
 | 
			
		||||
        self.federation = federation.FederationManager(self)
 | 
			
		||||
        self.groups = groups.GroupManager(self)
 | 
			
		||||
        self.oauth1 = oauth1.create_oauth_manager(self)
 | 
			
		||||
        self.policies = policies.PolicyManager(self)
 | 
			
		||||
        self.projects = projects.ProjectManager(self)
 | 
			
		||||
        self.roles = roles.RoleManager(self)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								keystoneclient/v3/contrib/oauth1/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								keystoneclient/v3/contrib/oauth1/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
# 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.v3.contrib.oauth1.core import *  # noqa
 | 
			
		||||
							
								
								
									
										46
									
								
								keystoneclient/v3/contrib/oauth1/access_tokens.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								keystoneclient/v3/contrib/oauth1/access_tokens.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
# 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 __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
from keystoneclient import base
 | 
			
		||||
from keystoneclient.v3.contrib.oauth1 import utils
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from oauthlib import oauth1
 | 
			
		||||
except ImportError:
 | 
			
		||||
    oauth1 = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AccessToken(base.Resource):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AccessTokenManager(base.CrudManager):
 | 
			
		||||
    """Manager class for manipulating identity OAuth access tokens."""
 | 
			
		||||
    resource_class = AccessToken
 | 
			
		||||
 | 
			
		||||
    def create(self, consumer_key, consumer_secret, request_key,
 | 
			
		||||
               request_secret, verifier):
 | 
			
		||||
        endpoint = utils.OAUTH_PATH + '/access_token'
 | 
			
		||||
        oauth_client = oauth1.Client(consumer_key,
 | 
			
		||||
                                     client_secret=consumer_secret,
 | 
			
		||||
                                     resource_owner_key=request_key,
 | 
			
		||||
                                     resource_owner_secret=request_secret,
 | 
			
		||||
                                     signature_method=oauth1.SIGNATURE_HMAC,
 | 
			
		||||
                                     verifier=verifier)
 | 
			
		||||
        url = self.client.auth_url.rstrip("/") + endpoint
 | 
			
		||||
        url, headers, body = oauth_client.sign(url, http_method='POST')
 | 
			
		||||
        resp, body = self.client.post(endpoint, headers=headers)
 | 
			
		||||
        token = utils.get_oauth_token_from_body(body)
 | 
			
		||||
        return self.resource_class(self, token)
 | 
			
		||||
							
								
								
									
										52
									
								
								keystoneclient/v3/contrib/oauth1/consumers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								keystoneclient/v3/contrib/oauth1/consumers.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
# 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 import base
 | 
			
		||||
from keystoneclient.v3.contrib.oauth1 import utils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Consumer(base.Resource):
 | 
			
		||||
    """Represents an OAuth consumer.
 | 
			
		||||
 | 
			
		||||
    Attributes:
 | 
			
		||||
        * id: a uuid that identifies the consumer
 | 
			
		||||
        * description: a short description of the consumer
 | 
			
		||||
    """
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConsumerManager(base.CrudManager):
 | 
			
		||||
    """Manager class for manipulating identity consumers."""
 | 
			
		||||
    resource_class = Consumer
 | 
			
		||||
    collection_key = 'consumers'
 | 
			
		||||
    key = 'consumer'
 | 
			
		||||
    base_url = utils.OAUTH_PATH
 | 
			
		||||
 | 
			
		||||
    def create(self, description=None, **kwargs):
 | 
			
		||||
        return super(ConsumerManager, self).create(
 | 
			
		||||
            description=description,
 | 
			
		||||
            **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get(self, consumer):
 | 
			
		||||
        return super(ConsumerManager, self).get(
 | 
			
		||||
            consumer_id=base.getid(consumer))
 | 
			
		||||
 | 
			
		||||
    def update(self, consumer, description=None, **kwargs):
 | 
			
		||||
        return super(ConsumerManager, self).update(
 | 
			
		||||
            consumer_id=base.getid(consumer),
 | 
			
		||||
            description=description,
 | 
			
		||||
            **kwargs)
 | 
			
		||||
 | 
			
		||||
    def delete(self, consumer):
 | 
			
		||||
        return super(ConsumerManager, self).delete(
 | 
			
		||||
            consumer_id=base.getid(consumer))
 | 
			
		||||
							
								
								
									
										64
									
								
								keystoneclient/v3/contrib/oauth1/core.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								keystoneclient/v3/contrib/oauth1/core.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
# 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.v3.contrib.oauth1 import access_tokens
 | 
			
		||||
from keystoneclient.v3.contrib.oauth1 import consumers
 | 
			
		||||
from keystoneclient.v3.contrib.oauth1 import request_tokens
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_oauth_manager(self):
 | 
			
		||||
    # NOTE(stevemar): Attempt to import the oauthlib package at this point.
 | 
			
		||||
    try:
 | 
			
		||||
        import oauthlib  # noqa
 | 
			
		||||
    # NOTE(stevemar): Return an object instead of raising an exception here,
 | 
			
		||||
    # this will allow users to see an exception only when trying to access the
 | 
			
		||||
    # oauth portions of client. Otherwise an exception would be raised
 | 
			
		||||
    # when the client is created.
 | 
			
		||||
    except ImportError:
 | 
			
		||||
        return OAuthManagerOptionalImportProxy()
 | 
			
		||||
    else:
 | 
			
		||||
        return OAuthManager(self)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OAuthManager(object):
 | 
			
		||||
    def __init__(self, api):
 | 
			
		||||
        self.access_tokens = access_tokens.AccessTokenManager(api)
 | 
			
		||||
        self.consumers = consumers.ConsumerManager(api)
 | 
			
		||||
        self.request_tokens = request_tokens.RequestTokenManager(api)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OAuthManagerOptionalImportProxy(object):
 | 
			
		||||
    """Act as a proxy manager in case oauthlib is no installed.
 | 
			
		||||
 | 
			
		||||
    This class will only be created if oauthlib is not in the system,
 | 
			
		||||
    trying to access any of the attributes in name (access_tokens,
 | 
			
		||||
    consumers, request_tokens), will result in a NotImplementedError,
 | 
			
		||||
    and a message.
 | 
			
		||||
 | 
			
		||||
    >>> manager.access_tokens.blah
 | 
			
		||||
    NotImplementedError: To use 'access_tokens' oauthlib must be installed
 | 
			
		||||
 | 
			
		||||
    Otherwise, if trying to access an attribute other than the ones in name,
 | 
			
		||||
    the manager will state that the attribute does not exist.
 | 
			
		||||
 | 
			
		||||
    >>> manager.dne.blah
 | 
			
		||||
    AttributeError: 'OAuthManagerOptionalImportProxy' object has no
 | 
			
		||||
    attribute 'dne'
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __getattribute__(self, name):
 | 
			
		||||
        if name in ('access_tokens', 'comsumers', 'request_tokens'):
 | 
			
		||||
            raise NotImplementedError(
 | 
			
		||||
                'To use %r oauthlib must be installed' % name)
 | 
			
		||||
        return super(OAuthManagerOptionalImportProxy,
 | 
			
		||||
                     self).__getattribute__(name)
 | 
			
		||||
							
								
								
									
										70
									
								
								keystoneclient/v3/contrib/oauth1/request_tokens.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								keystoneclient/v3/contrib/oauth1/request_tokens.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
			
		||||
# 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 __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
from six.moves.urllib import parse as urlparse
 | 
			
		||||
 | 
			
		||||
from keystoneclient import base
 | 
			
		||||
from keystoneclient.v3.contrib.oauth1 import utils
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from oauthlib import oauth1
 | 
			
		||||
except ImportError:
 | 
			
		||||
    oauth1 = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RequestToken(base.Resource):
 | 
			
		||||
    def authorize(self, roles):
 | 
			
		||||
        try:
 | 
			
		||||
            retval = self.manager.authorize(self.id, roles)
 | 
			
		||||
            self = retval
 | 
			
		||||
        except Exception:
 | 
			
		||||
            retval = None
 | 
			
		||||
 | 
			
		||||
        return retval
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RequestTokenManager(base.CrudManager):
 | 
			
		||||
    """Manager class for manipulating identity OAuth request tokens."""
 | 
			
		||||
    resource_class = RequestToken
 | 
			
		||||
 | 
			
		||||
    def authorize(self, request_token, roles):
 | 
			
		||||
        """Authorize a request token with specific roles.
 | 
			
		||||
 | 
			
		||||
        Utilize Identity API operation:
 | 
			
		||||
        PUT /OS-OAUTH1/authorize/$request_token_id
 | 
			
		||||
 | 
			
		||||
        :param request_token: a request token that will be authorized, and
 | 
			
		||||
            can be exchanged for an access token.
 | 
			
		||||
        :param roles: a list of roles, that will be delegated to the user.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        request_id = urlparse.quote(base.getid(request_token))
 | 
			
		||||
        endpoint = utils.OAUTH_PATH + '/authorize/%s' % (request_id)
 | 
			
		||||
        body = {'roles': [{'id': base.getid(r_id)} for r_id in roles]}
 | 
			
		||||
        return self._put(endpoint, body, "token")
 | 
			
		||||
 | 
			
		||||
    def create(self, consumer_key, consumer_secret, project):
 | 
			
		||||
        endpoint = utils.OAUTH_PATH + '/request_token'
 | 
			
		||||
        headers = {'requested_project_id': base.getid(project)}
 | 
			
		||||
        oauth_client = oauth1.Client(consumer_key,
 | 
			
		||||
                                     client_secret=consumer_secret,
 | 
			
		||||
                                     signature_method=oauth1.SIGNATURE_HMAC,
 | 
			
		||||
                                     callback_uri="oob")
 | 
			
		||||
        url = self.client.auth_url.rstrip("/") + endpoint
 | 
			
		||||
        url, headers, body = oauth_client.sign(url, http_method='POST',
 | 
			
		||||
                                               headers=headers)
 | 
			
		||||
        resp, body = self.client.post(endpoint, headers=headers)
 | 
			
		||||
        token = utils.get_oauth_token_from_body(body)
 | 
			
		||||
        return self.resource_class(self, token)
 | 
			
		||||
							
								
								
									
										35
									
								
								keystoneclient/v3/contrib/oauth1/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								keystoneclient/v3/contrib/oauth1/utils.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
# 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 six.moves.urllib import parse as urlparse
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
OAUTH_PATH = '/OS-OAUTH1'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_oauth_token_from_body(body):
 | 
			
		||||
    """Parse the URL response body to retrieve the oauth token key and secret
 | 
			
		||||
 | 
			
		||||
    The response body will look like:
 | 
			
		||||
    'oauth_token=12345&oauth_token_secret=67890' with
 | 
			
		||||
    'oauth_expires_at=2013-03-30T05:27:19.463201' possibly there, too.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    credentials = urlparse.parse_qs(body)
 | 
			
		||||
    key = credentials['oauth_token'][0]
 | 
			
		||||
    secret = credentials['oauth_token_secret'][0]
 | 
			
		||||
    token = {'key': key, 'id': key, 'secret': secret}
 | 
			
		||||
    expires_at = credentials.get('oauth_expires_at')
 | 
			
		||||
    if expires_at:
 | 
			
		||||
        token['expires'] = expires_at[0]
 | 
			
		||||
    return token
 | 
			
		||||
@@ -6,6 +6,7 @@ httpretty>=0.8.0
 | 
			
		||||
keyring>=2.1
 | 
			
		||||
mock>=1.0
 | 
			
		||||
mox3>=0.7.0
 | 
			
		||||
oauthlib>=0.6
 | 
			
		||||
pycrypto>=2.6
 | 
			
		||||
sphinx>=1.1.2,<1.2
 | 
			
		||||
stevedore>=0.14
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user