From 9dbdb3a651fb38bcda34adee3aa6b95fa2a3959e Mon Sep 17 00:00:00 2001 From: Marek Denis Date: Fri, 28 Mar 2014 18:14:07 +0100 Subject: [PATCH] Add CRUD operations for Federated Protocols. Add relevant methods for adding, updating, listing, getting, and deleting Federated Protocol objects. Change-Id: Ibd8488158619d1689fbdfe21df114433c41c91f8 Implements: blueprint federation-crud-operations --- keystoneclient/tests/v3/test_federation.py | 198 ++++++++++++++++++ keystoneclient/v3/contrib/federation/core.py | 2 + .../v3/contrib/federation/protocols.py | 145 +++++++++++++ 3 files changed, 345 insertions(+) create mode 100644 keystoneclient/v3/contrib/federation/protocols.py diff --git a/keystoneclient/tests/v3/test_federation.py b/keystoneclient/tests/v3/test_federation.py index f16cf2eb2..36e3c4996 100644 --- a/keystoneclient/tests/v3/test_federation.py +++ b/keystoneclient/tests/v3/test_federation.py @@ -10,13 +10,16 @@ # License for the specific language governing permissions and limitations # under the License. +import copy import uuid import httpretty +from keystoneclient import exceptions from keystoneclient.tests.v3 import utils from keystoneclient.v3.contrib.federation import identity_providers from keystoneclient.v3.contrib.federation import mappings +from keystoneclient.v3.contrib.federation import protocols class IdentityProviderTests(utils.TestCase, utils.CrudTests): @@ -125,3 +128,198 @@ class MappingTests(utils.TestCase, utils.CrudTests): req_ref[attr], 'Expected different %s' % attr) self.assertEntityRequestBodyIs(manager_ref) + + +class ProtocolTests(utils.TestCase, utils.CrudTests): + def setUp(self): + super(ProtocolTests, self).setUp() + self.key = 'protocol' + self.collection_key = 'protocols' + self.model = protocols.Protocol + self.manager = self.client.federation.protocols + self.path_prefix = 'OS-FEDERATION/identity_providers' + + def _transform_to_response(self, ref): + """Rebuild dictionary so it can be used as a + reference response body. + + """ + response = copy.deepcopy(ref) + response['id'] = response.pop('protocol_id') + del response['identity_provider'] + return response + + def new_ref(self, **kwargs): + kwargs.setdefault('mapping', uuid.uuid4().hex) + kwargs.setdefault('identity_provider', uuid.uuid4().hex) + kwargs.setdefault('protocol_id', uuid.uuid4().hex) + return kwargs + + def build_parts(self, identity_provider, protocol_id=None): + """Build array used to construct httpretty URL/ + + Construct and return array with URL parts later used + by methods like utils.TestCase.stub_entity(). + Example of URL: + ``OS-FEDERATION/identity_providers/{idp_id}/ + protocols/{protocol_id}`` + + """ + parts = ['OS-FEDERATION', 'identity_providers', + identity_provider, 'protocols'] + if protocol_id: + parts.append(protocol_id) + return parts + + def test_build_url_provide_base_url(self): + base_url = uuid.uuid4().hex + parameters = {'base_url': base_url} + url = self.manager.build_url(dict_args_in_out=parameters) + self.assertEqual('/'.join([base_url, self.collection_key]), url) + + def test_build_url_w_idp_id(self): + """Test whether kwargs ``base_url`` discards object's base_url + + This test shows, that when ``base_url`` is specified in the + dict_args_in_out dictionary, values like ``identity_provider_id`` + are not taken into consideration while building the url. + + """ + base_url, identity_provider_id = uuid.uuid4().hex, uuid.uuid4().hex + parameters = { + 'base_url': base_url, + 'identity_provider_id': identity_provider_id + } + url = self.manager.build_url(dict_args_in_out=parameters) + self.assertEqual('/'.join([base_url, self.collection_key]), url) + + def test_build_url_default_base_url(self): + identity_provider_id = uuid.uuid4().hex + parameters = { + 'identity_provider_id': identity_provider_id + } + + url = self.manager.build_url(dict_args_in_out=parameters) + self.assertEqual( + '/'.join([self.manager.base_url, identity_provider_id, + self.manager.collection_key]), url) + + @httpretty.activate + def test_create(self): + """Test creating federation protocol tied to an Identity Provider. + + URL to be tested: PUT /OS-FEDERATION/identity_providers/ + $identity_provider/protocols/$protocol + + """ + request_args = self.new_ref() + expected = self._transform_to_response(request_args) + parts = self.build_parts(request_args['identity_provider'], + request_args['protocol_id']) + self.stub_entity(httpretty.PUT, entity=expected, + parts=parts, status=201) + returned = self.manager.create(**request_args) + self.assertEqual(expected, returned.to_dict()) + request_body = {'mapping_id': request_args['mapping']} + self.assertEntityRequestBodyIs(request_body) + + @httpretty.activate + def test_get(self): + """Fetch federation protocol object. + + URL to be tested: GET /OS-FEDERATION/identity_providers/ + $identity_provider/protocols/$protocol + + """ + request_args = self.new_ref() + expected = self._transform_to_response(request_args) + + parts = self.build_parts(request_args['identity_provider'], + request_args['protocol_id']) + self.stub_entity(httpretty.GET, entity=expected, + parts=parts, status=201) + + returned = self.manager.get(request_args['identity_provider'], + request_args['protocol_id']) + self.assertIsInstance(returned, self.model) + self.assertEqual(expected, returned.to_dict()) + + @httpretty.activate + def test_delete(self): + """Delete federation protocol object. + + URL to be tested: DELETE /OS-FEDERATION/identity_providers/ + $identity_provider/protocols/$protocol + + """ + request_args = self.new_ref() + parts = self.build_parts(request_args['identity_provider'], + request_args['protocol_id']) + + self.stub_entity(httpretty.DELETE, parts=parts, status=204) + + self.manager.delete(request_args['identity_provider'], + request_args['protocol_id']) + + @httpretty.activate + def test_list(self): + """Test listing all federation protocols tied to the Identity Provider. + + URL to be tested: GET /OS-FEDERATION/identity_providers/ + $identity_provider/protocols + + """ + def _ref_protocols(): + return { + 'id': uuid.uuid4().hex, + 'mapping_id': uuid.uuid4().hex + } + + request_args = self.new_ref() + expected = [_ref_protocols() for _ in range(3)] + parts = self.build_parts(request_args['identity_provider']) + self.stub_entity(httpretty.GET, parts=parts, + entity=expected, status=200) + + returned = self.manager.list(request_args['identity_provider']) + for obj, ref_obj in zip(returned, expected): + self.assertEqual(obj.to_dict(), ref_obj) + + @httpretty.activate + def test_list_params(self): + request_args = self.new_ref() + filter_kwargs = {uuid.uuid4().hex: uuid.uuid4().hex} + parts = self.build_parts(request_args['identity_provider']) + + # Return HTTP 401 as we don't accept such requests. + self.stub_entity(httpretty.GET, parts=parts, status=401) + self.assertRaises(exceptions.Unauthorized, + self.manager.list, + request_args['identity_provider'], + **filter_kwargs) + self.assertQueryStringContains(**filter_kwargs) + + @httpretty.activate + def test_update(self): + """Test updating federation protocol + + URL to be tested: PATCH /OS-FEDERATION/identity_providers/ + $identity_provider/protocols/$protocol + + """ + request_args = self.new_ref() + expected = self._transform_to_response(request_args) + + parts = self.build_parts(request_args['identity_provider'], + request_args['protocol_id']) + + self.stub_entity(httpretty.PATCH, parts=parts, + entity=expected, status=200) + + returned = self.manager.update(request_args['identity_provider'], + request_args['protocol_id'], + mapping=request_args['mapping']) + self.assertIsInstance(returned, self.model) + self.assertEqual(expected, returned.to_dict()) + request_body = {'mapping_id': request_args['mapping']} + self.assertEntityRequestBodyIs(request_body) diff --git a/keystoneclient/v3/contrib/federation/core.py b/keystoneclient/v3/contrib/federation/core.py index 3cb1061f9..d0a60fb8a 100644 --- a/keystoneclient/v3/contrib/federation/core.py +++ b/keystoneclient/v3/contrib/federation/core.py @@ -12,6 +12,7 @@ from keystoneclient.v3.contrib.federation import identity_providers from keystoneclient.v3.contrib.federation import mappings +from keystoneclient.v3.contrib.federation import protocols class FederationManager(object): @@ -19,3 +20,4 @@ class FederationManager(object): self.identity_providers = identity_providers.IdentityProviderManager( api) self.mappings = mappings.MappingManager(api) + self.protocols = protocols.ProtocolManager(api) diff --git a/keystoneclient/v3/contrib/federation/protocols.py b/keystoneclient/v3/contrib/federation/protocols.py new file mode 100644 index 000000000..97d88297b --- /dev/null +++ b/keystoneclient/v3/contrib/federation/protocols.py @@ -0,0 +1,145 @@ +# 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 import utils + + +class Protocol(base.Resource): + """An object representing federation protocol container. + + Attributes: + * id: user-defined unique per Identity Provider string identifying + federation protocol. + + """ + pass + + +class ProtocolManager(base.CrudManager): + """Manager class for manipulating federation protocols.""" + + resource_class = Protocol + collection_key = 'protocols' + key = 'protocol' + base_url = 'OS-FEDERATION/identity_providers' + + def build_url(self, dict_args_in_out=None): + """Build URL for federation protocols.""" + + if dict_args_in_out is None: + dict_args_in_out = {} + + identity_provider_id = dict_args_in_out.pop('identity_provider_id', + None) + if identity_provider_id: + base_url = '/'.join([self.base_url, identity_provider_id]) + else: + base_url = self.base_url + + dict_args_in_out.setdefault('base_url', base_url) + return super(ProtocolManager, self).build_url(dict_args_in_out) + + def _build_url_and_put(self, request_body=None, **kwargs): + url = self.build_url(dict_args_in_out=kwargs) + body = {self.key: request_body} + return self._update(url, body=body, + response_key=self.key, + method='PUT') + + @utils.positional.method(3) + def create(self, protocol_id, identity_provider, mapping, **kwargs): + """Create federation protocol object and tie to the Identity Provider. + + Utilize Identity API operation: + PUT /OS-FEDERATION/identity_providers/ + $identity_provider/protocols/$protocol + + :param protocol_id: a string type parameter identifying a federation + protocol + :param identity_provider: a string type parameter identifying an + Identity Provider + :param mapping: a base.Resource object with federation mapping id + + """ + return self._build_url_and_put( + request_body={'mapping_id': base.getid(mapping)}, + identity_provider_id=base.getid(identity_provider), + protocol_id=protocol_id, **kwargs) + + def get(self, identity_provider, protocol, **kwargs): + """Fetch federation protocol object tied to the Identity Provider. + + Utilize Identity API operation: + GET /OS-FEDERATION/identity_providers/ + $identity_provider/protocols/$protocol + + :param identity_provider: a base.Resource type object with Identity + Provider id stored inside + :param protocol: a base.Resource type object with federation protocol + id stored inside + + """ + return super(ProtocolManager, self).get( + identity_provider_id=base.getid(identity_provider), + protocol_id=base.getid(protocol), **kwargs) + + def list(self, identity_provider, **kwargs): + """List all federation protocol objects tied to the Identity Provider. + + Utilize Identity API operation: + GET /OS-FEDERATION/identity_providers/ + $identity_provider/protocols + + :param identity_provider: a base.Resource type object with Identity + Provider id stored inside + + """ + return super(ProtocolManager, self).list( + identity_provider_id=base.getid(identity_provider), **kwargs) + + def update(self, identity_provider, protocol, mapping, **kwargs): + """Update Protocol object tied to the Identity Provider. + + Utilize Identity API operation: + PATCH /OS-FEDERATION/identity_providers/ + $identity_provider/protocols/$protocol + + :param identity_provider: a base.Resource type object with Identity + Provider id stored inside + :param protocol: a base.Resource type object with federation protocol + id stored inside + :param mapping: a base.Resource object with federation mapping id + + + """ + return super(ProtocolManager, self).update( + identity_provider_id=base.getid(identity_provider), + protocol_id=base.getid(protocol), mapping_id=base.getid(mapping), + **kwargs) + + def delete(self, identity_provider, protocol): + """Delete Protocol object tied to the Identity Provider. + + Utilize Identity API operation: + DELETE /OS-FEDERATION/identity_providers/ + $identity_provider/protocols/$protocol + + :param identity_provider: a base.Resource type object with + Identity Provider id stored inside + :param protocol: a base.Resource type object with federation + protocol id stored inside + + """ + return super(ProtocolManager, self).delete( + identity_provider_id=base.getid(identity_provider), + protocol_id=base.getid(protocol))