|  | @@ -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,10 +196,19 @@ func LoginUser(uname, passwd string) (*User, error) {
 | 
											
												
													
														|  |  			}
 |  |  			}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  			for _, source := range sources {
 |  |  			for _, source := range sources {
 | 
											
												
													
														|  | -				u, err := LoginUserLdapSource(nil, u.LoginName, passwd,
 |  | 
 | 
											
												
													
														|  | -					source.Id, source.Cfg.(*LDAPConfig), true)
 |  | 
 | 
											
												
													
														|  | -				if err == nil {
 |  | 
 | 
											
												
													
														|  | -					return u, err
 |  | 
 | 
											
												
													
														|  | 
 |  | +				if source.Type == LT_LDAP {
 | 
											
												
													
														|  | 
 |  | +					u, err := LoginUserLdapSource(nil, u.LoginName, passwd,
 | 
											
												
													
														|  | 
 |  | +						source.Id, source.Cfg.(*LDAPConfig), true)
 | 
											
												
													
														|  | 
 |  | +					if err == nil {
 | 
											
												
													
														|  | 
 |  | +						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
 | 
											
												
													
														|  | 
 |  | +					}
 | 
											
												
													
														|  |  				}
 |  |  				}
 | 
											
												
													
														|  |  			}
 |  |  			}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -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)
 | 
											
												
													
														|  | 
 |  | +}
 |