Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,10 @@ func (c *Config) UnmarshalYAML(unmarshal func(any) error) error {
return errors.New("at most one of rocketchat_token_id & rocketchat_token_id_file must be configured")
}

if len(c.Global.SMTPAuthSecret) > 0 && len(c.Global.SMTPAuthSecretFile) > 0 {
return fmt.Errorf("at most one of smtp_auth_secret & smtp_auth_secret_file must be configured")
}

if c.Global.WeChatAPISecret != "" && len(c.Global.WeChatAPISecretFile) > 0 {
return errors.New("at most one of wechat_api_secret & wechat_api_secret_file must be configured")
}
Expand Down Expand Up @@ -540,8 +544,9 @@ func (c *Config) UnmarshalYAML(unmarshal func(any) error) error {
ec.AuthPassword = c.Global.SMTPAuthPassword
ec.AuthPasswordFile = c.Global.SMTPAuthPasswordFile
}
if ec.AuthSecret == "" {
if ec.AuthSecret == "" && ec.AuthSecretFile == "" {
ec.AuthSecret = c.Global.SMTPAuthSecret
ec.AuthSecretFile = c.Global.SMTPAuthSecretFile
}
if ec.AuthIdentity == "" {
ec.AuthIdentity = c.Global.SMTPAuthIdentity
Expand Down Expand Up @@ -983,6 +988,7 @@ type GlobalConfig struct {
SMTPAuthPassword Secret `yaml:"smtp_auth_password,omitempty" json:"smtp_auth_password,omitempty"`
SMTPAuthPasswordFile string `yaml:"smtp_auth_password_file,omitempty" json:"smtp_auth_password_file,omitempty"`
SMTPAuthSecret Secret `yaml:"smtp_auth_secret,omitempty" json:"smtp_auth_secret,omitempty"`
SMTPAuthSecretFile string `yaml:"smtp_auth_secret_file,omitempty" json:"smtp_auth_secret_file,omitempty"`
SMTPAuthIdentity string `yaml:"smtp_auth_identity,omitempty" json:"smtp_auth_identity,omitempty"`
SMTPRequireTLS bool `yaml:"smtp_require_tls" json:"smtp_require_tls,omitempty"`
SMTPTLSConfig *commoncfg.TLSConfig `yaml:"smtp_tls_config,omitempty" json:"smtp_tls_config,omitempty"`
Expand Down
4 changes: 4 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,10 @@ func TestGlobalAndLocalSMTPPassword(t *testing.T) {

require.Equal(t, Secret("mysecret"), config.Receivers[0].EmailConfigs[2].AuthPassword, "third email should use password mysecret")
require.Emptyf(t, config.Receivers[0].EmailConfigs[2].AuthPasswordFile, "file field should be empty when password provided")

require.Equal(t, Secret("myprecious"), config.Receivers[0].EmailConfigs[3].AuthSecret, "fourth email should use secret myprecious")

require.Equal(t, "/tmp/localuser4secret", config.Receivers[0].EmailConfigs[4].AuthSecretFile, "fifth email should use secret file /tmp/localuser4secret")
}

func TestGroupByAll(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions config/notifiers.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ type EmailConfig struct {
AuthPassword Secret `yaml:"auth_password,omitempty" json:"auth_password,omitempty"`
AuthPasswordFile string `yaml:"auth_password_file,omitempty" json:"auth_password_file,omitempty"`
AuthSecret Secret `yaml:"auth_secret,omitempty" json:"auth_secret,omitempty"`
AuthSecretFile string `yaml:"auth_secret_file,omitempty" json:"auth_secret_file,omitempty"`
AuthIdentity string `yaml:"auth_identity,omitempty" json:"auth_identity,omitempty"`
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
HTML string `yaml:"html,omitempty" json:"html,omitempty"`
Expand Down
8 changes: 8 additions & 0 deletions config/testdata/conf.smtp-password-global-and-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@ receivers:
- to: 'three@example.org'
auth_username: 'localuser2'
auth_password: 'mysecret'
# Test auth secret
- to: 'four@exmaple.org'
auth_username: 'localuser3'
auth_secret: 'myprecious'
# Test auth secret from file
- to: 'five@exmaple.org'
auth_username: 'localuser4'
auth_secret_file: '/tmp/localuser4secret'
17 changes: 16 additions & 1 deletion notify/email/email.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ func (n *Email) auth(mechs string) (smtp.Auth, error) {
for mech := range strings.SplitSeq(mechs, " ") {
switch mech {
case "CRAM-MD5":
secret := string(n.conf.AuthSecret)
secret, secretErr := n.getAuthSecret()
if secretErr != nil {
err.Add(secretErr)
continue
}
if secret == "" {
err.Add(errors.New("missing secret for CRAM-MD5 auth mechanism"))
continue
Expand Down Expand Up @@ -410,3 +414,14 @@ func (n *Email) getPassword() (string, error) {
}
return string(n.conf.AuthPassword), nil
}

func (n *Email) getAuthSecret() (string, error) {
if len(n.conf.AuthSecretFile) > 0 {
content, err := os.ReadFile(n.conf.AuthSecretFile)
if err != nil {
return "", fmt.Errorf("could not read %s: %w", n.conf.AuthSecretFile, err)
}
return string(content), nil
}
return string(n.conf.AuthSecret), nil
}
110 changes: 110 additions & 0 deletions notify/email/email_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -870,3 +870,113 @@ func TestEmailNotifyWithThreading(t *testing.T) {
})
}
}

func TestEmailGetPassword(t *testing.T) {
passwordFile, err := os.CreateTemp("", "smtp-password")
require.NoError(t, err, "creating temp file failed")
_, err = passwordFile.WriteString("secret")
require.NoError(t, err, "writing to temp file failed")

for _, tc := range []struct {
title string
updateCfg func(*config.EmailConfig)

errMsg string
}{
{
title: "password from field",
updateCfg: func(cfg *config.EmailConfig) {
cfg.AuthPassword = "secret"
cfg.AuthPasswordFile = ""
},
},
{
title: "password from file field",
updateCfg: func(cfg *config.EmailConfig) {
cfg.AuthPassword = ""
cfg.AuthPasswordFile = passwordFile.Name()
},
},
{
title: "password file path incorrect",
updateCfg: func(cfg *config.EmailConfig) {
cfg.AuthPassword = ""
cfg.AuthPasswordFile = "/does/not/exist"
},
errMsg: "could not read",
},
} {
t.Run(tc.title, func(t *testing.T) {
email := &Email{
conf: &config.EmailConfig{},
}

tc.updateCfg(email.conf)

password, err := email.getPassword()
if len(tc.errMsg) > 0 {
require.Error(t, err)
require.Contains(t, err.Error(), tc.errMsg)
require.Empty(t, password)
} else {
require.NoError(t, err)
require.Equal(t, "secret", password)
}
})
}
}

func TestEmailGetSecret(t *testing.T) {
secretFile, err := os.CreateTemp("", "smtp-password")
require.NoError(t, err, "creating temp file failed")
_, err = secretFile.WriteString("secret")
require.NoError(t, err, "writing to temp file failed")

for _, tc := range []struct {
title string
updateCfg func(*config.EmailConfig)

errMsg string
}{
{
title: "secret from field",
updateCfg: func(cfg *config.EmailConfig) {
cfg.AuthSecret = "secret"
cfg.AuthSecretFile = ""
},
},
{
title: "secret from file field",
updateCfg: func(cfg *config.EmailConfig) {
cfg.AuthSecret = ""
cfg.AuthSecretFile = secretFile.Name()
},
},
{
title: "secret file path incorrect",
updateCfg: func(cfg *config.EmailConfig) {
cfg.AuthSecret = ""
cfg.AuthSecretFile = "/does/not/exist"
},
errMsg: "could not read",
},
} {
t.Run(tc.title, func(t *testing.T) {
email := &Email{
conf: &config.EmailConfig{},
}

tc.updateCfg(email.conf)

secret, err := email.getAuthSecret()
if len(tc.errMsg) > 0 {
require.Error(t, err)
require.Contains(t, err.Error(), tc.errMsg)
require.Empty(t, secret)
} else {
require.NoError(t, err)
require.Equal(t, "secret", secret)
}
})
}
}