mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Auth: Remove unused Authenticator service (#73143)
Auth: remove unused Authenticator service
This commit is contained in:
parent
d29f4a8f76
commit
43aab615c3
@ -12,7 +12,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
"github.com/grafana/grafana/pkg/infra/network"
|
||||
"github.com/grafana/grafana/pkg/login"
|
||||
"github.com/grafana/grafana/pkg/middleware/cookies"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
@ -38,31 +37,39 @@ var getViewIndex = func() string {
|
||||
return viewIndex
|
||||
}
|
||||
|
||||
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")
|
||||
)
|
||||
|
||||
func (hs *HTTPServer) ValidateRedirectTo(redirectTo string) error {
|
||||
to, err := url.Parse(redirectTo)
|
||||
if err != nil {
|
||||
return login.ErrInvalidRedirectTo
|
||||
return errInvalidRedirectTo
|
||||
}
|
||||
|
||||
if to.IsAbs() {
|
||||
return login.ErrAbsoluteRedirectTo
|
||||
return errAbsoluteRedirectTo
|
||||
}
|
||||
|
||||
if to.Host != "" {
|
||||
return login.ErrForbiddenRedirectTo
|
||||
return errForbiddenRedirectTo
|
||||
}
|
||||
|
||||
// path should have exactly one leading slash
|
||||
if !strings.HasPrefix(to.Path, "/") {
|
||||
return login.ErrForbiddenRedirectTo
|
||||
return errForbiddenRedirectTo
|
||||
}
|
||||
|
||||
if strings.HasPrefix(to.Path, "//") {
|
||||
return login.ErrForbiddenRedirectTo
|
||||
return errForbiddenRedirectTo
|
||||
}
|
||||
|
||||
// when using a subUrl, the redirect_to should start with the subUrl (which contains the leading slash), otherwise the redirect
|
||||
// will send the user to the wrong location
|
||||
if hs.Cfg.AppSubURL != "" && !strings.HasPrefix(to.Path, hs.Cfg.AppSubURL+"/") {
|
||||
return login.ErrInvalidRedirectTo
|
||||
return errInvalidRedirectTo
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -1,8 +1,9 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
"github.com/grafana/grafana/pkg/login"
|
||||
"github.com/grafana/grafana/pkg/middleware/cookies"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
@ -21,7 +22,7 @@ func (hs *HTTPServer) OAuthLogin(reqCtx *contextmodel.ReqContext) {
|
||||
errorDesc := reqCtx.Query("error_description")
|
||||
hs.log.Error("failed to login ", "error", errorParam, "errorDesc", errorDesc)
|
||||
|
||||
hs.redirectWithError(reqCtx, login.ErrProviderDeniedRequest, "error", errorParam, "errorDesc", errorDesc)
|
||||
hs.redirectWithError(reqCtx, errors.New("login provider denied login request"), "error", errorParam, "errorDesc", errorDesc)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,114 +0,0 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/ldap"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/loginattempt"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrEmailNotAllowed = errors.New("required email domain not fulfilled")
|
||||
ErrInvalidCredentials = errors.New("invalid username or password")
|
||||
ErrNoEmail = errors.New("login provider didn't return an email address")
|
||||
ErrProviderDeniedRequest = errors.New("login provider denied login request")
|
||||
ErrTooManyLoginAttempts = errors.New("too many consecutive incorrect login attempts for user - login for user temporarily blocked")
|
||||
ErrPasswordEmpty = errors.New("no password provided")
|
||||
ErrUserDisabled = errors.New("user is disabled")
|
||||
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")
|
||||
|
||||
type Authenticator interface {
|
||||
AuthenticateUser(context.Context, *login.LoginUserQuery) error
|
||||
}
|
||||
|
||||
type AuthenticatorService struct {
|
||||
loginService login.Service
|
||||
loginAttemptService loginattempt.Service
|
||||
userService user.Service
|
||||
cfg *setting.Cfg
|
||||
}
|
||||
|
||||
func ProvideService(store db.DB, loginService login.Service,
|
||||
loginAttemptService loginattempt.Service,
|
||||
userService user.Service, cfg *setting.Cfg) *AuthenticatorService {
|
||||
a := &AuthenticatorService{
|
||||
loginService: loginService,
|
||||
loginAttemptService: loginAttemptService,
|
||||
userService: userService,
|
||||
cfg: cfg,
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// AuthenticateUser authenticates the user via username & password
|
||||
func (a *AuthenticatorService) AuthenticateUser(ctx context.Context, query *login.LoginUserQuery) error {
|
||||
ok, err := a.loginAttemptService.Validate(ctx, query.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return ErrTooManyLoginAttempts
|
||||
}
|
||||
|
||||
if err := validatePasswordSet(query.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isGrafanaLoginEnabled := !query.Cfg.DisableLogin
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
ldapEnabled, ldapErr := loginUsingLDAP(ctx, query, a.loginService, a.cfg)
|
||||
if ldapEnabled {
|
||||
query.AuthModule = login.LDAPAuthModule
|
||||
if ldapErr == nil || !errors.Is(ldapErr, ldap.ErrInvalidCredentials) {
|
||||
return ldapErr
|
||||
}
|
||||
|
||||
if !errors.Is(err, ErrUserDisabled) || !errors.Is(ldapErr, ldap.ErrInvalidCredentials) {
|
||||
err = ldapErr
|
||||
}
|
||||
}
|
||||
|
||||
if errors.Is(err, ErrInvalidCredentials) || errors.Is(err, ldap.ErrInvalidCredentials) {
|
||||
if err := a.loginAttemptService.Add(ctx, query.Username, query.IpAddress); err != nil {
|
||||
loginLogger.Error("Failed to save invalid login attempt", "err", err)
|
||||
}
|
||||
|
||||
return ErrInvalidCredentials
|
||||
}
|
||||
|
||||
if !isGrafanaLoginEnabled && !ldapEnabled {
|
||||
return ErrNoAuthProvider
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func validatePasswordSet(password string) error {
|
||||
if len(password) == 0 {
|
||||
return ErrPasswordEmpty
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,241 +0,0 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ldap"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||
"github.com/grafana/grafana/pkg/services/loginattempt/loginattempttest"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func TestAuthenticateUser(t *testing.T) {
|
||||
authScenario(t, "When a user authenticates without setting a password", func(sc *authScenarioContext) {
|
||||
mockLoginUsingGrafanaDB(nil, sc)
|
||||
mockLoginUsingLDAP(false, nil, sc)
|
||||
|
||||
loginAttemptService := &loginattempttest.FakeLoginAttemptService{ExpectedValid: true}
|
||||
cfg := setting.NewCfg()
|
||||
a := AuthenticatorService{loginAttemptService: loginAttemptService, loginService: &logintest.LoginServiceFake{}, cfg: cfg}
|
||||
err := a.AuthenticateUser(context.Background(), &login.LoginUserQuery{
|
||||
Username: "user",
|
||||
Password: "",
|
||||
})
|
||||
|
||||
require.EqualError(t, err, ErrPasswordEmpty.Error())
|
||||
assert.False(t, sc.grafanaLoginWasCalled)
|
||||
assert.False(t, sc.ldapLoginWasCalled)
|
||||
assert.Empty(t, sc.loginUserQuery.AuthModule)
|
||||
})
|
||||
|
||||
authScenario(t, "When user authenticates with no auth provider enabled", func(sc *authScenarioContext) {
|
||||
cfg := setting.NewCfg()
|
||||
sc.loginUserQuery.Cfg.DisableLogin = true
|
||||
|
||||
loginAttemptService := &loginattempttest.MockLoginAttemptService{ExpectedValid: true}
|
||||
a := AuthenticatorService{loginAttemptService: loginAttemptService, loginService: &logintest.LoginServiceFake{}, cfg: cfg}
|
||||
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||
|
||||
require.EqualError(t, err, ErrNoAuthProvider.Error())
|
||||
assert.False(t, sc.grafanaLoginWasCalled)
|
||||
assert.False(t, sc.ldapLoginWasCalled)
|
||||
assert.Equal(t, "", sc.loginUserQuery.AuthModule)
|
||||
assert.False(t, loginAttemptService.AddCalled)
|
||||
assert.True(t, loginAttemptService.ValidateCalled)
|
||||
})
|
||||
|
||||
authScenario(t, "When a user authenticates having too many login attempts", func(sc *authScenarioContext) {
|
||||
cfg := setting.NewCfg()
|
||||
mockLoginUsingGrafanaDB(nil, sc)
|
||||
mockLoginUsingLDAP(true, nil, sc)
|
||||
|
||||
loginAttemptService := &loginattempttest.MockLoginAttemptService{ExpectedValid: false}
|
||||
a := AuthenticatorService{loginAttemptService: loginAttemptService, loginService: &logintest.LoginServiceFake{}, cfg: cfg}
|
||||
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||
|
||||
require.EqualError(t, err, ErrTooManyLoginAttempts.Error())
|
||||
assert.False(t, sc.grafanaLoginWasCalled)
|
||||
assert.False(t, sc.ldapLoginWasCalled)
|
||||
assert.Empty(t, sc.loginUserQuery.AuthModule)
|
||||
assert.False(t, loginAttemptService.AddCalled)
|
||||
assert.True(t, loginAttemptService.ValidateCalled)
|
||||
})
|
||||
|
||||
authScenario(t, "When grafana user authenticate with valid credentials", func(sc *authScenarioContext) {
|
||||
cfg := setting.NewCfg()
|
||||
mockLoginUsingGrafanaDB(nil, sc)
|
||||
mockLoginUsingLDAP(true, ErrInvalidCredentials, sc)
|
||||
|
||||
loginAttemptService := &loginattempttest.MockLoginAttemptService{ExpectedValid: true}
|
||||
a := AuthenticatorService{loginAttemptService: loginAttemptService, loginService: &logintest.LoginServiceFake{}, cfg: cfg}
|
||||
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.True(t, sc.grafanaLoginWasCalled)
|
||||
assert.False(t, sc.ldapLoginWasCalled)
|
||||
assert.Equal(t, "grafana", sc.loginUserQuery.AuthModule)
|
||||
assert.False(t, loginAttemptService.AddCalled)
|
||||
assert.True(t, loginAttemptService.ValidateCalled)
|
||||
})
|
||||
|
||||
authScenario(t, "When grafana user authenticate and unexpected error occurs", func(sc *authScenarioContext) {
|
||||
cfg := setting.NewCfg()
|
||||
customErr := errors.New("custom")
|
||||
mockLoginUsingGrafanaDB(customErr, sc)
|
||||
mockLoginUsingLDAP(true, ErrInvalidCredentials, sc)
|
||||
|
||||
loginAttemptService := &loginattempttest.MockLoginAttemptService{ExpectedValid: true}
|
||||
a := AuthenticatorService{loginAttemptService: loginAttemptService, loginService: &logintest.LoginServiceFake{}, cfg: cfg}
|
||||
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||
|
||||
require.EqualError(t, err, customErr.Error())
|
||||
assert.True(t, sc.grafanaLoginWasCalled)
|
||||
assert.False(t, sc.ldapLoginWasCalled)
|
||||
assert.Equal(t, "grafana", sc.loginUserQuery.AuthModule)
|
||||
assert.False(t, loginAttemptService.AddCalled)
|
||||
assert.True(t, loginAttemptService.ValidateCalled)
|
||||
})
|
||||
|
||||
authScenario(t, "When a non-existing grafana user authenticate and ldap disabled", func(sc *authScenarioContext) {
|
||||
cfg := setting.NewCfg()
|
||||
mockLoginUsingGrafanaDB(user.ErrUserNotFound, sc)
|
||||
mockLoginUsingLDAP(false, nil, sc)
|
||||
|
||||
loginAttemptService := &loginattempttest.MockLoginAttemptService{ExpectedValid: true}
|
||||
a := AuthenticatorService{loginAttemptService: loginAttemptService, loginService: &logintest.LoginServiceFake{}, cfg: cfg}
|
||||
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||
|
||||
require.EqualError(t, err, user.ErrUserNotFound.Error())
|
||||
assert.True(t, sc.grafanaLoginWasCalled)
|
||||
assert.True(t, sc.ldapLoginWasCalled)
|
||||
assert.Empty(t, sc.loginUserQuery.AuthModule)
|
||||
assert.False(t, loginAttemptService.AddCalled)
|
||||
assert.True(t, loginAttemptService.ValidateCalled)
|
||||
})
|
||||
|
||||
authScenario(t, "When a non-existing grafana user authenticate and invalid ldap credentials", func(sc *authScenarioContext) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPAuthEnabled = true
|
||||
mockLoginUsingGrafanaDB(user.ErrUserNotFound, sc)
|
||||
mockLoginUsingLDAP(true, ldap.ErrInvalidCredentials, sc)
|
||||
|
||||
loginAttemptService := &loginattempttest.MockLoginAttemptService{ExpectedValid: true}
|
||||
a := AuthenticatorService{loginAttemptService: loginAttemptService, loginService: &logintest.LoginServiceFake{}, cfg: cfg}
|
||||
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||
|
||||
require.EqualError(t, err, ErrInvalidCredentials.Error())
|
||||
assert.True(t, sc.grafanaLoginWasCalled)
|
||||
assert.True(t, sc.ldapLoginWasCalled)
|
||||
assert.Equal(t, login.LDAPAuthModule, sc.loginUserQuery.AuthModule)
|
||||
assert.True(t, loginAttemptService.AddCalled)
|
||||
assert.True(t, loginAttemptService.ValidateCalled)
|
||||
})
|
||||
|
||||
authScenario(t, "When a non-existing grafana user authenticate and valid ldap credentials", func(sc *authScenarioContext) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPAuthEnabled = true
|
||||
mockLoginUsingGrafanaDB(user.ErrUserNotFound, sc)
|
||||
mockLoginUsingLDAP(true, nil, sc)
|
||||
|
||||
loginAttemptService := &loginattempttest.MockLoginAttemptService{ExpectedValid: true}
|
||||
a := AuthenticatorService{loginAttemptService: loginAttemptService, loginService: &logintest.LoginServiceFake{}, cfg: cfg}
|
||||
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.True(t, sc.grafanaLoginWasCalled)
|
||||
assert.True(t, sc.ldapLoginWasCalled)
|
||||
assert.Equal(t, login.LDAPAuthModule, sc.loginUserQuery.AuthModule)
|
||||
assert.False(t, loginAttemptService.AddCalled)
|
||||
assert.True(t, loginAttemptService.ValidateCalled)
|
||||
})
|
||||
|
||||
authScenario(t, "When a non-existing grafana user authenticate and ldap returns unexpected error", func(sc *authScenarioContext) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPAuthEnabled = true
|
||||
customErr := errors.New("custom")
|
||||
mockLoginUsingGrafanaDB(user.ErrUserNotFound, sc)
|
||||
mockLoginUsingLDAP(true, customErr, sc)
|
||||
|
||||
loginAttemptService := &loginattempttest.MockLoginAttemptService{ExpectedValid: true}
|
||||
a := AuthenticatorService{loginAttemptService: loginAttemptService, loginService: &logintest.LoginServiceFake{}, cfg: cfg}
|
||||
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||
|
||||
require.EqualError(t, err, customErr.Error())
|
||||
assert.True(t, sc.grafanaLoginWasCalled)
|
||||
assert.True(t, sc.ldapLoginWasCalled)
|
||||
assert.Equal(t, login.LDAPAuthModule, sc.loginUserQuery.AuthModule)
|
||||
assert.False(t, loginAttemptService.AddCalled)
|
||||
assert.True(t, loginAttemptService.ValidateCalled)
|
||||
})
|
||||
|
||||
authScenario(t, "When grafana user authenticate with invalid credentials and invalid ldap credentials", func(sc *authScenarioContext) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPAuthEnabled = true
|
||||
mockLoginUsingGrafanaDB(ErrInvalidCredentials, sc)
|
||||
mockLoginUsingLDAP(true, ldap.ErrInvalidCredentials, sc)
|
||||
|
||||
loginAttemptService := &loginattempttest.MockLoginAttemptService{ExpectedValid: true}
|
||||
a := AuthenticatorService{loginAttemptService: loginAttemptService, loginService: &logintest.LoginServiceFake{}, cfg: cfg}
|
||||
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||
|
||||
require.EqualError(t, err, ErrInvalidCredentials.Error())
|
||||
assert.True(t, sc.grafanaLoginWasCalled)
|
||||
assert.True(t, sc.ldapLoginWasCalled)
|
||||
assert.True(t, loginAttemptService.AddCalled)
|
||||
assert.True(t, loginAttemptService.ValidateCalled)
|
||||
})
|
||||
}
|
||||
|
||||
type authScenarioContext struct {
|
||||
loginUserQuery *login.LoginUserQuery
|
||||
grafanaLoginWasCalled bool
|
||||
ldapLoginWasCalled bool
|
||||
}
|
||||
|
||||
type authScenarioFunc func(sc *authScenarioContext)
|
||||
|
||||
func mockLoginUsingGrafanaDB(err error, sc *authScenarioContext) {
|
||||
loginUsingGrafanaDB = func(ctx context.Context, query *login.LoginUserQuery, _ user.Service) error {
|
||||
sc.grafanaLoginWasCalled = true
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func mockLoginUsingLDAP(enabled bool, err error, sc *authScenarioContext) {
|
||||
loginUsingLDAP = func(ctx context.Context, query *login.LoginUserQuery, _ login.Service, _ *setting.Cfg) (bool, error) {
|
||||
sc.ldapLoginWasCalled = true
|
||||
return enabled, err
|
||||
}
|
||||
}
|
||||
|
||||
func authScenario(t *testing.T, desc string, fn authScenarioFunc) {
|
||||
t.Helper()
|
||||
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
origLoginUsingGrafanaDB := loginUsingGrafanaDB
|
||||
origLoginUsingLDAP := loginUsingLDAP
|
||||
cfg := setting.Cfg{DisableLogin: false}
|
||||
sc := &authScenarioContext{
|
||||
loginUserQuery: &login.LoginUserQuery{
|
||||
Username: "user",
|
||||
Password: "pwd",
|
||||
IpAddress: "192.168.1.1:56433",
|
||||
Cfg: &cfg,
|
||||
},
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
loginUsingGrafanaDB = origLoginUsingGrafanaDB
|
||||
loginUsingLDAP = origLoginUsingLDAP
|
||||
})
|
||||
|
||||
fn(sc)
|
||||
})
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
var validatePassword = func(providedPassword string, userPassword string, userSalt string) error {
|
||||
passwordHashed, err := util.EncodePassword(providedPassword, userSalt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if subtle.ConstantTimeCompare([]byte(passwordHashed), []byte(userPassword)) != 1 {
|
||||
return ErrInvalidCredentials
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var loginUsingGrafanaDB = func(ctx context.Context, query *login.LoginUserQuery, userService user.Service) error {
|
||||
userQuery := user.GetUserByLoginQuery{LoginOrEmail: query.Username}
|
||||
|
||||
user, err := userService.GetByLogin(ctx, &userQuery)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.IsDisabled {
|
||||
return ErrUserDisabled
|
||||
}
|
||||
|
||||
if err := validatePassword(query.Password, user.Password, user.Salt); err != nil {
|
||||
return err
|
||||
}
|
||||
query.User = user
|
||||
return nil
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/services/user/usertest"
|
||||
)
|
||||
|
||||
func TestLoginUsingGrafanaDB(t *testing.T) {
|
||||
grafanaLoginScenario(t, "When login with non-existing user", func(sc *grafanaLoginScenarioContext) {
|
||||
sc.withNonExistingUser()
|
||||
err := loginUsingGrafanaDB(context.Background(), sc.loginUserQuery, sc.userService)
|
||||
require.EqualError(t, err, user.ErrUserNotFound.Error())
|
||||
|
||||
assert.False(t, sc.validatePasswordCalled)
|
||||
assert.Nil(t, sc.loginUserQuery.User)
|
||||
})
|
||||
|
||||
grafanaLoginScenario(t, "When login with invalid credentials", func(sc *grafanaLoginScenarioContext) {
|
||||
sc.withInvalidPassword()
|
||||
err := loginUsingGrafanaDB(context.Background(), sc.loginUserQuery, sc.userService)
|
||||
|
||||
require.EqualError(t, err, ErrInvalidCredentials.Error())
|
||||
|
||||
assert.True(t, sc.validatePasswordCalled)
|
||||
assert.Nil(t, sc.loginUserQuery.User)
|
||||
})
|
||||
|
||||
grafanaLoginScenario(t, "When login with valid credentials", func(sc *grafanaLoginScenarioContext) {
|
||||
sc.withValidCredentials()
|
||||
err := loginUsingGrafanaDB(context.Background(), sc.loginUserQuery, sc.userService)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, sc.validatePasswordCalled)
|
||||
|
||||
require.NotNil(t, sc.loginUserQuery.User)
|
||||
assert.Equal(t, sc.loginUserQuery.Username, sc.loginUserQuery.User.Login)
|
||||
assert.Equal(t, sc.loginUserQuery.Password, sc.loginUserQuery.User.Password)
|
||||
})
|
||||
|
||||
grafanaLoginScenario(t, "When login with disabled user", func(sc *grafanaLoginScenarioContext) {
|
||||
sc.withDisabledUser()
|
||||
err := loginUsingGrafanaDB(context.Background(), sc.loginUserQuery, sc.userService)
|
||||
require.EqualError(t, err, ErrUserDisabled.Error())
|
||||
|
||||
assert.False(t, sc.validatePasswordCalled)
|
||||
assert.Nil(t, sc.loginUserQuery.User)
|
||||
})
|
||||
}
|
||||
|
||||
type grafanaLoginScenarioContext struct {
|
||||
store db.DB
|
||||
userService *usertest.FakeUserService
|
||||
loginUserQuery *login.LoginUserQuery
|
||||
validatePasswordCalled bool
|
||||
}
|
||||
|
||||
type grafanaLoginScenarioFunc func(c *grafanaLoginScenarioContext)
|
||||
|
||||
func grafanaLoginScenario(t *testing.T, desc string, fn grafanaLoginScenarioFunc) {
|
||||
t.Helper()
|
||||
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
origValidatePassword := validatePassword
|
||||
|
||||
sc := &grafanaLoginScenarioContext{
|
||||
store: dbtest.NewFakeDB(),
|
||||
loginUserQuery: &login.LoginUserQuery{
|
||||
Username: "user",
|
||||
Password: "pwd",
|
||||
IpAddress: "192.168.1.1:56433",
|
||||
},
|
||||
validatePasswordCalled: false,
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
validatePassword = origValidatePassword
|
||||
})
|
||||
|
||||
fn(sc)
|
||||
})
|
||||
}
|
||||
|
||||
func mockPasswordValidation(valid bool, sc *grafanaLoginScenarioContext) {
|
||||
validatePassword = func(providedPassword string, userPassword string, userSalt string) error {
|
||||
sc.validatePasswordCalled = true
|
||||
|
||||
if !valid {
|
||||
return ErrInvalidCredentials
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *grafanaLoginScenarioContext) getUserByLoginQueryReturns(usr *user.User) {
|
||||
sc.userService = usertest.NewUserServiceFake()
|
||||
sc.userService.ExpectedUser = usr
|
||||
if usr == nil {
|
||||
sc.userService.ExpectedError = user.ErrUserNotFound
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *grafanaLoginScenarioContext) withValidCredentials() {
|
||||
sc.getUserByLoginQueryReturns(&user.User{
|
||||
ID: 1,
|
||||
Login: sc.loginUserQuery.Username,
|
||||
Password: sc.loginUserQuery.Password,
|
||||
Salt: "salt",
|
||||
})
|
||||
mockPasswordValidation(true, sc)
|
||||
}
|
||||
|
||||
func (sc *grafanaLoginScenarioContext) withNonExistingUser() {
|
||||
sc.getUserByLoginQueryReturns(nil)
|
||||
}
|
||||
|
||||
func (sc *grafanaLoginScenarioContext) withInvalidPassword() {
|
||||
sc.getUserByLoginQueryReturns(&user.User{
|
||||
Password: sc.loginUserQuery.Password,
|
||||
Salt: "salt",
|
||||
})
|
||||
mockPasswordValidation(false, sc)
|
||||
}
|
||||
|
||||
func (sc *grafanaLoginScenarioContext) withDisabledUser() {
|
||||
sc.getUserByLoginQueryReturns(&user.User{
|
||||
IsDisabled: true,
|
||||
})
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/ldap"
|
||||
"github.com/grafana/grafana/pkg/services/ldap/multildap"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
// getLDAPConfig gets LDAP config
|
||||
var getLDAPConfig = multildap.GetConfig
|
||||
|
||||
// newLDAP creates multiple LDAP instance
|
||||
var newLDAP = multildap.New
|
||||
|
||||
// logger for the LDAP auth
|
||||
var ldapLogger = log.New("login.ldap")
|
||||
|
||||
// loginUsingLDAP logs in user using LDAP. It returns whether LDAP is enabled and optional error and query arg will be
|
||||
// populated with the logged in user if successful.
|
||||
var loginUsingLDAP = func(ctx context.Context, query *login.LoginUserQuery,
|
||||
loginService login.Service, cfg *setting.Cfg) (bool, error) {
|
||||
if !cfg.LDAPAuthEnabled {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
config, err := getLDAPConfig(query.Cfg)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("%v: %w", "Failed to get LDAP config", err)
|
||||
}
|
||||
|
||||
externalUser, err := newLDAP(config.Servers, cfg).Login(query)
|
||||
if err != nil {
|
||||
if errors.Is(err, ldap.ErrCouldNotFindUser) {
|
||||
// Ignore the error since user might not be present anyway
|
||||
if err := loginService.DisableExternalUser(ctx, query.Username); err != nil {
|
||||
ldapLogger.Debug("Failed to disable external user", "err", err)
|
||||
}
|
||||
|
||||
// Return invalid credentials if we couldn't find the user anywhere
|
||||
return true, ldap.ErrInvalidCredentials
|
||||
}
|
||||
|
||||
return true, err
|
||||
}
|
||||
|
||||
upsert := &login.UpsertUserCommand{
|
||||
ReqContext: query.ReqContext,
|
||||
ExternalUser: externalUser,
|
||||
SignupAllowed: cfg.LDAPAllowSignup,
|
||||
UserLookupParams: login.UserLookupParams{
|
||||
Login: &externalUser.Login,
|
||||
Email: &externalUser.Email,
|
||||
UserID: nil,
|
||||
},
|
||||
}
|
||||
query.User, err = loginService.UpsertUser(ctx, upsert)
|
||||
return true, err
|
||||
}
|
@ -1,160 +0,0 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ldap"
|
||||
"github.com/grafana/grafana/pkg/services/ldap/multildap"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var errTest = errors.New("test error")
|
||||
|
||||
func TestLoginUsingLDAP(t *testing.T) {
|
||||
LDAPLoginScenario(t, "When LDAP enabled and no server configured", func(sc *LDAPLoginScenarioContext) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
sc.withLoginResult(false)
|
||||
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
||||
config := &ldap.Config{
|
||||
Servers: []*ldap.ServerConfig{},
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
loginService := &logintest.LoginServiceFake{}
|
||||
enabled, err := loginUsingLDAP(context.Background(), sc.loginUserQuery, loginService, cfg)
|
||||
require.EqualError(t, err, errTest.Error())
|
||||
|
||||
assert.True(t, enabled)
|
||||
assert.True(t, sc.LDAPAuthenticatorMock.loginCalled)
|
||||
})
|
||||
|
||||
LDAPLoginScenario(t, "When LDAP disabled", func(sc *LDAPLoginScenarioContext) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPAuthEnabled = false
|
||||
|
||||
sc.withLoginResult(false)
|
||||
loginService := &logintest.LoginServiceFake{}
|
||||
enabled, err := loginUsingLDAP(context.Background(), sc.loginUserQuery, loginService, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.False(t, enabled)
|
||||
assert.False(t, sc.LDAPAuthenticatorMock.loginCalled)
|
||||
})
|
||||
}
|
||||
|
||||
type mockAuth struct {
|
||||
validLogin bool
|
||||
loginCalled bool
|
||||
pingCalled bool
|
||||
}
|
||||
|
||||
func (auth *mockAuth) Ping() ([]*multildap.ServerStatus, error) {
|
||||
auth.pingCalled = true
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (auth *mockAuth) Login(query *login.LoginUserQuery) (
|
||||
*login.ExternalUserInfo,
|
||||
error,
|
||||
) {
|
||||
auth.loginCalled = true
|
||||
|
||||
if !auth.validLogin {
|
||||
return nil, errTest
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (auth *mockAuth) Users(logins []string) (
|
||||
[]*login.ExternalUserInfo,
|
||||
error,
|
||||
) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (auth *mockAuth) User(login string) (
|
||||
*login.ExternalUserInfo,
|
||||
ldap.ServerConfig,
|
||||
error,
|
||||
) {
|
||||
return nil, ldap.ServerConfig{}, nil
|
||||
}
|
||||
|
||||
func (auth *mockAuth) Add(dn string, values map[string][]string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (auth *mockAuth) Remove(dn string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func mockLDAPAuthenticator(valid bool) *mockAuth {
|
||||
mock := &mockAuth{
|
||||
validLogin: valid,
|
||||
}
|
||||
|
||||
newLDAP = func(servers []*ldap.ServerConfig, _ *setting.Cfg) multildap.IMultiLDAP {
|
||||
return mock
|
||||
}
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
type LDAPLoginScenarioContext struct {
|
||||
loginUserQuery *login.LoginUserQuery
|
||||
LDAPAuthenticatorMock *mockAuth
|
||||
}
|
||||
|
||||
type LDAPLoginScenarioFunc func(c *LDAPLoginScenarioContext)
|
||||
|
||||
func LDAPLoginScenario(t *testing.T, desc string, fn LDAPLoginScenarioFunc) {
|
||||
t.Helper()
|
||||
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
mock := &mockAuth{}
|
||||
|
||||
sc := &LDAPLoginScenarioContext{
|
||||
loginUserQuery: &login.LoginUserQuery{
|
||||
Username: "user",
|
||||
Password: "pwd",
|
||||
IpAddress: "192.168.1.1:56433",
|
||||
},
|
||||
LDAPAuthenticatorMock: mock,
|
||||
}
|
||||
|
||||
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
||||
config := &ldap.Config{
|
||||
Servers: []*ldap.ServerConfig{
|
||||
{
|
||||
Host: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
newLDAP = func(server []*ldap.ServerConfig, _ *setting.Cfg) multildap.IMultiLDAP {
|
||||
return mock
|
||||
}
|
||||
|
||||
fn(sc)
|
||||
})
|
||||
}
|
||||
|
||||
func (sc *LDAPLoginScenarioContext) withLoginResult(valid bool) {
|
||||
sc.LDAPAuthenticatorMock = mockLDAPAuthenticator(valid)
|
||||
}
|
@ -26,7 +26,6 @@ import (
|
||||
uss "github.com/grafana/grafana/pkg/infra/usagestats/service"
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats/statscollector"
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats/validator"
|
||||
loginpkg "github.com/grafana/grafana/pkg/login"
|
||||
"github.com/grafana/grafana/pkg/login/social"
|
||||
"github.com/grafana/grafana/pkg/middleware/csrf"
|
||||
"github.com/grafana/grafana/pkg/middleware/loggermw"
|
||||
@ -220,8 +219,6 @@ var wireBasicSet = wire.NewSet(
|
||||
authinfoservice.ProvideAuthInfoService,
|
||||
wire.Bind(new(login.AuthInfoService), new(*authinfoservice.Implementation)),
|
||||
authinfodatabase.ProvideAuthInfoStore,
|
||||
loginpkg.ProvideService,
|
||||
wire.Bind(new(loginpkg.Authenticator), new(*loginpkg.AuthenticatorService)),
|
||||
datasourceproxy.ProvideService,
|
||||
search.ProvideService,
|
||||
searchV2.ProvideService,
|
||||
|
Loading…
Reference in New Issue
Block a user