Accept bare local part for mailbox creation; reject foreign domains with 400
closedProblem
On the domain page, adding a mailbox requires the full address:
test2 fails (splitAddr yields empty host → validateIdentity error →
HTTP 500), and [email protected] fails opaquely (500 instead of
a 400). Users should be able to type just test2 for
[email protected], and a foreign-domain address must be rejected
with a clear client error.
Proposal (decided: lowercase + trim the input)
- Normalize centrally in
appview.ProvisionAccount(covers UI + XRPC): trim + lowercase; no@→ append the domain’s own name;@-with-wrong-host (case-insensitive) → new typedErrDomainMismatch. - Map
ErrDomainMismatchto 400InvalidRequestinmail_create_account.go; friendly flash + redirect inui.createAccount(currently both paths surface 500s). - UI: update placeholder/help text (
test2works; full address accepted). - Lexicon: document that
MailCreateAccount_Input.Addressaccepts a bare local part (schema unchanged).
Files
internal/appview/provision.go,mail_create_account.go,internal/appview/ui/handler.go,domain.templ, lexicon docs
Acceptance
test2→ creates[email protected].[email protected]→ 400 / flash error, no writes.- Unit tests for the normalization matrix.
1 Comment
Implemented (bug 6651065)
Bare local parts accepted, foreign domains rejected with 4xx, all user errors inline.
internal/appview/provision.go- NewErrDomainMismatch,ErrInvalidAddress,ErrAccountExists+normalizeAccountAddress(raw, domainName): trim + lowercase whole input; no@→ append domain;@-with-wrong-host (case-insensitive) →ErrDomainMismatch; double-@/spaces/empty user-or-host →ErrInvalidAddress. Other local-part rules left to Stalwart per Q1. -ProvisionAccountnormalizes centrally (UI + XRPC), passes canonicald.Domainas host toaccount.Ensure, persists normalizedfullAddr(Q2: always lowercase).*account.AssignedErrorand sqliteUNIQUE constraintcollisions map toErrAccountExists. - NewIsUserInputError: sentinels +*stalwart.SetErrorFailure+invalidArguments/forbidden→ true;serverFail/serverUnavailable/infra → false (500).mail_create_account.go—ErrAccountExists→ 409Exists; mismatch/invalid/Stalwart-input → 400InvalidRequestwith no writes;store.ErrNotFoundstays 400; rest 500.ui/hx.go+ui/handler.go— typederrors.Isbranches replace the string STUB (doesn't belong… use just the name,valid mailbox name…,already exists). HX path already 422-inline viawriteMailboxError(list preserved); non-HX user errors now 303?msg=instead of 500 (Q3).domain.templ— placeholdertest2+ help line;domain_templ.goregenerated. LexiconcreateAccount.jsondocuments bare-local-part acceptance (schema unchanged, no API regen needed — generated structs don’t propagate property descriptions).Tests —
provision_test.go(13-case matrix + classifier),mail_create_account_test.go(foreign/invalid → 400, no writes),ui/create_account_test.go(HX 422 inline + list preserved, non-JS 303),hx_test.gotyped-error cases.go build ./...,go vet,go test -count=1 ./internal/appview/...all pass; integration suite skips (needs live Stalwart). One pre-existinggofmtmisalignment inProvisionDomainleft untouched.