[PLT-8024] Support LOGIN authentication method for SMTP (#8140)
* [PLT-8024] Support LOGIN authentication method for SMTP * added initial unit tests
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
6e024c45b5
Коммит
4b5541db46
@@ -5,6 +5,8 @@ package utils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
"mime"
|
"mime"
|
||||||
"net"
|
"net"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
@@ -15,8 +17,6 @@ import (
|
|||||||
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"io"
|
|
||||||
|
|
||||||
l4g "github.com/alecthomas/log4go"
|
l4g "github.com/alecthomas/log4go"
|
||||||
"github.com/mattermost/html2text"
|
"github.com/mattermost/html2text"
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
@@ -26,6 +26,56 @@ func encodeRFC2047Word(s string) string {
|
|||||||
return mime.BEncoding.Encode("utf-8", s)
|
return mime.BEncoding.Encode("utf-8", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type authChooser struct {
|
||||||
|
smtp.Auth
|
||||||
|
Config *model.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *authChooser) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||||
|
a.Auth = LoginAuth(a.Config.EmailSettings.SMTPUsername, a.Config.EmailSettings.SMTPPassword, a.Config.EmailSettings.SMTPServer+":"+a.Config.EmailSettings.SMTPPort)
|
||||||
|
for _, method := range server.Auth {
|
||||||
|
if method == "PLAIN" {
|
||||||
|
a.Auth = smtp.PlainAuth("", a.Config.EmailSettings.SMTPUsername, a.Config.EmailSettings.SMTPPassword, a.Config.EmailSettings.SMTPServer+":"+a.Config.EmailSettings.SMTPPort)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a.Auth.Start(server)
|
||||||
|
}
|
||||||
|
|
||||||
|
type loginAuth struct {
|
||||||
|
username, password, host string
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoginAuth(username, password, host string) smtp.Auth {
|
||||||
|
return &loginAuth{username, password, host}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||||
|
if !server.TLS {
|
||||||
|
return "", nil, errors.New("unencrypted connection")
|
||||||
|
}
|
||||||
|
|
||||||
|
if server.Name != a.host {
|
||||||
|
return "", nil, errors.New("wrong host name")
|
||||||
|
}
|
||||||
|
|
||||||
|
return "LOGIN", []byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||||
|
if more {
|
||||||
|
switch string(fromServer) {
|
||||||
|
case "Username:":
|
||||||
|
return []byte(a.username), nil
|
||||||
|
case "Password:":
|
||||||
|
return []byte(a.password), nil
|
||||||
|
default:
|
||||||
|
return nil, errors.New("Unkown fromServer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func connectToSMTPServer(config *model.Config) (net.Conn, *model.AppError) {
|
func connectToSMTPServer(config *model.Config) (net.Conn, *model.AppError) {
|
||||||
var conn net.Conn
|
var conn net.Conn
|
||||||
var err error
|
var err error
|
||||||
@@ -75,9 +125,7 @@ func newSMTPClient(conn net.Conn, config *model.Config) (*smtp.Client, *model.Ap
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *config.EmailSettings.EnableSMTPAuth {
|
if *config.EmailSettings.EnableSMTPAuth {
|
||||||
auth := smtp.PlainAuth("", config.EmailSettings.SMTPUsername, config.EmailSettings.SMTPPassword, config.EmailSettings.SMTPServer+":"+config.EmailSettings.SMTPPort)
|
if err = c.Auth(&authChooser{Config: config}); err != nil {
|
||||||
|
|
||||||
if err = c.Auth(auth); err != nil {
|
|
||||||
return nil, model.NewAppError("SendMail", "utils.mail.new_client.auth.app_error", nil, err.Error(), http.StatusInternalServerError)
|
return nil, model.NewAppError("SendMail", "utils.mail.new_client.auth.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"net/smtp"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -169,3 +172,64 @@ func TestSendMailUsingConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
func TestAuthMethods(t *testing.T) {
|
||||||
|
config := model.Config{
|
||||||
|
EmailSettings: model.EmailSettings{
|
||||||
|
EnableSMTPAuth: model.NewBool(false),
|
||||||
|
SMTPUsername: "test",
|
||||||
|
SMTPPassword: "fakepass",
|
||||||
|
SMTPServer: "fakeserver",
|
||||||
|
SMTPPort: "25",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
auth := &authChooser{Config: &config}
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
server *smtp.ServerInfo
|
||||||
|
err string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "auth PLAIN success",
|
||||||
|
server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"PLAIN"}, TLS: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "auth PLAIN unencrypted connection fail",
|
||||||
|
server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"PLAIN"}, TLS: false},
|
||||||
|
err: "unencrypted connection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "auth PLAIN wrong host name",
|
||||||
|
server: &smtp.ServerInfo{Name: "wrongServer:999", Auth: []string{"PLAIN"}, TLS: true},
|
||||||
|
err: "wrong host name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "auth LOGIN success",
|
||||||
|
server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"LOGIN"}, TLS: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "auth LOGIN unencrypted connection fail",
|
||||||
|
server: &smtp.ServerInfo{Name: "wrongServer:999", Auth: []string{"LOGIN"}, TLS: true},
|
||||||
|
err: "wrong host name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "auth LOGIN wrong host name",
|
||||||
|
server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"LOGIN"}, TLS: false},
|
||||||
|
err: "unencrypted connection",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
_, _, err := auth.Start(test.server)
|
||||||
|
got := ""
|
||||||
|
if err != nil {
|
||||||
|
got = err.Error()
|
||||||
|
}
|
||||||
|
if got != test.err {
|
||||||
|
t.Errorf("%d. got error = %q; want %q", i, got, test.err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user