mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 21:19:28 -06:00
299c142f6a
* Feature (quota service): Use ReplDB for quota service Gets This adds the replDB to the quota service, as well as some more test helper functions to simplify updating tests. My intent is that the helper functions can be removed when this is fully rolled out (or not) and we're consistently using the ReplDB interface (or not!) * test updates
730 lines
25 KiB
Go
730 lines
25 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/grafana/pkg/api/dtos"
|
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
|
"github.com/grafana/grafana/pkg/login/social"
|
|
"github.com/grafana/grafana/pkg/login/social/socialtest"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
"github.com/grafana/grafana/pkg/services/login"
|
|
"github.com/grafana/grafana/pkg/services/login/authinfotest"
|
|
"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/sqlstore"
|
|
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
|
"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/web/webtest"
|
|
)
|
|
|
|
func setUpGetOrgUsersDB(t *testing.T, sqlStore db.DB, cfg *setting.Cfg) {
|
|
cfg.AutoAssignOrg = true
|
|
cfg.AutoAssignOrgId = int(testOrgID)
|
|
|
|
quotaService := quotaimpl.ProvideService(db.FakeReplDBFromDB(sqlStore), cfg)
|
|
orgService, err := orgimpl.ProvideService(sqlStore, cfg, quotaService)
|
|
require.NoError(t, err)
|
|
usrSvc, err := userimpl.ProvideService(
|
|
sqlStore, orgService, cfg, nil, nil, tracing.InitializeTracerForTest(),
|
|
quotaService, supportbundlestest.NewFakeBundleService(),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
id, err := orgService.GetOrCreate(context.Background(), "testOrg")
|
|
require.NoError(t, err)
|
|
require.Equal(t, testOrgID, id)
|
|
|
|
_, err = usrSvc.Create(context.Background(), &user.CreateUserCommand{Email: "testUser@grafana.com", Login: testUserLogin})
|
|
require.NoError(t, err)
|
|
_, err = usrSvc.Create(context.Background(), &user.CreateUserCommand{Email: "user1@grafana.com", Login: "user1"})
|
|
require.NoError(t, err)
|
|
_, err = usrSvc.Create(context.Background(), &user.CreateUserCommand{Email: "user2@grafana.com", Login: "user2"})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestOrgUsersAPIEndpoint_userLoggedIn(t *testing.T) {
|
|
hs := setupSimpleHTTPServer(featuremgmt.WithFeatures())
|
|
settings := hs.Cfg
|
|
|
|
sqlStore := db.InitTestDB(t, sqlstore.InitTestDBOpt{Cfg: settings})
|
|
hs.SQLStore = sqlStore
|
|
orgService := orgtest.NewOrgServiceFake()
|
|
orgService.ExpectedSearchOrgUsersResult = &org.SearchOrgUsersQueryResult{}
|
|
hs.orgService = orgService
|
|
setUpGetOrgUsersDB(t, sqlStore, settings)
|
|
mock := dbtest.NewFakeDB()
|
|
|
|
loggedInUserScenario(t, "When calling GET on", "api/org/users", "api/org/users", func(sc *scenarioContext) {
|
|
orgService.ExpectedSearchOrgUsersResult = &org.SearchOrgUsersQueryResult{
|
|
OrgUsers: []*org.OrgUserDTO{
|
|
{Login: testUserLogin, Email: "testUser@grafana.com"},
|
|
{Login: "user1", Email: "user1@grafana.com"},
|
|
{Login: "user2", Email: "user2@grafana.com"},
|
|
},
|
|
}
|
|
sc.handlerFunc = hs.GetOrgUsersForCurrentOrg
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
|
|
require.Equal(t, http.StatusOK, sc.resp.Code)
|
|
|
|
var resp []org.OrgUserDTO
|
|
err := json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
assert.Len(t, resp, 3)
|
|
}, mock)
|
|
|
|
loggedInUserScenario(t, "When calling GET on", "api/org/users/search", "api/org/users/search", func(sc *scenarioContext) {
|
|
orgService.ExpectedSearchOrgUsersResult = &org.SearchOrgUsersQueryResult{
|
|
OrgUsers: []*org.OrgUserDTO{
|
|
{
|
|
Login: "user1",
|
|
},
|
|
{
|
|
Login: "user2",
|
|
},
|
|
{
|
|
Login: "user3",
|
|
},
|
|
},
|
|
TotalCount: 3,
|
|
PerPage: 1000,
|
|
Page: 1,
|
|
}
|
|
sc.handlerFunc = hs.SearchOrgUsersWithPaging
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
|
|
require.Equal(t, http.StatusOK, sc.resp.Code)
|
|
|
|
var resp org.SearchOrgUsersQueryResult
|
|
err := json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
|
|
assert.Len(t, resp.OrgUsers, 3)
|
|
assert.Equal(t, int64(3), resp.TotalCount)
|
|
assert.Equal(t, 1000, resp.PerPage)
|
|
assert.Equal(t, 1, resp.Page)
|
|
}, mock)
|
|
|
|
loggedInUserScenario(t, "When calling GET with page and limit query parameters on", "api/org/users/search", "api/org/users/search", func(sc *scenarioContext) {
|
|
orgService.ExpectedSearchOrgUsersResult = &org.SearchOrgUsersQueryResult{
|
|
OrgUsers: []*org.OrgUserDTO{
|
|
{
|
|
Login: "user1",
|
|
},
|
|
},
|
|
TotalCount: 3,
|
|
PerPage: 2,
|
|
Page: 2,
|
|
}
|
|
|
|
sc.handlerFunc = hs.SearchOrgUsersWithPaging
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "2", "page": "2"}).exec()
|
|
|
|
require.Equal(t, http.StatusOK, sc.resp.Code)
|
|
|
|
var resp org.SearchOrgUsersQueryResult
|
|
err := json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
|
|
assert.Len(t, resp.OrgUsers, 1)
|
|
assert.Equal(t, int64(3), resp.TotalCount)
|
|
assert.Equal(t, 2, resp.PerPage)
|
|
assert.Equal(t, 2, resp.Page)
|
|
}, mock)
|
|
|
|
t.Run("Given there are two hidden users", func(t *testing.T) {
|
|
settings.HiddenUsers = map[string]struct{}{
|
|
"user1": {},
|
|
testUserLogin: {},
|
|
}
|
|
t.Cleanup(func() { settings.HiddenUsers = make(map[string]struct{}) })
|
|
|
|
loggedInUserScenario(t, "When calling GET on", "api/org/users", "api/org/users", func(sc *scenarioContext) {
|
|
orgService.ExpectedSearchOrgUsersResult = &org.SearchOrgUsersQueryResult{
|
|
OrgUsers: []*org.OrgUserDTO{
|
|
{Login: testUserLogin, Email: "testUser@grafana.com"},
|
|
{Login: "user1", Email: "user1@grafana.com"},
|
|
{Login: "user2", Email: "user2@grafana.com"},
|
|
},
|
|
}
|
|
|
|
sc.handlerFunc = hs.GetOrgUsersForCurrentOrg
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
|
|
require.Equal(t, http.StatusOK, sc.resp.Code)
|
|
|
|
var resp []org.OrgUserDTO
|
|
err := json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
assert.Len(t, resp, 2)
|
|
assert.Equal(t, testUserLogin, resp[0].Login)
|
|
assert.Equal(t, "user2", resp[1].Login)
|
|
}, mock)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET as an admin on", "GET", "api/org/users/lookup",
|
|
"api/org/users/lookup", org.RoleAdmin, func(sc *scenarioContext) {
|
|
sc.handlerFunc = hs.GetOrgUsersForCurrentOrgLookup
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
|
|
require.Equal(t, http.StatusOK, sc.resp.Code)
|
|
|
|
var resp []dtos.UserLookupDTO
|
|
err := json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
assert.Len(t, resp, 2)
|
|
assert.Equal(t, testUserLogin, resp[0].Login)
|
|
assert.Equal(t, "user2", resp[1].Login)
|
|
}, mock)
|
|
})
|
|
}
|
|
|
|
func TestOrgUsersAPIEndpoint_updateOrgRole(t *testing.T) {
|
|
type testCase struct {
|
|
desc string
|
|
SkipOrgRoleSync bool
|
|
AuthEnabled bool
|
|
AuthModule string
|
|
expectedCode int
|
|
}
|
|
permissions := []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionOrgUsersRead, Scope: "users:*"},
|
|
{Action: accesscontrol.ActionOrgUsersWrite, Scope: "users:*"},
|
|
{Action: accesscontrol.ActionOrgUsersAdd, Scope: "users:*"},
|
|
{Action: accesscontrol.ActionOrgUsersRemove, Scope: "users:*"},
|
|
}
|
|
tests := []testCase{
|
|
{
|
|
desc: "should be able to change basicRole when skip_org_role_sync true",
|
|
SkipOrgRoleSync: true,
|
|
AuthEnabled: true,
|
|
AuthModule: login.LDAPAuthModule,
|
|
expectedCode: http.StatusOK,
|
|
},
|
|
{
|
|
desc: "should not be able to change basicRole when skip_org_role_sync false",
|
|
SkipOrgRoleSync: false,
|
|
AuthEnabled: true,
|
|
AuthModule: login.LDAPAuthModule,
|
|
expectedCode: http.StatusForbidden,
|
|
},
|
|
{
|
|
desc: "should not be able to change basicRole for a user synced through an OAuth provider",
|
|
SkipOrgRoleSync: false,
|
|
AuthEnabled: true,
|
|
AuthModule: login.GrafanaComAuthModule,
|
|
expectedCode: http.StatusForbidden,
|
|
},
|
|
{
|
|
desc: "should be able to change basicRole with a basic Auth",
|
|
SkipOrgRoleSync: false,
|
|
AuthEnabled: false,
|
|
AuthModule: "",
|
|
expectedCode: http.StatusOK,
|
|
},
|
|
{
|
|
desc: "should be able to change basicRole with a basic Auth",
|
|
SkipOrgRoleSync: true,
|
|
AuthEnabled: true,
|
|
AuthModule: "",
|
|
expectedCode: http.StatusOK,
|
|
},
|
|
}
|
|
|
|
userWithPermissions := userWithPermissions(1, permissions)
|
|
userRequesting := &user.User{ID: 2, OrgID: 1}
|
|
reqBody := `{"userId": "1", "role": "Admin", "orgId": "1"}`
|
|
for _, tt := range tests {
|
|
t.Run(tt.desc, func(t *testing.T) {
|
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
|
hs.Cfg = setting.NewCfg()
|
|
hs.Cfg.LDAPAuthEnabled = tt.AuthEnabled
|
|
if tt.AuthModule == login.LDAPAuthModule {
|
|
hs.Cfg.LDAPAuthEnabled = tt.AuthEnabled
|
|
hs.Cfg.LDAPSkipOrgRoleSync = tt.SkipOrgRoleSync
|
|
}
|
|
// AuthModule empty means basic auth
|
|
|
|
hs.authInfoService = &authinfotest.FakeService{
|
|
ExpectedUserAuth: &login.UserAuth{AuthModule: tt.AuthModule},
|
|
}
|
|
hs.userService = &usertest.FakeUserService{ExpectedSignedInUser: userWithPermissions}
|
|
hs.orgService = &orgtest.FakeOrgService{}
|
|
hs.SocialService = &socialtest.FakeSocialService{
|
|
ExpectedAuthInfoProvider: &social.OAuthInfo{Enabled: tt.AuthEnabled, SkipOrgRoleSync: tt.SkipOrgRoleSync},
|
|
}
|
|
hs.accesscontrolService = &actest.FakeService{
|
|
ExpectedPermissions: permissions,
|
|
}
|
|
})
|
|
req := server.NewRequest(http.MethodPatch, fmt.Sprintf("/api/orgs/%d/users/%d", userRequesting.OrgID, userRequesting.ID), strings.NewReader(reqBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
userWithPermissions.OrgRole = identity.RoleAdmin
|
|
res, err := server.Send(webtest.RequestWithSignedInUser(req, userWithPermissions))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tt.expectedCode, res.StatusCode)
|
|
require.NoError(t, res.Body.Close())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOrgUsersAPIEndpoint(t *testing.T) {
|
|
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",
|
|
permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersRead, Scope: accesscontrol.ScopeUsersAll}},
|
|
},
|
|
{
|
|
expectedCode: http.StatusForbidden,
|
|
desc: "UsersLookupGet should return 403 for user without required permissions",
|
|
permissions: []accesscontrol.Permission{{Action: "wrong"}},
|
|
},
|
|
}
|
|
|
|
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 = &authinfotest.FakeService{}
|
|
})
|
|
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())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetOrgUsersAPIEndpoint_AccessControlMetadata(t *testing.T) {
|
|
type testCase struct {
|
|
desc string
|
|
permissions []accesscontrol.Permission
|
|
includeMetadata bool
|
|
expectedCode int
|
|
expectedMetadata map[string]bool
|
|
}
|
|
|
|
tests := []testCase{
|
|
{
|
|
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,
|
|
},
|
|
{
|
|
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,
|
|
},
|
|
},
|
|
}
|
|
|
|
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 = &authinfotest.FakeService{}
|
|
hs.userService = &usertest.FakeUserService{ExpectedSignedInUser: userWithPermissions(1, tt.permissions)}
|
|
hs.accesscontrolService = actest.FakeService{ExpectedPermissions: tt.permissions}
|
|
})
|
|
|
|
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(res.Body).Decode(&userList)
|
|
require.NoError(t, err)
|
|
|
|
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) {
|
|
type testCase struct {
|
|
name string
|
|
permissions []accesscontrol.Permission
|
|
expectedCode int
|
|
targetOrg int64
|
|
}
|
|
|
|
tests := []testCase{
|
|
{
|
|
name: "user with permissions can get users in org",
|
|
permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionOrgUsersRead, Scope: "users:*"},
|
|
},
|
|
expectedCode: http.StatusOK,
|
|
targetOrg: 1,
|
|
},
|
|
{
|
|
name: "user without permissions cannot get users in org",
|
|
expectedCode: http.StatusForbidden,
|
|
targetOrg: 1,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
|
hs.Cfg = setting.NewCfg()
|
|
hs.orgService = &orgtest.FakeOrgService{
|
|
ExpectedSearchOrgUsersResult: &org.SearchOrgUsersQueryResult{},
|
|
}
|
|
hs.authInfoService = &authinfotest.FakeService{}
|
|
hs.userService = &usertest.FakeUserService{ExpectedSignedInUser: userWithPermissions(1, tc.permissions)}
|
|
hs.accesscontrolService = &actest.FakeService{}
|
|
})
|
|
|
|
u := userWithPermissions(1, tc.permissions)
|
|
|
|
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) {
|
|
type testCase struct {
|
|
desc string
|
|
permissions []accesscontrol.Permission
|
|
input string
|
|
expectedCode int
|
|
}
|
|
|
|
tests := []testCase{
|
|
{
|
|
desc: "user with permissions can add users to org",
|
|
permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionOrgUsersAdd, Scope: "users:*"},
|
|
},
|
|
input: `{"loginOrEmail": "user", "role": "Viewer"}`,
|
|
expectedCode: http.StatusOK,
|
|
},
|
|
{
|
|
desc: "user without permissions cannot add users to org",
|
|
expectedCode: http.StatusForbidden,
|
|
input: `{"loginOrEmail": "user", "role": "Viewer"}`,
|
|
},
|
|
}
|
|
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 = &authinfotest.FakeService{}
|
|
hs.userService = &usertest.FakeUserService{
|
|
ExpectedUser: &user.User{},
|
|
ExpectedSignedInUser: userWithPermissions(1, tt.permissions),
|
|
}
|
|
hs.accesscontrolService = &actest.FakeService{}
|
|
})
|
|
|
|
u := userWithPermissions(1, tt.permissions)
|
|
|
|
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 testCase struct {
|
|
expectedCode int
|
|
desc string
|
|
url string
|
|
method string
|
|
role org.RoleType
|
|
permissions []accesscontrol.Permission
|
|
input string
|
|
}
|
|
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": "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": "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": "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": "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", 1),
|
|
method: http.MethodPatch,
|
|
role: org.RoleViewer,
|
|
permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersWrite, Scope: accesscontrol.ScopeUsersAll}},
|
|
input: `{"role": "Viewer"}`,
|
|
},
|
|
{
|
|
expectedCode: http.StatusForbidden,
|
|
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": "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", 1),
|
|
method: http.MethodPatch,
|
|
role: org.RoleViewer,
|
|
permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersWrite, Scope: accesscontrol.ScopeUsersAll}},
|
|
input: `{"role": "Viewer"}`,
|
|
},
|
|
{
|
|
expectedCode: http.StatusForbidden,
|
|
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": "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": "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": "Editor"}`,
|
|
},
|
|
}
|
|
|
|
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 = &authinfotest.FakeService{
|
|
ExpectedUserAuth: &login.UserAuth{
|
|
AuthModule: "",
|
|
},
|
|
}
|
|
hs.userService = &usertest.FakeUserService{
|
|
ExpectedUser: &user.User{},
|
|
ExpectedSignedInUser: userWithPermissions(1, tt.permissions),
|
|
}
|
|
hs.accesscontrolService = &actest.FakeService{}
|
|
})
|
|
|
|
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) {
|
|
type testCase struct {
|
|
name string
|
|
role org.RoleType
|
|
permissions []accesscontrol.Permission
|
|
input string
|
|
expectedCode int
|
|
}
|
|
|
|
tests := []testCase{
|
|
{
|
|
name: "user with permissions can update org role",
|
|
permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersWrite, Scope: "users:*"}},
|
|
role: org.RoleAdmin,
|
|
input: `{"role": "Viewer"}`,
|
|
expectedCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "user without permissions cannot update org role",
|
|
permissions: []accesscontrol.Permission{},
|
|
input: `{"role": "Editor"}`,
|
|
expectedCode: http.StatusForbidden,
|
|
},
|
|
{
|
|
name: "user with permissions cannot update org role with more privileges",
|
|
permissions: []accesscontrol.Permission{{Action: accesscontrol.ActionOrgUsersWrite, Scope: "users:*"}},
|
|
role: org.RoleViewer,
|
|
input: `{"role": "Admin"}`,
|
|
expectedCode: http.StatusForbidden,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
|
hs.Cfg = setting.NewCfg()
|
|
hs.orgService = &orgtest.FakeOrgService{}
|
|
hs.authInfoService = &authinfotest.FakeService{
|
|
ExpectedUserAuth: &login.UserAuth{
|
|
AuthModule: "",
|
|
},
|
|
}
|
|
hs.accesscontrolService = &actest.FakeService{}
|
|
hs.userService = &usertest.FakeUserService{
|
|
ExpectedUser: &user.User{},
|
|
ExpectedSignedInUser: userWithPermissions(1, tt.permissions),
|
|
}
|
|
})
|
|
|
|
u := userWithPermissions(1, tt.permissions)
|
|
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())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeleteOrgUsersAPIEndpoint_AccessControl(t *testing.T) {
|
|
type testCase struct {
|
|
name string
|
|
permissions []accesscontrol.Permission
|
|
isGrafanaAdmin bool
|
|
expectedCode int
|
|
}
|
|
|
|
tests := []testCase{
|
|
{
|
|
name: "user with permissions can remove user from org",
|
|
permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionOrgUsersRemove, Scope: "users:*"},
|
|
},
|
|
expectedCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "user without permissions cannot remove user from org",
|
|
expectedCode: http.StatusForbidden,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
|
hs.Cfg = setting.NewCfg()
|
|
hs.accesscontrolService = actest.FakeService{}
|
|
hs.orgService = &orgtest.FakeOrgService{
|
|
ExpectedOrgListResponse: orgtest.OrgListResponse{struct {
|
|
OrgID int64
|
|
Response error
|
|
}{OrgID: 1, Response: nil}},
|
|
}
|
|
hs.authInfoService = &authinfotest.FakeService{}
|
|
hs.userService = &usertest.FakeUserService{
|
|
ExpectedUser: &user.User{},
|
|
ExpectedSignedInUser: userWithPermissions(1, tt.permissions),
|
|
}
|
|
})
|
|
|
|
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())
|
|
})
|
|
}
|
|
}
|