Allow user to change own expired password
Currently, if a users password expires, they must contact an administrator in order to have their password reset for them. This change allows a user to perform the change_password call without a token, which will allow a user with an expired password to change it if they are using PCI-DSS related features. This removes the issue of needing an administrator to reset any user's password that has expired. Also updated the api-ref with the related changes. Change-Id: I4d3421c56642cfdbb25cb33b3aaaacbac4c64dd1 Closes-Bug: #1641645
This commit is contained in:
parent
73939d90cd
commit
3ae73b6752
|
@ -360,6 +360,10 @@ Relationship: ``http://docs.openstack.org/api/openstack-identity/3/rel/user_chan
|
|||
|
||||
Changes the password for a user.
|
||||
|
||||
.. note::
|
||||
This API can be called directly without authenticating and providing a token in the
|
||||
``X-Auth-Token`` request header.
|
||||
|
||||
Response Codes
|
||||
--------------
|
||||
|
||||
|
|
|
@ -248,8 +248,8 @@ class Unauthorized(SecurityError):
|
|||
|
||||
|
||||
class PasswordExpired(Unauthorized):
|
||||
message_format = _("The password is expired and needs to be reset by an "
|
||||
"administrator for user: %(user_id)s.")
|
||||
message_format = _("The password is expired and needs to be changed for "
|
||||
"user: %(user_id)s.")
|
||||
|
||||
|
||||
class AuthPluginException(Unauthorized):
|
||||
|
|
|
@ -276,7 +276,9 @@ class UserV3(controller.V3Controller):
|
|||
user_id, initiator=request.audit_initiator
|
||||
)
|
||||
|
||||
@controller.protected()
|
||||
# NOTE(gagehugo): We do not need this to be @protected.
|
||||
# A user is already expected to know their password in order
|
||||
# to change it, and can be authenticated as such.
|
||||
def change_password(self, request, user_id, user):
|
||||
original_password = user.get('original_password')
|
||||
if original_password is None:
|
||||
|
|
|
@ -1286,7 +1286,11 @@ class Manager(manager.Manager):
|
|||
new_password, initiator=None):
|
||||
|
||||
# authenticate() will raise an AssertionError if authentication fails
|
||||
self.authenticate(request, user_id, original_password)
|
||||
try:
|
||||
self.authenticate(request, user_id, original_password)
|
||||
except exception.PasswordExpired:
|
||||
# If a password has expired, we want users to be able to change it
|
||||
pass
|
||||
|
||||
domain_id, driver, entity_id = (
|
||||
self._get_domain_driver_and_entity_id(user_id))
|
||||
|
|
|
@ -835,6 +835,20 @@ class UserSelfServiceChangingPasswordsTestCase(ChangePasswordTestCase):
|
|||
def setUp(self):
|
||||
super(UserSelfServiceChangingPasswordsTestCase, self).setUp()
|
||||
|
||||
def _create_user_with_expired_password(self):
|
||||
expire_days = CONF.security_compliance.password_expires_days + 1
|
||||
time = (
|
||||
datetime.datetime.utcnow() -
|
||||
datetime.timedelta(expire_days)
|
||||
)
|
||||
password = uuid.uuid4().hex
|
||||
user_ref = unit.new_user_ref(domain_id=self.domain_id,
|
||||
password=password)
|
||||
with freezegun.freeze_time(time):
|
||||
self.user_ref = self.identity_api.create_user(user_ref)
|
||||
|
||||
return password
|
||||
|
||||
def test_changing_password(self):
|
||||
# original password works
|
||||
token_id = self.get_request_token(self.user_ref['password'],
|
||||
|
@ -931,6 +945,31 @@ class UserSelfServiceChangingPasswordsTestCase(ChangePasswordTestCase):
|
|||
self.assertNotIn(self.user_ref['password'], log_fix.output)
|
||||
self.assertNotIn(new_password, log_fix.output)
|
||||
|
||||
def test_changing_expired_password_succeeds(self):
|
||||
self.config_fixture.config(group='security_compliance',
|
||||
password_expires_days=2)
|
||||
password = self._create_user_with_expired_password()
|
||||
|
||||
new_password = uuid.uuid4().hex
|
||||
self.change_password(password=new_password,
|
||||
original_password=password,
|
||||
expected_status=http_client.NO_CONTENT)
|
||||
|
||||
def test_changing_expired_password_with_disabled_user_fails(self):
|
||||
self.config_fixture.config(group='security_compliance',
|
||||
password_expires_days=2)
|
||||
|
||||
password = self._create_user_with_expired_password()
|
||||
# disable the user account
|
||||
self.user_ref['enabled'] = False
|
||||
self.patch('/users/%s' % self.user_ref['id'],
|
||||
body={'user': self.user_ref})
|
||||
|
||||
new_password = uuid.uuid4().hex
|
||||
self.change_password(password=new_password,
|
||||
original_password=password,
|
||||
expected_status=http_client.UNAUTHORIZED)
|
||||
|
||||
|
||||
class PasswordValidationTestCase(ChangePasswordTestCase):
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Removes RBAC protection from the `Self-service change user
|
||||
password` API (``/v3/user/$user_id/password``). A user is expected
|
||||
to know their own password and can be authenticated as such. This
|
||||
change is related to PCI-DSS features and allows a user with an
|
||||
expired password to change it without the need of an
|
||||
administrator.
|
Loading…
Reference in New Issue