Merge "Add ignore_user_inactivity user option"

This commit is contained in:
Zuul 2020-07-15 16:15:53 +00:00 committed by Gerrit Code Review
commit 5feffb0319
6 changed files with 93 additions and 3 deletions

View File

@ -1954,7 +1954,8 @@ response_user_options_body_required:
The resource options for the user. Available resource options are
``ignore_change_password_upon_first_use``, ``ignore_password_expiry``,
``ignore_lockout_failure_attempts``, ``lock_password``,
``multi_factor_auth_enabled``, and ``multi_factor_auth_rules``.
``multi_factor_auth_enabled``, and ``multi_factor_auth_rules``
``ignore_user_inactivity``.
in: body
required: true
type: object
@ -2249,7 +2250,8 @@ user_options_request_body:
The resource options for the user. Available resource options are
``ignore_change_password_upon_first_use``, ``ignore_password_expiry``,
``ignore_lockout_failure_attempts``, ``lock_password``,
``multi_factor_auth_enabled``, and ``multi_factor_auth_rules``.
``multi_factor_auth_enabled``, and ``multi_factor_auth_rules``
``ignore_user_inactivity``.
in: body
required: false
type: object

View File

@ -42,6 +42,36 @@ or by updating an existing user to include new options
``None``; if the option is set to ``None``, it is removed from the user's
data structure.
.. _ignore_user_inactivity:
ignore_user_inactivity
----------------------
Type: ``Boolean``
Opt into ignoring global inactivity lock settings defined in
``keystone.conf [security_compliance]`` on a per-user basis. Setting this
option to ``True`` will make users not set as disabled even after the
globally configured inactivity period is reached.
.. code-block:: json
{
"user": {
"options": {
"ignore_user_inactivity": true
}
}
}
.. note::
Setting this option for users which are already disabled will not
make them automatically enabled. Such users must be enabled manually
after setting this option to True for them.
See the `security compliance documentation
<security-compliance.html>`_ for more details.
.. _ignore_change_password_upon_first_use:
ignore_change_password_upon_first_use

View File

@ -80,6 +80,12 @@ LOCK_PASSWORD_OPT = (
option_name='lock_password',
validator=resource_options.boolean_validator,
json_schema_validation=parameter_types.boolean))
IGNORE_USER_INACTIVITY_OPT = (
resource_options.ResourceOption(
option_id='1004',
option_name='ignore_user_inactivity',
validator=resource_options.boolean_validator,
json_schema_validation=parameter_types.boolean))
MFA_RULES_OPT = (
resource_options.ResourceOption(
option_id='MFAR',
@ -117,6 +123,7 @@ def register_user_options():
IGNORE_PASSWORD_EXPIRY_OPT,
IGNORE_LOCKOUT_ATTEMPT_OPT,
LOCK_PASSWORD_OPT,
IGNORE_USER_INACTIVITY_OPT,
MFA_RULES_OPT,
MFA_ENABLED_OPT,
]:

View File

@ -199,13 +199,18 @@ class User(sql.ModelBase, sql.ModelDictMixinWithExtras):
if self._enabled:
max_days = (
CONF.security_compliance.disable_user_account_days_inactive)
inactivity_exempt = getattr(
self.get_resource_option(
iro.IGNORE_USER_INACTIVITY_OPT.option_id),
'option_value',
False)
last_active = self.last_active_at
if not last_active and self.created_at:
last_active = self.created_at.date()
if max_days and last_active:
now = datetime.datetime.utcnow().date()
days_inactive = (now - last_active).days
if days_inactive >= max_days:
if days_inactive >= max_days and not inactivity_exempt:
self._enabled = False
return self._enabled

View File

@ -342,6 +342,43 @@ class DisableInactiveUserTests(test_backend_sql.SqlTests):
user_ref = self._get_user_ref(user['id'])
self.assertTrue(user_ref.enabled)
def test_ignore_user_inactivity(self):
self.user_dict['options'] = {'ignore_user_inactivity': True}
user = PROVIDERS.identity_api.create_user(
self.user_dict)
# set last_active_at just beyond the max
last_active_at = (
datetime.datetime.utcnow() -
datetime.timedelta(self.max_inactive_days + 1)).date()
self._update_user_last_active_at(user['id'], last_active_at)
# get user and verify that the user is not disabled
user = PROVIDERS.identity_api.get_user(user['id'])
self.assertTrue(user['enabled'])
def test_ignore_user_inactivity_with_user_disabled(self):
user = PROVIDERS.identity_api.create_user(
self.user_dict)
# set last_active_at just beyond the max
last_active_at = (
datetime.datetime.utcnow() -
datetime.timedelta(self.max_inactive_days + 1)).date()
self._update_user_last_active_at(user['id'], last_active_at)
# get user and verify that the user is disabled
user = PROVIDERS.identity_api.get_user(user['id'])
self.assertFalse(user['enabled'])
# update disabled user with ignore_user_inactivity to true
user['options'] = {'ignore_user_inactivity': True}
user = PROVIDERS.identity_api.update_user(
user['id'], user)
# user is not enabled
user = PROVIDERS.identity_api.get_user(user['id'])
self.assertFalse(user['enabled'])
# Manually set enabled and test
user['enabled'] = True
PROVIDERS.identity_api.update_user(user['id'], user)
user = PROVIDERS.identity_api.get_user(user['id'])
self.assertTrue(user['enabled'])
def _get_user_dict(self, password):
user = {
'name': uuid.uuid4().hex,

View File

@ -0,0 +1,9 @@
---
features:
- |
[`bug 1827431 <https://bugs.launchpad.net/keystone/+bug/1827431>`_]
Added a new user option 'ignore_user_inactivity' (defaults to False).
When set to True, it overrides disabling the user after being inactive
for certain time as set in
``[security_compliance]disable_user_account_days_inactive`` option
in Keystone configuration file.