From 49f6032b699804b1b0ed56137ab14ba266251157 Mon Sep 17 00:00:00 2001 From: adrian-turjak Date: Mon, 26 Sep 2016 13:06:42 +1300 Subject: [PATCH] Non-Admin can't list own projects Due to a default Keystone policy until Newtown, and the use of resource_find, non-admins are unable to list their own projects. This patch bypasses this problem while also introducing better UX for non-admins wishing to get their project list. 'openstack project list' retains the default of 'list all projects' but on a forbidden error will default instead to 'list my projects'. This way for non-admins 'list my projects' feels like the default without breaking the expected admin default. Adding the '--my-projects' option allows admins to easily list their own projects or allows non-admins to be explicit and bypass the forbidden error fallback. Change-Id: I1021276f69fbbf28e13e17c4e567d932fce7ed8b Closes-Bug: #1627555 --- doc/source/command-objects/project.rst | 7 +++++ openstackclient/identity/v3/project.py | 24 ++++++++++++++- .../tests/unit/identity/v3/test_project.py | 30 +++++++++++++++++++ .../notes/bug-1627555-3b47eba215e35b3c.yaml | 9 ++++++ 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/bug-1627555-3b47eba215e35b3c.yaml diff --git a/doc/source/command-objects/project.rst b/doc/source/command-objects/project.rst index 018cea3e0e..cb0941ca12 100644 --- a/doc/source/command-objects/project.rst +++ b/doc/source/command-objects/project.rst @@ -95,6 +95,7 @@ List projects openstack project list [--domain ] [--user ] + [--my-projects] [--long] [--sort [:,:,..]] @@ -110,6 +111,12 @@ List projects .. versionadded:: 3 +.. option:: --my-projects + + List projects for the authenticated user. Supersedes other filters. + + .. versionadded:: 3 + .. option:: --long List additional fields in output diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 473dda1a20..873ee9c73e 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -188,6 +188,12 @@ class ListProject(command.Lister): metavar='', help=_('Filter projects by (name or ID)'), ) + parser.add_argument( + '--my-projects', + action='store_true', + help=_('List projects for the authenticated user. ' + 'Supersedes other filters.'), + ) parser.add_argument( '--long', action='store_true', @@ -228,9 +234,25 @@ class ListProject(command.Lister): kwargs['user'] = user_id - data = identity_client.projects.list(**kwargs) + if parsed_args.my_projects: + # NOTE(adriant): my-projects supersedes all the other filters. + kwargs = {'user': self.app.client_manager.auth_ref.user_id} + + try: + data = identity_client.projects.list(**kwargs) + except ks_exc.Forbidden: + # NOTE(adriant): if no filters, assume a forbidden is non-admin + # wanting their own project list. + if not kwargs: + user = self.app.client_manager.auth_ref.user_id + data = identity_client.projects.list( + user=user) + else: + raise + if parsed_args.sort: data = utils.sort_items(data, parsed_args.sort) + return (columns, (utils.get_item_properties( s, columns, diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py index a27bf2a509..7be81153c4 100644 --- a/openstackclient/tests/unit/identity/v3/test_project.py +++ b/openstackclient/tests/unit/identity/v3/test_project.py @@ -617,6 +617,36 @@ class TestProjectList(TestProject): self.assertEqual(datalists, tuple(data)) + def test_project_list_my_projects(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN_WITH_PROJECT_ID, + ) + ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = ar_mock + + arglist = [ + '--my-projects', + ] + verifylist = [ + ('my_projects', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.projects_mock.list.assert_called_with( + user=self.app.client_manager.auth_ref.user_id) + + collist = ('ID', 'Name') + self.assertEqual(collist, columns) + datalist = (( + self.project.id, + self.project.name, + ), ) + self.assertEqual(datalist, tuple(data)) + class TestProjectSet(TestProject): diff --git a/releasenotes/notes/bug-1627555-3b47eba215e35b3c.yaml b/releasenotes/notes/bug-1627555-3b47eba215e35b3c.yaml new file mode 100644 index 0000000000..6000905f3d --- /dev/null +++ b/releasenotes/notes/bug-1627555-3b47eba215e35b3c.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + The ``project list`` command lists all projects when called by an + admin user. For non-admin users it will now list projects for the + authenticated user instead of exiting with an authorization failure. + The ``--my-projects`` option has also been added to the ``project list`` + command to allow admin users to list their own projects. + [Bug `1627555 `_]