diff options
-rw-r--r-- | .zuul.yaml | 4 | ||||
-rw-r--r-- | doc/requirements.txt | 3 | ||||
-rw-r--r-- | examples/pki/gen_cmsz.py | 1 | ||||
-rw-r--r-- | keystoneclient/auth/__init__.py | 1 | ||||
-rw-r--r-- | keystoneclient/auth/identity/v3/__init__.py | 2 | ||||
-rw-r--r-- | keystoneclient/contrib/ec2/utils.py | 2 | ||||
-rw-r--r-- | keystoneclient/exceptions.py | 2 | ||||
-rw-r--r-- | keystoneclient/fixture/__init__.py | 2 | ||||
-rw-r--r-- | keystoneclient/tests/unit/v3/test_access_rules.py | 41 | ||||
-rw-r--r-- | keystoneclient/tests/unit/v3/test_application_credentials.py | 21 | ||||
-rw-r--r-- | keystoneclient/v3/access_rules.py | 118 | ||||
-rw-r--r-- | keystoneclient/v3/application_credentials.py | 6 | ||||
-rw-r--r-- | keystoneclient/v3/client.py | 4 | ||||
-rw-r--r-- | releasenotes/notes/bp-whitelist-extension-for-app-creds-d03526e52e3edcce.yaml | 5 | ||||
-rw-r--r-- | releasenotes/notes/drop-py-2-7-5ac18e82de83fcfa.yaml | 6 | ||||
-rw-r--r-- | setup.cfg | 2 | ||||
-rw-r--r-- | test-requirements.txt | 2 | ||||
-rw-r--r-- | tox.ini | 21 |
18 files changed, 219 insertions, 24 deletions
@@ -19,11 +19,9 @@ templates: - openstack-cover-jobs - openstack-lower-constraints-jobs - - openstack-python-jobs - - openstack-python3-train-jobs + - openstack-python3-ussuri-jobs - publish-openstack-docs-pti - check-requirements - - lib-forward-testing - lib-forward-testing-python3 - release-notes-jobs-python3 check: diff --git a/doc/requirements.txt b/doc/requirements.txt index 67c99ed..c4f9c7d 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -4,8 +4,7 @@ # These are needed for docs generation openstackdocstheme>=1.20.0 # Apache-2.0 -sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD -sphinx!=1.6.6,!=1.6.7,>=1.6.2;python_version>='3.4' # BSD +sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD reno>=2.5.0 # Apache-2.0 lxml!=3.7.0,>=3.4.1 # BSD fixtures>=3.0.0 # Apache-2.0/BSD diff --git a/examples/pki/gen_cmsz.py b/examples/pki/gen_cmsz.py index bd0d886..35a6a8f 100644 --- a/examples/pki/gen_cmsz.py +++ b/examples/pki/gen_cmsz.py @@ -84,6 +84,7 @@ def generate_der_form(name): SIGNING_KEY_FILE_NAME, cms.PKIZ_CMS_FORM) f.write(derform) + for name in EXAMPLE_TOKENS: json_file = make_filename('cms', name + '.json') pkiz_file = make_filename('cms', name + '.pkiz') diff --git a/keystoneclient/auth/__init__.py b/keystoneclient/auth/__init__.py index eeae768..c9acef8 100644 --- a/keystoneclient/auth/__init__.py +++ b/keystoneclient/auth/__init__.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +# flake8: noqa: F405 from keystoneclient.auth.base import * # noqa from keystoneclient.auth.cli import * # noqa diff --git a/keystoneclient/auth/identity/v3/__init__.py b/keystoneclient/auth/identity/v3/__init__.py index f25bf5e..abbaa65 100644 --- a/keystoneclient/auth/identity/v3/__init__.py +++ b/keystoneclient/auth/identity/v3/__init__.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +# flake8: noqa: F405 + from keystoneclient.auth.identity.v3.base import * # noqa from keystoneclient.auth.identity.v3.federated import * # noqa from keystoneclient.auth.identity.v3.password import * # noqa diff --git a/keystoneclient/contrib/ec2/utils.py b/keystoneclient/contrib/ec2/utils.py index 1ef5df4..4e14e78 100644 --- a/keystoneclient/contrib/ec2/utils.py +++ b/keystoneclient/contrib/ec2/utils.py @@ -225,7 +225,7 @@ class Ec2Signer(object): # port if we detect an old boto version. FIXME: remove when all # distros package boto >= 2.9.3, this is a transitional workaround user_agent = headers_lower.get('user-agent', '') - strip_port = re.match('Boto/2\.[0-9]\.[0-2]', user_agent) + strip_port = re.match(r'Boto/2\.[0-9]\.[0-2]', user_agent) header_list = [] sh_str = auth_param('SignedHeaders') diff --git a/keystoneclient/exceptions.py b/keystoneclient/exceptions.py index 5b06be9..034e5c9 100644 --- a/keystoneclient/exceptions.py +++ b/keystoneclient/exceptions.py @@ -376,6 +376,7 @@ class CMSError(Exception): msg = _('Unable to sign or verify data.') super(CMSError, self).__init__(msg) + EmptyCatalog = _exc.EmptyCatalog """The service catalog is empty. @@ -398,6 +399,7 @@ An alias of :py:exc:`keystoneauth1.exceptions.discovery.VersionNotAvailable` class MethodNotImplemented(ClientException): """Method not implemented by the keystoneclient API.""" + MissingAuthPlugin = _exc.MissingAuthPlugin """An authenticated request is required but no plugin available. diff --git a/keystoneclient/fixture/__init__.py b/keystoneclient/fixture/__init__.py index 768f969..92034a0 100644 --- a/keystoneclient/fixture/__init__.py +++ b/keystoneclient/fixture/__init__.py @@ -28,6 +28,8 @@ testing. """ +# flake8: noqa: F405 + import warnings from keystoneclient.fixture.discovery import * # noqa diff --git a/keystoneclient/tests/unit/v3/test_access_rules.py b/keystoneclient/tests/unit/v3/test_access_rules.py new file mode 100644 index 0000000..d3e22f8 --- /dev/null +++ b/keystoneclient/tests/unit/v3/test_access_rules.py @@ -0,0 +1,41 @@ +# 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 access_rules + + +class AccessRuleTests(utils.ClientTestCase, utils.CrudTests): + def setUp(self): + super(AccessRuleTests, self).setUp() + self.key = 'access_rule' + self.collection_key = 'access_rules' + self.model = access_rules.AccessRule + self.manager = self.client.access_rules + self.path_prefix = 'users/%s' % self.TEST_USER_ID + + def new_ref(self, **kwargs): + kwargs = super(AccessRuleTests, self).new_ref(**kwargs) + kwargs.setdefault('path', uuid.uuid4().hex) + kwargs.setdefault('method', uuid.uuid4().hex) + kwargs.setdefault('service', uuid.uuid4().hex) + return kwargs + + def test_update(self): + self.assertRaises(exceptions.MethodNotImplemented, self.manager.update) + + def test_create(self): + self.assertRaises(exceptions.MethodNotImplemented, self.manager.create) diff --git a/keystoneclient/tests/unit/v3/test_application_credentials.py b/keystoneclient/tests/unit/v3/test_application_credentials.py index be3c62a..6e4bba3 100644 --- a/keystoneclient/tests/unit/v3/test_application_credentials.py +++ b/keystoneclient/tests/unit/v3/test_application_credentials.py @@ -98,6 +98,27 @@ class ApplicationCredentialTests(utils.ClientTestCase, utils.CrudTests): super(ApplicationCredentialTests, self).test_create(ref=ref, req_ref=req_ref) + def test_create_with_access_rules(self): + ref = self.new_ref(user=uuid.uuid4().hex) + access_rules = [ + { + 'method': 'GET', + 'path': '/v3/projects', + 'service': 'identity' + } + ] + ref['access_rules'] = access_rules + req_ref = ref.copy() + req_ref.pop('id') + user = req_ref.pop('user') + + self.stub_entity('POST', + ['users', user, self.collection_key], + status_code=201, entity=req_ref) + + super(ApplicationCredentialTests, self).test_create(ref=ref, + req_ref=req_ref) + def test_get(self): ref = self.new_ref(user=uuid.uuid4().hex) diff --git a/keystoneclient/v3/access_rules.py b/keystoneclient/v3/access_rules.py new file mode 100644 index 0000000..78fd015 --- /dev/null +++ b/keystoneclient/v3/access_rules.py @@ -0,0 +1,118 @@ +# Copyright 2019 SUSE LLC +# +# 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 AccessRule(base.Resource): + """Represents an Identity access rule for application credentials. + + Attributes: + * id: a uuid that identifies the access rule + * method: The request method that the application credential is + permitted to use for a given API endpoint + * path: The API path that the application credential is permitted to + access + * service: The service type identifier for the service that the + application credential is permitted to access + + """ + + pass + + +class AccessRuleManager(base.CrudManager): + """Manager class for manipulating Identity access rules.""" + + resource_class = AccessRule + collection_key = 'access_rules' + key = 'access_rule' + + def get(self, access_rule, user=None): + """Retrieve an access rule. + + :param access_rule: the access rule to be retrieved from the + server + :type access_rule: str or + :class:`keystoneclient.v3.access_rules.AccessRule` + :param string user: User ID + + :returns: the specified access rule + :rtype: + :class:`keystoneclient.v3.access_rules.AccessRule` + + """ + user = user or self.client.user_id + self.base_url = '/users/%(user)s' % {'user': user} + + return super(AccessRuleManager, self).get( + access_rule_id=base.getid(access_rule)) + + def list(self, user=None, **kwargs): + """List access rules. + + :param string user: User ID + + :returns: a list of access rules + :rtype: list of + :class:`keystoneclient.v3.access_rules.AccessRule` + """ + user = user or self.client.user_id + self.base_url = '/users/%(user)s' % {'user': user} + + return super(AccessRuleManager, self).list(**kwargs) + + def find(self, user=None, **kwargs): + """Find an access rule with attributes matching ``**kwargs``. + + :param string user: User ID + + :returns: a list of matching access rules + :rtype: list of + :class:`keystoneclient.v3.access_rules.AccessRule` + """ + user = user or self.client.user_id + self.base_url = '/users/%(user)s' % {'user': user} + + return super(AccessRuleManager, self).find(**kwargs) + + def delete(self, access_rule, user=None): + """Delete an access rule. + + :param access_rule: the access rule to be deleted + :type access_rule: str or + :class:`keystoneclient.v3.access_rules.AccessRule` + :param string user: User ID + + :returns: response object with 204 status + :rtype: :class:`requests.models.Response` + + """ + user = user or self.client.user_id + self.base_url = '/users/%(user)s' % {'user': user} + + return super(AccessRuleManager, self).delete( + access_rule_id=base.getid(access_rule)) + + def update(self): + raise exceptions.MethodNotImplemented( + _('Access rules are immutable, updating is not' + ' supported.')) + + def create(self): + raise exceptions.MethodNotImplemented( + _('Access rules can only be created as attributes of application ' + 'credentials.')) diff --git a/keystoneclient/v3/application_credentials.py b/keystoneclient/v3/application_credentials.py index 0fc94af..694ee8c 100644 --- a/keystoneclient/v3/application_credentials.py +++ b/keystoneclient/v3/application_credentials.py @@ -33,6 +33,8 @@ class ApplicationCredential(base.Resource): * roles: role assignments on the project * unrestricted: whether the application credential has restrictions applied + * access_rules: a list of access rules defining what API requests the + application credential may be used for """ @@ -48,7 +50,7 @@ class ApplicationCredentialManager(base.CrudManager): def create(self, name, user=None, secret=None, description=None, expires_at=None, roles=None, - unrestricted=False, **kwargs): + unrestricted=False, access_rules=None, **kwargs): """Create a credential. :param string name: application credential name @@ -60,6 +62,7 @@ class ApplicationCredentialManager(base.CrudManager): or a list of dicts specifying role name and domain :param bool unrestricted: whether the application credential has restrictions applied + :param List access_rules: a list of dicts representing access rules :returns: the created application credential :rtype: @@ -99,6 +102,7 @@ class ApplicationCredentialManager(base.CrudManager): expires_at=expires_str, roles=role_list, unrestricted=unrestricted, + access_rules=access_rules, **kwargs) def get(self, application_credential, user=None): diff --git a/keystoneclient/v3/client.py b/keystoneclient/v3/client.py index 89ba5ac..e99b065 100644 --- a/keystoneclient/v3/client.py +++ b/keystoneclient/v3/client.py @@ -22,6 +22,7 @@ from keystoneclient.auth.identity import v3 as v3_auth from keystoneclient import exceptions from keystoneclient import httpclient from keystoneclient.i18n import _ +from keystoneclient.v3 import access_rules from keystoneclient.v3 import application_credentials from keystoneclient.v3 import auth from keystoneclient.v3.contrib import endpoint_filter @@ -223,6 +224,9 @@ class Client(httpclient.HTTPClient): 'deprecated as of the 1.7.0 release and may be removed in ' 'the 2.0.0 release.', DeprecationWarning) + self.access_rules = ( + access_rules.AccessRuleManager(self._adapter) + ) self.application_credentials = ( application_credentials.ApplicationCredentialManager(self._adapter) ) diff --git a/releasenotes/notes/bp-whitelist-extension-for-app-creds-d03526e52e3edcce.yaml b/releasenotes/notes/bp-whitelist-extension-for-app-creds-d03526e52e3edcce.yaml new file mode 100644 index 0000000..9c7dc2b --- /dev/null +++ b/releasenotes/notes/bp-whitelist-extension-for-app-creds-d03526e52e3edcce.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Adds support for creating access rules as an attribute of application + credentials as well as for retrieving and deleting them. diff --git a/releasenotes/notes/drop-py-2-7-5ac18e82de83fcfa.yaml b/releasenotes/notes/drop-py-2-7-5ac18e82de83fcfa.yaml new file mode 100644 index 0000000..b977484 --- /dev/null +++ b/releasenotes/notes/drop-py-2-7-5ac18e82de83fcfa.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + Python 2.7 support has been dropped. Last release of python-keystoneclient + to support python 2.7 is OpenStack Train. The minimum version of Python now + supported is Python 3.6.
\ No newline at end of file @@ -13,8 +13,6 @@ classifier = License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 diff --git a/test-requirements.txt b/test-requirements.txt index 4683c46..d27f88c 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -2,7 +2,7 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -hacking>=1.1.0,<1.2.0 # Apache-2.0 +hacking>=3.0,<3.1.0 # Apache-2.0 flake8-docstrings==0.2.1.post1 # MIT coverage!=4.4,>=4.0 # Apache-2.0 @@ -1,7 +1,8 @@ [tox] -minversion = 2.5.0 +minversion = 3.1.1 skipsdist = True -envlist = py27,py37,pep8,releasenotes +envlist = py37,pep8,releasenotes +ignore_basepython_conflict = True [testenv] usedevelop = True @@ -17,25 +18,22 @@ deps = commands = find . -type f -name "*.pyc" -delete stestr run --slowest {posargs} whitelist_externals = find +basepython = python3 [testenv:pep8] -basepython = python3 commands = flake8 bandit -r keystoneclient -x tests -n5 [testenv:bandit] -basepython = python3 # NOTE(browne): This is required for the integration test job of the bandit # project. Please do not remove. commands = bandit -r keystoneclient -x tests -n5 [testenv:venv] -basepython = python3 commands = {posargs} [testenv:cover] -basepython = python3 setenv = PYTHON=coverage run --source keystoneclient --parallel-mode commands = @@ -46,11 +44,9 @@ commands = coverage report [testenv:debug] -basepython = python3 commands = oslo_debug_helper -t keystoneclient/tests {posargs} [testenv:functional] -basepython = python3 setenv = {[testenv]setenv} OS_TEST_PATH=./keystoneclient/tests/functional passenv = OS_* @@ -62,17 +58,17 @@ passenv = OS_* # D103: Missing docstring in public function # D104: Missing docstring in public package # D203: 1 blank line required before class docstring (deprecated in pep257) -ignore = D100,D101,D102,D103,D104,D203 +# W504 line break after binary operator +# F601 dictionary key repeated with different value +ignore = D100,D101,D102,D103,D104,D203,W504,F601 show-source = True exclude = .venv,.tox,dist,doc,*egg,build [testenv:docs] -basepython = python3 commands = python setup.py build_sphinx deps = -r{toxinidir}/doc/requirements.txt [testenv:pdf-docs] -basepython = python3 envdir = {toxworkdir}/docs deps = {[testenv:docs]deps} whitelist_externals = @@ -84,7 +80,6 @@ commands = make -C doc/build/pdf [testenv:releasenotes] -basepython = python3 commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html deps = -r{toxinidir}/doc/requirements.txt @@ -93,7 +88,6 @@ import_exceptions = keystoneclient.i18n [testenv:bindep] -basepython = python3 # Do not install any requirements. We want this to be fast and work even if # system dependencies are missing, since it's used to tell you what system # dependencies are missing! This also means that bindep must be installed @@ -102,7 +96,6 @@ deps = bindep commands = bindep test [testenv:lower-constraints] -basepython = python3 deps = -c{toxinidir}/lower-constraints.txt -r{toxinidir}/test-requirements.txt |