UserService: use the UserService instead of calling sqlstore directly (#55745)

* UserService: update callers to use the UserService instead of calling sqlstore directly

There is one major change hiding in this PR. UserService.Delete originally called a number of services to delete user-related records. I moved everything except the actual call to the user table, and moved those into the API. This was done to avoid dependencies cycles; many of our services depend on the user service, so the user service itself should have as few dependencies as possible.
This commit is contained in:
Kristin Laemmert
2022-09-27 07:58:49 -04:00
committed by GitHub
parent 4f6c2d35c2
commit 701f6d5436
33 changed files with 277 additions and 360 deletions

View File

@ -6,10 +6,13 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"golang.org/x/sync/errgroup"
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
@ -156,7 +159,7 @@ func (hs *HTTPServer) AdminUpdateUserPermissions(c *models.ReqContext) response.
return response.Error(http.StatusBadRequest, "id is invalid", err) return response.Error(http.StatusBadRequest, "id is invalid", err)
} }
err = hs.SQLStore.UpdateUserPermissions(userID, form.IsGrafanaAdmin) err = hs.userService.UpdatePermissions(userID, form.IsGrafanaAdmin)
if err != nil { if err != nil {
if errors.Is(err, user.ErrLastGrafanaAdmin) { if errors.Is(err, user.ErrLastGrafanaAdmin) {
return response.Error(400, user.ErrLastGrafanaAdmin.Error(), nil) return response.Error(400, user.ErrLastGrafanaAdmin.Error(), nil)
@ -198,6 +201,65 @@ func (hs *HTTPServer) AdminDeleteUser(c *models.ReqContext) response.Response {
return response.Error(500, "Failed to delete user", err) return response.Error(500, "Failed to delete user", err)
} }
g, ctx := errgroup.WithContext(c.Req.Context())
g.Go(func() error {
if err := hs.starService.DeleteByUser(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := hs.orgService.DeleteUserFromAll(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := hs.DashboardService.DeleteACLByUser(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := hs.preferenceService.DeleteByUser(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := hs.teamGuardian.DeleteByUser(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := hs.userAuthService.Delete(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := hs.userAuthService.DeleteToken(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := hs.QuotaService.DeleteByUser(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := hs.accesscontrolService.DeleteUserPermissions(ctx, accesscontrol.GlobalOrgID, cmd.UserID); err != nil {
return err
}
return nil
})
if err := g.Wait(); err != nil {
return response.Error(500, "Failed to delete user", err)
}
return response.Success("User deleted") return response.Success("User deleted")
} }

View File

@ -37,14 +37,12 @@ func TestAdminAPIEndpoint(t *testing.T) {
updateCmd := dtos.AdminUpdateUserPermissionsForm{ updateCmd := dtos.AdminUpdateUserPermissionsForm{
IsGrafanaAdmin: false, IsGrafanaAdmin: false,
} }
mock := &mockstore.SQLStoreMock{ userService := usertest.FakeUserService{ExpectedError: user.ErrLastGrafanaAdmin}
ExpectedError: user.ErrLastGrafanaAdmin,
}
putAdminScenario(t, "When calling PUT on", "/api/admin/users/1/permissions", putAdminScenario(t, "When calling PUT on", "/api/admin/users/1/permissions",
"/api/admin/users/:id/permissions", role, updateCmd, func(sc *scenarioContext) { "/api/admin/users/:id/permissions", role, updateCmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec() sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
assert.Equal(t, 400, sc.resp.Code) assert.Equal(t, 400, sc.resp.Code)
}, mock) }, nil, &userService)
}) })
t.Run("When a server admin attempts to logout himself from all devices", func(t *testing.T) { t.Run("When a server admin attempts to logout himself from all devices", func(t *testing.T) {
@ -235,12 +233,13 @@ func TestAdminAPIEndpoint(t *testing.T) {
} }
func putAdminScenario(t *testing.T, desc string, url string, routePattern string, role org.RoleType, func putAdminScenario(t *testing.T, desc string, url string, routePattern string, role org.RoleType,
cmd dtos.AdminUpdateUserPermissionsForm, fn scenarioFunc, sqlStore sqlstore.Store) { cmd dtos.AdminUpdateUserPermissionsForm, fn scenarioFunc, sqlStore sqlstore.Store, userSvc user.Service) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) { t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
hs := &HTTPServer{ hs := &HTTPServer{
Cfg: setting.NewCfg(), Cfg: setting.NewCfg(),
SQLStore: sqlStore, SQLStore: sqlStore,
authInfoService: &logintest.AuthInfoServiceFake{}, authInfoService: &logintest.AuthInfoServiceFake{},
userService: userSvc,
} }
sc := setupScenarioContext(t, url) sc := setupScenarioContext(t, url)

View File

@ -56,6 +56,7 @@ import (
"github.com/grafana/grafana/pkg/services/team/teamimpl" "github.com/grafana/grafana/pkg/services/team/teamimpl"
"github.com/grafana/grafana/pkg/services/team/teamtest" "github.com/grafana/grafana/pkg/services/team/teamtest"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/userimpl"
"github.com/grafana/grafana/pkg/services/user/usertest" "github.com/grafana/grafana/pkg/services/user/usertest"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
@ -209,7 +210,7 @@ func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHa
renderSvc := &fakeRenderService{} renderSvc := &fakeRenderService{}
authJWTSvc := models.NewFakeJWTService() authJWTSvc := models.NewFakeJWTService()
tracer := tracing.InitializeTracerForTest() tracer := tracing.InitializeTracerForTest()
authProxy := authproxy.ProvideAuthProxy(cfg, remoteCacheSvc, loginservice.LoginServiceMock{}, sqlStore) authProxy := authproxy.ProvideAuthProxy(cfg, remoteCacheSvc, loginservice.LoginServiceMock{}, &usertest.FakeUserService{}, sqlStore)
loginService := &logintest.LoginServiceFake{} loginService := &logintest.LoginServiceFake{}
authenticator := &logintest.AuthenticatorFake{} authenticator := &logintest.AuthenticatorFake{}
ctxHdlr := contexthandler.ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, sqlStore, tracer, authProxy, loginService, nil, authenticator, usertest.NewUserServiceFake()) ctxHdlr := contexthandler.ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, sqlStore, tracer, authProxy, loginService, nil, authenticator, usertest.NewUserServiceFake())
@ -292,8 +293,6 @@ type accessControlScenarioContext struct {
// acmock is an accesscontrol mock used to fake users rights. // acmock is an accesscontrol mock used to fake users rights.
acmock *accesscontrolmock.Mock acmock *accesscontrolmock.Mock
usermock *usertest.FakeUserService
// db is a test database initialized with InitTestDB // db is a test database initialized with InitTestDB
db *sqlstore.SQLStore db *sqlstore.SQLStore
@ -302,6 +301,7 @@ type accessControlScenarioContext struct {
dashboardsStore dashboards.Store dashboardsStore dashboards.Store
teamService team.Service teamService team.Service
userService user.Service
} }
func setAccessControlPermissions(acmock *accesscontrolmock.Mock, perms []accesscontrol.Permission, org int64) { func setAccessControlPermissions(acmock *accesscontrolmock.Mock, perms []accesscontrol.Permission, org int64) {
@ -378,6 +378,8 @@ func setupHTTPServerWithCfgDb(
var ac accesscontrol.AccessControl var ac accesscontrol.AccessControl
var acService accesscontrol.Service var acService accesscontrol.Service
var userSvc user.Service
// Defining the accesscontrol service has to be done before registering routes // Defining the accesscontrol service has to be done before registering routes
if useFakeAccessControl { if useFakeAccessControl {
acmock = accesscontrolmock.New() acmock = accesscontrolmock.New()
@ -386,19 +388,18 @@ func setupHTTPServerWithCfgDb(
} }
ac = acmock ac = acmock
acService = acmock acService = acmock
userSvc = &usertest.FakeUserService{}
} else { } else {
var err error var err error
acService, err = acimpl.ProvideService(cfg, db, routeRegister, localcache.ProvideService()) acService, err = acimpl.ProvideService(cfg, db, routeRegister, localcache.ProvideService())
require.NoError(t, err) require.NoError(t, err)
ac = acimpl.ProvideAccessControl(cfg) ac = acimpl.ProvideAccessControl(cfg)
userSvc = userimpl.ProvideService(db, nil, cfg, db)
} }
teamPermissionService, err := ossaccesscontrol.ProvideTeamPermissions(cfg, routeRegister, db, ac, license, acService, teamService, userSvc)
teamPermissionService, err := ossaccesscontrol.ProvideTeamPermissions(cfg, routeRegister, db, ac, license, acService, teamService)
require.NoError(t, err) require.NoError(t, err)
// Create minimal HTTP Server // Create minimal HTTP Server
userMock := usertest.NewUserServiceFake()
userMock.ExpectedUser = &user.User{ID: 1}
hs := &HTTPServer{ hs := &HTTPServer{
Cfg: cfg, Cfg: cfg,
Features: features, Features: features,
@ -416,7 +417,7 @@ func setupHTTPServerWithCfgDb(
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(), ac, accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(), ac,
), ),
preferenceService: preftest.NewPreferenceServiceFake(), preferenceService: preftest.NewPreferenceServiceFake(),
userService: userMock, userService: userSvc,
orgService: orgtest.NewOrgServiceFake(), orgService: orgtest.NewOrgServiceFake(),
teamService: teamService, teamService: teamService,
annotationsRepo: annotationstest.NewFakeAnnotationsRepo(), annotationsRepo: annotationstest.NewFakeAnnotationsRepo(),
@ -455,7 +456,7 @@ func setupHTTPServerWithCfgDb(
cfg: cfg, cfg: cfg,
dashboardsStore: dashboardsStore, dashboardsStore: dashboardsStore,
teamService: teamService, teamService: teamService,
usermock: userMock, userService: userSvc,
} }
} }

View File

@ -16,6 +16,7 @@ import (
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware/csrf" "github.com/grafana/grafana/pkg/middleware/csrf"
"github.com/grafana/grafana/pkg/services/searchV2" "github.com/grafana/grafana/pkg/services/searchV2"
"github.com/grafana/grafana/pkg/services/userauth"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
@ -199,6 +200,7 @@ type HTTPServer struct {
accesscontrolService accesscontrol.Service accesscontrolService accesscontrol.Service
annotationsRepo annotations.Repository annotationsRepo annotations.Repository
tagService tag.Service tagService tag.Service
userAuthService userauth.Service
} }
type ServerOptions struct { type ServerOptions struct {
@ -240,6 +242,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
loginAttemptService loginAttempt.Service, orgService org.Service, teamService team.Service, loginAttemptService loginAttempt.Service, orgService org.Service, teamService team.Service,
accesscontrolService accesscontrol.Service, dashboardThumbsService dashboardThumbs.Service, navTreeService navtree.Service, accesscontrolService accesscontrol.Service, dashboardThumbsService dashboardThumbs.Service, navTreeService navtree.Service,
annotationRepo annotations.Repository, tagService tag.Service, searchv2HTTPService searchV2.SearchHTTPService, annotationRepo annotations.Repository, tagService tag.Service, searchv2HTTPService searchV2.SearchHTTPService,
userAuthService userauth.Service,
) (*HTTPServer, error) { ) (*HTTPServer, error) {
web.Env = cfg.Env web.Env = cfg.Env
m := web.New() m := web.New()
@ -340,6 +343,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
accesscontrolService: accesscontrolService, accesscontrolService: accesscontrolService,
annotationsRepo: annotationRepo, annotationsRepo: annotationRepo,
tagService: tagService, tagService: tagService,
userAuthService: userAuthService,
} }
if hs.Listener != nil { if hs.Listener != nil {
hs.log.Debug("Using provided listener") hs.log.Debug("Using provided listener")
@ -592,7 +596,7 @@ func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() {
m.Use(hs.frontendLogEndpoints()) m.Use(hs.frontendLogEndpoints())
m.UseMiddleware(hs.ContextHandler.Middleware) m.UseMiddleware(hs.ContextHandler.Middleware)
m.Use(middleware.OrgRedirect(hs.Cfg, hs.SQLStore)) m.Use(middleware.OrgRedirect(hs.Cfg, hs.userService))
m.Use(accesscontrol.LoadPermissionsMiddleware(hs.accesscontrolService)) m.Use(accesscontrol.LoadPermissionsMiddleware(hs.accesscontrolService))
// needs to be after context handler // needs to be after context handler

View File

@ -302,7 +302,7 @@ func (hs *HTTPServer) applyUserInvite(ctx context.Context, usr *user.User, invit
if setActive { if setActive {
// set org to active // set org to active
if err := hs.SQLStore.SetUsingOrg(ctx, &models.SetUsingOrgCommand{OrgId: invite.OrgId, UserId: usr.ID}); err != nil { if err := hs.userService.SetUsingOrg(ctx, &user.SetUsingOrgCommand{OrgID: invite.OrgId, UserID: usr.ID}); err != nil {
return false, response.Error(500, "Failed to set org as active", err) return false, response.Error(500, "Failed to set org as active", err)
} }
} }

View File

@ -12,6 +12,7 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/org/orgimpl" "github.com/grafana/grafana/pkg/services/org/orgimpl"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/user/usertest"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
@ -198,7 +199,7 @@ func setupOrgsDBForAccessControlTests(t *testing.T, db sqlstore.Store, c accessC
setInitCtxSignedInViewer(c.initCtx) setInitCtxSignedInViewer(c.initCtx)
u := *c.initCtx.SignedInUser u := *c.initCtx.SignedInUser
u.OrgID = orgID u.OrgID = orgID
c.usermock.ExpectedSignedInUser = &u c.userService.(*usertest.FakeUserService).ExpectedSignedInUser = &u
// Create `orgsCount` orgs // Create `orgsCount` orgs
for i := 1; i <= int(orgID); i++ { for i := 1; i <= int(orgID); i++ {

View File

@ -388,8 +388,7 @@ func TestGetOrgUsersAPIEndpoint_AccessControlMetadata(t *testing.T) {
cfg.RBACEnabled = tc.enableAccessControl cfg.RBACEnabled = tc.enableAccessControl
sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) {
hs.userService = userimpl.ProvideService( hs.userService = userimpl.ProvideService(
hs.SQLStore, nil, nil, nil, nil, hs.SQLStore, nil, cfg, hs.SQLStore.(*sqlstore.SQLStore),
nil, nil, nil, nil, nil, hs.SQLStore.(*sqlstore.SQLStore),
) )
hs.orgService = orgimpl.ProvideService(hs.SQLStore, cfg) hs.orgService = orgimpl.ProvideService(hs.SQLStore, cfg)
}) })
@ -493,8 +492,7 @@ func TestGetOrgUsersAPIEndpoint_AccessControl(t *testing.T) {
cfg.RBACEnabled = tc.enableAccessControl cfg.RBACEnabled = tc.enableAccessControl
sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) {
hs.userService = userimpl.ProvideService( hs.userService = userimpl.ProvideService(
hs.SQLStore, nil, nil, nil, nil, hs.SQLStore, nil, cfg, hs.SQLStore.(*sqlstore.SQLStore),
nil, nil, nil, nil, nil, hs.SQLStore.(*sqlstore.SQLStore),
) )
hs.orgService = orgimpl.ProvideService(hs.SQLStore, cfg) hs.orgService = orgimpl.ProvideService(hs.SQLStore, cfg)
}) })
@ -599,10 +597,10 @@ func TestPostOrgUsersAPIEndpoint_AccessControl(t *testing.T) {
cfg.RBACEnabled = tc.enableAccessControl cfg.RBACEnabled = tc.enableAccessControl
sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) {
hs.userService = userimpl.ProvideService( hs.userService = userimpl.ProvideService(
hs.SQLStore, nil, nil, nil, nil, hs.SQLStore, nil, cfg, hs.SQLStore.(*sqlstore.SQLStore),
nil, nil, nil, nil, nil, hs.SQLStore.(*sqlstore.SQLStore),
) )
}) })
setupOrgUsersDBForAccessControlTests(t, sc.db) setupOrgUsersDBForAccessControlTests(t, sc.db)
setInitCtxSignedInUser(sc.initCtx, tc.user) setInitCtxSignedInUser(sc.initCtx, tc.user)
@ -718,8 +716,7 @@ func TestOrgUsersAPIEndpointWithSetPerms_AccessControl(t *testing.T) {
sc := setupHTTPServer(t, true, func(hs *HTTPServer) { sc := setupHTTPServer(t, true, func(hs *HTTPServer) {
hs.tempUserService = tempuserimpl.ProvideService(hs.SQLStore) hs.tempUserService = tempuserimpl.ProvideService(hs.SQLStore)
hs.userService = userimpl.ProvideService( hs.userService = userimpl.ProvideService(
hs.SQLStore, nil, nil, nil, nil, hs.SQLStore, nil, nil, hs.SQLStore.(*sqlstore.SQLStore),
nil, nil, nil, nil, nil, hs.SQLStore.(*sqlstore.SQLStore),
) )
}) })
setInitCtxSignedInViewer(sc.initCtx) setInitCtxSignedInViewer(sc.initCtx)
@ -837,8 +834,7 @@ func TestPatchOrgUsersAPIEndpoint_AccessControl(t *testing.T) {
cfg.RBACEnabled = tc.enableAccessControl cfg.RBACEnabled = tc.enableAccessControl
sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) {
hs.userService = userimpl.ProvideService( hs.userService = userimpl.ProvideService(
hs.SQLStore, nil, nil, nil, nil, hs.SQLStore, nil, cfg, hs.SQLStore.(*sqlstore.SQLStore),
nil, nil, nil, nil, nil, hs.SQLStore.(*sqlstore.SQLStore),
) )
hs.orgService = orgimpl.ProvideService(hs.SQLStore, cfg) hs.orgService = orgimpl.ProvideService(hs.SQLStore, cfg)
}) })
@ -858,13 +854,13 @@ func TestPatchOrgUsersAPIEndpoint_AccessControl(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, tc.expectedMessage, message) assert.Equal(t, tc.expectedMessage, message)
getUserQuery := models.GetSignedInUserQuery{ getUserQuery := user.GetSignedInUserQuery{
UserId: tc.targetUserId, UserID: tc.targetUserId,
OrgId: tc.targetOrg, OrgID: tc.targetOrg,
} }
err = sc.db.GetSignedInUser(context.Background(), &getUserQuery) usr, err := sc.userService.GetSignedInUser(context.Background(), &getUserQuery)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, tc.expectedUserRole, getUserQuery.Result.OrgRole) assert.Equal(t, tc.expectedUserRole, usr.OrgRole)
} }
}) })
} }
@ -965,8 +961,7 @@ func TestDeleteOrgUsersAPIEndpoint_AccessControl(t *testing.T) {
cfg.RBACEnabled = tc.enableAccessControl cfg.RBACEnabled = tc.enableAccessControl
sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) {
hs.userService = userimpl.ProvideService( hs.userService = userimpl.ProvideService(
hs.SQLStore, nil, nil, nil, nil, hs.SQLStore, nil, cfg, hs.SQLStore.(*sqlstore.SQLStore),
nil, nil, nil, nil, nil, hs.SQLStore.(*sqlstore.SQLStore),
) )
hs.orgService = orgimpl.ProvideService(hs.SQLStore, cfg) hs.orgService = orgimpl.ProvideService(hs.SQLStore, cfg)
}) })

