Auth: Remove unused Authenticator service (#73143)

Auth: remove unused Authenticator service
This commit is contained in:
Karl Persson 2023-08-10 11:02:32 +02:00 committed by GitHub
parent d29f4a8f76
commit 43aab615c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 17 additions and 770 deletions

View File

@ -12,7 +12,6 @@ import (
"github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/infra/network" "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/middleware/cookies"
"github.com/grafana/grafana/pkg/services/auth" "github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/authn" "github.com/grafana/grafana/pkg/services/authn"
@ -38,31 +37,39 @@ var getViewIndex = func() string {
return viewIndex 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 { func (hs *HTTPServer) ValidateRedirectTo(redirectTo string) error {
to, err := url.Parse(redirectTo) to, err := url.Parse(redirectTo)
if err != nil { if err != nil {
return login.ErrInvalidRedirectTo return errInvalidRedirectTo
} }
if to.IsAbs() { if to.IsAbs() {
return login.ErrAbsoluteRedirectTo return errAbsoluteRedirectTo
} }
if to.Host != "" { if to.Host != "" {
return login.ErrForbiddenRedirectTo return errForbiddenRedirectTo
} }
// path should have exactly one leading slash // path should have exactly one leading slash
if !strings.HasPrefix(to.Path, "/") { if !strings.HasPrefix(to.Path, "/") {
return login.ErrForbiddenRedirectTo return errForbiddenRedirectTo
} }
if strings.HasPrefix(to.Path, "//") { 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 // 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 // will send the user to the wrong location
if hs.Cfg.AppSubURL != "" && !strings.HasPrefix(to.Path, hs.Cfg.AppSubURL+"/") { if hs.Cfg.AppSubURL != "" && !strings.HasPrefix(to.Path, hs.Cfg.AppSubURL+"/") {
return login.ErrInvalidRedirectTo return errInvalidRedirectTo
} }
return nil return nil

View File

@ -1,8 +1,9 @@
package api package api
import ( import (
"errors"
"github.com/grafana/grafana/pkg/infra/metrics" "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/middleware/cookies"
"github.com/grafana/grafana/pkg/services/authn" "github.com/grafana/grafana/pkg/services/authn"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" 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") errorDesc := reqCtx.Query("error_description")
hs.log.Error("failed to login ", "error", errorParam, "errorDesc", errorDesc) 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 return
} }

View File

@ -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
}

View File

@ -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)
})
}

View File

@ -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
}

View File

@ -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,
})
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -26,7 +26,6 @@ import (
uss "github.com/grafana/grafana/pkg/infra/usagestats/service" uss "github.com/grafana/grafana/pkg/infra/usagestats/service"
"github.com/grafana/grafana/pkg/infra/usagestats/statscollector" "github.com/grafana/grafana/pkg/infra/usagestats/statscollector"
"github.com/grafana/grafana/pkg/infra/usagestats/validator" "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/login/social"
"github.com/grafana/grafana/pkg/middleware/csrf" "github.com/grafana/grafana/pkg/middleware/csrf"
"github.com/grafana/grafana/pkg/middleware/loggermw" "github.com/grafana/grafana/pkg/middleware/loggermw"
@ -220,8 +219,6 @@ var wireBasicSet = wire.NewSet(
authinfoservice.ProvideAuthInfoService, authinfoservice.ProvideAuthInfoService,
wire.Bind(new(login.AuthInfoService), new(*authinfoservice.Implementation)), wire.Bind(new(login.AuthInfoService), new(*authinfoservice.Implementation)),
authinfodatabase.ProvideAuthInfoStore, authinfodatabase.ProvideAuthInfoStore,
loginpkg.ProvideService,
wire.Bind(new(loginpkg.Authenticator), new(*loginpkg.AuthenticatorService)),
datasourceproxy.ProvideService, datasourceproxy.ProvideService,
search.ProvideService, search.ProvideService,
searchV2.ProvideService, searchV2.ProvideService,