Skip to content

Show a lockout banner and clear the lockout on password change - #38116

Open
AmitPhulera wants to merge 5 commits into
masterfrom
ap/lockout-banner
Open

Show a lockout banner and clear the lockout on password change#38116
AmitPhulera wants to merge 5 commits into
masterfrom
ap/lockout-banner

Conversation

@AmitPhulera

@AmitPhulera AmitPhulera commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Product Description

Ticket: https://dimagi.atlassian.net/browse/SAAS-19943

After five failed password attempts an account is locked for password-based auth. Because the lock is only checked by the login form and by Basic-auth API requests, a web user with an existing session keeps browsing HQ normally and has no way of knowing that their OData feed (or any other integration that signs in with their password) has stopped authenticating. They only find out after logging out and trying to log back in.

This PR:

  • Shows a banner on every page while the account is locked, explaining that integrations are blocked and linking to Change My Password.
  • Makes the in-session Change My Password form actually lift the lock. Previously only the login form and the emailed reset link cleared the failed-attempt counter, so a locked user who changed their password from My Account stayed locked for API access.
  • SSO sessions can't change a password, so they are told to log out and back in instead.

Review by commit 🐡

Dashboard :

lockout-banner-dashboard

Feature Flag

None.

Safety Assurance

Safety story

  • The context processor is read-only and only returns a flag for authenticated web users whose account is already locked; it returns {} in every other case.
  • Clearing login_attempts on password change mirrors what the login form and the password-reset-by-email flow already do.
  • Verified locally end to end: web login, five bad Basic-auth attempts on an OData feed (correct password then returns maximum password attempts exceeded), banner appears on Bootstrap 3 and Bootstrap 5 pages, changing the password in-session removes the banner and the API accepts the new password.

Automated test coverage

Added some tests for the changes made.

QA Plan

N/A

Rollback instructions

  • This PR can be reverted after deploy with no further considerations

Labels & Review

  • Risk label is set correctly
  • The set of people pinged as reviewers is appropriate for the level of risk of the change

Too many failed password attempts lock an account for password-based
auth (login form, Basic-auth APIs such as OData feeds). The lock was
cleared by the login form and the emailed reset link, but not by the
in-session Change My Password form, so a locked user who changed their
password stayed locked for API access.
Follow the post/redirect/get pattern so the re-rendered page reflects
the updated user state, and refresh the session auth hash the way
Django's own PasswordChangeView does so the user is not logged out of
the session they just used to change their password. Other sessions
are still invalidated.
The lockout after too many failed password attempts only affects
password-based auth, so a user with an existing web session never
learns about it and cannot tell why their OData feed or other API
integration stopped authenticating. SSO sessions cannot change a
password, so the flag also says whether to offer that as the fix.

SAAS-19943
@dimagimon dimagimon added the Risk: High Change affects files that have been flagged as high risk. label Sep 3, 2026
Explains that the lock blocks OData feeds and other integrations that
sign in with the user's password, and links to Change My Password,
which now clears it. SSO sessions are told to log out and back in
instead.

The banner reuses the maintenance/trial banner rule so it sits flush
against the navbar and is layered above fixed elements such as the App
Manager preview toggler.

SAAS-19943
base_navigation.html gained the lockout banner include and the alert
stylesheets gained the alert-lockout selector in both Bootstrap 3 and
Bootstrap 5 versions.
@AmitPhulera
AmitPhulera marked this pull request as ready for review September 3, 2026 17:09
clean_password(request.POST['new_password1'])
self.password_change_form.save()
# keep the current session valid; other sessions are invalidated
update_session_auth_hash(request, self.password_change_form.user)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This slightly changes existing behaviour, we used to log out user when they have changed the password. Now it would keep the current session logged in after the password is change.
In my opinion, this is a better User Experience.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm slightly skeptical about this change security-wise but I agree it's nicer UX. I'd guess that by updating the session auth hash we're invalidating the prior session token and issuing a new one? Would be interested to hear other opinions on it before approving.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd guess that by updating the session auth hash we're invalidating the prior session token and issuing a new one?

Yes. That is true.

Would be interested to hear other opinions on it before approving.

Makes sense. I also wanted inputs on it before I merge it. I will open it up to the wider team to get the feedback.

user = super(HQPasswordChangeForm, self).save(commit)
couch_user = CouchUser.from_django_user(user)
couch_user.last_password_set = datetime.utcnow()
# a new password also lifts the lockout from too many failed attempts

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"also" without context of something else is odd here. I don't think I find this comment necessary

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. I thought the commit message paired with this docstring would be helpful. Fine to remove if it was not helpful.
Sharing the reasoning - Earlier while resetting password from HQ directly failed attempts was not reset. It used to be only reset with forget password workflow.

That is what "also" signifies here.

{% include "hqwebapp/partials/trial_banner.html" %}
{% endif %}
@@ -17,38 +17,62 @@
@@ -20,38 +20,62 @@

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to see this commit with its default "Bootstrap 5 Migration - Rebuilt diffs" message, but that's a minor nitpick.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh! I see. I asked Claude to redo the commit messages, I think I messed up the original message in that. I will keep that in mind. Thanks for flagging.

clean_password(request.POST['new_password1'])
self.password_change_form.save()
# keep the current session valid; other sessions are invalidated
update_session_auth_hash(request, self.password_change_form.user)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm slightly skeptical about this change security-wise but I agree it's nicer UX. I'd guess that by updating the session auth hash we're invalidating the prior session token and issuing a new one? Would be interested to hear other opinions on it before approving.

@AmitPhulera AmitPhulera changed the title Show a lockout banner and clear the lockout on password change (SAAS-19943) Show a lockout banner and clear the lockout on password change Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Risk: High Change affects files that have been flagged as high risk.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants