From 05da145eaee329e299b449ba2d7ea88d1325e432 Mon Sep 17 00:00:00 2001 From: Vishakha Agarwal Date: Thu, 5 Dec 2019 16:48:16 +0530 Subject: Adding options to user cli User options [1] can be set by making POST and PATCH request for /v3/users API calls but cannot by openstack CLI because of no user options defined in create and update user CLI [2]. This patch adds the user options [1] in create user and update user CLI. [1] https://docs.openstack.org/keystone/latest/admin/resource-options.html#multi-factor-auth-rules [2] https://docs.openstack.org/api-ref/identity/v3/#create-user Change-Id: I4e41bae2e8cfbe92d52b14d856991bedcd44164f --- openstackclient/identity/v3/user.py | 120 +- openstackclient/tests/unit/identity/v3/fakes.py | 3 + .../tests/unit/identity/v3/test_user.py | 1326 ++++++++++++++++---- 3 files changed, 1211 insertions(+), 238 deletions(-) (limited to 'openstackclient') diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index ca85c5d8..cbc112a0 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -30,6 +30,114 @@ from openstackclient.identity import common LOG = logging.getLogger(__name__) +def _get_options_for_user(identity_client, parsed_args): + options = {} + if parsed_args.ignore_lockout_failure_attempts: + options['ignore_lockout_failure_attempts'] = True + if parsed_args.no_ignore_lockout_failure_attempts: + options['ignore_lockout_failure_attempts'] = False + if parsed_args.ignore_password_expiry: + options['ignore_password_expiry'] = True + if parsed_args.no_ignore_password_expiry: + options['ignore_password_expiry'] = False + if parsed_args.ignore_change_password_upon_first_use: + options['ignore_change_password_upon_first_use'] = True + if parsed_args.no_ignore_change_password_upon_first_use: + options['ignore_change_password_upon_first_use'] = False + if parsed_args.enable_lock_password: + options['lock_password'] = True + if parsed_args.disable_lock_password: + options['lock_password'] = False + if parsed_args.enable_multi_factor_auth: + options['multi_factor_auth_enabled'] = True + if parsed_args.disable_multi_factor_auth: + options['multi_factor_auth_enabled'] = False + if parsed_args.multi_factor_auth_rule: + auth_rules = [rule.split(",") for rule in + parsed_args.multi_factor_auth_rule] + if auth_rules: + options['multi_factor_auth_rules'] = auth_rules + return options + + +def _add_user_options(parser): + # Add additional user options + + parser.add_argument( + '--ignore-lockout-failure-attempts', + action="store_true", + help=_('Opt into ignoring the number of times a user has ' + 'authenticated and locking out the user as a result'), + ) + parser.add_argument( + '--no-ignore-lockout-failure-attempts', + action="store_true", + help=_('Opt out of ignoring the number of times a user has ' + 'authenticated and locking out the user as a result'), + ) + parser.add_argument( + '--ignore-password-expiry', + action="store_true", + help=_('Opt into allowing user to continue using passwords that ' + 'may be expired'), + ) + parser.add_argument( + '--no-ignore-password-expiry', + action="store_true", + help=_('Opt out of allowing user to continue using passwords ' + 'that may be expired'), + ) + parser.add_argument( + '--ignore-change-password-upon-first-use', + action="store_true", + help=_('Control if a user should be forced to change their password ' + 'immediately after they log into keystone for the first time. ' + 'Opt into ignoring the user to change their password during ' + 'first time login in keystone'), + ) + parser.add_argument( + '--no-ignore-change-password-upon-first-use', + action="store_true", + help=_('Control if a user should be forced to change their password ' + 'immediately after they log into keystone for the first time. ' + 'Opt out of ignoring the user to change their password during ' + 'first time login in keystone'), + ) + parser.add_argument( + '--enable-lock-password', + action="store_true", + help=_('Disables the ability for a user to change its password ' + 'through self-service APIs'), + ) + parser.add_argument( + '--disable-lock-password', + action="store_true", + help=_('Enables the ability for a user to change its password ' + 'through self-service APIs'), + ) + parser.add_argument( + '--enable-multi-factor-auth', + action="store_true", + help=_('Enables the MFA (Multi Factor Auth)'), + ) + parser.add_argument( + '--disable-multi-factor-auth', + action="store_true", + help=_('Disables the MFA (Multi Factor Auth)'), + ) + parser.add_argument( + '--multi-factor-auth-rule', + metavar='', + action="append", + default=[], + help=_('Set multi-factor auth rules. For example, to set a rule ' + 'requiring the "password" and "totp" auth methods to be ' + 'provided, use: "--multi-factor-auth-rule password,totp". ' + 'May be provided multiple times to set different rule ' + 'combinations.') + ) + + class CreateUser(command.ShowOne): _description = _("Create new user") @@ -72,6 +180,8 @@ class CreateUser(command.ShowOne): metavar='', help=_('User description'), ) + _add_user_options(parser) + enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', @@ -113,6 +223,7 @@ class CreateUser(command.ShowOne): if not parsed_args.password: LOG.warning(_("No password was supplied, authentication will fail " "when a user does not have a password.")) + options = _get_options_for_user(identity_client, parsed_args) try: user = identity_client.users.create( @@ -122,7 +233,8 @@ class CreateUser(command.ShowOne): password=parsed_args.password, email=parsed_args.email, description=parsed_args.description, - enabled=enabled + enabled=enabled, + options=options, ) except ks_exc.Conflict: if parsed_args.or_show: @@ -333,6 +445,8 @@ class SetUser(command.Command): metavar='', help=_('Set user description'), ) + _add_user_options(parser) + enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', @@ -390,6 +504,10 @@ class SetUser(command.Command): if parsed_args.disable: kwargs['enabled'] = False + options = _get_options_for_user(identity_client, parsed_args) + if options: + kwargs['options'] = options + identity_client.users.update(user.id, **kwargs) diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py index eb3ce2a3..58d5d14d 100644 --- a/openstackclient/tests/unit/identity/v3/fakes.py +++ b/openstackclient/tests/unit/identity/v3/fakes.py @@ -108,6 +108,9 @@ MAPPING_RESPONSE_2 = { "rules": MAPPING_RULES_2 } +mfa_opt1 = 'password,totp' +mfa_opt2 = 'password' + project_id = '8-9-64' project_name = 'beatles' project_description = 'Fab Four' diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index 4b14bca0..c71435ba 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -111,6 +111,7 @@ class TestUserCreate(TestUser): 'description': None, 'domain': None, 'email': None, + 'options': {}, 'enabled': True, 'password': None, } @@ -150,6 +151,7 @@ class TestUserCreate(TestUser): 'description': None, 'domain': None, 'email': None, + 'options': {}, 'enabled': True, 'password': 'secret', } @@ -190,6 +192,7 @@ class TestUserCreate(TestUser): 'description': None, 'domain': None, 'email': None, + 'options': {}, 'enabled': True, 'password': 'abc123', } @@ -228,6 +231,7 @@ class TestUserCreate(TestUser): 'domain': None, 'email': 'barney@example.com', 'enabled': True, + 'options': {}, 'password': None, } # UserManager.create(name=, domain=, project=, password=, email=, @@ -265,6 +269,7 @@ class TestUserCreate(TestUser): 'domain': None, 'email': None, 'enabled': True, + 'options': {}, 'password': None, } # UserManager.create(name=, domain=, project=, password=, email=, @@ -311,6 +316,7 @@ class TestUserCreate(TestUser): 'description': None, 'domain': None, 'email': None, + 'options': {}, 'enabled': True, 'password': None, } @@ -356,6 +362,7 @@ class TestUserCreate(TestUser): 'description': None, 'domain': self.domain.id, 'email': None, + 'options': {}, 'enabled': True, 'password': None, } @@ -392,6 +399,7 @@ class TestUserCreate(TestUser): 'description': None, 'domain': None, 'email': None, + 'options': {}, 'enabled': True, 'password': None, } @@ -428,6 +436,7 @@ class TestUserCreate(TestUser): 'description': None, 'domain': None, 'email': None, + 'options': {}, 'enabled': False, 'password': None, } @@ -438,280 +447,1112 @@ class TestUserCreate(TestUser): self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) + def test_user_create_ignore_lockout_failure_attempts(self): + arglist = [ + '--ignore-lockout-failure-attempts', + self.user.name, + ] + verifylist = [ + ('ignore_lockout_failure_attempts', True), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) -class TestUserDelete(TestUser): - - user = identity_fakes.FakeUser.create_one_user() - - def setUp(self): - super(TestUserDelete, self).setUp() + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) - # This is the return value for utils.find_resource() - self.users_mock.get.return_value = self.user - self.users_mock.delete.return_value = None + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'options': {'ignore_lockout_failure_attempts': True}, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) - # Get the command object to test - self.cmd = user.DeleteUser(self.app, None) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) - def test_user_delete_no_options(self): + def test_user_create_no_ignore_lockout_failure_attempts(self): arglist = [ - self.user.id, + '--no-ignore-lockout-failure-attempts', + self.user.name, ] verifylist = [ - ('users', [self.user.id]), + ('no_ignore_lockout_failure_attempts', True), + ('enable', False), + ('disable', False), + ('name', self.user.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) - self.users_mock.delete.assert_called_with( - self.user.id, + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'options': {'ignore_lockout_failure_attempts': False}, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs ) - self.assertIsNone(result) - @mock.patch.object(utils, 'find_resource') - def test_delete_multi_users_with_exception(self, find_mock): - find_mock.side_effect = [self.user, - exceptions.CommandError] + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_ignore_password_expiry(self): arglist = [ - self.user.id, - 'unexist_user', + '--ignore-password-expiry', + self.user.name, ] verifylist = [ - ('users', arglist), + ('ignore_password_expiry', True), + ('enable', False), + ('disable', False), + ('name', self.user.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual('1 of 2 users failed to delete.', - str(e)) + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) - find_mock.assert_any_call(self.users_mock, self.user.id) - find_mock.assert_any_call(self.users_mock, 'unexist_user') + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'options': {'ignore_password_expiry': True}, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) - self.assertEqual(2, find_mock.call_count) - self.users_mock.delete.assert_called_once_with(self.user.id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + def test_user_create_no_ignore_password_expiry(self): + arglist = [ + '--no-ignore-password-expiry', + self.user.name, + ] + verifylist = [ + ('no_ignore_password_expiry', True), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) -class TestUserList(TestUser): + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) - domain = identity_fakes.FakeDomain.create_one_domain() - project = identity_fakes.FakeProject.create_one_project() - user = identity_fakes.FakeUser.create_one_user( - attrs={'domain_id': domain.id, - 'default_project_id': project.id} - ) - group = identity_fakes.FakeGroup.create_one_group() - role_assignment = ( - identity_fakes.FakeRoleAssignment.create_one_role_assignment( - attrs={'user': {'id': user.id}})) + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'options': {'ignore_password_expiry': False}, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) - columns = [ - 'ID', - 'Name' - ] - datalist = ( - ( - user.id, - user.name, - ), - ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) - def setUp(self): - super(TestUserList, self).setUp() + def test_user_create_ignore_change_password_upon_first_use(self): + arglist = [ + '--ignore-change-password-upon-first-use', + self.user.name, + ] + verifylist = [ + ('ignore_change_password_upon_first_use', True), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.users_mock.get.return_value = self.user - self.users_mock.list.return_value = [self.user] - self.domains_mock.get.return_value = self.domain - self.groups_mock.get.return_value = self.group - self.projects_mock.get.return_value = self.project - self.role_assignments_mock.list.return_value = [self.role_assignment] + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) - # Get the command object to test - self.cmd = user.ListUser(self.app, None) + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'options': {'ignore_change_password_upon_first_use': True}, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) - def test_user_list_no_options(self): - arglist = [] - verifylist = [] + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_no_ignore_change_password_upon_first_use(self): + arglist = [ + '--no-ignore-change-password-upon-first-use', + self.user.name, + ] + verifylist = [ + ('no_ignore_change_password_upon_first_use', True), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] 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. + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. columns, data = self.cmd.take_action(parsed_args) # Set expected values kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, 'domain': None, - 'group': None, + 'email': None, + 'enabled': True, + 'options': {'ignore_change_password_upon_first_use': False}, + 'password': None, } - - self.users_mock.list.assert_called_with( + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( **kwargs ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, tuple(data)) + self.assertEqual(self.datalist, data) + + def test_user_create_enables_lock_password(self): + arglist = [ + '--enable-lock-password', + self.user.name, + ] + verifylist = [ + ('enable_lock_password', True), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'options': {'lock_password': True}, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_disables_lock_password(self): + arglist = [ + '--disable-lock-password', + self.user.name, + ] + verifylist = [ + ('disable_lock_password', True), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'options': {'lock_password': False}, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_enable_multi_factor_auth(self): + arglist = [ + '--enable-multi-factor-auth', + self.user.name, + ] + verifylist = [ + ('enable_multi_factor_auth', True), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'options': {'multi_factor_auth_enabled': True}, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_disable_multi_factor_auth(self): + arglist = [ + '--disable-multi-factor-auth', + self.user.name, + ] + verifylist = [ + ('disable_multi_factor_auth', True), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'options': {'multi_factor_auth_enabled': False}, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_option_with_multi_factor_auth_rule(self): + arglist = [ + '--multi-factor-auth-rule', identity_fakes.mfa_opt1, + '--multi-factor-auth-rule', identity_fakes.mfa_opt2, + self.user.name, + ] + verifylist = [ + ('multi_factor_auth_rule', [identity_fakes.mfa_opt1, + identity_fakes.mfa_opt2]), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'options': {'multi_factor_auth_rules': [["password", "totp"], + ["password"]]}, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_with_multiple_options(self): + arglist = [ + '--ignore-password-expiry', + '--disable-multi-factor-auth', + '--multi-factor-auth-rule', identity_fakes.mfa_opt1, + self.user.name, + ] + verifylist = [ + ('ignore_password_expiry', True), + ('disable_multi_factor_auth', True), + ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'options': {'ignore_password_expiry': True, + 'multi_factor_auth_enabled': False, + 'multi_factor_auth_rules': [["password", "totp"]]}, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + +class TestUserDelete(TestUser): + + user = identity_fakes.FakeUser.create_one_user() + + def setUp(self): + super(TestUserDelete, self).setUp() + + # This is the return value for utils.find_resource() + self.users_mock.get.return_value = self.user + self.users_mock.delete.return_value = None + + # Get the command object to test + self.cmd = user.DeleteUser(self.app, None) + + def test_user_delete_no_options(self): + arglist = [ + self.user.id, + ] + verifylist = [ + ('users', [self.user.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.users_mock.delete.assert_called_with( + self.user.id, + ) + self.assertIsNone(result) + + @mock.patch.object(utils, 'find_resource') + def test_delete_multi_users_with_exception(self, find_mock): + find_mock.side_effect = [self.user, + exceptions.CommandError] + arglist = [ + self.user.id, + 'unexist_user', + ] + verifylist = [ + ('users', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 users failed to delete.', + str(e)) + + find_mock.assert_any_call(self.users_mock, self.user.id) + find_mock.assert_any_call(self.users_mock, 'unexist_user') + + self.assertEqual(2, find_mock.call_count) + self.users_mock.delete.assert_called_once_with(self.user.id) + + +class TestUserList(TestUser): + + domain = identity_fakes.FakeDomain.create_one_domain() + project = identity_fakes.FakeProject.create_one_project() + user = identity_fakes.FakeUser.create_one_user( + attrs={'domain_id': domain.id, + 'default_project_id': project.id} + ) + group = identity_fakes.FakeGroup.create_one_group() + role_assignment = ( + identity_fakes.FakeRoleAssignment.create_one_role_assignment( + attrs={'user': {'id': user.id}})) + + columns = [ + 'ID', + 'Name' + ] + datalist = ( + ( + user.id, + user.name, + ), + ) + + def setUp(self): + super(TestUserList, self).setUp() + + self.users_mock.get.return_value = self.user + self.users_mock.list.return_value = [self.user] + self.domains_mock.get.return_value = self.domain + self.groups_mock.get.return_value = self.group + self.projects_mock.get.return_value = self.project + self.role_assignments_mock.list.return_value = [self.role_assignment] + + # Get the command object to test + self.cmd = user.ListUser(self.app, None) + + def test_user_list_no_options(self): + arglist = [] + verifylist = [] + 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) + + # Set expected values + kwargs = { + 'domain': None, + 'group': None, + } + + self.users_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_user_list_domain(self): + arglist = [ + '--domain', self.domain.id, + ] + verifylist = [ + ('domain', self.domain.id), + ] + 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) + + # Set expected values + kwargs = { + 'domain': self.domain.id, + 'group': None, + } + + self.users_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_user_list_group(self): + arglist = [ + '--group', self.group.name, + ] + verifylist = [ + ('group', self.group.name), + ] + 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) + + # Set expected values + kwargs = { + 'domain': None, + 'group': self.group.id, + } + + self.users_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_user_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', 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) + + # Set expected values + kwargs = { + 'domain': None, + 'group': None, + } + + self.users_mock.list.assert_called_with( + **kwargs + ) + + collist = [ + 'ID', + 'Name', + 'Project', + 'Domain', + 'Description', + 'Email', + 'Enabled', + ] + self.assertEqual(collist, columns) + datalist = ( + ( + self.user.id, + self.user.name, + self.project.id, + self.domain.id, + '', + self.user.email, + True, + ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_user_list_project(self): + arglist = [ + '--project', self.project.name, + ] + verifylist = [ + ('project', self.project.name), + ] + 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) + + kwargs = { + 'project': self.project.id, + } + + self.role_assignments_mock.list.assert_called_with(**kwargs) + self.users_mock.get.assert_called_with(self.user.id) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + +class TestUserSet(TestUser): + + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() + user = identity_fakes.FakeUser.create_one_user( + attrs={'default_project_id': project.id} + ) + user2 = identity_fakes.FakeUser.create_one_user( + attrs={'default_project_id': project.id, + 'domain_id': domain.id} + ) + + def setUp(self): + super(TestUserSet, self).setUp() + + self.projects_mock.get.return_value = self.project + self.users_mock.get.return_value = self.user + self.users_mock.update.return_value = self.user + + # Get the command object to test + self.cmd = user.SetUser(self.app, None) + + def test_user_set_no_options(self): + arglist = [ + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + def test_user_set_name(self): + arglist = [ + '--name', 'qwerty', + self.user.name, + ] + verifylist = [ + ('name', 'qwerty'), + ('password', None), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'name': 'qwerty', + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_specify_domain(self): + arglist = [ + '--name', 'qwerty', + '--domain', self.domain.id, + self.user2.name + ] + verifylist = [ + ('name', 'qwerty'), + ('password', None), + ('domain', self.domain.id), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.user2.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'enabled': True, + 'name': 'qwerty' + } + + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_password(self): + arglist = [ + '--password', 'secret', + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', 'secret'), + ('password_prompt', False), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'password': 'secret', + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_password_prompt(self): + arglist = [ + '--password-prompt', + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('password_prompt', True), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + mocker = mock.Mock() + mocker.return_value = 'abc123' + with mock.patch("osc_lib.utils.get_password", mocker): + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'password': 'abc123', + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_email(self): + arglist = [ + '--email', 'barney@example.com', + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', 'barney@example.com'), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'email': 'barney@example.com', + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_project(self): + arglist = [ + '--project', self.project.id, + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', None), + ('project', self.project.id), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'default_project': self.project.id, + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_project_domain(self): + arglist = [ + '--project', self.project.id, + '--project-domain', self.project.domain_id, + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', None), + ('project', self.project.id), + ('project_domain', self.project.domain_id), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'default_project': self.project.id, + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) - def test_user_list_domain(self): + def test_user_set_enable(self): arglist = [ - '--domain', self.domain.id, + '--enable', + self.user.name, ] verifylist = [ - ('domain', self.domain.id), + ('name', None), + ('password', None), + ('email', None), + ('project', None), + ('enable', True), + ('disable', False), + ('user', self.user.name), ] 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) + result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'domain': self.domain.id, - 'group': None, + 'enabled': True, } - - self.users_mock.list.assert_called_with( + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, **kwargs ) + self.assertIsNone(result) - self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, tuple(data)) - - def test_user_list_group(self): + def test_user_set_disable(self): arglist = [ - '--group', self.group.name, + '--disable', + self.user.name, ] verifylist = [ - ('group', self.group.name), + ('name', None), + ('password', None), + ('email', None), + ('project', None), + ('enable', False), + ('disable', True), + ('user', self.user.name), ] 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) + result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'domain': None, - 'group': self.group.id, + 'enabled': False, } - - self.users_mock.list.assert_called_with( + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, **kwargs ) + self.assertIsNone(result) - self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, tuple(data)) - - def test_user_list_long(self): + def test_user_set_ignore_lockout_failure_attempts(self): arglist = [ - '--long', + '--ignore-lockout-failure-attempts', + self.user.name, ] verifylist = [ - ('long', True), + ('name', None), + ('password', None), + ('email', None), + ('ignore_lockout_failure_attempts', True), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.user.name), ] 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) - + result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'domain': None, - 'group': None, + 'enabled': True, + 'options': {'ignore_lockout_failure_attempts': True}, } - - self.users_mock.list.assert_called_with( + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, **kwargs ) + self.assertIsNone(result) - collist = [ - 'ID', - 'Name', - 'Project', - 'Domain', - 'Description', - 'Email', - 'Enabled', - ] - self.assertEqual(collist, columns) - datalist = ( - ( - self.user.id, - self.user.name, - self.project.id, - self.domain.id, - '', - self.user.email, - True, - ), - ) - self.assertEqual(datalist, tuple(data)) - - def test_user_list_project(self): + def test_user_set_no_ignore_lockout_failure_attempts(self): arglist = [ - '--project', self.project.name, + '--no-ignore-lockout-failure-attempts', + self.user.name, ] verifylist = [ - ('project', self.project.name), + ('name', None), + ('password', None), + ('email', None), + ('no_ignore_lockout_failure_attempts', True), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.user.name), ] 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) - + result = self.cmd.take_action(parsed_args) + # Set expected values kwargs = { - 'project': self.project.id, + 'enabled': True, + 'options': {'ignore_lockout_failure_attempts': False}, } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) - self.role_assignments_mock.list.assert_called_with(**kwargs) - self.users_mock.get.assert_called_with(self.user.id) - - self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, tuple(data)) - - -class TestUserSet(TestUser): - - project = identity_fakes.FakeProject.create_one_project() - domain = identity_fakes.FakeDomain.create_one_domain() - user = identity_fakes.FakeUser.create_one_user( - attrs={'default_project_id': project.id} - ) - user2 = identity_fakes.FakeUser.create_one_user( - attrs={'default_project_id': project.id, - 'domain_id': domain.id} - ) - - def setUp(self): - super(TestUserSet, self).setUp() - - self.projects_mock.get.return_value = self.project - self.users_mock.get.return_value = self.user - self.users_mock.update.return_value = self.user - - # Get the command object to test - self.cmd = user.SetUser(self.app, None) - - def test_user_set_no_options(self): + def test_user_set_ignore_password_expiry(self): arglist = [ + '--ignore-password-expiry', self.user.name, ] verifylist = [ ('name', None), ('password', None), ('email', None), + ('ignore_password_expiry', True), ('project', None), ('enable', False), ('disable', False), @@ -720,18 +1561,29 @@ class TestUserSet(TestUser): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - + # Set expected values + kwargs = { + 'enabled': True, + 'options': {'ignore_password_expiry': True}, + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) self.assertIsNone(result) - def test_user_set_name(self): + def test_user_set_no_ignore_password_expiry(self): arglist = [ - '--name', 'qwerty', + '--no-ignore-password-expiry', self.user.name, ] verifylist = [ - ('name', 'qwerty'), + ('name', None), ('password', None), ('email', None), + ('no_ignore_password_expiry', True), ('project', None), ('enable', False), ('disable', False), @@ -740,11 +1592,10 @@ class TestUserSet(TestUser): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - # Set expected values kwargs = { 'enabled': True, - 'name': 'qwerty', + 'options': {'ignore_password_expiry': False}, } # UserManager.update(user, name=, domain=, project=, password=, # email=, description=, enabled=, default_project=) @@ -754,47 +1605,47 @@ class TestUserSet(TestUser): ) self.assertIsNone(result) - def test_user_set_specify_domain(self): + def test_user_set_ignore_change_password_upon_first_use(self): arglist = [ - '--name', 'qwerty', - '--domain', self.domain.id, - self.user2.name + '--ignore-change-password-upon-first-use', + self.user.name, ] verifylist = [ - ('name', 'qwerty'), + ('name', None), ('password', None), - ('domain', self.domain.id), ('email', None), + ('ignore_change_password_upon_first_use', True), ('project', None), ('enable', False), ('disable', False), - ('user', self.user2.name), + ('user', self.user.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - + # Set expected values kwargs = { 'enabled': True, - 'name': 'qwerty' + 'options': {'ignore_change_password_upon_first_use': True}, } - + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) self.users_mock.update.assert_called_with( self.user.id, **kwargs ) self.assertIsNone(result) - def test_user_set_password(self): + def test_user_set_no_ignore_change_password_upon_first_use(self): arglist = [ - '--password', 'secret', + '--no-ignore-change-password-upon-first-use', self.user.name, ] verifylist = [ ('name', None), - ('password', 'secret'), - ('password_prompt', False), + ('password', None), ('email', None), + ('no_ignore_change_password_upon_first_use', True), ('project', None), ('enable', False), ('disable', False), @@ -803,11 +1654,10 @@ class TestUserSet(TestUser): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - # Set expected values kwargs = { 'enabled': True, - 'password': 'secret', + 'options': {'ignore_change_password_upon_first_use': False}, } # UserManager.update(user, name=, domain=, project=, password=, # email=, description=, enabled=, default_project=) @@ -817,16 +1667,16 @@ class TestUserSet(TestUser): ) self.assertIsNone(result) - def test_user_set_password_prompt(self): + def test_user_set_enable_lock_password(self): arglist = [ - '--password-prompt', + '--enable-lock-password', self.user.name, ] verifylist = [ ('name', None), ('password', None), - ('password_prompt', True), ('email', None), + ('enable_lock_password', True), ('project', None), ('enable', False), ('disable', False), @@ -834,15 +1684,11 @@ class TestUserSet(TestUser): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - mocker = mock.Mock() - mocker.return_value = 'abc123' - with mock.patch("osc_lib.utils.get_password", mocker): - result = self.cmd.take_action(parsed_args) - + result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { 'enabled': True, - 'password': 'abc123', + 'options': {'lock_password': True}, } # UserManager.update(user, name=, domain=, project=, password=, # email=, description=, enabled=, default_project=) @@ -852,15 +1698,16 @@ class TestUserSet(TestUser): ) self.assertIsNone(result) - def test_user_set_email(self): + def test_user_set_disable_lock_password(self): arglist = [ - '--email', 'barney@example.com', + '--disable-lock-password', self.user.name, ] verifylist = [ ('name', None), ('password', None), - ('email', 'barney@example.com'), + ('email', None), + ('disable_lock_password', True), ('project', None), ('enable', False), ('disable', False), @@ -869,11 +1716,10 @@ class TestUserSet(TestUser): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - # Set expected values kwargs = { 'enabled': True, - 'email': 'barney@example.com', + 'options': {'lock_password': False}, } # UserManager.update(user, name=, domain=, project=, password=, # email=, description=, enabled=, default_project=) @@ -883,16 +1729,17 @@ class TestUserSet(TestUser): ) self.assertIsNone(result) - def test_user_set_project(self): + def test_user_set_enable_multi_factor_auth(self): arglist = [ - '--project', self.project.id, + '--enable-multi-factor-auth', self.user.name, ] verifylist = [ ('name', None), ('password', None), ('email', None), - ('project', self.project.id), + ('enable_multi_factor_auth', True), + ('project', None), ('enable', False), ('disable', False), ('user', self.user.name), @@ -900,11 +1747,10 @@ class TestUserSet(TestUser): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - # Set expected values kwargs = { 'enabled': True, - 'default_project': self.project.id, + 'options': {'multi_factor_auth_enabled': True}, } # UserManager.update(user, name=, domain=, project=, password=, # email=, description=, enabled=, default_project=) @@ -914,18 +1760,17 @@ class TestUserSet(TestUser): ) self.assertIsNone(result) - def test_user_set_project_domain(self): + def test_user_set_disable_multi_factor_auth(self): arglist = [ - '--project', self.project.id, - '--project-domain', self.project.domain_id, + '--disable-multi-factor-auth', self.user.name, ] verifylist = [ ('name', None), ('password', None), ('email', None), - ('project', self.project.id), - ('project_domain', self.project.domain_id), + ('disable_multi_factor_auth', True), + ('project', None), ('enable', False), ('disable', False), ('user', self.user.name), @@ -933,11 +1778,10 @@ class TestUserSet(TestUser): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - # Set expected values kwargs = { 'enabled': True, - 'default_project': self.project.id, + 'options': {'multi_factor_auth_enabled': False}, } # UserManager.update(user, name=, domain=, project=, password=, # email=, description=, enabled=, default_project=) @@ -947,28 +1791,29 @@ class TestUserSet(TestUser): ) self.assertIsNone(result) - def test_user_set_enable(self): + def test_user_set_option_multi_factor_auth_rule(self): arglist = [ - '--enable', + '--multi-factor-auth-rule', identity_fakes.mfa_opt1, self.user.name, ] verifylist = [ ('name', None), ('password', None), ('email', None), + ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]), ('project', None), - ('enable', True), + ('enable', False), ('disable', False), ('user', self.user.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - # Set expected values kwargs = { 'enabled': True, - } + 'options': {'multi_factor_auth_rules': [["password", "totp"]]}} + # UserManager.update(user, name=, domain=, project=, password=, # email=, description=, enabled=, default_project=) self.users_mock.update.assert_called_with( @@ -977,28 +1822,35 @@ class TestUserSet(TestUser): ) self.assertIsNone(result) - def test_user_set_disable(self): + def test_user_set_with_multiple_options(self): arglist = [ - '--disable', + '--ignore-password-expiry', + '--enable-multi-factor-auth', + '--multi-factor-auth-rule', identity_fakes.mfa_opt1, self.user.name, ] verifylist = [ ('name', None), ('password', None), ('email', None), + ('ignore_password_expiry', True), + ('enable_multi_factor_auth', True), + ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]), ('project', None), ('enable', False), - ('disable', True), + ('disable', False), ('user', self.user.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - # Set expected values kwargs = { - 'enabled': False, - } + 'enabled': True, + 'options': {'ignore_password_expiry': True, + 'multi_factor_auth_enabled': True, + 'multi_factor_auth_rules': [["password", "totp"]]}} + # UserManager.update(user, name=, domain=, project=, password=, # email=, description=, enabled=, default_project=) self.users_mock.update.assert_called_with( -- cgit v1.2.1