Skip to content

Commit

Permalink
fix(notification): smtp auth not configured (#4647)
Browse files Browse the repository at this point in the history
This fixes an issue introduced by the pending migration to the new SMTP library in 0bb657e where the auth mechanism is never defined. This only affects commits that are yet to be versioned.
  • Loading branch information
james-d-elliott committed Dec 26, 2022
1 parent 3ecb9f1 commit 7b8ed46
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 1 deletion.
86 changes: 86 additions & 0 deletions internal/notification/smtp_auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package notification

import (
"fmt"
"net/smtp"
"strings"

gomail "github.com/wneessen/go-mail"
"github.com/wneessen/go-mail/auth"

"github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/utils"
)

// NewOpportunisticSMTPAuth is an opportunistic smtp.Auth implementation.
func NewOpportunisticSMTPAuth(config *schema.SMTPNotifierConfiguration) *OpportunisticSMTPAuth {
if config.Username == "" && config.Password == "" {
return nil
}

return &OpportunisticSMTPAuth{
username: config.Username,
password: config.Password,
host: config.Host,
}
}

// OpportunisticSMTPAuth is an opportunistic smtp.Auth implementation.
type OpportunisticSMTPAuth struct {
username, password, host string

satPreference []gomail.SMTPAuthType
sa smtp.Auth
}

// Start begins an authentication with a server.
// It returns the name of the authentication protocol
// and optionally data to include in the initial AUTH message
// sent to the server.
// If it returns a non-nil error, the SMTP client aborts
// the authentication attempt and closes the connection.
func (a *OpportunisticSMTPAuth) Start(server *smtp.ServerInfo) (proto string, toServer []byte, err error) {
for _, pref := range a.satPreference {
if utils.IsStringInSlice(string(pref), server.Auth) {
switch pref {
case gomail.SMTPAuthPlain:
a.sa = smtp.PlainAuth("", a.username, a.password, a.host)
case gomail.SMTPAuthLogin:
a.sa = auth.LoginAuth(a.username, a.password, a.host)
case gomail.SMTPAuthCramMD5:
a.sa = smtp.CRAMMD5Auth(a.username, a.password)
}

break
}
}

if a.sa == nil {
for _, sa := range server.Auth {
switch gomail.SMTPAuthType(sa) {
case gomail.SMTPAuthPlain:
a.sa = smtp.PlainAuth("", a.username, a.password, a.host)
case gomail.SMTPAuthLogin:
a.sa = auth.LoginAuth(a.username, a.password, a.host)
case gomail.SMTPAuthCramMD5:
a.sa = smtp.CRAMMD5Auth(a.username, a.password)
}
}
}

if a.sa == nil {
return "", nil, fmt.Errorf("unsupported SMTP AUTH types: %s", strings.Join(server.Auth, ", "))
}

return a.sa.Start(server)
}

// Next continues the authentication. The server has just sent
// the fromServer data. If more is true, the server expects a
// response, which Next should return as toServer; otherwise
// Next should return toServer == nil.
// If Next returns a non-nil error, the SMTP client aborts
// the authentication attempt and closes the connection.
func (a *OpportunisticSMTPAuth) Next(fromServer []byte, more bool) (toServer []byte, err error) {
return a.sa.Next(fromServer, more)
}
9 changes: 8 additions & 1 deletion internal/notification/smtp_notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ func NewSMTPNotifier(config *schema.SMTPNotifierConfiguration, certPool *x509.Ce
opts := []gomail.Option{
gomail.WithPort(config.Port),
gomail.WithTLSConfig(utils.NewTLSConfig(config.TLS, certPool)),
gomail.WithPassword(config.Password),
gomail.WithHELO(config.Identifier),
}

Expand Down Expand Up @@ -65,6 +64,7 @@ type SMTPNotifier struct {
opts []gomail.Option
}

// StartupCheck implements model.StartupCheck to perform startup check operations.
func (n *SMTPNotifier) StartupCheck() (err error) {
var client *gomail.Client

Expand All @@ -85,6 +85,7 @@ func (n *SMTPNotifier) StartupCheck() (err error) {
return nil
}

// Send a notification via the SMTPNotifier.
func (n *SMTPNotifier) Send(ctx context.Context, recipient mail.Address, subject string, et *templates.EmailTemplate, data any) (err error) {
msg := gomail.NewMsg(
gomail.WithMIMEVersion(gomail.Mime10),
Expand Down Expand Up @@ -118,10 +119,16 @@ func (n *SMTPNotifier) Send(ctx context.Context, recipient mail.Address, subject

var client *gomail.Client

n.log.Debugf("creating client with %d options: %+v", len(n.opts), n.opts)

if client, err = gomail.NewClient(n.config.Host, n.opts...); err != nil {
return fmt.Errorf("notifier: smtp: failed to establish client: %w", err)
}

if auth := NewOpportunisticSMTPAuth(n.config); auth != nil {
client.SetSMTPAuthCustom(auth)
}

if err = client.DialWithContext(ctx); err != nil {
return fmt.Errorf("notifier: smtp: failed to dial connection: %w", err)
}
Expand Down

0 comments on commit 7b8ed46

Please sign in to comment.