[FEATURE] Customizable Email Subject (#830)
* [FEATURE] Customizable Email Subject * allow users to optionally change email subject * this is so they can more easily communicate the source of the email * Update docs/configuration/notifier/smtp.md Co-Authored-By: Amir Zarrinkafsh <nightah@me.com> Co-authored-by: Clément Michaud <clement.michaud34@gmail.com> Co-authored-by: Amir Zarrinkafsh <nightah@me.com>pull/832/head^2
parent
aae665eff2
commit
2fed503e5e
|
@ -361,6 +361,9 @@ notifier:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 1025
|
port: 1025
|
||||||
sender: admin@example.com
|
sender: admin@example.com
|
||||||
|
# Subject configuration of the emails sent.
|
||||||
|
# {title} is replaced by the text from the notifier
|
||||||
|
subject: "[Authelia] {title}"
|
||||||
## disable_require_tls: false
|
## disable_require_tls: false
|
||||||
## disable_verify_cert: false
|
## disable_verify_cert: false
|
||||||
## trusted_cert: ""
|
## trusted_cert: ""
|
||||||
|
|
|
@ -12,7 +12,16 @@ nav_order: 2
|
||||||
It can be configured as described below.
|
It can be configured as described below.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
# Configuration of the notification system.
|
||||||
|
#
|
||||||
|
# Notifications are sent to users when they require a password reset, a u2f
|
||||||
|
# registration or a TOTP registration.
|
||||||
|
# Use only an available configuration: filesystem, smtp
|
||||||
notifier:
|
notifier:
|
||||||
|
# For testing purpose, notifications can be sent in a file
|
||||||
|
## filesystem:
|
||||||
|
## filename: /tmp/authelia/notification.txt
|
||||||
|
|
||||||
# Use a SMTP server for sending notifications. Authelia uses PLAIN or LOGIN method to authenticate.
|
# Use a SMTP server for sending notifications. Authelia uses PLAIN or LOGIN method to authenticate.
|
||||||
# [Security] By default Authelia will:
|
# [Security] By default Authelia will:
|
||||||
# - force all SMTP connections over TLS including unauthenticated connections
|
# - force all SMTP connections over TLS including unauthenticated connections
|
||||||
|
@ -31,6 +40,9 @@ notifier:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 1025
|
port: 1025
|
||||||
sender: admin@example.com
|
sender: admin@example.com
|
||||||
|
# Subject configuration of the emails sent.
|
||||||
|
# {title} is replaced by the text from the notifier
|
||||||
|
subject: "[Authelia] {title}"
|
||||||
## disable_require_tls: false
|
## disable_require_tls: false
|
||||||
## disable_verify_cert: false
|
## disable_verify_cert: false
|
||||||
## trusted_cert: ""
|
## trusted_cert: ""
|
||||||
|
@ -50,4 +62,4 @@ notifier:
|
||||||
sender: admin@example.com
|
sender: admin@example.com
|
||||||
host: smtp.gmail.com
|
host: smtp.gmail.com
|
||||||
port: 587
|
port: 587
|
||||||
```
|
```
|
||||||
|
|
|
@ -10,6 +10,7 @@ type SMTPNotifierConfiguration struct {
|
||||||
Username string `mapstructure:"username"`
|
Username string `mapstructure:"username"`
|
||||||
Password string `mapstructure:"password"`
|
Password string `mapstructure:"password"`
|
||||||
Sender string `mapstructure:"sender"`
|
Sender string `mapstructure:"sender"`
|
||||||
|
Subject string `mapstructure:"subject"`
|
||||||
Host string `mapstructure:"host"`
|
Host string `mapstructure:"host"`
|
||||||
Port int `mapstructure:"port"`
|
Port int `mapstructure:"port"`
|
||||||
TrustedCert string `mapstructure:"trusted_cert"`
|
TrustedCert string `mapstructure:"trusted_cert"`
|
||||||
|
@ -22,3 +23,7 @@ type NotifierConfiguration struct {
|
||||||
FileSystem *FileSystemNotifierConfiguration `mapstructure:"filesystem"`
|
FileSystem *FileSystemNotifierConfiguration `mapstructure:"filesystem"`
|
||||||
SMTP *SMTPNotifierConfiguration `mapstructure:"smtp"`
|
SMTP *SMTPNotifierConfiguration `mapstructure:"smtp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var DefaultSMTPNotifierConfiguration = SMTPNotifierConfiguration{
|
||||||
|
Subject: "[Authelia] {title}",
|
||||||
|
}
|
||||||
|
|
|
@ -37,6 +37,10 @@ func ValidateNotifier(configuration *schema.NotifierConfiguration, validator *sc
|
||||||
if configuration.SMTP.Sender == "" {
|
if configuration.SMTP.Sender == "" {
|
||||||
validator.Push(fmt.Errorf("Sender of SMTP notifier must be provided"))
|
validator.Push(fmt.Errorf("Sender of SMTP notifier must be provided"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if configuration.SMTP.Subject == "" {
|
||||||
|
configuration.SMTP.Subject = schema.DefaultSMTPNotifierConfiguration.Subject
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ func isTokenUserValidFor2FARegistration(ctx *middlewares.AutheliaCtx, username s
|
||||||
|
|
||||||
// SecondFactorTOTPIdentityStart the handler for initiating the identity validation.
|
// SecondFactorTOTPIdentityStart the handler for initiating the identity validation.
|
||||||
var SecondFactorTOTPIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{
|
var SecondFactorTOTPIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{
|
||||||
MailSubject: "[Authelia] Register your mobile",
|
|
||||||
MailTitle: "Register your mobile",
|
MailTitle: "Register your mobile",
|
||||||
MailButtonContent: "Register",
|
MailButtonContent: "Register",
|
||||||
TargetEndpoint: "/one-time-password/register",
|
TargetEndpoint: "/one-time-password/register",
|
||||||
|
|
|
@ -16,7 +16,6 @@ var u2fConfig = &u2f.Config{
|
||||||
|
|
||||||
// SecondFactorU2FIdentityStart the handler for initiating the identity validation.
|
// SecondFactorU2FIdentityStart the handler for initiating the identity validation.
|
||||||
var SecondFactorU2FIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{
|
var SecondFactorU2FIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{
|
||||||
MailSubject: "[Authelia] Register your key",
|
|
||||||
MailTitle: "Register your key",
|
MailTitle: "Register your key",
|
||||||
MailButtonContent: "Register",
|
MailButtonContent: "Register",
|
||||||
TargetEndpoint: "/security-key/register",
|
TargetEndpoint: "/security-key/register",
|
||||||
|
|
|
@ -35,7 +35,6 @@ func identityRetrieverFromStorage(ctx *middlewares.AutheliaCtx) (*session.Identi
|
||||||
// ResetPasswordIdentityStart the handler for initiating the identity validation for resetting a password.
|
// ResetPasswordIdentityStart the handler for initiating the identity validation for resetting a password.
|
||||||
// We need to ensure the attacker cannot perform user enumeration by always replying with 200 whatever what happens in backend.
|
// We need to ensure the attacker cannot perform user enumeration by always replying with 200 whatever what happens in backend.
|
||||||
var ResetPasswordIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{
|
var ResetPasswordIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{
|
||||||
MailSubject: "[Authelia] Reset your password",
|
|
||||||
MailTitle: "Reset your password",
|
MailTitle: "Reset your password",
|
||||||
MailButtonContent: "Reset",
|
MailButtonContent: "Reset",
|
||||||
TargetEndpoint: "/reset-password/step2",
|
TargetEndpoint: "/reset-password/step2",
|
||||||
|
|
|
@ -78,7 +78,7 @@ func IdentityVerificationStart(args IdentityVerificationStartArgs) RequestHandle
|
||||||
|
|
||||||
ctx.Logger.Debugf("Sending an email to user %s (%s) to confirm identity for registering a device.",
|
ctx.Logger.Debugf("Sending an email to user %s (%s) to confirm identity for registering a device.",
|
||||||
identity.Username, identity.Email)
|
identity.Username, identity.Email)
|
||||||
err = ctx.Providers.Notifier.Send(identity.Email, args.MailSubject, buf.String())
|
err = ctx.Providers.Notifier.Send(identity.Email, args.MailTitle, buf.String())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(err, operationFailedMessage)
|
ctx.Error(err, operationFailedMessage)
|
||||||
|
|
|
@ -19,7 +19,6 @@ func newArgs(retriever func(ctx *middlewares.AutheliaCtx) (*session.Identity, er
|
||||||
return middlewares.IdentityVerificationStartArgs{
|
return middlewares.IdentityVerificationStartArgs{
|
||||||
ActionClaim: "Claim",
|
ActionClaim: "Claim",
|
||||||
MailButtonContent: "Register",
|
MailButtonContent: "Register",
|
||||||
MailSubject: "Subject",
|
|
||||||
MailTitle: "Title",
|
MailTitle: "Title",
|
||||||
TargetEndpoint: "/target",
|
TargetEndpoint: "/target",
|
||||||
IdentityRetrieverFunc: retriever,
|
IdentityRetrieverFunc: retriever,
|
||||||
|
@ -77,7 +76,7 @@ func TestShouldFailSendingAnEmail(t *testing.T) {
|
||||||
Return(nil)
|
Return(nil)
|
||||||
|
|
||||||
mock.NotifierMock.EXPECT().
|
mock.NotifierMock.EXPECT().
|
||||||
Send(gomock.Eq("john@example.com"), gomock.Eq("Subject"), gomock.Any()).
|
Send(gomock.Eq("john@example.com"), gomock.Eq("Title"), gomock.Any()).
|
||||||
Return(fmt.Errorf("no notif"))
|
Return(fmt.Errorf("no notif"))
|
||||||
|
|
||||||
args := newArgs(defaultRetriever)
|
args := newArgs(defaultRetriever)
|
||||||
|
@ -136,7 +135,7 @@ func TestShouldSucceedIdentityVerificationStartProcess(t *testing.T) {
|
||||||
Return(nil)
|
Return(nil)
|
||||||
|
|
||||||
mock.NotifierMock.EXPECT().
|
mock.NotifierMock.EXPECT().
|
||||||
Send(gomock.Eq("john@example.com"), gomock.Eq("Subject"), gomock.Any()).
|
Send(gomock.Eq("john@example.com"), gomock.Eq("Title"), gomock.Any()).
|
||||||
Return(nil)
|
Return(nil)
|
||||||
|
|
||||||
args := newArgs(defaultRetriever)
|
args := newArgs(defaultRetriever)
|
||||||
|
|
|
@ -47,7 +47,6 @@ type Middleware = func(RequestHandler) RequestHandler
|
||||||
// of the identity verification process.
|
// of the identity verification process.
|
||||||
type IdentityVerificationStartArgs struct {
|
type IdentityVerificationStartArgs struct {
|
||||||
// Email template needs a subject, a title and the content of the button.
|
// Email template needs a subject, a title and the content of the button.
|
||||||
MailSubject string
|
|
||||||
MailTitle string
|
MailTitle string
|
||||||
MailButtonContent string
|
MailButtonContent string
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ type SMTPNotifier struct {
|
||||||
disableVerifyCert bool
|
disableVerifyCert bool
|
||||||
disableRequireTLS bool
|
disableRequireTLS bool
|
||||||
address string
|
address string
|
||||||
|
subject string
|
||||||
client *smtp.Client
|
client *smtp.Client
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
}
|
}
|
||||||
|
@ -42,6 +43,7 @@ func NewSMTPNotifier(configuration schema.SMTPNotifierConfiguration) *SMTPNotifi
|
||||||
disableVerifyCert: configuration.DisableVerifyCert,
|
disableVerifyCert: configuration.DisableVerifyCert,
|
||||||
disableRequireTLS: configuration.DisableRequireTLS,
|
disableRequireTLS: configuration.DisableRequireTLS,
|
||||||
address: fmt.Sprintf("%s:%d", configuration.Host, configuration.Port),
|
address: fmt.Sprintf("%s:%d", configuration.Host, configuration.Port),
|
||||||
|
subject: configuration.Subject,
|
||||||
}
|
}
|
||||||
notifier.initializeTLSConfig()
|
notifier.initializeTLSConfig()
|
||||||
return notifier
|
return notifier
|
||||||
|
@ -231,7 +233,8 @@ func (n *SMTPNotifier) cleanup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send an email
|
// Send an email
|
||||||
func (n *SMTPNotifier) Send(recipient, subject, body string) error {
|
func (n *SMTPNotifier) Send(recipient, title, body string) error {
|
||||||
|
subject := strings.ReplaceAll(n.subject, "{title}", title)
|
||||||
if err := n.dial(); err != nil {
|
if err := n.dial(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue