|  | @@ -27,6 +27,7 @@ const (
 | 
	
		
			
				|  |  |  	NOTYPE LoginType = iota
 | 
	
		
			
				|  |  |  	PLAIN
 | 
	
		
			
				|  |  |  	LDAP
 | 
	
		
			
				|  |  | +	DLDAP
 | 
	
		
			
				|  |  |  	SMTP
 | 
	
		
			
				|  |  |  	PAM
 | 
	
		
			
				|  |  |  )
 | 
	
	
		
			
				|  | @@ -38,9 +39,10 @@ var (
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  var LoginTypes = map[LoginType]string{
 | 
	
		
			
				|  |  | -	LDAP: "LDAP",
 | 
	
		
			
				|  |  | -	SMTP: "SMTP",
 | 
	
		
			
				|  |  | -	PAM:  "PAM",
 | 
	
		
			
				|  |  | +	LDAP:  "LDAP (via BindDN)",
 | 
	
		
			
				|  |  | +	DLDAP: "LDAP (simple auth)",
 | 
	
		
			
				|  |  | +	SMTP:  "SMTP",
 | 
	
		
			
				|  |  | +	PAM:   "PAM",
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Ensure structs implemented interface.
 | 
	
	
		
			
				|  | @@ -106,6 +108,8 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
 | 
	
		
			
				|  |  |  	case "type":
 | 
	
		
			
				|  |  |  		switch LoginType((*val).(int64)) {
 | 
	
		
			
				|  |  |  		case LDAP:
 | 
	
		
			
				|  |  | +			fallthrough
 | 
	
		
			
				|  |  | +		case DLDAP:
 | 
	
		
			
				|  |  |  			source.Cfg = new(LDAPConfig)
 | 
	
		
			
				|  |  |  		case SMTP:
 | 
	
		
			
				|  |  |  			source.Cfg = new(SMTPConfig)
 | 
	
	
		
			
				|  | @@ -171,84 +175,74 @@ func DelLoginSource(source *LoginSource) error {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // UserSignIn validates user name and password.
 | 
	
		
			
				|  |  |  func UserSignIn(uname, passwd string) (*User, error) {
 | 
	
		
			
				|  |  | -	u := new(User)
 | 
	
		
			
				|  |  | +	var u *User
 | 
	
		
			
				|  |  |  	if strings.Contains(uname, "@") {
 | 
	
		
			
				|  |  |  		u = &User{Email: uname}
 | 
	
		
			
				|  |  |  	} else {
 | 
	
		
			
				|  |  |  		u = &User{LowerName: strings.ToLower(uname)}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	has, err := x.Get(u)
 | 
	
		
			
				|  |  | +	userExists, err := x.Get(u)
 | 
	
		
			
				|  |  |  	if err != nil {
 | 
	
		
			
				|  |  |  		return nil, err
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	if u.LoginType == NOTYPE && has {
 | 
	
		
			
				|  |  | -		u.LoginType = PLAIN
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | +	if userExists {
 | 
	
		
			
				|  |  | +		switch u.LoginType {
 | 
	
		
			
				|  |  | +		case NOTYPE:
 | 
	
		
			
				|  |  | +			fallthrough
 | 
	
		
			
				|  |  | +		case PLAIN:
 | 
	
		
			
				|  |  | +			if u.ValidatePassword(passwd) {
 | 
	
		
			
				|  |  | +				return u, nil
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	// For plain login, user must exist to reach this line.
 | 
	
		
			
				|  |  | -	// Now verify password.
 | 
	
		
			
				|  |  | -	if u.LoginType == PLAIN {
 | 
	
		
			
				|  |  | -		if !u.ValidatePassword(passwd) {
 | 
	
		
			
				|  |  |  			return nil, ErrUserNotExist{u.Id, u.Name}
 | 
	
		
			
				|  |  | +		default:
 | 
	
		
			
				|  |  | +			var source LoginSource
 | 
	
		
			
				|  |  | +			hasSource, err := x.Id(u.LoginSource).Get(&source)
 | 
	
		
			
				|  |  | +			if err != nil {
 | 
	
		
			
				|  |  | +				return nil, err
 | 
	
		
			
				|  |  | +			} else if !hasSource {
 | 
	
		
			
				|  |  | +				return nil, ErrLoginSourceNotExist
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			return ExternalUserLogin(u, u.LoginName, passwd, &source, false)
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | -		return u, nil
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	if !has {
 | 
	
		
			
				|  |  | -		var sources []LoginSource
 | 
	
		
			
				|  |  | -		if err = x.UseBool().Find(&sources,
 | 
	
		
			
				|  |  | -			&LoginSource{IsActived: true, AllowAutoRegister: true}); err != nil {
 | 
	
		
			
				|  |  | -			return nil, err
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | +	var sources []LoginSource
 | 
	
		
			
				|  |  | +	if err = x.UseBool().Find(&sources, &LoginSource{IsActived: true, AllowAutoRegister: true}); err != nil {
 | 
	
		
			
				|  |  | +		return nil, err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		for _, source := range sources {
 | 
	
		
			
				|  |  | -			if source.Type == LDAP {
 | 
	
		
			
				|  |  | -				u, err := LoginUserLdapSource(nil, uname, passwd,
 | 
	
		
			
				|  |  | -					source.ID, source.Cfg.(*LDAPConfig), true)
 | 
	
		
			
				|  |  | -				if err == nil {
 | 
	
		
			
				|  |  | -					return u, nil
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -				log.Warn("Fail to login(%s) by LDAP(%s): %v", uname, source.Name, err)
 | 
	
		
			
				|  |  | -			} else if source.Type == SMTP {
 | 
	
		
			
				|  |  | -				u, err := LoginUserSMTPSource(nil, uname, passwd,
 | 
	
		
			
				|  |  | -					source.ID, source.Cfg.(*SMTPConfig), true)
 | 
	
		
			
				|  |  | -				if err == nil {
 | 
	
		
			
				|  |  | -					return u, nil
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -				log.Warn("Fail to login(%s) by SMTP(%s): %v", uname, source.Name, err)
 | 
	
		
			
				|  |  | -			} else if source.Type == PAM {
 | 
	
		
			
				|  |  | -				u, err := LoginUserPAMSource(nil, uname, passwd,
 | 
	
		
			
				|  |  | -					source.ID, source.Cfg.(*PAMConfig), true)
 | 
	
		
			
				|  |  | -				if err == nil {
 | 
	
		
			
				|  |  | -					return u, nil
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -				log.Warn("Fail to login(%s) by PAM(%s): %v", uname, source.Name, err)
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | +	for _, source := range sources {
 | 
	
		
			
				|  |  | +		u, err := ExternalUserLogin(nil, uname, passwd, &source, true)
 | 
	
		
			
				|  |  | +		if err == nil {
 | 
	
		
			
				|  |  | +			return u, nil
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		return nil, ErrUserNotExist{u.Id, u.Name}
 | 
	
		
			
				|  |  | +		log.Warn("Failed to login '%s' via '%s': %v", uname, source.Name, err)
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	var source LoginSource
 | 
	
		
			
				|  |  | -	hasSource, err := x.Id(u.LoginSource).Get(&source)
 | 
	
		
			
				|  |  | -	if err != nil {
 | 
	
		
			
				|  |  | -		return nil, err
 | 
	
		
			
				|  |  | -	} else if !hasSource {
 | 
	
		
			
				|  |  | -		return nil, ErrLoginSourceNotExist
 | 
	
		
			
				|  |  | -	} else if !source.IsActived {
 | 
	
		
			
				|  |  | +	return nil, ErrUserNotExist{u.Id, u.Name}
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func ExternalUserLogin(u *User, name, passwd string, source *LoginSource, autoRegister bool) (*User, error) {
 | 
	
		
			
				|  |  | +	if !source.IsActived {
 | 
	
		
			
				|  |  |  		return nil, ErrLoginSourceNotActived
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	switch u.LoginType {
 | 
	
		
			
				|  |  | +	switch source.Type {
 | 
	
		
			
				|  |  |  	case LDAP:
 | 
	
		
			
				|  |  | -		return LoginUserLdapSource(u, u.LoginName, passwd, source.ID, source.Cfg.(*LDAPConfig), false)
 | 
	
		
			
				|  |  | +		fallthrough
 | 
	
		
			
				|  |  | +	case DLDAP:
 | 
	
		
			
				|  |  | +		return LoginUserLdapSource(u, name, passwd, source, autoRegister)
 | 
	
		
			
				|  |  |  	case SMTP:
 | 
	
		
			
				|  |  | -		return LoginUserSMTPSource(u, u.LoginName, passwd, source.ID, source.Cfg.(*SMTPConfig), false)
 | 
	
		
			
				|  |  | +		return LoginUserSMTPSource(u, name, passwd, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
 | 
	
		
			
				|  |  |  	case PAM:
 | 
	
		
			
				|  |  | -		return LoginUserPAMSource(u, u.LoginName, passwd, source.ID, source.Cfg.(*PAMConfig), false)
 | 
	
		
			
				|  |  | +		return LoginUserPAMSource(u, name, passwd, source.ID, source.Cfg.(*PAMConfig), autoRegister)
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	return nil, ErrUnsupportedLoginType
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -256,8 +250,10 @@ func UserSignIn(uname, passwd string) (*User, error) {
 | 
	
		
			
				|  |  |  // Create a local user if success
 | 
	
		
			
				|  |  |  // Return the same LoginUserPlain semantic
 | 
	
		
			
				|  |  |  // FIXME: https://github.com/gogits/gogs/issues/672
 | 
	
		
			
				|  |  | -func LoginUserLdapSource(u *User, name, passwd string, sourceId int64, cfg *LDAPConfig, autoRegister bool) (*User, error) {
 | 
	
		
			
				|  |  | -	fn, sn, mail, admin, logged := cfg.Ldapsource.SearchEntry(name, passwd)
 | 
	
		
			
				|  |  | +func LoginUserLdapSource(u *User, name, passwd string, source *LoginSource, autoRegister bool) (*User, error) {
 | 
	
		
			
				|  |  | +	cfg := source.Cfg.(*LDAPConfig)
 | 
	
		
			
				|  |  | +	directBind := (source.Type == DLDAP)
 | 
	
		
			
				|  |  | +	fn, sn, mail, admin, logged := cfg.Ldapsource.SearchEntry(name, passwd, directBind)
 | 
	
		
			
				|  |  |  	if !logged {
 | 
	
		
			
				|  |  |  		// User not in LDAP, do nothing
 | 
	
		
			
				|  |  |  		return nil, ErrUserNotExist{0, name}
 | 
	
	
		
			
				|  | @@ -276,8 +272,8 @@ func LoginUserLdapSource(u *User, name, passwd string, sourceId int64, cfg *LDAP
 | 
	
		
			
				|  |  |  		LowerName:   strings.ToLower(name),
 | 
	
		
			
				|  |  |  		Name:        name,
 | 
	
		
			
				|  |  |  		FullName:    fn + " " + sn,
 | 
	
		
			
				|  |  | -		LoginType:   LDAP,
 | 
	
		
			
				|  |  | -		LoginSource: sourceId,
 | 
	
		
			
				|  |  | +		LoginType:   source.Type,
 | 
	
		
			
				|  |  | +		LoginSource: source.ID,
 | 
	
		
			
				|  |  |  		LoginName:   name,
 | 
	
		
			
				|  |  |  		Passwd:      passwd,
 | 
	
		
			
				|  |  |  		Email:       mail,
 |