Auth: Make built-in login configurable (#46978)
This commit is contained in:
@@ -409,6 +409,9 @@ hidden_users =
|
||||
# Login cookie name
|
||||
login_cookie_name = grafana_session
|
||||
|
||||
# Disable usage of Grafana build-in login solution.
|
||||
disable_login = false
|
||||
|
||||
# The maximum lifetime (duration) an authenticated user can be inactive before being required to login at next visit. Default is 7 days (7d). This setting should be expressed as a duration, e.g. 5m (minutes), 6h (hours), 10d (days), 2w (weeks), 1M (month). The lifetime resets at each successful token rotation (token_rotation_interval_minutes).
|
||||
login_maximum_inactive_lifetime_duration =
|
||||
|
||||
|
||||
@@ -410,6 +410,9 @@
|
||||
# Login cookie name
|
||||
;login_cookie_name = grafana_session
|
||||
|
||||
# Disable usage of Grafana build-in login solution.
|
||||
;disable_login = false
|
||||
|
||||
# The maximum lifetime (duration) an authenticated user can be inactive before being required to login at next visit. Default is 7 days (7d). This setting should be expressed as a duration, e.g. 5m (minutes), 6h (hours), 10d (days), 2w (weeks), 1M (month). The lifetime resets at each successful token rotation.
|
||||
;login_maximum_inactive_lifetime_duration =
|
||||
|
||||
|
||||
@@ -220,6 +220,11 @@ func (hs *HTTPServer) LoginPost(c *models.ReqContext) response.Response {
|
||||
return resp
|
||||
}
|
||||
|
||||
if errors.Is(err, login.ErrNoAuthProvider) {
|
||||
resp = response.Error(http.StatusInternalServerError, "No authorization providers enabled", err)
|
||||
return resp
|
||||
}
|
||||
|
||||
// Do not expose disabled status,
|
||||
// just show incorrect user credentials error (see #17947)
|
||||
if errors.Is(err, login.ErrUserDisabled) {
|
||||
|
||||
+14
-3
@@ -24,6 +24,7 @@ var (
|
||||
ErrAbsoluteRedirectTo = errors.New("absolute URLs are not allowed for redirect_to cookie value")
|
||||
ErrInvalidRedirectTo = errors.New("invalid redirect_to cookie value")
|
||||
ErrForbiddenRedirectTo = errors.New("forbidden redirect_to cookie value")
|
||||
ErrNoAuthProvider = errors.New("enable at least one login provider")
|
||||
)
|
||||
|
||||
var loginLogger = log.New("login")
|
||||
@@ -57,9 +58,15 @@ func (a *AuthenticatorService) AuthenticateUser(ctx context.Context, query *mode
|
||||
return err
|
||||
}
|
||||
|
||||
err := loginUsingGrafanaDB(ctx, query, a.userService)
|
||||
if err == nil || (!errors.Is(err, user.ErrUserNotFound) && !errors.Is(err, ErrInvalidCredentials) &&
|
||||
!errors.Is(err, ErrUserDisabled)) {
|
||||
isGrafanaLoginEnabled := !query.Cfg.DisableLogin
|
||||
var err error
|
||||
|
||||
if isGrafanaLoginEnabled {
|
||||
err = loginUsingGrafanaDB(ctx, query, a.userService)
|
||||
}
|
||||
|
||||
if isGrafanaLoginEnabled && (err == nil || (!errors.Is(err, user.ErrUserNotFound) && !errors.Is(err, ErrInvalidCredentials) &&
|
||||
!errors.Is(err, ErrUserDisabled))) {
|
||||
query.AuthModule = "grafana"
|
||||
return err
|
||||
}
|
||||
@@ -84,6 +91,10 @@ func (a *AuthenticatorService) AuthenticateUser(ctx context.Context, query *mode
|
||||
return ErrInvalidCredentials
|
||||
}
|
||||
|
||||
if !isGrafanaLoginEnabled && !ldapEnabled {
|
||||
return ErrNoAuthProvider
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+18
-1
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||
"github.com/grafana/grafana/pkg/services/loginattempt"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -34,6 +35,21 @@ func TestAuthenticateUser(t *testing.T) {
|
||||
assert.Empty(t, sc.loginUserQuery.AuthModule)
|
||||
})
|
||||
|
||||
authScenario(t, "When user authenticates with no auth provider enabled", func(sc *authScenarioContext) {
|
||||
mockLoginAttemptValidation(nil, sc)
|
||||
sc.loginUserQuery.Cfg.DisableLogin = true
|
||||
|
||||
a := AuthenticatorService{loginAttemptService: nil, loginService: &logintest.LoginServiceFake{}}
|
||||
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||
|
||||
require.EqualError(t, err, ErrNoAuthProvider.Error())
|
||||
assert.True(t, sc.loginAttemptValidationWasCalled)
|
||||
assert.False(t, sc.grafanaLoginWasCalled)
|
||||
assert.False(t, sc.ldapLoginWasCalled)
|
||||
assert.False(t, sc.saveInvalidLoginAttemptWasCalled)
|
||||
assert.Equal(t, "", sc.loginUserQuery.AuthModule)
|
||||
})
|
||||
|
||||
authScenario(t, "When a user authenticates having too many login attempts", func(sc *authScenarioContext) {
|
||||
mockLoginAttemptValidation(ErrTooManyLoginAttempts, sc)
|
||||
mockLoginUsingGrafanaDB(nil, sc)
|
||||
@@ -218,12 +234,13 @@ func authScenario(t *testing.T, desc string, fn authScenarioFunc) {
|
||||
origLoginUsingLDAP := loginUsingLDAP
|
||||
origValidateLoginAttempts := validateLoginAttempts
|
||||
origSaveInvalidLoginAttempt := saveInvalidLoginAttempt
|
||||
|
||||
cfg := setting.Cfg{DisableLogin: false}
|
||||
sc := &authScenarioContext{
|
||||
loginUserQuery: &models.LoginUserQuery{
|
||||
Username: "user",
|
||||
Password: "pwd",
|
||||
IpAddress: "192.168.1.1:56433",
|
||||
Cfg: &cfg,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -301,6 +301,7 @@ type Cfg struct {
|
||||
BasicAuthEnabled bool
|
||||
AdminUser string
|
||||
AdminPassword string
|
||||
DisableLogin bool
|
||||
AdminEmail string
|
||||
DisableSyncLock bool
|
||||
|
||||
@@ -1337,6 +1338,8 @@ func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) {
|
||||
SignoutRedirectUrl = valueAsString(auth, "signout_redirect_url", "")
|
||||
cfg.OAuthSkipOrgRoleUpdateSync = auth.Key("oauth_skip_org_role_update_sync").MustBool(false)
|
||||
|
||||
cfg.DisableLogin = auth.Key("disable_login").MustBool(false)
|
||||
|
||||
// SigV4
|
||||
SigV4AuthEnabled = auth.Key("sigv4_auth_enabled").MustBool(false)
|
||||
cfg.SigV4AuthEnabled = SigV4AuthEnabled
|
||||
|
||||
Reference in New Issue
Block a user