diff options
author | zeripath <art27@cantab.net> | 2021-09-17 12:43:47 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-17 12:43:47 +0100 |
commit | 27b351aba564804f65e5574919a88d6194c75256 (patch) | |
tree | fa4857e05e344693e629aa14b05b7f8ffba42cfc /services | |
parent | f96d0d3d5b2acb3545c3a2ced7972879a9750c9d (diff) | |
download | gitea-27b351aba564804f65e5574919a88d6194c75256.tar.gz gitea-27b351aba564804f65e5574919a88d6194c75256.zip |
Make LDAP be able to skip local 2FA (#16954)
This PR extends #16594 to allow LDAP to be able to be set to skip local 2FA too. The technique used here would be extensible to PAM and SMTP sources.
Signed-off-by: Andrew Thornton <art27@cantab.net>
Diffstat (limited to 'services')
-rw-r--r-- | services/auth/basic.go | 6 | ||||
-rw-r--r-- | services/auth/interface.go | 5 | ||||
-rw-r--r-- | services/auth/signin.go | 28 | ||||
-rw-r--r-- | services/auth/source/ldap/assert_interface_test.go | 1 | ||||
-rw-r--r-- | services/auth/source/ldap/source.go | 1 | ||||
-rw-r--r-- | services/auth/source/ldap/source_authenticate.go | 5 | ||||
-rw-r--r-- | services/auth/source/oauth2/source_authenticate.go | 3 |
7 files changed, 34 insertions, 15 deletions
diff --git a/services/auth/basic.go b/services/auth/basic.go index 244f63d2f7..0c400b6b53 100644 --- a/services/auth/basic.go +++ b/services/auth/basic.go @@ -107,7 +107,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore } log.Trace("Basic Authorization: Attempting SignIn for %s", uname) - u, err := UserSignIn(uname, passwd) + u, source, err := UserSignIn(uname, passwd) if err != nil { if !models.IsErrUserNotExist(err) { log.Error("UserSignIn: %v", err) @@ -115,6 +115,10 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore return nil } + if skipper, ok := source.Cfg.(LocalTwoFASkipper); ok && skipper.IsSkipLocalTwoFA() { + store.GetData()["SkipLocalTwoFA"] = true + } + log.Trace("Basic Authorization: Logged in user %-v", u) return u diff --git a/services/auth/interface.go b/services/auth/interface.go index 51c7043370..a198fbe5b8 100644 --- a/services/auth/interface.go +++ b/services/auth/interface.go @@ -54,6 +54,11 @@ type PasswordAuthenticator interface { Authenticate(user *models.User, login, password string) (*models.User, error) } +// LocalTwoFASkipper represents a source of authentication that can skip local 2fa +type LocalTwoFASkipper interface { + IsSkipLocalTwoFA() bool +} + // SynchronizableSource represents a source that can synchronize users type SynchronizableSource interface { Sync(ctx context.Context, updateExisting bool) error diff --git a/services/auth/signin.go b/services/auth/signin.go index 2c4bf9b35b..0ac2634c80 100644 --- a/services/auth/signin.go +++ b/services/auth/signin.go @@ -20,24 +20,24 @@ import ( ) // UserSignIn validates user name and password. -func UserSignIn(username, password string) (*models.User, error) { +func UserSignIn(username, password string) (*models.User, *models.LoginSource, error) { var user *models.User if strings.Contains(username, "@") { user = &models.User{Email: strings.ToLower(strings.TrimSpace(username))} // check same email cnt, err := models.Count(user) if err != nil { - return nil, err + return nil, nil, err } if cnt > 1 { - return nil, models.ErrEmailAlreadyUsed{ + return nil, nil, models.ErrEmailAlreadyUsed{ Email: user.Email, } } } else { trimmedUsername := strings.TrimSpace(username) if len(trimmedUsername) == 0 { - return nil, models.ErrUserNotExist{Name: username} + return nil, nil, models.ErrUserNotExist{Name: username} } user = &models.User{LowerName: strings.ToLower(trimmedUsername)} @@ -45,41 +45,41 @@ func UserSignIn(username, password string) (*models.User, error) { hasUser, err := models.GetUser(user) if err != nil { - return nil, err + return nil, nil, err } if hasUser { source, err := models.GetLoginSourceByID(user.LoginSource) if err != nil { - return nil, err + return nil, nil, err } if !source.IsActive { - return nil, models.ErrLoginSourceNotActived + return nil, nil, models.ErrLoginSourceNotActived } authenticator, ok := source.Cfg.(PasswordAuthenticator) if !ok { - return nil, models.ErrUnsupportedLoginType + return nil, nil, models.ErrUnsupportedLoginType } user, err := authenticator.Authenticate(user, username, password) if err != nil { - return nil, err + return nil, nil, err } // 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 { - return nil, models.ErrUserProhibitLogin{UID: user.ID, Name: user.Name} + return nil, nil, models.ErrUserProhibitLogin{UID: user.ID, Name: user.Name} } - return user, nil + return user, source, nil } sources, err := models.AllActiveLoginSources() if err != nil { - return nil, err + return nil, nil, err } for _, source := range sources { @@ -97,7 +97,7 @@ func UserSignIn(username, password string) (*models.User, error) { if err == nil { if !authUser.ProhibitLogin { - return authUser, nil + return authUser, source, nil } err = models.ErrUserProhibitLogin{UID: authUser.ID, Name: authUser.Name} } @@ -109,5 +109,5 @@ func UserSignIn(username, password string) (*models.User, error) { } } - return nil, models.ErrUserNotExist{Name: username} + return nil, nil, models.ErrUserNotExist{Name: username} } diff --git a/services/auth/source/ldap/assert_interface_test.go b/services/auth/source/ldap/assert_interface_test.go index 4cf3eafe76..a0425d2f76 100644 --- a/services/auth/source/ldap/assert_interface_test.go +++ b/services/auth/source/ldap/assert_interface_test.go @@ -16,6 +16,7 @@ import ( type sourceInterface interface { auth.PasswordAuthenticator auth.SynchronizableSource + auth.LocalTwoFASkipper models.SSHKeyProvider models.LoginConfig models.SkipVerifiable diff --git a/services/auth/source/ldap/source.go b/services/auth/source/ldap/source.go index 79f118f784..d1228d41ae 100644 --- a/services/auth/source/ldap/source.go +++ b/services/auth/source/ldap/source.go @@ -52,6 +52,7 @@ type Source struct { GroupFilter string // Group Name Filter GroupMemberUID string // Group Attribute containing array of UserUID UserUID string // User Attribute listed in Group + SkipLocalTwoFA bool // Skip Local 2fa for users authenticated with this source // reference to the loginSource loginSource *models.LoginSource diff --git a/services/auth/source/ldap/source_authenticate.go b/services/auth/source/ldap/source_authenticate.go index ecc95fbd56..46478e6029 100644 --- a/services/auth/source/ldap/source_authenticate.go +++ b/services/auth/source/ldap/source_authenticate.go @@ -97,3 +97,8 @@ func (source *Source) Authenticate(user *models.User, login, password string) (* return user, err } + +// IsSkipLocalTwoFA returns if this source should skip local 2fa for password authentication +func (source *Source) IsSkipLocalTwoFA() bool { + return source.SkipLocalTwoFA +} diff --git a/services/auth/source/oauth2/source_authenticate.go b/services/auth/source/oauth2/source_authenticate.go index 2e39f245df..be2ff05356 100644 --- a/services/auth/source/oauth2/source_authenticate.go +++ b/services/auth/source/oauth2/source_authenticate.go @@ -13,3 +13,6 @@ import ( func (source *Source) Authenticate(user *models.User, login, password string) (*models.User, error) { return db.Authenticate(user, login, password) } + +// NB: Oauth2 does not implement LocalTwoFASkipper for password authentication +// as its password authentication drops to db authentication |