2019-04-24 21:52:08 +00:00
|
|
|
package notification
|
|
|
|
|
|
|
|
import (
|
2022-12-23 05:06:49 +00:00
|
|
|
"context"
|
2019-12-20 18:40:01 +00:00
|
|
|
"crypto/tls"
|
2019-12-30 02:03:51 +00:00
|
|
|
"crypto/x509"
|
2019-04-24 21:52:08 +00:00
|
|
|
"fmt"
|
2022-07-18 00:56:09 +00:00
|
|
|
"net/mail"
|
2022-12-26 21:32:00 +00:00
|
|
|
"os"
|
2019-12-20 18:40:01 +00:00
|
|
|
"strings"
|
2019-04-24 21:52:08 +00:00
|
|
|
|
2021-11-30 11:15:21 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2022-12-23 05:06:49 +00:00
|
|
|
gomail "github.com/wneessen/go-mail"
|
2021-11-30 11:15:21 +00:00
|
|
|
|
2021-08-11 01:04:35 +00:00
|
|
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
|
|
|
"github.com/authelia/authelia/v4/internal/logging"
|
2022-07-18 00:56:09 +00:00
|
|
|
"github.com/authelia/authelia/v4/internal/templates"
|
2021-08-11 01:04:35 +00:00
|
|
|
"github.com/authelia/authelia/v4/internal/utils"
|
2019-04-24 21:52:08 +00:00
|
|
|
)
|
|
|
|
|
2022-07-18 00:56:09 +00:00
|
|
|
// NewSMTPNotifier creates a SMTPNotifier using the notifier configuration.
|
2022-12-23 05:06:49 +00:00
|
|
|
func NewSMTPNotifier(config *schema.SMTPNotifierConfiguration, certPool *x509.CertPool) *SMTPNotifier {
|
2022-12-26 21:32:00 +00:00
|
|
|
var tlsconfig *tls.Config
|
|
|
|
|
|
|
|
if config.TLS != nil {
|
|
|
|
tlsconfig = utils.NewTLSConfig(config.TLS, certPool)
|
|
|
|
}
|
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
opts := []gomail.Option{
|
|
|
|
gomail.WithPort(config.Port),
|
2022-12-26 21:32:00 +00:00
|
|
|
gomail.WithTLSConfig(tlsconfig),
|
2022-12-23 05:06:49 +00:00
|
|
|
gomail.WithHELO(config.Identifier),
|
2022-12-26 21:32:00 +00:00
|
|
|
gomail.WithTimeout(config.Timeout),
|
|
|
|
gomail.WithoutNoop(),
|
|
|
|
}
|
|
|
|
|
|
|
|
ssl := config.Port == smtpPortSUBMISSIONS
|
|
|
|
|
|
|
|
if ssl {
|
|
|
|
opts = append(opts, gomail.WithSSL())
|
2022-07-18 00:56:09 +00:00
|
|
|
}
|
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
switch {
|
2022-12-26 21:32:00 +00:00
|
|
|
case ssl:
|
|
|
|
break
|
2022-12-23 05:06:49 +00:00
|
|
|
case config.DisableStartTLS:
|
|
|
|
opts = append(opts, gomail.WithTLSPolicy(gomail.NoTLS))
|
|
|
|
case config.DisableRequireTLS:
|
|
|
|
opts = append(opts, gomail.WithTLSPolicy(gomail.TLSOpportunistic))
|
|
|
|
default:
|
|
|
|
opts = append(opts, gomail.WithTLSPolicy(gomail.TLSMandatory))
|
2022-07-18 00:56:09 +00:00
|
|
|
}
|
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
var domain string
|
2022-07-18 00:56:09 +00:00
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
at := strings.LastIndex(config.Sender.Address, "@")
|
2022-07-18 00:56:09 +00:00
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
if at >= 0 {
|
2022-12-26 23:54:58 +00:00
|
|
|
domain = config.Sender.Address[at+1:]
|
|
|
|
} else {
|
|
|
|
domain = "localhost.localdomain"
|
2022-07-18 00:56:09 +00:00
|
|
|
}
|
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
return &SMTPNotifier{
|
|
|
|
config: config,
|
|
|
|
domain: domain,
|
|
|
|
tls: utils.NewTLSConfig(config.TLS, certPool),
|
|
|
|
log: logging.Logger(),
|
|
|
|
opts: opts,
|
2022-07-18 00:56:09 +00:00
|
|
|
}
|
2022-12-23 05:06:49 +00:00
|
|
|
}
|
2022-07-18 00:56:09 +00:00
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
// SMTPNotifier a notifier to send emails to SMTP servers.
|
|
|
|
type SMTPNotifier struct {
|
|
|
|
config *schema.SMTPNotifierConfiguration
|
|
|
|
domain string
|
|
|
|
tls *tls.Config
|
|
|
|
log *logrus.Logger
|
|
|
|
opts []gomail.Option
|
2022-07-18 00:56:09 +00:00
|
|
|
}
|
|
|
|
|
2022-12-26 10:39:19 +00:00
|
|
|
// StartupCheck implements model.StartupCheck to perform startup check operations.
|
2022-12-23 05:06:49 +00:00
|
|
|
func (n *SMTPNotifier) StartupCheck() (err error) {
|
|
|
|
var client *gomail.Client
|
2022-07-18 00:56:09 +00:00
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
if client, err = gomail.NewClient(n.config.Host, n.opts...); err != nil {
|
|
|
|
return fmt.Errorf("failed to establish client: %w", err)
|
2022-07-18 00:56:09 +00:00
|
|
|
}
|
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
ctx := context.Background()
|
2022-07-18 00:56:09 +00:00
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
if err = client.DialWithContext(ctx); err != nil {
|
|
|
|
return fmt.Errorf("failed to dial connection: %w", err)
|
2022-07-18 00:56:09 +00:00
|
|
|
}
|
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
if err = client.Close(); err != nil {
|
|
|
|
return fmt.Errorf("failed to close connection: %w", err)
|
2022-07-18 00:56:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-12-26 10:39:19 +00:00
|
|
|
// Send a notification via the SMTPNotifier.
|
2022-12-23 05:06:49 +00:00
|
|
|
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),
|
2022-12-26 21:32:00 +00:00
|
|
|
gomail.WithBoundary(utils.RandomString(30, utils.CharSetAlphaNumeric)),
|
2022-07-18 00:56:09 +00:00
|
|
|
)
|
|
|
|
|
2022-12-26 21:32:00 +00:00
|
|
|
setMessageID(msg, n.domain)
|
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
if err = msg.From(n.config.Sender.String()); err != nil {
|
|
|
|
return fmt.Errorf("notifier: smtp: failed to set from address: %w", err)
|
2022-07-18 00:56:09 +00:00
|
|
|
}
|
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
if err = msg.AddTo(recipient.String()); err != nil {
|
|
|
|
return fmt.Errorf("notifier: smtp: failed to set to address: %w", err)
|
2022-07-18 00:56:09 +00:00
|
|
|
}
|
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
msg.Subject(strings.ReplaceAll(n.config.Subject, "{title}", subject))
|
2022-07-18 00:56:09 +00:00
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
switch {
|
|
|
|
case n.config.DisableHTMLEmails:
|
|
|
|
if err = msg.SetBodyTextTemplate(et.Text, data); err != nil {
|
|
|
|
return fmt.Errorf("notifier: smtp: failed to set body: text template errored: %w", err)
|
2019-12-20 18:40:01 +00:00
|
|
|
}
|
2020-05-06 00:52:06 +00:00
|
|
|
default:
|
2022-12-23 05:06:49 +00:00
|
|
|
if err = msg.AddAlternativeHTMLTemplate(et.HTML, data); err != nil {
|
|
|
|
return fmt.Errorf("notifier: smtp: failed to set body: html template errored: %w", err)
|
2020-05-06 00:52:06 +00:00
|
|
|
}
|
2020-05-05 19:35:32 +00:00
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
if err = msg.AddAlternativeTextTemplate(et.Text, data); err != nil {
|
|
|
|
return fmt.Errorf("notifier: smtp: failed to set body: text template errored: %w", err)
|
2019-12-30 02:03:51 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-05 19:35:32 +00:00
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
var client *gomail.Client
|
2022-07-09 02:40:02 +00:00
|
|
|
|
2022-12-26 10:39:19 +00:00
|
|
|
n.log.Debugf("creating client with %d options: %+v", len(n.opts), n.opts)
|
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
if client, err = gomail.NewClient(n.config.Host, n.opts...); err != nil {
|
|
|
|
return fmt.Errorf("notifier: smtp: failed to establish client: %w", err)
|
2021-08-10 00:52:41 +00:00
|
|
|
}
|
2020-05-05 19:35:32 +00:00
|
|
|
|
2022-12-26 10:39:19 +00:00
|
|
|
if auth := NewOpportunisticSMTPAuth(n.config); auth != nil {
|
|
|
|
client.SetSMTPAuthCustom(auth)
|
|
|
|
}
|
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
if err = client.DialWithContext(ctx); err != nil {
|
|
|
|
return fmt.Errorf("notifier: smtp: failed to dial connection: %w", err)
|
2022-08-26 21:39:20 +00:00
|
|
|
}
|
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
if err = client.Send(msg); err != nil {
|
|
|
|
return fmt.Errorf("notifier: smtp: failed to send message: %w", err)
|
2022-08-26 21:39:20 +00:00
|
|
|
}
|
|
|
|
|
2022-12-23 05:06:49 +00:00
|
|
|
if err = client.Close(); err != nil {
|
|
|
|
return fmt.Errorf("notifier: smtp: failed to close connection: %w", err)
|
2019-04-24 21:52:08 +00:00
|
|
|
}
|
2020-05-05 19:35:32 +00:00
|
|
|
|
2019-12-30 02:03:51 +00:00
|
|
|
return nil
|
|
|
|
}
|
2022-12-26 21:32:00 +00:00
|
|
|
|
|
|
|
func setMessageID(msg *gomail.Msg, domain string) {
|
|
|
|
rn, _ := utils.RandomInt(100000000)
|
|
|
|
rm, _ := utils.RandomInt(10000)
|
|
|
|
rs := utils.RandomString(17, utils.CharSetAlphaNumeric)
|
|
|
|
pid := os.Getpid() + rm
|
|
|
|
|
|
|
|
msg.SetMessageIDWithValue(fmt.Sprintf("%d.%d%d.%s@%s", pid, rn, rm, rs, domain))
|
|
|
|
}
|