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:
Gage Hugo 2016-11-28 23:01:51 -06:00 committed by Sean Dague
parent 73939d90cd
commit 3ae73b6752
6 changed files with 62 additions and 4 deletions

View File

@ -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
--------------

View File

@ -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):

View File

@ -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:

View File

@ -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))

View File

@ -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):

View File

@ -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.