From ceaf11cbe329a9884f96fee64c0f88c9463e7dd1 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sat, 21 Aug 2021 16:18:18 +0100 Subject: [PATCH 1/3] Add setting to OAuth handlers to override local 2FA settings This PR adds a setting to OAuth and OpenID login sources to allow the source to override local 2FA requirements. Fix #13939 Signed-off-by: Andrew Thornton --- cmd/admin.go | 5 +++++ options/locale/locale_en-US.ini | 2 ++ routers/web/admin/auths.go | 1 + routers/web/user/auth.go | 20 ++++++++++++-------- services/auth/source/oauth2/source.go | 1 + services/forms/auth_form.go | 1 + templates/admin/auth/edit.tmpl | 7 +++++++ templates/admin/auth/source/oauth.tmpl | 7 +++++++ 8 files changed, 36 insertions(+), 8 deletions(-) diff --git a/cmd/admin.go b/cmd/admin.go index 94e78186c902f..ab0d3ce95056f 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -288,6 +288,10 @@ var ( Value: "", Usage: "Custom icon URL for OAuth2 login source", }, + cli.BoolFlag{ + Name: "override-local-2fa", + Usage: "Set to true to override local 2fa settings", + }, } microcmdAuthUpdateOauth = cli.Command{ @@ -616,6 +620,7 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source { OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"), CustomURLMapping: customURLMapping, IconURL: c.String("icon-url"), + OverrideLocalTwoFA: c.Bool("override-local-2fa"), } } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 4715afcd3e54d..ccee3104df50f 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2448,6 +2448,8 @@ auths.oauth2_tokenURL = Token URL auths.oauth2_authURL = Authorize URL auths.oauth2_profileURL = Profile URL auths.oauth2_emailURL = Email URL +auths.override_local_two_fa = Override local 2FA +auths.override_local_two_fa_helper = Leaving unset means local users with 2FA set will still have to pass 2FA to log on auths.oauth2_tenant = Tenant auths.enable_auto_register = Enable Auto Registration auths.sspi_auto_create_users = Automatically create users diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index 342318e04e9b5..04143e3610615 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -181,6 +181,7 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source { OpenIDConnectAutoDiscoveryURL: form.OpenIDConnectAutoDiscoveryURL, CustomURLMapping: customURLMapping, IconURL: form.Oauth2IconURL, + OverrideLocalTwoFA: form.OverrideLocalTwoFA, } } diff --git a/routers/web/user/auth.go b/routers/web/user/auth.go index 313a583004a57..9b8de29802940 100644 --- a/routers/web/user/auth.go +++ b/routers/web/user/auth.go @@ -574,7 +574,7 @@ func SignInOAuth(ctx *context.Context) { user, gothUser, err := oAuth2UserLoginCallback(loginSource, ctx.Req, ctx.Resp) if err == nil && user != nil { // we got the user without going through the whole OAuth2 authentication flow again - handleOAuth2SignIn(ctx, user, gothUser) + handleOAuth2SignIn(ctx, loginSource, user, gothUser) return } @@ -660,7 +660,7 @@ func SignInOAuthCallback(ctx *context.Context) { } } - handleOAuth2SignIn(ctx, u, gothUser) + handleOAuth2SignIn(ctx, loginSource, u, gothUser) } func getUserName(gothUser *goth.User) string { @@ -702,18 +702,22 @@ func updateAvatarIfNeed(url string, u *models.User) { } } -func handleOAuth2SignIn(ctx *context.Context, u *models.User, gothUser goth.User) { +func handleOAuth2SignIn(ctx *context.Context, source *models.LoginSource, u *models.User, gothUser goth.User) { updateAvatarIfNeed(gothUser.AvatarURL, u) - // If this user is enrolled in 2FA, we can't sign the user in just yet. - // Instead, redirect them to the 2FA authentication page. - _, err := models.GetTwoFactorByUID(u.ID) - if err != nil { - if !models.IsErrTwoFactorNotEnrolled(err) { + needs2FA := false + if !source.Cfg.(*oauth2.Source).OverrideLocalTwoFA { + _, err := models.GetTwoFactorByUID(u.ID) + if err != nil && !models.IsErrTwoFactorNotEnrolled(err) { ctx.ServerError("UserSignIn", err) return } + needs2FA = err == nil + } + // If this user is enrolled in 2FA and this source doesn't override it, + // we can't sign the user in just yet. Instead, redirect them to the 2FA authentication page. + if !needs2FA { if err := ctx.Session.Set("uid", u.ID); err != nil { log.Error("Error setting uid in session: %v", err) } diff --git a/services/auth/source/oauth2/source.go b/services/auth/source/oauth2/source.go index 40d8973b4b2ce..542a0b57aec3c 100644 --- a/services/auth/source/oauth2/source.go +++ b/services/auth/source/oauth2/source.go @@ -24,6 +24,7 @@ type Source struct { OpenIDConnectAutoDiscoveryURL string CustomURLMapping *CustomURLMapping IconURL string + OverrideLocalTwoFA bool // reference to the loginSource loginSource *models.LoginSource diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go index b45ea6ea124f7..288b87b45b208 100644 --- a/services/forms/auth_form.go +++ b/services/forms/auth_form.go @@ -66,6 +66,7 @@ type AuthenticationForm struct { Oauth2EmailURL string Oauth2IconURL string Oauth2Tenant string + OverrideLocalTwoFA bool SSPIAutoCreateUsers bool SSPIAutoActivateUsers bool SSPIStripDomainNames bool diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 109186a17821f..6e1a785908f55 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -255,6 +255,13 @@ +
+
+ + +

