From 64e4520b2a79b9046a791f5e3729f5cbfc2d3fa5 Mon Sep 17 00:00:00 2001 From: Nicolas Belouin Date: Fri, 14 Jan 2022 15:44:11 +0100 Subject: [PATCH] Add trustor and trustee filtering to trusts list The keystone API supports filtering trusts by trustor and/or trustee. Also adds a shortcut parameter to get trusts with current user as trustee or trustor. Signed-off-by: Nicolas Belouin Change-Id: I00ed2b68cf8ada214a59f640f4f0a5c9dbc37063 --- openstackclient/identity/v3/trust.py | 87 +++++++++++++- .../tests/unit/identity/v3/test_trust.py | 108 +++++++++++++++++- ...sing-trust-list-opts-500fd1e4c14e1504.yaml | 9 ++ 3 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/add-missing-trust-list-opts-500fd1e4c14e1504.yaml diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py index cd3a65d0da..61273f410d 100644 --- a/openstackclient/identity/v3/trust.py +++ b/openstackclient/identity/v3/trust.py @@ -176,10 +176,95 @@ class DeleteTrust(command.Command): class ListTrust(command.Lister): _description = _("List trusts") + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + '--trustor', + metavar='', + help=_('Trustor user to filter (name or ID)'), + ) + parser.add_argument( + '--trustee', + metavar='', + help=_('Trustee user to filter (name or ID)'), + ) + parser.add_argument( + '--trustor-domain', + metavar='', + help=_('Domain that contains (name or ID)'), + ) + parser.add_argument( + '--trustee-domain', + metavar='', + help=_('Domain that contains (name or ID)'), + ) + parser.add_argument( + '--auth-user', + action="store_true", + dest='authuser', + help=_('Only list trusts related to the authenticated user'), + ) + return parser + def take_action(self, parsed_args): + identity_client = self.app.client_manager.identity + auth_ref = self.app.client_manager.auth_ref + + if parsed_args.authuser and any([ + parsed_args.trustor, + parsed_args.trustor_domain, + parsed_args.trustee, + parsed_args.trustee_domain, + ]): + msg = _("--authuser cannot be used with --trustee or --trustor") + raise exceptions.CommandError(msg) + + if parsed_args.trustee_domain and not parsed_args.trustee: + msg = _("Using --trustee-domain mandates the use of --trustee") + raise exceptions.CommandError(msg) + + if parsed_args.trustor_domain and not parsed_args.trustor: + msg = _("Using --trustor-domain mandates the use of --trustor") + raise exceptions.CommandError(msg) + + if parsed_args.authuser: + if auth_ref: + user = common.find_user( + identity_client, + auth_ref.user_id + ) + # We need two calls here as we want trusts with + # either the trustor or the trustee set to current user + # using a single call would give us trusts with both + # trustee and trustor set to current user + data1 = identity_client.trusts.list(trustor_user=user) + data2 = identity_client.trusts.list(trustee_user=user) + data = set(data1 + data2) + else: + trustor = None + if parsed_args.trustor: + trustor = common.find_user( + identity_client, + parsed_args.trustor, + parsed_args.trustor_domain, + ) + + trustee = None + if parsed_args.trustee: + trustee = common.find_user( + identity_client, + parsed_args.trustor, + parsed_args.trustor_domain, + ) + + data = self.app.client_manager.identity.trusts.list( + trustor_user=trustor, + trustee_user=trustee, + ) + columns = ('ID', 'Expires At', 'Impersonation', 'Project ID', 'Trustee User ID', 'Trustor User ID') - data = self.app.client_manager.identity.trusts.list() + return (columns, (utils.get_item_properties( s, columns, diff --git a/openstackclient/tests/unit/identity/v3/test_trust.py b/openstackclient/tests/unit/identity/v3/test_trust.py index d8cfc59fe8..d530adf5a5 100644 --- a/openstackclient/tests/unit/identity/v3/test_trust.py +++ b/openstackclient/tests/unit/identity/v3/test_trust.py @@ -206,7 +206,113 @@ class TestTrustList(TestTrust): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.trusts_mock.list.assert_called_with() + self.trusts_mock.list.assert_called_with( + trustor_user=None, + trustee_user=None, + ) + + collist = ('ID', 'Expires At', 'Impersonation', 'Project ID', + 'Trustee User ID', 'Trustor User ID') + self.assertEqual(collist, columns) + datalist = (( + identity_fakes.trust_id, + identity_fakes.trust_expires, + identity_fakes.trust_impersonation, + identity_fakes.project_id, + identity_fakes.user_id, + identity_fakes.user_id + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_trust_list_auth_user(self): + auth_ref = self.app.client_manager.auth_ref = mock.Mock() + auth_ref.user_id.return_value = identity_fakes.user_id + + arglist = ['--auth-user'] + verifylist = [ + ('trustor', None), + ('trustee', None), + ('authuser', 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.trusts_mock.list.assert_any_call( + trustor_user=self.users_mock.get() + ) + self.trusts_mock.list.assert_any_call( + trustee_user=self.users_mock.get() + ) + + collist = ('ID', 'Expires At', 'Impersonation', 'Project ID', + 'Trustee User ID', 'Trustor User ID') + self.assertEqual(collist, columns) + datalist = (( + identity_fakes.trust_id, + identity_fakes.trust_expires, + identity_fakes.trust_impersonation, + identity_fakes.project_id, + identity_fakes.user_id, + identity_fakes.user_id + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_trust_list_trustee(self): + arglist = ['--trustee', identity_fakes.user_name] + verifylist = [ + ('trustor', None), + ('trustee', identity_fakes.user_name), + ('authuser', False), + ] + 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) + + print(self.trusts_mock.list.call_args_list) + self.trusts_mock.list.assert_any_call( + trustee_user=self.users_mock.get(), + trustor_user=None, + ) + + collist = ('ID', 'Expires At', 'Impersonation', 'Project ID', + 'Trustee User ID', 'Trustor User ID') + self.assertEqual(collist, columns) + datalist = (( + identity_fakes.trust_id, + identity_fakes.trust_expires, + identity_fakes.trust_impersonation, + identity_fakes.project_id, + identity_fakes.user_id, + identity_fakes.user_id + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_trust_list_trustor(self): + arglist = ['--trustor', identity_fakes.user_name] + verifylist = [ + ('trustee', None), + ('trustor', identity_fakes.user_name), + ('authuser', False), + ] + 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) + + print(self.trusts_mock.list.call_args_list) + self.trusts_mock.list.assert_any_call( + trustor_user=self.users_mock.get(), + trustee_user=None, + ) collist = ('ID', 'Expires At', 'Impersonation', 'Project ID', 'Trustee User ID', 'Trustor User ID') diff --git a/releasenotes/notes/add-missing-trust-list-opts-500fd1e4c14e1504.yaml b/releasenotes/notes/add-missing-trust-list-opts-500fd1e4c14e1504.yaml new file mode 100644 index 0000000000..8918dd8777 --- /dev/null +++ b/releasenotes/notes/add-missing-trust-list-opts-500fd1e4c14e1504.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Add missing ``--trustee``, ``--trustee-domain``, ``--trustor``, + ``--trustor-domain`` options to ``trust list`` command, to allow + filtering trusts by trustee and trustor. + - | + Add ``--authuser`` option to ``trust list`` command, to allow + displaying only trusts related to current authenticated user