Skip to content

Allow users to change their email #8781

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open

Conversation

knollengewaechs
Copy link
Contributor

@knollengewaechs knollengewaechs commented Jul 15, 2025

URL of deployed dev instance (used for testing):

  • https://___.webknossos.xyz

Newer version of #8671!
see https://scm.slack.com/archives/C5AKLAV0B/p1752504188608299

more context in slack:

Steps to test:

  • Log in to wk, open http://localhost:9000/auth/changeEmail
  • change email, play around with the fields to check their validation
  • you should be locked out and only be able to log in with the new email address
  • best done locally: with the changed application.conf, check that the email verfication reminder toast shows up upon next login
  • lower grace period manually (e.g. to 1s) and check that you can not log in afterwards

TODOs:

  • check email verification workflow
  • backend: permissions for non-admin users
  • frontend
  • find out: should there be two routes, one for admin and one for user
  • validate pw
  • merge and incorporate into new pretty UI -> follow-up!

Issues:


(Please delete unneeded items, merge only when none are left open)

knollengewaechs and others added 25 commits June 4, 2025 10:21
…rs-can-change-email

- plus add changelog entry
Co-authored-by: Daniel <daniel.werner@scalableminds.com>
Copy link
Contributor

coderabbitai bot commented Jul 15, 2025

📝 Walkthrough

Walkthrough

This change enables users to update their own email addresses by introducing a new self-service UI and backend logic, while removing the ability for administrators to change user emails. It includes password verification for email changes, updates the database schema to track email change timestamps, and enforces email verification requirements.

Changes

File(s) Change Summary
app/controllers/UserController.scala Added password verification for self-initiated email changes, refined permission checks, and updated user update logic.
app/models/user/EmailVerificationService.scala Email verification grace period now based on emailChangeDate instead of account creation time.
app/models/user/MultiUser.scala Added emailChangeDate field; updated logic to set this timestamp on email change.
app/models/user/UserService.scala Log email changes and update logic to accommodate new email change flow.
conf/application.conf Enabled and required email verification.
conf/messages Added message for incorrect password during email change.
frontend/javascripts/admin/auth/change_email_view.tsx Introduced new UI component for users to change their own email with password confirmation.
frontend/javascripts/admin/user/user_list_view.tsx Removed admin capability to edit user emails in the user list.
frontend/javascripts/router.tsx Added route for the new "Change Email" view.
tools/postgres/schema.sql Added emailChangeDate column to multiUsers table; updated schema version.
unreleased_changes/8671.md Documented the new feature and database change.
conf/evolutions/136-extra-column-for-email-changed.sql Migration: added emailChangeDate column and updated related views.
conf/evolutions/reversions/136-extra-column-for-email-changed.sql Migration reversal: removed emailChangeDate column and reverted views.

Assessment against linked issues

