summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/cli/command-objects/user.rst126
-rw-r--r--openstackclient/identity/v3/user.py120
-rw-r--r--openstackclient/tests/unit/identity/v3/fakes.py3
-rw-r--r--openstackclient/tests/unit/identity/v3/test_user.py852
-rw-r--r--releasenotes/notes/add_options_to_user_create_and_set-302401520f36d153.yaml19
5 files changed, 1119 insertions, 1 deletions
diff --git a/doc/source/cli/command-objects/user.rst b/doc/source/cli/command-objects/user.rst
index 632d0e25..d0fc3f87 100644
--- a/doc/source/cli/command-objects/user.rst
+++ b/doc/source/cli/command-objects/user.rst
@@ -19,6 +19,12 @@ Create new user
[--password-prompt]
[--email <email-address>]
[--description <description>]
+ [--multi-factor-auth-rule <rule>]
+ [--ignore-lockout-failure-attempts| --no-ignore-lockout-failure-attempts]
+ [--ignore-password-expiry| --no-ignore-password-expiry]
+ [--ignore-change-password-upon-first-use| --no-ignore-change-password-upon-first-use]
+ [--enable-lock-password| --disable-lock-password]
+ [--enable-multi-factor-auth| --disable-multi-factor-auth]
[--enable | --disable]
[--or-show]
<user-name>
@@ -56,6 +62,63 @@ Create new user
.. versionadded:: 3
+.. option:: --ignore-lockout-failure-attempts
+
+ Opt into ignoring the number of times a user has authenticated and
+ locking out the user as a result
+
+.. option:: --no-ignore-lockout-failure-attempts
+
+ Opt out of ignoring the number of times a user has authenticated
+ and locking out the user as a result
+
+.. option:: --ignore-change-password-upon-first-use
+
+ 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.
+
+.. option:: --no-ignore-change-password-upon-first-use
+
+ 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.
+
+.. option:: --ignore-password-expiry
+
+ Opt into allowing user to continue using passwords that may be
+ expired
+
+.. option:: --no-ignore-password-expiry
+
+ Opt out of allowing user to continue using passwords that may be
+ expired
+
+.. option:: --enable-lock-password
+
+ Disables the ability for a user to change its password through
+ self-service APIs
+
+.. option:: --disable-lock-password
+
+ Enables the ability for a user to change its password through
+ self-service APIs
+
+.. option:: --enable-multi-factor-auth
+
+ Enables the MFA (Multi Factor Auth)
+
+.. option:: --disable-multi-factor-auth
+
+ Disables the MFA (Multi Factor Auth)
+
+.. option:: --multi-factor-auth-rule <rule>
+
+ 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.
+
.. option:: --enable
Enable user (default)
@@ -146,6 +209,12 @@ Set user properties
[--password-prompt]
[--email <email-address>]
[--description <description>]
+ [--multi-factor-auth-rule <rule>]
+ [--ignore-lockout-failure-attempts| --no-ignore-lockout-failure-attempts]
+ [--ignore-password-expiry| --no-ignore-password-expiry]
+ [--ignore-change-password-upon-first-use| --no-ignore-change-password-upon-first-use]
+ [--enable-lock-password| --disable-lock-password]
+ [--enable-multi-factor-auth| --disable-multi-factor-auth]
[--enable|--disable]
<user>
@@ -187,6 +256,63 @@ Set user properties
.. versionadded:: 3
+.. option:: --ignore-lockout-failure-attempts
+
+ Opt into ignoring the number of times a user has authenticated and
+ locking out the user as a result
+
+.. option:: --no-ignore-lockout-failure-attempts
+
+ Opt out of ignoring the number of times a user has authenticated
+ and locking out the user as a result
+
+.. option:: --ignore-change-password-upon-first-use
+
+ 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.
+
+.. option:: --no-ignore-change-password-upon-first-use
+
+ 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.
+
+.. option:: --ignore-password-expiry
+
+ Opt into allowing user to continue using passwords that may be
+ expired
+
+.. option:: --no-ignore-password-expiry
+
+ Opt out of allowing user to continue using passwords that may be
+ expired
+
+.. option:: --enable-lock-password
+
+ Disables the ability for a user to change its password through
+ self-service APIs
+
+.. option:: --disable-lock-password
+
+ Enables the ability for a user to change its password through
+ self-service APIs
+
+.. option:: --enable-multi-factor-auth
+
+ Enables the MFA (Multi Factor Auth)
+
+.. option:: --disable-multi-factor-auth
+
+ Disables the MFA (Multi Factor Auth)
+
+.. option:: --multi-factor-auth-rule <rule>
+
+ 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.
+
.. option:: --enable
Enable user (default)
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='<rule>',
+ 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='<description>',
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='<description>',
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,6 +447,471 @@ 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)
+
+ # 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_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
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_user_create_no_ignore_lockout_failure_attempts(self):
+ arglist = [
+ '--no-ignore-lockout-failure-attempts',
+ self.user.name,
+ ]
+ verifylist = [
+ ('no_ignore_lockout_failure_attempts', 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': {'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.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_user_create_ignore_password_expiry(self):
+ arglist = [
+ '--ignore-password-expiry',
+ self.user.name,
+ ]
+ verifylist = [
+ ('ignore_password_expiry', 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': {'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(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)
+
+ # 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': 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_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)
+
+ # 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_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
+ )
+
+ 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 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_change_password_upon_first_use': 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_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):
@@ -1007,6 +1481,384 @@ class TestUserSet(TestUser):
)
self.assertIsNone(result)
+ def test_user_set_ignore_lockout_failure_attempts(self):
+ arglist = [
+ '--ignore-lockout-failure-attempts',
+ self.user.name,
+ ]
+ verifylist = [
+ ('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)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'options': {'ignore_lockout_failure_attempts': 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_no_ignore_lockout_failure_attempts(self):
+ arglist = [
+ '--no-ignore-lockout-failure-attempts',
+ self.user.name,
+ ]
+ verifylist = [
+ ('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)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ '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)
+
+ 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),
+ ('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': {'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_no_ignore_password_expiry(self):
+ arglist = [
+ '--no-ignore-password-expiry',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('no_ignore_password_expiry', True),
+ ('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,
+ 'options': {'ignore_password_expiry': 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)
+
+ def test_user_set_ignore_change_password_upon_first_use(self):
+ arglist = [
+ '--ignore-change-password-upon-first-use',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('ignore_change_password_upon_first_use', True),
+ ('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,
+ '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_no_ignore_change_password_upon_first_use(self):
+ arglist = [
+ '--no-ignore-change-password-upon-first-use',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('no_ignore_change_password_upon_first_use', True),
+ ('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,
+ 'options': {'ignore_change_password_upon_first_use': 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)
+
+ def test_user_set_enable_lock_password(self):
+ arglist = [
+ '--enable-lock-password',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('enable_lock_password', True),
+ ('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,
+ 'options': {'lock_password': 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_disable_lock_password(self):
+ arglist = [
+ '--disable-lock-password',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('disable_lock_password', True),
+ ('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,
+ 'options': {'lock_password': 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)
+
+ def test_user_set_enable_multi_factor_auth(self):
+ arglist = [
+ '--enable-multi-factor-auth',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('enable_multi_factor_auth', True),
+ ('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,
+ 'options': {'multi_factor_auth_enabled': 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_disable_multi_factor_auth(self):
+ arglist = [
+ '--disable-multi-factor-auth',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('disable_multi_factor_auth', True),
+ ('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,
+ 'options': {'multi_factor_auth_enabled': 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)
+
+ def test_user_set_option_multi_factor_auth_rule(self):
+ arglist = [
+ '--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', 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(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_user_set_with_multiple_options(self):
+ arglist = [
+ '--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', 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': {'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(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
class TestUserSetPassword(TestUser):
diff --git a/releasenotes/notes/add_options_to_user_create_and_set-302401520f36d153.yaml b/releasenotes/notes/add_options_to_user_create_and_set-302401520f36d153.yaml
new file mode 100644
index 00000000..698a6f18
--- /dev/null
+++ b/releasenotes/notes/add_options_to_user_create_and_set-302401520f36d153.yaml
@@ -0,0 +1,19 @@
+---
+features:
+ - |
+ Added the below mentioned parameters to the user create and set commands.
+
+ * --ignore-lockout-failure-attempts
+ * --no-ignore-lockout-failure-attempts
+ * --ignore-password-expiry
+ * --no-ignore-password-expiry
+ * --ignore-change-password-upon-first-use
+ * --no-ignore-change-password-upon-first-use
+ * --enable-lock-password
+ * --disable-lock-password
+ * --enable-multi-factor-auth
+ * --disable-multi-factor-auth
+ * --multi-factor-auth-rule
+
+ This will now allow users to set user options via CLI.
+ <https://docs.openstack.org/keystone/latest/admin/resource-options.html#user-options>