View File

@ -49,9 +49,10 @@ func (hs *HTTPServer) GetUserByID(c *models.ReqContext) response.Response {
} }
func (hs *HTTPServer) getUserUserProfile(c *models.ReqContext, userID int64) response.Response { func (hs *HTTPServer) getUserUserProfile(c *models.ReqContext, userID int64) response.Response {
query := models.GetUserProfileQuery{UserId: userID} query := user.GetUserProfileQuery{UserID: userID}
if err := hs.SQLStore.GetUserProfile(c.Req.Context(), &query); err != nil { userProfile, err := hs.userService.GetUserProfile(c.Req.Context(), &query)
if err != nil {
if errors.Is(err, user.ErrUserNotFound) { if errors.Is(err, user.ErrUserNotFound) {
return response.Error(404, user.ErrUserNotFound.Error(), nil) return response.Error(404, user.ErrUserNotFound.Error(), nil)
} }
@ -59,17 +60,17 @@ func (hs *HTTPServer) getUserUserProfile(c *models.ReqContext, userID int64) res
} }
getAuthQuery := models.GetAuthInfoQuery{UserId: userID} getAuthQuery := models.GetAuthInfoQuery{UserId: userID}
query.Result.AuthLabels = []string{} userProfile.AuthLabels = []string{}
if err := hs.authInfoService.GetAuthInfo(c.Req.Context(), &getAuthQuery); err == nil { if err := hs.authInfoService.GetAuthInfo(c.Req.Context(), &getAuthQuery); err == nil {
authLabel := login.GetAuthProviderLabel(getAuthQuery.Result.AuthModule) authLabel := login.GetAuthProviderLabel(getAuthQuery.Result.AuthModule)
query.Result.AuthLabels = append(query.Result.AuthLabels, authLabel) userProfile.AuthLabels = append(userProfile.AuthLabels, authLabel)
query.Result.IsExternal = true userProfile.IsExternal = true
} }
query.Result.AccessControl = hs.getAccessControlMetadata(c, c.OrgID, "global.users:id:", strconv.FormatInt(userID, 10)) userProfile.AccessControl = hs.getAccessControlMetadata(c, c.OrgID, "global.users:id:", strconv.FormatInt(userID, 10))
query.Result.AvatarUrl = dtos.GetGravatarUrl(query.Result.Email) userProfile.AvatarUrl = dtos.GetGravatarUrl(userProfile.Email)
return response.JSON(http.StatusOK, query.Result) return response.JSON(http.StatusOK, userProfile)
} }
// swagger:route GET /users/lookup users getUserByLoginOrEmail // swagger:route GET /users/lookup users getUserByLoginOrEmail
@ -171,9 +172,9 @@ func (hs *HTTPServer) UpdateUserActiveOrg(c *models.ReqContext) response.Respons
return response.Error(401, "Not a valid organization", nil) return response.Error(401, "Not a valid organization", nil)
} }
cmd := models.SetUsingOrgCommand{UserId: userID, OrgId: orgID} cmd := user.SetUsingOrgCommand{UserID: userID, OrgID: orgID}
if err := hs.SQLStore.SetUsingOrg(c.Req.Context(), &cmd); err != nil { if err := hs.userService.SetUsingOrg(c.Req.Context(), &cmd); err != nil {
return response.Error(500, "Failed to change active organization", err) return response.Error(500, "Failed to change active organization", err)
} }
@ -334,9 +335,9 @@ func (hs *HTTPServer) UserSetUsingOrg(c *models.ReqContext) response.Response {
return response.Error(401, "Not a valid organization", nil) return response.Error(401, "Not a valid organization", nil)
} }
cmd := models.SetUsingOrgCommand{UserId: c.UserID, OrgId: orgID} cmd := user.SetUsingOrgCommand{UserID: c.UserID, OrgID: orgID}
if err := hs.SQLStore.SetUsingOrg(c.Req.Context(), &cmd); err != nil { if err := hs.userService.SetUsingOrg(c.Req.Context(), &cmd); err != nil {
return response.Error(500, "Failed to change active organization", err) return response.Error(500, "Failed to change active organization", err)
} }
@ -355,9 +356,9 @@ func (hs *HTTPServer) ChangeActiveOrgAndRedirectToHome(c *models.ReqContext) {
hs.NotFoundHandler(c) hs.NotFoundHandler(c)
} }
cmd := models.SetUsingOrgCommand{UserId: c.UserID, OrgId: orgID} cmd := user.SetUsingOrgCommand{UserID: c.UserID, OrgID: orgID}
if err := hs.SQLStore.SetUsingOrg(c.Req.Context(), &cmd); err != nil { if err := hs.userService.SetUsingOrg(c.Req.Context(), &cmd); err != nil {
hs.NotFoundHandler(c) hs.NotFoundHandler(c)
} }
@ -449,12 +450,12 @@ func (hs *HTTPServer) SetHelpFlag(c *models.ReqContext) response.Response {
bitmask := &c.HelpFlags1 bitmask := &c.HelpFlags1
bitmask.AddFlag(user.HelpFlags1(flag)) bitmask.AddFlag(user.HelpFlags1(flag))
cmd := models.SetUserHelpFlagCommand{ cmd := user.SetUserHelpFlagCommand{
UserId: c.UserID, UserID: c.UserID,
HelpFlags1: *bitmask, HelpFlags1: *bitmask,
} }
if err := hs.SQLStore.SetUserHelpFlag(c.Req.Context(), &cmd); err != nil { if err := hs.userService.SetUserHelpFlag(c.Req.Context(), &cmd); err != nil {
return response.Error(500, "Failed to update help flag", err) return response.Error(500, "Failed to update help flag", err)
} }
@ -471,12 +472,12 @@ func (hs *HTTPServer) SetHelpFlag(c *models.ReqContext) response.Response {
// 403: forbiddenError // 403: forbiddenError
// 500: internalServerError // 500: internalServerError
func (hs *HTTPServer) ClearHelpFlags(c *models.ReqContext) response.Response { func (hs *HTTPServer) ClearHelpFlags(c *models.ReqContext) response.Response {
cmd := models.SetUserHelpFlagCommand{ cmd := user.SetUserHelpFlagCommand{
UserId: c.UserID, UserID: c.UserID,
HelpFlags1: user.HelpFlags1(0), HelpFlags1: user.HelpFlags1(0),
} }
if err := hs.SQLStore.SetUserHelpFlag(c.Req.Context(), &cmd); err != nil { if err := hs.userService.SetUserHelpFlag(c.Req.Context(), &cmd); err != nil {
return response.Error(500, "Failed to update help flag", err) return response.Error(500, "Failed to update help flag", err)
} }

View File

@ -26,6 +26,7 @@ import (
"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/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/userimpl"
"github.com/grafana/grafana/pkg/services/user/usertest" "github.com/grafana/grafana/pkg/services/user/usertest"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
@ -67,6 +68,7 @@ func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
} }
user, err := sqlStore.CreateUser(context.Background(), createUserCmd) user, err := sqlStore.CreateUser(context.Background(), createUserCmd)
require.Nil(t, err) require.Nil(t, err)
hs.userService = userimpl.ProvideService(sqlStore, nil, sc.cfg, sqlStore)
sc.handlerFunc = hs.GetUserByID sc.handlerFunc = hs.GetUserByID

View File

@ -13,6 +13,9 @@ import (
"time" "time"
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime" "github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/infra/fs" "github.com/grafana/grafana/pkg/infra/fs"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
@ -36,8 +39,6 @@ import (
"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/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func fakeGetTime() func() time.Time { func fakeGetTime() func() time.Time {
@ -372,8 +373,7 @@ func TestMiddlewareContext(t *testing.T) {
const group = "grafana-core-team" const group = "grafana-core-team"
middlewareScenario(t, "Should not sync the user if it's in the cache", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "Should not sync the user if it's in the cache", func(t *testing.T, sc *scenarioContext) {
sc.mockSQLStore.ExpectedSignedInUser = &user.SignedInUser{OrgID: orgID, UserID: userID} sc.userService.ExpectedSignedInUser = &user.SignedInUser{OrgID: orgID, UserID: userID}
h, err := authproxy.HashCacheKey(hdrName + "-" + group) h, err := authproxy.HashCacheKey(hdrName + "-" + group)
require.NoError(t, err) require.NoError(t, err)
key := fmt.Sprintf(authproxy.CachePrefix, h) key := fmt.Sprintf(authproxy.CachePrefix, h)
@ -412,9 +412,8 @@ func TestMiddlewareContext(t *testing.T) {
}) })
middlewareScenario(t, "Should create an user from a header", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "Should create an user from a header", func(t *testing.T, sc *scenarioContext) {
sc.mockSQLStore.ExpectedSignedInUser = &user.SignedInUser{OrgID: orgID, UserID: userID}
sc.loginService.ExpectedUser = &user.User{ID: userID} sc.loginService.ExpectedUser = &user.User{ID: userID}
sc.userService.ExpectedSignedInUser = &user.SignedInUser{OrgID: orgID, UserID: userID}
sc.fakeReq("GET", "/") sc.fakeReq("GET", "/")
sc.req.Header.Set(sc.cfg.AuthProxyHeaderName, hdrName) sc.req.Header.Set(sc.cfg.AuthProxyHeaderName, hdrName)
sc.exec() sc.exec()
@ -432,7 +431,7 @@ func TestMiddlewareContext(t *testing.T) {
var storedRoleInfo map[int64]org.RoleType = nil var storedRoleInfo map[int64]org.RoleType = nil
sc.loginService.ExpectedUserFunc = func(cmd *models.UpsertUserCommand) *user.User { sc.loginService.ExpectedUserFunc = func(cmd *models.UpsertUserCommand) *user.User {
storedRoleInfo = cmd.ExternalUser.OrgRoles storedRoleInfo = cmd.ExternalUser.OrgRoles
sc.mockSQLStore.ExpectedSignedInUser = &user.SignedInUser{OrgID: defaultOrgId, UserID: userID, OrgRole: storedRoleInfo[defaultOrgId]} sc.userService.ExpectedSignedInUser = &user.SignedInUser{OrgID: defaultOrgId, UserID: userID, OrgRole: storedRoleInfo[defaultOrgId]}
return &user.User{ID: userID} return &user.User{ID: userID}
} }
@ -455,7 +454,7 @@ func TestMiddlewareContext(t *testing.T) {
var storedRoleInfo map[int64]org.RoleType = nil var storedRoleInfo map[int64]org.RoleType = nil
sc.loginService.ExpectedUserFunc = func(cmd *models.UpsertUserCommand) *user.User { sc.loginService.ExpectedUserFunc = func(cmd *models.UpsertUserCommand) *user.User {
storedRoleInfo = cmd.ExternalUser.OrgRoles storedRoleInfo = cmd.ExternalUser.OrgRoles
sc.mockSQLStore.ExpectedSignedInUser = &user.SignedInUser{OrgID: orgID, UserID: userID, OrgRole: storedRoleInfo[orgID]} sc.userService.ExpectedSignedInUser = &user.SignedInUser{OrgID: orgID, UserID: userID, OrgRole: storedRoleInfo[orgID]}
return &user.User{ID: userID} return &user.User{ID: userID}
} }
@ -479,7 +478,7 @@ func TestMiddlewareContext(t *testing.T) {
middlewareScenario(t, "Should use organisation specified by targetOrgId parameter", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "Should use organisation specified by targetOrgId parameter", func(t *testing.T, sc *scenarioContext) {
var targetOrgID int64 = 123 var targetOrgID int64 = 123
sc.mockSQLStore.ExpectedSignedInUser = &user.SignedInUser{OrgID: targetOrgID, UserID: userID} sc.userService.ExpectedSignedInUser = &user.SignedInUser{OrgID: targetOrgID, UserID: userID}
sc.loginService.ExpectedUser = &user.User{ID: userID} sc.loginService.ExpectedUser = &user.User{ID: userID}
sc.fakeReq("GET", fmt.Sprintf("/?targetOrgId=%d", targetOrgID)) sc.fakeReq("GET", fmt.Sprintf("/?targetOrgId=%d", targetOrgID))
@ -553,7 +552,7 @@ func TestMiddlewareContext(t *testing.T) {
const userID int64 = 12 const userID int64 = 12
const orgID int64 = 2 const orgID int64 = 2
sc.mockSQLStore.ExpectedSignedInUser = &user.SignedInUser{OrgID: orgID, UserID: userID} sc.userService.ExpectedSignedInUser = &user.SignedInUser{OrgID: orgID, UserID: userID}
sc.loginService.ExpectedUser = &user.User{ID: userID} sc.loginService.ExpectedUser = &user.User{ID: userID}
sc.fakeReq("GET", "/") sc.fakeReq("GET", "/")
@ -569,7 +568,7 @@ func TestMiddlewareContext(t *testing.T) {
}) })
middlewareScenario(t, "Should allow the request from whitelist IP", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "Should allow the request from whitelist IP", func(t *testing.T, sc *scenarioContext) {
sc.mockSQLStore.ExpectedSignedInUser = &user.SignedInUser{OrgID: orgID, UserID: userID} sc.userService.ExpectedSignedInUser = &user.SignedInUser{OrgID: orgID, UserID: userID}
sc.loginService.ExpectedUser = &user.User{ID: userID} sc.loginService.ExpectedUser = &user.User{ID: userID}
sc.fakeReq("GET", "/") sc.fakeReq("GET", "/")
@ -659,7 +658,7 @@ func middlewareScenario(t *testing.T, desc string, fn scenarioFunc, cbs ...func(
sc.sqlStore = ctxHdlr.SQLStore sc.sqlStore = ctxHdlr.SQLStore
sc.contextHandler = ctxHdlr sc.contextHandler = ctxHdlr
sc.m.Use(ctxHdlr.Middleware) sc.m.Use(ctxHdlr.Middleware)
sc.m.Use(OrgRedirect(sc.cfg, sc.mockSQLStore)) sc.m.Use(OrgRedirect(sc.cfg, sc.userService))
sc.userAuthTokenService = ctxHdlr.AuthTokenService.(*auth.FakeUserAuthTokenService) sc.userAuthTokenService = ctxHdlr.AuthTokenService.(*auth.FakeUserAuthTokenService)
sc.jwtAuthService = ctxHdlr.JWTAuthService.(*models.FakeJWTService) sc.jwtAuthService = ctxHdlr.JWTAuthService.(*models.FakeJWTService)
@ -703,7 +702,7 @@ func getContextHandler(t *testing.T, cfg *setting.Cfg, mockSQLStore *mockstore.S
renderSvc := &fakeRenderService{} renderSvc := &fakeRenderService{}
authJWTSvc := models.NewFakeJWTService() authJWTSvc := models.NewFakeJWTService()
tracer := tracing.InitializeTracerForTest() tracer := tracing.InitializeTracerForTest()
authProxy := authproxy.ProvideAuthProxy(cfg, remoteCacheSvc, loginService, mockSQLStore) authProxy := authproxy.ProvideAuthProxy(cfg, remoteCacheSvc, loginService, userService, mockSQLStore)
authenticator := &logintest.AuthenticatorFake{ExpectedUser: &user.User{}} authenticator := &logintest.AuthenticatorFake{ExpectedUser: &user.User{}}
return contexthandler.ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, mockSQLStore, tracer, authProxy, loginService, apiKeyService, authenticator, userService) return contexthandler.ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, mockSQLStore, tracer, authProxy, loginService, apiKeyService, authenticator, userService)
} }

View File

@ -6,16 +6,15 @@ import (
"strconv" "strconv"
"strings" "strings"
"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/sqlstore" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
) )
// OrgRedirect changes org and redirects users if the // OrgRedirect changes org and redirects users if the
// querystring `orgId` doesn't match the active org. // querystring `orgId` doesn't match the active org.
func OrgRedirect(cfg *setting.Cfg, store sqlstore.Store) web.Handler { func OrgRedirect(cfg *setting.Cfg, userSvc user.Service) web.Handler {
return func(res http.ResponseWriter, req *http.Request, c *web.Context) { return func(res http.ResponseWriter, req *http.Request, c *web.Context) {
orgIdValue := req.URL.Query().Get("orgId") orgIdValue := req.URL.Query().Get("orgId")
orgId, err := strconv.ParseInt(orgIdValue, 10, 64) orgId, err := strconv.ParseInt(orgIdValue, 10, 64)
@ -33,8 +32,8 @@ func OrgRedirect(cfg *setting.Cfg, store sqlstore.Store) web.Handler {
return return
} }
cmd := models.SetUsingOrgCommand{UserId: ctx.UserID, OrgId: orgId} cmd := user.SetUsingOrgCommand{UserID: ctx.UserID, OrgID: orgId}
if err := store.SetUsingOrg(ctx.Req.Context(), &cmd); err != nil { if err := userSvc.SetUsingOrg(ctx.Req.Context(), &cmd); err != nil {
if ctx.IsApiRequest() { if ctx.IsApiRequest() {
ctx.JsonApiErr(404, "Not found", nil) ctx.JsonApiErr(404, "Not found", nil)
} else { } else {

View File

@ -5,9 +5,10 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/stretchr/testify/require"
) )
func TestOrgRedirectMiddleware(t *testing.T) { func TestOrgRedirectMiddleware(t *testing.T) {
@ -46,7 +47,7 @@ func TestOrgRedirectMiddleware(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
middlewareScenario(t, tc.desc, func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, tc.desc, func(t *testing.T, sc *scenarioContext) {
sc.withTokenSessionCookie("token") sc.withTokenSessionCookie("token")
sc.mockSQLStore.ExpectedSignedInUser = &user.SignedInUser{OrgID: 1, UserID: 12} sc.userService.ExpectedSignedInUser = &user.SignedInUser{OrgID: 1, UserID: 12}
sc.userAuthTokenService.LookupTokenProvider = func(ctx context.Context, unhashedToken string) (*models.UserToken, error) { sc.userAuthTokenService.LookupTokenProvider = func(ctx context.Context, unhashedToken string) (*models.UserToken, error) {
return &models.UserToken{ return &models.UserToken{
UserId: 0, UserId: 0,
@ -64,8 +65,8 @@ func TestOrgRedirectMiddleware(t *testing.T) {
middlewareScenario(t, "when setting an invalid org for user", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "when setting an invalid org for user", func(t *testing.T, sc *scenarioContext) {
sc.withTokenSessionCookie("token") sc.withTokenSessionCookie("token")
sc.mockSQLStore.ExpectedSetUsingOrgError = fmt.Errorf("") sc.userService.ExpectedSetUsingOrgError = fmt.Errorf("")
sc.mockSQLStore.ExpectedSignedInUser = &user.SignedInUser{OrgID: 1, UserID: 12} sc.userService.ExpectedSignedInUser = &user.SignedInUser{OrgID: 1, UserID: 12}
sc.userAuthTokenService.LookupTokenProvider = func(ctx context.Context, unhashedToken string) (*models.UserToken, error) { sc.userAuthTokenService.LookupTokenProvider = func(ctx context.Context, unhashedToken string) (*models.UserToken, error) {
return &models.UserToken{ return &models.UserToken{

View File

@ -5,13 +5,14 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/remotecache" "github.com/grafana/grafana/pkg/infra/remotecache"
"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/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestRecoveryMiddleware(t *testing.T) { func TestRecoveryMiddleware(t *testing.T) {
@ -70,7 +71,7 @@ func recoveryScenario(t *testing.T, desc string, url string, fn scenarioFunc) {
contextHandler := getContextHandler(t, nil, nil, nil, nil, nil) contextHandler := getContextHandler(t, nil, nil, nil, nil, nil)
sc.m.Use(contextHandler.Middleware) sc.m.Use(contextHandler.Middleware)
// mock out gc goroutine // mock out gc goroutine
sc.m.Use(OrgRedirect(cfg, sc.mockSQLStore)) sc.m.Use(OrgRedirect(cfg, sc.userService))
sc.defaultHandler = func(c *models.ReqContext) { sc.defaultHandler = func(c *models.ReqContext) {
sc.context = c sc.context = c

View File

@ -9,13 +9,12 @@ import (
"testing" "testing"
"time" "time"
"gopkg.in/ini.v1"
"github.com/grafana/grafana-azure-sdk-go/azsettings" "github.com/grafana/grafana-azure-sdk-go/azsettings"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace"
"gopkg.in/ini.v1"
"github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
@ -103,7 +102,7 @@ func TestIntegrationPluginManager(t *testing.T) {
pg := postgres.ProvideService(cfg) pg := postgres.ProvideService(cfg)
my := mysql.ProvideService(cfg, hcp) my := mysql.ProvideService(cfg, hcp)
ms := mssql.ProvideService(cfg) ms := mssql.ProvideService(cfg)
sv2 := searchV2.ProvideService(cfg, sqlstore.InitTestDB(t), nil, nil, tracing.InitializeTracerForTest(), featuremgmt.WithFeatures(), nil) sv2 := searchV2.ProvideService(cfg, sqlstore.InitTestDB(t), nil, nil, tracing.InitializeTracerForTest(), featuremgmt.WithFeatures(), nil, nil)
graf := grafanads.ProvideService(cfg, sv2, nil) graf := grafanads.ProvideService(cfg, sv2, nil)
coreRegistry := coreplugin.ProvideCoreRegistry(am, cw, cm, es, grap, idb, lk, otsdb, pr, tmpo, td, pg, my, ms, graf) coreRegistry := coreplugin.ProvideCoreRegistry(am, cw, cm, es, grap, idb, lk, otsdb, pr, tmpo, td, pg, my, ms, graf)

View File

@ -6,13 +6,6 @@ package server
import ( import (
"github.com/google/wire" "github.com/google/wire"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/services/annotations/annotationsimpl"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/playlist/playlistimpl"
"github.com/grafana/grafana/pkg/services/store/sanitizer"
"github.com/grafana/grafana/pkg/api" "github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/api/avatar" "github.com/grafana/grafana/pkg/api/avatar"
"github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/api/routing"
@ -51,7 +44,10 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
"github.com/grafana/grafana/pkg/services/alerting" "github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/services/annotations/annotationsimpl"
"github.com/grafana/grafana/pkg/services/apikey/apikeyimpl" "github.com/grafana/grafana/pkg/services/apikey/apikeyimpl"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/auth/jwt" "github.com/grafana/grafana/pkg/services/auth/jwt"
"github.com/grafana/grafana/pkg/services/cleanup" "github.com/grafana/grafana/pkg/services/cleanup"
"github.com/grafana/grafana/pkg/services/comments" "github.com/grafana/grafana/pkg/services/comments"
@ -95,6 +91,7 @@ import (
"github.com/grafana/grafana/pkg/services/notifications" "github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/services/oauthtoken" "github.com/grafana/grafana/pkg/services/oauthtoken"
"github.com/grafana/grafana/pkg/services/org/orgimpl" "github.com/grafana/grafana/pkg/services/org/orgimpl"
"github.com/grafana/grafana/pkg/services/playlist/playlistimpl"
"github.com/grafana/grafana/pkg/services/plugindashboards" "github.com/grafana/grafana/pkg/services/plugindashboards"
plugindashboardsservice "github.com/grafana/grafana/pkg/services/plugindashboards/service" plugindashboardsservice "github.com/grafana/grafana/pkg/services/plugindashboards/service"
"github.com/grafana/grafana/pkg/services/pluginsettings" "github.com/grafana/grafana/pkg/services/pluginsettings"
@ -125,6 +122,7 @@ import (
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore" "github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
"github.com/grafana/grafana/pkg/services/star/starimpl" "github.com/grafana/grafana/pkg/services/star/starimpl"
"github.com/grafana/grafana/pkg/services/store" "github.com/grafana/grafana/pkg/services/store"
"github.com/grafana/grafana/pkg/services/store/sanitizer"
"github.com/grafana/grafana/pkg/services/tag" "github.com/grafana/grafana/pkg/services/tag"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/team/teamimpl" "github.com/grafana/grafana/pkg/services/team/teamimpl"

View File

@ -40,7 +40,7 @@ var (
func ProvideTeamPermissions( func ProvideTeamPermissions(
cfg *setting.Cfg, router routing.RouteRegister, sql *sqlstore.SQLStore, cfg *setting.Cfg, router routing.RouteRegister, sql *sqlstore.SQLStore,
ac accesscontrol.AccessControl, license models.Licensing, service accesscontrol.Service, ac accesscontrol.AccessControl, license models.Licensing, service accesscontrol.Service,
teamService team.Service, teamService team.Service, userService user.Service,
) (*TeamPermissionsService, error) { ) (*TeamPermissionsService, error) {
options := resourcepermissions.Options{ options := resourcepermissions.Options{
Resource: "teams", Resource: "teams",
@ -96,7 +96,7 @@ func ProvideTeamPermissions(
}, },
} }
srv, err := resourcepermissions.New(options, cfg, router, license, ac, service, sql, teamService) srv, err := resourcepermissions.New(options, cfg, router, license, ac, service, sql, teamService, userService)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -114,7 +114,7 @@ var DashboardAdminActions = append(DashboardEditActions, []string{dashboards.Act
func ProvideDashboardPermissions( func ProvideDashboardPermissions(
cfg *setting.Cfg, router routing.RouteRegister, sql *sqlstore.SQLStore, ac accesscontrol.AccessControl, cfg *setting.Cfg, router routing.RouteRegister, sql *sqlstore.SQLStore, ac accesscontrol.AccessControl,
license models.Licensing, dashboardStore dashboards.Store, service accesscontrol.Service, license models.Licensing, dashboardStore dashboards.Store, service accesscontrol.Service,
teamService team.Service, teamService team.Service, userService user.Service,
) (*DashboardPermissionsService, error) { ) (*DashboardPermissionsService, error) {
getDashboard := func(ctx context.Context, orgID int64, resourceID string) (*models.Dashboard, error) { getDashboard := func(ctx context.Context, orgID int64, resourceID string) (*models.Dashboard, error) {
query := &models.GetDashboardQuery{Uid: resourceID, OrgId: orgID} query := &models.GetDashboardQuery{Uid: resourceID, OrgId: orgID}
@ -168,7 +168,7 @@ func ProvideDashboardPermissions(
RoleGroup: "Dashboards", RoleGroup: "Dashboards",
} }
srv, err := resourcepermissions.New(options, cfg, router, license, ac, service, sql, teamService) srv, err := resourcepermissions.New(options, cfg, router, license, ac, service, sql, teamService, userService)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -193,7 +193,7 @@ var FolderAdminActions = append(FolderEditActions, []string{dashboards.ActionFol
func ProvideFolderPermissions( func ProvideFolderPermissions(
cfg *setting.Cfg, router routing.RouteRegister, sql *sqlstore.SQLStore, accesscontrol accesscontrol.AccessControl, cfg *setting.Cfg, router routing.RouteRegister, sql *sqlstore.SQLStore, accesscontrol accesscontrol.AccessControl,
license models.Licensing, dashboardStore dashboards.Store, service accesscontrol.Service, license models.Licensing, dashboardStore dashboards.Store, service accesscontrol.Service,
teamService team.Service, teamService team.Service, userService user.Service,
) (*FolderPermissionsService, error) { ) (*FolderPermissionsService, error) {
options := resourcepermissions.Options{ options := resourcepermissions.Options{
Resource: "folders", Resource: "folders",
@ -224,7 +224,7 @@ func ProvideFolderPermissions(
WriterRoleName: "Folder permission writer", WriterRoleName: "Folder permission writer",
RoleGroup: "Folders", RoleGroup: "Folders",
} }
srv, err := resourcepermissions.New(options, cfg, router, license, accesscontrol, service, sql, teamService) srv, err := resourcepermissions.New(options, cfg, router, license, accesscontrol, service, sql, teamService, userService)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -284,7 +284,7 @@ type ServiceAccountPermissionsService struct {
func ProvideServiceAccountPermissions( func ProvideServiceAccountPermissions(
cfg *setting.Cfg, router routing.RouteRegister, sql *sqlstore.SQLStore, ac accesscontrol.AccessControl, cfg *setting.Cfg, router routing.RouteRegister, sql *sqlstore.SQLStore, ac accesscontrol.AccessControl,
license models.Licensing, serviceAccountStore serviceaccounts.Store, service accesscontrol.Service, license models.Licensing, serviceAccountStore serviceaccounts.Store, service accesscontrol.Service,
teamService team.Service, teamService team.Service, userService user.Service,
) (*ServiceAccountPermissionsService, error) { ) (*ServiceAccountPermissionsService, error) {
options := resourcepermissions.Options{ options := resourcepermissions.Options{
Resource: "serviceaccounts", Resource: "serviceaccounts",
@ -311,7 +311,7 @@ func ProvideServiceAccountPermissions(
RoleGroup: "Service accounts", RoleGroup: "Service accounts",
} }
srv, err := resourcepermissions.New(options, cfg, router, license, ac, service, sql, teamService) srv, err := resourcepermissions.New(options, cfg, router, license, ac, service, sql, teamService, userService)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -51,7 +51,7 @@ type Store interface {
func New( func New(
options Options, cfg *setting.Cfg, router routing.RouteRegister, license models.Licensing, options Options, cfg *setting.Cfg, router routing.RouteRegister, license models.Licensing,
ac accesscontrol.AccessControl, service accesscontrol.Service, sqlStore *sqlstore.SQLStore, ac accesscontrol.AccessControl, service accesscontrol.Service, sqlStore *sqlstore.SQLStore,
teamService team.Service, teamService team.Service, userService user.Service,
) (*Service, error) { ) (*Service, error) {
var permissions []string var permissions []string
actionSet := make(map[string]struct{}) actionSet := make(map[string]struct{})
@ -83,6 +83,7 @@ func New(
sqlStore: sqlStore, sqlStore: sqlStore,
service: service, service: service,
teamService: teamService, teamService: teamService,
userService: userService,
} }
s.api = newApi(ac, router, s) s.api = newApi(ac, router, s)
@ -110,6 +111,7 @@ type Service struct {
actions []string actions []string
sqlStore *sqlstore.SQLStore sqlStore *sqlstore.SQLStore
teamService team.Service teamService team.Service
userService user.Service
} }
func (s *Service) GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]accesscontrol.ResourcePermission, error) { func (s *Service) GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]accesscontrol.ResourcePermission, error) {
@ -286,10 +288,8 @@ func (s *Service) validateUser(ctx context.Context, orgID, userID int64) error {
return ErrInvalidAssignment return ErrInvalidAssignment
} }
if err := s.sqlStore.GetSignedInUser(ctx, &models.GetSignedInUserQuery{OrgId: orgID, UserId: userID}); err != nil { _, err := s.userService.GetSignedInUser(ctx, &user.GetSignedInUserQuery{OrgID: orgID, UserID: userID})
return err return err
}
return nil
} }
func (s *Service) validateTeam(ctx context.Context, orgID, teamID int64) error { func (s *Service) validateTeam(ctx context.Context, orgID, teamID int64) error {

View File

@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/services/team" "github.com/grafana/grafana/pkg/services/team"
"github.com/grafana/grafana/pkg/services/team/teamimpl" "github.com/grafana/grafana/pkg/services/team/teamimpl"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/userimpl"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
@ -223,12 +224,13 @@ func setupTestEnvironment(t *testing.T, permissions []accesscontrol.Permission,
sql := sqlstore.InitTestDB(t) sql := sqlstore.InitTestDB(t)
cfg := setting.NewCfg() cfg := setting.NewCfg()
teamSvc := teamimpl.ProvideService(sql, cfg) teamSvc := teamimpl.ProvideService(sql, cfg)
userSvc := userimpl.ProvideService(sql, nil, cfg, sql)
license := licensingtest.NewFakeLicensing() license := licensingtest.NewFakeLicensing()
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe() license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()
mock := accesscontrolmock.New().WithPermissions(permissions) mock := accesscontrolmock.New().WithPermissions(permissions)
service, err := New( service, err := New(
ops, cfg, routing.NewRouteRegister(), license, ops, cfg, routing.NewRouteRegister(), license,
accesscontrolmock.New().WithPermissions(permissions), mock, sql, teamSvc, accesscontrolmock.New().WithPermissions(permissions), mock, sql, teamSvc, userSvc,
) )
require.NoError(t, err) require.NoError(t, err)

View File

@ -6,6 +6,8 @@ import (
"net/http" "net/http"
"testing" "testing"
"github.com/stretchr/testify/require"
"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"
@ -19,7 +21,6 @@ import (
"github.com/grafana/grafana/pkg/services/user/usertest" "github.com/grafana/grafana/pkg/services/user/usertest"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
"github.com/stretchr/testify/require"
) )
const userID = int64(1) const userID = int64(1)
@ -84,10 +85,23 @@ func getContextHandler(t *testing.T) *ContextHandler {
tracer := tracing.InitializeTracerForTest() tracer := tracing.InitializeTracerForTest()
loginService := loginservice.LoginServiceMock{ExpectedUser: &user.User{ID: userID}} loginService := loginservice.LoginServiceMock{ExpectedUser: &user.User{ID: userID}}
authProxy := authproxy.ProvideAuthProxy(cfg, remoteCacheSvc, loginService, &FakeGetSignUserStore{}) userService := usertest.FakeUserService{
GetSignedInUserFn: func(ctx context.Context, query *user.GetSignedInUserQuery) (*user.SignedInUser, error) {
if query.UserID != userID {
return &user.SignedInUser{}, user.ErrUserNotFound
}
return &user.SignedInUser{
UserID: userID,
OrgID: orgID,
}, nil
},
}
authProxy := authproxy.ProvideAuthProxy(cfg, remoteCacheSvc, loginService, &userService, &FakeGetSignUserStore{})
authenticator := &fakeAuthenticator{} authenticator := &fakeAuthenticator{}
return ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, sqlStore, tracer, authProxy, loginService, nil, authenticator, &usertest.FakeUserService{}) return ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc,
renderSvc, sqlStore, tracer, authProxy, loginService, nil, authenticator, &userService)
} }
type FakeGetSignUserStore struct { type FakeGetSignUserStore struct {

View File

@ -56,16 +56,18 @@ type AuthProxy struct {
remoteCache *remotecache.RemoteCache remoteCache *remotecache.RemoteCache
loginService login.Service loginService login.Service
sqlStore sqlstore.Store sqlStore sqlstore.Store
userService user.Service
logger log.Logger logger log.Logger
} }
func ProvideAuthProxy(cfg *setting.Cfg, remoteCache *remotecache.RemoteCache, loginService login.Service, sqlStore sqlstore.Store) *AuthProxy { func ProvideAuthProxy(cfg *setting.Cfg, remoteCache *remotecache.RemoteCache, loginService login.Service, userService user.Service, sqlStore sqlstore.Store) *AuthProxy {
return &AuthProxy{ return &AuthProxy{
cfg: cfg, cfg: cfg,
remoteCache: remoteCache, remoteCache: remoteCache,
loginService: loginService, loginService: loginService,
sqlStore: sqlStore, sqlStore: sqlStore,
userService: userService,
logger: log.New("auth.proxy"), logger: log.New("auth.proxy"),
} }
} }
@ -347,16 +349,10 @@ func (auth *AuthProxy) headersIterator(reqCtx *models.ReqContext, fn func(field
// GetSignedInUser gets full signed in user info. // GetSignedInUser gets full signed in user info.
func (auth *AuthProxy) GetSignedInUser(userID int64, orgID int64) (*user.SignedInUser, error) { func (auth *AuthProxy) GetSignedInUser(userID int64, orgID int64) (*user.SignedInUser, error) {
query := &models.GetSignedInUserQuery{ return auth.userService.GetSignedInUser(context.Background(), &user.GetSignedInUserQuery{
OrgId: orgID, OrgID: orgID,
UserId: userID, UserID: userID,
} })
if err := auth.sqlStore.GetSignedInUser(context.Background(), query); err != nil {
return nil, err
}
return query.Result, nil
} }
// Remember user in cache // Remember user in cache

View File

@ -7,6 +7,9 @@ import (
"net/http" "net/http"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/remotecache" "github.com/grafana/grafana/pkg/infra/remotecache"
"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"
@ -15,9 +18,6 @@ import (
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
const hdrName = "markelog" const hdrName = "markelog"
@ -48,7 +48,7 @@ func prepareMiddleware(t *testing.T, remoteCache *remotecache.RemoteCache, confi
}, },
} }
return ProvideAuthProxy(cfg, remoteCache, loginService, nil), ctx return ProvideAuthProxy(cfg, remoteCache, loginService, nil, nil), ctx
} }
func TestMiddlewareContext(t *testing.T) { func TestMiddlewareContext(t *testing.T) {

View File

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/team/teamimpl" "github.com/grafana/grafana/pkg/services/team/teamimpl"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/userimpl"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
@ -603,12 +604,13 @@ func setupAccessControlGuardianTest(t *testing.T, uid string, permissions []acce
license := licensingtest.NewFakeLicensing() license := licensingtest.NewFakeLicensing()
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe() license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()
teamSvc := teamimpl.ProvideService(store, store.Cfg) teamSvc := teamimpl.ProvideService(store, store.Cfg)
userSvc := userimpl.ProvideService(store, nil, store.Cfg, store)
folderPermissions, err := ossaccesscontrol.ProvideFolderPermissions( folderPermissions, err := ossaccesscontrol.ProvideFolderPermissions(
setting.NewCfg(), routing.NewRouteRegister(), store, ac, license, &dashboards.FakeDashboardStore{}, ac, teamSvc) setting.NewCfg(), routing.NewRouteRegister(), store, ac, license, &dashboards.FakeDashboardStore{}, ac, teamSvc, userSvc)
require.NoError(t, err) require.NoError(t, err)
dashboardPermissions, err := ossaccesscontrol.ProvideDashboardPermissions( dashboardPermissions, err := ossaccesscontrol.ProvideDashboardPermissions(
setting.NewCfg(), routing.NewRouteRegister(), store, ac, license, &dashboards.FakeDashboardStore{}, ac, teamSvc) setting.NewCfg(), routing.NewRouteRegister(), store, ac, license, &dashboards.FakeDashboardStore{}, ac, teamSvc, userSvc)
require.NoError(t, err) require.NoError(t, err)
if dashboardSvc == nil { if dashboardSvc == nil {
dashboardSvc = &dashboards.FakeDashboardService{} dashboardSvc = &dashboards.FakeDashboardService{}

View File

@ -148,7 +148,7 @@ func (ls *Implementation) UpsertUser(ctx context.Context, cmd *models.UpsertUser
// Sync isGrafanaAdmin permission // Sync isGrafanaAdmin permission
if extUser.IsGrafanaAdmin != nil && *extUser.IsGrafanaAdmin != cmd.Result.IsAdmin { if extUser.IsGrafanaAdmin != nil && *extUser.IsGrafanaAdmin != cmd.Result.IsAdmin {
if errPerms := ls.SQLStore.UpdateUserPermissions(cmd.Result.ID, *extUser.IsGrafanaAdmin); errPerms != nil { if errPerms := ls.userService.UpdatePermissions(cmd.Result.ID, *extUser.IsGrafanaAdmin); errPerms != nil {
return errPerms return errPerms
} }
} }
@ -334,9 +334,9 @@ func (ls *Implementation) syncOrgRoles(ctx context.Context, usr *user.User, extU
break break
} }
return ls.SQLStore.SetUsingOrg(ctx, &models.SetUsingOrgCommand{ return ls.userService.SetUsingOrg(ctx, &user.SetUsingOrgCommand{
UserId: usr.ID, UserID: usr.ID,
OrgId: usr.OrgID, OrgID: usr.OrgID,
}) })
} }

View File

@ -9,6 +9,8 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana-plugin-sdk-go/experimental" "github.com/grafana/grafana-plugin-sdk-go/experimental"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/tracing"
ac "github.com/grafana/grafana/pkg/services/accesscontrol" ac "github.com/grafana/grafana/pkg/services/accesscontrol"
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
@ -17,7 +19,6 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require"
) )
var ( var (
@ -83,7 +84,9 @@ var (
) )
func service(t *testing.T) *StandardSearchService { func service(t *testing.T) *StandardSearchService {
service, ok := ProvideService(&setting.Cfg{Search: setting.SearchSettings{}}, nil, nil, accesscontrolmock.New(), tracing.InitializeTracerForTest(), featuremgmt.WithFeatures(), nil).(*StandardSearchService) service, ok := ProvideService(&setting.Cfg{Search: setting.SearchSettings{}},
nil, nil, accesscontrolmock.New(), tracing.InitializeTracerForTest(), featuremgmt.WithFeatures(),
nil, nil).(*StandardSearchService)
require.True(t, ok) require.True(t, ok)
return service return service
} }

View File

@ -6,6 +6,10 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
@ -17,10 +21,6 @@ import (
"github.com/grafana/grafana/pkg/services/store" "github.com/grafana/grafana/pkg/services/store"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/grafana/grafana-plugin-sdk-go/backend"
) )
var ( var (
@ -63,11 +63,12 @@ var (
type StandardSearchService struct { type StandardSearchService struct {
registry.BackgroundService registry.BackgroundService
cfg *setting.Cfg cfg *setting.Cfg
sql *sqlstore.SQLStore sql *sqlstore.SQLStore
auth FutureAuthService // eventually injected from elsewhere auth FutureAuthService // eventually injected from elsewhere
ac accesscontrol.Service ac accesscontrol.Service
orgService org.Service orgService org.Service
userService user.Service
logger log.Logger logger log.Logger
dashboardIndex *searchIndex dashboardIndex *searchIndex
@ -79,7 +80,9 @@ func (s *StandardSearchService) IsReady(ctx context.Context, orgId int64) IsSear
return s.dashboardIndex.isInitialized(ctx, orgId) return s.dashboardIndex.isInitialized(ctx, orgId)
} }
func ProvideService(cfg *setting.Cfg, sql *sqlstore.SQLStore, entityEventStore store.EntityEventsService, ac accesscontrol.Service, tracer tracing.Tracer, features featuremgmt.FeatureToggles, orgService org.Service) SearchService { func ProvideService(cfg *setting.Cfg, sql *sqlstore.SQLStore, entityEventStore store.EntityEventsService,
ac accesscontrol.Service, tracer tracing.Tracer, features featuremgmt.FeatureToggles, orgService org.Service,
userService user.Service) SearchService {
extender := &NoopExtender{} extender := &NoopExtender{}
s := &StandardSearchService{ s := &StandardSearchService{
cfg: cfg, cfg: cfg,
@ -98,10 +101,11 @@ func ProvideService(cfg *setting.Cfg, sql *sqlstore.SQLStore, entityEventStore s
features, features,
cfg.Search, cfg.Search,
), ),
logger: log.New("searchV2"), logger: log.New("searchV2"),
extender: extender, extender: extender,
reIndexCh: make(chan struct{}, 1), reIndexCh: make(chan struct{}, 1),
orgService: orgService, orgService: orgService,
userService: userService,
} }
return s return s
} }
@ -157,23 +161,22 @@ func (s *StandardSearchService) getUser(ctx context.Context, backendUser *backen
IsAnonymous: true, IsAnonymous: true,
} }
} else { } else {
getSignedInUserQuery := &models.GetSignedInUserQuery{ getSignedInUserQuery := &user.GetSignedInUserQuery{
Login: backendUser.Login, Login: backendUser.Login,
Email: backendUser.Email, Email: backendUser.Email,
OrgId: orgId, OrgID: orgId,
} }
err := s.sql.GetSignedInUser(ctx, getSignedInUserQuery) var err error
usr, err = s.userService.GetSignedInUser(ctx, getSignedInUserQuery)
if err != nil { if err != nil {
s.logger.Error("Error while retrieving user", "error", err, "email", backendUser.Email, "login", getSignedInUserQuery.Login) s.logger.Error("Error while retrieving user", "error", err, "email", backendUser.Email, "login", getSignedInUserQuery.Login)
return nil, errors.New("auth error") return nil, errors.New("auth error")
} }
if getSignedInUserQuery.Result == nil { if usr == nil {
s.logger.Error("No user found", "email", backendUser.Email) s.logger.Error("No user found", "email", backendUser.Email)
return nil, errors.New("auth error") return nil, errors.New("auth error")
} }
usr = getSignedInUserQuery.Result
} }
if s.ac.IsDisabled() { if s.ac.IsDisabled() {

View File

@ -11,6 +11,9 @@ import (
"strconv" "strconv"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/kvstore"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
@ -29,10 +32,9 @@ import (
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/team/teamimpl" "github.com/grafana/grafana/pkg/services/team/teamimpl"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/userimpl"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
var ( var (
@ -282,7 +284,9 @@ func setupTestServer(t *testing.T, svc *tests.ServiceAccountMock,
sqlStore *sqlstore.SQLStore, saStore serviceaccounts.Store) (*web.Mux, *ServiceAccountsAPI) { sqlStore *sqlstore.SQLStore, saStore serviceaccounts.Store) (*web.Mux, *ServiceAccountsAPI) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
teamSvc := teamimpl.ProvideService(sqlStore, cfg) teamSvc := teamimpl.ProvideService(sqlStore, cfg)
saPermissionService, err := ossaccesscontrol.ProvideServiceAccountPermissions(cfg, routing.NewRouteRegister(), sqlStore, acmock, &licensing.OSSLicensingService{}, saStore, acmock, teamSvc) userSvc := userimpl.ProvideService(sqlStore, nil, cfg, sqlStore)
saPermissionService, err := ossaccesscontrol.ProvideServiceAccountPermissions(
cfg, routing.NewRouteRegister(), sqlStore, acmock, &licensing.OSSLicensingService{}, saStore, acmock, teamSvc, userSvc)
require.NoError(t, err) require.NoError(t, err)
a := NewServiceAccountsAPI(cfg, svc, acmock, routerRegister, saStore, saPermissionService) a := NewServiceAccountsAPI(cfg, svc, acmock, routerRegister, saStore, saPermissionService)

View File

@ -5,11 +5,12 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestIntegrationStatsDataAccess(t *testing.T) { func TestIntegrationStatsDataAccess(t *testing.T) {
@ -118,13 +119,6 @@ func populateDB(t *testing.T, sqlStore *SQLStore) {
err = sqlStore.AddOrgUser(context.Background(), cmd) err = sqlStore.AddOrgUser(context.Background(), cmd)
require.NoError(t, err) require.NoError(t, err)
// update 1st user last seen at
updateUserLastSeenAtCmd := &models.UpdateUserLastSeenAtCommand{
UserId: users[0].ID,
}
err = sqlStore.UpdateUserLastSeenAt(context.Background(), updateUserLastSeenAtCmd)
require.NoError(t, err)
// force renewal of user stats // force renewal of user stats
err = sqlStore.updateUserRoleCountsIfNecessary(context.Background(), true) err = sqlStore.updateUserRoleCountsIfNecessary(context.Background(), true)
require.NoError(t, err) require.NoError(t, err)

View File

@ -17,27 +17,6 @@ import (
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
type ErrCaseInsensitiveLoginConflict struct {
users []user.User
}
func (e *ErrCaseInsensitiveLoginConflict) Unwrap() error {
return user.ErrCaseInsensitive
}
func (e *ErrCaseInsensitiveLoginConflict) Error() string {
n := len(e.users)
userStrings := make([]string, 0, n)
for _, v := range e.users {
userStrings = append(userStrings, fmt.Sprintf("%s (email:%s, id:%d)", v.Login, v.Email, v.ID))
}
return fmt.Sprintf(
"Found a conflict in user login information. %d users already exist with either the same login or email: [%s].",
n, strings.Join(userStrings, ", "))
}
func (ss *SQLStore) getOrgIDForNewUser(sess *DBSession, args user.CreateUserCommand) (int64, error) { func (ss *SQLStore) getOrgIDForNewUser(sess *DBSession, args user.CreateUserCommand) (int64, error) {
if ss.Cfg.AutoAssignOrg && args.OrgID != 0 { if ss.Cfg.AutoAssignOrg && args.OrgID != 0 {
if err := verifyExistingOrg(sess, args.OrgID); err != nil { if err := verifyExistingOrg(sess, args.OrgID); err != nil {
@ -63,7 +42,7 @@ func (ss *SQLStore) userCaseInsensitiveLoginConflict(ctx context.Context, sess *
} }
if len(users) > 1 { if len(users) > 1 {
return &ErrCaseInsensitiveLoginConflict{users} return &user.ErrCaseInsensitiveLoginConflict{Users: users}
} }
return nil return nil

View File

@ -5,10 +5,12 @@ import (
"fmt" "fmt"
"github.com/grafana/grafana/pkg/events" "github.com/grafana/grafana/pkg/events"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/db" "github.com/grafana/grafana/pkg/services/sqlstore/db"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator" "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
) )
type store interface { type store interface {
@ -23,6 +25,17 @@ type store interface {
type sqlStore struct { type sqlStore struct {
db db.DB db db.DB
dialect migrator.Dialect dialect migrator.Dialect
logger log.Logger
cfg *setting.Cfg
}
func ProvideStore(db db.DB, cfg *setting.Cfg) sqlStore {
return sqlStore{
db: db,
dialect: db.GetDialect(),
cfg: cfg,
logger: log.New("user.store"),
}
} }
func (ss *sqlStore) Insert(ctx context.Context, cmd *user.User) (int64, error) { func (ss *sqlStore) Insert(ctx context.Context, cmd *user.User) (int64, error) {

View File

@ -5,10 +5,11 @@ import (
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require"
) )
func TestIntegrationUserDataAccess(t *testing.T) { func TestIntegrationUserDataAccess(t *testing.T) {
@ -17,7 +18,7 @@ func TestIntegrationUserDataAccess(t *testing.T) {
} }
ss := sqlstore.InitTestDB(t) ss := sqlstore.InitTestDB(t)
userStore := sqlStore{db: ss} userStore := ProvideStore(ss, setting.NewCfg())
t.Run("user not found", func(t *testing.T) { t.Run("user not found", func(t *testing.T) {
_, err := userStore.Get(context.Background(), _, err := userStore.Get(context.Background(),

View File

@ -6,67 +6,34 @@ import (
"time" "time"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
pref "github.com/grafana/grafana/pkg/services/preference"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/db" "github.com/grafana/grafana/pkg/services/sqlstore/db"
"github.com/grafana/grafana/pkg/services/star"
"github.com/grafana/grafana/pkg/services/teamguardian"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/userauth"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
"golang.org/x/sync/errgroup"
) )
type Service struct { type Service struct {
store store store store
orgService org.Service orgService org.Service
starService star.Service
dashboardService dashboards.DashboardService
preferenceService pref.Service
teamMemberService teamguardian.TeamGuardian
userAuthService userauth.Service
quotaService quota.Service
accessControlStore accesscontrol.Service
// TODO remove sqlstore // TODO remove sqlstore
sqlStore *sqlstore.SQLStore sqlStore *sqlstore.SQLStore
cfg *setting.Cfg
cfg *setting.Cfg
} }
func ProvideService( func ProvideService(
db db.DB, db db.DB,
orgService org.Service, orgService org.Service,
starService star.Service,
dashboardService dashboards.DashboardService,
preferenceService pref.Service,
teamMemberService teamguardian.TeamGuardian,
userAuthService userauth.Service,
quotaService quota.Service,
accessControlStore accesscontrol.Service,
cfg *setting.Cfg, cfg *setting.Cfg,
ss *sqlstore.SQLStore, ss *sqlstore.SQLStore,
) user.Service { ) user.Service {
store := ProvideStore(db, cfg)
return &Service{ return &Service{
store: &sqlStore{ store: &store,
db: db, orgService: orgService,
dialect: db.GetDialect(), cfg: cfg,
}, sqlStore: ss,
orgService: orgService,
starService: starService,
dashboardService: dashboardService,
preferenceService: preferenceService,
teamMemberService: teamMemberService,
userAuthService: userAuthService,
quotaService: quotaService,
accessControlStore: accessControlStore,
cfg: cfg,
sqlStore: ss,
} }
} }
@ -169,70 +136,7 @@ func (s *Service) Delete(ctx context.Context, cmd *user.DeleteUserCommand) error
return err return err
} }
// delete from all the stores // delete from all the stores
if err := s.store.Delete(ctx, cmd.UserID); err != nil { return s.store.Delete(ctx, cmd.UserID)
return err
}
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error {
if err := s.starService.DeleteByUser(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := s.orgService.DeleteUserFromAll(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := s.dashboardService.DeleteACLByUser(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := s.preferenceService.DeleteByUser(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := s.teamMemberService.DeleteByUser(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := s.userAuthService.Delete(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := s.userAuthService.DeleteToken(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := s.quotaService.DeleteByUser(ctx, cmd.UserID); err != nil {
return err
}
return nil
})
g.Go(func() error {
if err := s.accessControlStore.DeleteUserPermissions(ctx, accesscontrol.GlobalOrgID, cmd.UserID); err != nil {
return err
}
return nil
})
if err := g.Wait(); err != nil {
return err
}
return nil
} }
func (s *Service) GetByID(ctx context.Context, query *user.GetUserByIDQuery) (*user.User, error) { func (s *Service) GetByID(ctx context.Context, query *user.GetUserByIDQuery) (*user.User, error) {
@ -331,10 +235,7 @@ func (s *Service) GetSignedInUser(ctx context.Context, query *user.GetSignedInUs
OrgId: query.OrgID, OrgId: query.OrgID,
} }
err := s.sqlStore.GetSignedInUser(ctx, q) err := s.sqlStore.GetSignedInUser(ctx, q)
if err != nil { return q.Result, err
return nil, err
}
return q.Result, nil
} }
// TODO: remove wrapper around sqlstore // TODO: remove wrapper around sqlstore

View File

@ -2,18 +2,10 @@ package userimpl
import ( import (
"context" "context"
"errors"
"testing" "testing"
"github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/org/orgtest" "github.com/grafana/grafana/pkg/services/org/orgtest"
"github.com/grafana/grafana/pkg/services/preference/preftest"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/star/startest"
"github.com/grafana/grafana/pkg/services/teamguardian/manager"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/userauth/userauthtest"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -22,23 +14,9 @@ import (
func TestUserService(t *testing.T) { func TestUserService(t *testing.T) {
userStore := newUserStoreFake() userStore := newUserStoreFake()
orgService := orgtest.NewOrgServiceFake() orgService := orgtest.NewOrgServiceFake()
starService := startest.NewStarServiceFake()
dashboardService := dashboards.NewFakeDashboardService(t)
preferenceService := preftest.NewPreferenceServiceFake()
teamMemberService := manager.NewTeamGuardianMock()
userAuthService := userauthtest.NewFakeUserAuthService()
quotaService := quotatest.NewQuotaServiceFake()
accessControlStore := mock.New()
userService := Service{ userService := Service{
store: userStore, store: userStore,
orgService: orgService, orgService: orgService,
starService: starService,
dashboardService: dashboardService,
preferenceService: preferenceService,
teamMemberService: teamMemberService,
userAuthService: userAuthService,
quotaService: quotaService,
accessControlStore: accessControlStore,
} }
t.Run("create user", func(t *testing.T) { t.Run("create user", func(t *testing.T) {
@ -81,26 +59,6 @@ func TestUserService(t *testing.T) {
require.Error(t, err, user.ErrUserNotFound) require.Error(t, err, user.ErrUserNotFound)
}) })
t.Run("delete user returns from team", func(t *testing.T) {
teamMemberService.ExpectedError = errors.New("some error")
t.Cleanup(func() {
teamMemberService.ExpectedError = nil
})
err := userService.Delete(context.Background(), &user.DeleteUserCommand{UserID: 1})
require.Error(t, err)
})
t.Run("delete user returns from team and pref", func(t *testing.T) {
teamMemberService.ExpectedError = errors.New("some error")
preferenceService.ExpectedError = errors.New("some error 2")
t.Cleanup(func() {
teamMemberService.ExpectedError = nil
preferenceService.ExpectedError = nil
})
err := userService.Delete(context.Background(), &user.DeleteUserCommand{UserID: 1})
require.Error(t, err)
})
t.Run("delete user successfully", func(t *testing.T) { t.Run("delete user successfully", func(t *testing.T) {
err := userService.Delete(context.Background(), &user.DeleteUserCommand{UserID: 1}) err := userService.Delete(context.Background(), &user.DeleteUserCommand{UserID: 1})
require.NoError(t, err) require.NoError(t, err)
@ -115,26 +73,6 @@ func TestUserService(t *testing.T) {
require.Error(t, err, user.ErrUserNotFound) require.Error(t, err, user.ErrUserNotFound)
}) })
t.Run("delete user returns from team", func(t *testing.T) {
teamMemberService.ExpectedError = errors.New("some error")
t.Cleanup(func() {
teamMemberService.ExpectedError = nil
})
err := userService.Delete(context.Background(), &user.DeleteUserCommand{UserID: 1})
require.Error(t, err)
})
t.Run("delete user returns from team and pref", func(t *testing.T) {
teamMemberService.ExpectedError = errors.New("some error")
preferenceService.ExpectedError = errors.New("some error 2")
t.Cleanup(func() {
teamMemberService.ExpectedError = nil
preferenceService.ExpectedError = nil
})
err := userService.Delete(context.Background(), &user.DeleteUserCommand{UserID: 1})
require.Error(t, err)
})
t.Run("delete user successfully", func(t *testing.T) { t.Run("delete user successfully", func(t *testing.T) {
err := userService.Delete(context.Background(), &user.DeleteUserCommand{UserID: 1}) err := userService.Delete(context.Background(), &user.DeleteUserCommand{UserID: 1})
require.NoError(t, err) require.NoError(t, err)

View File

@ -13,6 +13,8 @@ type FakeUserService struct {
ExpectedSetUsingOrgError error ExpectedSetUsingOrgError error
ExpectedSearchUsers user.SearchUserQueryResult ExpectedSearchUsers user.SearchUserQueryResult
ExpectedUSerProfileDTO user.UserProfileDTO ExpectedUSerProfileDTO user.UserProfileDTO
GetSignedInUserFn func(ctx context.Context, query *user.GetSignedInUserQuery) (*user.SignedInUser, error)
} }
func NewUserServiceFake() *FakeUserService { func NewUserServiceFake() *FakeUserService {
@ -60,6 +62,9 @@ func (f *FakeUserService) GetSignedInUserWithCacheCtx(ctx context.Context, query
} }
func (f *FakeUserService) GetSignedInUser(ctx context.Context, query *user.GetSignedInUserQuery) (*user.SignedInUser, error) { func (f *FakeUserService) GetSignedInUser(ctx context.Context, query *user.GetSignedInUserQuery) (*user.SignedInUser, error) {
if f.GetSignedInUserFn != nil {
return f.GetSignedInUserFn(ctx, query)
}
if f.ExpectedSignedInUser == nil { if f.ExpectedSignedInUser == nil {
return &user.SignedInUser{}, f.ExpectedError return &user.SignedInUser{}, f.ExpectedError
} }