fix: resolve default sender to canonical identity on local-part login
When authenticating with a local-part username (e.g. 'user' instead of 'user@domain.tld') on Stalwart 0.15.x, the default sender could resolve to an alias identity instead of the canonical mailbox address. - Add emailMatchesUsername() helper that matches local-part usernames against full email addresses (e.g. 'user' matches 'user@domain.tld') - Prefer canonical identities (mayDelete=false) over aliases as tiebreaker - Add preferredPrimaryId to identity store (persisted to localStorage) so users can explicitly set their default sender - Add 'Set as Primary' star button in identity manager modal - Fix sendEmail() fallback identity resolution for local-part usernames - Add i18n strings for all 8 supported locales Fixes #43
This commit is contained in:
+34
-5
@@ -52,12 +52,41 @@ function classifyLoginError(error: unknown): string {
|
||||
return 'generic';
|
||||
}
|
||||
|
||||
function loadIdentities(rawIdentities: Identity[], username: string): { identities: Identity[]; primaryIdentity: Identity | null } {
|
||||
const identities = [...rawIdentities].sort((a, b) => {
|
||||
const aMatch = a.email === username ? -1 : 0;
|
||||
const bMatch = b.email === username ? -1 : 0;
|
||||
return aMatch - bMatch;
|
||||
function emailMatchesUsername(email: string, username: string): boolean {
|
||||
if (email === username) return true;
|
||||
// Handle local-part login: username "user" should match "user@domain.tld"
|
||||
if (!username.includes('@') && email.split('@')[0] === username) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function sortIdentities(rawIdentities: Identity[], username: string): Identity[] {
|
||||
return [...rawIdentities].sort((a, b) => {
|
||||
const aMatch = emailMatchesUsername(a.email, username);
|
||||
const bMatch = emailMatchesUsername(b.email, username);
|
||||
if (aMatch && !bMatch) return -1;
|
||||
if (!aMatch && bMatch) return 1;
|
||||
// Among matching identities, prefer canonical (non-deletable) over aliases
|
||||
if (aMatch && bMatch) {
|
||||
if (!a.mayDelete && b.mayDelete) return -1;
|
||||
if (a.mayDelete && !b.mayDelete) return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
function loadIdentities(rawIdentities: Identity[], username: string): { identities: Identity[]; primaryIdentity: Identity | null } {
|
||||
const preferredPrimaryId = useIdentityStore.getState().preferredPrimaryId;
|
||||
const identities = sortIdentities(rawIdentities, username);
|
||||
|
||||
// If user has a preferred primary, move it to front
|
||||
if (preferredPrimaryId) {
|
||||
const idx = identities.findIndex((id) => id.id === preferredPrimaryId);
|
||||
if (idx > 0) {
|
||||
const [preferred] = identities.splice(idx, 1);
|
||||
identities.unshift(preferred);
|
||||
}
|
||||
}
|
||||
|
||||
const primaryIdentity = identities[0] ?? null;
|
||||
useIdentityStore.getState().setIdentities(identities);
|
||||
return { identities, primaryIdentity };
|
||||
|
||||
Reference in New Issue
Block a user