mirror of
https://github.com/grafana/grafana.git
synced 2025-08-03 04:22:13 +08:00
Chore: Remove bus.Dispatch from some login packages (#47248)
* Chore: Remove bus.Dispatch from some login packages * remove debug log Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * remove login.Init() * remove unused reset function * remove AuthenticateUserFunc global * swap conditional branches Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * fix formatting Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
This commit is contained in:
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/auth"
|
"github.com/grafana/grafana/pkg/services/auth"
|
||||||
"github.com/grafana/grafana/pkg/services/login/loginservice"
|
"github.com/grafana/grafana/pkg/services/login/loginservice"
|
||||||
|
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
@ -27,28 +28,6 @@ const (
|
|||||||
existingTestLogin = "existing@example.com"
|
existingTestLogin = "existing@example.com"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockAuthInfoService struct {
|
|
||||||
LatestUserID int64
|
|
||||||
ExpectedError error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockAuthInfoService) LookupAndUpdate(ctx context.Context, query *models.GetUserByAuthInfoQuery) (*models.User, error) {
|
|
||||||
m.LatestUserID = query.UserId
|
|
||||||
return nil, m.ExpectedError
|
|
||||||
}
|
|
||||||
func (m *mockAuthInfoService) GetAuthInfo(ctx context.Context, query *models.GetAuthInfoQuery) error {
|
|
||||||
m.LatestUserID = query.UserId
|
|
||||||
return m.ExpectedError
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockAuthInfoService) SetAuthInfo(ctx context.Context, query *models.SetAuthInfoCommand) error {
|
|
||||||
return m.ExpectedError
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockAuthInfoService) UpdateAuthInfo(ctx context.Context, query *models.UpdateAuthInfoCommand) error {
|
|
||||||
return m.ExpectedError
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAdminAPIEndpoint(t *testing.T) {
|
func TestAdminAPIEndpoint(t *testing.T) {
|
||||||
const role = models.ROLE_ADMIN
|
const role = models.ROLE_ADMIN
|
||||||
|
|
||||||
@ -282,7 +261,7 @@ func putAdminScenario(t *testing.T, desc string, url string, routePattern string
|
|||||||
hs := &HTTPServer{
|
hs := &HTTPServer{
|
||||||
Cfg: setting.NewCfg(),
|
Cfg: setting.NewCfg(),
|
||||||
SQLStore: sqlStore,
|
SQLStore: sqlStore,
|
||||||
authInfoService: &mockAuthInfoService{},
|
authInfoService: &logintest.AuthInfoServiceFake{},
|
||||||
}
|
}
|
||||||
|
|
||||||
sc := setupScenarioContext(t, url)
|
sc := setupScenarioContext(t, url)
|
||||||
@ -397,7 +376,7 @@ func adminDisableUserScenario(t *testing.T, desc string, action string, url stri
|
|||||||
|
|
||||||
fakeAuthTokenService := auth.NewFakeUserAuthTokenService()
|
fakeAuthTokenService := auth.NewFakeUserAuthTokenService()
|
||||||
|
|
||||||
authInfoService := &mockAuthInfoService{}
|
authInfoService := &logintest.AuthInfoServiceFake{}
|
||||||
|
|
||||||
hs := HTTPServer{
|
hs := HTTPServer{
|
||||||
Bus: bus.GetBus(),
|
Bus: bus.GetBus(),
|
||||||
@ -435,7 +414,7 @@ func adminDeleteUserScenario(t *testing.T, desc string, url string, routePattern
|
|||||||
|
|
||||||
sc := setupScenarioContext(t, url)
|
sc := setupScenarioContext(t, url)
|
||||||
sc.sqlStore = hs.SQLStore
|
sc.sqlStore = hs.SQLStore
|
||||||
sc.authInfoService = &mockAuthInfoService{}
|
sc.authInfoService = &logintest.AuthInfoServiceFake{}
|
||||||
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||||
sc.context = c
|
sc.context = c
|
||||||
sc.context.UserId = testUserID
|
sc.context.UserId = testUserID
|
||||||
|
@ -34,6 +34,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"github.com/grafana/grafana/pkg/services/ldap"
|
||||||
"github.com/grafana/grafana/pkg/services/login/loginservice"
|
"github.com/grafana/grafana/pkg/services/login/loginservice"
|
||||||
|
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||||
"github.com/grafana/grafana/pkg/services/quota"
|
"github.com/grafana/grafana/pkg/services/quota"
|
||||||
"github.com/grafana/grafana/pkg/services/rendering"
|
"github.com/grafana/grafana/pkg/services/rendering"
|
||||||
"github.com/grafana/grafana/pkg/services/searchusers"
|
"github.com/grafana/grafana/pkg/services/searchusers"
|
||||||
@ -168,7 +169,7 @@ type scenarioContext struct {
|
|||||||
url string
|
url string
|
||||||
userAuthTokenService *auth.FakeUserAuthTokenService
|
userAuthTokenService *auth.FakeUserAuthTokenService
|
||||||
sqlStore sqlstore.Store
|
sqlStore sqlstore.Store
|
||||||
authInfoService *mockAuthInfoService
|
authInfoService *logintest.AuthInfoServiceFake
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *scenarioContext) exec() {
|
func (sc *scenarioContext) exec() {
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
|
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"
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
@ -134,6 +135,7 @@ type HTTPServer struct {
|
|||||||
queryDataService *query.Service
|
queryDataService *query.Service
|
||||||
serviceAccountsService serviceaccounts.Service
|
serviceAccountsService serviceaccounts.Service
|
||||||
authInfoService login.AuthInfoService
|
authInfoService login.AuthInfoService
|
||||||
|
authenticator loginpkg.Authenticator
|
||||||
teamPermissionsService accesscontrol.PermissionsService
|
teamPermissionsService accesscontrol.PermissionsService
|
||||||
permissionServices accesscontrol.PermissionsServices
|
permissionServices accesscontrol.PermissionsServices
|
||||||
NotificationService *notifications.NotificationService
|
NotificationService *notifications.NotificationService
|
||||||
@ -160,7 +162,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
dataSourceCache datasources.CacheService, userTokenService models.UserTokenService,
|
dataSourceCache datasources.CacheService, userTokenService models.UserTokenService,
|
||||||
cleanUpService *cleanup.CleanUpService, shortURLService shorturls.Service, queryHistoryService queryhistory.Service,
|
cleanUpService *cleanup.CleanUpService, shortURLService shorturls.Service, queryHistoryService queryhistory.Service,
|
||||||
thumbService thumbs.Service, remoteCache *remotecache.RemoteCache, provisioningService provisioning.ProvisioningService,
|
thumbService thumbs.Service, remoteCache *remotecache.RemoteCache, provisioningService provisioning.ProvisioningService,
|
||||||
loginService login.Service, accessControl accesscontrol.AccessControl,
|
loginService login.Service, authenticator loginpkg.Authenticator, accessControl accesscontrol.AccessControl,
|
||||||
dataSourceProxy *datasourceproxy.DataSourceProxyService, searchService *search.SearchService,
|
dataSourceProxy *datasourceproxy.DataSourceProxyService, searchService *search.SearchService,
|
||||||
live *live.GrafanaLive, livePushGateway *pushhttp.Gateway, plugCtxProvider *plugincontext.Provider,
|
live *live.GrafanaLive, livePushGateway *pushhttp.Gateway, plugCtxProvider *plugincontext.Provider,
|
||||||
contextHandler *contexthandler.ContextHandler, features *featuremgmt.FeatureManager,
|
contextHandler *contexthandler.ContextHandler, features *featuremgmt.FeatureManager,
|
||||||
@ -236,6 +238,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
queryDataService: queryDataService,
|
queryDataService: queryDataService,
|
||||||
serviceAccountsService: serviceaccountsService,
|
serviceAccountsService: serviceaccountsService,
|
||||||
authInfoService: authInfoService,
|
authInfoService: authInfoService,
|
||||||
|
authenticator: authenticator,
|
||||||
NotificationService: notificationService,
|
NotificationService: notificationService,
|
||||||
dashboardService: dashboardService,
|
dashboardService: dashboardService,
|
||||||
dashboardProvisioningService: dashboardProvisioningService,
|
dashboardProvisioningService: dashboardProvisioningService,
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/login"
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"github.com/grafana/grafana/pkg/services/ldap"
|
||||||
"github.com/grafana/grafana/pkg/services/multildap"
|
"github.com/grafana/grafana/pkg/services/multildap"
|
||||||
@ -199,7 +198,7 @@ func (hs *HTTPServer) PostSyncUserWithLDAP(c *models.ReqContext) response.Respon
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Since the user was not in the LDAP server. Let's disable it.
|
// Since the user was not in the LDAP server. Let's disable it.
|
||||||
err := login.DisableExternalUser(c.Req.Context(), query.Result.Login)
|
err := hs.Login.DisableExternalUser(c.Req.Context(), query.Result.Login)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "Failed to disable the user", err)
|
return response.Error(http.StatusInternalServerError, "Failed to disable the user", err)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
@ -10,12 +9,12 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/login/loginservice"
|
"github.com/grafana/grafana/pkg/services/login/loginservice"
|
||||||
|
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/auth"
|
"github.com/grafana/grafana/pkg/services/auth"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"github.com/grafana/grafana/pkg/services/ldap"
|
||||||
@ -368,6 +367,7 @@ func postSyncUserWithLDAPContext(t *testing.T, requestURL string, preHook func(*
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
sc := setupScenarioContext(t, requestURL)
|
sc := setupScenarioContext(t, requestURL)
|
||||||
|
sc.authInfoService = &logintest.AuthInfoServiceFake{}
|
||||||
|
|
||||||
ldap := setting.LDAPEnabled
|
ldap := setting.LDAPEnabled
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
@ -380,7 +380,7 @@ func postSyncUserWithLDAPContext(t *testing.T, requestURL string, preHook func(*
|
|||||||
AuthTokenService: auth.NewFakeUserAuthTokenService(),
|
AuthTokenService: auth.NewFakeUserAuthTokenService(),
|
||||||
SQLStore: sqlstoremock,
|
SQLStore: sqlstoremock,
|
||||||
Login: loginservice.LoginServiceMock{},
|
Login: loginservice.LoginServiceMock{},
|
||||||
authInfoService: &mockAuthInfoService{},
|
authInfoService: sc.authInfoService,
|
||||||
}
|
}
|
||||||
|
|
||||||
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||||
@ -483,6 +483,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenGrafanaAdmin(t *testing.T) {
|
|||||||
func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotInLDAP(t *testing.T) {
|
func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotInLDAP(t *testing.T) {
|
||||||
sqlstoremock := mockstore.SQLStoreMock{ExpectedUser: &models.User{Login: "ldap-daniel", Id: 34}}
|
sqlstoremock := mockstore.SQLStoreMock{ExpectedUser: &models.User{Login: "ldap-daniel", Id: 34}}
|
||||||
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T, sc *scenarioContext) {
|
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T, sc *scenarioContext) {
|
||||||
|
sc.authInfoService.ExpectedExternalUser = &models.ExternalUserInfo{IsDisabled: true, UserId: 34}
|
||||||
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
||||||
return &ldap.Config{}, nil
|
return &ldap.Config{}, nil
|
||||||
}
|
}
|
||||||
@ -492,18 +493,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotInLDAP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userSearchResult = nil
|
userSearchResult = nil
|
||||||
|
userSearchError = multildap.ErrDidNotFindUser
|
||||||
bus.AddHandler("test", func(ctx context.Context, q *models.GetExternalUserInfoByLoginQuery) error {
|
|
||||||
assert.Equal(t, "ldap-daniel", q.LoginOrEmail)
|
|
||||||
q.Result = &models.ExternalUserInfo{IsDisabled: true, UserId: 34}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
bus.AddHandler("test", func(ctx context.Context, cmd *models.DisableUserCommand) error {
|
|
||||||
assert.Equal(t, 34, cmd.UserId)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}, &sqlstoremock)
|
}, &sqlstoremock)
|
||||||
|
|
||||||
assert.Equal(t, http.StatusBadRequest, sc.resp.Code)
|
assert.Equal(t, http.StatusBadRequest, sc.resp.Code)
|
||||||
@ -616,7 +606,7 @@ func TestLDAP_AccessControl(t *testing.T) {
|
|||||||
cfg.LDAPEnabled = true
|
cfg.LDAPEnabled = true
|
||||||
sc, hs := setupAccessControlScenarioContext(t, cfg, test.url, test.permissions)
|
sc, hs := setupAccessControlScenarioContext(t, cfg, test.url, test.permissions)
|
||||||
hs.SQLStore = &mockstore.SQLStoreMock{ExpectedUser: &models.User{}}
|
hs.SQLStore = &mockstore.SQLStoreMock{ExpectedUser: &models.User{}}
|
||||||
hs.authInfoService = &mockAuthInfoService{}
|
hs.authInfoService = &logintest.AuthInfoServiceFake{}
|
||||||
hs.Login = &loginservice.LoginServiceMock{}
|
hs.Login = &loginservice.LoginServiceMock{}
|
||||||
sc.resp = httptest.NewRecorder()
|
sc.resp = httptest.NewRecorder()
|
||||||
sc.req, err = http.NewRequest(test.method, test.url, nil)
|
sc.req, err = http.NewRequest(test.method, test.url, nil)
|
||||||
|
@ -209,7 +209,7 @@ func (hs *HTTPServer) LoginPost(c *models.ReqContext) response.Response {
|
|||||||
Cfg: hs.Cfg,
|
Cfg: hs.Cfg,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := login.AuthenticateUserFunc(c.Req.Context(), authQuery)
|
err := hs.authenticator.AuthenticateUser(c.Req.Context(), authQuery)
|
||||||
authModule = authQuery.AuthModule
|
authModule = authQuery.AuthModule
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp = response.Error(401, "Invalid username or password", err)
|
resp = response.Error(401, "Invalid username or password", err)
|
||||||
|
@ -351,8 +351,7 @@ func TestLoginPostRedirect(t *testing.T) {
|
|||||||
Email: "",
|
Email: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
mockAuthenticateUserFunc(user, "", nil)
|
hs.authenticator = &fakeAuthenticator{user, "", nil}
|
||||||
t.Cleanup(resetAuthenticateUserFunc)
|
|
||||||
|
|
||||||
redirectCases := []redirectCase{
|
redirectCases := []redirectCase{
|
||||||
{
|
{
|
||||||
@ -684,8 +683,7 @@ func TestLoginPostRunLokingHook(t *testing.T) {
|
|||||||
|
|
||||||
for _, c := range testCases {
|
for _, c := range testCases {
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
t.Run(c.desc, func(t *testing.T) {
|
||||||
mockAuthenticateUserFunc(c.authUser, c.authModule, c.authErr)
|
hs.authenticator = &fakeAuthenticator{c.authUser, c.authModule, c.authErr}
|
||||||
t.Cleanup(resetAuthenticateUserFunc)
|
|
||||||
sc.m.Post(sc.url, sc.defaultHandler)
|
sc.m.Post(sc.url, sc.defaultHandler)
|
||||||
sc.fakeReqNoAssertions("POST", sc.url).exec()
|
sc.fakeReqNoAssertions("POST", sc.url).exec()
|
||||||
|
|
||||||
@ -732,13 +730,14 @@ func (m *mockSocialService) GetConnector(string) (social.SocialConnector, error)
|
|||||||
return m.socialConnector, m.err
|
return m.socialConnector, m.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func mockAuthenticateUserFunc(user *models.User, authmodule string, err error) {
|
type fakeAuthenticator struct {
|
||||||
login.AuthenticateUserFunc = func(ctx context.Context, query *models.LoginUserQuery) error {
|
ExpectedUser *models.User
|
||||||
query.User = user
|
ExpectedAuthModule string
|
||||||
query.AuthModule = authmodule
|
ExpectedError error
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
func resetAuthenticateUserFunc() {
|
|
||||||
login.AuthenticateUserFunc = login.AuthenticateUser
|
func (fa *fakeAuthenticator) AuthenticateUser(c context.Context, query *models.LoginUserQuery) error {
|
||||||
|
query.User = fa.ExpectedUser
|
||||||
|
query.AuthModule = fa.ExpectedAuthModule
|
||||||
|
return fa.ExpectedError
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"github.com/grafana/grafana/pkg/services/ldap"
|
||||||
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -25,14 +27,26 @@ var (
|
|||||||
|
|
||||||
var loginLogger = log.New("login")
|
var loginLogger = log.New("login")
|
||||||
|
|
||||||
var AuthenticateUserFunc = AuthenticateUser
|
type Authenticator interface {
|
||||||
|
AuthenticateUser(context.Context, *models.LoginUserQuery) error
|
||||||
|
}
|
||||||
|
|
||||||
func Init() {
|
type AuthenticatorService struct {
|
||||||
bus.AddHandler("auth", AuthenticateUser)
|
store sqlstore.Store
|
||||||
|
loginService login.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideService(store sqlstore.Store, loginService login.Service) *AuthenticatorService {
|
||||||
|
a := &AuthenticatorService{
|
||||||
|
store: store,
|
||||||
|
loginService: loginService,
|
||||||
|
}
|
||||||
|
bus.AddHandler("auth", a.AuthenticateUser)
|
||||||
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthenticateUser authenticates the user via username & password
|
// AuthenticateUser authenticates the user via username & password
|
||||||
func AuthenticateUser(ctx context.Context, query *models.LoginUserQuery) error {
|
func (a *AuthenticatorService) AuthenticateUser(ctx context.Context, query *models.LoginUserQuery) error {
|
||||||
if err := validateLoginAttempts(ctx, query); err != nil {
|
if err := validateLoginAttempts(ctx, query); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -48,7 +62,7 @@ func AuthenticateUser(ctx context.Context, query *models.LoginUserQuery) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ldapEnabled, ldapErr := loginUsingLDAP(ctx, query)
|
ldapEnabled, ldapErr := loginUsingLDAP(ctx, query, a.loginService)
|
||||||
if ldapEnabled {
|
if ldapEnabled {
|
||||||
query.AuthModule = models.AuthModuleLDAP
|
query.AuthModule = models.AuthModuleLDAP
|
||||||
if ldapErr == nil || !errors.Is(ldapErr, ldap.ErrInvalidCredentials) {
|
if ldapErr == nil || !errors.Is(ldapErr, ldap.ErrInvalidCredentials) {
|
||||||
|
@ -7,6 +7,9 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"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/sqlstore/mockstore"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@ -21,7 +24,8 @@ func TestAuthenticateUser(t *testing.T) {
|
|||||||
Username: "user",
|
Username: "user",
|
||||||
Password: "",
|
Password: "",
|
||||||
}
|
}
|
||||||
err := AuthenticateUserFunc(context.Background(), &loginQuery)
|
a := AuthenticatorService{store: mockstore.NewSQLStoreMock(), loginService: &logintest.LoginServiceFake{}}
|
||||||
|
err := a.AuthenticateUser(context.Background(), &loginQuery)
|
||||||
|
|
||||||
require.EqualError(t, err, ErrPasswordEmpty.Error())
|
require.EqualError(t, err, ErrPasswordEmpty.Error())
|
||||||
assert.False(t, sc.grafanaLoginWasCalled)
|
assert.False(t, sc.grafanaLoginWasCalled)
|
||||||
@ -35,7 +39,8 @@ func TestAuthenticateUser(t *testing.T) {
|
|||||||
mockLoginUsingLDAP(true, nil, sc)
|
mockLoginUsingLDAP(true, nil, sc)
|
||||||
mockSaveInvalidLoginAttempt(sc)
|
mockSaveInvalidLoginAttempt(sc)
|
||||||
|
|
||||||
err := AuthenticateUserFunc(context.Background(), sc.loginUserQuery)
|
a := AuthenticatorService{store: mockstore.NewSQLStoreMock(), loginService: &logintest.LoginServiceFake{}}
|
||||||
|
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||||
|
|
||||||
require.EqualError(t, err, ErrTooManyLoginAttempts.Error())
|
require.EqualError(t, err, ErrTooManyLoginAttempts.Error())
|
||||||
assert.True(t, sc.loginAttemptValidationWasCalled)
|
assert.True(t, sc.loginAttemptValidationWasCalled)
|
||||||
@ -51,7 +56,8 @@ func TestAuthenticateUser(t *testing.T) {
|
|||||||
mockLoginUsingLDAP(true, ErrInvalidCredentials, sc)
|
mockLoginUsingLDAP(true, ErrInvalidCredentials, sc)
|
||||||
mockSaveInvalidLoginAttempt(sc)
|
mockSaveInvalidLoginAttempt(sc)
|
||||||
|
|
||||||
err := AuthenticateUserFunc(context.Background(), sc.loginUserQuery)
|
a := AuthenticatorService{store: mockstore.NewSQLStoreMock(), loginService: &logintest.LoginServiceFake{}}
|
||||||
|
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.True(t, sc.loginAttemptValidationWasCalled)
|
assert.True(t, sc.loginAttemptValidationWasCalled)
|
||||||
@ -68,7 +74,8 @@ func TestAuthenticateUser(t *testing.T) {
|
|||||||
mockLoginUsingLDAP(true, ErrInvalidCredentials, sc)
|
mockLoginUsingLDAP(true, ErrInvalidCredentials, sc)
|
||||||
mockSaveInvalidLoginAttempt(sc)
|
mockSaveInvalidLoginAttempt(sc)
|
||||||
|
|
||||||
err := AuthenticateUserFunc(context.Background(), sc.loginUserQuery)
|
a := AuthenticatorService{store: mockstore.NewSQLStoreMock(), loginService: &logintest.LoginServiceFake{}}
|
||||||
|
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||||
|
|
||||||
require.EqualError(t, err, customErr.Error())
|
require.EqualError(t, err, customErr.Error())
|
||||||
assert.True(t, sc.loginAttemptValidationWasCalled)
|
assert.True(t, sc.loginAttemptValidationWasCalled)
|
||||||
@ -84,7 +91,8 @@ func TestAuthenticateUser(t *testing.T) {
|
|||||||
mockLoginUsingLDAP(false, nil, sc)
|
mockLoginUsingLDAP(false, nil, sc)
|
||||||
mockSaveInvalidLoginAttempt(sc)
|
mockSaveInvalidLoginAttempt(sc)
|
||||||
|
|
||||||
err := AuthenticateUserFunc(context.Background(), sc.loginUserQuery)
|
a := AuthenticatorService{store: mockstore.NewSQLStoreMock(), loginService: &logintest.LoginServiceFake{}}
|
||||||
|
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||||
|
|
||||||
require.EqualError(t, err, models.ErrUserNotFound.Error())
|
require.EqualError(t, err, models.ErrUserNotFound.Error())
|
||||||
assert.True(t, sc.loginAttemptValidationWasCalled)
|
assert.True(t, sc.loginAttemptValidationWasCalled)
|
||||||
@ -100,7 +108,8 @@ func TestAuthenticateUser(t *testing.T) {
|
|||||||
mockLoginUsingLDAP(true, ldap.ErrInvalidCredentials, sc)
|
mockLoginUsingLDAP(true, ldap.ErrInvalidCredentials, sc)
|
||||||
mockSaveInvalidLoginAttempt(sc)
|
mockSaveInvalidLoginAttempt(sc)
|
||||||
|
|
||||||
err := AuthenticateUserFunc(context.Background(), sc.loginUserQuery)
|
a := AuthenticatorService{store: mockstore.NewSQLStoreMock(), loginService: &logintest.LoginServiceFake{}}
|
||||||
|
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||||
|
|
||||||
require.EqualError(t, err, ErrInvalidCredentials.Error())
|
require.EqualError(t, err, ErrInvalidCredentials.Error())
|
||||||
assert.True(t, sc.loginAttemptValidationWasCalled)
|
assert.True(t, sc.loginAttemptValidationWasCalled)
|
||||||
@ -116,7 +125,8 @@ func TestAuthenticateUser(t *testing.T) {
|
|||||||
mockLoginUsingLDAP(true, nil, sc)
|
mockLoginUsingLDAP(true, nil, sc)
|
||||||
mockSaveInvalidLoginAttempt(sc)
|
mockSaveInvalidLoginAttempt(sc)
|
||||||
|
|
||||||
err := AuthenticateUserFunc(context.Background(), sc.loginUserQuery)
|
a := AuthenticatorService{store: mockstore.NewSQLStoreMock(), loginService: &logintest.LoginServiceFake{}}
|
||||||
|
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.True(t, sc.loginAttemptValidationWasCalled)
|
assert.True(t, sc.loginAttemptValidationWasCalled)
|
||||||
@ -133,7 +143,8 @@ func TestAuthenticateUser(t *testing.T) {
|
|||||||
mockLoginUsingLDAP(true, customErr, sc)
|
mockLoginUsingLDAP(true, customErr, sc)
|
||||||
mockSaveInvalidLoginAttempt(sc)
|
mockSaveInvalidLoginAttempt(sc)
|
||||||
|
|
||||||
err := AuthenticateUserFunc(context.Background(), sc.loginUserQuery)
|
a := AuthenticatorService{store: mockstore.NewSQLStoreMock(), loginService: &logintest.LoginServiceFake{}}
|
||||||
|
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||||
|
|
||||||
require.EqualError(t, err, customErr.Error())
|
require.EqualError(t, err, customErr.Error())
|
||||||
assert.True(t, sc.loginAttemptValidationWasCalled)
|
assert.True(t, sc.loginAttemptValidationWasCalled)
|
||||||
@ -149,7 +160,8 @@ func TestAuthenticateUser(t *testing.T) {
|
|||||||
mockLoginUsingLDAP(true, ldap.ErrInvalidCredentials, sc)
|
mockLoginUsingLDAP(true, ldap.ErrInvalidCredentials, sc)
|
||||||
mockSaveInvalidLoginAttempt(sc)
|
mockSaveInvalidLoginAttempt(sc)
|
||||||
|
|
||||||
err := AuthenticateUserFunc(context.Background(), sc.loginUserQuery)
|
a := AuthenticatorService{store: mockstore.NewSQLStoreMock(), loginService: &logintest.LoginServiceFake{}}
|
||||||
|
err := a.AuthenticateUser(context.Background(), sc.loginUserQuery)
|
||||||
|
|
||||||
require.EqualError(t, err, ErrInvalidCredentials.Error())
|
require.EqualError(t, err, ErrInvalidCredentials.Error())
|
||||||
assert.True(t, sc.loginAttemptValidationWasCalled)
|
assert.True(t, sc.loginAttemptValidationWasCalled)
|
||||||
@ -177,7 +189,7 @@ func mockLoginUsingGrafanaDB(err error, sc *authScenarioContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mockLoginUsingLDAP(enabled bool, err error, sc *authScenarioContext) {
|
func mockLoginUsingLDAP(enabled bool, err error, sc *authScenarioContext) {
|
||||||
loginUsingLDAP = func(ctx context.Context, query *models.LoginUserQuery) (bool, error) {
|
loginUsingLDAP = func(ctx context.Context, query *models.LoginUserQuery, _ login.Service) (bool, error) {
|
||||||
sc.ldapLoginWasCalled = true
|
sc.ldapLoginWasCalled = true
|
||||||
return enabled, err
|
return enabled, err
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"github.com/grafana/grafana/pkg/services/ldap"
|
||||||
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
"github.com/grafana/grafana/pkg/services/multildap"
|
"github.com/grafana/grafana/pkg/services/multildap"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
@ -27,7 +28,7 @@ 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
|
// 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.
|
// populated with the logged in user if successful.
|
||||||
var loginUsingLDAP = func(ctx context.Context, query *models.LoginUserQuery) (bool, error) {
|
var loginUsingLDAP = func(ctx context.Context, query *models.LoginUserQuery, loginService login.Service) (bool, error) {
|
||||||
enabled := isLDAPEnabled()
|
enabled := isLDAPEnabled()
|
||||||
|
|
||||||
if !enabled {
|
if !enabled {
|
||||||
@ -43,7 +44,7 @@ var loginUsingLDAP = func(ctx context.Context, query *models.LoginUserQuery) (bo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, ldap.ErrCouldNotFindUser) {
|
if errors.Is(err, ldap.ErrCouldNotFindUser) {
|
||||||
// Ignore the error since user might not be present anyway
|
// Ignore the error since user might not be present anyway
|
||||||
if err := DisableExternalUser(ctx, query.Username); err != nil {
|
if err := loginService.DisableExternalUser(ctx, query.Username); err != nil {
|
||||||
ldapLogger.Debug("Failed to disable external user", "err", err)
|
ldapLogger.Debug("Failed to disable external user", "err", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,42 +67,3 @@ var loginUsingLDAP = func(ctx context.Context, query *models.LoginUserQuery) (bo
|
|||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisableExternalUser marks external user as disabled in Grafana db
|
|
||||||
func DisableExternalUser(ctx context.Context, username string) error {
|
|
||||||
// Check if external user exist in Grafana
|
|
||||||
userQuery := &models.GetExternalUserInfoByLoginQuery{
|
|
||||||
LoginOrEmail: username,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bus.Dispatch(ctx, userQuery); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
userInfo := userQuery.Result
|
|
||||||
if !userInfo.IsDisabled {
|
|
||||||
ldapLogger.Debug(
|
|
||||||
"Disabling external user",
|
|
||||||
"user",
|
|
||||||
userQuery.Result.Login,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Mark user as disabled in grafana db
|
|
||||||
disableUserCmd := &models.DisableUserCommand{
|
|
||||||
UserId: userQuery.Result.UserId,
|
|
||||||
IsDisabled: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bus.Dispatch(ctx, disableUserCmd); err != nil {
|
|
||||||
ldapLogger.Debug(
|
|
||||||
"Error disabling external user",
|
|
||||||
"user",
|
|
||||||
userQuery.Result.Login,
|
|
||||||
"message",
|
|
||||||
err.Error(),
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"github.com/grafana/grafana/pkg/services/ldap"
|
||||||
|
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||||
"github.com/grafana/grafana/pkg/services/multildap"
|
"github.com/grafana/grafana/pkg/services/multildap"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -28,7 +29,8 @@ func TestLoginUsingLDAP(t *testing.T) {
|
|||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
enabled, err := loginUsingLDAP(context.Background(), sc.loginUserQuery)
|
loginService := &logintest.LoginServiceFake{}
|
||||||
|
enabled, err := loginUsingLDAP(context.Background(), sc.loginUserQuery, loginService)
|
||||||
require.EqualError(t, err, errTest.Error())
|
require.EqualError(t, err, errTest.Error())
|
||||||
|
|
||||||
assert.True(t, enabled)
|
assert.True(t, enabled)
|
||||||
@ -39,7 +41,8 @@ func TestLoginUsingLDAP(t *testing.T) {
|
|||||||
setting.LDAPEnabled = false
|
setting.LDAPEnabled = false
|
||||||
|
|
||||||
sc.withLoginResult(false)
|
sc.withLoginResult(false)
|
||||||
enabled, err := loginUsingLDAP(context.Background(), sc.loginUserQuery)
|
loginService := &logintest.LoginServiceFake{}
|
||||||
|
enabled, err := loginUsingLDAP(context.Background(), sc.loginUserQuery, loginService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.False(t, enabled)
|
assert.False(t, enabled)
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/login"
|
"github.com/grafana/grafana/pkg/login"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/contexthandler"
|
"github.com/grafana/grafana/pkg/services/contexthandler"
|
||||||
|
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -78,7 +79,7 @@ func TestMiddlewareBasicAuth(t *testing.T) {
|
|||||||
const password = "MyPass"
|
const password = "MyPass"
|
||||||
const salt = "Salt"
|
const salt = "Salt"
|
||||||
|
|
||||||
login.Init()
|
login.ProvideService(sc.sqlStore, &logintest.LoginServiceFake{})
|
||||||
|
|
||||||
bus.AddHandler("user-query", func(ctx context.Context, query *models.GetUserByLoginQuery) error {
|
bus.AddHandler("user-query", func(ctx context.Context, query *models.GetUserByLoginQuery) error {
|
||||||
encoded, err := util.EncodePassword(password, salt)
|
encoded, err := util.EncodePassword(password, salt)
|
||||||
|
@ -118,7 +118,7 @@ func (s *Server) init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
login.Init()
|
login.ProvideService(s.HTTPServer.SQLStore, s.HTTPServer.Login)
|
||||||
social.ProvideService(s.cfg)
|
social.ProvideService(s.cfg)
|
||||||
|
|
||||||
if err := s.roleRegistry.RegisterFixedRoles(); err != nil {
|
if err := s.roleRegistry.RegisterFixedRoles(); err != nil {
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||||
uss "github.com/grafana/grafana/pkg/infra/usagestats/service"
|
uss "github.com/grafana/grafana/pkg/infra/usagestats/service"
|
||||||
|
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/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
@ -159,6 +160,8 @@ var wireBasicSet = wire.NewSet(
|
|||||||
wire.Bind(new(login.AuthInfoService), new(*authinfoservice.Implementation)),
|
wire.Bind(new(login.AuthInfoService), new(*authinfoservice.Implementation)),
|
||||||
authinfodatabase.ProvideAuthInfoStore,
|
authinfodatabase.ProvideAuthInfoStore,
|
||||||
wire.Bind(new(login.Store), new(*authinfodatabase.AuthInfoStore)),
|
wire.Bind(new(login.Store), new(*authinfodatabase.AuthInfoStore)),
|
||||||
|
loginpkg.ProvideService,
|
||||||
|
wire.Bind(new(loginpkg.Authenticator), new(*loginpkg.AuthenticatorService)),
|
||||||
datasourceproxy.ProvideService,
|
datasourceproxy.ProvideService,
|
||||||
search.ProvideService,
|
search.ProvideService,
|
||||||
searchV2.ProvideService,
|
searchV2.ProvideService,
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
type AuthInfoService interface {
|
type AuthInfoService interface {
|
||||||
LookupAndUpdate(ctx context.Context, query *models.GetUserByAuthInfoQuery) (*models.User, error)
|
LookupAndUpdate(ctx context.Context, query *models.GetUserByAuthInfoQuery) (*models.User, error)
|
||||||
GetAuthInfo(ctx context.Context, query *models.GetAuthInfoQuery) error
|
GetAuthInfo(ctx context.Context, query *models.GetAuthInfoQuery) error
|
||||||
|
GetExternalUserInfoByLogin(ctx context.Context, query *models.GetExternalUserInfoByLoginQuery) error
|
||||||
SetAuthInfo(ctx context.Context, cmd *models.SetAuthInfoCommand) error
|
SetAuthInfo(ctx context.Context, cmd *models.SetAuthInfoCommand) error
|
||||||
UpdateAuthInfo(ctx context.Context, cmd *models.UpdateAuthInfoCommand) error
|
UpdateAuthInfo(ctx context.Context, cmd *models.UpdateAuthInfoCommand) error
|
||||||
}
|
}
|
||||||
|
@ -183,3 +183,7 @@ func (s *Implementation) UpdateAuthInfo(ctx context.Context, cmd *models.UpdateA
|
|||||||
func (s *Implementation) SetAuthInfo(ctx context.Context, cmd *models.SetAuthInfoCommand) error {
|
func (s *Implementation) SetAuthInfo(ctx context.Context, cmd *models.SetAuthInfoCommand) error {
|
||||||
return s.authInfoStore.SetAuthInfo(ctx, cmd)
|
return s.authInfoStore.SetAuthInfo(ctx, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Implementation) GetExternalUserInfoByLogin(ctx context.Context, query *models.GetExternalUserInfoByLoginQuery) error {
|
||||||
|
return s.authInfoStore.GetExternalUserInfoByLogin(ctx, query)
|
||||||
|
}
|
||||||
|
@ -18,5 +18,6 @@ type TeamSyncFunc func(user *models.User, externalUser *models.ExternalUserInfo)
|
|||||||
type Service interface {
|
type Service interface {
|
||||||
CreateUser(cmd models.CreateUserCommand) (*models.User, error)
|
CreateUser(cmd models.CreateUserCommand) (*models.User, error)
|
||||||
UpsertUser(ctx context.Context, cmd *models.UpsertUserCommand) error
|
UpsertUser(ctx context.Context, cmd *models.UpsertUserCommand) error
|
||||||
|
DisableExternalUser(ctx context.Context, username string) error
|
||||||
SetTeamSyncFunc(TeamSyncFunc)
|
SetTeamSyncFunc(TeamSyncFunc)
|
||||||
}
|
}
|
||||||
|
@ -130,6 +130,46 @@ func (ls *Implementation) UpsertUser(ctx context.Context, cmd *models.UpsertUser
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ls *Implementation) DisableExternalUser(ctx context.Context, username string) error {
|
||||||
|
// Check if external user exist in Grafana
|
||||||
|
userQuery := &models.GetExternalUserInfoByLoginQuery{
|
||||||
|
LoginOrEmail: username,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ls.AuthInfoService.GetExternalUserInfoByLogin(ctx, userQuery); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
userInfo := userQuery.Result
|
||||||
|
if userInfo.IsDisabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug(
|
||||||
|
"Disabling external user",
|
||||||
|
"user",
|
||||||
|
userQuery.Result.Login,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mark user as disabled in grafana db
|
||||||
|
disableUserCmd := &models.DisableUserCommand{
|
||||||
|
UserId: userQuery.Result.UserId,
|
||||||
|
IsDisabled: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ls.SQLStore.DisableUser(ctx, disableUserCmd); err != nil {
|
||||||
|
logger.Debug(
|
||||||
|
"Error disabling external user",
|
||||||
|
"user",
|
||||||
|
userQuery.Result.Login,
|
||||||
|
"message",
|
||||||
|
err.Error(),
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetTeamSyncFunc sets the function received through args as the team sync function.
|
// SetTeamSyncFunc sets the function received through args as the team sync function.
|
||||||
func (ls *Implementation) SetTeamSyncFunc(teamSyncFunc login.TeamSyncFunc) {
|
func (ls *Implementation) SetTeamSyncFunc(teamSyncFunc login.TeamSyncFunc) {
|
||||||
ls.TeamSync = teamSyncFunc
|
ls.TeamSync = teamSyncFunc
|
||||||
|
@ -45,3 +45,7 @@ func (s LoginServiceMock) UpsertUser(ctx context.Context, cmd *models.UpsertUser
|
|||||||
cmd.Result = s.ExpectedUser
|
cmd.Result = s.ExpectedUser
|
||||||
return s.ExpectedError
|
return s.ExpectedError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s LoginServiceMock) DisableExternalUser(ctx context.Context, username string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/infra/log/level"
|
"github.com/grafana/grafana/pkg/infra/log/level"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||||
"github.com/grafana/grafana/pkg/services/quota"
|
"github.com/grafana/grafana/pkg/services/quota"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -19,7 +20,7 @@ import (
|
|||||||
func Test_syncOrgRoles_doesNotBreakWhenTryingToRemoveLastOrgAdmin(t *testing.T) {
|
func Test_syncOrgRoles_doesNotBreakWhenTryingToRemoveLastOrgAdmin(t *testing.T) {
|
||||||
user := createSimpleUser()
|
user := createSimpleUser()
|
||||||
externalUser := createSimpleExternalUser()
|
externalUser := createSimpleExternalUser()
|
||||||
authInfoMock := &authInfoServiceMock{}
|
authInfoMock := &logintest.AuthInfoServiceFake{}
|
||||||
|
|
||||||
store := &mockstore.SQLStoreMock{
|
store := &mockstore.SQLStoreMock{
|
||||||
ExpectedUserOrgList: createUserOrgDTO(),
|
ExpectedUserOrgList: createUserOrgDTO(),
|
||||||
@ -44,7 +45,7 @@ func Test_syncOrgRoles_whenTryingToRemoveLastOrgLogsError(t *testing.T) {
|
|||||||
user := createSimpleUser()
|
user := createSimpleUser()
|
||||||
externalUser := createSimpleExternalUser()
|
externalUser := createSimpleExternalUser()
|
||||||
|
|
||||||
authInfoMock := &authInfoServiceMock{}
|
authInfoMock := &logintest.AuthInfoServiceFake{}
|
||||||
|
|
||||||
store := &mockstore.SQLStoreMock{
|
store := &mockstore.SQLStoreMock{
|
||||||
ExpectedUserOrgList: createUserOrgDTO(),
|
ExpectedUserOrgList: createUserOrgDTO(),
|
||||||
@ -63,29 +64,8 @@ func Test_syncOrgRoles_whenTryingToRemoveLastOrgLogsError(t *testing.T) {
|
|||||||
assert.Contains(t, buf.String(), models.ErrLastOrgAdmin.Error())
|
assert.Contains(t, buf.String(), models.ErrLastOrgAdmin.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
type authInfoServiceMock struct {
|
|
||||||
user *models.User
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authInfoServiceMock) LookupAndUpdate(ctx context.Context, query *models.GetUserByAuthInfoQuery) (*models.User, error) {
|
|
||||||
return a.user, a.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authInfoServiceMock) GetAuthInfo(ctx context.Context, query *models.GetAuthInfoQuery) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authInfoServiceMock) SetAuthInfo(ctx context.Context, cmd *models.SetAuthInfoCommand) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authInfoServiceMock) UpdateAuthInfo(ctx context.Context, cmd *models.UpdateAuthInfoCommand) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_teamSync(t *testing.T) {
|
func Test_teamSync(t *testing.T) {
|
||||||
authInfoMock := &authInfoServiceMock{}
|
authInfoMock := &logintest.AuthInfoServiceFake{}
|
||||||
login := Implementation{
|
login := Implementation{
|
||||||
Bus: bus.New(),
|
Bus: bus.New(),
|
||||||
QuotaService: "a.QuotaService{},
|
QuotaService: "a.QuotaService{},
|
||||||
@ -99,7 +79,7 @@ func Test_teamSync(t *testing.T) {
|
|||||||
Name: "test_user",
|
Name: "test_user",
|
||||||
Login: "test_user",
|
Login: "test_user",
|
||||||
}
|
}
|
||||||
authInfoMock.user = expectedUser
|
authInfoMock.ExpectedUser = expectedUser
|
||||||
bus.ClearBusHandlers()
|
bus.ClearBusHandlers()
|
||||||
t.Cleanup(func() { bus.ClearBusHandlers() })
|
t.Cleanup(func() { bus.ClearBusHandlers() })
|
||||||
|
|
||||||
|
51
pkg/services/login/logintest/logintest.go
Normal file
51
pkg/services/login/logintest/logintest.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package logintest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoginServiceFake struct{}
|
||||||
|
|
||||||
|
func (l *LoginServiceFake) CreateUser(cmd models.CreateUserCommand) (*models.User, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (l *LoginServiceFake) UpsertUser(ctx context.Context, cmd *models.UpsertUserCommand) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (l *LoginServiceFake) DisableExternalUser(ctx context.Context, username string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (l *LoginServiceFake) SetTeamSyncFunc(login.TeamSyncFunc) {}
|
||||||
|
|
||||||
|
type AuthInfoServiceFake struct {
|
||||||
|
LatestUserID int64
|
||||||
|
ExpectedUser *models.User
|
||||||
|
ExpectedExternalUser *models.ExternalUserInfo
|
||||||
|
ExpectedError error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthInfoServiceFake) LookupAndUpdate(ctx context.Context, query *models.GetUserByAuthInfoQuery) (*models.User, error) {
|
||||||
|
a.LatestUserID = query.UserId
|
||||||
|
return a.ExpectedUser, a.ExpectedError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthInfoServiceFake) GetAuthInfo(ctx context.Context, query *models.GetAuthInfoQuery) error {
|
||||||
|
a.LatestUserID = query.UserId
|
||||||
|
return a.ExpectedError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthInfoServiceFake) SetAuthInfo(ctx context.Context, cmd *models.SetAuthInfoCommand) error {
|
||||||
|
return a.ExpectedError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthInfoServiceFake) UpdateAuthInfo(ctx context.Context, cmd *models.UpdateAuthInfoCommand) error {
|
||||||
|
return a.ExpectedError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthInfoServiceFake) GetExternalUserInfoByLogin(ctx context.Context, query *models.GetExternalUserInfoByLoginQuery) error {
|
||||||
|
query.Result = a.ExpectedExternalUser
|
||||||
|
return a.ExpectedError
|
||||||
|
}
|
Reference in New Issue
Block a user