Skip to content

Commit

Permalink
Allow password change for OTPW sessions (#15510)
Browse files Browse the repository at this point in the history
When provisioning new admin users on TrueNAS the sysadmin may
provide the user with a one-time password in order to set up
their own credentials. This means that OTPW authentication should
be allowed to set the associated user's password.

(cherry picked from commit 4b24b41)

Co-authored-by: Andrew Walker <[email protected]>
  • Loading branch information
bugclerk and anodos325 authored Jan 28, 2025
1 parent cdea6c5 commit 5f67ec4
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 3 deletions.
11 changes: 8 additions & 3 deletions src/middlewared/middlewared/plugins/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -1537,16 +1537,19 @@ async def set_password(self, app, data):
password changes will be rejected if the payload does not match the
currently-authenticated user.
API keys granting access to this endpoint will be able to reset
the password of any user.
NOTE: users authenticated with a one-time password will be able
to change the password without submitting a second time.
"""

verrors = ValidationErrors()
is_full_admin = credential_has_full_admin(app.authenticated_credentials)
is_otp_login = False
authenticated_user = None

if app.authenticated_credentials.is_user_session:
authenticated_user = app.authenticated_credentials.user['username']
if 'OTPW' in app.authenticated_credentials.user['account_attributes']:
is_otp_login = True

username = data['username']
password = data['new_password']
Expand Down Expand Up @@ -1580,7 +1583,9 @@ async def set_password(self, app, data):
f'{username}: user is not local to the TrueNAS server.'
)

if not is_full_admin:
# Require submitting password twice if this is not a full admin session
# and does not have a one-time password.
if not is_full_admin and not is_otp_login:
if data['old_password'] is None:
verrors.add(
'user.set_password.old_password',
Expand Down
20 changes: 20 additions & 0 deletions tests/api2/test_password_reset.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,23 @@ def test_restricted_user_set_password():
'old_password': TEST_PASSWORD2,
'new_password': TEST_PASSWORD2_2
})


def test_password_reset_via_onetime_password():
with unprivileged_user(
username=TEST_USERNAME,
group_name=TEST_GROUPNAME,
privilege_name='TEST_PASSWD_RESET_PRIVILEGE',
web_shell=False,
roles=['READONLY_ADMIN']
) as acct:
otp = call('auth.generate_onetime_password', {'username': acct.username})
with client(auth=(acct.username, otp)) as c:
c.call('user.set_password', {
'username': TEST_USERNAME,
'new_password': TEST_PASSWORD2_2
})

# Verify that password change worked
with client(auth=(acct.username, TEST_PASSWORD2_2)):
pass

0 comments on commit 5f67ec4

Please sign in to comment.