{{.i18n.Tr "admin.auths.override_local_two_fa_helper"}}

+
+
diff --git a/templates/admin/auth/source/oauth.tmpl b/templates/admin/auth/source/oauth.tmpl index b19fe3d42825d..07077471b235d 100644 --- a/templates/admin/auth/source/oauth.tmpl +++ b/templates/admin/auth/source/oauth.tmpl @@ -28,6 +28,13 @@
+
+
+ + +

{{.i18n.Tr "admin.auths.override_local_two_fa_helper"}}

+
+
From cb6b4c23abb6b4e77008b3df52c1337f6429d0ba Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sat, 4 Sep 2021 11:43:09 +0100 Subject: [PATCH 2/3] Change to Skip Local 2fa instead of Override Local 2fa Signed-off-by: Andrew Thornton --- cmd/admin.go | 6 +++--- options/locale/locale_en-US.ini | 4 ++-- routers/web/admin/auths.go | 2 +- routers/web/user/auth.go | 2 +- services/auth/source/oauth2/source.go | 2 +- services/forms/auth_form.go | 2 +- templates/admin/auth/edit.tmpl | 6 +++--- templates/admin/auth/source/oauth.tmpl | 6 +++--- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cmd/admin.go b/cmd/admin.go index ab0d3ce95056f..cfc297c474646 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -289,8 +289,8 @@ var ( Usage: "Custom icon URL for OAuth2 login source", }, cli.BoolFlag{ - Name: "override-local-2fa", - Usage: "Set to true to override local 2fa settings", + Name: "skip-local-2fa", + Usage: "Set to true to skip local 2fa for users authenticated by this source", }, } @@ -620,7 +620,7 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source { OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"), CustomURLMapping: customURLMapping, IconURL: c.String("icon-url"), - OverrideLocalTwoFA: c.Bool("override-local-2fa"), + SkipLocalTwoFA: c.Bool("skip-local-2fa"), } } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index ccee3104df50f..f1c3ba7e2a858 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2448,8 +2448,8 @@ auths.oauth2_tokenURL = Token URL auths.oauth2_authURL = Authorize URL auths.oauth2_profileURL = Profile URL auths.oauth2_emailURL = Email URL -auths.override_local_two_fa = Override local 2FA -auths.override_local_two_fa_helper = Leaving unset means local users with 2FA set will still have to pass 2FA to log on +auths.skip_local_two_fa = Skip local 2FA +auths.skip_local_two_fa_helper = Leaving unset means local users with 2FA set will still have to pass 2FA to log on auths.oauth2_tenant = Tenant auths.enable_auto_register = Enable Auto Registration auths.sspi_auto_create_users = Automatically create users diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index 04143e3610615..b2879d7c4f66c 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -181,7 +181,7 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source { OpenIDConnectAutoDiscoveryURL: form.OpenIDConnectAutoDiscoveryURL, CustomURLMapping: customURLMapping, IconURL: form.Oauth2IconURL, - OverrideLocalTwoFA: form.OverrideLocalTwoFA, + SkipLocalTwoFA: form.SkipLocalTwoFA, } } diff --git a/routers/web/user/auth.go b/routers/web/user/auth.go index 9b8de29802940..38e0d989b8d56 100644 --- a/routers/web/user/auth.go +++ b/routers/web/user/auth.go @@ -706,7 +706,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *models.LoginSource, u *mod updateAvatarIfNeed(gothUser.AvatarURL, u) needs2FA := false - if !source.Cfg.(*oauth2.Source).OverrideLocalTwoFA { + if !source.Cfg.(*oauth2.Source).SkipLocalTwoFA { _, err := models.GetTwoFactorByUID(u.ID) if err != nil && !models.IsErrTwoFactorNotEnrolled(err) { ctx.ServerError("UserSignIn", err) diff --git a/services/auth/source/oauth2/source.go b/services/auth/source/oauth2/source.go index 542a0b57aec3c..7b22383d7ed6d 100644 --- a/services/auth/source/oauth2/source.go +++ b/services/auth/source/oauth2/source.go @@ -24,7 +24,7 @@ type Source struct { OpenIDConnectAutoDiscoveryURL string CustomURLMapping *CustomURLMapping IconURL string - OverrideLocalTwoFA bool + SkipLocalTwoFA bool // reference to the loginSource loginSource *models.LoginSource diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go index 288b87b45b208..229728cf7daf3 100644 --- a/services/forms/auth_form.go +++ b/services/forms/auth_form.go @@ -66,7 +66,7 @@ type AuthenticationForm struct { Oauth2EmailURL string Oauth2IconURL string Oauth2Tenant string - OverrideLocalTwoFA bool + SkipLocalTwoFA bool SSPIAutoCreateUsers bool SSPIAutoActivateUsers bool SSPIStripDomainNames bool diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 6e1a785908f55..3e21710353e35 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -257,9 +257,9 @@
- - -

{{.i18n.Tr "admin.auths.override_local_two_fa_helper"}}

+ + +

{{.i18n.Tr "admin.auths.skip_local_two_fa_helper"}}

diff --git a/templates/admin/auth/source/oauth.tmpl b/templates/admin/auth/source/oauth.tmpl index 07077471b235d..6e91da14e24f0 100644 --- a/templates/admin/auth/source/oauth.tmpl +++ b/templates/admin/auth/source/oauth.tmpl @@ -30,9 +30,9 @@
- - -

{{.i18n.Tr "admin.auths.override_local_two_fa_helper"}}

+ + +

{{.i18n.Tr "admin.auths.skip_local_two_fa_helper"}}

From bbccfc7dcc7dff662bc5818aa35f5e83049172f5 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sat, 4 Sep 2021 15:25:22 +0100 Subject: [PATCH 3/3] Make LDAP be able to SkipLocalTwoFA This is slightly more involved than with OAuth2, but would be extensible to PAM and SMTP. Signed-off-by: Andrew Thornton --- cmd/admin_auth_ldap.go | 8 ++++++ modules/context/api.go | 4 +++ modules/context/auth.go | 3 ++ routers/web/admin/auths.go | 1 + routers/web/user/auth.go | 14 ++++++++-- routers/web/user/auth_openid.go | 2 +- routers/web/user/setting/account.go | 2 +- services/auth/basic.go | 6 +++- services/auth/interface.go | 5 ++++ services/auth/signin.go | 28 +++++++++---------- .../auth/source/ldap/assert_interface_test.go | 1 + services/auth/source/ldap/source.go | 1 + .../auth/source/ldap/source_authenticate.go | 5 ++++ .../auth/source/oauth2/source_authenticate.go | 3 ++ templates/admin/auth/edit.tmpl | 7 +++++ templates/admin/auth/source/ldap.tmpl | 13 +++++++++ 16 files changed, 84 insertions(+), 19 deletions(-) diff --git a/cmd/admin_auth_ldap.go b/cmd/admin_auth_ldap.go index 4314930a3e083..feeaf17661f7c 100644 --- a/cmd/admin_auth_ldap.go +++ b/cmd/admin_auth_ldap.go @@ -89,6 +89,10 @@ var ( Name: "public-ssh-key-attribute", Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.", }, + cli.BoolFlag{ + Name: "skip-local-2fa", + Usage: "Set to true to skip local 2fa for users authenticated by this source", + }, } ldapBindDnCLIFlags = append(commonLdapCLIFlags, @@ -245,6 +249,10 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error { if c.IsSet("allow-deactivate-all") { config.AllowDeactivateAll = c.Bool("allow-deactivate-all") } + if c.IsSet("skip-local-2fa") { + config.SkipLocalTwoFA = c.Bool("skip-local-2fa") + } + return nil } diff --git a/modules/context/api.go b/modules/context/api.go index 47ea8acfe05f3..e80e63cd96233 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -214,6 +214,10 @@ func (ctx *APIContext) RequireCSRF() { // CheckForOTP validates OTP func (ctx *APIContext) CheckForOTP() { + if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) { + return // Skip 2FA + } + otpHeader := ctx.Req.Header.Get("X-Gitea-OTP") twofa, err := models.GetTwoFactorByUID(ctx.Context.User.ID) if err != nil { diff --git a/modules/context/auth.go b/modules/context/auth.go index ed220d542032b..0a62b2741e4a7 100644 --- a/modules/context/auth.go +++ b/modules/context/auth.go @@ -151,6 +151,9 @@ func ToggleAPI(options *ToggleOptions) func(ctx *APIContext) { return } if ctx.IsSigned && ctx.IsBasicAuth { + if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) { + return // Skip 2FA + } twofa, err := models.GetTwoFactorByUID(ctx.User.ID) if err != nil { if models.IsErrTwoFactorNotEnrolled(err) { diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index b2879d7c4f66c..2937190a1f501 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -145,6 +145,7 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source { RestrictedFilter: form.RestrictedFilter, AllowDeactivateAll: form.AllowDeactivateAll, Enabled: true, + SkipLocalTwoFA: form.SkipLocalTwoFA, } } diff --git a/routers/web/user/auth.go b/routers/web/user/auth.go index 38e0d989b8d56..a5c0a14d17f3e 100644 --- a/routers/web/user/auth.go +++ b/routers/web/user/auth.go @@ -175,7 +175,7 @@ func SignInPost(ctx *context.Context) { } form := web.GetForm(ctx).(*forms.SignInForm) - u, err := auth.UserSignIn(form.UserName, form.Password) + u, source, err := auth.UserSignIn(form.UserName, form.Password) if err != nil { if models.IsErrUserNotExist(err) { ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form) @@ -201,6 +201,15 @@ func SignInPost(ctx *context.Context) { } return } + + // Now handle 2FA: + + // First of all if the source can skip local two fa we're done + if skipper, ok := source.Cfg.(auth.LocalTwoFASkipper); ok && skipper.IsSkipLocalTwoFA() { + handleSignIn(ctx, u, form.Remember) + return + } + // If this user is enrolled in 2FA, we can't sign the user in just yet. // Instead, redirect them to the 2FA authentication page. _, err = models.GetTwoFactorByUID(u.ID) @@ -905,7 +914,7 @@ func LinkAccountPostSignIn(ctx *context.Context) { return } - u, err := auth.UserSignIn(signInForm.UserName, signInForm.Password) + u, _, err := auth.UserSignIn(signInForm.UserName, signInForm.Password) if err != nil { if models.IsErrUserNotExist(err) { ctx.Data["user_exists"] = true @@ -924,6 +933,7 @@ func linkAccount(ctx *context.Context, u *models.User, gothUser goth.User, remem // If this user is enrolled in 2FA, we can't sign the user in just yet. // Instead, redirect them to the 2FA authentication page. + // We deliberately ignore the skip local 2fa setting here because we are linking to a previous user here _, err := models.GetTwoFactorByUID(u.ID) if err != nil { if !models.IsErrTwoFactorNotEnrolled(err) { diff --git a/routers/web/user/auth_openid.go b/routers/web/user/auth_openid.go index fc419a7f6ea63..e6ad6fef4c413 100644 --- a/routers/web/user/auth_openid.go +++ b/routers/web/user/auth_openid.go @@ -291,7 +291,7 @@ func ConnectOpenIDPost(ctx *context.Context) { ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp ctx.Data["OpenID"] = oid - u, err := auth.UserSignIn(form.UserName, form.Password) + u, _, err := auth.UserSignIn(form.UserName, form.Password) if err != nil { if models.IsErrUserNotExist(err) { ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplConnectOID, &form) diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index 6201078954412..249793578a1a9 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -229,7 +229,7 @@ func DeleteAccount(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsAccount"] = true - if _, err := auth.UserSignIn(ctx.User.Name, ctx.FormString("password")); err != nil { + if _, _, err := auth.UserSignIn(ctx.User.Name, ctx.FormString("password")); err != nil { if models.IsErrUserNotExist(err) { loadAccountData(ctx) diff --git a/services/auth/basic.go b/services/auth/basic.go index 244f63d2f733a..0c400b6b5329a 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 51c7043370bb1..a198fbe5b8479 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 2c4bf9b35b7e3..0ac2634c80a32 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 4cf3eafe76358..a0425d2f763c1 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 79f118f784654..d1228d41aeb11 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 ecc95fbd56eff..46478e60296ed 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 2e39f245dff07..be2ff05356cd4 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 diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 3e21710353e35..9ff80663846b0 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -147,6 +147,13 @@
{{end}} +
+
+ + +

{{.i18n.Tr "admin.auths.skip_local_two_fa_helper"}}

+
+
diff --git a/templates/admin/auth/source/ldap.tmpl b/templates/admin/auth/source/ldap.tmpl index 295e001cf4a36..b8018f5b548cc 100644 --- a/templates/admin/auth/source/ldap.tmpl +++ b/templates/admin/auth/source/ldap.tmpl @@ -111,4 +111,17 @@
+
+
+ + +

{{.i18n.Tr "admin.auths.skip_local_two_fa_helper"}}

+
+
+
+
+ + +
+