From 1204d607a5ed305aa257de70585d54a895f04e31 Mon Sep 17 00:00:00 2001 From: Karl Persson Date: Thu, 2 Feb 2023 15:33:25 +0100 Subject: [PATCH] RBAC: Rewrite org users rbac tests (#62469) * API: refactor legacy org users access control tests --- pkg/api/common_test.go | 28 - pkg/api/org_users_test.go | 870 +++++++++++------------------ pkg/services/team/teamtest/team.go | 3 +- 3 files changed, 321 insertions(+), 580 deletions(-) diff --git a/pkg/api/common_test.go b/pkg/api/common_test.go index 735aa557fc5..2929fb9ec20 100644 --- a/pkg/api/common_test.go +++ b/pkg/api/common_test.go @@ -253,14 +253,6 @@ func (s *fakeRenderService) Init() error { return nil } -type accessControlTestCase struct { - expectedCode int - desc string - url string - method string - permissions []accesscontrol.Permission -} - // accessControlScenarioContext contains the setups for accesscontrol tests type accessControlScenarioContext struct { // server we registered hs routes on. @@ -289,16 +281,6 @@ type accessControlScenarioContext struct { dashboardPermissionsService *accesscontrolmock.MockPermissionsService } -func setAccessControlPermissions(acmock *accesscontrolmock.Mock, perms []accesscontrol.Permission, org int64) { - acmock.GetUserPermissionsFunc = - func(_ context.Context, u *user.SignedInUser, _ accesscontrol.Options) ([]accesscontrol.Permission, error) { - if u.OrgID == org { - return perms, nil - } - return nil, nil - } -} - func userWithPermissions(orgID int64, permissions []accesscontrol.Permission) *user.SignedInUser { return &user.SignedInUser{OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByAction(permissions)}} } @@ -309,16 +291,6 @@ func setInitCtxSignedInUser(initCtx *contextmodel.ReqContext, user user.SignedIn initCtx.SignedInUser = &user } -func setInitCtxSignedInViewer(initCtx *contextmodel.ReqContext) { - initCtx.IsSignedIn = true - initCtx.SignedInUser = &user.SignedInUser{UserID: testUserID, OrgID: 1, OrgRole: org.RoleViewer, Login: testUserLogin} -} - -func setInitCtxSignedInOrgAdmin(initCtx *contextmodel.ReqContext) { - initCtx.IsSignedIn = true - initCtx.SignedInUser = &user.SignedInUser{UserID: testUserID, OrgID: 1, OrgRole: org.RoleAdmin, Login: testUserLogin} -} - func setupSimpleHTTPServer(features *featuremgmt.FeatureManager) *HTTPServer { if features == nil { features = featuremgmt.WithFeatures() diff --git a/pkg/api/org_users_test.go b/pkg/api/org_users_test.go index 47902f5045d..2f2d0cd8f7f 100644 --- a/pkg/api/org_users_test.go +++ b/pkg/api/org_users_test.go @@ -4,34 +4,34 @@ import ( "context" "encoding/json" "fmt" + "io" "net/http" "strings" "testing" - "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/api/dtos" - "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db/dbtest" - "github.com/grafana/grafana/pkg/infra/localcache" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/accesscontrol/actest" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/services/login/logintest" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org/orgimpl" "github.com/grafana/grafana/pkg/services/org/orgtest" "github.com/grafana/grafana/pkg/services/quota/quotaimpl" - "github.com/grafana/grafana/pkg/services/quota/quotatest" "github.com/grafana/grafana/pkg/services/sqlstore" - "github.com/grafana/grafana/pkg/services/team/teamimpl" - "github.com/grafana/grafana/pkg/services/temp_user/tempuserimpl" + "github.com/grafana/grafana/pkg/services/team/teamtest" "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/setting" - "github.com/grafana/grafana/pkg/util" + "github.com/grafana/grafana/pkg/web/webtest" ) func setUpGetOrgUsersDB(t *testing.T, sqlStore *sqlstore.SQLStore) { @@ -196,256 +196,189 @@ func TestOrgUsersAPIEndpoint_userLoggedIn(t *testing.T) { }) } -func TestOrgUsersAPIEndpoint_LegacyAccessControl_FolderAdmin(t *testing.T) { - cfg := setting.NewCfg() - cfg.RBACEnabled = false - sc := setupHTTPServerWithCfg(t, true, cfg) - setInitCtxSignedInViewer(sc.initCtx) - - // Create a dashboard folder - cmd := dashboards.SaveDashboardCommand{ - OrgID: testOrgID, - FolderID: 1, - IsFolder: true, - Dashboard: simplejson.NewFromAny(map[string]interface{}{ - "id": nil, - "title": "1 test dash folder", - "tags": "prod", - }), +func TestOrgUsersAPIEndpoint_LegacyAccessControl(t *testing.T) { + type testCase struct { + desc string + isTeamAdmin bool + isFolderAdmin bool + role org.RoleType + expectedCode int } - folder, err := sc.dashboardsStore.SaveDashboard(context.Background(), cmd) - require.NoError(t, err) - require.NotNil(t, folder) - // Grant our test Viewer with permission to admin the folder - acls := []*dashboards.DashboardACL{ + tests := []testCase{ { - DashboardID: folder.ID, - OrgID: testOrgID, - UserID: testUserID, - Permission: dashboards.PERMISSION_ADMIN, - Created: time.Now(), - Updated: time.Now(), + desc: "should be able to search org user when user is folder admin", + isFolderAdmin: true, + role: org.RoleViewer, + expectedCode: http.StatusOK, + }, + { + desc: "should be able to search org user when user is team admin", + isFolderAdmin: true, + role: org.RoleViewer, + expectedCode: http.StatusOK, + }, + { + desc: "should be able to search org user when user is admin", + role: org.RoleAdmin, + expectedCode: http.StatusOK, + }, + { + desc: "should not be able to search org user when user is viewer", + role: org.RoleViewer, + expectedCode: http.StatusForbidden, + }, + { + desc: "should not be able to search org user when user is editor", + role: org.RoleEditor, + expectedCode: http.StatusForbidden, }, } - err = sc.dashboardsStore.UpdateDashboardACL(context.Background(), folder.ID, acls) - require.NoError(t, err) - response := callAPI(sc.server, http.MethodGet, "/api/org/users/lookup", nil, t) - assert.Equal(t, http.StatusOK, response.Code) -} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + server := SetupAPITestServer(t, func(hs *HTTPServer) { + cfg := setting.NewCfg() + cfg.RBACEnabled = false + hs.Cfg = cfg -func TestOrgUsersAPIEndpoint_LegacyAccessControl_TeamAdmin(t *testing.T) { - cfg := setting.NewCfg() - cfg.RBACEnabled = false - sc := setupHTTPServerWithCfg(t, true, cfg) - setInitCtxSignedInViewer(sc.initCtx) + dashboardService := dashboards.NewFakeDashboardService(t) + dashboardService.On("HasAdminPermissionInDashboardsOrFolders", mock.Anything, mock.Anything).Return(tt.isFolderAdmin, nil).Maybe() + hs.DashboardService = dashboardService - // Setup store teams - team1, err := sc.teamService.CreateTeam("testteam1", "testteam1@example.org", testOrgID) - require.NoError(t, err) - err = sc.teamService.AddTeamMember(testUserID, testOrgID, team1.ID, false, dashboards.PERMISSION_ADMIN) - require.NoError(t, err) + teamService := teamtest.NewFakeService() + teamService.ExpectedIsAdmin = tt.isTeamAdmin + hs.teamService = teamService + hs.orgService = &orgtest.FakeOrgService{ExpectedSearchOrgUsersResult: &org.SearchOrgUsersQueryResult{}} + hs.authInfoService = &logintest.AuthInfoServiceFake{} + }) - response := callAPI(sc.server, http.MethodGet, "/api/org/users/lookup", nil, t) - assert.Equal(t, http.StatusOK, response.Code) -} - -func TestOrgUsersAPIEndpoint_LegacyAccessControl_Admin(t *testing.T) { - cfg := setting.NewCfg() - cfg.RBACEnabled = false - sc := setupHTTPServerWithCfg(t, true, cfg) - setInitCtxSignedInOrgAdmin(sc.initCtx) - - response := callAPI(sc.server, http.MethodGet, "/api/org/users/lookup", nil, t) - assert.Equal(t, http.StatusOK, response.Code) -} - -func TestOrgUsersAPIEndpoint_LegacyAccessControl_Viewer(t *testing.T) { - cfg := setting.NewCfg() - cfg.RBACEnabled = false - sc := setupHTTPServerWithCfg(t, true, cfg) - setInitCtxSignedInViewer(sc.initCtx) - - response := callAPI(sc.server, http.MethodGet, "/api/org/users/lookup", nil, t) - assert.Equal(t, http.StatusForbidden, response.Code) + res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/org/users/lookup"), &user.SignedInUser{OrgID: 1, OrgRole: tt.role})) + require.NoError(t, err) + assert.Equal(t, tt.expectedCode, res.StatusCode) + require.NoError(t, res.Body.Close()) + }) + } } func TestOrgUsersAPIEndpoint_AccessControl(t *testing.T) { - tests := []accessControlTestCase{ + type testCase struct { + desc string + permissions []accesscontrol.Permission + expectedCode int + } + tests := []testCase{ { expectedCode: http.StatusOK, desc: "UsersLookupGet should return 200 for user with correct permissions", - url: "/api/org/users/lookup", - method: http.MethodGet, permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersRead, Scope: accesscontrol.ScopeUsersAll}}, }, { expectedCode: http.StatusForbidden, desc: "UsersLookupGet should return 403 for user without required permissions", - url: "/api/org/users/lookup", - method: http.MethodGet, permissions: []accesscontrol.Permission{{Action: "wrong"}}, }, } - for _, test := range tests { - t.Run(test.desc, func(t *testing.T) { - sc := setupHTTPServer(t, true) - setInitCtxSignedInViewer(sc.initCtx) - setAccessControlPermissions(sc.acmock, test.permissions, sc.initCtx.OrgID) - - response := callAPI(sc.server, http.MethodGet, test.url, nil, t) - assert.Equal(t, test.expectedCode, response.Code) + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + server := SetupAPITestServer(t, func(hs *HTTPServer) { + hs.Cfg = setting.NewCfg() + hs.orgService = &orgtest.FakeOrgService{ExpectedSearchOrgUsersResult: &org.SearchOrgUsersQueryResult{}} + hs.authInfoService = &logintest.AuthInfoServiceFake{} + }) + res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/org/users/lookup"), userWithPermissions(1, tt.permissions))) + require.NoError(t, err) + assert.Equal(t, tt.expectedCode, res.StatusCode) + require.NoError(t, res.Body.Close()) }) } } -var ( - testServerAdminViewer = user.SignedInUser{ - UserID: 1, - OrgID: 1, - OrgName: "TestOrg1", - OrgRole: org.RoleViewer, - Login: "testServerAdmin", - Name: "testServerAdmin", - Email: "testServerAdmin@example.org", - OrgCount: 2, - IsGrafanaAdmin: true, - IsAnonymous: false, - } - - testAdminOrg2 = user.SignedInUser{ - UserID: 2, - OrgID: 2, - OrgName: "TestOrg2", - OrgRole: org.RoleAdmin, - Login: "testAdmin", - Name: "testAdmin", - Email: "testAdmin@example.org", - OrgCount: 1, - IsGrafanaAdmin: false, - IsAnonymous: false, - } - - testEditorOrg1 = user.SignedInUser{ - UserID: 3, - OrgID: 1, - OrgName: "TestOrg1", - OrgRole: org.RoleEditor, - Login: "testEditor", - Name: "testEditor", - Email: "testEditor@example.org", - OrgCount: 1, - IsGrafanaAdmin: false, - IsAnonymous: false, - } -) - -// setupOrgUsersDBForAccessControlTests creates three users placed in two orgs -// Org1: testServerAdminViewer, testEditorOrg1 -// Org2: testServerAdminViewer, testAdminOrg2 -func setupOrgUsersDBForAccessControlTests(t *testing.T, db *sqlstore.SQLStore, orgService org.Service) { - t.Helper() - - quotaService := quotaimpl.ProvideService(db, db.Cfg) - usrSvc, err := userimpl.ProvideService(db, orgService, db.Cfg, nil, nil, quotaService) - require.NoError(t, err) - - _, err = usrSvc.Create(context.Background(), &user.CreateUserCommand{Email: testServerAdminViewer.Email, SkipOrgSetup: true, Login: testServerAdminViewer.Login}) - require.NoError(t, err) - _, err = usrSvc.Create(context.Background(), &user.CreateUserCommand{Email: testAdminOrg2.Email, SkipOrgSetup: true, Login: testAdminOrg2.Login}) - require.NoError(t, err) - _, err = usrSvc.Create(context.Background(), &user.CreateUserCommand{Email: testEditorOrg1.Email, SkipOrgSetup: true, Login: testEditorOrg1.Login}) - require.NoError(t, err) - - // Create both orgs with server admin - _, err = orgService.CreateWithMember(context.Background(), &org.CreateOrgCommand{Name: testServerAdminViewer.OrgName, UserID: testServerAdminViewer.UserID}) - require.NoError(t, err) - _, err = orgService.CreateWithMember(context.Background(), &org.CreateOrgCommand{Name: testAdminOrg2.OrgName, UserID: testServerAdminViewer.UserID}) - require.NoError(t, err) - - err = orgService.AddOrgUser(context.Background(), &org.AddOrgUserCommand{LoginOrEmail: testAdminOrg2.Login, Role: testAdminOrg2.OrgRole, OrgID: testAdminOrg2.OrgID, UserID: testAdminOrg2.UserID}) - require.NoError(t, err) - err = orgService.AddOrgUser(context.Background(), &org.AddOrgUserCommand{LoginOrEmail: testEditorOrg1.Login, Role: testEditorOrg1.OrgRole, OrgID: testEditorOrg1.OrgID, UserID: testEditorOrg1.UserID}) - require.NoError(t, err) -} - func TestGetOrgUsersAPIEndpoint_AccessControlMetadata(t *testing.T) { - url := "/api/orgs/%v/users?accesscontrol=true" type testCase struct { - name string - enableAccessControl bool - expectedCode int - expectedMetadata map[string]bool - user user.SignedInUser - targetOrg int64 + desc string + permissions []accesscontrol.Permission + includeMetadata bool + expectedCode int + expectedMetadata map[string]bool } tests := []testCase{ { - name: "access control metadata not requested", - enableAccessControl: false, - expectedCode: http.StatusOK, - expectedMetadata: nil, - user: testServerAdminViewer, - targetOrg: testServerAdminViewer.OrgID, + desc: "should not get access control metadata", + includeMetadata: false, + permissions: []accesscontrol.Permission{ + {Action: accesscontrol.ActionOrgUsersRead, Scope: "users:*"}, + {Action: accesscontrol.ActionOrgUsersWrite, Scope: "users:*"}, + {Action: accesscontrol.ActionOrgUsersAdd, Scope: "users:*"}, + {Action: accesscontrol.ActionOrgUsersRemove, Scope: "users:*"}, + }, + expectedCode: http.StatusOK, + expectedMetadata: nil, }, { - name: "access control metadata requested", - enableAccessControl: true, - expectedCode: http.StatusOK, + desc: "should get access control metadata", + includeMetadata: true, + permissions: []accesscontrol.Permission{ + {Action: accesscontrol.ActionOrgUsersRead, Scope: "users:*"}, + {Action: accesscontrol.ActionOrgUsersWrite, Scope: "users:*"}, + {Action: accesscontrol.ActionOrgUsersAdd, Scope: "users:*"}, + {Action: accesscontrol.ActionOrgUsersRemove, Scope: "users:*"}, + }, + expectedCode: http.StatusOK, expectedMetadata: map[string]bool{ - "org.users:write": true, - "org.users:add": true, - "org.users:read": true, - "org.users:remove": true, - "users.permissions:read": true}, - user: testServerAdminViewer, - targetOrg: testServerAdminViewer.OrgID, + "org.users:write": true, + "org.users:add": true, + "org.users:read": true, + "org.users:remove": true, + }, }, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - cfg := setting.NewCfg() - cfg.RBACEnabled = tc.enableAccessControl - var err error - sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { - hs.userService, err = userimpl.ProvideService( - hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(), quotatest.New(false, nil)) - require.NoError(t, err) - hs.orgService, err = orgimpl.ProvideService(hs.SQLStore, cfg, quotatest.New(false, nil)) - require.NoError(t, err) + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + server := SetupAPITestServer(t, func(hs *HTTPServer) { + hs.Cfg = setting.NewCfg() + hs.orgService = &orgtest.FakeOrgService{ + ExpectedSearchOrgUsersResult: &org.SearchOrgUsersQueryResult{OrgUsers: []*org.OrgUserDTO{{UserID: 1}}}, + } + hs.authInfoService = &logintest.AuthInfoServiceFake{} + hs.userService = &usertest.FakeUserService{ExpectedSignedInUser: userWithPermissions(1, tt.permissions)} }) - setupOrgUsersDBForAccessControlTests(t, sc.db, sc.hs.orgService) - setInitCtxSignedInUser(sc.initCtx, tc.user) - // Perform test - response := callAPI(sc.server, http.MethodGet, fmt.Sprintf(url, tc.targetOrg), nil, t) - require.Equal(t, tc.expectedCode, response.Code) + url := "/api/orgs/1/users" + if tt.includeMetadata { + url += "?accesscontrol=true" + } + + res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest(url), userWithPermissions(1, tt.permissions))) + require.NoError(t, err) + assert.Equal(t, tt.expectedCode, res.StatusCode) var userList []*org.OrgUserDTO - err = json.NewDecoder(response.Body).Decode(&userList) + err = json.NewDecoder(res.Body).Decode(&userList) require.NoError(t, err) - if tc.expectedMetadata != nil { - assert.Equal(t, tc.expectedMetadata, userList[0].AccessControl) + if tt.expectedMetadata != nil { + assert.Equal(t, tt.expectedMetadata, userList[0].AccessControl) } else { assert.Nil(t, userList[0].AccessControl) } + + require.NoError(t, res.Body.Close()) }) } } func TestGetOrgUsersAPIEndpoint_AccessControl(t *testing.T) { - url := "/api/orgs/%v/users/" type testCase struct { name string enableAccessControl bool + role org.RoleType + isGrafanaAdmin bool + permissions []accesscontrol.Permission expectedCode int - expectedUserCount int - user user.SignedInUser targetOrg int64 } @@ -453,571 +386,406 @@ func TestGetOrgUsersAPIEndpoint_AccessControl(t *testing.T) { { name: "server admin can get users in his org (legacy)", enableAccessControl: false, + role: org.RoleViewer, + isGrafanaAdmin: true, expectedCode: http.StatusOK, - expectedUserCount: 2, - user: testServerAdminViewer, - targetOrg: testServerAdminViewer.OrgID, + targetOrg: 1, }, { name: "server admin can get users in another org (legacy)", enableAccessControl: false, + isGrafanaAdmin: true, expectedCode: http.StatusOK, - expectedUserCount: 2, - user: testServerAdminViewer, targetOrg: 2, }, { name: "org admin cannot get users in his org (legacy)", enableAccessControl: false, expectedCode: http.StatusForbidden, - user: testAdminOrg2, - targetOrg: testAdminOrg2.OrgID, + targetOrg: 1, }, { name: "org admin cannot get users in another org (legacy)", enableAccessControl: false, expectedCode: http.StatusForbidden, - user: testAdminOrg2, targetOrg: 1, }, { - name: "server admin can get users in his org", + name: "user with permissions can get users in org", enableAccessControl: true, - expectedCode: http.StatusOK, - expectedUserCount: 2, - user: testServerAdminViewer, - targetOrg: testServerAdminViewer.OrgID, + permissions: []accesscontrol.Permission{ + {Action: accesscontrol.ActionOrgUsersRead, Scope: "users:*"}, + }, + expectedCode: http.StatusOK, + targetOrg: 1, }, { - name: "server admin can get users in another org", - enableAccessControl: true, - expectedCode: http.StatusOK, - expectedUserCount: 2, - user: testServerAdminViewer, - targetOrg: 2, - }, - { - name: "org admin can get users in their org", - enableAccessControl: true, - expectedCode: http.StatusOK, - expectedUserCount: 2, - user: testAdminOrg2, - targetOrg: testAdminOrg2.OrgID, - }, - { - name: "org admin cannot get users in another org", + name: "user without permissions cannot get users in org", enableAccessControl: true, expectedCode: http.StatusForbidden, - user: testAdminOrg2, targetOrg: 1, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - cfg := setting.NewCfg() - cfg.RBACEnabled = tc.enableAccessControl - var err error - sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { - quotaService := quotatest.New(false, nil) - hs.userService, err = userimpl.ProvideService( - hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(), quotaService) - require.NoError(t, err) - hs.orgService, err = orgimpl.ProvideService(hs.SQLStore, cfg, quotaService) - require.NoError(t, err) + server := SetupAPITestServer(t, func(hs *HTTPServer) { + hs.Cfg = setting.NewCfg() + hs.Cfg.RBACEnabled = tc.enableAccessControl + hs.orgService = &orgtest.FakeOrgService{ + ExpectedSearchOrgUsersResult: &org.SearchOrgUsersQueryResult{}, + } + hs.authInfoService = &logintest.AuthInfoServiceFake{} + hs.userService = &usertest.FakeUserService{ExpectedSignedInUser: userWithPermissions(1, tc.permissions)} }) - setInitCtxSignedInUser(sc.initCtx, tc.user) - setupOrgUsersDBForAccessControlTests(t, sc.db, sc.hs.orgService) - // Perform test - response := callAPI(sc.server, http.MethodGet, fmt.Sprintf(url, tc.targetOrg), nil, t) - require.Equal(t, tc.expectedCode, response.Code) + u := userWithPermissions(1, tc.permissions) + u.OrgRole = tc.role + u.IsGrafanaAdmin = tc.isGrafanaAdmin - if tc.expectedCode != http.StatusForbidden { - var userList []*org.OrgUserDTO - err := json.NewDecoder(response.Body).Decode(&userList) - require.NoError(t, err) - - assert.Len(t, userList, tc.expectedUserCount) - } + res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest(fmt.Sprintf("/api/orgs/%d/users/", tc.targetOrg)), u)) + require.NoError(t, err) + assert.Equal(t, tc.expectedCode, res.StatusCode) + require.NoError(t, res.Body.Close()) }) } } func TestPostOrgUsersAPIEndpoint_AccessControl(t *testing.T) { - url := "/api/orgs/%v/users/" type testCase struct { - name string + desc string enableAccessControl bool - user user.SignedInUser - targetOrg int64 + permissions []accesscontrol.Permission + isGrafanaAdmin bool + role org.RoleType input string expectedCode int } tests := []testCase{ { - name: "server admin can add users to his org (legacy)", + desc: "server admin can add users to his org (legacy)", enableAccessControl: false, - user: testServerAdminViewer, - targetOrg: testServerAdminViewer.OrgID, - input: `{"loginOrEmail": "` + testAdminOrg2.Login + `", "role": "` + string(testAdminOrg2.OrgRole) + `"}`, + isGrafanaAdmin: true, + input: `{"loginOrEmail": "user", "role": "Viewer"}`, expectedCode: http.StatusOK, }, { - name: "server admin can add users to another org (legacy)", - enableAccessControl: false, - user: testServerAdminViewer, - targetOrg: 2, - input: `{"loginOrEmail": "` + testEditorOrg1.Login + `", "role": "` + string(testEditorOrg1.OrgRole) + `"}`, - expectedCode: http.StatusOK, - }, - { - name: "org admin cannot add users to his org (legacy)", + desc: "org admin cannot add users to his org (legacy)", enableAccessControl: false, + role: org.RoleAdmin, expectedCode: http.StatusForbidden, - user: testAdminOrg2, - targetOrg: testAdminOrg2.OrgID, - input: `{"loginOrEmail": "` + testEditorOrg1.Login + `", "role": "` + string(testEditorOrg1.OrgRole) + `"}`, + input: `{"loginOrEmail": "user", "role": "Viewer"}`, }, { - name: "org admin cannot add users to another org (legacy)", - enableAccessControl: false, - expectedCode: http.StatusForbidden, - user: testAdminOrg2, - targetOrg: 1, - input: `{"loginOrEmail": "` + testAdminOrg2.Login + `", "role": "` + string(testAdminOrg2.OrgRole) + `"}`, - }, - { - name: "server admin can add users to his org", + desc: "user with permissions can add users to org", enableAccessControl: true, - user: testServerAdminViewer, - targetOrg: testServerAdminViewer.OrgID, - input: `{"loginOrEmail": "` + testAdminOrg2.Login + `", "role": "` + string(testAdminOrg2.OrgRole) + `"}`, - expectedCode: http.StatusOK, + role: org.RoleViewer, + permissions: []accesscontrol.Permission{ + {Action: accesscontrol.ActionOrgUsersAdd, Scope: "users:*"}, + }, + input: `{"loginOrEmail": "user", "role": "Viewer"}`, + expectedCode: http.StatusOK, }, { - name: "server admin can add users to another org", - enableAccessControl: true, - user: testServerAdminViewer, - targetOrg: 2, - input: `{"loginOrEmail": "` + testEditorOrg1.Login + `", "role": "` + string(testEditorOrg1.OrgRole) + `"}`, - expectedCode: http.StatusOK, - }, - { - name: "org admin can add users to his org", - enableAccessControl: true, - user: testAdminOrg2, - targetOrg: testAdminOrg2.OrgID, - input: `{"loginOrEmail": "` + testEditorOrg1.Login + `", "role": "` + string(testEditorOrg1.OrgRole) + `"}`, - expectedCode: http.StatusOK, - }, - { - name: "org admin cannot add users to another org", + desc: "user without permissions cannot add users to org", enableAccessControl: true, expectedCode: http.StatusForbidden, - user: testAdminOrg2, - targetOrg: 1, - input: `{"loginOrEmail": "` + testAdminOrg2.Login + `", "role": "` + string(testAdminOrg2.OrgRole) + `"}`, + input: `{"loginOrEmail": "user", "role": "Viewer"}`, }, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - cfg := setting.NewCfg() - cfg.RBACEnabled = tc.enableAccessControl - var err error - sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { - hs.orgService, err = orgimpl.ProvideService(hs.SQLStore, cfg, quotatest.New(false, nil)) - require.NoError(t, err) - hs.userService, err = userimpl.ProvideService( - hs.SQLStore, hs.orgService, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(), quotatest.New(false, nil)) - require.NoError(t, err) + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + server := SetupAPITestServer(t, func(hs *HTTPServer) { + hs.Cfg = setting.NewCfg() + hs.Cfg.RBACEnabled = tt.enableAccessControl + hs.orgService = &orgtest.FakeOrgService{} + hs.authInfoService = &logintest.AuthInfoServiceFake{} + hs.userService = &usertest.FakeUserService{ + ExpectedUser: &user.User{}, + ExpectedSignedInUser: userWithPermissions(1, tt.permissions), + } }) - setupOrgUsersDBForAccessControlTests(t, sc.db, sc.hs.orgService) - setInitCtxSignedInUser(sc.initCtx, tc.user) + u := userWithPermissions(1, tt.permissions) + u.OrgRole = tt.role + u.IsGrafanaAdmin = tt.isGrafanaAdmin - // Perform request - input := strings.NewReader(tc.input) - response := callAPI(sc.server, http.MethodPost, fmt.Sprintf(url, tc.targetOrg), input, t) - assert.Equal(t, tc.expectedCode, response.Code) - - if tc.expectedCode != http.StatusForbidden { - // Check result - var message util.DynMap - err := json.NewDecoder(response.Body).Decode(&message) - require.NoError(t, err) - } + res, err := server.SendJSON(webtest.RequestWithSignedInUser(server.NewPostRequest("/api/orgs/1/users", strings.NewReader(tt.input)), u)) + require.NoError(t, err) + assert.Equal(t, tt.expectedCode, res.StatusCode) + require.NoError(t, res.Body.Close()) }) } } func TestOrgUsersAPIEndpointWithSetPerms_AccessControl(t *testing.T) { - type accessControlTestCase2 struct { + type testCase struct { expectedCode int desc string url string method string + role org.RoleType permissions []accesscontrol.Permission input string } - tests := []accessControlTestCase2{ + tests := []testCase{ { expectedCode: http.StatusOK, desc: "org viewer with the correct permissions can add a user as a viewer to his org", url: "/api/org/users", method: http.MethodPost, + role: org.RoleViewer, permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersAdd, Scope: accesscontrol.ScopeUsersAll}}, - input: `{"loginOrEmail": "` + testAdminOrg2.Login + `", "role": "` + string(org.RoleViewer) + `"}`, + input: `{"loginOrEmail": "user", "role": "Viewer"}`, }, { expectedCode: http.StatusForbidden, desc: "org viewer with the correct permissions cannot add a user as an editor to his org", url: "/api/org/users", + role: org.RoleViewer, method: http.MethodPost, permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersAdd, Scope: accesscontrol.ScopeUsersAll}}, - input: `{"loginOrEmail": "` + testAdminOrg2.Login + `", "role": "` + string(org.RoleEditor) + `"}`, + input: `{"loginOrEmail": "user", "role": "Editor"}`, }, { expectedCode: http.StatusOK, desc: "org viewer with the correct permissions can add a user as a viewer to his org", url: "/api/orgs/1/users", method: http.MethodPost, + role: org.RoleViewer, permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersAdd, Scope: accesscontrol.ScopeUsersAll}}, - input: `{"loginOrEmail": "` + testAdminOrg2.Login + `", "role": "` + string(org.RoleViewer) + `"}`, + input: `{"loginOrEmail": "user", "role": "Viewer"}`, }, { expectedCode: http.StatusForbidden, desc: "org viewer with the correct permissions cannot add a user as an editor to his org", url: "/api/orgs/1/users", method: http.MethodPost, + role: org.RoleViewer, permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersAdd, Scope: accesscontrol.ScopeUsersAll}}, - input: `{"loginOrEmail": "` + testAdminOrg2.Login + `", "role": "` + string(org.RoleEditor) + `"}`, + input: `{"loginOrEmail": "user", "role": "Editor"}`, }, { expectedCode: http.StatusOK, desc: "org viewer with the correct permissions can update a user's role to a viewer in his org", - url: fmt.Sprintf("/api/org/users/%d", testEditorOrg1.UserID), + url: fmt.Sprintf("/api/org/users/%d", 1), method: http.MethodPatch, + role: org.RoleViewer, permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersWrite, Scope: accesscontrol.ScopeUsersAll}}, - input: `{"role": "` + string(org.RoleViewer) + `"}`, + input: `{"role": "Viewer"}`, }, { expectedCode: http.StatusForbidden, - desc: "org viewer with the correct permissions cannot update a user's role to a viewer in his org", - url: fmt.Sprintf("/api/org/users/%d", testEditorOrg1.UserID), + desc: "org viewer with the correct permissions cannot update a user's role to a Editorin his org", + url: fmt.Sprintf("/api/org/users/%d", 1), method: http.MethodPatch, permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersWrite, Scope: accesscontrol.ScopeUsersAll}}, - input: `{"role": "` + string(org.RoleEditor) + `"}`, + input: `{"role": "Editor"}`, }, { expectedCode: http.StatusOK, desc: "org viewer with the correct permissions can update a user's role to a viewer in his org", - url: fmt.Sprintf("/api/orgs/1/users/%d", testEditorOrg1.UserID), + url: fmt.Sprintf("/api/orgs/1/users/%d", 1), method: http.MethodPatch, + role: org.RoleViewer, permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersWrite, Scope: accesscontrol.ScopeUsersAll}}, - input: `{"role": "` + string(org.RoleViewer) + `"}`, + input: `{"role": "Viewer"}`, }, { expectedCode: http.StatusForbidden, - desc: "org viewer with the correct permissions cannot update a user's role to a viewer in his org", - url: fmt.Sprintf("/api/orgs/1/users/%d", testEditorOrg1.UserID), + desc: "org viewer with the correct permissions cannot update a user's role to a editor in his org", + url: fmt.Sprintf("/api/orgs/1/users/%d", 1), method: http.MethodPatch, + role: org.RoleViewer, permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersWrite, Scope: accesscontrol.ScopeUsersAll}}, - input: `{"role": "` + string(org.RoleEditor) + `"}`, + input: `{"role": "Editor"}`, }, { expectedCode: http.StatusOK, desc: "org viewer with the correct permissions can invite a user as a viewer in his org", url: "/api/org/invites", method: http.MethodPost, + role: org.RoleViewer, permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersAdd, Scope: accesscontrol.ScopeUsersAll}}, - input: `{"loginOrEmail": "newUserEmail@test.com", "sendEmail": false, "role": "` + string(org.RoleViewer) + `"}`, + input: `{"loginOrEmail": "newUserEmail@test.com", "sendEmail": false, "role": "Viewer"}`, }, { expectedCode: http.StatusForbidden, desc: "org viewer with the correct permissions cannot invite a user as an editor in his org", url: "/api/org/invites", method: http.MethodPost, + role: org.RoleEditor, permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionUsersCreate}}, - input: `{"loginOrEmail": "newUserEmail@test.com", "sendEmail": false, "role": "` + string(org.RoleEditor) + `"}`, + input: `{"loginOrEmail": "newUserEmail@test.com", "sendEmail": false, "role": "Editor"}`, }, } - for _, test := range tests { - t.Run(test.desc, func(t *testing.T) { - var err error - sc := setupHTTPServer(t, true, func(hs *HTTPServer) { - hs.tempUserService = tempuserimpl.ProvideService(hs.SQLStore) - hs.orgService, err = orgimpl.ProvideService(hs.SQLStore, setting.NewCfg(), quotatest.New(false, nil)) - hs.userService, err = userimpl.ProvideService( - hs.SQLStore, nil, setting.NewCfg(), teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), setting.NewCfg()), localcache.ProvideService(), quotatest.New(false, nil)) - require.NoError(t, err) + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + server := SetupAPITestServer(t, func(hs *HTTPServer) { + hs.Cfg = setting.NewCfg() + hs.orgService = &orgtest.FakeOrgService{} + hs.authInfoService = &logintest.AuthInfoServiceFake{} + hs.userService = &usertest.FakeUserService{ + ExpectedUser: &user.User{}, + ExpectedSignedInUser: userWithPermissions(1, tt.permissions), + } }) - setInitCtxSignedInViewer(sc.initCtx) - setupOrgUsersDBForAccessControlTests(t, sc.db, sc.hs.orgService) - setAccessControlPermissions(sc.acmock, test.permissions, sc.initCtx.OrgID) - input := strings.NewReader(test.input) - response := callAPI(sc.server, test.method, test.url, input, t) - assert.Equal(t, test.expectedCode, response.Code) + u := userWithPermissions(1, tt.permissions) + var reader io.Reader + if tt.input != "" { + reader = strings.NewReader(tt.input) + } + + res, err := server.SendJSON(webtest.RequestWithSignedInUser(server.NewRequest(tt.method, tt.url, reader), u)) + require.NoError(t, err) + assert.Equal(t, tt.expectedCode, res.StatusCode) + require.NoError(t, res.Body.Close()) }) } } func TestPatchOrgUsersAPIEndpoint_AccessControl(t *testing.T) { - url := "/api/orgs/%v/users/%v" type testCase struct { name string enableAccessControl bool - user user.SignedInUser - targetUserId int64 - targetOrg int64 + isGrafanaAdmin bool + role org.RoleType + permissions []accesscontrol.Permission input string expectedCode int - expectedMessage util.DynMap - expectedUserRole org.RoleType } tests := []testCase{ { name: "server admin can update users in his org (legacy)", enableAccessControl: false, - user: testServerAdminViewer, - targetUserId: testEditorOrg1.UserID, - targetOrg: testServerAdminViewer.OrgID, + isGrafanaAdmin: true, input: `{"role": "Viewer"}`, expectedCode: http.StatusOK, - expectedMessage: util.DynMap{"message": "Organization user updated"}, - expectedUserRole: org.RoleViewer, - }, - { - name: "server admin can update users in another org (legacy)", - enableAccessControl: false, - user: testServerAdminViewer, - targetUserId: testServerAdminViewer.UserID, - targetOrg: 2, - input: `{"role": "Editor"}`, - expectedCode: http.StatusOK, - expectedMessage: util.DynMap{"message": "Organization user updated"}, - expectedUserRole: org.RoleEditor, }, { name: "org admin cannot update users in his org (legacy)", enableAccessControl: false, - user: testAdminOrg2, - targetUserId: testServerAdminViewer.UserID, - targetOrg: testAdminOrg2.OrgID, + role: org.RoleAdmin, input: `{"role": "Editor"}`, expectedCode: http.StatusForbidden, }, { - name: "org admin cannot update users in another org (legacy)", - enableAccessControl: false, - user: testAdminOrg2, - targetUserId: testServerAdminViewer.UserID, - targetOrg: 1, - input: `{"role": "Editor"}`, - expectedCode: http.StatusForbidden, - }, - { - name: "server admin can update users in his org", + name: "user with permissions can update org role", enableAccessControl: true, - user: testServerAdminViewer, - targetUserId: testEditorOrg1.UserID, - targetOrg: testServerAdminViewer.OrgID, + permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersWrite, Scope: "users:*"}}, + role: org.RoleAdmin, input: `{"role": "Viewer"}`, expectedCode: http.StatusOK, - expectedMessage: util.DynMap{"message": "Organization user updated"}, - expectedUserRole: org.RoleViewer, }, { - name: "server admin can update users in another org", + name: "user without permissions cannot update org role", enableAccessControl: true, - user: testServerAdminViewer, - targetUserId: testServerAdminViewer.UserID, - targetOrg: 2, + permissions: []accesscontrol.Permission{}, input: `{"role": "Editor"}`, - expectedCode: http.StatusOK, - expectedMessage: util.DynMap{"message": "Organization user updated"}, - expectedUserRole: org.RoleEditor, + expectedCode: http.StatusForbidden, }, { - name: "org admin can update users in his org", + name: "user with permissions cannot update org role with more privileges", enableAccessControl: true, - user: testAdminOrg2, - targetUserId: testServerAdminViewer.UserID, - targetOrg: testAdminOrg2.OrgID, - input: `{"role": "Editor"}`, - expectedCode: http.StatusOK, - expectedMessage: util.DynMap{"message": "Organization user updated"}, - expectedUserRole: org.RoleEditor, - }, - { - name: "org admin cannot update users in another org", - enableAccessControl: true, - user: testAdminOrg2, - targetUserId: testServerAdminViewer.UserID, - targetOrg: 1, - input: `{"role": "Editor"}`, + permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersWrite, Scope: "users:*"}}, + role: org.RoleViewer, + input: `{"role": "Admin"}`, expectedCode: http.StatusForbidden, }, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - cfg := setting.NewCfg() - cfg.RBACEnabled = tc.enableAccessControl - var err error - sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { - quotaService := quotatest.New(false, nil) - hs.userService, err = userimpl.ProvideService( - hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(), quotaService) - require.NoError(t, err) - hs.orgService, err = orgimpl.ProvideService(hs.SQLStore, cfg, quotaService) - require.NoError(t, err) - }) - setupOrgUsersDBForAccessControlTests(t, sc.db, sc.hs.orgService) - setInitCtxSignedInUser(sc.initCtx, tc.user) - - // Perform request - input := strings.NewReader(tc.input) - setInitCtxSignedInUser(sc.initCtx, tc.user) - response := callAPI(sc.server, http.MethodPatch, fmt.Sprintf(url, tc.targetOrg, tc.targetUserId), input, t) - assert.Equal(t, tc.expectedCode, response.Code) - - if tc.expectedCode != http.StatusForbidden { - // Check result - var message util.DynMap - err := json.NewDecoder(response.Body).Decode(&message) - require.NoError(t, err) - assert.Equal(t, tc.expectedMessage, message) - - getUserQuery := user.GetSignedInUserQuery{ - UserID: tc.targetUserId, - OrgID: tc.targetOrg, + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server := SetupAPITestServer(t, func(hs *HTTPServer) { + hs.Cfg = setting.NewCfg() + hs.Cfg.RBACEnabled = tt.enableAccessControl + hs.orgService = &orgtest.FakeOrgService{} + hs.authInfoService = &logintest.AuthInfoServiceFake{} + hs.userService = &usertest.FakeUserService{ + ExpectedUser: &user.User{}, + ExpectedSignedInUser: userWithPermissions(1, tt.permissions), } - usr, err := sc.userService.GetSignedInUser(context.Background(), &getUserQuery) - require.NoError(t, err) - assert.Equal(t, tc.expectedUserRole, usr.OrgRole) - } + }) + + u := userWithPermissions(1, tt.permissions) + u.IsGrafanaAdmin = tt.isGrafanaAdmin + res, err := server.SendJSON(webtest.RequestWithSignedInUser(server.NewRequest(http.MethodPatch, "/api/orgs/1/users/1", strings.NewReader(tt.input)), u)) + require.NoError(t, err) + assert.Equal(t, tt.expectedCode, res.StatusCode) + require.NoError(t, res.Body.Close()) + + cfg := setting.NewCfg() + cfg.RBACEnabled = tt.enableAccessControl }) } } func TestDeleteOrgUsersAPIEndpoint_AccessControl(t *testing.T) { - url := "/api/orgs/%v/users/%v" type testCase struct { name string enableAccessControl bool - user user.SignedInUser - targetUserId int64 - targetOrg int64 + permissions []accesscontrol.Permission + role org.RoleType + isGrafanaAdmin bool expectedCode int - expectedMessage util.DynMap - expectedUserCount int } tests := []testCase{ { - name: "server admin can delete users from his org (legacy)", + name: "server admin can remove users from org (legacy)", enableAccessControl: false, - user: testServerAdminViewer, - targetUserId: testEditorOrg1.UserID, - targetOrg: testServerAdminViewer.OrgID, + isGrafanaAdmin: true, expectedCode: http.StatusOK, - expectedMessage: util.DynMap{"message": "User removed from organization"}, - expectedUserCount: 1, }, { - name: "server admin can delete users from another org (legacy)", - enableAccessControl: false, - user: testServerAdminViewer, - targetUserId: testServerAdminViewer.UserID, - targetOrg: 2, - expectedCode: http.StatusOK, - expectedMessage: util.DynMap{"message": "User removed from organization"}, - expectedUserCount: 1, + name: "org admin can remove users from org (legacy)", + role: org.RoleAdmin, + expectedCode: http.StatusForbidden, }, { - name: "org admin can delete users from his org (legacy)", - enableAccessControl: false, - user: testAdminOrg2, - targetUserId: testServerAdminViewer.UserID, - targetOrg: testAdminOrg2.OrgID, - expectedCode: http.StatusForbidden, - }, - { - name: "org admin cannot delete users from another org (legacy)", - enableAccessControl: false, - user: testAdminOrg2, - targetUserId: testEditorOrg1.UserID, - targetOrg: 1, - expectedCode: http.StatusForbidden, - }, - { - name: "server admin can delete users from his org", + name: "user with permissions can remove user from org", enableAccessControl: true, - user: testServerAdminViewer, - targetUserId: testEditorOrg1.UserID, - targetOrg: testServerAdminViewer.OrgID, - expectedCode: http.StatusOK, - expectedMessage: util.DynMap{"message": "User removed from organization"}, - expectedUserCount: 1, + permissions: []accesscontrol.Permission{ + {Action: accesscontrol.ActionOrgUsersRemove, Scope: "users:*"}, + }, + expectedCode: http.StatusOK, }, { - name: "server admin can delete users from another org", + name: "user without permissions cannot remove user from org", enableAccessControl: true, - user: testServerAdminViewer, - targetUserId: testServerAdminViewer.UserID, - targetOrg: 2, - expectedCode: http.StatusOK, - expectedMessage: util.DynMap{"message": "User removed from organization"}, - expectedUserCount: 1, - }, - { - name: "org admin can delete users from his org", - enableAccessControl: true, - user: testAdminOrg2, - targetUserId: testServerAdminViewer.UserID, - targetOrg: testAdminOrg2.OrgID, - expectedCode: http.StatusOK, - expectedMessage: util.DynMap{"message": "User removed from organization"}, - expectedUserCount: 1, - }, - { - name: "org admin cannot delete users from another org", - enableAccessControl: true, - user: testAdminOrg2, - targetUserId: testEditorOrg1.UserID, - targetOrg: 1, expectedCode: http.StatusForbidden, }, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - cfg := setting.NewCfg() - cfg.RBACEnabled = tc.enableAccessControl - var err error - sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { - quotaService := quotatest.New(false, nil) - hs.userService, err = userimpl.ProvideService( - hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(), quotaService) - require.NoError(t, err) - hs.orgService, err = orgimpl.ProvideService(hs.SQLStore, cfg, quotaService) - require.NoError(t, err) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server := SetupAPITestServer(t, func(hs *HTTPServer) { + hs.Cfg = setting.NewCfg() + hs.Cfg.RBACEnabled = tt.enableAccessControl + hs.accesscontrolService = actest.FakeService{} + hs.orgService = &orgtest.FakeOrgService{ + ExpectedOrgListResponse: orgtest.OrgListResponse{struct { + OrgID int64 + Response error + }{OrgID: 1, Response: nil}}, + } + hs.authInfoService = &logintest.AuthInfoServiceFake{} + hs.userService = &usertest.FakeUserService{ + ExpectedUser: &user.User{}, + ExpectedSignedInUser: userWithPermissions(1, tt.permissions), + } }) - setupOrgUsersDBForAccessControlTests(t, sc.db, sc.hs.orgService) - setInitCtxSignedInUser(sc.initCtx, tc.user) - response := callAPI(sc.server, http.MethodDelete, fmt.Sprintf(url, tc.targetOrg, tc.targetUserId), nil, t) - assert.Equal(t, tc.expectedCode, response.Code) + u := userWithPermissions(1, tt.permissions) + u.IsGrafanaAdmin = tt.isGrafanaAdmin + res, err := server.SendJSON(webtest.RequestWithSignedInUser(server.NewRequest(http.MethodDelete, "/api/orgs/1/users/1", nil), u)) + require.NoError(t, err) + assert.Equal(t, tt.expectedCode, res.StatusCode) + require.NoError(t, res.Body.Close()) - if tc.expectedCode != http.StatusForbidden { - // Check result - var message util.DynMap - err := json.NewDecoder(response.Body).Decode(&message) - require.NoError(t, err) - assert.Equal(t, tc.expectedMessage, message) - } + cfg := setting.NewCfg() + cfg.RBACEnabled = tt.enableAccessControl }) } } diff --git a/pkg/services/team/teamtest/team.go b/pkg/services/team/teamtest/team.go index 332e9bce0af..dc10a7150e1 100644 --- a/pkg/services/team/teamtest/team.go +++ b/pkg/services/team/teamtest/team.go @@ -10,6 +10,7 @@ import ( type FakeService struct { ExpectedTeam team.Team ExpectedIsMember bool + ExpectedIsAdmin bool ExpectedTeamDTO *team.TeamDTO ExpectedTeamsByUser []*team.TeamDTO ExpectedMembers []*team.TeamMemberDTO @@ -69,5 +70,5 @@ func (s *FakeService) GetTeamMembers(ctx context.Context, query *team.GetTeamMem } func (s *FakeService) IsAdminOfTeams(ctx context.Context, query *team.IsAdminOfTeamsQuery) (bool, error) { - return false, s.ExpectedError + return s.ExpectedIsAdmin, s.ExpectedError }