fixes
This commit is contained in:
parent
1ddf6fe0c8
commit
4aeeee425b
5 changed files with 51 additions and 6 deletions
|
|
@ -67,7 +67,7 @@ TELESRV_SMTP_PORT=587
|
||||||
TELESRV_SMTP_USERNAME=
|
TELESRV_SMTP_USERNAME=
|
||||||
TELESRV_SMTP_PASSWORD=
|
TELESRV_SMTP_PASSWORD=
|
||||||
TELESRV_SMTP_FROM=
|
TELESRV_SMTP_FROM=
|
||||||
TELESRV_SMTP_FROM_NAME=telesrv
|
TELESRV_SMTP_FROM_NAME=OwpenGram
|
||||||
TELESRV_SMTP_TLS=starttls
|
TELESRV_SMTP_TLS=starttls
|
||||||
TELESRV_SMTP_TIMEOUT=10s
|
TELESRV_SMTP_TIMEOUT=10s
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -573,6 +573,7 @@ func run(logger *zap.Logger) error {
|
||||||
Password: cfg.SMTPPassword,
|
Password: cfg.SMTPPassword,
|
||||||
From: cfg.SMTPFrom,
|
From: cfg.SMTPFrom,
|
||||||
FromName: cfg.SMTPFromName,
|
FromName: cfg.SMTPFromName,
|
||||||
|
AppName: cfg.PublicAppName,
|
||||||
TLSMode: cfg.SMTPTLSMode,
|
TLSMode: cfg.SMTPTLSMode,
|
||||||
Timeout: cfg.SMTPTimeout,
|
Timeout: cfg.SMTPTimeout,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -525,7 +525,7 @@ func Load() (Config, error) {
|
||||||
SMTPUsername: envOr("TELESRV_SMTP_USERNAME", ""),
|
SMTPUsername: envOr("TELESRV_SMTP_USERNAME", ""),
|
||||||
SMTPPassword: envOr("TELESRV_SMTP_PASSWORD", ""),
|
SMTPPassword: envOr("TELESRV_SMTP_PASSWORD", ""),
|
||||||
SMTPFrom: envOr("TELESRV_SMTP_FROM", ""),
|
SMTPFrom: envOr("TELESRV_SMTP_FROM", ""),
|
||||||
SMTPFromName: envOr("TELESRV_SMTP_FROM_NAME", "telesrv"),
|
SMTPFromName: envOr("TELESRV_SMTP_FROM_NAME", "OwpenGram"),
|
||||||
SMTPTLSMode: strings.ToLower(strings.TrimSpace(envOr("TELESRV_SMTP_TLS", "starttls"))),
|
SMTPTLSMode: strings.ToLower(strings.TrimSpace(envOr("TELESRV_SMTP_TLS", "starttls"))),
|
||||||
SMTPTimeout: envDurationOr("TELESRV_SMTP_TIMEOUT", 10*time.Second),
|
SMTPTimeout: envDurationOr("TELESRV_SMTP_TIMEOUT", 10*time.Second),
|
||||||
LangPackSeedDir: envOr("TELESRV_LANGPACK_SEED_DIR", "data/langpack"),
|
LangPackSeedDir: envOr("TELESRV_LANGPACK_SEED_DIR", "data/langpack"),
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,14 @@ type Config struct {
|
||||||
Password string
|
Password string
|
||||||
From string
|
From string
|
||||||
FromName string
|
FromName string
|
||||||
TLSMode string
|
// AppName is the product name shown in the email subject/body (e.g.
|
||||||
Timeout time.Duration
|
// "Your <AppName> login code"). Defaults to "telesrv" if empty, matching
|
||||||
|
// this package's other defaults — callers should pass the same brand
|
||||||
|
// name used elsewhere (e.g. Config.PublicAppName), or codes will read as
|
||||||
|
// coming from "telesrv" regardless of the operator's own branding.
|
||||||
|
AppName string
|
||||||
|
TLSMode string
|
||||||
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
type Sender struct {
|
type Sender struct {
|
||||||
|
|
@ -41,6 +47,9 @@ func New(cfg Config) *Sender {
|
||||||
if strings.TrimSpace(cfg.From) == "" {
|
if strings.TrimSpace(cfg.From) == "" {
|
||||||
cfg.From = cfg.Username
|
cfg.From = cfg.Username
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(cfg.AppName) == "" {
|
||||||
|
cfg.AppName = "telesrv"
|
||||||
|
}
|
||||||
return &Sender{cfg: cfg}
|
return &Sender{cfg: cfg}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,8 +61,7 @@ func (s *Sender) Deliver(ctx context.Context, req otpdelivery.Request) (otpdeliv
|
||||||
return otpdelivery.Result{}, fmt.Errorf("smtp cannot deliver channel %q", req.Channel)
|
return otpdelivery.Result{}, fmt.Errorf("smtp cannot deliver channel %q", req.Channel)
|
||||||
}
|
}
|
||||||
ttl := time.Until(req.ExpiresAt)
|
ttl := time.Until(req.ExpiresAt)
|
||||||
subject := "Your telesrv login code"
|
subject, body := emailContent(s.cfg.AppName, req.Code, ttl)
|
||||||
body := fmt.Sprintf("Your telesrv login code is %s.\n\nThis code expires in %s. If you did not request it, ignore this email.\n", req.Code, humanTTL(ttl))
|
|
||||||
if err := s.send(ctx, req.Recipient, subject, body); err != nil {
|
if err := s.send(ctx, req.Recipient, subject, body); err != nil {
|
||||||
return otpdelivery.Result{}, err
|
return otpdelivery.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -147,6 +155,15 @@ func buildMessage(from, to, subject, body string) []byte {
|
||||||
return b.Bytes()
|
return b.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// emailContent builds the login-code email subject/body, branded with the
|
||||||
|
// operator's configured product name (Config.AppName) instead of the
|
||||||
|
// package's internal "telesrv" fallback.
|
||||||
|
func emailContent(appName, code string, ttl time.Duration) (subject, body string) {
|
||||||
|
subject = fmt.Sprintf("Your %s login code", appName)
|
||||||
|
body = fmt.Sprintf("Your %s login code is %s.\n\nThis code expires in %s. If you did not request it, ignore this email.\n", appName, code, humanTTL(ttl))
|
||||||
|
return subject, body
|
||||||
|
}
|
||||||
|
|
||||||
func humanTTL(ttl time.Duration) string {
|
func humanTTL(ttl time.Duration) string {
|
||||||
if ttl <= 0 {
|
if ttl <= 0 {
|
||||||
return "a short time"
|
return "a short time"
|
||||||
|
|
|
||||||
|
|
@ -29,3 +29,30 @@ func TestHumanTTLRoundsNetworkSkew(t *testing.T) {
|
||||||
t.Fatalf("humanTTL = %q", got)
|
t.Fatalf("humanTTL = %q", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestEmailContentUsesConfiguredAppName asserts the login-code email is
|
||||||
|
// branded with the operator's configured product name, not the package's
|
||||||
|
// internal "telesrv" fallback — the subject/body previously always said
|
||||||
|
// "Your telesrv login code" regardless of Config.AppName.
|
||||||
|
func TestEmailContentUsesConfiguredAppName(t *testing.T) {
|
||||||
|
subject, body := emailContent("OwpenGram", "12345", 5*time.Minute)
|
||||||
|
if subject != "Your OwpenGram login code" {
|
||||||
|
t.Fatalf("subject = %q, want %q", subject, "Your OwpenGram login code")
|
||||||
|
}
|
||||||
|
if !strings.Contains(body, "Your OwpenGram login code is 12345.") {
|
||||||
|
t.Fatalf("body missing branded code line: %q", body)
|
||||||
|
}
|
||||||
|
if strings.Contains(subject, "telesrv") || strings.Contains(body, "telesrv") {
|
||||||
|
t.Fatalf("email still mentions telesrv instead of the configured app name:\nsubject=%q\nbody=%q", subject, body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNewDefaultsAppNameToTelesrvWhenUnset asserts the package's own
|
||||||
|
// self-contained fallback (used only if a caller forgets to pass AppName)
|
||||||
|
// still works, mirroring FromName/TLSMode/Timeout's existing defaulting.
|
||||||
|
func TestNewDefaultsAppNameToTelesrvWhenUnset(t *testing.T) {
|
||||||
|
sender := New(Config{Host: "smtp.example.test", Port: 25, From: "noreply@example.test"})
|
||||||
|
if sender.cfg.AppName != "telesrv" {
|
||||||
|
t.Fatalf("cfg.AppName = %q, want default %q", sender.cfg.AppName, "telesrv")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue