Merge "Deprecate [security_compliance]\password_expires_ignore_user_ids"
This commit is contained in:
commit
781db8e67a
@ -11,6 +11,7 @@
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import versionutils
|
||||
|
||||
from keystone.conf import utils
|
||||
|
||||
@ -77,6 +78,16 @@ the `[identity] driver`.
|
||||
|
||||
password_expires_ignore_user_ids = cfg.ListOpt(
|
||||
'password_expires_ignore_user_ids',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason=utils.fmt("""
|
||||
Functionality added as a per-user option "ignore_password_expiry" in Ocata.
|
||||
Each user that should ignore password expiry should have the value set to
|
||||
"true" in the user's `options` attribute (e.g.
|
||||
`user['options']['ignore_password_expiry'] = True`) with an "update_user" call.
|
||||
This avoids the need to restart keystone to adjust the users that ignore
|
||||
password expiry. This option will be removed in the Pike release.
|
||||
"""),
|
||||
deprecated_since=versionutils.deprecated.OCATA,
|
||||
default=[],
|
||||
help=utils.fmt("""
|
||||
Comma separated list of user IDs to be ignored when checking if a password
|
||||
|
@ -17,6 +17,9 @@ USER_OPTIONS_REGISTRY = resource_options.ResourceOptionRegistry('USER')
|
||||
IGNORE_CHANGE_PASSWORD_OPT = (
|
||||
resource_options.ResourceOption('1000',
|
||||
'ignore_change_password_upon_first_use'))
|
||||
IGNORE_PASSWORD_EXPIRY_OPT = (
|
||||
resource_options.ResourceOption('1001',
|
||||
'ignore_password_expiry'))
|
||||
|
||||
|
||||
# NOTE(notmorgan): wrap this in a function for testing purposes.
|
||||
@ -24,6 +27,7 @@ IGNORE_CHANGE_PASSWORD_OPT = (
|
||||
def register_user_options():
|
||||
for opt in [
|
||||
IGNORE_CHANGE_PASSWORD_OPT,
|
||||
IGNORE_PASSWORD_EXPIRY_OPT,
|
||||
]:
|
||||
USER_OPTIONS_REGISTRY.register_option(opt)
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
import datetime
|
||||
|
||||
from oslo_log import versionutils
|
||||
import sqlalchemy
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from sqlalchemy import orm
|
||||
@ -149,10 +150,29 @@ class User(sql.ModelBase, sql.DictBase):
|
||||
|
||||
def _get_password_expires_at(self, created_at):
|
||||
expires_days = CONF.security_compliance.password_expires_days
|
||||
# NOTE(notmorgan): This option is deprecated and subject to removal
|
||||
# in a future release.
|
||||
ignore_list = CONF.security_compliance.password_expires_ignore_user_ids
|
||||
if expires_days and (self.id not in ignore_list):
|
||||
expired_date = (created_at + datetime.timedelta(days=expires_days))
|
||||
return expired_date.replace(microsecond=0)
|
||||
if ignore_list:
|
||||
versionutils.deprecated(
|
||||
what='[security_compliance]\password_expires_ignore_user_ids',
|
||||
as_of=versionutils.deprecated.OCATA,
|
||||
remove_in=+1,
|
||||
in_favor_of=('Using the `ignore_password_expiry` value set to '
|
||||
'`True` in the `user["options"]` dictionary on '
|
||||
'User creation or update (via API call).'))
|
||||
# Get the IGNORE_PASSWORD_EXPIRY_OPT value from the user's
|
||||
# option_mapper.
|
||||
|
||||
ignore_pw_expiry = getattr(
|
||||
self.get_resource_option(iro.IGNORE_PASSWORD_EXPIRY_OPT.option_id),
|
||||
'option_value',
|
||||
False)
|
||||
if (self.id not in ignore_list) and not ignore_pw_expiry:
|
||||
if expires_days:
|
||||
expired_date = (created_at +
|
||||
datetime.timedelta(days=expires_days))
|
||||
return expired_date.replace(microsecond=0)
|
||||
return None
|
||||
|
||||
@password.expression
|
||||
|
@ -695,6 +695,33 @@ class PasswordExpiresValidationTests(test_backend_sql.SqlTests):
|
||||
user_id=user['id'],
|
||||
password=self.password)
|
||||
|
||||
def test_authenticate_with_expired_password_for_ignore_user_option(self):
|
||||
# set user to have the 'ignore_password_expiry' option set to False
|
||||
self.user_dict.setdefault('options', {})[
|
||||
iro.IGNORE_PASSWORD_EXPIRY_OPT.option_name] = False
|
||||
# set password created_at so that the password will expire
|
||||
password_created_at = (
|
||||
datetime.datetime.utcnow() -
|
||||
datetime.timedelta(
|
||||
days=CONF.security_compliance.password_expires_days + 1)
|
||||
)
|
||||
user = self._create_user(self.user_dict, password_created_at)
|
||||
self.assertRaises(exception.PasswordExpired,
|
||||
self.identity_api.authenticate,
|
||||
self.make_request(),
|
||||
user_id=user['id'],
|
||||
password=self.password)
|
||||
|
||||
# update user to explicitly have the expiry option to True
|
||||
user['options'][
|
||||
iro.IGNORE_PASSWORD_EXPIRY_OPT.option_name] = True
|
||||
user = self.identity_api.update_user(user['id'],
|
||||
user)
|
||||
# test password is not expired due to ignore option
|
||||
self.identity_api.authenticate(self.make_request(),
|
||||
user_id=user['id'],
|
||||
password=self.password)
|
||||
|
||||
def _get_test_user_dict(self, password):
|
||||
test_user_dict = {
|
||||
'id': uuid.uuid4().hex,
|
||||
@ -706,13 +733,15 @@ class PasswordExpiresValidationTests(test_backend_sql.SqlTests):
|
||||
return test_user_dict
|
||||
|
||||
def _create_user(self, user_dict, password_created_at):
|
||||
user_dict = utils.hash_user_password(user_dict)
|
||||
# Bypass business logic and go straight for the identity driver
|
||||
# (SQL in this case)
|
||||
driver = self.identity_api.driver
|
||||
driver.create_user(user_dict['id'], user_dict)
|
||||
with sql.session_for_write() as session:
|
||||
user_ref = model.User.from_dict(user_dict)
|
||||
user_ref = session.query(model.User).get(user_dict['id'])
|
||||
user_ref.password_ref.created_at = password_created_at
|
||||
user_ref.password_ref.expires_at = (
|
||||
user_ref._get_password_expires_at(password_created_at))
|
||||
session.add(user_ref)
|
||||
return base.filter_user(user_ref.to_dict())
|
||||
|
||||
|
||||
|
14
releasenotes/notes/bug-1659995-f3e716de743b7291.yaml
Normal file
14
releasenotes/notes/bug-1659995-f3e716de743b7291.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
[`bug 1659995 <https://bugs.launchpad.net/keystone/+bug/1659995>`_]
|
||||
A new option has been made available via the user create and update API
|
||||
(``POST/PATCH /v3/users) call, the option will allow an admin to
|
||||
mark users as exempt from the PCI password expiry policy via an API.
|
||||
This can be done like so: ``user['options']['ignore_password_expiry']``.
|
||||
deprecations:
|
||||
- |
|
||||
[`bug 1659995 <https://bugs.launchpad.net/keystone/+bug/1659995>`_]
|
||||
The config option ``[security_compliance] ignore_password_expires_user_ids``
|
||||
has been deprecated in favor of using the option value set, available via
|
||||
the user create and update API call
|
Loading…
Reference in New Issue
Block a user