mirror of
				https://github.com/volatiletech/authboss.git
				synced 2025-10-30 23:47:59 +02:00 
			
		
		
		
	Start moving default implementations
This commit is contained in:
		| @@ -3,11 +3,8 @@ package authboss | ||||
| import ( | ||||
| 	"context" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| ) | ||||
|  | ||||
| // Config holds all the configuration for both authboss and it's modules. | ||||
| @@ -118,7 +115,7 @@ type Config struct { | ||||
|  | ||||
| // Defaults sets the configuration's default values. | ||||
| func (c *Config) Defaults() { | ||||
| 	c.MountPath = "/" | ||||
| 	/*c.MountPath = "/" | ||||
| 	c.ViewsPath = "./" | ||||
| 	c.RootURL = "http://localhost:8080" | ||||
| 	c.BCryptCost = bcrypt.DefaultCost | ||||
| @@ -162,5 +159,5 @@ func (c *Config) Defaults() { | ||||
| 	c.Mailer = LogMailer(ioutil.Discard) | ||||
| 	c.ContextProvider = func(req *http.Request) context.Context { | ||||
| 		return context.TODO() | ||||
| 	} | ||||
| 	}*/ | ||||
| } | ||||
|   | ||||
							
								
								
									
										43
									
								
								defaults/log_mailer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								defaults/log_mailer.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| package defaults | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"io" | ||||
|  | ||||
| 	"github.com/volatiletech/authboss" | ||||
| ) | ||||
|  | ||||
| // LogMailer logs e-mails instead of sending them. | ||||
| type LogMailer struct { | ||||
| 	io.Writer | ||||
| } | ||||
|  | ||||
| // NewLogMailer creates a mailer that doesn't deliver e-mails but | ||||
| // simply logs them. | ||||
| func NewLogMailer(writer io.Writer) *LogMailer { | ||||
| 	return &LogMailer{writer} | ||||
| } | ||||
|  | ||||
| // Send an e-mail | ||||
| func (l LogMailer) Send(ctx context.Context, mail authboss.Email) error { | ||||
| 	buf := &bytes.Buffer{} | ||||
|  | ||||
| 	data := struct { | ||||
| 		Boundary string | ||||
| 		Mail     authboss.Email | ||||
| 	}{ | ||||
| 		Boundary: "284fad24nao8f4na284f2n4", | ||||
| 		Mail:     mail, | ||||
| 	} | ||||
|  | ||||
| 	err := emailTmpl.Execute(buf, data) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	toSend := bytes.Replace(buf.Bytes(), []byte{'\n'}, []byte{'\r', '\n'}, -1) | ||||
|  | ||||
| 	_, err = l.Write(toSend) | ||||
| 	return err | ||||
| } | ||||
| @@ -1,25 +1,22 @@ | ||||
| package authboss | ||||
| package defaults | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"io/ioutil" | ||||
| 	"math/rand" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/volatiletech/authboss" | ||||
| ) | ||||
| 
 | ||||
