Skip to content

Commit f23a050

Browse files
authored
feat: support optional SMTP auth (coder#14533)
1 parent 0eca1fc commit f23a050

File tree

2 files changed

+27
-5
lines changed

2 files changed

+27
-5
lines changed

coderd/notifications/dispatch/smtp.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ type SMTPHandler struct {
5353
cfg codersdk.NotificationsEmailConfig
5454
log slog.Logger
5555

56-
loginWarnOnce sync.Once
56+
noAuthWarnOnce sync.Once
57+
loginWarnOnce sync.Once
5758

5859
helpers template.FuncMap
5960
}
@@ -136,14 +137,20 @@ func (s *SMTPHandler) dispatch(subject, htmlBody, plainBody, to string) Delivery
136137

137138
// Check for authentication capabilities.
138139
if ok, avail := c.Extension("AUTH"); ok {
139-
// Ensure the auth mechanisms available are ones we can use.
140+
// Ensure the auth mechanisms available are ones we can use, and create a SASL client.
140141
auth, err := s.auth(ctx, avail)
141142
if err != nil {
142143
return true, xerrors.Errorf("determine auth mechanism: %w", err)
143144
}
144145

145-
// If so, use the auth mechanism to authenticate.
146-
if auth != nil {
146+
if auth == nil {
147+
// If we get here, no SASL client (which handles authentication) was returned.
148+
// This is expected if auth is supported by the smarthost BUT no authentication details were configured.
149+
s.noAuthWarnOnce.Do(func() {
150+
s.log.Warn(ctx, "skipping auth; no authentication client created")
151+
})
152+
} else {
153+
// We have a SASL client, use it to authenticate.
147154
if err := c.Auth(auth); err != nil {
148155
return true, xerrors.Errorf("%T auth: %w", auth, err)
149156
}
@@ -431,6 +438,12 @@ func (s *SMTPHandler) loadCertificate() (*tls.Certificate, error) {
431438
func (s *SMTPHandler) auth(ctx context.Context, mechs string) (sasl.Client, error) {
432439
username := s.cfg.Auth.Username.String()
433440

441+
// All auth mechanisms require username, so if one is not defined then don't return an auth client.
442+
if username == "" {
443+
// nolint:nilnil // This is a valid response.
444+
return nil, nil
445+
}
446+
434447
var errs error
435448
list := strings.Split(mechs, " ")
436449
for _, mech := range list {

coderd/notifications/dispatch/smtp_test.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ func TestSMTP(t *testing.T) {
203203
retryable: false,
204204
},
205205
{
206-
// No auth, no problem!
207206
name: "No auth mechanisms supported, none configured",
208207
authMechs: []string{},
209208
cfg: codersdk.NotificationsEmailConfig{
@@ -213,6 +212,16 @@ func TestSMTP(t *testing.T) {
213212
toAddrs: []string{to},
214213
expectedAuthMeth: "",
215214
},
215+
{
216+
name: "Auth mechanisms supported optionally, none configured",
217+
authMechs: []string{sasl.Login, sasl.Plain},
218+
cfg: codersdk.NotificationsEmailConfig{
219+
Hello: hello,
220+
From: from,
221+
},
222+
toAddrs: []string{to},
223+
expectedAuthMeth: "",
224+
},
216225
/**
217226
* TLS connections
218227
*/

0 commit comments

Comments
 (0)