You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Check customer e-mails in the account's store and change passwords on the account - #1009
Follow-up to #991, and the develop side of #1008. Customer e-mails and passwords were not handled the same way in every step:
The storefront validators checked an e-mail or username as typed and in the current store. The commands store it trimmed and lowercased, on an account that has its own store.
A password change finds its account by e-mail, even when the caller already holds the account. An e-mail can change between the two steps, and with per-store identity it can belong to several stores.
Solution
SubAccountEditValidator and CustomerInfoValidator check the normalized value in the store of the edited account, also when a case-only repair applies.
ICustomerManagerService gets ChangePassword(Customer, PasswordFormat, string), which changes the password of that account without an e-mail lookup. The existing method resolves the e-mail and calls it. Both write only the password fields through a new ICustomerService.UpdateCustomerPassword, so a copy the caller loaded earlier cannot undo other edits.
The sub-account edit, the customer's own password change and the SCIM credentials reset use the new method.
Password recovery and the admin panel keep the existing method. The API SetPassword also keeps it, because it receives only an e-mail.
No new resources, settings or migrations.
Breaking changes
ICustomerManagerService and ICustomerService each have a new member, so a custom implementation of either must add it. Callers are not affected. An account whose stored e-mail differs only in case now has that address checked on save, even when changing e-mails is turned off.
Testing
Unit tests pass: Grand.Web.Tests (403), Grand.Business.Authentication.Tests (399), Grand.Business.Customers.Tests (142), Grand.Web.Admin.Tests (1839). The new tests failed before the callers were switched. The full solution and the steps below were not run.
Set HideSubAccountsTab to false, sign in on the storefront, add a sub-account, change its password, and check it can sign in with the new one.
With AllowUsersToChangeEmail on, change its e-mail to one with capitals and surrounding spaces, together with a new password. The address is saved trimmed and lowercased, and the sub-account can sign in. An address that is already taken is rejected in any form.
In My account, change your e-mail the same way, and with usernames on, your username. Taken values are rejected.
In My account, change your own password and sign in with the new one.
With Customer:RegisterCustomersPerStore on and the same e-mail registered in two stores, change the password in one store. The account in the other store keeps its password.
The storefront validators checked an e-mail or username as typed and in the
current store, while the commands store it trimmed and lowercased on an
account that belongs to its own store. They now check the stored form in the
store of the account being edited, including when a case-only repair applies.
A password change finds its account by e-mail, and with per-store identity
one e-mail can belong to several stores. The customer's own change and the
SCIM credentials reset now use the store of the account they act on, and a
sub-account password is changed only when its e-mail resolves to that account.
These callers already hold the account, but the password change looked it up
again by e-mail, a value that can change between the two steps and is unique
only as far as the application keeps it so. A new overload takes the customer
and the existing method now resolves the e-mail and calls it.
The sub-account edit no longer needs its check that the e-mail leads back to
the account, nor can it skip the remaining edits without telling the owner.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The password-change overload writes a stale customer snapshot, potentially overwriting concurrent account edits or reactivating a deactivated account, so credential updates must be persisted atomically by customer ID.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
A concurrent e-mail change can cause the password update to reset a different account’s password, while guard failures silently discard edits without notifying the user.
The password change saved the whole customer, including its e-mail, username,
store and active flag. With a copy the caller loaded earlier in the request,
that could undo an edit made in the meantime, such as a deactivation. A
dedicated update now writes the four password fields in one operation, and
still refuses a system account and raises the update event.
KrzysztofPajak
changed the title
Check customer e-mails and change passwords in the account's store
Check customer e-mails in the account's store and change passwords on the account
Nov 1, 2026
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Token rotation uses stale customer fields and can append a duplicate token, leaving earlier cookies valid after a password change or SCIM credentials reset; atomic rotation and a regression test are needed.
If a first sign-in creates PasswordToken after the caller loads this customer, the token rotation below uses the stale UserFields and appends a second token instead of replacing the stored one. Cookie validation reads the first token, so the earlier cookie remains valid after the password change, including a SCIM credentials reset. Rotate the token atomically by customer ID and key, independently of the snapshot, and add a regression test where the stored token is missing from the caller's copy.
A lookup can return the customer being edited, for example where it ignores
case and the stored value differs only in case. Saving My account then failed
with an "already exists" message about the customer's own address.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Type: bugfix
Issue
Follow-up to #991, and the
developside of #1008. Customer e-mails and passwords were not handled the same way in every step:Solution
SubAccountEditValidatorandCustomerInfoValidatorcheck the normalized value in the store of the edited account, also when a case-only repair applies.ICustomerManagerServicegetsChangePassword(Customer, PasswordFormat, string), which changes the password of that account without an e-mail lookup. The existing method resolves the e-mail and calls it. Both write only the password fields through a newICustomerService.UpdateCustomerPassword, so a copy the caller loaded earlier cannot undo other edits.Password recovery and the admin panel keep the existing method. The API
SetPasswordalso keeps it, because it receives only an e-mail.No new resources, settings or migrations.
Breaking changes
ICustomerManagerServiceandICustomerServiceeach have a new member, so a custom implementation of either must add it. Callers are not affected. An account whose stored e-mail differs only in case now has that address checked on save, even when changing e-mails is turned off.Testing
Unit tests pass:
Grand.Web.Tests(403),Grand.Business.Authentication.Tests(399),Grand.Business.Customers.Tests(142),Grand.Web.Admin.Tests(1839). The new tests failed before the callers were switched. The full solution and the steps below were not run.HideSubAccountsTabtofalse, sign in on the storefront, add a sub-account, change its password, and check it can sign in with the new one.AllowUsersToChangeEmailon, change its e-mail to one with capitals and surrounding spaces, together with a new password. The address is saved trimmed and lowercased, and the sub-account can sign in. An address that is already taken is rejected in any form.Customer:RegisterCustomersPerStoreon and the same e-mail registered in two stores, change the password in one store. The account in the other store keeps its password.