| func TestMailer(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 
 | ||||
| 	ab := New() | ||||
| 	mailServer := &bytes.Buffer{} | ||||
| 	mailer := NewLogMailer(mailServer) | ||||
| 
 | ||||
| 	ab.Mailer = LogMailer(mailServer) | ||||
| 	ab.StoreLoader = mockStoreLoader{} | ||||
| 	ab.LogWriter = ioutil.Discard | ||||
| 
 | ||||
| 	err := ab.SendMail(context.TODO(), Email{ | ||||
| 	err := mailer.Send(context.TODO(), authboss.Email{ | ||||
| 		To:       []string{"some@email.com", "a@a.com"}, | ||||
| 		ToNames:  []string{"Jake", "Noname"}, | ||||
| 		From:     "some@guy.com", | ||||
| @@ -53,19 +50,23 @@ func TestMailer(t *testing.T) { | ||||
| 	if !strings.Contains(str, "<html>body</html>") { | ||||
| 		t.Error("Html body not present.") | ||||
| 	} | ||||
| 
 | ||||
| 	if t.Failed() { | ||||
| 		t.Log(str) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestSMTPMailer(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 
 | ||||
| 	var _ Mailer = SMTPMailer("server", nil) | ||||
| 	_ = NewSMTPMailer("server", nil) | ||||
| 
 | ||||
| 	recovered := false | ||||
| 	defer func() { | ||||
| 		recovered = recover() != nil | ||||
| 	}() | ||||
| 
 | ||||
| 	SMTPMailer("", nil) | ||||
| 	NewSMTPMailer("", nil) | ||||
| 
 | ||||
| 	if !recovered { | ||||
| 		t.Error("Should have panicd.") | ||||
| @@ -75,7 +76,7 @@ func TestSMTPMailer(t *testing.T) { | ||||
| func TestBoundary(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 
 | ||||
| 	mailer := smtpMailer{"server", nil, rand.New(rand.NewSource(3))} | ||||
| 	mailer := &SMTPMailer{"server", nil, rand.New(rand.NewSource(3))} | ||||
| 	if got := mailer.boundary(); got != "fe3fhpsm69lx8jvnrnju0wr" { | ||||
| 		t.Error("boundary was wrong", got) | ||||
| 	} | ||||
							
								
								
									
										124
									
								
								defaults/smtp_mailer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								defaults/smtp_mailer.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| package defaults | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"math/rand" | ||||
| 	"net/smtp" | ||||
| 	"strings" | ||||
| 	"text/template" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/volatiletech/authboss" | ||||
| ) | ||||
|  | ||||
| // NewSMTPMailer creates an SMTP Mailer to send emails with. | ||||
| func NewSMTPMailer(server string, auth smtp.Auth) *SMTPMailer { | ||||
| 	if len(server) == 0 { | ||||
| 		panic("SMTP Mailer must be created with a server string.") | ||||
| 	} | ||||
| 	random := rand.New(rand.NewSource(time.Now().UnixNano())) | ||||
| 	return &SMTPMailer{server, auth, random} | ||||
| } | ||||
|  | ||||
| // SMTPMailer uses smtp to actually send e-mails | ||||
| type SMTPMailer struct { | ||||
| 	Server string | ||||
| 	Auth   smtp.Auth | ||||
| 	rand   *rand.Rand | ||||
| } | ||||
|  | ||||
| // Send an e-mail | ||||
| func (s SMTPMailer) Send(ctx context.Context, mail authboss.Email) error { | ||||
| 	buf := &bytes.Buffer{} | ||||
|  | ||||
| 	data := struct { | ||||
| 		Boundary string | ||||
| 		Mail     authboss.Email | ||||
| 	}{ | ||||
| 		Boundary: s.boundary(), | ||||
| 		Mail:     mail, | ||||
| 	} | ||||
|  | ||||
| 	err := emailTmpl.Execute(buf, data) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	toSend := bytes.Replace(buf.Bytes(), []byte{'\n'}, []byte{'\r', '\n'}, -1) | ||||
|  | ||||
| 	return smtp.SendMail(s.Server, s.Auth, mail.From, mail.To, toSend) | ||||
| } | ||||
|  | ||||
| // boundary makes mime boundaries, these are largely useless strings that just | ||||
| // need to be the same in the mime structure. We choose from the alphabet below | ||||
| // and create a random string of length 23 | ||||
| // Example: | ||||
| // 284fad24nao8f4na284f2n4 | ||||
| func (s SMTPMailer) boundary() string { | ||||
| 	const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789" | ||||
| 	buf := &bytes.Buffer{} | ||||
|  | ||||
| 	for i := 0; i < 23; i++ { | ||||
| 		buf.WriteByte(alphabet[s.rand.Int()%len(alphabet)]) | ||||
| 	} | ||||
|  | ||||
| 	return buf.String() | ||||
| } | ||||
|  | ||||
| func namedAddress(name, address string) string { | ||||
| 	if len(name) == 0 { | ||||
| 		return address | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Sprintf("%s <%s>", name, address) | ||||
| } | ||||
|  | ||||
| func namedAddresses(names, addresses []string) string { | ||||
| 	if len(names) == 0 { | ||||
| 		return strings.Join(addresses, ", ") | ||||
| 	} | ||||
|  | ||||
| 	buf := &bytes.Buffer{} | ||||
| 	first := true | ||||
|  | ||||
| 	for i, address := range addresses { | ||||
| 		if first { | ||||
| 			first = false | ||||
| 		} else { | ||||
| 			buf.WriteString(", ") | ||||
| 		} | ||||
|  | ||||
| 		buf.WriteString(namedAddress(names[i], address)) | ||||
| 	} | ||||
|  | ||||
| 	return buf.String() | ||||
| } | ||||
|  | ||||
| var emailTmpl = template.Must(template.New("email").Funcs(template.FuncMap{ | ||||
| 	"join":           strings.Join, | ||||
| 	"namedAddress":   namedAddress, | ||||
| 	"namedAddresses": namedAddresses, | ||||
| }).Parse(`To: {{namedAddresses .Mail.ToNames .Mail.To}}{{if .Mail.Cc}} | ||||
| Cc: {{namedAddresses .Mail.CcNames .Mail.Cc}}{{end}}{{if .Mail.Bcc}} | ||||
| Bcc: {{namedAddresses .Mail.BccNames .Mail.Bcc}}{{end}} | ||||
| From: {{namedAddress .Mail.FromName .Mail.From}} | ||||
| Subject: {{.Mail.Subject}}{{if .Mail.ReplyTo}} | ||||
| Reply-To: {{namedAddress .Mail.ReplyToName .Mail.ReplyTo}}{{end}} | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: multipart/alternative; boundary="==============={{.Boundary}}==" | ||||
| Content-Transfer-Encoding: 7bit | ||||
|  | ||||
| --==============={{.Boundary}}== | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 7bit | ||||
|  | ||||
| {{.Mail.TextBody}} | ||||
| --==============={{.Boundary}}== | ||||
| Content-Type: text/html; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 7bit | ||||
|  | ||||
| {{.Mail.HTMLBody}} | ||||
| --==============={{.Boundary}}==-- | ||||
| `)) | ||||
							
								
								
									
										148
									
								
								mailer.go
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								mailer.go
									
									
									
									
									
								
							| @@ -1,15 +1,7 @@ | ||||
| package authboss | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math/rand" | ||||
| 	"net/smtp" | ||||
| 	"strings" | ||||
| 	"text/template" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // SendMail uses the currently configured mailer to deliver e-mails. | ||||
| @@ -22,21 +14,6 @@ type Mailer interface { | ||||
| 	Send(context.Context, Email) error | ||||
| } | ||||
|  | ||||
| // LogMailer creates a mailer that doesn't deliver e-mails but | ||||
| // simply logs them. | ||||
| func LogMailer(writer io.Writer) Mailer { | ||||
| 	return logMailer{writer} | ||||
| } | ||||
|  | ||||
| // SMTPMailer creates an SMTP Mailer to send emails with. | ||||
| func SMTPMailer(server string, auth smtp.Auth) Mailer { | ||||
| 	if len(server) == 0 { | ||||
| 		panic("SMTP Mailer must be created with a server string.") | ||||
| 	} | ||||
| 	random := rand.New(rand.NewSource(time.Now().UnixNano())) | ||||
| 	return smtpMailer{server, auth, random} | ||||
| } | ||||
|  | ||||
| // Email all the things. The ToNames and friends are parallel arrays and must | ||||
| // be 0-length or the same length as their counterpart. To omit a name | ||||
| // for a user at an index in To simply use an empty string at that | ||||
| @@ -51,128 +28,3 @@ type Email struct { | ||||
| 	TextBody string | ||||
| 	HTMLBody string | ||||
| } | ||||
|  | ||||
| type logMailer struct { | ||||
| 	io.Writer | ||||
| } | ||||
|  | ||||
| func (l logMailer) Send(ctx context.Context, mail Email) error { | ||||
| 	buf := &bytes.Buffer{} | ||||
|  | ||||
| 	data := struct { | ||||
| 		Boundary string | ||||
| 		Mail     Email | ||||
| 	}{ | ||||
| 		Boundary: "284fad24nao8f4na284f2n4", | ||||
| 		Mail:     mail, | ||||
| 	} | ||||
|  | ||||
| 	err := emailTmpl.Execute(buf, data) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	toSend := bytes.Replace(buf.Bytes(), []byte{'\n'}, []byte{'\r', '\n'}, -1) | ||||
|  | ||||
| 	_, err = l.Write(toSend) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| type smtpMailer struct { | ||||
| 	Server string | ||||
| 	Auth   smtp.Auth | ||||
| 	rand   *rand.Rand | ||||
| } | ||||
|  | ||||
| func (s smtpMailer) Send(ctx context.Context, mail Email) error { | ||||
| 	buf := &bytes.Buffer{} | ||||
|  | ||||
| 	data := struct { | ||||
| 		Boundary string | ||||
| 		Mail     Email | ||||
| 	}{ | ||||
| 		Boundary: s.boundary(), | ||||
| 		Mail:     mail, | ||||
| 	} | ||||
|  | ||||
| 	err := emailTmpl.Execute(buf, data) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	toSend := bytes.Replace(buf.Bytes(), []byte{'\n'}, []byte{'\r', '\n'}, -1) | ||||
|  | ||||
| 	return smtp.SendMail(s.Server, s.Auth, mail.From, mail.To, toSend) | ||||
| } | ||||
|  | ||||
| // boundary makes mime boundaries, these are largely useless strings that just | ||||
| // need to be the same in the mime structure. We choose from the alphabet below | ||||
| // and create a random string of length 23 | ||||
| // Example: | ||||
| // 284fad24nao8f4na284f2n4 | ||||
| func (s smtpMailer) boundary() string { | ||||
| 	const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789" | ||||
| 	buf := &bytes.Buffer{} | ||||
|  | ||||
| 	for i := 0; i < 23; i++ { | ||||
| 		buf.WriteByte(alphabet[s.rand.Int()%len(alphabet)]) | ||||
| 	} | ||||
|  | ||||
| 	return buf.String() | ||||
| } | ||||
|  | ||||
| func namedAddress(name, address string) string { | ||||
| 	if len(name) == 0 { | ||||
| 		return address | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Sprintf("%s <%s>", name, address) | ||||
| } | ||||
|  | ||||
| func namedAddresses(names, addresses []string) string { | ||||
| 	if len(names) == 0 { | ||||
| 		return strings.Join(addresses, ", ") | ||||
| 	} | ||||
|  | ||||
| 	buf := &bytes.Buffer{} | ||||
| 	first := true | ||||
|  | ||||
| 	for i, address := range addresses { | ||||
| 		if first { | ||||
| 			first = false | ||||
| 		} else { | ||||
| 			buf.WriteString(", ") | ||||
| 		} | ||||
|  | ||||
| 		buf.WriteString(namedAddress(names[i], address)) | ||||
| 	} | ||||
|  | ||||
| 	return buf.String() | ||||
| } | ||||
|  | ||||
| var emailTmpl = template.Must(template.New("email").Funcs(template.FuncMap{ | ||||
| 	"join":           strings.Join, | ||||
| 	"namedAddress":   namedAddress, | ||||
| 	"namedAddresses": namedAddresses, | ||||
| }).Parse(`To: {{namedAddresses .Mail.ToNames .Mail.To}}{{if .Mail.Cc}} | ||||
| Cc: {{namedAddresses .Mail.CcNames .Mail.Cc}}{{end}}{{if .Mail.Bcc}} | ||||
| Bcc: {{namedAddresses .Mail.BccNames .Mail.Bcc}}{{end}} | ||||
| From: {{namedAddress .Mail.FromName .Mail.From}} | ||||
| Subject: {{.Mail.Subject}}{{if .Mail.ReplyTo}} | ||||
| Reply-To: {{namedAddress .Mail.ReplyToName .Mail.ReplyTo}}{{end}} | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: multipart/alternative; boundary="==============={{.Boundary}}==" | ||||
| Content-Transfer-Encoding: 7bit | ||||
|  | ||||
| --==============={{.Boundary}}== | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 7bit | ||||
|  | ||||
| {{.Mail.TextBody}} | ||||
| --==============={{.Boundary}}== | ||||
| Content-Type: text/html; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 7bit | ||||
|  | ||||
| {{.Mail.HTMLBody}} | ||||
| --==============={{.Boundary}}==-- | ||||
| `)) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user