mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	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>
This commit is contained in:
		@@ -89,6 +89,10 @@ var (
 | 
				
			|||||||
			Name:  "public-ssh-key-attribute",
 | 
								Name:  "public-ssh-key-attribute",
 | 
				
			||||||
			Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.",
 | 
								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,
 | 
						ldapBindDnCLIFlags = append(commonLdapCLIFlags,
 | 
				
			||||||
@@ -245,6 +249,10 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
 | 
				
			|||||||
	if c.IsSet("allow-deactivate-all") {
 | 
						if c.IsSet("allow-deactivate-all") {
 | 
				
			||||||
		config.AllowDeactivateAll = c.Bool("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
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -214,6 +214,10 @@ func (ctx *APIContext) RequireCSRF() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// CheckForOTP validates OTP
 | 
					// CheckForOTP validates OTP
 | 
				
			||||||
func (ctx *APIContext) CheckForOTP() {
 | 
					func (ctx *APIContext) CheckForOTP() {
 | 
				
			||||||
 | 
						if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) {
 | 
				
			||||||
 | 
							return // Skip 2FA
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	otpHeader := ctx.Req.Header.Get("X-Gitea-OTP")
 | 
						otpHeader := ctx.Req.Header.Get("X-Gitea-OTP")
 | 
				
			||||||
	twofa, err := models.GetTwoFactorByUID(ctx.Context.User.ID)
 | 
						twofa, err := models.GetTwoFactorByUID(ctx.Context.User.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -151,6 +151,9 @@ func ToggleAPI(options *ToggleOptions) func(ctx *APIContext) {
 | 
				
			|||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if ctx.IsSigned && ctx.IsBasicAuth {
 | 
								if ctx.IsSigned && ctx.IsBasicAuth {
 | 
				
			||||||
 | 
									if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) {
 | 
				
			||||||
 | 
										return // Skip 2FA
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				twofa, err := models.GetTwoFactorByUID(ctx.User.ID)
 | 
									twofa, err := models.GetTwoFactorByUID(ctx.User.ID)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					if models.IsErrTwoFactorNotEnrolled(err) {
 | 
										if models.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -145,6 +145,7 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source {
 | 
				
			|||||||
		RestrictedFilter:      form.RestrictedFilter,
 | 
							RestrictedFilter:      form.RestrictedFilter,
 | 
				
			||||||
		AllowDeactivateAll:    form.AllowDeactivateAll,
 | 
							AllowDeactivateAll:    form.AllowDeactivateAll,
 | 
				
			||||||
		Enabled:               true,
 | 
							Enabled:               true,
 | 
				
			||||||
 | 
							SkipLocalTwoFA:        form.SkipLocalTwoFA,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -175,7 +175,7 @@ func SignInPost(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	form := web.GetForm(ctx).(*forms.SignInForm)
 | 
						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 err != nil {
 | 
				
			||||||
		if models.IsErrUserNotExist(err) {
 | 
							if models.IsErrUserNotExist(err) {
 | 
				
			||||||
			ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form)
 | 
								ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form)
 | 
				
			||||||
@@ -201,6 +201,15 @@ func SignInPost(ctx *context.Context) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							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.
 | 
						// If this user is enrolled in 2FA, we can't sign the user in just yet.
 | 
				
			||||||
	// Instead, redirect them to the 2FA authentication page.
 | 
						// Instead, redirect them to the 2FA authentication page.
 | 
				
			||||||
	_, err = models.GetTwoFactorByUID(u.ID)
 | 
						_, err = models.GetTwoFactorByUID(u.ID)
 | 
				
			||||||
@@ -905,7 +914,7 @@ func LinkAccountPostSignIn(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	u, err := auth.UserSignIn(signInForm.UserName, signInForm.Password)
 | 
						u, _, err := auth.UserSignIn(signInForm.UserName, signInForm.Password)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if models.IsErrUserNotExist(err) {
 | 
							if models.IsErrUserNotExist(err) {
 | 
				
			||||||
			ctx.Data["user_exists"] = true
 | 
								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.
 | 
						// If this user is enrolled in 2FA, we can't sign the user in just yet.
 | 
				
			||||||
	// Instead, redirect them to the 2FA authentication page.
 | 
						// 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)
 | 
						_, err := models.GetTwoFactorByUID(u.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if !models.IsErrTwoFactorNotEnrolled(err) {
 | 
							if !models.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -291,7 +291,7 @@ func ConnectOpenIDPost(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
 | 
						ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
 | 
				
			||||||
	ctx.Data["OpenID"] = oid
 | 
						ctx.Data["OpenID"] = oid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	u, err := auth.UserSignIn(form.UserName, form.Password)
 | 
						u, _, err := auth.UserSignIn(form.UserName, form.Password)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if models.IsErrUserNotExist(err) {
 | 
							if models.IsErrUserNotExist(err) {
 | 
				
			||||||
			ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplConnectOID, &form)
 | 
								ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplConnectOID, &form)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -229,7 +229,7 @@ func DeleteAccount(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["Title"] = ctx.Tr("settings")
 | 
						ctx.Data["Title"] = ctx.Tr("settings")
 | 
				
			||||||
	ctx.Data["PageIsSettingsAccount"] = true
 | 
						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) {
 | 
							if models.IsErrUserNotExist(err) {
 | 
				
			||||||
			loadAccountData(ctx)
 | 
								loadAccountData(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
						log.Trace("Basic Authorization: Attempting SignIn for %s", uname)
 | 
				
			||||||
	u, err := UserSignIn(uname, passwd)
 | 
						u, source, err := UserSignIn(uname, passwd)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if !models.IsErrUserNotExist(err) {
 | 
							if !models.IsErrUserNotExist(err) {
 | 
				
			||||||
			log.Error("UserSignIn: %v", err)
 | 
								log.Error("UserSignIn: %v", err)
 | 
				
			||||||
@@ -115,6 +115,10 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
 | 
				
			|||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if skipper, ok := source.Cfg.(LocalTwoFASkipper); ok && skipper.IsSkipLocalTwoFA() {
 | 
				
			||||||
 | 
							store.GetData()["SkipLocalTwoFA"] = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Trace("Basic Authorization: Logged in user %-v", u)
 | 
						log.Trace("Basic Authorization: Logged in user %-v", u)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return u
 | 
						return u
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,6 +54,11 @@ type PasswordAuthenticator interface {
 | 
				
			|||||||
	Authenticate(user *models.User, login, password string) (*models.User, error)
 | 
						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
 | 
					// SynchronizableSource represents a source that can synchronize users
 | 
				
			||||||
type SynchronizableSource interface {
 | 
					type SynchronizableSource interface {
 | 
				
			||||||
	Sync(ctx context.Context, updateExisting bool) error
 | 
						Sync(ctx context.Context, updateExisting bool) error
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,24 +20,24 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UserSignIn validates user name and password.
 | 
					// 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
 | 
						var user *models.User
 | 
				
			||||||
	if strings.Contains(username, "@") {
 | 
						if strings.Contains(username, "@") {
 | 
				
			||||||
		user = &models.User{Email: strings.ToLower(strings.TrimSpace(username))}
 | 
							user = &models.User{Email: strings.ToLower(strings.TrimSpace(username))}
 | 
				
			||||||
		// check same email
 | 
							// check same email
 | 
				
			||||||
		cnt, err := models.Count(user)
 | 
							cnt, err := models.Count(user)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if cnt > 1 {
 | 
							if cnt > 1 {
 | 
				
			||||||
			return nil, models.ErrEmailAlreadyUsed{
 | 
								return nil, nil, models.ErrEmailAlreadyUsed{
 | 
				
			||||||
				Email: user.Email,
 | 
									Email: user.Email,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		trimmedUsername := strings.TrimSpace(username)
 | 
							trimmedUsername := strings.TrimSpace(username)
 | 
				
			||||||
		if len(trimmedUsername) == 0 {
 | 
							if len(trimmedUsername) == 0 {
 | 
				
			||||||
			return nil, models.ErrUserNotExist{Name: username}
 | 
								return nil, nil, models.ErrUserNotExist{Name: username}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		user = &models.User{LowerName: strings.ToLower(trimmedUsername)}
 | 
							user = &models.User{LowerName: strings.ToLower(trimmedUsername)}
 | 
				
			||||||
@@ -45,41 +45,41 @@ func UserSignIn(username, password string) (*models.User, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	hasUser, err := models.GetUser(user)
 | 
						hasUser, err := models.GetUser(user)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if hasUser {
 | 
						if hasUser {
 | 
				
			||||||
		source, err := models.GetLoginSourceByID(user.LoginSource)
 | 
							source, err := models.GetLoginSourceByID(user.LoginSource)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if !source.IsActive {
 | 
							if !source.IsActive {
 | 
				
			||||||
			return nil, models.ErrLoginSourceNotActived
 | 
								return nil, nil, models.ErrLoginSourceNotActived
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		authenticator, ok := source.Cfg.(PasswordAuthenticator)
 | 
							authenticator, ok := source.Cfg.(PasswordAuthenticator)
 | 
				
			||||||
		if !ok {
 | 
							if !ok {
 | 
				
			||||||
			return nil, models.ErrUnsupportedLoginType
 | 
								return nil, nil, models.ErrUnsupportedLoginType
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		user, err := authenticator.Authenticate(user, username, password)
 | 
							user, err := authenticator.Authenticate(user, username, password)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// WARN: DON'T check user.IsActive, that will be checked on reqSign so that
 | 
							// WARN: DON'T check user.IsActive, that will be checked on reqSign so that
 | 
				
			||||||
		// user could be hint to resend confirm email.
 | 
							// user could be hint to resend confirm email.
 | 
				
			||||||
		if user.ProhibitLogin {
 | 
							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()
 | 
						sources, err := models.AllActiveLoginSources()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, source := range sources {
 | 
						for _, source := range sources {
 | 
				
			||||||
@@ -97,7 +97,7 @@ func UserSignIn(username, password string) (*models.User, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			if !authUser.ProhibitLogin {
 | 
								if !authUser.ProhibitLogin {
 | 
				
			||||||
				return authUser, nil
 | 
									return authUser, source, nil
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			err = models.ErrUserProhibitLogin{UID: authUser.ID, Name: authUser.Name}
 | 
								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}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@ import (
 | 
				
			|||||||
type sourceInterface interface {
 | 
					type sourceInterface interface {
 | 
				
			||||||
	auth.PasswordAuthenticator
 | 
						auth.PasswordAuthenticator
 | 
				
			||||||
	auth.SynchronizableSource
 | 
						auth.SynchronizableSource
 | 
				
			||||||
 | 
						auth.LocalTwoFASkipper
 | 
				
			||||||
	models.SSHKeyProvider
 | 
						models.SSHKeyProvider
 | 
				
			||||||
	models.LoginConfig
 | 
						models.LoginConfig
 | 
				
			||||||
	models.SkipVerifiable
 | 
						models.SkipVerifiable
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,6 +52,7 @@ type Source struct {
 | 
				
			|||||||
	GroupFilter           string // Group Name Filter
 | 
						GroupFilter           string // Group Name Filter
 | 
				
			||||||
	GroupMemberUID        string // Group Attribute containing array of UserUID
 | 
						GroupMemberUID        string // Group Attribute containing array of UserUID
 | 
				
			||||||
	UserUID               string // User Attribute listed in Group
 | 
						UserUID               string // User Attribute listed in Group
 | 
				
			||||||
 | 
						SkipLocalTwoFA        bool   // Skip Local 2fa for users authenticated with this source
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// reference to the loginSource
 | 
						// reference to the loginSource
 | 
				
			||||||
	loginSource *models.LoginSource
 | 
						loginSource *models.LoginSource
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -97,3 +97,8 @@ func (source *Source) Authenticate(user *models.User, login, password string) (*
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return user, err
 | 
						return user, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsSkipLocalTwoFA returns if this source should skip local 2fa for password authentication
 | 
				
			||||||
 | 
					func (source *Source) IsSkipLocalTwoFA() bool {
 | 
				
			||||||
 | 
						return source.SkipLocalTwoFA
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,3 +13,6 @@ import (
 | 
				
			|||||||
func (source *Source) Authenticate(user *models.User, login, password string) (*models.User, error) {
 | 
					func (source *Source) Authenticate(user *models.User, login, password string) (*models.User, error) {
 | 
				
			||||||
	return db.Authenticate(user, login, password)
 | 
						return db.Authenticate(user, login, password)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NB: Oauth2 does not implement LocalTwoFASkipper for password authentication
 | 
				
			||||||
 | 
					// as its password authentication drops to db authentication
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -147,6 +147,13 @@
 | 
				
			|||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
 | 
										<div class="optional field">
 | 
				
			||||||
 | 
											<div class="ui checkbox">
 | 
				
			||||||
 | 
												<label for="skip_local_two_fa"><strong>{{.i18n.Tr "admin.auths.skip_local_two_fa"}}</strong></label>
 | 
				
			||||||
 | 
												<input id="skip_local_two_fa" name="skip_local_two_fa" type="checkbox" {{if $cfg.SkipLocalTwoFA}}checked{{end}}>
 | 
				
			||||||
 | 
												<p class="help">{{.i18n.Tr "admin.auths.skip_local_two_fa_helper"}}</p>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
					<div class="inline field">
 | 
										<div class="inline field">
 | 
				
			||||||
						<div class="ui checkbox">
 | 
											<div class="ui checkbox">
 | 
				
			||||||
							<label for="allow_deactivate_all"><strong>{{.i18n.Tr "admin.auths.allow_deactivate_all"}}</strong></label>
 | 
												<label for="allow_deactivate_all"><strong>{{.i18n.Tr "admin.auths.allow_deactivate_all"}}</strong></label>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -111,4 +111,17 @@
 | 
				
			|||||||
		<label for="search_page_size">{{.i18n.Tr "admin.auths.search_page_size"}}</label>
 | 
							<label for="search_page_size">{{.i18n.Tr "admin.auths.search_page_size"}}</label>
 | 
				
			||||||
		<input id="search_page_size" name="search_page_size" value="{{.search_page_size}}">
 | 
							<input id="search_page_size" name="search_page_size" value="{{.search_page_size}}">
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
 | 
						<div class="optional field">
 | 
				
			||||||
 | 
							<div class="ui checkbox">
 | 
				
			||||||
 | 
								<label for="skip_local_two_fa"><strong>{{.i18n.Tr "admin.auths.skip_local_two_fa"}}</strong></label>
 | 
				
			||||||
 | 
								<input id="skip_local_two_fa" name="skip_local_two_fa" type="checkbox" {{if .skip_local_two_fa}}checked{{end}}>
 | 
				
			||||||
 | 
								<p class="help">{{.i18n.Tr "admin.auths.skip_local_two_fa_helper"}}</p>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						<div class="inline field">
 | 
				
			||||||
 | 
							<div class="ui checkbox">
 | 
				
			||||||
 | 
								<label for="allow_deactivate_all"><strong>{{.i18n.Tr "admin.auths.allow_deactivate_all"}}</strong></label>
 | 
				
			||||||
 | 
								<input id="allow_deactivate_all" name="allow_deactivate_all" type="checkbox" {{if .allow_deactivate_all}}checked{{end}}>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user