Distinguish between "not found" and "multiple entries found" instead of
the generic "User does not exist or too many entries returned", making
it easier to diagnose issues in logs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ldap.Dial and ldap.DialTLS are deprecated in go-ldap/ldap/v3. Switch to
ldap.DialURL which is the recommended API. Also use fmt.Errorf with %w
for proper error wrapping.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The LDAP struct was mixing LDAP connection settings with unrelated mail
settings. Extract mail fields into a dedicated SMTPConfig struct with
its own global (mySMTP), keeping concerns cleanly separated.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LDAP connections were never closed, leaking TCP connections on every
request. Also refactors change.go from chained else-if to early returns
for cleaner defer placement.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace SHA512-based deterministic token with 32-byte crypto/rand token
- Store tokens server-side with 1-hour expiry and single-use semantics
- Remove genToken (previously broken due to time.Add immutability bug)
- Add CSRF double-submit cookie protection to change/lost/reset forms
- Remove token from form action URL (use hidden fields only, POST body)
- Add MailFrom field and SMTP_FROM env var for configurable sender address
- Add SMTP_PASSWORD_FILE env var for secure SMTP password loading
- Add PUBLIC_URL env var and --public-url flag for configurable reset link domain
- Use generic error messages in handlers to avoid information disclosure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use ldap.EscapeFilter() on all user-controlled inputs before interpolating
them into LDAP search filter strings in SearchDN and SearchMailAlias.
Prevents authentication bypass via filter manipulation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>