Merge pull request #487 from ericchiang/unify-email-config
*: depricate --email-from flag and move to email config files
This commit is contained in:
commit
33010e22c4
15 changed files with 303 additions and 88 deletions
|
@ -12,14 +12,13 @@ specific fields.
|
|||
|
||||
If using SMTP the `type` field **must** be set to `smtp`. Additionally both
|
||||
`host` and `port` are required. If you wish to use SMTP plain auth, then
|
||||
set `auth` to `plain` and specify your username and password.
|
||||
specify your username and password.
|
||||
|
||||
```
|
||||
{
|
||||
"type": "smtp",
|
||||
"host": "smtp.example.org",
|
||||
"port": 587,
|
||||
"auth": "plain",
|
||||
"host": "smtp.example.org:587",
|
||||
"from": "postmaster@example.com",
|
||||
"username": "postmaster@example.org",
|
||||
"password": "foo"
|
||||
}
|
||||
|
@ -33,6 +32,7 @@ If using Mailgun the `type` field **must** be set to `mailgun`. Additionally
|
|||
```
|
||||
{
|
||||
"type": "mailgun",
|
||||
"from": "noreply@example.com",
|
||||
"privateAPIKey": "key-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
"publicAPIKey": "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
|
||||
"domain": "sandboxZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.mailgun.org"
|
||||
|
@ -49,4 +49,4 @@ emailer the `type` field **must** be set to `fake`.
|
|||
{
|
||||
"type": "fake"
|
||||
}
|
||||
```
|
||||
```
|
||||
|
|
|
@ -43,7 +43,7 @@ func main() {
|
|||
emailTemplateDirs := flagutil.StringSliceFlag{"./static/email"}
|
||||
fs.Var(&emailTemplateDirs, "email-templates", "comma separated list of directories of email template files")
|
||||
|
||||
emailFrom := fs.String("email-from", "", "emails sent from dex will come from this address")
|
||||
emailFrom := fs.String("email-from", "", `DEPRICATED: use "from" field in email config.`)
|
||||
emailConfig := fs.String("email-cfg", "./static/fixtures/emailer.json", "configures emailer.")
|
||||
|
||||
enableRegistration := fs.Bool("enable-registration", false, "Allows users to self-register. This flag cannot be used in combination with --enable-automatic-registration.")
|
||||
|
@ -132,6 +132,10 @@ func main() {
|
|||
log.Fatalf("Only 'http' and 'https' schemes are supported")
|
||||
}
|
||||
|
||||
if *emailFrom != "" {
|
||||
log.Errorf(`--email-from flag is depricated. Use "from" field in email config.`)
|
||||
}
|
||||
|
||||
scfg := server.ServerConfig{
|
||||
IssuerURL: *issuer,
|
||||
TemplateDir: *templates,
|
||||
|
|
|
@ -28,14 +28,20 @@ type Emailer interface {
|
|||
// SendMail queues an email to be sent to 1 or more recipients.
|
||||
// At least one of "text" or "html" must not be blank. If text is blank, but
|
||||
// html is not, then an html-only email should be sent and vice-versal.
|
||||
SendMail(from, subject, text, html string, to ...string) error
|
||||
SendMail(subject, text, html string, to ...string) error
|
||||
}
|
||||
|
||||
//go:generate genconfig -o config.go email Emailer
|
||||
type EmailerConfig interface {
|
||||
EmailerID() string
|
||||
EmailerType() string
|
||||
Emailer() (Emailer, error)
|
||||
|
||||
// Because emailers can either be configured through command line flags or through
|
||||
// JSON configs, we need a way to set the fromAddr when initializing the emailer.
|
||||
//
|
||||
// Values passed in the JSON config should override this value. Supplying neither
|
||||
// should result in an error.
|
||||
Emailer(fromAddress string) (Emailer, error)
|
||||
}
|
||||
|
||||
func newEmailerConfigFromReader(r io.Reader) (EmailerConfig, error) {
|
||||
|
@ -65,6 +71,7 @@ func NewEmailerConfigFromFile(loc string) (EmailerConfig, error) {
|
|||
}
|
||||
|
||||
type FakeEmailerConfig struct {
|
||||
FromAddr string `json:"from"`
|
||||
}
|
||||
|
||||
func (cfg FakeEmailerConfig) EmailerType() string {
|
||||
|
@ -75,15 +82,26 @@ func (cfg FakeEmailerConfig) EmailerID() string {
|
|||
return FakeEmailerType
|
||||
}
|
||||
|
||||
func (cfg FakeEmailerConfig) Emailer() (Emailer, error) {
|
||||
return FakeEmailer{}, nil
|
||||
func (cfg FakeEmailerConfig) Emailer(fromAddr string) (Emailer, error) {
|
||||
from := cfg.FromAddr
|
||||
if from == "" {
|
||||
from = fromAddr
|
||||
}
|
||||
if from == "" {
|
||||
// Since the emailer just prints to stdout, the actual value doesn't matter.
|
||||
from = "noreply@example.com"
|
||||
}
|
||||
|
||||
return FakeEmailer{from}, nil
|
||||
}
|
||||
|
||||
// FakeEmailer is an Emailer that writes emails to stdout. Should only be used in development.
|
||||
type FakeEmailer struct{}
|
||||
type FakeEmailer struct {
|
||||
from string
|
||||
}
|
||||
|
||||
func (f FakeEmailer) SendMail(from, subject, text, html string, to ...string) error {
|
||||
fmt.Printf("From: %v\n", from)
|
||||
func (f FakeEmailer) SendMail(subject, text, html string, to ...string) error {
|
||||
fmt.Printf("From: %v\n", f.from)
|
||||
fmt.Printf("Subject: %v\n", subject)
|
||||
fmt.Printf("To: %v\n", strings.Join(to, ","))
|
||||
fmt.Printf("Body(text): %v\n", text)
|
||||
|
|
|
@ -17,6 +17,7 @@ func init() {
|
|||
}
|
||||
|
||||
type MailgunEmailerConfig struct {
|
||||
FromAddr string `json:"from"`
|
||||
PrivateAPIKey string `json:"privateAPIKey"`
|
||||
PublicAPIKey string `json:"publicAPIKey"`
|
||||
Domain string `json:"domain"`
|
||||
|
@ -30,10 +31,19 @@ func (cfg MailgunEmailerConfig) EmailerID() string {
|
|||
return MailgunEmailerType
|
||||
}
|
||||
|
||||
func (cfg MailgunEmailerConfig) Emailer() (Emailer, error) {
|
||||
func (cfg MailgunEmailerConfig) Emailer(fromAddr string) (Emailer, error) {
|
||||
from := cfg.FromAddr
|
||||
if from == "" {
|
||||
from = fromAddr
|
||||
}
|
||||
|
||||
if from == "" {
|
||||
return nil, errors.New(`missing "from" field in email config`)
|
||||
}
|
||||
mg := mailgun.NewMailgun(cfg.Domain, cfg.PrivateAPIKey, cfg.PublicAPIKey)
|
||||
return &mailgunEmailer{
|
||||
mg: mg,
|
||||
mg: mg,
|
||||
from: from,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -64,11 +74,12 @@ func (cfg *MailgunEmailerConfig) UnmarshalJSON(data []byte) error {
|
|||
}
|
||||
|
||||
type mailgunEmailer struct {
|
||||
mg mailgun.Mailgun
|
||||
mg mailgun.Mailgun
|
||||
from string
|
||||
}
|
||||
|
||||
func (m *mailgunEmailer) SendMail(from, subject, text, html string, to ...string) error {
|
||||
msg := m.mg.NewMessage(from, subject, text, to...)
|
||||
func (m *mailgunEmailer) SendMail(subject, text, html string, to ...string) error {
|
||||
msg := m.mg.NewMessage(m.from, subject, text, to...)
|
||||
if html != "" {
|
||||
msg.SetHtml(html)
|
||||
}
|
||||
|
|
|
@ -9,20 +9,22 @@ import (
|
|||
|
||||
func TestNewEmailConfigFromReader(t *testing.T) {
|
||||
tests := []struct {
|
||||
json string
|
||||
want MailgunEmailerConfig
|
||||
wantErr bool
|
||||
json string
|
||||
want MailgunEmailerConfig
|
||||
wantErr bool
|
||||
wantInitErr bool // want error when calling Emailer() with no fromAddr
|
||||
}{
|
||||
{
|
||||
json: `{"type":"mailgun","id":"mg","privateAPIKey":"private","publicAPIKey":"public","domain":"example.com"}`,
|
||||
json: `{"type":"mailgun","id":"mg","privateAPIKey":"private","publicAPIKey":"public","domain":"example.com","from":"admin@example.com"}`,
|
||||
want: MailgunEmailerConfig{
|
||||
PrivateAPIKey: "private",
|
||||
PublicAPIKey: "public",
|
||||
Domain: "example.com",
|
||||
FromAddr: "admin@example.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
json: `{"type":"mailgun","id":"mg","publicAPIKey":"public","domain":"example.com"}`,
|
||||
json: `{"type":"mailgun","id":"mg","publicAPIKey":"public","domain":"example.com",""}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
|
@ -33,6 +35,18 @@ func TestNewEmailConfigFromReader(t *testing.T) {
|
|||
json: `{"type":"mailgun","id":"mg","privateAPIKey":"private","domain":"example.com"}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
json: `{"type":"mailgun","id":"mg","privateAPIKey":"private","publicAPIKey":"public","domain":"example.com"}`,
|
||||
want: MailgunEmailerConfig{
|
||||
PrivateAPIKey: "private",
|
||||
PublicAPIKey: "public",
|
||||
Domain: "example.com",
|
||||
},
|
||||
|
||||
// No fromAddr email provided. Calling Emailer("") should error since fromAddr needs to be provided
|
||||
// in the config or as a command line argument.
|
||||
wantInitErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
|
@ -42,7 +56,6 @@ func TestNewEmailConfigFromReader(t *testing.T) {
|
|||
if err == nil {
|
||||
t.Errorf("case %d: want non-nil err.", i)
|
||||
}
|
||||
t.Logf("WHAT: %v", err)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -52,5 +65,13 @@ func TestNewEmailConfigFromReader(t *testing.T) {
|
|||
if diff := pretty.Compare(tt.want, ec); diff != "" {
|
||||
t.Errorf("case %d: Compare(want, got): %v", i, diff)
|
||||
}
|
||||
|
||||
_, err = ec.Emailer("")
|
||||
if err != nil && !tt.wantInitErr {
|
||||
t.Errorf("case %d: failed to initialize emailer: %v", i, err)
|
||||
}
|
||||
if err == nil && tt.wantInitErr {
|
||||
t.Errorf("case %d: expected error initializing emailer", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ package email
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"gopkg.in/gomail.v2"
|
||||
)
|
||||
|
@ -17,10 +20,16 @@ func init() {
|
|||
|
||||
type SmtpEmailerConfig struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Auth string `json:"auth"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
FromAddr string `json:"from"`
|
||||
|
||||
// OPTIONAL: If empty and host is of form "host:port" just use that. For backward
|
||||
// compatibility do not change this.
|
||||
Port int `json:"port"`
|
||||
|
||||
// DEPRICATED: If "username" and "password" are provided, use them.
|
||||
Auth string `json:"auth"`
|
||||
}
|
||||
|
||||
func (cfg SmtpEmailerConfig) EmailerType() string {
|
||||
|
@ -31,19 +40,43 @@ func (cfg SmtpEmailerConfig) EmailerID() string {
|
|||
return SmtpEmailerType
|
||||
}
|
||||
|
||||
func (cfg SmtpEmailerConfig) Emailer() (Emailer, error) {
|
||||
var dialer *gomail.Dialer
|
||||
if cfg.Auth == "plain" {
|
||||
dialer = gomail.NewPlainDialer(cfg.Host, cfg.Port, cfg.Username, cfg.Password)
|
||||
} else {
|
||||
dialer = &gomail.Dialer{
|
||||
Host: cfg.Host,
|
||||
Port: cfg.Port,
|
||||
func (cfg SmtpEmailerConfig) Emailer(fromAddr string) (Emailer, error) {
|
||||
from := cfg.FromAddr
|
||||
if from == "" {
|
||||
from = fromAddr
|
||||
}
|
||||
if from == "" {
|
||||
return nil, errors.New(`missing "from" field in email config`)
|
||||
}
|
||||
|
||||
host, port := cfg.Host, cfg.Port
|
||||
|
||||
// If port hasn't been supplied, check the "host" field.
|
||||
if port == 0 {
|
||||
hostStr, portStr, err := net.SplitHostPort(cfg.Host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`"host" must be in format of "host:port" %v`, err)
|
||||
}
|
||||
host = hostStr
|
||||
if port, err = strconv.Atoi(portStr); err != nil {
|
||||
return nil, fmt.Errorf(`failed to parse %q as "host:port" %v`, cfg.Host, err)
|
||||
}
|
||||
}
|
||||
return &smtpEmailer{
|
||||
dialer: dialer,
|
||||
}, nil
|
||||
|
||||
if (cfg.Username == "") != (cfg.Password == "") {
|
||||
return nil, errors.New(`must provide both "username" and "password"`)
|
||||
}
|
||||
|
||||
var dialer *gomail.Dialer
|
||||
if cfg.Username == "" {
|
||||
// NOTE(ericchiang): Guess SSL using the same logic as gomail. We should
|
||||
// eventually allow this to be set explicitly.
|
||||
dialer = &gomail.Dialer{Host: host, Port: port, SSL: port == 465}
|
||||
} else {
|
||||
dialer = gomail.NewPlainDialer(host, port, cfg.Username, cfg.Password)
|
||||
}
|
||||
|
||||
return &smtpEmailer{dialer: dialer, from: from}, nil
|
||||
}
|
||||
|
||||
type smtpEmailerConfig SmtpEmailerConfig
|
||||
|
@ -66,11 +99,12 @@ func (cfg *SmtpEmailerConfig) UnmarshalJSON(data []byte) error {
|
|||
|
||||
type smtpEmailer struct {
|
||||
dialer *gomail.Dialer
|
||||
from string
|
||||
}
|
||||
|
||||
func (emailer *smtpEmailer) SendMail(from, subject, text, html string, to ...string) error {
|
||||
func (emailer *smtpEmailer) SendMail(subject, text, html string, to ...string) error {
|
||||
msg := gomail.NewMessage()
|
||||
msg.SetHeader("From", from)
|
||||
msg.SetHeader("From", emailer.from)
|
||||
msg.SetHeader("To", to...)
|
||||
msg.SetHeader("Subject", subject)
|
||||
msg.SetBody("text/plain", text)
|
||||
|
|
144
email/smtp_test.go
Normal file
144
email/smtp_test.go
Normal file
|
@ -0,0 +1,144 @@
|
|||
package email
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
|
||||
"gopkg.in/gomail.v2"
|
||||
)
|
||||
|
||||
func TestNewSmtpEmailer(t *testing.T) {
|
||||
// If (and only if) this port is provided, gomail assumes SSL.
|
||||
gomailSSLPort := 465
|
||||
|
||||
tests := []struct {
|
||||
config SmtpEmailerConfig
|
||||
|
||||
// formAddr set by the dex-worker flag
|
||||
fromAddrFlag string
|
||||
|
||||
wantEmailer Emailer
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
config: SmtpEmailerConfig{
|
||||
Host: "example.com:" + strconv.Itoa(gomailSSLPort),
|
||||
FromAddr: "foo@example.com",
|
||||
},
|
||||
wantEmailer: &smtpEmailer{
|
||||
from: "foo@example.com",
|
||||
dialer: &gomail.Dialer{
|
||||
Host: "example.com",
|
||||
Port: gomailSSLPort,
|
||||
SSL: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
config: SmtpEmailerConfig{
|
||||
Host: "example.com",
|
||||
Port: gomailSSLPort,
|
||||
FromAddr: "foo@example.com",
|
||||
},
|
||||
wantEmailer: &smtpEmailer{
|
||||
from: "foo@example.com",
|
||||
dialer: &gomail.Dialer{
|
||||
Host: "example.com",
|
||||
Port: gomailSSLPort,
|
||||
SSL: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
config: SmtpEmailerConfig{
|
||||
Host: "example.com",
|
||||
Port: 80,
|
||||
FromAddr: "foo@example.com",
|
||||
},
|
||||
wantEmailer: &smtpEmailer{
|
||||
from: "foo@example.com",
|
||||
dialer: &gomail.Dialer{
|
||||
Host: "example.com",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// No port provided.
|
||||
config: SmtpEmailerConfig{
|
||||
Host: "example.com",
|
||||
FromAddr: "foo@example.com",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
config: SmtpEmailerConfig{
|
||||
Host: "example.com",
|
||||
Port: 80,
|
||||
FromAddr: "foo@example.com",
|
||||
},
|
||||
fromAddrFlag: "bar@example.com",
|
||||
wantEmailer: &smtpEmailer{
|
||||
from: "foo@example.com", // config should override flag.
|
||||
dialer: &gomail.Dialer{
|
||||
Host: "example.com",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// No fromAddr provided as a flag or in config.
|
||||
config: SmtpEmailerConfig{Host: "example.com"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
config: SmtpEmailerConfig{
|
||||
Host: "example.com",
|
||||
Port: 80,
|
||||
Username: "foo",
|
||||
Password: "bar",
|
||||
FromAddr: "foo@example.com",
|
||||
},
|
||||
wantEmailer: &smtpEmailer{
|
||||
from: "foo@example.com", // config should override flag.
|
||||
dialer: gomail.NewPlainDialer("example.com", 80, "foo", "bar"),
|
||||
},
|
||||
},
|
||||
{
|
||||
// Password provided without username.
|
||||
config: SmtpEmailerConfig{
|
||||
Host: "example.com",
|
||||
Port: 80,
|
||||
Password: "bar",
|
||||
FromAddr: "foo@example.com",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
testCase, err := json.MarshalIndent(tt.config, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
emailer, err := tt.config.Emailer(tt.fromAddrFlag)
|
||||
if err != nil {
|
||||
if !tt.wantErr {
|
||||
t.Errorf("case %d %s.Emailer(): %v", i, testCase, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if tt.wantErr {
|
||||
t.Errorf("case %d %s.Emailer(): expected error creating emailer", i, testCase)
|
||||
continue
|
||||
}
|
||||
|
||||
if diff := pretty.Compare(emailer, tt.wantEmailer); diff != "" {
|
||||
t.Errorf("case %d: unexpected emailer %s", i, diff)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
// NewTemplatizedEmailerFromGlobs creates a new TemplatizedEmailer, parsing the templates found in the given filepattern globs.
|
||||
func NewTemplatizedEmailerFromGlobs(textGlob, htmlGlob string, emailer Emailer) (*TemplatizedEmailer, error) {
|
||||
func NewTemplatizedEmailerFromGlobs(textGlob, htmlGlob string, emailer Emailer, fromAddr string) (*TemplatizedEmailer, error) {
|
||||
textTemplates, err := template.ParseGlob(textGlob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -19,15 +19,16 @@ func NewTemplatizedEmailerFromGlobs(textGlob, htmlGlob string, emailer Emailer)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return NewTemplatizedEmailerFromTemplates(textTemplates, htmlTemplates, emailer), nil
|
||||
return NewTemplatizedEmailerFromTemplates(textTemplates, htmlTemplates, emailer, fromAddr), nil
|
||||
}
|
||||
|
||||
// NewTemplatizedEmailerFromTemplates creates a new TemplatizedEmailer, given root text and html templates.
|
||||
func NewTemplatizedEmailerFromTemplates(textTemplates *template.Template, htmlTemplates *htmltemplate.Template, emailer Emailer) *TemplatizedEmailer {
|
||||
func NewTemplatizedEmailerFromTemplates(textTemplates *template.Template, htmlTemplates *htmltemplate.Template, emailer Emailer, fromAddr string) *TemplatizedEmailer {
|
||||
return &TemplatizedEmailer{
|
||||
emailer: emailer,
|
||||
textTemplates: textTemplates,
|
||||
htmlTemplates: htmlTemplates,
|
||||
fromAddr: fromAddr,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,6 +38,7 @@ type TemplatizedEmailer struct {
|
|||
htmlTemplates *htmltemplate.Template
|
||||
emailer Emailer
|
||||
globalCtx map[string]interface{}
|
||||
fromAddr string
|
||||
}
|
||||
|
||||
func (t *TemplatizedEmailer) SetGlobalContext(ctx map[string]interface{}) {
|
||||
|
@ -48,7 +50,7 @@ func (t *TemplatizedEmailer) SetGlobalContext(ctx map[string]interface{}) {
|
|||
// the template names you want to base the message on instead of the actual
|
||||
// text. "to", "from" and "subject" will be added into the data map regardless
|
||||
// of if they are used.
|
||||
func (t *TemplatizedEmailer) SendMail(from, subject, tplName string, data map[string]interface{}, to string) error {
|
||||
func (t *TemplatizedEmailer) SendMail(subject, tplName string, data map[string]interface{}, to string) error {
|
||||
if tplName == "" {
|
||||
return errors.New("Must provide a template name")
|
||||
}
|
||||
|
@ -61,7 +63,7 @@ func (t *TemplatizedEmailer) SendMail(from, subject, tplName string, data map[st
|
|||
}
|
||||
|
||||
data["to"] = to
|
||||
data["from"] = from
|
||||
data["from"] = t.fromAddr
|
||||
data["subject"] = subject
|
||||
|
||||
for k, v := range t.globalCtx {
|
||||
|
@ -84,5 +86,5 @@ func (t *TemplatizedEmailer) SendMail(from, subject, tplName string, data map[st
|
|||
}
|
||||
}
|
||||
|
||||
return t.emailer.SendMail(from, subject, textBuffer.String(), htmlBuffer.String(), to)
|
||||
return t.emailer.SendMail(subject, textBuffer.String(), htmlBuffer.String(), to)
|
||||
}
|
||||
|
|
|
@ -22,8 +22,7 @@ type testEmailer struct {
|
|||
to []string
|
||||
}
|
||||
|
||||
func (t *testEmailer) SendMail(from, subject, text, html string, to ...string) error {
|
||||
t.from = from
|
||||
func (t *testEmailer) SendMail(subject, text, html string, to ...string) error {
|
||||
t.subject = subject
|
||||
t.text = text
|
||||
t.html = html
|
||||
|
@ -118,12 +117,12 @@ func TestTemplatizedEmailSendMail(t *testing.T) {
|
|||
|
||||
for i, tt := range tests {
|
||||
emailer := &testEmailer{}
|
||||
templatizer := NewTemplatizedEmailerFromTemplates(textTemplates, htmlTemplates, emailer)
|
||||
templatizer := NewTemplatizedEmailerFromTemplates(textTemplates, htmlTemplates, emailer, tt.from)
|
||||
if tt.ctx != nil {
|
||||
templatizer.SetGlobalContext(tt.ctx)
|
||||
}
|
||||
|
||||
err := templatizer.SendMail(tt.from, tt.subject, tt.tplName, tt.data, tt.to)
|
||||
err := templatizer.SendMail(tt.subject, tt.tplName, tt.data, tt.to)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("case %d: err == nil, want non-nil err", i)
|
||||
|
@ -131,9 +130,6 @@ func TestTemplatizedEmailSendMail(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
if emailer.from != tt.from {
|
||||
t.Errorf("case %d: want=%q, got=%q", i, tt.from, emailer.from)
|
||||
}
|
||||
if emailer.subject != tt.subject {
|
||||
t.Errorf("case %d: want=%q, got=%q", i, tt.subject, emailer.subject)
|
||||
}
|
||||
|
|
|
@ -327,7 +327,7 @@ func setEmailer(srv *Server, issuerName, fromAddress, emailerConfigFile string,
|
|||
return err
|
||||
}
|
||||
|
||||
emailer, err := cfg.Emailer()
|
||||
emailer, err := cfg.Emailer(fromAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -371,7 +371,7 @@ func setEmailer(srv *Server, issuerName, fromAddress, emailerConfigFile string,
|
|||
return err
|
||||
}
|
||||
}
|
||||
tMailer := email.NewTemplatizedEmailerFromTemplates(textTemplates, htmlTemplates, emailer)
|
||||
tMailer := email.NewTemplatizedEmailerFromTemplates(textTemplates, htmlTemplates, emailer, fromAddress)
|
||||
tMailer.SetGlobalContext(map[string]interface{}{
|
||||
"issuer_name": issuerName,
|
||||
})
|
||||
|
@ -382,7 +382,6 @@ func setEmailer(srv *Server, issuerName, fromAddress, emailerConfigFile string,
|
|||
srv.SessionManager.ValidityWindow,
|
||||
srv.IssuerURL,
|
||||
tMailer,
|
||||
fromAddress,
|
||||
srv.absURL(httpPathResetPassword),
|
||||
srv.absURL(httpPathEmailVerify),
|
||||
srv.absURL(httpPathAcceptInvitation),
|
||||
|
|
|
@ -101,7 +101,6 @@ func TestSendResetPasswordEmailHandler(t *testing.T) {
|
|||
wantCode: http.StatusOK,
|
||||
wantEmailer: &testEmailer{
|
||||
to: str("email-1@example.com"),
|
||||
from: "noreply@example.com",
|
||||
subject: "Reset Your Password",
|
||||
},
|
||||
wantPRUserID: "ID-1",
|
||||
|
@ -137,7 +136,6 @@ func TestSendResetPasswordEmailHandler(t *testing.T) {
|
|||
wantCode: http.StatusOK,
|
||||
wantEmailer: &testEmailer{
|
||||
to: str("email-1@example.com"),
|
||||
from: "noreply@example.com",
|
||||
subject: "Reset Your Password",
|
||||
},
|
||||
wantPRPassword: "password",
|
||||
|
@ -261,7 +259,7 @@ func TestSendResetPasswordEmailHandler(t *testing.T) {
|
|||
emailer := &testEmailer{
|
||||
sent: make(chan struct{}),
|
||||
}
|
||||
templatizer := email.NewTemplatizedEmailerFromTemplates(textTemplates, htmlTemplates, emailer)
|
||||
templatizer := email.NewTemplatizedEmailerFromTemplates(textTemplates, htmlTemplates, emailer, "admin@example.com")
|
||||
f.srv.UserEmailer.SetEmailer(templatizer)
|
||||
hdlr := SendResetPasswordEmailHandler{
|
||||
tpl: f.srv.SendResetPasswordEmailTemplate,
|
||||
|
@ -584,13 +582,12 @@ func TestResetPasswordHandler(t *testing.T) {
|
|||
}
|
||||
|
||||
type testEmailer struct {
|
||||
from, subject, text, html string
|
||||
to []string
|
||||
sent chan struct{}
|
||||
subject, text, html string
|
||||
to []string
|
||||
sent chan struct{}
|
||||
}
|
||||
|
||||
func (t *testEmailer) SendMail(from, subject, text, html string, to ...string) error {
|
||||
t.from = from
|
||||
func (t *testEmailer) SendMail(subject, text, html string, to ...string) error {
|
||||
t.subject = subject
|
||||
t.text = text
|
||||
t.html = html
|
||||
|
|
|
@ -198,7 +198,8 @@ func makeTestFixturesWithOptions(options testFixtureOptions) (*testFixtures, err
|
|||
emailer, err := email.NewTemplatizedEmailerFromGlobs(
|
||||
emailTemplatesLocation+"/*.txt",
|
||||
emailTemplatesLocation+"/*.html",
|
||||
&email.FakeEmailer{})
|
||||
&email.FakeEmailer{},
|
||||
"admin@example.com")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -266,7 +267,6 @@ func makeTestFixturesWithOptions(options testFixtureOptions) (*testFixtures, err
|
|||
srv.SessionManager.ValidityWindow,
|
||||
srv.IssuerURL,
|
||||
emailer,
|
||||
"noreply@example.com",
|
||||
srv.absURL(httpPathResetPassword),
|
||||
srv.absURL(httpPathEmailVerify),
|
||||
srv.absURL(httpPathAcceptInvitation),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
"type": "fake"
|
||||
}
|
||||
"type": "fake",
|
||||
"from": "noreply@example.com"
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ type UserEmailer struct {
|
|||
tokenValidityWindow time.Duration
|
||||
issuerURL url.URL
|
||||
emailer *email.TemplatizedEmailer
|
||||
fromAddress string
|
||||
|
||||
passwordResetURL url.URL
|
||||
verifyEmailURL url.URL
|
||||
|
@ -33,7 +32,6 @@ func NewUserEmailer(ur user.UserRepo,
|
|||
tokenValidityWindow time.Duration,
|
||||
issuerURL url.URL,
|
||||
emailer *email.TemplatizedEmailer,
|
||||
fromAddress string,
|
||||
passwordResetURL url.URL,
|
||||
verifyEmailURL url.URL,
|
||||
invitationURL url.URL,
|
||||
|
@ -45,7 +43,6 @@ func NewUserEmailer(ur user.UserRepo,
|
|||
tokenValidityWindow: tokenValidityWindow,
|
||||
issuerURL: issuerURL,
|
||||
emailer: emailer,
|
||||
fromAddress: fromAddress,
|
||||
passwordResetURL: passwordResetURL,
|
||||
verifyEmailURL: verifyEmailURL,
|
||||
invitationURL: invitationURL,
|
||||
|
@ -106,7 +103,7 @@ func (u *UserEmailer) SendResetPasswordEmail(email string, redirectURL url.URL,
|
|||
resetURL.RawQuery = q.Encode()
|
||||
|
||||
if u.emailer != nil {
|
||||
err = u.emailer.SendMail(u.fromAddress, "Reset Your Password", "password-reset",
|
||||
err = u.emailer.SendMail("Reset Your Password", "password-reset",
|
||||
map[string]interface{}{
|
||||
"email": usr.Email,
|
||||
"link": resetURL.String(),
|
||||
|
@ -144,7 +141,7 @@ func (u *UserEmailer) SendInviteEmail(email string, redirectURL url.URL, clientI
|
|||
resetURL.RawQuery = q.Encode()
|
||||
|
||||
if u.emailer != nil {
|
||||
err = u.emailer.SendMail(u.fromAddress, "Activate Your Account", "invite",
|
||||
err = u.emailer.SendMail("Activate Your Account", "invite",
|
||||
map[string]interface{}{
|
||||
"email": usr.Email,
|
||||
"link": resetURL.String(),
|
||||
|
@ -191,7 +188,7 @@ func (u *UserEmailer) SendEmailVerification(userID, clientID string, redirectURL
|
|||
verifyURL.RawQuery = q.Encode()
|
||||
|
||||
if u.emailer != nil {
|
||||
err = u.emailer.SendMail(u.fromAddress, "Please verify your email address.", "verify-email",
|
||||
err = u.emailer.SendMail("Please verify your email address.", "verify-email",
|
||||
map[string]interface{}{
|
||||
"email": usr.Email,
|
||||
"link": verifyURL.String(),
|
||||
|
|
|
@ -29,13 +29,12 @@ var (
|
|||
)
|
||||
|
||||
type testEmailer struct {
|
||||
from, subject, text, html string
|
||||
to []string
|
||||
sent bool
|
||||
subject, text, html string
|
||||
to []string
|
||||
sent bool
|
||||
}
|
||||
|
||||
func (t *testEmailer) SendMail(from, subject, text, html string, to ...string) error {
|
||||
t.from = from
|
||||
func (t *testEmailer) SendMail(subject, text, html string, to ...string) error {
|
||||
t.subject = subject
|
||||
t.text = text
|
||||
t.html = html
|
||||
|
@ -112,9 +111,9 @@ func makeTestFixtures() (*UserEmailer, *testEmailer, *key.PublicKey) {
|
|||
htmlTemplates := htmltemplate.New("html")
|
||||
|
||||
emailer := &testEmailer{}
|
||||
tEmailer := email.NewTemplatizedEmailerFromTemplates(textTemplates, htmlTemplates, emailer)
|
||||
tEmailer := email.NewTemplatizedEmailerFromTemplates(textTemplates, htmlTemplates, emailer, fromAddress)
|
||||
|
||||
userEmailer := NewUserEmailer(ur, pwr, signerFn, validityWindow, issuerURL, tEmailer, fromAddress, passwordResetURL, verifyEmailURL, acceptInvitationURL)
|
||||
userEmailer := NewUserEmailer(ur, pwr, signerFn, validityWindow, issuerURL, tEmailer, passwordResetURL, verifyEmailURL, acceptInvitationURL)
|
||||
|
||||
return userEmailer, emailer, publicKey
|
||||
}
|
||||
|
@ -203,10 +202,6 @@ func TestSendResetPasswordEmail(t *testing.T) {
|
|||
t.Errorf("case %d: want==%v, got==%v", i, tt.email, emailer.to[0])
|
||||
}
|
||||
|
||||
if fromAddress != emailer.from {
|
||||
t.Errorf("case %d: want==%v, got==%v", i, fromAddress, emailer.from)
|
||||
}
|
||||
|
||||
} else if emailer.sent {
|
||||
t.Errorf("case %d: want !emailer.sent", i)
|
||||
}
|
||||
|
@ -306,10 +301,6 @@ func TestSendEmailVerificationEmail(t *testing.T) {
|
|||
t.Errorf("case %d: want==%v, got==%v", i, tt.wantEmailAddress, emailer.to[0])
|
||||
}
|
||||
|
||||
if fromAddress != emailer.from {
|
||||
t.Errorf("case %d: want==%v, got==%v", i, fromAddress, emailer.from)
|
||||
}
|
||||
|
||||
} else if emailer.sent {
|
||||
t.Errorf("case %d: want !emailer.sent", i)
|
||||
}
|
||||
|
|
Reference in a new issue