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 (
 | 
					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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user