Objective (Issue #) Addressed Explanation
Allow users to change their own email address (#8619)
UI for changing email, accessible via avatar menu or similar (#8619)
Require password confirmation for email change (#8619)
Remove admin ability to change user emails; email field read-only for admins (#8619)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Suggested labels

backend, frontend, enhancement

Suggested reviewers

  • daniel-wer
  • MichaelBuessemeyer

Poem

🐇
A hop and a skip, your email can flip—
No admin required, just a password equipped!
With a click and a cheer, your new mail appears,
And rabbits rejoice—no more admin fears.
📨✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0ca9c5 and 6b14824.

📒 Files selected for processing (3)
  • frontend/javascripts/admin/auth/change_email_view.tsx (1 hunks)
  • frontend/javascripts/navbar.tsx (2 hunks)
  • frontend/javascripts/router.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/javascripts/router.tsx
  • frontend/javascripts/admin/auth/change_email_view.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
frontend/javascripts/navbar.tsx (1)
frontend/javascripts/admin/rest_api.ts (1)
  • logoutUser (153-155)
🔇 Additional comments (2)
frontend/javascripts/navbar.tsx (2)

35-35: LGTM: Clean import addition for centralized logout functionality.

The addition of the logoutUser import from "admin/rest_api" properly supports the refactoring to centralize logout logic across the application.


793-793: LGTM: Proper usage of the centralized logout function.

The replacement of the inline API call with the imported logoutUser() function maintains the same async/await pattern and behavior while improving code organization. This centralization will help ensure consistent logout handling across the application, which is particularly important for the new email change feature.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@knollengewaechs
Copy link
Contributor Author

knollengewaechs commented Jul 15, 2025

review from @daniel-wer
#8671 (review)

"Notes from testing:

  • When entering the wrong password, the user is still logged out without any indication that the email update did not succeed. It would be great if the user is only logged out if the email update succeeded and sees an error that the password is wrong otherwise.

  • It feels a bit weird to be logged out and then after a second of waiting have the page reload. Do you also observe this or is it specific to my setup? Could we avoid reloading the page or is that tied to being logged out?"

@knollengewaechs
Copy link
Contributor Author

backend review see #8671 (review)

@knollengewaechs
Copy link
Contributor Author

knollengewaechs commented Jul 15, 2025

@daniel-wer thank you for the feedback!

When entering the wrong password, the user is still logged out without any indication that the email update did not succeed. It would be great if the user is only logged out if the email update succeeded and sees an error that the password is wrong otherwise.

@MichaelBuessemeyer I think this is because the request does not fail if admins enter the wrong password. The bahaviour is as expected for non-admins users. Could this be a remnant of the old implementation that admins dont have to provide their password? I understand that the email update is part of the user update route, and I understand that admins arent supposed to need their PW every time. But when changing the email, I thought this would be useful to prevent abuse? I thought of two solutions (there are probably many more):

  • Omit the pw field for admins. Only frontend-changes needed. No pw needed for admins to change their email.
  • Not exactly sure if this is possible in the backend: Only require PW for certain user changes, even for admins. This could include the email address. Prevents abuse in some cases.

Let me know what you think! :)

@MichaelBuessemeyer
Copy link
Contributor

IMO

Not exactly sure if this is possible in the backend: Only require PW for certain user changes, even for admins. This could include the email address. Prevents abuse in some cases.

should be the way to go. Admins should also be only able to change their email address, in case they provide their password correctly. It should be the same as for normal users.

I think this is because the request does not fail if admins enter the wrong password.

That sounds very fishy. Thanks for investigating. IMO it should be failing in that case!. Likely me that messed up here 🙃. I'll give it a check once I have time for it in the next days :)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9811f0a and 30e72ab.

📒 Files selected for processing (5)
  • app/controllers/UserController.scala (6 hunks)
  • conf/evolutions/136-extra-column-for-email-changed.sql (1 hunks)
  • conf/evolutions/reversions/136-extra-column-for-email-changed.sql (1 hunks)
  • tools/postgres/schema.sql (2 hunks)
  • unreleased_changes/8671.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • unreleased_changes/8671.md
  • tools/postgres/schema.sql
  • app/controllers/UserController.scala
🔇 Additional comments (2)
conf/evolutions/reversions/136-extra-column-for-email-changed.sql (2)

5-7: Mirror the CASCADE change in the down migration
If you adopt CASCADE in the forward script you should do the same here to guarantee the rollback path always works, even when additional objects reference these views.

-DROP VIEW IF EXISTS webknossos.userInfos;
-DROP VIEW IF EXISTS webknossos.multiUsers_;
+DROP VIEW IF EXISTS webknossos.userInfos CASCADE;
+DROP VIEW IF EXISTS webknossos.multiUsers_ CASCADE;

10-20: Symmetry check – confirm the recreated views match version 135 exactly
After removing emailChangeDate, be sure the column order and aliases of userInfos match the original v135 definition; otherwise clients relying on SELECT * will break during rollback. Please compare against the historical definition or run a diff in CI to prevent subtle regressions.

Comment on lines +5 to +7
DROP VIEW IF EXISTS webknossos.userInfos;
DROP VIEW IF EXISTS webknossos.multiUsers_;

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using CASCADE when dropping views to avoid dependency errors
If other objects depend on userInfos or multiUsers_, the DROP VIEW will fail and abort the whole migration. Adding CASCADE is safer in complex schemas.

-DROP VIEW IF EXISTS webknossos.userInfos;
-DROP VIEW IF EXISTS webknossos.multiUsers_;
+DROP VIEW IF EXISTS webknossos.userInfos CASCADE;
+DROP VIEW IF EXISTS webknossos.multiUsers_ CASCADE;
🤖 Prompt for AI Agents
In conf/evolutions/136-extra-column-for-email-changed.sql around lines 5 to 7,
the DROP VIEW statements for userInfos and multiUsers_ do not use CASCADE, which
can cause failures if other objects depend on these views. Modify the DROP VIEW
statements to include CASCADE (e.g., DROP VIEW IF EXISTS webknossos.userInfos
CASCADE;) to ensure dependent objects are also dropped and avoid migration
aborts.

Comment on lines +8 to +10
ALTER TABLE webknossos.multiUsers ADD COLUMN emailChangeDate TIMESTAMPTZ NOT NULL DEFAULT NOW();
UPDATE webknossos.multiUsers SET emailChangeDate = created;

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Heavy rewrite risk – split the NOT NULL DEFAULT change into two steps
ADD COLUMN … NOT NULL DEFAULT NOW() forces a full-table rewrite because NOW() is volatile. On large multiUsers tables this can lock the table for a long time.

A less disruptive pattern:

-ALTER TABLE webknossos.multiUsers ADD COLUMN emailChangeDate TIMESTAMPTZ NOT NULL DEFAULT NOW();
+-- 1. add nullable column without default (no rewrite)
+ALTER TABLE webknossos.multiUsers ADD COLUMN emailChangeDate TIMESTAMPTZ;
+
+-- 2. back-fill existing rows
+UPDATE webknossos.multiUsers SET emailChangeDate = created;
+
+-- 3. make it NOT NULL and set the default for new rows
+ALTER TABLE webknossos.multiUsers
+    ALTER COLUMN emailChangeDate SET NOT NULL,
+    ALTER COLUMN emailChangeDate SET DEFAULT NOW();

This avoids the rewrite while achieving the same outcome.

🤖 Prompt for AI Agents
In conf/evolutions/136-extra-column-for-email-changed.sql around lines 8 to 10,
avoid adding the new column with NOT NULL DEFAULT NOW() in one step because it
causes a full-table rewrite and long locks. Instead, first add the column as
nullable without a default, then update all rows to set the column value to
created, and finally alter the column to set NOT NULL and add the default NOW().
This staged approach prevents heavy table locking on large tables.

Comment on lines +11 to +20
CREATE VIEW webknossos.multiUsers_ AS SELECT * FROM webknossos.multiUsers WHERE NOT isDeleted;
CREATE VIEW webknossos.userInfos AS
SELECT
u._id AS _user, m.email, u.firstName, u.lastname, o.name AS organization_name,
u.isDeactivated, u.isDatasetManager, u.isAdmin, m.isSuperUser,
u._organization, o._id AS organization_id, u.created AS user_created,
m.created AS multiuser_created, u._multiUser, m._lastLoggedInIdentity, u.lastActivity, m.isEmailVerified
FROM webknossos.users_ u
JOIN webknossos.organizations_ o ON u._organization = o._id
JOIN webknossos.multiUsers_ m on u._multiUser = m._id;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing emailChangeDate in userInfos view
The purpose of adding the column is to expose it for downstream logic (e.g. grace-period checks). The recreated userInfos view still omits it, forcing every consumer to query multiUsers directly.

Add the column so callers get it “for free”:

-SELECT
-u._id AS _user, m.email, u.firstName, u.lastname, o.name AS organization_name,
+SELECT
+u._id AS _user, m.email, u.firstName, u.lastname, o.name AS organization_name,
+m.emailChangeDate,

Update all select lists and keep the column order consistent with the forward-migration philosophy that views reflect relevant business data.

🤖 Prompt for AI Agents
In conf/evolutions/136-extra-column-for-email-changed.sql around lines 11 to 20,
the userInfos view definition is missing the emailChangeDate column from the
multiUsers table. To fix this, add the emailChangeDate column to the SELECT list
in the userInfos view, placing it in a logical position consistent with the
existing column order to ensure downstream consumers can access it without
querying multiUsers directly.

@knollengewaechs
Copy link
Contributor Author

@daniel-wer

It feels a bit weird to be logged out and then after a second of waiting have the page reload. Do you also observe this or is it specific to my setup? Could we avoid reloading the page or is that tied to being logged out?

I only experience this when activating throttling or paying very close attention to my browser, but nonetheless, it would be nice to omit this! From what I can tell, this behaviour is tied to being logged out. The user needs to be logged out and then navigated to the login page, otherwise you would see the login view with another route in the browsers address bar (at least thats my explanation). Thats why the last step of the log-out procedure is to set the location to auth/login. This is done in other scenarios, too, e.g. in the account_password_view.tsx.
I didnt find a good solution to avoid this tbh.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow users to change their own email address
4 participants