mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-04 05:18:25 +00:00 
			
		
		
		
	add smtp authentication
This commit is contained in:
		
							
								
								
									
										121
									
								
								models/login.go
									
									
									
									
									
								
							
							
						
						
									
										121
									
								
								models/login.go
									
									
									
									
									
								
							@@ -7,6 +7,8 @@ package models
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/smtp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
@@ -50,6 +52,22 @@ func (cfg *LDAPConfig) ToDB() ([]byte, error) {
 | 
			
		||||
	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 {
 | 
			
		||||
	Id                int64
 | 
			
		||||
	Type              int
 | 
			
		||||
@@ -69,6 +87,10 @@ func (source *LoginSource) LDAP() *LDAPConfig {
 | 
			
		||||
	return source.Cfg.(*LDAPConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (source *LoginSource) SMTP() *SMTPConfig {
 | 
			
		||||
	return source.Cfg.(*SMTPConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// for xorm callback
 | 
			
		||||
func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
 | 
			
		||||
	if colName == "type" {
 | 
			
		||||
@@ -76,6 +98,8 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
 | 
			
		||||
		switch ty {
 | 
			
		||||
		case LT_LDAP:
 | 
			
		||||
			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 {
 | 
			
		||||
				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
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return nil, ErrUserNotExist
 | 
			
		||||
@@ -200,6 +233,8 @@ func LoginUser(uname, passwd string) (*User, error) {
 | 
			
		||||
			return LoginUserLdapSource(u, u.LoginName, passwd,
 | 
			
		||||
				source.Id, source.Cfg.(*LDAPConfig), false)
 | 
			
		||||
		case LT_SMTP:
 | 
			
		||||
			return LoginUserSMTPSource(u, u.LoginName, passwd,
 | 
			
		||||
				source.Id, source.Cfg.(*SMTPConfig), false)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, ErrUnsupportedLoginType
 | 
			
		||||
	}
 | 
			
		||||
@@ -232,3 +267,89 @@ func LoginUserLdapSource(user *User, name, passwd string, sourceId int64, cfg *L
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user