[
https://issues.apache.org/jira/browse/FINERACT-2961?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18131887#comment-18131887
]
Mandib Chaulagain commented on FINERACT-2961:
---------------------------------------------
Change of plan from my comment above, after the review on [PR
#6670|https://github.com/apache/fineract/pull/6670].
I moved the current-password check out of that PR and into this ticket. One
thing that came up in review was that someone resending an unchanged password
on {{PUT /v1/users/<id>}} shouldn't suddenly start getting a 400. So here's
what I'm planning now. It's two behaviour changes, and I'll keep them in
separate commits.
*1. Sending the current password won't count as a change anymore*
I'll switch {{JsonCommand.isChangeInPasswordParameterNamed}} to use
{{PlatformPasswordEncoder.matches()}} (added in #6670) instead of hashing the
password again and comparing with {{equals()}}. On {{PUT /v1/users/<id>}} that
means an unchanged password just gets ignored, the same as any other field that
hasn't changed: no new hash, no history row, {{last_time_password_updated}}
stays put, and {{password}} doesn't show up under {{changes}}.
A couple of knock-on effects worth knowing about:
* If an admin resends a user's unchanged password on {{PUT}}, it won't set
"password reset required" anymore (when force password reset on first login is
on).
* Resubmitting the same password won't re-hash an old SHA-256 password (like
the seeded default users have) to bcrypt anymore. Fineract doesn't upgrade
hashes at login, so up to now this was the only way to get onto bcrypt without
picking a new password. If we want hashes upgraded, I think that belongs at
login and is out of scope here.
*2. {{POST /v1/users/<id>/pwd}} will reject the current password*
It'll return a 400 with its own error code ("the new password must be different
from the current one"), no matter what {{password-reuse-check-history-count}}
is set to, even with the reuse check off. The setting itself still only counts
previous passwords, but on {{/pwd}} the two add up: with the setting at N, you
can't use your current password or any of your last N, so N+1 in total. That's
the same N+1 I described on FINERACT-2956 before I moved the check here, except
that blocking the current password no longer depends on the setting.
Without it, change 1 would turn {{/pwd}} into a silent 200 that changes
nothing, and a user who has to reset their password would still have to. Right
now it's the opposite problem: resubmitting the current password on {{/pwd}}
clears the reset flag and restarts the expiry clock without the password
actually changing. This also covers forgot password: someone who logs in with
the emailed temporary password and then sends their old password to {{/pwd}}
will get the 400 too.
This shouldn't cost any extra bcrypt work, since {{matches()}} just takes the
place of the {{encode()}} the current check does.
It depends on {{PlatformPasswordEncoder.matches()}} from #6670, so I'll open
the PR once that's merged.
> Submitting the current password is always treated as a password change
> ----------------------------------------------------------------------
>
> Key: FINERACT-2961
> URL: https://issues.apache.org/jira/browse/FINERACT-2961
> Project: Apache Fineract
> Issue Type: Bug
> Components: Security, User Management
> Affects Versions: 1.15.0
> Environment: FINERACT-2956 branch @ 1a9b0b2920 (PR #6670, on top of
> develop @ dd48e89d37)
> Reporter: Mandib Chaulagain
> Assignee: Mandib Chaulagain
> Priority: Minor
> Fix For: 1.17.0
>
> Attachments: image-2026-10-16-19-44-54-613.png,
> image-2026-10-16-19-45-27-140.png, image-2026-10-16-19-47-52-513.png,
> image-2026-10-16-19-50-04-971.png, image-2026-10-16-19-50-23-067.png,
> image-2026-10-16-19-54-04-455.png, image-2026-10-16-19-56-22-322.png
>
>
> If you set a user's password to the one they already have, Fineract treats it
> as a real change. It saves a new hash, adds a row to the password history,
> updates the password's last-updated date and returns {{"password": true}} in
> the response. This happens whether the password reuse check is on or off,
> because the reuse check only looks at the password history, and the current
> password only goes into the history once it has been replaced.
> The problem is in {{{}JsonCommand.isChangeInPasswordParameterNamed{}}}, which
> checks whether the submitted password is different from the stored one. It
> hashes the submitted password and compares that hash with the stored one
> using {{{}equals(){}}}. Fineract uses bcrypt, which adds a new random salt
> every time it hashes, so the two hashes are never equal and the method always
> returns true. It's the same root cause as FINERACT-2956, just in a different
> method, and PR #6670 doesn't touch it.
> Steps to reproduce, with the reuse check
> ({{{}password-reuse-check-history-count{}}}) turned off:
> 1. Create a user with password A on {{{}POST /v1/users{}}}:
> !image-2026-10-16-19-44-54-613.png!
> I got user id 5 back:
> !image-2026-10-16-19-45-27-140.png!
> 2. Set the user's last-updated date to a date in the past, so it's easy to
> see if it changes, and check what's stored:
> {code:sql}
> UPDATE m_appuser SET last_time_password_updated = DATE '2020-01-01' WHERE id
> = 5;
> SELECT password, last_time_password_updated FROM m_appuser WHERE id = 5;
> SELECT id, removal_date FROM m_appuser_previous_password WHERE user_id = 5;
> {code}
> !image-2026-10-16-19-47-52-513.png!
> 3. Send the same password A again on {{{}POST /v1/users/5/pwd{}}}:
> !image-2026-10-16-19-50-04-971.png!
> Response:
> !image-2026-10-16-19-50-23-067.png!
> 4. Run the two SELECTs from step 2 again:
> !image-2026-10-16-19-56-22-322.png!
> !image-2026-10-16-19-54-04-455.png!
> I did steps 2 to 4 twice, and each time one more row was added to the history.
> The request returns 200 with {{"password": true}} under {{{}changes{}}}. In
> the database, the hash is different, {{last_time_password_updated}} has moved
> from 2020-01-01 to today, and there's a new history row with the same
> password in it.
> Since the password didn't actually change, I'd expect none of this to happen:
> no password change in the response, and the stored hash, the history and
> {{last_time_password_updated}} left as they were.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)