Auth: Remove unused Authenticator service (#73143)

Auth: remove unused Authenticator service
pull/73145/head
Karl Persson 2 years ago committed by GitHub
parent d29f4a8f76
commit 43aab615c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      pkg/api/login.go
  2. 5
      pkg/api/login_oauth.go
  3. 114
      pkg/login/auth.go
  4. 241
      pkg/login/auth_test.go
  5. 41
      pkg/login/grafana_login.go
  6. 138
      pkg/login/grafana_login_test.go
  7. 64
      pkg/login/ldap_login.go
  8. 160
      pkg/login/ldap_login_test.go
  9. 3
      pkg/server/wire.go

@ -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…
Cancel
Save