Add ignore_user_inactivity user option

this option allows to override the
[security_compliance]disable_user_account_days_inactive setting from
config on per-user basis.

Co-Authored-By: Vishakha Agarwal <agarwalvishakha18@gmail.com>

Change-Id: Ida360e215426184195687bee2a800877af33af04
Closes-Bug: #1827431
This commit is contained in:
Pavlo Shchelokovskyy 2019-05-02 16:50:56 -06:00 committed by Vishakha Agarwal
parent e3bd1d747d
commit c9c655a1e1
6 changed files with 93 additions and 3 deletions

View File

@ -1945,7 +1945,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
@ -2240,7 +2241,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.