Hello community,
here is the log from the commit of package python-keystoneclient for
openSUSE:Factory checked in at 2018-02-14 10:50:33
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-keystoneclient (Old)
and /work/SRC/openSUSE:Factory/.python-keystoneclient.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-keystoneclient"
Wed Feb 14 10:50:33 2018 rev:27 rq:575938 version:3.15.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-keystoneclient/python-keystoneclient.changes
2018-01-24 15:26:57.372319298 +0100
+++
/work/SRC/openSUSE:Factory/.python-keystoneclient.new/python-keystoneclient.changes
2018-02-14 10:50:35.499908695 +0100
@@ -1,0 +2,10 @@
+Mon Feb 12 09:58:03 UTC 2018 - [email protected]
+
+- update to version 3.15.0 (bsc#1078607)
+ - Create doc/requirements.txt
+ - Add system role functionality
+ - Add CRUD support for application credentials
+ - Updated from global requirements
+ - Add project tags to keystoneclient
+
+-------------------------------------------------------------------
Old:
----
python-keystoneclient-3.14.0.tar.gz
New:
----
python-keystoneclient-3.15.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-keystoneclient.spec ++++++
--- /var/tmp/diff_new_pack.HhYWXc/_old 2018-02-14 10:50:36.355877891 +0100
+++ /var/tmp/diff_new_pack.HhYWXc/_new 2018-02-14 10:50:36.363877603 +0100
@@ -16,29 +16,27 @@
#
-%global sname python-keystoneclient
Name: python-keystoneclient
-Version: 3.14.0
+Version: 3.15.0
Release: 0
Summary: Client library for OpenStack Identity API
License: Apache-2.0
Group: Development/Languages/Python
-Url: http://launchpad.net/%{sname}
-Source0:
https://files.pythonhosted.org/packages/source/p/%{sname}/%{sname}-%{version}.tar.gz
+Url: https://launchpad.net/python-keystoneclient
+Source0:
https://files.pythonhosted.org/packages/source/p/python-keystoneclient/python-keystoneclient-3.15.0.tar.gz
BuildRequires: openssl
BuildRequires: openstack-macros
BuildRequires: python-devel
BuildRequires: python2-debtcollector >= 1.2.0
-BuildRequires: python2-keystoneauth1 >= 3.2.0
+BuildRequires: python2-keystoneauth1 >= 3.3.0
BuildRequires: python2-lxml >= 3.4.1
BuildRequires: python2-mock >= 2.0.0
-BuildRequires: python2-oslo.config >= 4.6.0
+BuildRequires: python2-oslo.config >= 5.1.0
BuildRequires: python2-oslo.i18n >= 3.15.3
BuildRequires: python2-oslo.serialization >= 2.18.0
-BuildRequires: python2-oslo.utils >= 3.31.0
-BuildRequires: python2-oslotest >= 1.10.0
+BuildRequires: python2-oslo.utils >= 3.33.0
+BuildRequires: python2-oslotest >= 3.2.0
BuildRequires: python2-pbr >= 2.0.0
-BuildRequires: python2-positional
BuildRequires: python2-requests-mock >= 1.1.0
BuildRequires: python2-six >= 1.10.0
BuildRequires: python2-testrepository >= 0.0.18
@@ -46,14 +44,14 @@
BuildRequires: python2-testscenarios >= 0.4
BuildRequires: python3-debtcollector >= 1.2.0
BuildRequires: python3-devel
-BuildRequires: python3-keystoneauth1 >= 3.2.0
+BuildRequires: python3-keystoneauth1 >= 3.3.0
BuildRequires: python3-lxml >= 3.4.1
BuildRequires: python3-mock >= 2.0.0
-BuildRequires: python3-oslo.config >= 4.6.0
+BuildRequires: python3-oslo.config >= 5.1.0
BuildRequires: python3-oslo.i18n >= 3.15.3
BuildRequires: python3-oslo.serialization >= 2.18.0
-BuildRequires: python3-oslo.utils >= 3.31.0
-BuildRequires: python3-oslotest >= 1.10.0
+BuildRequires: python3-oslo.utils >= 3.33.0
+BuildRequires: python3-oslotest >= 3.2.0
BuildRequires: python3-pbr >= 2.0.0
BuildRequires: python3-requests-mock >= 1.1.0
BuildRequires: python3-six >= 1.10.0
@@ -61,12 +59,11 @@
BuildRequires: python3-testresources >= 2.0.0
BuildRequires: python3-testscenarios >= 0.4
Requires: python-debtcollector >= 1.2.0
-Requires: python-keystoneauth1 >= 3.2.0
-Requires: python-oslo.config >= 4.6.0
+Requires: python-keystoneauth1 >= 3.3.0
+Requires: python-oslo.config >= 5.1.0
Requires: python-oslo.i18n >= 3.15.3
Requires: python-oslo.serialization >= 2.18.0
-Requires: python-oslo.utils >= 3.31.0
-Requires: python-positional
+Requires: python-oslo.utils >= 3.33.0
Requires: python-requests >= 2.14.2
Requires: python-six >= 1.10.0
Requires: python-stevedore >= 1.20.0
@@ -87,7 +84,7 @@
Identity API.
%prep
-%autosetup -p1 -n %{sname}-%{version}
+%autosetup -p1 -n python-keystoneclient-3.15.0
%py_req_cleanup
# disable intersphinx - no network access during build
echo "intersphinx_mapping = {}" >> doc/source/conf.py
++++++ python-keystoneclient-3.14.0.tar.gz ->
python-keystoneclient-3.15.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-keystoneclient-3.14.0/AUTHORS
new/python-keystoneclient-3.15.0/AUTHORS
--- old/python-keystoneclient-3.14.0/AUTHORS 2017-12-09 05:06:22.000000000
+0100
+++ new/python-keystoneclient-3.15.0/AUTHORS 2018-01-24 21:10:42.000000000
+0100
@@ -46,6 +46,7 @@
Clark Boylan <[email protected]>
Claudiu Belu <[email protected]>
Clint Byrum <[email protected]>
+Colleen Murphy <[email protected]>
Corey Bryant <[email protected]>
Cyril Roelandt <[email protected]>
Dan Prince <[email protected]>
@@ -164,6 +165,7 @@
Peter Portante <[email protected]>
Pradeep Kilambi <[email protected]>
Qin Zhao <[email protected]>
+QinglinCheng <[email protected]>
Qiu Yu <[email protected]>
Rakesh H S <[email protected]>
Rob Crittenden <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-keystoneclient-3.14.0/ChangeLog
new/python-keystoneclient-3.15.0/ChangeLog
--- old/python-keystoneclient-3.14.0/ChangeLog 2017-12-09 05:06:22.000000000
+0100
+++ new/python-keystoneclient-3.15.0/ChangeLog 2018-01-24 21:10:42.000000000
+0100
@@ -1,6 +1,16 @@
CHANGES
=======
+3.15.0
+------
+
+* Add system role functionality
+* Add CRUD support for application credentials
+* Updated from global requirements
+* Add project tags to keystoneclient
+* Create doc/requirements.txt
+* Updated from global requirements
+
3.14.0
------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-keystoneclient-3.14.0/PKG-INFO
new/python-keystoneclient-3.15.0/PKG-INFO
--- old/python-keystoneclient-3.14.0/PKG-INFO 2017-12-09 05:06:23.000000000
+0100
+++ new/python-keystoneclient-3.15.0/PKG-INFO 2018-01-24 21:10:43.000000000
+0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: python-keystoneclient
-Version: 3.14.0
+Version: 3.15.0
Summary: Client Library for OpenStack Identity
Home-page: https://docs.openstack.org/python-keystoneclient/latest/
Author: OpenStack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-keystoneclient-3.14.0/doc/requirements.txt
new/python-keystoneclient-3.15.0/doc/requirements.txt
--- old/python-keystoneclient-3.14.0/doc/requirements.txt 1970-01-01
01:00:00.000000000 +0100
+++ new/python-keystoneclient-3.15.0/doc/requirements.txt 2018-01-24
21:08:40.000000000 +0100
@@ -0,0 +1,10 @@
+# The order of packages is significant, because pip processes them in the order
+# of appearance. Changing the order has an impact on the overall integration
+# process, which may cause wedges in the gate later.
+
+# These are needed for docs generation
+openstackdocstheme>=1.17.0 # Apache-2.0
+sphinx!=1.6.6,>=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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-keystoneclient-3.14.0/keystoneclient/base.py
new/python-keystoneclient-3.15.0/keystoneclient/base.py
--- old/python-keystoneclient-3.14.0/keystoneclient/base.py 2017-12-09
05:02:58.000000000 +0100
+++ new/python-keystoneclient-3.15.0/keystoneclient/base.py 2018-01-24
21:08:40.000000000 +0100
@@ -356,6 +356,13 @@
if params is None:
return ''
else:
+ # NOTE(spilla) Since the manager cannot take in a hyphen as a
+ # key in the kwarg, it is passed in with a _. This needs to be
+ # replaced with a proper hyphen for the URL to work properly.
+ tags_params = ('tags_any', 'not_tags', 'not_tags_any')
+ for tag_param in tags_params:
+ if tag_param in params:
+ params[tag_param.replace('_', '-')] = params.pop(tag_param)
return '?%s' % urllib.parse.urlencode(params, doseq=True)
def build_key_only_query(self, params_list):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/keystoneclient/tests/functional/v3/client_fixtures.py
new/python-keystoneclient-3.15.0/keystoneclient/tests/functional/v3/client_fixtures.py
---
old/python-keystoneclient-3.14.0/keystoneclient/tests/functional/v3/client_fixtures.py
2017-12-09 05:02:58.000000000 +0100
+++
new/python-keystoneclient-3.15.0/keystoneclient/tests/functional/v3/client_fixtures.py
2018-01-24 21:08:40.000000000 +0100
@@ -71,9 +71,10 @@
class Project(Base):
- def __init__(self, client, domain_id=None, parent=None):
+ def __init__(self, client, domain_id=None, parent=None, tags=None):
super(Project, self).__init__(client, domain_id)
self.parent = parent
+ self.tags = tags if tags else []
def setUp(self):
super(Project, self).setUp()
@@ -81,7 +82,8 @@
self.ref = {'name': RESOURCE_NAME_PREFIX + uuid.uuid4().hex,
'domain': self.domain_id,
'enabled': True,
- 'parent': self.parent}
+ 'parent': self.parent,
+ 'tags': self.tags}
self.entity = self.client.projects.create(**self.ref)
self.addCleanup(self.client.projects.delete, self.entity)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/keystoneclient/tests/functional/v3/test_projects.py
new/python-keystoneclient-3.15.0/keystoneclient/tests/functional/v3/test_projects.py
---
old/python-keystoneclient-3.14.0/keystoneclient/tests/functional/v3/test_projects.py
2017-12-09 05:02:58.000000000 +0100
+++
new/python-keystoneclient-3.15.0/keystoneclient/tests/functional/v3/test_projects.py
2018-01-24 21:08:40.000000000 +0100
@@ -53,6 +53,7 @@
self.test_project = fixtures.Project(self.client, self.test_domain.id)
self.useFixture(self.test_project)
+ self.special_tag = '~`!@#$%^&*()-_+=<>.? \'"'
def test_create_subproject(self):
project_ref = {
@@ -188,3 +189,257 @@
self.assertRaises(http.NotFound,
self.client.projects.get,
project.id)
+
+ def test_list_projects_with_tag_filters(self):
+ project_one = fixtures.Project(
+ self.client, self.test_domain.id,
+ tags=['tag1'])
+ project_two = fixtures.Project(
+ self.client, self.test_domain.id,
+ tags=['tag1', 'tag2'])
+ project_three = fixtures.Project(
+ self.client, self.test_domain.id,
+ tags=['tag2', 'tag3'])
+
+ self.useFixture(project_one)
+ self.useFixture(project_two)
+ self.useFixture(project_three)
+
+ projects = self.client.projects.list(tags='tag1')
+ project_ids = []
+ for project in projects:
+ project_ids.append(project.id)
+ self.assertIn(project_one.id, project_ids)
+
+ projects = self.client.projects.list(tags_any='tag1')
+ project_ids = []
+ for project in projects:
+ project_ids.append(project.id)
+ self.assertIn(project_one.id, project_ids)
+ self.assertIn(project_two.id, project_ids)
+
+ projects = self.client.projects.list(not_tags='tag1')
+ project_ids = []
+ for project in projects:
+ project_ids.append(project.id)
+ self.assertNotIn(project_one.id, project_ids)
+
+ projects = self.client.projects.list(not_tags_any='tag1,tag2')
+ project_ids = []
+ for project in projects:
+ project_ids.append(project.id)
+ self.assertNotIn(project_one.id, project_ids)
+ self.assertNotIn(project_two.id, project_ids)
+ self.assertNotIn(project_three.id, project_ids)
+
+ projects = self.client.projects.list(tags='tag1,tag2')
+ project_ids = []
+ for project in projects:
+ project_ids.append(project.id)
+ self.assertNotIn(project_one.id, project_ids)
+ self.assertIn(project_two.id, project_ids)
+ self.assertNotIn(project_three.id, project_ids)
+
+ def test_add_tag(self):
+ project = fixtures.Project(self.client, self.test_domain.id)
+ self.useFixture(project)
+
+ tags = self.client.projects.get(project.id).tags
+ self.assertEqual([], tags)
+
+ project.add_tag('tag1')
+ tags = self.client.projects.get(project.id).tags
+ self.assertEqual(['tag1'], tags)
+
+ # verify there is an error when you try to add the same tag
+ self.assertRaises(http.BadRequest,
+ project.add_tag,
+ 'tag1')
+
+ def test_update_tags(self):
+ project = fixtures.Project(self.client, self.test_domain.id)
+ self.useFixture(project)
+
+ tags = self.client.projects.get(project.id).tags
+ self.assertEqual([], tags)
+
+ project.update_tags(['tag1', 'tag2', self.special_tag])
+ tags = self.client.projects.get(project.id).tags
+ self.assertIn('tag1', tags)
+ self.assertIn('tag2', tags)
+ self.assertIn(self.special_tag, tags)
+ self.assertEqual(3, len(tags))
+
+ project.update_tags([])
+ tags = self.client.projects.get(project.id).tags
+ self.assertEqual([], tags)
+
+ # cannot have duplicate tags in update
+ self.assertRaises(http.BadRequest,
+ project.update_tags,
+ ['tag1', 'tag1'])
+
+ def test_delete_tag(self):
+ project = fixtures.Project(
+ self.client, self.test_domain.id,
+ tags=['tag1', self.special_tag])
+ self.useFixture(project)
+
+ project.delete_tag('tag1')
+ tags = self.client.projects.get(project.id).tags
+ self.assertEqual([self.special_tag], tags)
+
+ project.delete_tag(self.special_tag)
+ tags = self.client.projects.get(project.id).tags
+ self.assertEqual([], tags)
+
+ def test_delete_all_tags(self):
+ project_one = fixtures.Project(
+ self.client, self.test_domain.id,
+ tags=['tag1'])
+
+ project_two = fixtures.Project(
+ self.client, self.test_domain.id,
+ tags=['tag1', 'tag2', self.special_tag])
+
+ project_three = fixtures.Project(
+ self.client, self.test_domain.id,
+ tags=[])
+
+ self.useFixture(project_one)
+ self.useFixture(project_two)
+ self.useFixture(project_three)
+
+ result_one = project_one.delete_all_tags()
+ tags_one = self.client.projects.get(project_one.id).tags
+ tags_two = self.client.projects.get(project_two.id).tags
+ self.assertEqual([], result_one)
+ self.assertEqual([], tags_one)
+ self.assertIn('tag1', tags_two)
+
+ result_two = project_two.delete_all_tags()
+ tags_two = self.client.projects.get(project_two.id).tags
+ self.assertEqual([], result_two)
+ self.assertEqual([], tags_two)
+
+ result_three = project_three.delete_all_tags()
+ tags_three = self.client.projects.get(project_three.id).tags
+ self.assertEqual([], result_three)
+ self.assertEqual([], tags_three)
+
+ def test_list_tags(self):
+ tags_one = ['tag1']
+ project_one = fixtures.Project(
+ self.client, self.test_domain.id,
+ tags=tags_one)
+
+ tags_two = ['tag1', 'tag2']
+ project_two = fixtures.Project(
+ self.client, self.test_domain.id,
+ tags=tags_two)
+
+ tags_three = []
+ project_three = fixtures.Project(
+ self.client, self.test_domain.id,
+ tags=tags_three)
+
+ self.useFixture(project_one)
+ self.useFixture(project_two)
+ self.useFixture(project_three)
+
+ result_one = project_one.list_tags()
+ result_two = project_two.list_tags()
+ result_three = project_three.list_tags()
+
+ for tag in tags_one:
+ self.assertIn(tag, result_one)
+ self.assertEqual(1, len(result_one))
+
+ for tag in tags_two:
+ self.assertIn(tag, result_two)
+ self.assertEqual(2, len(result_two))
+
+ for tag in tags_three:
+ self.assertIn(tag, result_three)
+ self.assertEqual(0, len(result_three))
+
+ def test_check_tag(self):
+ project = fixtures.Project(
+ self.client, self.test_domain.id,
+ tags=['tag1'])
+ self.useFixture(project)
+
+ tags = self.client.projects.get(project.id).tags
+ self.assertEqual(['tag1'], tags)
+ self.assertTrue(project.check_tag('tag1'))
+ self.assertFalse(project.check_tag('tag2'))
+ self.assertFalse(project.check_tag(self.special_tag))
+
+ def test_add_invalid_tags(self):
+ project_one = fixtures.Project(
+ self.client, self.test_domain.id)
+
+ self.useFixture(project_one)
+
+ self.assertRaises(exceptions.BadRequest,
+ project_one.add_tag,
+ ',')
+ self.assertRaises(exceptions.BadRequest,
+ project_one.add_tag,
+ '/')
+ self.assertRaises(exceptions.BadRequest,
+ project_one.add_tag,
+ '')
+
+ def test_update_invalid_tags(self):
+ tags_comma = ['tag1', ',']
+ tags_slash = ['tag1', '/']
+ tags_blank = ['tag1', '']
+ project_one = fixtures.Project(
+ self.client, self.test_domain.id)
+
+ self.useFixture(project_one)
+
+ self.assertRaises(exceptions.BadRequest,
+ project_one.update_tags,
+ tags_comma)
+ self.assertRaises(exceptions.BadRequest,
+ project_one.update_tags,
+ tags_slash)
+ self.assertRaises(exceptions.BadRequest,
+ project_one.update_tags,
+ tags_blank)
+
+ def test_create_project_invalid_tags(self):
+ project_ref = {
+ 'name': fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex,
+ 'domain': self.test_domain.id,
+ 'enabled': True,
+ 'description': uuid.uuid4().hex,
+ 'tags': ','}
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.projects.create,
+ **project_ref)
+
+ project_ref = {
+ 'name': fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex,
+ 'domain': self.test_domain.id,
+ 'enabled': True,
+ 'description': uuid.uuid4().hex,
+ 'tags': '/'}
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.projects.create,
+ **project_ref)
+
+ project_ref = {
+ 'name': fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex,
+ 'domain': self.test_domain.id,
+ 'enabled': True,
+ 'description': uuid.uuid4().hex,
+ 'tags': ''}
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.projects.create,
+ **project_ref)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/keystoneclient/tests/unit/v3/test_application_credentials.py
new/python-keystoneclient-3.15.0/keystoneclient/tests/unit/v3/test_application_credentials.py
---
old/python-keystoneclient-3.14.0/keystoneclient/tests/unit/v3/test_application_credentials.py
1970-01-01 01:00:00.000000000 +0100
+++
new/python-keystoneclient-3.15.0/keystoneclient/tests/unit/v3/test_application_credentials.py
2018-01-24 21:09:04.000000000 +0100
@@ -0,0 +1,116 @@
+# 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 oslo_utils import timeutils
+
+from keystoneclient import exceptions
+from keystoneclient.tests.unit.v3 import utils
+from keystoneclient.v3 import application_credentials
+
+
+class ApplicationCredentialTests(utils.ClientTestCase, utils.CrudTests):
+ def setUp(self):
+ super(ApplicationCredentialTests, self).setUp()
+ self.key = 'application_credential'
+ self.collection_key = 'application_credentials'
+ self.model = application_credentials.ApplicationCredential
+ self.manager = self.client.application_credentials
+ self.path_prefix = 'users/%s' % self.TEST_USER_ID
+
+ def new_ref(self, **kwargs):
+ kwargs = super(ApplicationCredentialTests, self).new_ref(**kwargs)
+ kwargs.setdefault('name', uuid.uuid4().hex)
+ kwargs.setdefault('description', uuid.uuid4().hex)
+ kwargs.setdefault('unrestricted', False)
+ return kwargs
+
+ def test_create_with_roles(self):
+ ref = self.new_ref(user=uuid.uuid4().hex)
+ ref['roles'] = [{'name': 'atestrole'}]
+ 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_create_with_role_id_and_names(self):
+ ref = self.new_ref(user=uuid.uuid4().hex)
+ ref['roles'] = [{'name': 'atestrole', 'domain': 'nondefault'},
+ uuid.uuid4().hex]
+ req_ref = ref.copy()
+ req_ref.pop('id')
+ user = req_ref.pop('user')
+
+ req_ref['roles'] = [{'name': 'atestrole', 'domain': 'nondefault'},
+ {'id': ref['roles'][1]}]
+ 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_create_expires(self):
+ ref = self.new_ref(user=uuid.uuid4().hex)
+ ref['expires_at'] = timeutils.parse_isotime(
+ '2013-03-04T12:00:01.000000Z')
+ req_ref = ref.copy()
+ req_ref.pop('id')
+ user = req_ref.pop('user')
+
+ req_ref['expires_at'] = '2013-03-04T12:00:01.000000Z'
+
+ 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_create_unrestricted(self):
+ ref = self.new_ref(user=uuid.uuid4().hex)
+ ref['unrestricted'] = True
+ 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)
+
+ self.stub_entity(
+ 'GET', ['users', ref['user'], self.collection_key, ref['id']],
+ entity=ref)
+ returned = self.manager.get(ref['id'], ref['user'])
+ self.assertIsInstance(returned, self.model)
+ for attr in ref:
+ self.assertEqual(
+ getattr(returned, attr),
+ ref[attr],
+ 'Expected different %s' % attr)
+
+ def test_update(self):
+ self.assertRaises(exceptions.MethodNotImplemented, self.manager.update)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/keystoneclient/tests/unit/v3/test_projects.py
new/python-keystoneclient-3.15.0/keystoneclient/tests/unit/v3/test_projects.py
---
old/python-keystoneclient-3.14.0/keystoneclient/tests/unit/v3/test_projects.py
2017-12-09 05:02:58.000000000 +0100
+++
new/python-keystoneclient-3.15.0/keystoneclient/tests/unit/v3/test_projects.py
2018-01-24 21:08:40.000000000 +0100
@@ -312,3 +312,86 @@
# server, a different implementation might not fail this request.
self.assertRaises(ksa_exceptions.Forbidden, self.manager.update,
ref['id'], **utils.parameterize(req_ref))
+
+ def test_add_tag(self):
+ ref = self.new_ref()
+ tag_name = "blue"
+
+ self.stub_url("PUT",
+ parts=[self.collection_key, ref['id'], "tags", tag_name],
+ status_code=201)
+ self.manager.add_tag(ref['id'], tag_name)
+
+ def test_update_tags(self):
+ new_tags = ["blue", "orange"]
+ ref = self.new_ref()
+
+ self.stub_url("PUT",
+ parts=[self.collection_key, ref['id'], "tags"],
+ json={"tags": new_tags},
+ status_code=200)
+
+ ret = self.manager.update_tags(ref['id'], new_tags)
+ self.assertEqual(ret, new_tags)
+
+ def test_delete_tag(self):
+ ref = self.new_ref()
+ tag_name = "blue"
+
+ self.stub_url("DELETE",
+ parts=[self.collection_key, ref['id'], "tags", tag_name],
+ status_code=204)
+
+ self.manager.delete_tag(ref['id'], tag_name)
+
+ def test_delete_all_tags(self):
+ ref = self.new_ref()
+
+ self.stub_url("PUT",
+ parts=[self.collection_key, ref['id'], "tags"],
+ json={"tags": []},
+ status_code=200)
+
+ ret = self.manager.update_tags(ref['id'], [])
+ self.assertEqual([], ret)
+
+ def test_list_tags(self):
+ ref = self.new_ref()
+ tags = ["blue", "orange", "green"]
+
+ self.stub_url("GET",
+ parts=[self.collection_key, ref['id'], "tags"],
+ json={"tags": tags},
+ status_code=200)
+
+ ret_tags = self.manager.list_tags(ref['id'])
+ self.assertEqual(tags, ret_tags)
+
+ def test_check_tag(self):
+ ref = self.new_ref()
+
+ tag_name = "blue"
+ self.stub_url("HEAD",
+ parts=[self.collection_key, ref['id'], "tags", tag_name],
+ status_code=204)
+ self.assertTrue(self.manager.check_tag(ref['id'], tag_name))
+
+ no_tag = "orange"
+ self.stub_url("HEAD",
+ parts=[self.collection_key, ref['id'], "tags", no_tag],
+ status_code=404)
+ self.assertFalse(self.manager.check_tag(ref['id'], no_tag))
+
+ def _build_project_response(self, tags):
+ project_id = uuid.uuid4().hex
+ ret = {"projects": [
+ {"is_domain": False,
+ "description": "",
+ "tags": tags,
+ "enabled": True,
+ "id": project_id,
+ "parent_id": "default",
+ "domain_id": "default",
+ "name": project_id}
+ ]}
+ return ret
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/keystoneclient/tests/unit/v3/test_role_assignments.py
new/python-keystoneclient-3.15.0/keystoneclient/tests/unit/v3/test_role_assignments.py
---
old/python-keystoneclient-3.14.0/keystoneclient/tests/unit/v3/test_role_assignments.py
2017-12-09 05:02:58.000000000 +0100
+++
new/python-keystoneclient-3.15.0/keystoneclient/tests/unit/v3/test_role_assignments.py
2018-01-24 21:09:04.000000000 +0100
@@ -23,6 +23,32 @@
self.collection_key = 'role_assignments'
self.model = role_assignments.RoleAssignment
self.manager = self.client.role_assignments
+ self.TEST_USER_SYSTEM_LIST = [{
+ 'role': {
+ 'id': self.TEST_ROLE_ID
+ },
+ 'scope': {
+ 'system': {
+ 'all': True
+ }
+ },
+ 'user': {
+ 'id': self.TEST_USER_ID
+ }
+ }]
+ self.TEST_GROUP_SYSTEM_LIST = [{
+ 'role': {
+ 'id': self.TEST_ROLE_ID
+ },
+ 'scope': {
+ 'system': {
+ 'all': True
+ }
+ },
+ 'group': {
+ 'id': self.TEST_GROUP_ID
+ }
+ }]
self.TEST_USER_DOMAIN_LIST = [{
'role': {
'id': self.TEST_ROLE_ID
@@ -65,7 +91,9 @@
self.TEST_ALL_RESPONSE_LIST = (self.TEST_USER_PROJECT_LIST +
self.TEST_GROUP_PROJECT_LIST +
- self.TEST_USER_DOMAIN_LIST)
+ self.TEST_USER_DOMAIN_LIST +
+ self.TEST_USER_SYSTEM_LIST +
+ self.TEST_GROUP_SYSTEM_LIST)
def _assert_returned_list(self, ref_list, returned_list):
self.assertEqual(len(ref_list), len(returned_list))
@@ -150,6 +178,50 @@
kwargs = {'scope.domain.id': self.TEST_DOMAIN_ID}
self.assertQueryStringContains(**kwargs)
+ def test_system_assignment_list(self):
+ ref_list = self.TEST_USER_SYSTEM_LIST + self.TEST_GROUP_SYSTEM_LIST
+
+ self.stub_entity('GET',
+ [self.collection_key, '?scope.system=all'],
+ entity=ref_list)
+
+ returned_list = self.manager.list(system='all')
+ self._assert_returned_list(ref_list, returned_list)
+
+ kwargs = {'scope.system': 'all'}
+ self.assertQueryStringContains(**kwargs)
+
+ def test_system_assignment_list_for_user(self):
+ ref_list = self.TEST_USER_SYSTEM_LIST
+
+ self.stub_entity('GET',
+ [self.collection_key,
+ '?user.id=%s&scope.system=all' % self.TEST_USER_ID],
+ entity=ref_list)
+
+ returned_list = self.manager.list(system='all', user=self.TEST_USER_ID)
+ self._assert_returned_list(ref_list, returned_list)
+
+ kwargs = {'scope.system': 'all', 'user.id': self.TEST_USER_ID}
+ self.assertQueryStringContains(**kwargs)
+
+ def test_system_assignment_list_for_group(self):
+ ref_list = self.TEST_GROUP_SYSTEM_LIST
+
+ self.stub_entity(
+ 'GET',
+ [self.collection_key,
+ '?group.id=%s&scope.system=all' % self.TEST_GROUP_ID],
+ entity=ref_list)
+
+ returned_list = self.manager.list(
+ system='all', group=self.TEST_GROUP_ID
+ )
+ self._assert_returned_list(ref_list, returned_list)
+
+ kwargs = {'scope.system': 'all', 'group.id': self.TEST_GROUP_ID}
+ self.assertQueryStringContains(**kwargs)
+
def test_group_assignments_list(self):
ref_list = self.TEST_GROUP_PROJECT_LIST
self.stub_entity('GET',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/keystoneclient/v3/application_credentials.py
new/python-keystoneclient-3.15.0/keystoneclient/v3/application_credentials.py
---
old/python-keystoneclient-3.14.0/keystoneclient/v3/application_credentials.py
1970-01-01 01:00:00.000000000 +0100
+++
new/python-keystoneclient-3.15.0/keystoneclient/v3/application_credentials.py
2018-01-24 21:09:04.000000000 +0100
@@ -0,0 +1,171 @@
+# Copyright 2018 SUSE Linux GmbH
+#
+# 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 six
+
+from keystoneclient import base
+from keystoneclient import exceptions
+from keystoneclient.i18n import _
+from keystoneclient import utils
+
+
+class ApplicationCredential(base.Resource):
+ """Represents an Identity application credential.
+
+ Attributes:
+ * id: a uuid that identifies the application credential
+ * user: the user who owns the application credential
+ * name: application credential name
+ * secret: application credential secret
+ * description: application credential description
+ * expires_at: expiry time
+ * roles: role assignments on the project
+ * unrestricted: whether the application credential has restrictions
+ applied
+
+ """
+
+ pass
+
+
+class ApplicationCredentialManager(base.CrudManager):
+ """Manager class for manipulating Identity application credentials."""
+
+ resource_class = ApplicationCredential
+ collection_key = 'application_credentials'
+ key = 'application_credential'
+
+ def create(self, name, user=None, secret=None, description=None,
+ expires_at=None, roles=None,
+ unrestricted=False, **kwargs):
+ """Create a credential.
+
+ :param string name: application credential name
+ :param string user: User ID
+ :param secret: application credential secret
+ :param description: application credential description
+ :param datetime.datetime expires_at: expiry time
+ :param List roles: list of roles on the project. Maybe a list of IDs
+ or a list of dicts specifying role name and domain
+ :param bool unrestricted: whether the application credential has
+ restrictions applied
+
+ :returns: the created application credential
+ :rtype:
+
:class:`keystoneclient.v3.application_credentials.ApplicationCredential`
+
+ """
+ user = user or self.client.user_id
+ self.base_url = '/users/%(user)s' % {'user': user}
+
+ # Convert roles list into list-of-dict API format
+ role_list = []
+ if roles:
+ if not isinstance(roles, list):
+ roles = [roles]
+ for role in roles:
+ if isinstance(role, six.string_types):
+ role_list.extend([{'id': role}])
+ elif isinstance(role, dict):
+ role_list.extend([role])
+ else:
+ msg = (_("Roles must be a list of IDs or role dicts."))
+ raise exceptions.CommandError(msg)
+
+ if not role_list:
+ role_list = None
+
+ # Convert datetime.datetime expires_at to iso format string
+ if expires_at:
+ expires_str = utils.isotime(at=expires_at, subsecond=True)
+ else:
+ expires_str = None
+
+ return super(ApplicationCredentialManager, self).create(
+ name=name,
+ secret=secret,
+ description=description,
+ expires_at=expires_str,
+ roles=role_list,
+ unrestricted=unrestricted,
+ **kwargs)
+
+ def get(self, application_credential, user=None):
+ """Retrieve an application credential.
+
+ :param application_credential: the credential to be retrieved from the
+ server
+ :type applicationcredential: str or
+
:class:`keystoneclient.v3.application_credentials.ApplicationCredential`
+
+ :returns: the specified application credential
+ :rtype:
+
:class:`keystoneclient.v3.application_credentials.ApplicationCredential`
+
+ """
+ user = user or self.client.user_id
+ self.base_url = '/users/%(user)s' % {'user': user}
+
+ return super(ApplicationCredentialManager, self).get(
+ application_credential_id=base.getid(application_credential))
+
+ def list(self, user=None, **kwargs):
+ """List application credentials.
+
+ :param string user: User ID
+
+ :returns: a list of application credentials
+ :rtype: list of
+
:class:`keystoneclient.v3.application_credentials.ApplicationCredential`
+ """
+ user = user or self.client.user_id
+ self.base_url = '/users/%(user)s' % {'user': user}
+
+ return super(ApplicationCredentialManager, self).list(**kwargs)
+
+ def find(self, user=None, **kwargs):
+ """Find an application credential with attributes matching
``**kwargs``.
+
+ :param string user: User ID
+
+ :returns: a list of matching application credentials
+ :rtype: list of
+
:class:`keystoneclient.v3.application_credentials.ApplicationCredential`
+ """
+ user = user or self.client.user_id
+ self.base_url = '/users/%(user)s' % {'user': user}
+
+ return super(ApplicationCredentialManager, self).find(**kwargs)
+
+ def delete(self, application_credential, user=None):
+ """Delete an application credential.
+
+ :param application_credential: the application credential to be deleted
+ :type credential: str or
+
:class:`keystoneclient.v3.application_credentials.ApplicationCredential`
+
+ :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(ApplicationCredentialManager, self).delete(
+ application_credential_id=base.getid(application_credential))
+
+ def update(self):
+ raise exceptions.MethodNotImplemented(
+ _('Application credentials are immutable, updating is not'
+ ' supported.'))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/keystoneclient/v3/client.py
new/python-keystoneclient-3.15.0/keystoneclient/v3/client.py
--- old/python-keystoneclient-3.14.0/keystoneclient/v3/client.py
2017-12-09 05:02:58.000000000 +0100
+++ new/python-keystoneclient-3.15.0/keystoneclient/v3/client.py
2018-01-24 21:09:04.000000000 +0100
@@ -22,6 +22,7 @@
from keystoneclient import exceptions
from keystoneclient import httpclient
from keystoneclient.i18n import _
+from keystoneclient.v3 import application_credentials
from keystoneclient.v3 import auth
from keystoneclient.v3.contrib import endpoint_filter
from keystoneclient.v3.contrib import endpoint_policy
@@ -212,6 +213,9 @@
'deprecated as of the 1.7.0 release and may be removed in '
'the 2.0.0 release.', DeprecationWarning)
+ self.application_credentials = (
+ application_credentials.ApplicationCredentialManager(self._adapter)
+ )
self.auth = auth.AuthManager(self._adapter)
self.credentials = credentials.CredentialManager(self._adapter)
self.ec2 = ec2.EC2Manager(self._adapter)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/keystoneclient/v3/projects.py
new/python-keystoneclient-3.15.0/keystoneclient/v3/projects.py
--- old/python-keystoneclient-3.14.0/keystoneclient/v3/projects.py
2017-12-09 05:02:58.000000000 +0100
+++ new/python-keystoneclient-3.15.0/keystoneclient/v3/projects.py
2018-01-24 21:08:40.000000000 +0100
@@ -14,6 +14,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import six.moves.urllib as urllib
+
from keystoneclient import base
from keystoneclient import exceptions
from keystoneclient.i18n import _
@@ -52,6 +54,24 @@
return retval
+ def add_tag(self, tag):
+ self.manager.add_tag(self, tag)
+
+ def update_tags(self, tags):
+ return self.manager.update_tags(self, tags)
+
+ def delete_tag(self, tag):
+ self.manager.delete_tag(self, tag)
+
+ def delete_all_tags(self):
+ return self.manager.update_tags(self, [])
+
+ def list_tags(self):
+ return self.manager.list_tags(self)
+
+ def check_tag(self, tag):
+ return self.manager.check_tag(self, tag)
+
class ProjectManager(base.CrudManager):
"""Manager class for manipulating Identity projects."""
@@ -101,17 +121,24 @@
assignments on.
:type user: str or :class:`keystoneclient.v3.users.User`
:param kwargs: any other attribute provided will filter projects on.
+ Project tags filter keyword: ``tags``, ``tags_any``,
+ ``not_tags``, and ``not_tags_any``. tag attribute type
+ string. Pass in a comma separated string to filter
+ with multiple tags.
:returns: a list of projects.
:rtype: list of :class:`keystoneclient.v3.projects.Project`
"""
base_url = '/users/%s' % base.getid(user) if user else None
- return super(ProjectManager, self).list(
+ projects = super(ProjectManager, self).list(
base_url=base_url,
domain_id=base.getid(domain),
fallback_to_auth=True,
**kwargs)
+ for p in projects:
+ p.tags = self._encode_tags(getattr(p, 'tags', []))
+ return projects
def _check_not_parents_as_ids_and_parents_as_list(self, parents_as_ids,
parents_as_list):
@@ -174,7 +201,9 @@
query = self.build_key_only_query(query_params)
dict_args = {'project_id': base.getid(project)}
url = self.build_url(dict_args_in_out=dict_args)
- return self._get(url + query, self.key)
+ p = self._get(url + query, self.key)
+ p.tags = self._encode_tags(getattr(p, 'tags', []))
+ return p
def update(self, project, name=None, domain=None, description=None,
enabled=None, **kwargs):
@@ -213,3 +242,82 @@
"""
return super(ProjectManager, self).delete(
project_id=base.getid(project))
+
+ def _encode_tags(self, tags):
+ """Encode tags to non-unicode string in python2.
+
+ :param tags: list of unicode tags
+
+ :returns: List of strings
+ """
+ return [str(t) for t in tags]
+
+ def add_tag(self, project, tag):
+ """Add a tag to a project.
+
+ :param project: project to add a tag to.
+ :param tag: str name of tag.
+
+ """
+ url = "/projects/%s/tags/%s" % (base.getid(project),
+ urllib.parse.quote(tag))
+ self.client.put(url)
+
+ def update_tags(self, project, tags):
+ """Update tag list of a project.
+
+ Replaces current tag list with list specified in tags parameter.
+
+ :param project: project to update.
+ :param tags: list of str tag names to add to the project
+
+ :returns: list of tags
+
+ """
+ url = "/projects/%s/tags" % base.getid(project)
+ for tag in tags:
+ tag = urllib.parse.quote(tag)
+ resp, body = self.client.put(url, body={"tags": tags})
+ return body['tags']
+
+ def delete_tag(self, project, tag):
+ """Remove tag from project.
+
+ :param projectd: project to remove tag from.
+ :param tag: str name of tag to remove from project
+
+ """
+ self._delete(
+ "/projects/%s/tags/%s" % (base.getid(project),
+ urllib.parse.quote(tag)))
+
+ def list_tags(self, project):
+ """List tags associated with project.
+
+ :param project: project to list tags for.
+
+ :returns: list of str tag names
+
+ """
+ url = "/projects/%s/tags" % base.getid(project)
+ resp, body = self.client.get(url)
+ return self._encode_tags(body['tags'])
+
+ def check_tag(self, project, tag):
+ """Check if tag is associated with project.
+
+ :param project: project to check tags for.
+ :param tag: str name of tag
+
+ :returns: true if tag is associated, false otherwise
+
+ """
+ url = "/projects/%s/tags/%s" % (base.getid(project),
+ urllib.parse.quote(tag))
+ try:
+ self.client.head(url)
+ # no errors means found the tag
+ return True
+ except exceptions.NotFound:
+ # 404 means tag not in project
+ return False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/keystoneclient/v3/role_assignments.py
new/python-keystoneclient-3.15.0/keystoneclient/v3/role_assignments.py
--- old/python-keystoneclient-3.14.0/keystoneclient/v3/role_assignments.py
2017-12-09 05:02:58.000000000 +0100
+++ new/python-keystoneclient-3.15.0/keystoneclient/v3/role_assignments.py
2018-01-24 21:09:04.000000000 +0100
@@ -46,9 +46,25 @@
msg = _('Specify either a domain or project, not both')
raise exceptions.ValidationError(msg)
- def list(self, user=None, group=None, project=None, domain=None, role=None,
- effective=False, os_inherit_extension_inherited_to=None,
- include_subtree=False, include_names=False):
+ def _check_not_system_and_domain(self, system, domain):
+ if system and domain:
+ msg = _('Specify either system or domain, not both')
+ raise exceptions.ValidationError(msg)
+
+ def _check_not_system_and_project(self, system, project):
+ if system and project:
+ msg = _('Specify either system or project, not both')
+ raise exceptions.ValidationError(msg)
+
+ def _check_system_value(self, system):
+ if system and system != 'all':
+ msg = _("Only a system scope of 'all' is currently supported")
+ raise exceptions.ValidationError(msg)
+
+ def list(self, user=None, group=None, project=None, domain=None,
+ system=False, role=None, effective=False,
+ os_inherit_extension_inherited_to=None, include_subtree=False,
+ include_names=False):
"""List role assignments.
If no arguments are provided, all role assignments in the
@@ -64,6 +80,8 @@
(optional)
:param domain: Domain to be used as query
filter. (optional)
+ :param system: Boolean to be used to filter system assignments.
+ (optional)
:param role: Role to be used as query filter. (optional)
:param boolean effective: return effective role
assignments. (optional)
@@ -76,6 +94,9 @@
"""
self._check_not_user_and_group(user, group)
self._check_not_domain_and_project(domain, project)
+ self._check_not_system_and_domain(system, domain)
+ self._check_not_system_and_project(system, project)
+ self._check_system_value(system)
query_params = {}
if user:
@@ -86,6 +107,8 @@
query_params['scope.project.id'] = base.getid(project)
if domain:
query_params['scope.domain.id'] = base.getid(domain)
+ if system:
+ query_params['scope.system'] = system
if role:
query_params['role.id'] = base.getid(role)
if effective:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/keystoneclient/v3/roles.py
new/python-keystoneclient-3.15.0/keystoneclient/v3/roles.py
--- old/python-keystoneclient-3.14.0/keystoneclient/v3/roles.py 2017-12-09
05:02:58.000000000 +0100
+++ new/python-keystoneclient-3.15.0/keystoneclient/v3/roles.py 2018-01-24
21:09:04.000000000 +0100
@@ -54,7 +54,7 @@
key = 'role'
deprecation_msg = 'keystoneclient.v3.roles.InferenceRuleManager'
- def _role_grants_base_url(self, user, group, domain, project,
+ def _role_grants_base_url(self, user, group, system, domain, project,
use_inherit_extension):
# When called, we have already checked that only one of user & group
# and one of domain & project have been specified
@@ -66,6 +66,18 @@
elif domain:
params['domain_id'] = base.getid(domain)
base_url = '/domains/%(domain_id)s'
+ elif system:
+ if system == 'all':
+ base_url = '/system'
+ else:
+ # NOTE(lbragstad): If we've made it this far, a user is
+ # attempting to do something with system scope that isn't
+ # supported yet (e.g. 'all' is currently the only supported
+ # system scope). In the future that may change but until then
+ # we should fail like we would if a user provided a bogus
+ # project name or domain ID.
+ msg = _("Only a system scope of 'all' is currently supported")
+ raise exceptions.ValidationError(msg)
if use_inherit_extension:
base_url = '/OS-INHERIT' + base_url
@@ -79,13 +91,26 @@
return base_url % params
- def _require_domain_xor_project(self, domain, project):
- if domain and project:
- msg = _('Specify either a domain or project, not both')
- raise exceptions.ValidationError(msg)
- elif not (domain or project):
- msg = _('Must specify either a domain or project')
- raise exceptions.ValidationError(msg)
+ def _enforce_mutually_exclusive_group(self, system, domain, project):
+ if not system:
+ if domain and project:
+ msg = _('Specify either a domain or project, not both')
+ raise exceptions.ValidationError(msg)
+ elif not (domain or project):
+ msg = _('Must specify either system, domain, or project')
+ raise exceptions.ValidationError(msg)
+ elif system:
+ if domain and project:
+ msg = _(
+ 'Specify either system, domain, or project, not all three.'
+ )
+ raise exceptions.ValidationError(msg)
+ if domain:
+ msg = _('Specify either system or a domain, not both')
+ raise exceptions.ValidationError(msg)
+ if project:
+ msg = _('Specify either a system or project, not both')
+ raise exceptions.ValidationError(msg)
def _require_user_xor_group(self, user, group):
if user and group:
@@ -130,7 +155,7 @@
"""
return super(RoleManager, self).get(role_id=base.getid(role))
- def list(self, user=None, group=None, domain=None,
+ def list(self, user=None, group=None, system=None, domain=None,
project=None, os_inherit_extension_inherited=False, **kwargs):
"""List roles and role grants.
@@ -143,12 +168,12 @@
User and group are mutually exclusive.
:type group: str or :class:`keystoneclient.v3.groups.Group`
:param domain: filter in role grants on the specified domain. Either
- user or group must be specified. Project and domain
- are mutually exclusive.
+ user or group must be specified. Project, domain, and
+ system are mutually exclusive.
:type domain: str or :class:`keystoneclient.v3.domains.Domain`
:param project: filter in role grants on the specified project. Either
- user or group must be specified. Project and domain
- are mutually exclusive.
+ user or group must be specified. Project, domain and
+ system are mutually exclusive.
:type project: str or :class:`keystoneclient.v3.projects.Project`
:param bool os_inherit_extension_inherited: OS-INHERIT will be used.
It provides the ability for
@@ -166,10 +191,12 @@
kwargs['tail'] = '/inherited_to_projects'
if user or group:
self._require_user_xor_group(user, group)
- self._require_domain_xor_project(domain, project)
+ self._enforce_mutually_exclusive_group(system, domain, project)
base_url = self._role_grants_base_url(
- user, group, domain, project, os_inherit_extension_inherited)
+ user, group, system, domain, project,
+ os_inherit_extension_inherited
+ )
return super(RoleManager, self).list(base_url=base_url,
**kwargs)
@@ -208,8 +235,8 @@
return super(RoleManager, self).delete(
role_id=base.getid(role))
- def grant(self, role, user=None, group=None, domain=None, project=None,
- os_inherit_extension_inherited=False, **kwargs):
+ def grant(self, role, user=None, group=None, system=None, domain=None,
+ project=None, os_inherit_extension_inherited=False, **kwargs):
"""Grant a role to a user or group on a domain or project.
:param role: the role to be granted on the server.
@@ -222,13 +249,16 @@
resource. Domain or project must be specified.
User and group are mutually exclusive.
:type group: str or :class:`keystoneclient.v3.groups.Group`
+ :param system: system information to grant the role on. Project,
+ domain, and system are mutually exclusive.
+ :type system: str
:param domain: the domain in which the role will be granted. Either
- user or group must be specified. Project and domain
- are mutually exclusive.
+ user or group must be specified. Project, domain, and
+ system are mutually exclusive.
:type domain: str or :class:`keystoneclient.v3.domains.Domain`
:param project: the project in which the role will be granted. Either
- user or group must be specified. Project and domain
- are mutually exclusive.
+ user or group must be specified. Project, domain, and
+ system are mutually exclusive.
:type project: str or :class:`keystoneclient.v3.projects.Project`
:param bool os_inherit_extension_inherited: OS-INHERIT will be used.
It provides the ability for
@@ -242,20 +272,21 @@
:rtype: :class:`keystoneclient.v3.roles.Role`
"""
- self._require_domain_xor_project(domain, project)
+ self._enforce_mutually_exclusive_group(system, domain, project)
self._require_user_xor_group(user, group)
if os_inherit_extension_inherited:
kwargs['tail'] = '/inherited_to_projects'
base_url = self._role_grants_base_url(
- user, group, domain, project, os_inherit_extension_inherited)
+ user, group, system, domain, project,
+ os_inherit_extension_inherited)
return super(RoleManager, self).put(base_url=base_url,
role_id=base.getid(role),
**kwargs)
- def check(self, role, user=None, group=None, domain=None, project=None,
- os_inherit_extension_inherited=False, **kwargs):
+ def check(self, role, user=None, group=None, system=None, domain=None,
+ project=None, os_inherit_extension_inherited=False, **kwargs):
"""Check if a user or group has a role on a domain or project.
:param user: check for role grants for the specified user on a
@@ -266,13 +297,16 @@
resource. Domain or project must be specified.
User and group are mutually exclusive.
:type group: str or :class:`keystoneclient.v3.groups.Group`
+ :param system: check for role grants on the system. Project, domain,
+ and system are mutually exclusive.
+ :type system: str
:param domain: check for role grants on the specified domain. Either
- user or group must be specified. Project and domain
- are mutually exclusive.
+ user or group must be specified. Project, domain, and
+ system are mutually exclusive.
:type domain: str or :class:`keystoneclient.v3.domains.Domain`
:param project: check for role grants on the specified project. Either
- user or group must be specified. Project and domain
- are mutually exclusive.
+ user or group must be specified. Project, domain, and
+ system are mutually exclusive.
:type project: str or :class:`keystoneclient.v3.projects.Project`
:param bool os_inherit_extension_inherited: OS-INHERIT will be used.
It provides the ability for
@@ -290,22 +324,23 @@
:rtype: :class:`requests.models.Response`
"""
- self._require_domain_xor_project(domain, project)
+ self._enforce_mutually_exclusive_group(system, domain, project)
self._require_user_xor_group(user, group)
if os_inherit_extension_inherited:
kwargs['tail'] = '/inherited_to_projects'
base_url = self._role_grants_base_url(
- user, group, domain, project, os_inherit_extension_inherited)
+ user, group, system, domain, project,
+ os_inherit_extension_inherited)
return super(RoleManager, self).head(
base_url=base_url,
role_id=base.getid(role),
os_inherit_extension_inherited=os_inherit_extension_inherited,
**kwargs)
- def revoke(self, role, user=None, group=None, domain=None, project=None,
- os_inherit_extension_inherited=False, **kwargs):
+ def revoke(self, role, user=None, group=None, system=None, domain=None,
+ project=None, os_inherit_extension_inherited=False, **kwargs):
"""Revoke a role from a user or group on a domain or project.
:param user: revoke role grants for the specified user on a
@@ -316,13 +351,16 @@
resource. Domain or project must be specified.
User and group are mutually exclusive.
:type group: str or :class:`keystoneclient.v3.groups.Group`
+ :param system: revoke role grants on the system. Project, domain, and
+ system are mutually exclusive.
+ :type system: str
:param domain: revoke role grants on the specified domain. Either
- user or group must be specified. Project and domain
- are mutually exclusive.
+ user or group must be specified. Project, domain, and
+ system are mutually exclusive.
:type domain: str or :class:`keystoneclient.v3.domains.Domain`
:param project: revoke role grants on the specified project. Either
- user or group must be specified. Project and domain
- are mutually exclusive.
+ user or group must be specified. Project, domain, and
+ system are mutually exclusive.
:type project: str or :class:`keystoneclient.v3.projects.Project`
:param bool os_inherit_extension_inherited: OS-INHERIT will be used.
It provides the ability for
@@ -336,14 +374,15 @@
:rtype: list of :class:`keystoneclient.v3.roles.Role`
"""
- self._require_domain_xor_project(domain, project)
+ self._enforce_mutually_exclusive_group(system, domain, project)
self._require_user_xor_group(user, group)
if os_inherit_extension_inherited:
kwargs['tail'] = '/inherited_to_projects'
base_url = self._role_grants_base_url(
- user, group, domain, project, os_inherit_extension_inherited)
+ user, group, system, domain, project,
+ os_inherit_extension_inherited)
return super(RoleManager, self).delete(
base_url=base_url,
role_id=base.getid(role),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/python_keystoneclient.egg-info/PKG-INFO
new/python-keystoneclient-3.15.0/python_keystoneclient.egg-info/PKG-INFO
--- old/python-keystoneclient-3.14.0/python_keystoneclient.egg-info/PKG-INFO
2017-12-09 05:06:22.000000000 +0100
+++ new/python-keystoneclient-3.15.0/python_keystoneclient.egg-info/PKG-INFO
2018-01-24 21:10:42.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: python-keystoneclient
-Version: 3.14.0
+Version: 3.15.0
Summary: Client Library for OpenStack Identity
Home-page: https://docs.openstack.org/python-keystoneclient/latest/
Author: OpenStack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/python_keystoneclient.egg-info/SOURCES.txt
new/python-keystoneclient-3.15.0/python_keystoneclient.egg-info/SOURCES.txt
--- old/python-keystoneclient-3.14.0/python_keystoneclient.egg-info/SOURCES.txt
2017-12-09 05:06:23.000000000 +0100
+++ new/python-keystoneclient-3.15.0/python_keystoneclient.egg-info/SOURCES.txt
2018-01-24 21:10:43.000000000 +0100
@@ -16,6 +16,7 @@
tox.ini
doc/.gitignore
doc/Makefile
+doc/requirements.txt
doc/source/conf.py
doc/source/history.rst
doc/source/index.rst
@@ -179,6 +180,7 @@
keystoneclient/tests/unit/v3/client_fixtures.py
keystoneclient/tests/unit/v3/saml2_fixtures.py
keystoneclient/tests/unit/v3/test_access.py
+keystoneclient/tests/unit/v3/test_application_credentials.py
keystoneclient/tests/unit/v3/test_auth.py
keystoneclient/tests/unit/v3/test_auth_manager.py
keystoneclient/tests/unit/v3/test_auth_oidc.py
@@ -222,6 +224,7 @@
keystoneclient/v2_0/tokens.py
keystoneclient/v2_0/users.py
keystoneclient/v3/__init__.py
+keystoneclient/v3/application_credentials.py
keystoneclient/v3/auth.py
keystoneclient/v3/client.py
keystoneclient/v3/credentials.py
@@ -271,6 +274,7 @@
python_keystoneclient.egg-info/top_level.txt
releasenotes/notes/.placeholder
releasenotes/notes/Add-allow-expired-flag-to-validate-25b8914f4deb359b.yaml
+releasenotes/notes/bp-application-credentials-27728ded876d7d5a.yaml
releasenotes/notes/bp-domain-config-9566e672a98f4e7f.yaml
releasenotes/notes/bp-pci-dss-query-password-expired-users-b0c4b1bbdcf33f16.yaml
releasenotes/notes/bug-1616105-cc8b85eb056e99e2.yaml
@@ -280,6 +284,7 @@
releasenotes/notes/implied_roles-ea39d3c3d998d482.yaml
releasenotes/notes/ksc_2.1.0-739ded9c4c3f8aaa.yaml
releasenotes/notes/list_role_assignment_names-7e1b7eb8c2d22d7c.yaml
+releasenotes/notes/project-tags-1f8a32d389951e7a.yaml
releasenotes/notes/remove-credentials-data-46ab3c3c248047cf.yaml
releasenotes/notes/remove-middleware-eef8c40117b465aa.yaml
releasenotes/notes/remove_apiclient_exceptions-0cd5c8d16aa09a22.yaml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/python_keystoneclient.egg-info/pbr.json
new/python-keystoneclient-3.15.0/python_keystoneclient.egg-info/pbr.json
--- old/python-keystoneclient-3.14.0/python_keystoneclient.egg-info/pbr.json
2017-12-09 05:06:22.000000000 +0100
+++ new/python-keystoneclient-3.15.0/python_keystoneclient.egg-info/pbr.json
2018-01-24 21:10:42.000000000 +0100
@@ -1 +1 @@
-{"git_version": "2bea645", "is_release": true}
\ No newline at end of file
+{"git_version": "1e8c930", "is_release": true}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/python_keystoneclient.egg-info/requires.txt
new/python-keystoneclient-3.15.0/python_keystoneclient.egg-info/requires.txt
---
old/python-keystoneclient-3.14.0/python_keystoneclient.egg-info/requires.txt
2017-12-09 05:06:22.000000000 +0100
+++
new/python-keystoneclient-3.15.0/python_keystoneclient.egg-info/requires.txt
2018-01-24 21:10:42.000000000 +0100
@@ -4,7 +4,7 @@
oslo.config>=5.1.0
oslo.i18n>=3.15.3
oslo.serialization!=2.19.1,>=2.18.0
-oslo.utils>=3.31.0
+oslo.utils>=3.33.0
requests>=2.14.2
six>=1.10.0
stevedore>=1.20.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/releasenotes/notes/bp-application-credentials-27728ded876d7d5a.yaml
new/python-keystoneclient-3.15.0/releasenotes/notes/bp-application-credentials-27728ded876d7d5a.yaml
---
old/python-keystoneclient-3.14.0/releasenotes/notes/bp-application-credentials-27728ded876d7d5a.yaml
1970-01-01 01:00:00.000000000 +0100
+++
new/python-keystoneclient-3.15.0/releasenotes/notes/bp-application-credentials-27728ded876d7d5a.yaml
2018-01-24 21:09:04.000000000 +0100
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ Adds support for creating, reading, and deleting application credentials.
+ With application credentials, a user can grant their applications limited
+ access to their cloud resources. Applications can use keystoneauth with
+ the `v3applicationcredential` auth plugin to authenticate with keystone
+ without needing the user's password.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-keystoneclient-3.14.0/releasenotes/notes/project-tags-1f8a32d389951e7a.yaml
new/python-keystoneclient-3.15.0/releasenotes/notes/project-tags-1f8a32d389951e7a.yaml
---
old/python-keystoneclient-3.14.0/releasenotes/notes/project-tags-1f8a32d389951e7a.yaml
1970-01-01 01:00:00.000000000 +0100
+++
new/python-keystoneclient-3.15.0/releasenotes/notes/project-tags-1f8a32d389951e7a.yaml
2018-01-24 21:08:40.000000000 +0100
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ [`blueprint project-tags
<https://blueprints.launchpad.net/keystone/+spec/project-tags>`_]
+ The keystoneclient now supports project tags feature in keystone. This
+ allows operators to use the client to associate tags to a project,
+ retrieve tags associated with a project, delete tags associated with a
+ project, and filter projects based on tags.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-keystoneclient-3.14.0/requirements.txt
new/python-keystoneclient-3.15.0/requirements.txt
--- old/python-keystoneclient-3.14.0/requirements.txt 2017-12-09
05:02:58.000000000 +0100
+++ new/python-keystoneclient-3.15.0/requirements.txt 2018-01-24
21:08:40.000000000 +0100
@@ -9,7 +9,7 @@
oslo.config>=5.1.0 # Apache-2.0
oslo.i18n>=3.15.3 # Apache-2.0
oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
-oslo.utils>=3.31.0 # Apache-2.0
+oslo.utils>=3.33.0 # Apache-2.0
requests>=2.14.2 # Apache-2.0
six>=1.10.0 # MIT
stevedore>=1.20.0 # Apache-2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-keystoneclient-3.14.0/test-requirements.txt
new/python-keystoneclient-3.15.0/test-requirements.txt
--- old/python-keystoneclient-3.14.0/test-requirements.txt 2017-12-09
05:02:58.000000000 +0100
+++ new/python-keystoneclient-3.15.0/test-requirements.txt 2018-01-24
21:08:40.000000000 +0100
@@ -11,11 +11,8 @@
lxml!=3.7.0,>=3.4.1 # BSD
mock>=2.0.0 # BSD
oauthlib>=0.6.0 # BSD
-openstackdocstheme>=1.17.0 # Apache-2.0
-oslotest>=1.10.0 # Apache-2.0
-reno>=2.5.0 # Apache-2.0
+oslotest>=3.2.0 # Apache-2.0
requests-mock>=1.1.0 # Apache-2.0
-sphinx>=1.6.2 # BSD
tempest>=17.1.0 # Apache-2.0
testrepository>=0.0.18 # Apache-2.0/BSD
testresources>=2.0.0 # Apache-2.0/BSD
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-keystoneclient-3.14.0/tox.ini
new/python-keystoneclient-3.15.0/tox.ini
--- old/python-keystoneclient-3.14.0/tox.ini 2017-12-09 05:02:58.000000000
+0100
+++ new/python-keystoneclient-3.15.0/tox.ini 2018-01-24 21:08:40.000000000
+0100
@@ -55,11 +55,12 @@
exclude = .venv,.tox,dist,doc,*egg,build
[testenv:docs]
-commands=
- python setup.py build_sphinx
+commands = python setup.py build_sphinx
+deps = -r{toxinidir}/doc/requirements.txt
[testenv:releasenotes]
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html
releasenotes/source releasenotes/build/html
+deps = -r{toxinidir}/doc/requirements.txt
[hacking]
import_exceptions =