|
|
@ -7,6 +7,8 @@ package models |
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"encoding/json" |
|
|
|
"encoding/json" |
|
|
|
"errors" |
|
|
|
"errors" |
|
|
|
|
|
|
|
"fmt" |
|
|
|
|
|
|
|
"net/smtp" |
|
|
|
"strings" |
|
|
|
"strings" |
|
|
|
"time" |
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
|
@ -50,6 +52,22 @@ func (cfg *LDAPConfig) ToDB() ([]byte, error) { |
|
|
|
return json.Marshal(cfg.Ldapsource) |
|
|
|
return json.Marshal(cfg.Ldapsource) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type SMTPConfig struct { |
|
|
|
|
|
|
|
Auth string |
|
|
|
|
|
|
|
Host string |
|
|
|
|
|
|
|
Post string |
|
|
|
|
|
|
|
TLS bool |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// implement
|
|
|
|
|
|
|
|
func (cfg *SMTPConfig) FromDB(bs []byte) error { |
|
|
|
|
|
|
|
return json.Unmarshal(bs, cfg) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (cfg *SMTPConfig) ToDB() ([]byte, error) { |
|
|
|
|
|
|
|
return json.Marshal(cfg) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type LoginSource struct { |
|
|
|
type LoginSource struct { |
|
|
|
Id int64 |
|
|
|
Id int64 |
|
|
|
Type int |
|
|
|
Type int |
|
|
@ -69,6 +87,10 @@ func (source *LoginSource) LDAP() *LDAPConfig { |
|
|
|
return source.Cfg.(*LDAPConfig) |
|
|
|
return source.Cfg.(*LDAPConfig) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (source *LoginSource) SMTP() *SMTPConfig { |
|
|
|
|
|
|
|
return source.Cfg.(*SMTPConfig) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// for xorm callback
|
|
|
|
// for xorm callback
|
|
|
|
func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) { |
|
|
|
func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) { |
|
|
|
if colName == "type" { |
|
|
|
if colName == "type" { |
|
|
@ -76,6 +98,8 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) { |
|
|
|
switch ty { |
|
|
|
switch ty { |
|
|
|
case LT_LDAP: |
|
|
|
case LT_LDAP: |
|
|
|
source.Cfg = new(LDAPConfig) |
|
|
|
source.Cfg = new(LDAPConfig) |
|
|
|
|
|
|
|
case LT_SMTP: |
|
|
|
|
|
|
|
source.Cfg = new(SMTPConfig) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -172,11 +196,20 @@ func LoginUser(uname, passwd string) (*User, error) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for _, source := range sources { |
|
|
|
for _, source := range sources { |
|
|
|
|
|
|
|
if source.Type == LT_LDAP { |
|
|
|
u, err := LoginUserLdapSource(nil, u.LoginName, passwd, |
|
|
|
u, err := LoginUserLdapSource(nil, u.LoginName, passwd, |
|
|
|
source.Id, source.Cfg.(*LDAPConfig), true) |
|
|
|
source.Id, source.Cfg.(*LDAPConfig), true) |
|
|
|
if err == nil { |
|
|
|
if err == nil { |
|
|
|
return u, err |
|
|
|
return u, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} else if source.Type == LT_SMTP { |
|
|
|
|
|
|
|
u, err := LoginUserSMTPSource(nil, u.LoginName, passwd, |
|
|
|
|
|
|
|
source.Id, source.Cfg.(*SMTPConfig), true) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err == nil { |
|
|
|
|
|
|
|
return u, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return nil, ErrUserNotExist |
|
|
|
return nil, ErrUserNotExist |
|
|
@ -200,6 +233,8 @@ func LoginUser(uname, passwd string) (*User, error) { |
|
|
|
return LoginUserLdapSource(u, u.LoginName, passwd, |
|
|
|
return LoginUserLdapSource(u, u.LoginName, passwd, |
|
|
|
source.Id, source.Cfg.(*LDAPConfig), false) |
|
|
|
source.Id, source.Cfg.(*LDAPConfig), false) |
|
|
|
case LT_SMTP: |
|
|
|
case LT_SMTP: |
|
|
|
|
|
|
|
return LoginUserSMTPSource(u, u.LoginName, passwd, |
|
|
|
|
|
|
|
source.Id, source.Cfg.(*SMTPConfig), false) |
|
|
|
} |
|
|
|
} |
|
|
|
return nil, ErrUnsupportedLoginType |
|
|
|
return nil, ErrUnsupportedLoginType |
|
|
|
} |
|
|
|
} |
|
|
@ -232,3 +267,89 @@ func LoginUserLdapSource(user *User, name, passwd string, sourceId int64, cfg *L |
|
|
|
|
|
|
|
|
|
|
|
return RegisterUser(user) |
|
|
|
return RegisterUser(user) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type loginAuth struct { |
|
|
|
|
|
|
|
username, password string |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func LoginAuth(username, password string) smtp.Auth { |
|
|
|
|
|
|
|
return &loginAuth{username, password} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) { |
|
|
|
|
|
|
|
return "LOGIN", []byte(a.username), 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 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return nil, nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var ( |
|
|
|
|
|
|
|
smtpAuths = []string{"plain", "login", ""} |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func SmtpAuth(addr string, a smtp.Auth) error { |
|
|
|
|
|
|
|
c, err := smtp.Dial(addr) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
defer c.Close() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ok, _ := c.Extension("STARTTLS"); ok { |
|
|
|
|
|
|
|
if err = c.StartTLS(nil); err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ok, _ := c.Extension("AUTH"); ok { |
|
|
|
|
|
|
|
if err = c.Auth(a); err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
return ErrUnsupportedLoginType |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Query if name/passwd can login against the LDAP direcotry pool
|
|
|
|
|
|
|
|
// Create a local user if success
|
|
|
|
|
|
|
|
// Return the same LoginUserPlain semantic
|
|
|
|
|
|
|
|
func LoginUserSMTPSource(user *User, name, passwd string, sourceId int64, cfg *SMTPConfig, autoRegister bool) (*User, error) { |
|
|
|
|
|
|
|
var auth smtp.Auth |
|
|
|
|
|
|
|
if cfg.Auth == "plain" { |
|
|
|
|
|
|
|
auth = smtp.PlainAuth("", name, passwd, cfg.Host) |
|
|
|
|
|
|
|
} else if cfg.Auth == "login" { |
|
|
|
|
|
|
|
auth = LoginAuth(name, passwd) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err := SmtpAuth(fmt.Sprintf("%s:%d", cfg.Host, cfg.Post), auth) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !autoRegister { |
|
|
|
|
|
|
|
return user, nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// fake a local user creation
|
|
|
|
|
|
|
|
user = &User{ |
|
|
|
|
|
|
|
LowerName: strings.ToLower(name), |
|
|
|
|
|
|
|
Name: strings.ToLower(name), |
|
|
|
|
|
|
|
LoginType: LT_SMTP, |
|
|
|
|
|
|
|
LoginSource: sourceId, |
|
|
|
|
|
|
|
LoginName: name, |
|
|
|
|
|
|
|
IsActive: true, |
|
|
|
|
|
|
|
Passwd: passwd, |
|
|
|
|
|
|
|
Email: name, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return RegisterUser(user) |
|
|
|
|
|
|
|
} |
|
|
|