2014-06-09 01:53:53 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2019-11-14 22:10:23 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2014-05-05 13:32:47 +04:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2014-04-26 10:21:04 +04:00
package models
2014-05-03 06:48:14 +04:00
import (
2014-05-15 22:46:04 +04:00
"crypto/tls"
2014-05-03 06:48:14 +04:00
"encoding/json"
2014-05-05 12:40:25 +04:00
"errors"
2014-05-11 11:49:36 +04:00
"fmt"
"net/smtp"
2015-12-12 02:57:45 +03:00
"net/textproto"
2019-05-14 16:52:18 +03:00
"regexp"
2014-05-11 10:12:45 +04:00
"strings"
2014-04-26 10:21:04 +04:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/auth/ldap"
2017-02-25 17:57:06 +03:00
"code.gitea.io/gitea/modules/auth/oauth2"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/auth/pam"
"code.gitea.io/gitea/modules/log"
2019-07-07 09:01:01 +03:00
"code.gitea.io/gitea/modules/setting"
2019-08-15 17:46:21 +03:00
"code.gitea.io/gitea/modules/timeutil"
2019-08-23 19:40:30 +03:00
"github.com/unknwon/com"
2019-08-15 17:46:21 +03:00
"xorm.io/core"
2019-10-17 12:26:49 +03:00
"xorm.io/xorm"
2014-05-03 06:48:14 +04:00
)
2014-04-26 10:21:04 +04:00
2016-11-24 14:34:38 +03:00
// LoginType represents an login type.
2014-06-09 01:53:53 +04:00
type LoginType int
2016-08-31 11:22:41 +03:00
// Note: new type must append to the end of list to maintain compatibility.
2014-05-05 12:40:25 +04:00
const (
2016-11-07 23:58:22 +03:00
LoginNoType LoginType = iota
2017-02-25 17:57:06 +03:00
LoginPlain // 1
LoginLDAP // 2
LoginSMTP // 3
LoginPAM // 4
LoginDLDAP // 5
LoginOAuth2 // 6
2019-11-23 02:33:31 +03:00
LoginSSPI // 7
2014-05-05 12:40:25 +04:00
)
2016-11-24 14:34:38 +03:00
// LoginNames contains the name of LoginType values.
2015-09-11 00:11:41 +03:00
var LoginNames = map [ LoginType ] string {
2017-02-22 10:14:37 +03:00
LoginLDAP : "LDAP (via BindDN)" ,
LoginDLDAP : "LDAP (simple auth)" , // Via direct bind
LoginSMTP : "SMTP" ,
LoginPAM : "PAM" ,
LoginOAuth2 : "OAuth2" ,
2019-11-23 02:33:31 +03:00
LoginSSPI : "SPNEGO with SSPI" ,
2014-05-05 12:40:25 +04:00
}
2014-04-26 10:21:04 +04:00
2016-11-24 14:34:38 +03:00
// SecurityProtocolNames contains the name of SecurityProtocol values.
2016-07-08 02:25:09 +03:00
var SecurityProtocolNames = map [ ldap . SecurityProtocol ] string {
2016-11-07 19:38:43 +03:00
ldap . SecurityProtocolUnencrypted : "Unencrypted" ,
2016-11-07 23:58:22 +03:00
ldap . SecurityProtocolLDAPS : "LDAPS" ,
2016-11-10 18:16:32 +03:00
ldap . SecurityProtocolStartTLS : "StartTLS" ,
2016-07-08 02:25:09 +03:00
}
2014-12-07 04:22:48 +03:00
// Ensure structs implemented interface.
2014-05-12 19:02:36 +04:00
var (
_ core . Conversion = & LDAPConfig { }
_ core . Conversion = & SMTPConfig { }
2015-04-23 14:58:57 +03:00
_ core . Conversion = & PAMConfig { }
2017-02-22 10:14:37 +03:00
_ core . Conversion = & OAuth2Config { }
2019-11-23 02:33:31 +03:00
_ core . Conversion = & SSPIConfig { }
2014-05-12 19:02:36 +04:00
)
2014-04-26 10:21:04 +04:00
2016-11-24 14:34:38 +03:00
// LDAPConfig holds configuration for LDAP login source.
2014-04-26 10:21:04 +04:00
type LDAPConfig struct {
2015-09-14 22:48:51 +03:00
* ldap . Source
2014-04-26 10:21:04 +04:00
}
2016-11-24 14:34:38 +03:00
// FromDB fills up a LDAPConfig from serialized format.
2014-04-26 10:21:04 +04:00
func ( cfg * LDAPConfig ) FromDB ( bs [ ] byte ) error {
2015-09-14 22:48:51 +03:00
return json . Unmarshal ( bs , & cfg )
2014-04-26 10:21:04 +04:00
}
2016-11-24 14:34:38 +03:00
// ToDB exports a LDAPConfig to a serialized format.
2014-04-26 10:21:04 +04:00
func ( cfg * LDAPConfig ) ToDB ( ) ( [ ] byte , error ) {
2015-09-14 22:48:51 +03:00
return json . Marshal ( cfg )
2014-04-26 10:21:04 +04:00
}
2016-11-24 14:34:38 +03:00
// SecurityProtocolName returns the name of configured security
// protocol.
2016-07-08 02:25:09 +03:00
func ( cfg * LDAPConfig ) SecurityProtocolName ( ) string {
return SecurityProtocolNames [ cfg . SecurityProtocol ]
}
2016-11-24 14:34:38 +03:00
// SMTPConfig holds configuration for the SMTP login source.
2014-05-11 11:49:36 +04:00
type SMTPConfig struct {
2015-09-11 20:32:33 +03:00
Auth string
Host string
Port int
AllowedDomains string ` xorm:"TEXT" `
TLS bool
SkipVerify bool
2014-05-11 11:49:36 +04:00
}
2016-11-24 14:34:38 +03:00
// FromDB fills up an SMTPConfig from serialized format.
2014-05-11 11:49:36 +04:00
func ( cfg * SMTPConfig ) FromDB ( bs [ ] byte ) error {
return json . Unmarshal ( bs , cfg )
}
2016-11-24 14:34:38 +03:00
// ToDB exports an SMTPConfig to a serialized format.
2014-05-11 11:49:36 +04:00
func ( cfg * SMTPConfig ) ToDB ( ) ( [ ] byte , error ) {
return json . Marshal ( cfg )
}
2016-11-24 14:34:38 +03:00
// PAMConfig holds configuration for the PAM login source.
2015-04-23 14:58:57 +03:00
type PAMConfig struct {
ServiceName string // pam service (e.g. system-auth)
}
2016-11-24 14:34:38 +03:00
// FromDB fills up a PAMConfig from serialized format.
2015-04-23 14:58:57 +03:00
func ( cfg * PAMConfig ) FromDB ( bs [ ] byte ) error {
return json . Unmarshal ( bs , & cfg )
}
2016-11-24 14:34:38 +03:00
// ToDB exports a PAMConfig to a serialized format.
2015-04-23 14:58:57 +03:00
func ( cfg * PAMConfig ) ToDB ( ) ( [ ] byte , error ) {
return json . Marshal ( cfg )
}
2017-02-22 10:14:37 +03:00
// OAuth2Config holds configuration for the OAuth2 login source.
type OAuth2Config struct {
2017-05-01 16:26:53 +03:00
Provider string
ClientID string
ClientSecret string
OpenIDConnectAutoDiscoveryURL string
CustomURLMapping * oauth2 . CustomURLMapping
2017-02-22 10:14:37 +03:00
}
// FromDB fills up an OAuth2Config from serialized format.
func ( cfg * OAuth2Config ) FromDB ( bs [ ] byte ) error {
return json . Unmarshal ( bs , cfg )
}
// ToDB exports an SMTPConfig to a serialized format.
func ( cfg * OAuth2Config ) ToDB ( ) ( [ ] byte , error ) {
return json . Marshal ( cfg )
}
2019-11-23 02:33:31 +03:00
// SSPIConfig holds configuration for SSPI single sign-on.
type SSPIConfig struct {
AutoCreateUsers bool
AutoActivateUsers bool
StripDomainNames bool
SeparatorReplacement string
DefaultLanguage string
}
// FromDB fills up an SSPIConfig from serialized format.
func ( cfg * SSPIConfig ) FromDB ( bs [ ] byte ) error {
return json . Unmarshal ( bs , cfg )
}
// ToDB exports an SSPIConfig to a serialized format.
func ( cfg * SSPIConfig ) ToDB ( ) ( [ ] byte , error ) {
return json . Marshal ( cfg )
}
2016-08-31 11:22:41 +03:00
// LoginSource represents an external way for authorizing users.
2014-04-26 10:21:04 +04:00
type LoginSource struct {
2017-05-10 16:10:18 +03:00
ID int64 ` xorm:"pk autoincr" `
Type LoginType
Name string ` xorm:"UNIQUE" `
IsActived bool ` xorm:"INDEX NOT NULL DEFAULT false" `
IsSyncEnabled bool ` xorm:"INDEX NOT NULL DEFAULT false" `
Cfg core . Conversion ` xorm:"TEXT" `
2016-03-10 03:53:30 +03:00
2019-08-15 17:46:21 +03:00
CreatedUnix timeutil . TimeStamp ` xorm:"INDEX created" `
UpdatedUnix timeutil . TimeStamp ` xorm:"INDEX updated" `
2014-05-03 06:48:14 +04:00
}
2016-01-11 09:34:32 +03:00
// Cell2Int64 converts a xorm.Cell type to int64,
// and handles possible irregular cases.
func Cell2Int64 ( val xorm . Cell ) int64 {
switch ( * val ) . ( type ) {
2016-01-11 10:47:23 +03:00
case [ ] uint8 :
log . Trace ( "Cell2Int64 ([]uint8): %v" , * val )
return com . StrTo ( string ( ( * val ) . ( [ ] uint8 ) ) ) . MustInt64 ( )
2016-01-11 09:34:32 +03:00
}
return ( * val ) . ( int64 )
}
2016-11-24 14:34:38 +03:00
// BeforeSet is invoked from XORM before setting the value of a field of this object.
2015-08-29 10:45:58 +03:00
func ( source * LoginSource ) BeforeSet ( colName string , val xorm . Cell ) {
2019-06-12 22:41:28 +03:00
if colName == "type" {
2016-01-11 09:34:32 +03:00
switch LoginType ( Cell2Int64 ( val ) ) {
2016-11-07 23:58:22 +03:00
case LoginLDAP , LoginDLDAP :
2015-08-29 10:45:58 +03:00
source . Cfg = new ( LDAPConfig )
2016-11-07 23:58:22 +03:00
case LoginSMTP :
2015-08-29 10:45:58 +03:00
source . Cfg = new ( SMTPConfig )
2016-11-07 23:58:22 +03:00
case LoginPAM :
2015-08-29 10:45:58 +03:00
source . Cfg = new ( PAMConfig )
2017-02-22 10:14:37 +03:00
case LoginOAuth2 :
source . Cfg = new ( OAuth2Config )
2019-11-23 02:33:31 +03:00
case LoginSSPI :
source . Cfg = new ( SSPIConfig )
2015-09-11 19:03:08 +03:00
default :
panic ( "unrecognized login source type: " + com . ToStr ( * val ) )
2015-08-29 10:45:58 +03:00
}
}
}
2016-11-24 14:34:38 +03:00
// TypeName return name of this login source type.
2015-09-11 00:11:41 +03:00
func ( source * LoginSource ) TypeName ( ) string {
return LoginNames [ source . Type ]
2014-05-05 12:40:25 +04:00
}
2016-11-24 14:34:38 +03:00
// IsLDAP returns true of this source is of the LDAP type.
2015-09-11 19:03:08 +03:00
func ( source * LoginSource ) IsLDAP ( ) bool {
2016-11-07 23:58:22 +03:00
return source . Type == LoginLDAP
2015-09-11 19:03:08 +03:00
}
2016-11-24 14:34:38 +03:00
// IsDLDAP returns true of this source is of the DLDAP type.
2015-09-11 19:03:08 +03:00
func ( source * LoginSource ) IsDLDAP ( ) bool {
2016-11-07 23:58:22 +03:00
return source . Type == LoginDLDAP
2015-09-11 19:03:08 +03:00
}
2016-11-24 14:34:38 +03:00
// IsSMTP returns true of this source is of the SMTP type.
2015-09-11 19:03:08 +03:00
func ( source * LoginSource ) IsSMTP ( ) bool {
2016-11-07 23:58:22 +03:00
return source . Type == LoginSMTP
2015-09-11 19:03:08 +03:00
}
2016-11-24 14:34:38 +03:00
// IsPAM returns true of this source is of the PAM type.
2015-09-11 19:03:08 +03:00
func ( source * LoginSource ) IsPAM ( ) bool {
2016-11-07 23:58:22 +03:00
return source . Type == LoginPAM
2015-09-11 19:03:08 +03:00
}
2017-02-22 10:14:37 +03:00
// IsOAuth2 returns true of this source is of the OAuth2 type.
func ( source * LoginSource ) IsOAuth2 ( ) bool {
return source . Type == LoginOAuth2
}
2019-11-23 02:33:31 +03:00
// IsSSPI returns true of this source is of the SSPI type.
func ( source * LoginSource ) IsSSPI ( ) bool {
return source . Type == LoginSSPI
}
2016-11-24 14:34:38 +03:00
// HasTLS returns true of this source supports TLS.
2016-07-08 02:25:09 +03:00
func ( source * LoginSource ) HasTLS ( ) bool {
return ( ( source . IsLDAP ( ) || source . IsDLDAP ( ) ) &&
2016-11-07 19:38:43 +03:00
source . LDAP ( ) . SecurityProtocol > ldap . SecurityProtocolUnencrypted ) ||
2016-07-08 02:25:09 +03:00
source . IsSMTP ( )
}
2016-11-24 14:34:38 +03:00
// UseTLS returns true of this source is configured to use TLS.
2015-09-11 19:03:08 +03:00
func ( source * LoginSource ) UseTLS ( ) bool {
switch source . Type {
2016-11-07 23:58:22 +03:00
case LoginLDAP , LoginDLDAP :
2016-11-07 19:38:43 +03:00
return source . LDAP ( ) . SecurityProtocol != ldap . SecurityProtocolUnencrypted
2016-11-07 23:58:22 +03:00
case LoginSMTP :
2015-09-11 19:03:08 +03:00
return source . SMTP ( ) . TLS
}
return false
}
2016-11-24 14:34:38 +03:00
// SkipVerify returns true if this source is configured to skip SSL
// verification.
2015-09-14 22:48:51 +03:00
func ( source * LoginSource ) SkipVerify ( ) bool {
switch source . Type {
2016-11-07 23:58:22 +03:00
case LoginLDAP , LoginDLDAP :
2015-09-14 22:48:51 +03:00
return source . LDAP ( ) . SkipVerify
2016-11-07 23:58:22 +03:00
case LoginSMTP :
2015-09-14 22:48:51 +03:00
return source . SMTP ( ) . SkipVerify
}
return false
}
2016-11-24 14:34:38 +03:00
// LDAP returns LDAPConfig for this source, if of LDAP type.
2014-05-05 12:40:25 +04:00
func ( source * LoginSource ) LDAP ( ) * LDAPConfig {
return source . Cfg . ( * LDAPConfig )
}
2016-11-24 14:34:38 +03:00
// SMTP returns SMTPConfig for this source, if of SMTP type.
2014-05-11 11:49:36 +04:00
func ( source * LoginSource ) SMTP ( ) * SMTPConfig {
return source . Cfg . ( * SMTPConfig )
}
2016-11-24 14:34:38 +03:00
// PAM returns PAMConfig for this source, if of PAM type.
2015-04-23 14:58:57 +03:00
func ( source * LoginSource ) PAM ( ) * PAMConfig {
return source . Cfg . ( * PAMConfig )
}
2016-11-24 14:34:38 +03:00
2017-02-22 10:14:37 +03:00
// OAuth2 returns OAuth2Config for this source, if of OAuth2 type.
func ( source * LoginSource ) OAuth2 ( ) * OAuth2Config {
return source . Cfg . ( * OAuth2Config )
}
2019-11-23 02:33:31 +03:00
// SSPI returns SSPIConfig for this source, if of SSPI type.
func ( source * LoginSource ) SSPI ( ) * SSPIConfig {
return source . Cfg . ( * SSPIConfig )
}
2016-11-24 14:34:38 +03:00
// CreateLoginSource inserts a LoginSource in the DB if not already
// existing with the given name.
2016-08-31 10:56:10 +03:00
func CreateLoginSource ( source * LoginSource ) error {
has , err := x . Get ( & LoginSource { Name : source . Name } )
if err != nil {
return err
} else if has {
return ErrLoginSourceAlreadyExist { source . Name }
}
2017-05-10 16:10:18 +03:00
// Synchronization is only aviable with LDAP for now
if ! source . IsLDAP ( ) {
source . IsSyncEnabled = false
}
2016-08-31 10:56:10 +03:00
_ , err = x . Insert ( source )
2017-05-01 16:26:53 +03:00
if err == nil && source . IsOAuth2 ( ) && source . IsActived {
2017-02-22 10:14:37 +03:00
oAuth2Config := source . OAuth2 ( )
2017-05-01 16:26:53 +03:00
err = oauth2 . RegisterProvider ( source . Name , oAuth2Config . Provider , oAuth2Config . ClientID , oAuth2Config . ClientSecret , oAuth2Config . OpenIDConnectAutoDiscoveryURL , oAuth2Config . CustomURLMapping )
err = wrapOpenIDConnectInitializeError ( err , source . Name , oAuth2Config )
if err != nil {
// remove the LoginSource in case of errors while registering OAuth2 providers
2019-06-12 22:41:28 +03:00
if _ , err := x . Delete ( source ) ; err != nil {
log . Error ( "CreateLoginSource: Error while wrapOpenIDConnectInitializeError: %v" , err )
}
return err
2017-05-01 16:26:53 +03:00
}
2017-02-22 10:14:37 +03:00
}
2014-06-09 01:53:53 +04:00
return err
}
2016-11-24 14:34:38 +03:00
// LoginSources returns a slice of all login sources found in DB.
2015-09-13 16:51:51 +03:00
func LoginSources ( ) ( [ ] * LoginSource , error ) {
2017-02-22 10:14:37 +03:00
auths := make ( [ ] * LoginSource , 0 , 6 )
2015-08-29 10:45:58 +03:00
return auths , x . Find ( & auths )
2014-05-03 06:48:14 +04:00
}
2019-11-23 02:33:31 +03:00
// LoginSourcesByType returns all sources of the specified type
func LoginSourcesByType ( loginType LoginType ) ( [ ] * LoginSource , error ) {
sources := make ( [ ] * LoginSource , 0 , 1 )
if err := x . Where ( "type = ?" , loginType ) . Find ( & sources ) ; err != nil {
return nil , err
}
return sources , nil
}
// ActiveLoginSources returns all active sources of the specified type
func ActiveLoginSources ( loginType LoginType ) ( [ ] * LoginSource , error ) {
sources := make ( [ ] * LoginSource , 0 , 1 )
if err := x . Where ( "is_actived = ? and type = ?" , true , loginType ) . Find ( & sources ) ; err != nil {
return nil , err
}
return sources , nil
}
// IsSSPIEnabled returns true if there is at least one activated login
// source of type LoginSSPI
func IsSSPIEnabled ( ) bool {
if ! HasEngine {
return false
}
sources , err := ActiveLoginSources ( LoginSSPI )
if err != nil {
log . Error ( "ActiveLoginSources: %v" , err )
return false
}
return len ( sources ) > 0
}
2015-12-06 01:13:13 +03:00
// GetLoginSourceByID returns login source by given ID.
2015-08-29 10:45:58 +03:00
func GetLoginSourceByID ( id int64 ) ( * LoginSource , error ) {
2014-05-05 12:40:25 +04:00
source := new ( LoginSource )
2017-10-05 07:43:04 +03:00
has , err := x . ID ( id ) . Get ( source )
2014-05-05 12:40:25 +04:00
if err != nil {
return nil , err
2014-06-09 01:53:53 +04:00
} else if ! has {
2016-08-31 10:56:10 +03:00
return nil , ErrLoginSourceNotExist { id }
2014-05-05 12:40:25 +04:00
}
return source , nil
}
2016-11-24 14:34:38 +03:00
// UpdateSource updates a LoginSource record in DB.
2014-05-11 14:10:37 +04:00
func UpdateSource ( source * LoginSource ) error {
2017-05-01 16:26:53 +03:00
var originalLoginSource * LoginSource
if source . IsOAuth2 ( ) {
// keep track of the original values so we can restore in case of errors while registering OAuth2 providers
var err error
if originalLoginSource , err = GetLoginSourceByID ( source . ID ) ; err != nil {
return err
}
}
2017-10-05 07:43:04 +03:00
_ , err := x . ID ( source . ID ) . AllCols ( ) . Update ( source )
2017-05-01 16:26:53 +03:00
if err == nil && source . IsOAuth2 ( ) && source . IsActived {
2017-02-22 10:14:37 +03:00
oAuth2Config := source . OAuth2 ( )
2017-05-01 16:26:53 +03:00
err = oauth2 . RegisterProvider ( source . Name , oAuth2Config . Provider , oAuth2Config . ClientID , oAuth2Config . ClientSecret , oAuth2Config . OpenIDConnectAutoDiscoveryURL , oAuth2Config . CustomURLMapping )
err = wrapOpenIDConnectInitializeError ( err , source . Name , oAuth2Config )
if err != nil {
// restore original values since we cannot update the provider it self
2019-06-12 22:41:28 +03:00
if _ , err := x . ID ( source . ID ) . AllCols ( ) . Update ( originalLoginSource ) ; err != nil {
log . Error ( "UpdateSource: Error while wrapOpenIDConnectInitializeError: %v" , err )
}
return err
2017-05-01 16:26:53 +03:00
}
2017-02-22 10:14:37 +03:00
}
2014-05-03 06:48:14 +04:00
return err
}
2016-11-24 14:34:38 +03:00
// DeleteSource deletes a LoginSource record in DB.
2015-09-11 19:03:08 +03:00
func DeleteSource ( source * LoginSource ) error {
count , err := x . Count ( & User { LoginSource : source . ID } )
2014-05-05 12:40:25 +04:00
if err != nil {
return err
2015-09-11 19:03:08 +03:00
} else if count > 0 {
2016-08-31 11:22:41 +03:00
return ErrLoginSourceInUse { source . ID }
2014-05-05 12:40:25 +04:00
}
2017-02-22 10:14:37 +03:00
count , err = x . Count ( & ExternalLoginUser { LoginSourceID : source . ID } )
if err != nil {
return err
} else if count > 0 {
return ErrLoginSourceInUse { source . ID }
}
if source . IsOAuth2 ( ) {
oauth2 . RemoveProvider ( source . Name )
}
2017-10-05 07:43:04 +03:00
_ , err = x . ID ( source . ID ) . Delete ( new ( LoginSource ) )
2014-05-03 06:48:14 +04:00
return err
2014-04-26 10:21:04 +04:00
}
2014-05-11 10:12:45 +04:00
2016-08-31 11:22:41 +03:00
// CountLoginSources returns number of login sources.
func CountLoginSources ( ) int64 {
count , _ := x . Count ( new ( LoginSource ) )
return count
}
2015-09-13 03:58:51 +03:00
// .____ ________ _____ __________
// | | \______ \ / _ \\______ \
// | | | | \ / /_\ \| ___/
// | |___ | ` \/ | \ |
// |_______ \/_______ /\____|__ /____|
// \/ \/ \/
2014-05-11 10:12:45 +04:00
2016-08-31 11:22:41 +03:00
func composeFullName ( firstname , surname , username string ) string {
switch {
case len ( firstname ) == 0 && len ( surname ) == 0 :
return username
case len ( firstname ) == 0 :
return surname
case len ( surname ) == 0 :
return firstname
default :
return firstname + " " + surname
}
}
2019-05-14 16:52:18 +03:00
var (
2019-06-12 22:41:28 +03:00
alphaDashDotPattern = regexp . MustCompile ( ` [^\w-\.] ` )
2019-05-14 16:52:18 +03:00
)
2016-08-31 11:22:41 +03:00
// LoginViaLDAP queries if login/password is valid against the LDAP directory pool,
2015-11-21 20:58:31 +03:00
// and create a local user if success when enabled.
2016-11-21 22:08:21 +03:00
func LoginViaLDAP ( user * User , login , password string , source * LoginSource , autoRegister bool ) ( * User , error ) {
2017-05-10 16:10:18 +03:00
sr := source . Cfg . ( * LDAPConfig ) . SearchEntry ( login , password , source . Type == LoginDLDAP )
if sr == nil {
2014-12-06 02:08:09 +03:00
// User not in LDAP, do nothing
2016-11-12 02:01:09 +03:00
return nil , ErrUserNotExist { 0 , login , 0 }
2014-05-11 10:12:45 +04:00
}
2015-08-13 02:58:27 +03:00
2018-12-27 20:28:48 +03:00
var isAttributeSSHPublicKeySet = len ( strings . TrimSpace ( source . LDAP ( ) . AttributeSSHPublicKey ) ) > 0
2019-11-14 22:10:23 +03:00
// Update User admin flag if exist
if isExist , err := IsUserExist ( 0 , sr . Username ) ; err != nil {
return nil , err
} else if isExist &&
! user . ProhibitLogin && len ( source . LDAP ( ) . AdminFilter ) > 0 && user . IsAdmin != sr . IsAdmin {
// Change existing admin flag only if AdminFilter option is set
user . IsAdmin = sr . IsAdmin
err = UpdateUserCols ( user , "is_admin" )
if err != nil {
return nil , err
}
}
2014-05-11 10:12:45 +04:00
if ! autoRegister {
2018-12-27 20:28:48 +03:00
if isAttributeSSHPublicKeySet && synchronizeLdapSSHPublicKeys ( user , source , sr . SSHPublicKey ) {
2019-06-12 22:41:28 +03:00
return user , RewriteAllPublicKeys ( )
2018-12-27 20:28:48 +03:00
}
2016-08-31 11:22:41 +03:00
return user , nil
2014-05-11 10:12:45 +04:00
}
2014-12-06 02:08:09 +03:00
// Fallback.
2017-05-10 16:10:18 +03:00
if len ( sr . Username ) == 0 {
sr . Username = login
2015-12-01 16:49:49 +03:00
}
2016-07-12 02:07:57 +03:00
// Validate username make sure it satisfies requirement.
2019-05-14 16:52:18 +03:00
if alphaDashDotPattern . MatchString ( sr . Username ) {
2017-05-10 16:10:18 +03:00
return nil , fmt . Errorf ( "Invalid pattern for attribute 'username' [%s]: must be valid alpha or numeric or dash(-_) or dot characters" , sr . Username )
2016-07-12 02:07:57 +03:00
}
2017-05-10 16:10:18 +03:00
if len ( sr . Mail ) == 0 {
sr . Mail = fmt . Sprintf ( "%s@localhost" , sr . Username )
2014-12-06 02:08:09 +03:00
}
2016-08-31 11:22:41 +03:00
user = & User {
2017-05-10 16:10:18 +03:00
LowerName : strings . ToLower ( sr . Username ) ,
Name : sr . Username ,
FullName : composeFullName ( sr . Name , sr . Surname , sr . Username ) ,
Email : sr . Mail ,
2015-09-05 06:39:23 +03:00
LoginType : source . Type ,
LoginSource : source . ID ,
2016-08-31 11:22:41 +03:00
LoginName : login ,
2014-12-06 02:08:09 +03:00
IsActive : true ,
2017-05-10 16:10:18 +03:00
IsAdmin : sr . IsAdmin ,
2014-05-11 10:12:45 +04:00
}
2018-12-27 20:28:48 +03:00
err := CreateUser ( user )
if err == nil && isAttributeSSHPublicKeySet && addLdapSSHPublicKeys ( user , source , sr . SSHPublicKey ) {
2019-06-12 22:41:28 +03:00
err = RewriteAllPublicKeys ( )
2018-12-27 20:28:48 +03:00
}
return user , err
2015-12-01 16:49:49 +03:00
}
2015-09-13 03:58:51 +03:00
// _________ __________________________
// / _____/ / \__ ___/\______ \
// \_____ \ / \ / \| | | ___/
// / \/ Y \ | | |
// /_______ /\____|__ /____| |____|
// \/ \/
2016-08-31 11:22:41 +03:00
type smtpLoginAuth struct {
2014-05-11 11:49:36 +04:00
username , password string
}
2016-08-31 11:22:41 +03:00
func ( auth * smtpLoginAuth ) Start ( server * smtp . ServerInfo ) ( string , [ ] byte , error ) {
return "LOGIN" , [ ] byte ( auth . username ) , nil
2014-05-11 11:49:36 +04:00
}
2016-08-31 11:22:41 +03:00
func ( auth * smtpLoginAuth ) Next ( fromServer [ ] byte , more bool ) ( [ ] byte , error ) {
2014-05-11 11:49:36 +04:00
if more {
switch string ( fromServer ) {
case "Username:" :
2016-08-31 11:22:41 +03:00
return [ ] byte ( auth . username ) , nil
2014-05-11 11:49:36 +04:00
case "Password:" :
2016-08-31 11:22:41 +03:00
return [ ] byte ( auth . password ) , nil
2014-05-11 11:49:36 +04:00
}
}
return nil , nil
}
2016-11-24 14:34:38 +03:00
// SMTP authentication type names.
2015-08-29 10:45:58 +03:00
const (
2016-11-07 23:58:22 +03:00
SMTPPlain = "PLAIN"
SMTPLogin = "LOGIN"
2014-05-11 11:49:36 +04:00
)
2016-11-24 14:34:38 +03:00
// SMTPAuths contains available SMTP authentication type names.
2016-11-07 23:58:22 +03:00
var SMTPAuths = [ ] string { SMTPPlain , SMTPLogin }
2015-08-29 10:45:58 +03:00
2016-11-24 14:34:38 +03:00
// SMTPAuth performs an SMTP authentication.
2015-08-29 10:45:58 +03:00
func SMTPAuth ( a smtp . Auth , cfg * SMTPConfig ) error {
c , err := smtp . Dial ( fmt . Sprintf ( "%s:%d" , cfg . Host , cfg . Port ) )
2014-05-11 11:49:36 +04:00
if err != nil {
return err
}
defer c . Close ( )
2014-05-15 22:46:04 +04:00
if err = c . Hello ( "gogs" ) ; err != nil {
return err
}
2015-08-29 10:45:58 +03:00
if cfg . TLS {
2014-05-11 16:04:28 +04:00
if ok , _ := c . Extension ( "STARTTLS" ) ; ok {
2015-08-29 10:45:58 +03:00
if err = c . StartTLS ( & tls . Config {
InsecureSkipVerify : cfg . SkipVerify ,
ServerName : cfg . Host ,
} ) ; err != nil {
2014-05-11 16:04:28 +04:00
return err
}
} else {
2014-05-16 07:03:26 +04:00
return errors . New ( "SMTP server unsupports TLS" )
2014-05-11 11:49:36 +04:00
}
}
if ok , _ := c . Extension ( "AUTH" ) ; ok {
2017-09-19 11:08:30 +03:00
return c . Auth ( a )
2014-05-11 11:49:36 +04:00
}
2014-06-09 01:53:53 +04:00
return ErrUnsupportedLoginType
2014-05-11 11:49:36 +04:00
}
2016-08-31 11:22:41 +03:00
// LoginViaSMTP queries if login/password is valid against the SMTP,
// and create a local user if success when enabled.
func LoginViaSMTP ( user * User , login , password string , sourceID int64 , cfg * SMTPConfig , autoRegister bool ) ( * User , error ) {
2015-09-11 20:32:33 +03:00
// Verify allowed domains.
if len ( cfg . AllowedDomains ) > 0 {
2016-08-31 11:22:41 +03:00
idx := strings . Index ( login , "@" )
2015-09-11 20:32:33 +03:00
if idx == - 1 {
2016-11-12 02:01:09 +03:00
return nil , ErrUserNotExist { 0 , login , 0 }
2017-02-25 17:57:06 +03:00
} else if ! com . IsSliceContainsStr ( strings . Split ( cfg . AllowedDomains , "," ) , login [ idx + 1 : ] ) {
2016-11-12 02:01:09 +03:00
return nil , ErrUserNotExist { 0 , login , 0 }
2015-09-11 20:32:33 +03:00
}
}
2014-05-11 11:49:36 +04:00
var auth smtp . Auth
2016-11-07 23:58:22 +03:00
if cfg . Auth == SMTPPlain {
2016-08-31 11:22:41 +03:00
auth = smtp . PlainAuth ( "" , login , password , cfg . Host )
2016-11-07 23:58:22 +03:00
} else if cfg . Auth == SMTPLogin {
2016-08-31 11:22:41 +03:00
auth = & smtpLoginAuth { login , password }
2014-05-11 16:04:28 +04:00
} else {
2014-05-15 22:46:04 +04:00
return nil , errors . New ( "Unsupported SMTP auth type" )
2014-05-11 11:49:36 +04:00
}
2015-08-29 10:45:58 +03:00
if err := SMTPAuth ( auth , cfg ) ; err != nil {
2015-12-12 02:57:45 +03:00
// Check standard error format first,
// then fallback to worse case.
tperr , ok := err . ( * textproto . Error )
if ( ok && tperr . Code == 535 ) ||
strings . Contains ( err . Error ( ) , "Username and Password not accepted" ) {
2016-11-12 02:01:09 +03:00
return nil , ErrUserNotExist { 0 , login , 0 }
2014-05-15 22:46:04 +04:00
}
2014-05-11 11:49:36 +04:00
return nil , err
}
if ! autoRegister {
2016-08-31 11:22:41 +03:00
return user , nil
2014-05-11 11:49:36 +04:00
}
2016-08-31 11:22:41 +03:00
username := login
idx := strings . Index ( login , "@" )
2014-05-11 16:04:28 +04:00
if idx > - 1 {
2016-08-31 11:22:41 +03:00
username = login [ : idx ]
2014-05-11 16:04:28 +04:00
}
2016-08-31 11:22:41 +03:00
user = & User {
LowerName : strings . ToLower ( username ) ,
Name : strings . ToLower ( username ) ,
Email : login ,
Passwd : password ,
2016-11-07 23:58:22 +03:00
LoginType : LoginSMTP ,
2015-12-11 03:02:57 +03:00
LoginSource : sourceID ,
2016-08-31 11:22:41 +03:00
LoginName : login ,
2014-05-11 11:49:36 +04:00
IsActive : true ,
}
2016-08-31 11:22:41 +03:00
return user , CreateUser ( user )
2014-05-11 11:49:36 +04:00
}
2015-04-23 14:58:57 +03:00
2015-09-13 03:58:51 +03:00
// __________ _____ _____
// \______ \/ _ \ / \
// | ___/ /_\ \ / \ / \
// | | / | \/ Y \
// |____| \____|__ /\____|__ /
// \/ \/
2016-08-31 11:22:41 +03:00
// LoginViaPAM queries if login/password is valid against the PAM,
// and create a local user if success when enabled.
func LoginViaPAM ( user * User , login , password string , sourceID int64 , cfg * PAMConfig , autoRegister bool ) ( * User , error ) {
2016-11-27 09:03:59 +03:00
if err := pam . Auth ( cfg . ServiceName , login , password ) ; err != nil {
2015-04-23 14:58:57 +03:00
if strings . Contains ( err . Error ( ) , "Authentication failure" ) {
2016-11-12 02:01:09 +03:00
return nil , ErrUserNotExist { 0 , login , 0 }
2015-04-23 14:58:57 +03:00
}
return nil , err
}
if ! autoRegister {
2016-08-31 11:22:41 +03:00
return user , nil
2015-04-23 14:58:57 +03:00
}
2016-08-31 11:22:41 +03:00
user = & User {
LowerName : strings . ToLower ( login ) ,
Name : login ,
Email : login ,
Passwd : password ,
2016-11-07 23:58:22 +03:00
LoginType : LoginPAM ,
2015-12-11 03:02:57 +03:00
LoginSource : sourceID ,
2016-08-31 11:22:41 +03:00
LoginName : login ,
2015-04-23 14:58:57 +03:00
IsActive : true ,
}
2016-08-31 11:22:41 +03:00
return user , CreateUser ( user )
2015-04-23 14:58:57 +03:00
}
2015-09-13 03:58:51 +03:00
2016-11-24 14:34:38 +03:00
// ExternalUserLogin attempts a login using external source types.
2016-08-31 11:22:41 +03:00
func ExternalUserLogin ( user * User , login , password string , source * LoginSource , autoRegister bool ) ( * User , error ) {
2015-09-13 03:58:51 +03:00
if ! source . IsActived {
return nil , ErrLoginSourceNotActived
}
2019-02-19 10:19:28 +03:00
var err error
2015-09-13 03:58:51 +03:00
switch source . Type {
2016-11-07 23:58:22 +03:00
case LoginLDAP , LoginDLDAP :
2019-02-19 10:19:28 +03:00
user , err = LoginViaLDAP ( user , login , password , source , autoRegister )
2016-11-07 23:58:22 +03:00
case LoginSMTP :
2019-02-19 10:19:28 +03:00
user , err = LoginViaSMTP ( user , login , password , source . ID , source . Cfg . ( * SMTPConfig ) , autoRegister )
2016-11-07 23:58:22 +03:00
case LoginPAM :
2019-02-19 10:19:28 +03:00
user , err = LoginViaPAM ( user , login , password , source . ID , source . Cfg . ( * PAMConfig ) , autoRegister )
default :
return nil , ErrUnsupportedLoginType
}
if err != nil {
return nil , err
}
2019-04-02 08:44:33 +03:00
// WARN: DON'T check user.IsActive, that will be checked on reqSign so that
// user could be hint to resend confirm email.
if user . ProhibitLogin {
2019-02-19 10:19:28 +03:00
return nil , ErrUserProhibitLogin { user . ID , user . Name }
2015-09-13 03:58:51 +03:00
}
2019-02-19 10:19:28 +03:00
return user , nil
2015-09-13 03:58:51 +03:00
}
// UserSignIn validates user name and password.
2016-11-21 22:08:21 +03:00
func UserSignIn ( username , password string ) ( * User , error ) {
2016-08-31 11:22:41 +03:00
var user * User
if strings . Contains ( username , "@" ) {
2016-12-12 03:46:51 +03:00
user = & User { Email : strings . ToLower ( strings . TrimSpace ( username ) ) }
2017-02-25 17:57:06 +03:00
// check same email
cnt , err := x . Count ( user )
if err != nil {
return nil , err
}
if cnt > 1 {
return nil , ErrEmailAlreadyUsed {
Email : user . Email ,
}
}
2015-09-13 03:58:51 +03:00
} else {
2017-05-24 19:12:00 +03:00
trimmedUsername := strings . TrimSpace ( username )
if len ( trimmedUsername ) == 0 {
return nil , ErrUserNotExist { 0 , username , 0 }
}
user = & User { LowerName : strings . ToLower ( trimmedUsername ) }
2015-09-13 03:58:51 +03:00
}
2016-08-31 11:22:41 +03:00
hasUser , err := x . Get ( user )
2015-09-13 03:58:51 +03:00
if err != nil {
return nil , err
}
2016-08-31 11:22:41 +03:00
if hasUser {
switch user . LoginType {
2017-02-22 10:14:37 +03:00
case LoginNoType , LoginPlain , LoginOAuth2 :
2019-01-31 00:18:54 +03:00
if user . IsPasswordSet ( ) && user . ValidatePassword ( password ) {
2019-07-07 09:01:01 +03:00
// Update password hash if server password hash algorithm have changed
if user . PasswdHashAlgo != setting . PasswordHashAlgo {
user . HashPassword ( password )
if err := UpdateUserCols ( user , "passwd" , "passwd_hash_algo" ) ; err != nil {
return nil , err
}
}
2019-04-02 08:44:33 +03:00
// WARN: DON'T check user.IsActive, that will be checked on reqSign so that
// user could be hint to resend confirm email.
if user . ProhibitLogin {
2019-02-19 10:19:28 +03:00
return nil , ErrUserProhibitLogin { user . ID , user . Name }
}
2016-08-31 11:22:41 +03:00
return user , nil
2015-09-13 03:58:51 +03:00
}
2016-11-12 02:01:09 +03:00
return nil , ErrUserNotExist { user . ID , user . Name , 0 }
2015-09-13 03:58:51 +03:00
default :
var source LoginSource
2017-10-05 07:43:04 +03:00
hasSource , err := x . ID ( user . LoginSource ) . Get ( & source )
2015-09-13 03:58:51 +03:00
if err != nil {
return nil , err
} else if ! hasSource {
2016-08-31 11:22:41 +03:00
return nil , ErrLoginSourceNotExist { user . LoginSource }
2015-09-13 03:58:51 +03:00
}
2016-11-21 22:08:21 +03:00
return ExternalUserLogin ( user , user . LoginName , password , & source , false )
2015-09-13 03:58:51 +03:00
}
}
2017-02-22 10:14:37 +03:00
sources := make ( [ ] * LoginSource , 0 , 5 )
2017-08-01 08:47:31 +03:00
if err = x . Where ( "is_actived = ?" , true ) . Find ( & sources ) ; err != nil {
2015-09-13 03:58:51 +03:00
return nil , err
}
for _ , source := range sources {
2019-11-23 02:33:31 +03:00
if source . IsOAuth2 ( ) || source . IsSSPI ( ) {
// don't try to authenticate against OAuth2 and SSPI sources here
2017-02-22 10:14:37 +03:00
continue
}
2016-11-21 22:08:21 +03:00
authUser , err := ExternalUserLogin ( nil , username , password , source , true )
2015-09-13 03:58:51 +03:00
if err == nil {
2016-08-31 11:22:41 +03:00
return authUser , nil
2015-09-13 03:58:51 +03:00
}
2016-08-31 11:22:41 +03:00
log . Warn ( "Failed to login '%s' via '%s': %v" , username , source . Name , err )
2015-09-13 03:58:51 +03:00
}
2016-11-12 02:01:09 +03:00
return nil , ErrUserNotExist { user . ID , user . Name , 0 }
2017-05-04 08:54:56 +03:00
}