diff options
-rw-r--r-- | doc/source/using-api-v3.rst | 1 | ||||
-rw-r--r-- | keystoneclient/tests/functional/v3/client_fixtures.py | 17 | ||||
-rw-r--r-- | keystoneclient/tests/functional/v3/test_domain_configs.py | 101 | ||||
-rw-r--r-- | keystoneclient/tests/unit/v3/test_domain_configs.py | 96 | ||||
-rw-r--r-- | keystoneclient/v3/client.py | 6 | ||||
-rw-r--r-- | keystoneclient/v3/domain_configs.py | 130 | ||||
-rw-r--r-- | releasenotes/notes/bp-domain-config-9566e672a98f4e7f.yaml | 7 |
7 files changed, 358 insertions, 0 deletions
diff --git a/doc/source/using-api-v3.rst b/doc/source/using-api-v3.rst index 8e2093d..f0c82c5 100644 --- a/doc/source/using-api-v3.rst +++ b/doc/source/using-api-v3.rst @@ -8,6 +8,7 @@ Introduction The main concepts in the Identity v3 API are: * :py:mod:`~keystoneclient.v3.credentials` + * :py:mod:`~keystoneclient.v3.domain_configs` * :py:mod:`~keystoneclient.v3.domains` * :py:mod:`~keystoneclient.v3.endpoints` * :py:mod:`~keystoneclient.v3.groups` diff --git a/keystoneclient/tests/functional/v3/client_fixtures.py b/keystoneclient/tests/functional/v3/client_fixtures.py index 4b54b88..37da4a4 100644 --- a/keystoneclient/tests/functional/v3/client_fixtures.py +++ b/keystoneclient/tests/functional/v3/client_fixtures.py @@ -219,3 +219,20 @@ class EC2(Base): self.addCleanup(self.client.ec2.delete, self.user_id, self.entity.access) + + +class DomainConfig(Base): + + def __init__(self, client, domain_id): + super(DomainConfig, self).__init__(client, domain_id=domain_id) + self.domain_id = domain_id + + def setUp(self): + super(DomainConfig, self).setUp() + + self.ref = {'identity': {'driver': uuid.uuid4().hex}, + 'ldap': {'url': uuid.uuid4().hex}} + self.entity = self.client.domain_configs.create( + self.domain_id, self.ref) + self.addCleanup(self.client.domain_configs.delete, + self.domain_id) diff --git a/keystoneclient/tests/functional/v3/test_domain_configs.py b/keystoneclient/tests/functional/v3/test_domain_configs.py new file mode 100644 index 0000000..91112be --- /dev/null +++ b/keystoneclient/tests/functional/v3/test_domain_configs.py @@ -0,0 +1,101 @@ +# 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 + +from keystoneauth1.exceptions import http +from keystoneclient.tests.functional import base +from keystoneclient.tests.functional.v3 import client_fixtures as fixtures + + +class DomainConfigsTestCase(base.V3ClientTestCase): + + def check_domain_config(self, config, config_ref): + for attr in config_ref: + self.assertEqual( + getattr(config, attr), + config_ref[attr], + 'Expected different %s' % attr) + + def _new_ref(self): + return {'identity': {'driver': uuid.uuid4().hex}, + 'ldap': {'url': uuid.uuid4().hex}} + + def test_create_domain_config(self): + config_ref = self._new_ref() + config = self.client.domain_configs.create( + self.project_domain_id, config_ref) + self.addCleanup( + self.client.domain_configs.delete, self.project_domain_id) + self.check_domain_config(config, config_ref) + + def test_create_invalid_domain_config(self): + invalid_groups_ref = { + uuid.uuid4().hex: {uuid.uuid4().hex: uuid.uuid4().hex}, + uuid.uuid4().hex: {uuid.uuid4().hex: uuid.uuid4().hex}} + self.assertRaises(http.Forbidden, + self.client.domain_configs.create, + self.project_domain_id, + invalid_groups_ref) + + invalid_options_ref = { + 'identity': {uuid.uuid4().hex: uuid.uuid4().hex}, + 'ldap': {uuid.uuid4().hex: uuid.uuid4().hex}} + self.assertRaises(http.Forbidden, + self.client.domain_configs.create, + self.project_domain_id, + invalid_options_ref) + + def test_get_domain_config(self): + config = fixtures.DomainConfig(self.client, self.project_domain_id) + self.useFixture(config) + + config_ret = self.client.domain_configs.get(self.project_domain_id) + self.check_domain_config(config_ret, config.ref) + + def test_update_domain_config(self): + config = fixtures.DomainConfig(self.client, self.project_domain_id) + self.useFixture(config) + + update_config_ref = self._new_ref() + config_ret = self.client.domain_configs.update( + self.project_domain_id, update_config_ref) + self.check_domain_config(config_ret, update_config_ref) + + def test_update_invalid_domain_config(self): + config = fixtures.DomainConfig(self.client, self.project_domain_id) + self.useFixture(config) + + invalid_groups_ref = { + uuid.uuid4().hex: {uuid.uuid4().hex: uuid.uuid4().hex}, + uuid.uuid4().hex: {uuid.uuid4().hex: uuid.uuid4().hex}} + self.assertRaises(http.Forbidden, + self.client.domain_configs.update, + self.project_domain_id, + invalid_groups_ref) + + invalid_options_ref = { + 'identity': {uuid.uuid4().hex: uuid.uuid4().hex}, + 'ldap': {uuid.uuid4().hex: uuid.uuid4().hex}} + self.assertRaises(http.Forbidden, + self.client.domain_configs.update, + self.project_domain_id, + invalid_options_ref) + + def test_domain_config_delete(self): + config_ref = self._new_ref() + self.client.domain_configs.create(self.project_domain_id, config_ref) + + self.client.domain_configs.delete(self.project_domain_id) + self.assertRaises(http.NotFound, + self.client.domain_configs.get, + self.project_domain_id) diff --git a/keystoneclient/tests/unit/v3/test_domain_configs.py b/keystoneclient/tests/unit/v3/test_domain_configs.py new file mode 100644 index 0000000..2a7df09 --- /dev/null +++ b/keystoneclient/tests/unit/v3/test_domain_configs.py @@ -0,0 +1,96 @@ +# 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 + +from keystoneclient import exceptions +from keystoneclient.tests.unit.v3 import utils +from keystoneclient.v3 import domain_configs + + +class DomainConfigsTests(utils.ClientTestCase, utils.CrudTests): + """Test domain config database management.""" + + def setUp(self): + super(DomainConfigsTests, self).setUp() + self.key = 'config' + self.model = domain_configs.DomainConfig + self.manager = self.client.domain_configs + + def new_ref(self, **kwargs): + config_groups = {'identity': {uuid.uuid4().hex: uuid.uuid4().hex}, + 'ldap': {uuid.uuid4().hex: uuid.uuid4().hex}} + kwargs.setdefault('config', config_groups) + return kwargs + + def _assert_resource_attributes(self, resource, req_ref): + for attr in req_ref: + self.assertEqual( + getattr(resource, attr), + req_ref[attr], + 'Expected different %s' % attr) + + def test_create(self): + domain_id = uuid.uuid4().hex + config = self.new_ref() + + self.stub_url('PUT', + parts=['domains', domain_id, 'config'], + json=config, status_code=201) + res = self.manager.create(domain_id, config) + self._assert_resource_attributes(res, config['config']) + self.assertEntityRequestBodyIs(config) + + def test_update(self): + domain_id = uuid.uuid4().hex + config = self.new_ref() + + self.stub_url('PATCH', + parts=['domains', domain_id, 'config'], + json=config, status_code=200) + res = self.manager.update(domain_id, config) + self._assert_resource_attributes(res, config['config']) + self.assertEntityRequestBodyIs(config) + + def test_get(self): + domain_id = uuid.uuid4().hex + config = self.new_ref() + config = config['config'] + + self.stub_entity('GET', + parts=['domains', domain_id, 'config'], + entity=config) + res = self.manager.get(domain_id) + self._assert_resource_attributes(res, config) + + def test_delete(self): + domain_id = uuid.uuid4().hex + self.stub_url('DELETE', + parts=['domains', domain_id, 'config'], + status_code=204) + self.manager.delete(domain_id) + + def test_list(self): + # List not supported for domain config + self.assertRaises(exceptions.MethodNotImplemented, self.manager.list) + + def test_list_by_id(self): + # List not supported for domain config + self.assertRaises(exceptions.MethodNotImplemented, self.manager.list) + + def test_list_params(self): + # List not supported for domain config + self.assertRaises(exceptions.MethodNotImplemented, self.manager.list) + + def test_find(self): + # Find not supported for domain config + self.assertRaises(exceptions.MethodNotImplemented, self.manager.find) diff --git a/keystoneclient/v3/client.py b/keystoneclient/v3/client.py index 619de60..62c0c8d 100644 --- a/keystoneclient/v3/client.py +++ b/keystoneclient/v3/client.py @@ -30,6 +30,7 @@ from keystoneclient.v3.contrib import oauth1 from keystoneclient.v3.contrib import simple_cert from keystoneclient.v3.contrib import trusts from keystoneclient.v3 import credentials +from keystoneclient.v3 import domain_configs from keystoneclient.v3 import domains from keystoneclient.v3 import ec2 from keystoneclient.v3 import endpoints @@ -116,6 +117,10 @@ class Client(httpclient.HTTPClient): :py:class:`keystoneclient.v3.credentials.CredentialManager` + .. py:attribute:: domain_configs + + :py:class:`keystoneclient.v3.domain_configs.DomainConfigManager` + .. py:attribute:: ec2 :py:class:`keystoneclient.v3.ec2.EC2Manager` @@ -209,6 +214,7 @@ class Client(httpclient.HTTPClient): self.endpoint_policy = endpoint_policy.EndpointPolicyManager( self._adapter) self.endpoints = endpoints.EndpointManager(self._adapter) + self.domain_configs = domain_configs.DomainConfigManager(self._adapter) self.domains = domains.DomainManager(self._adapter) self.federation = federation.FederationManager(self._adapter) self.groups = groups.GroupManager(self._adapter) diff --git a/keystoneclient/v3/domain_configs.py b/keystoneclient/v3/domain_configs.py new file mode 100644 index 0000000..4c011bc --- /dev/null +++ b/keystoneclient/v3/domain_configs.py @@ -0,0 +1,130 @@ +# 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 exceptions +from keystoneclient.i18n import _ + + +class DomainConfig(base.Resource): + """An object representing a domain config association. + + This resource object does not necessarily contain fixed attributes, as new + attributes are added in the server, they are supported here directly. + The currently supported configs are `identity` and `ldap`. + + """ + + pass + + +class DomainConfigManager(base.Manager): + """Manager class for manipulating domain config associations.""" + + resource_class = DomainConfig + key = 'config' + + def build_url(self, domain): + return '/domains/%s/config' % base.getid(domain) + + def create(self, domain, config): + """Create a config for a domain. + + :param domain: the domain where the config is going to be applied. + :type domain: str or :py:class:`keystoneclient.v3.domains.Domain` + + :param dict config: a dictionary of domain configurations. + + Example of the ``config`` parameter:: + + { + "identity": { + "driver": "ldap" + }, + "ldap": { + "url": "ldap://myldap.com:389/", + "user_tree_dn": "ou=Users,dc=my_new_root,dc=org" + } + } + + :returns: the created domain config returned from server. + :rtype: :class:`keystoneclient.v3.domain_configs.DomainConfig` + + """ + base_url = self.build_url(domain) + body = {self.key: config} + return super(DomainConfigManager, self)._put( + base_url, body=body, response_key=self.key) + + def get(self, domain): + """Get a config for a domain. + + :param domain: the domain for which the config is defined. + :type domain: str or :py:class:`keystoneclient.v3.domains.Domain` + + :returns: the domain config returned from server. + :rtype: :class:`keystoneclient.v3.domain_configs.DomainConfig` + + """ + base_url = self.build_url(domain) + return super(DomainConfigManager, self)._get(base_url, self.key) + + def update(self, domain, config): + """Update a config for a domain. + + :param domain: the domain where the config is going to be updated. + :type domain: str or :py:class:`keystoneclient.v3.domains.Domain` + + :param dict config: a dictionary of domain configurations. + + Example of the ``config`` parameter:: + + { + "identity": { + "driver": "ldap" + }, + "ldap": { + "url": "ldap://myldap.com:389/", + "user_tree_dn": "ou=Users,dc=my_new_root,dc=org" + } + } + + :returns: the updated domain config returned from server. + :rtype: :class:`keystoneclient.v3.domain_configs.DomainConfig` + + """ + base_url = self.build_url(domain) + body = {self.key: config} + return super(DomainConfigManager, self)._patch( + base_url, body=body, response_key=self.key) + + def delete(self, domain): + """Delete a config for a domain. + + :param domain: the domain which the config will be deleted on + the server. + :type domain: str or :class:`keystoneclient.v3.domains.Domain` + + :returns: Response object with 204 status. + :rtype: :class:`requests.models.Response` + + """ + base_url = self.build_url(domain) + return super(DomainConfigManager, self)._delete(url=base_url) + + def find(self, **kwargs): + raise exceptions.MethodNotImplemented( + _('Find not supported for domain configs')) + + def list(self, **kwargs): + raise exceptions.MethodNotImplemented( + _('List not supported for domain configs')) diff --git a/releasenotes/notes/bp-domain-config-9566e672a98f4e7f.yaml b/releasenotes/notes/bp-domain-config-9566e672a98f4e7f.yaml new file mode 100644 index 0000000..e6ae2b0 --- /dev/null +++ b/releasenotes/notes/bp-domain-config-9566e672a98f4e7f.yaml @@ -0,0 +1,7 @@ +--- +features: + - Added support for ``domain configs``. A user can now + upload domain specific configurations to keytone + using the client. See ``client.domain_configs.create``, + ``client.domain_configs.delete``, ``client.domain_configs.get`` + and ``client.domain_configs.update``. |