322 lines
12 KiB
Go
322 lines
12 KiB
Go
package teamapi
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/grafana/pkg/api/routing"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
|
"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/licensing"
|
|
"github.com/grafana/grafana/pkg/services/org"
|
|
"github.com/grafana/grafana/pkg/services/preference/preftest"
|
|
"github.com/grafana/grafana/pkg/services/team"
|
|
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/services/user/usertest"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
"github.com/grafana/grafana/pkg/web/webtest"
|
|
)
|
|
|
|
func SetupAPITestServer(t *testing.T, teamService team.Service, opts ...func(a *TeamAPI)) *webtest.Server {
|
|
t.Helper()
|
|
router := routing.NewRouteRegister()
|
|
cfg := setting.NewCfg()
|
|
cfg.LDAPAuthEnabled = true
|
|
|
|
if teamService == nil {
|
|
teamService = teamtest.NewFakeService()
|
|
}
|
|
|
|
a := ProvideTeamAPI(router,
|
|
teamService,
|
|
actest.FakeService{},
|
|
acimpl.ProvideAccessControl(featuremgmt.WithFeatures()),
|
|
&actest.FakePermissionsService{},
|
|
&usertest.FakeUserService{},
|
|
&licensing.OSSLicensingService{},
|
|
cfg,
|
|
preftest.NewPreferenceServiceFake(),
|
|
dashboards.NewFakeDashboardService(t),
|
|
)
|
|
for _, o := range opts {
|
|
o(a)
|
|
}
|
|
|
|
server := webtest.NewServer(t, router)
|
|
|
|
return server
|
|
}
|
|
|
|
func TestAddTeamMembersAPIEndpoint(t *testing.T) {
|
|
server := SetupAPITestServer(t, &teamtest.FakeService{ExpectedTeamDTO: &team.TeamDTO{ID: 1, UID: "a00001"}})
|
|
|
|
t.Run("should be able to add team member with correct permission", func(t *testing.T) {
|
|
req := webtest.RequestWithSignedInUser(
|
|
server.NewRequest(http.MethodPost, "/api/teams/1/members", strings.NewReader("{\"userId\": 1}")),
|
|
authedUserWithPermissions(1, 1, []accesscontrol.Permission{{Action: accesscontrol.ActionTeamsPermissionsWrite, Scope: "teams:id:1"}}),
|
|
)
|
|
res, err := server.SendJSON(req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
require.NoError(t, res.Body.Close())
|
|
})
|
|
|
|
t.Run("should be able to add team member with correct permission by UID", func(t *testing.T) {
|
|
req := webtest.RequestWithSignedInUser(
|
|
server.NewRequest(http.MethodPost, "/api/teams/a00001/members", strings.NewReader("{\"userId\": 1}")),
|
|
authedUserWithPermissions(1, 1, []accesscontrol.Permission{{Action: accesscontrol.ActionTeamsPermissionsWrite, Scope: "teams:id:1"}}),
|
|
)
|
|
res, err := server.SendJSON(req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
require.NoError(t, res.Body.Close())
|
|
})
|
|
|
|
t.Run("should not be able to add team member without correct permission", func(t *testing.T) {
|
|
req := webtest.RequestWithSignedInUser(
|
|
server.NewRequest(http.MethodPost, "/api/teams/1/members", strings.NewReader("{\"userId\": 1}")),
|
|
authedUserWithPermissions(1, 1, []accesscontrol.Permission{{Action: accesscontrol.ActionTeamsPermissionsWrite, Scope: "teams:id:2"}}),
|
|
)
|
|
res, err := server.SendJSON(req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
|
require.NoError(t, res.Body.Close())
|
|
})
|
|
}
|
|
|
|
func TestGetTeamMembersAPIEndpoint(t *testing.T) {
|
|
server := SetupAPITestServer(t, &teamtest.FakeService{ExpectedIsMember: true, ExpectedTeamDTO: &team.TeamDTO{ID: 1, UID: "a00001"}})
|
|
|
|
t.Run("should be able to get team members with correct permission", func(t *testing.T) {
|
|
req := webtest.RequestWithSignedInUser(
|
|
server.NewGetRequest("/api/teams/1/members"),
|
|
authedUserWithPermissions(1, 1, []accesscontrol.Permission{{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:id:1"}}),
|
|
)
|
|
res, err := server.SendJSON(req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
require.NoError(t, res.Body.Close())
|
|
})
|
|
|
|
t.Run("should be able to get team members with correct permission by UID", func(t *testing.T) {
|
|
req := webtest.RequestWithSignedInUser(
|
|
server.NewGetRequest("/api/teams/a00001/members"),
|
|
authedUserWithPermissions(1, 1, []accesscontrol.Permission{{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:id:1"}}),
|
|
)
|
|
res, err := server.SendJSON(req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
require.NoError(t, res.Body.Close())
|
|
})
|
|
|
|
t.Run("should not be able to get team members without correct permission", func(t *testing.T) {
|
|
req := webtest.RequestWithSignedInUser(
|
|
server.NewGetRequest("/api/teams/1/members"),
|
|
authedUserWithPermissions(1, 1, []accesscontrol.Permission{{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:id:2"}}),
|
|
)
|
|
res, err := server.SendJSON(req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
|
require.NoError(t, res.Body.Close())
|
|
})
|
|
}
|
|
|
|
func TestUpdateTeamMembersAPIEndpoint(t *testing.T) {
|
|
server := SetupAPITestServer(t, &teamtest.FakeService{ExpectedIsMember: true, ExpectedTeamDTO: &team.TeamDTO{ID: 1, UID: "a00001"}})
|
|
|
|
t.Run("should be able to update team member with correct permission", func(t *testing.T) {
|
|
req := webtest.RequestWithSignedInUser(
|
|
server.NewRequest(http.MethodPut, "/api/teams/1/members/1", strings.NewReader("{\"permission\": 1}")),
|
|
authedUserWithPermissions(1, 1, []accesscontrol.Permission{{Action: accesscontrol.ActionTeamsPermissionsWrite, Scope: "teams:id:1"}}),
|
|
)
|
|
res, err := server.SendJSON(req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
require.NoError(t, res.Body.Close())
|
|
})
|
|
|
|
t.Run("should be able to update team member with correct permission by team UID", func(t *testing.T) {
|
|
req := webtest.RequestWithSignedInUser(
|
|
server.NewRequest(http.MethodPut, "/api/teams/a00001/members/1", strings.NewReader("{\"permission\": 1}")),
|
|
authedUserWithPermissions(1, 1, []accesscontrol.Permission{{Action: accesscontrol.ActionTeamsPermissionsWrite, Scope: "teams:id:1"}}),
|
|
)
|
|
res, err := server.SendJSON(req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
require.NoError(t, res.Body.Close())
|
|
})
|
|
|
|
t.Run("should not be able to update team member without correct permission", func(t *testing.T) {
|
|
req := webtest.RequestWithSignedInUser(
|
|
server.NewRequest(http.MethodPut, "/api/teams/1/members/1", strings.NewReader("{\"permission\": 1}")),
|
|
authedUserWithPermissions(1, 1, []accesscontrol.Permission{{Action: accesscontrol.ActionTeamsPermissionsWrite, Scope: "teams:id:2"}}),
|
|
)
|
|
res, err := server.SendJSON(req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
|
require.NoError(t, res.Body.Close())
|
|
})
|
|
}
|
|
|
|
func TestDeleteTeamMembersAPIEndpoint(t *testing.T) {
|
|
server := SetupAPITestServer(t, nil, func(hs *TeamAPI) {
|
|
hs.teamService = &teamtest.FakeService{ExpectedIsMember: true}
|
|
hs.teamPermissionsService = &actest.FakePermissionsService{}
|
|
})
|
|
|
|
t.Run("should be able to delete team member with correct permission", func(t *testing.T) {
|
|
req := webtest.RequestWithSignedInUser(
|
|
server.NewRequest(http.MethodDelete, "/api/teams/1/members/1", nil),
|
|
authedUserWithPermissions(1, 1, []accesscontrol.Permission{{Action: accesscontrol.ActionTeamsPermissionsWrite, Scope: "teams:id:1"}}),
|
|
)
|
|
res, err := server.SendJSON(req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
require.NoError(t, res.Body.Close())
|
|
})
|
|
t.Run("should not be able to delete member without correct permission", func(t *testing.T) {
|
|
req := webtest.RequestWithSignedInUser(
|
|
server.NewRequest(http.MethodDelete, "/api/teams/1/members/1", nil),
|
|
authedUserWithPermissions(1, 1, []accesscontrol.Permission{{Action: accesscontrol.ActionTeamsPermissionsWrite, Scope: "teams:id:2"}}),
|
|
)
|
|
res, err := server.SendJSON(req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
|
require.NoError(t, res.Body.Close())
|
|
})
|
|
}
|
|
|
|
func Test_getTeamMembershipUpdates(t *testing.T) {
|
|
type testCase struct {
|
|
description string
|
|
input team.SetTeamMembershipsCommand
|
|
currentMembers []*team.TeamMemberDTO
|
|
expectedUpdates []accesscontrol.SetResourcePermissionCommand
|
|
expectErr bool
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
description: "should correctly list members and admins for a team with no current members or admins",
|
|
input: team.SetTeamMembershipsCommand{
|
|
Members: []string{"user1", "user2"},
|
|
Admins: []string{"user3"},
|
|
},
|
|
expectedUpdates: []accesscontrol.SetResourcePermissionCommand{
|
|
{UserID: 1, Permission: team.PermissionTypeMember.String()},
|
|
{UserID: 2, Permission: team.PermissionTypeMember.String()},
|
|
{UserID: 3, Permission: team.PermissionTypeAdmin.String()},
|
|
},
|
|
},
|
|
{
|
|
description: "should correctly diff the member updates for a team with existing members and admins",
|
|
input: team.SetTeamMembershipsCommand{
|
|
Members: []string{"user1", "user2"},
|
|
Admins: []string{"user3"},
|
|
},
|
|
currentMembers: []*team.TeamMemberDTO{
|
|
{Email: "user1", Permission: team.PermissionTypeMember},
|
|
{Email: "user3", Permission: team.PermissionTypeAdmin},
|
|
},
|
|
expectedUpdates: []accesscontrol.SetResourcePermissionCommand{
|
|
{UserID: 2, Permission: team.PermissionTypeMember.String()},
|
|
},
|
|
},
|
|
{
|
|
description: "should correctly update membership type for the existing members and admins",
|
|
input: team.SetTeamMembershipsCommand{
|
|
Members: []string{"user1", "user2"},
|
|
Admins: []string{"user3"},
|
|
},
|
|
currentMembers: []*team.TeamMemberDTO{
|
|
{Email: "user1", Permission: team.PermissionTypeMember},
|
|
{Email: "user2", Permission: team.PermissionTypeAdmin},
|
|
{Email: "user3", Permission: team.PermissionTypeMember},
|
|
},
|
|
expectedUpdates: []accesscontrol.SetResourcePermissionCommand{
|
|
{UserID: 2, Permission: team.PermissionTypeMember.String()},
|
|
{UserID: 3, Permission: team.PermissionTypeAdmin.String()},
|
|
},
|
|
},
|
|
{
|
|
description: "should correctly remove current members and admins that are not in the new list",
|
|
input: team.SetTeamMembershipsCommand{
|
|
Members: []string{"user1"},
|
|
Admins: []string{"user3"},
|
|
},
|
|
currentMembers: []*team.TeamMemberDTO{
|
|
{Email: "user1", UserID: 1, Permission: team.PermissionTypeMember},
|
|
{Email: "user2", UserID: 2, Permission: team.PermissionTypeMember},
|
|
{Email: "user3", UserID: 3, Permission: team.PermissionTypeAdmin},
|
|
{Email: "user4", UserID: 4, Permission: team.PermissionTypeAdmin},
|
|
},
|
|
expectedUpdates: []accesscontrol.SetResourcePermissionCommand{
|
|
{UserID: 2, Permission: ""},
|
|
{UserID: 4, Permission: ""},
|
|
},
|
|
},
|
|
{
|
|
description: "should error if a non-existent user is provided",
|
|
input: team.SetTeamMembershipsCommand{
|
|
Members: []string{"non-existent-user", "user2"},
|
|
},
|
|
expectErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.description, func(t *testing.T) {
|
|
cfg := setting.NewCfg()
|
|
userService := &usertest.FakeUserService{
|
|
GetByEmailFn: func(ctx context.Context, query *user.GetUserByEmailQuery) (*user.User, error) {
|
|
id, err := strconv.Atoi(strings.TrimPrefix(query.Email, "user"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
user := &user.User{
|
|
ID: int64(id),
|
|
Email: query.Email,
|
|
}
|
|
return user, nil
|
|
},
|
|
}
|
|
teamSvc := teamtest.NewFakeService()
|
|
teamSvc.ExpectedMembers = tc.currentMembers
|
|
tapi := ProvideTeamAPI(routing.NewRouteRegister(),
|
|
teamSvc,
|
|
actest.FakeService{},
|
|
acimpl.ProvideAccessControl(featuremgmt.WithFeatures()),
|
|
&actest.FakePermissionsService{},
|
|
userService,
|
|
&licensing.OSSLicensingService{},
|
|
cfg,
|
|
preftest.NewPreferenceServiceFake(),
|
|
dashboards.NewFakeDashboardService(t),
|
|
)
|
|
|
|
user := &user.SignedInUser{UserID: 1, OrgID: 1, OrgRole: org.RoleAdmin, Permissions: map[int64]map[string][]string{1: {accesscontrol.ActionOrgUsersRead: {"users:id:*"}}}}
|
|
updates, err := tapi.getTeamMembershipUpdates(context.Background(), 1, 1, tc.input, user)
|
|
if tc.expectErr {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, tc.expectedUpdates, updates)
|
|
})
|
|
}
|
|
}
|
|
|
|
func authedUserWithPermissions(userID, orgID int64, permissions []accesscontrol.Permission) *user.SignedInUser {
|
|
return &user.SignedInUser{UserID: userID, OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByActionContext(context.Background(), permissions)}}
|
|
}
|