From 43aab615c3cdb94896c12e966ff3a463aca2dc7e Mon Sep 17 00:00:00 2001 From: Karl Persson Date: Thu, 10 Aug 2023 11:02:32 +0200 Subject: [PATCH] Auth: Remove unused Authenticator service (#73143) Auth: remove unused Authenticator service --- pkg/api/login.go | 21 ++- pkg/api/login_oauth.go | 5 +- pkg/login/auth.go | 114 --------------- pkg/login/auth_test.go | 241 -------------------------------- pkg/login/grafana_login.go | 41 ------ pkg/login/grafana_login_test.go | 138 ------------------ pkg/login/ldap_login.go | 64 --------- pkg/login/ldap_login_test.go | 160 --------------------- pkg/server/wire.go | 3 - 9 files changed, 17 insertions(+), 770 deletions(-) delete mode 100644 pkg/login/auth.go delete mode 100644 pkg/login/auth_test.go delete mode 100644 pkg/login/grafana_login.go delete mode 100644 pkg/login/grafana_login_test.go delete mode 100644 pkg/login/ldap_login.go delete mode 100644 pkg/login/ldap_login_test.go diff --git a/pkg/api/login.go b/pkg/api/login.go index 9ca800f5b0f..bf02fa71396 100644 --- a/pkg/api/login.go +++ b/pkg/api/login.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 diff --git a/pkg/api/login_oauth.go b/pkg/api/login_oauth.go index 77a0a251824..256a225959e 100644 --- a/pkg/api/login_oauth.go +++ b/pkg/api/login_oauth.go @@ -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 } diff --git a/pkg/login/auth.go b/pkg/login/auth.go deleted file mode 100644 index ad26e68b41e..00000000000 --- a/pkg/login/auth.go +++ /dev/null @@ -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 -} diff --git a/pkg/login/auth_test.go b/pkg/login/auth_test.go deleted file mode 100644 index 33557e03abd..00000000000 --- a/pkg/login/auth_test.go +++ /dev/null @@ -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) - }) -} diff --git a/pkg/login/grafana_login.go b/pkg/login/grafana_login.go deleted file mode 100644 index 3ef14a39f79..00000000000 --- a/pkg/login/grafana_login.go +++ /dev/null @@ -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 -} diff --git a/pkg/login/grafana_login_test.go b/pkg/login/grafana_login_test.go deleted file mode 100644 index cd68019de97..00000000000 --- a/pkg/login/grafana_login_test.go +++ /dev/null @@ -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, - }) -} diff --git a/pkg/login/ldap_login.go b/pkg/login/ldap_login.go deleted file mode 100644 index e4f646e16cb..00000000000 --- a/pkg/login/ldap_login.go +++ /dev/null @@ -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 -} diff --git a/pkg/login/ldap_login_test.go b/pkg/login/ldap_login_test.go deleted file mode 100644 index 9b6509f7796..00000000000 --- a/pkg/login/ldap_login_test.go +++ /dev/null @@ -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) -} diff --git a/pkg/server/wire.go b/pkg/server/wire.go index 55ea3b13347..c120c43eaa2 100644 --- a/pkg/server/wire.go +++ b/pkg/server/wire.go @@ -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,