diff --git a/doc/source/user/proxies/identity_v3.rst b/doc/source/user/proxies/identity_v3.rst index ccf541b8f..64a87f5b6 100644 --- a/doc/source/user/proxies/identity_v3.rst +++ b/doc/source/user/proxies/identity_v3.rst @@ -178,3 +178,12 @@ Access Rule Operations .. autoclass:: openstack.identity.v3._proxy.Proxy :noindex: :members: access_rules, access_rules, delete_access_rule + +Service Provider Operations +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: openstack.identity.v3._proxy.Proxy + :noindex: + :members: create_service_provider, delete_service_provider, + find_service_provider, get_service_provider, service_providers, + update_service_provider diff --git a/openstack/identity/v3/_proxy.py b/openstack/identity/v3/_proxy.py index 35b59faed..9f8de2715 100644 --- a/openstack/identity/v3/_proxy.py +++ b/openstack/identity/v3/_proxy.py @@ -49,6 +49,7 @@ from openstack.identity.v3 import ( role_system_user_assignment as _role_system_user_assignment, ) from openstack.identity.v3 import service as _service +from openstack.identity.v3 import service_provider as _service_provider from openstack.identity.v3 import system as _system from openstack.identity.v3 import trust as _trust from openstack.identity.v3 import user as _user @@ -316,7 +317,7 @@ class Proxy(proxy.Proxy): :param domain_id: The value can be the ID of a domain or a :class:`~openstack.identity.v3.domain.Domain` instance. - :attrs kwargs: The attributes to update on the config for a domain + :param attrs: The attributes to update on the config for a domain represented by ``domain_id``. :returns: The updated config for a domain @@ -2178,3 +2179,102 @@ class Proxy(proxy.Proxy): user_id=user.id, ignore_missing=ignore_missing, ) + + # ========== Service providers ========== + + def create_service_provider(self, **attrs): + """Create a new service provider from attributes + + :param dict attrs: Keyword arguments which will be used to create a + :class:`~openstack.identity.v3.service_provider.ServiceProvider`, + comprised of the properties on the ServiceProvider class. + + :returns: The results of service provider creation + :rtype: + :class:`~openstack.identity.v3.service_provider.ServiceProvider` + """ + return self._create(_service_provider.ServiceProvider, **attrs) + + def delete_service_provider(self, service_provider, ignore_missing=True): + """Delete a service provider + + :param service_provider: The ID of a service provider or a + :class:`~openstack.identity.v3.service_provider.ServiceProvider` + instance. + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be + raised when the service provider does not exist. + When set to ``True``, no exception will be set when + attempting to delete a nonexistent service provider. + + :returns: ``None`` + """ + self._delete( + _service_provider.ServiceProvider, + service_provider, + ignore_missing=ignore_missing, + ) + + def find_service_provider(self, name_or_id, ignore_missing=True): + """Find a single service provider + + :param name_or_id: The name or ID of a service provider + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised when + the resource does not exist. When set to ``True``, None will be + returned when attempting to find a nonexistent resource. + + :returns: The details of an service provider or None. + :rtype: + :class:`~openstack.identity.v3.service_provider.ServiceProvider` + """ + return self._find( + _service_provider.ServiceProvider, + name_or_id, + ignore_missing=ignore_missing, + ) + + def get_service_provider(self, service_provider): + """Get a single service provider + + :param service_provider: The value can be the ID of a service provider + or a + :class:`~openstack.identity.v3.server_provider.ServiceProvider` + instance. + + :returns: The details of an service provider. + :rtype: + :class:`~openstack.identity.v3.service_provider.ServiceProvider` + :raises: :class:`~openstack.exceptions.ResourceNotFound` + when no resource can be found. + """ + return self._get(_service_provider.ServiceProvider, service_provider) + + def service_providers(self, **query): + """Retrieve a generator of service providers + + :param kwargs query: Optional query parameters to be sent to limit + the resources being returned. + + :returns: A generator of service provider instances. + :rtype: + :class:`~openstack.identity.v3.service_provider.ServiceProvider` + """ + return self._list(_service_provider.ServiceProvider, **query) + + def update_service_provider(self, service_provider, **attrs): + """Update a service provider + + :param service_provider: Either the ID of an service provider or a + :class:`~openstack.identity.v3.service_provider.ServiceProvider` + instance. + :param attrs: The attributes to update on the service provider + represented by ``service_provider``. + + :returns: The updated service provider. + :rtype: + :class:`~openstack.identity.v3.service_provider.ServiceProvider` + """ + return self._update( + _service_provider.ServiceProvider, service_provider, **attrs + ) diff --git a/openstack/identity/v3/service_provider.py b/openstack/identity/v3/service_provider.py new file mode 100644 index 000000000..7185a0807 --- /dev/null +++ b/openstack/identity/v3/service_provider.py @@ -0,0 +1,48 @@ +# 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 openstack import resource + + +class ServiceProvider(resource.Resource): + resource_key = 'service_provider' + resources_key = 'service_providers' + base_path = '/OS-FEDERATION/service_providers' + + # capabilities + allow_create = True + allow_fetch = True + allow_commit = True + allow_delete = True + allow_list = True + create_method = 'PUT' + create_exclude_id_from_body = True + commit_method = 'PATCH' + + _query_mapping = resource.QueryParameters( + 'id', + is_enabled='enabled', + ) + + # Properties + #: The URL to authenticate against. + auth_url = resource.Body('auth_url') + #: A description of this service provider. + description = resource.Body('description') + #: If the service provider is currently enabled. + is_enabled = resource.Body('enabled', type=bool) + #: The identifier of the service provider. + name = resource.Body('id') + #: The prefix of the RelayState SAML attribute. + relay_state_prefix = resource.Body('relay_state_prefix') + #: The service provider's URL. + sp_url = resource.Body('sp_url') diff --git a/openstack/tests/unit/identity/v3/test_proxy.py b/openstack/tests/unit/identity/v3/test_proxy.py index dd12919d9..3fd3895ab 100644 --- a/openstack/tests/unit/identity/v3/test_proxy.py +++ b/openstack/tests/unit/identity/v3/test_proxy.py @@ -30,6 +30,7 @@ from openstack.identity.v3 import role_project_user_assignment from openstack.identity.v3 import role_system_group_assignment from openstack.identity.v3 import role_system_user_assignment from openstack.identity.v3 import service +from openstack.identity.v3 import service_provider from openstack.identity.v3 import trust from openstack.identity.v3 import user from openstack.tests.unit import test_proxy_base @@ -753,3 +754,48 @@ class TestAccessRule(TestIdentityProxyBase): method_kwargs={'user': USER_ID}, expected_kwargs={'user_id': USER_ID}, ) + + +class TestServiceProvider(TestIdentityProxyBase): + def test_service_provider_create(self): + self.verify_create( + self.proxy.create_service_provider, + service_provider.ServiceProvider, + ) + + def test_service_provider_delete(self): + self.verify_delete( + self.proxy.delete_service_provider, + service_provider.ServiceProvider, + False, + ) + + def test_service_provider_delete_ignore(self): + self.verify_delete( + self.proxy.delete_service_provider, + service_provider.ServiceProvider, + True, + ) + + def test_service_provider_find(self): + self.verify_find( + self.proxy.find_service_provider, service_provider.ServiceProvider + ) + + def test_service_provider_get(self): + self.verify_get( + self.proxy.get_service_provider, + service_provider.ServiceProvider, + ) + + def test_service_providers(self): + self.verify_list( + self.proxy.service_providers, + service_provider.ServiceProvider, + ) + + def test_service_provider_update(self): + self.verify_update( + self.proxy.update_service_provider, + service_provider.ServiceProvider, + ) diff --git a/openstack/tests/unit/identity/v3/test_service_provider.py b/openstack/tests/unit/identity/v3/test_service_provider.py new file mode 100644 index 000000000..068b6afd4 --- /dev/null +++ b/openstack/tests/unit/identity/v3/test_service_provider.py @@ -0,0 +1,62 @@ +# 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 openstack.identity.v3 import service_provider +from openstack.tests.unit import base + + +IDENTIFIER = 'IDENTIFIER' +EXAMPLE = { + 'id': IDENTIFIER, + 'description': 'An example description', + 'is_enabled': True, + 'auth_url': ( + "https://auth.example.com/v3/OS-FEDERATION/" + "identity_providers/idp/protocols/saml2/auth" + ), + 'sp_url': 'https://auth.example.com/Shibboleth.sso/SAML2/ECP', +} + + +class TestServiceProvider(base.TestCase): + def test_basic(self): + sot = service_provider.ServiceProvider() + self.assertEqual('service_provider', sot.resource_key) + self.assertEqual('service_providers', sot.resources_key) + self.assertEqual('/OS-FEDERATION/service_providers', sot.base_path) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertTrue(sot.allow_commit) + self.assertTrue(sot.allow_delete) + self.assertTrue(sot.allow_list) + self.assertTrue(sot.create_exclude_id_from_body) + self.assertEqual('PATCH', sot.commit_method) + self.assertEqual('PUT', sot.create_method) + + self.assertDictEqual( + { + 'id': 'id', + 'limit': 'limit', + 'marker': 'marker', + 'is_enabled': 'enabled', + }, + sot._query_mapping._mapping, + ) + + def test_make_it(self): + sot = service_provider.ServiceProvider(**EXAMPLE) + self.assertEqual(EXAMPLE['id'], sot.id) + self.assertEqual(EXAMPLE['id'], sot.name) + self.assertEqual(EXAMPLE['description'], sot.description) + self.assertEqual(EXAMPLE['is_enabled'], sot.is_enabled) + self.assertEqual(EXAMPLE['auth_url'], sot.auth_url) + self.assertEqual(EXAMPLE['sp_url'], sot.sp_url) diff --git a/releasenotes/notes/add-identity-service-provider-support-8c97cbb157883626.yaml b/releasenotes/notes/add-identity-service-provider-support-8c97cbb157883626.yaml new file mode 100644 index 000000000..1b50dfe32 --- /dev/null +++ b/releasenotes/notes/add-identity-service-provider-support-8c97cbb157883626.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add support for service providers to the identity service.