2018-04-08 08:06:22 -05:00
|
|
|
package guardian
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2021-09-23 10:43:32 -05:00
|
|
|
"context"
|
2018-04-08 08:06:22 -05:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2022-05-17 13:52:22 -05:00
|
|
|
"github.com/stretchr/testify/assert"
|
2022-06-01 13:16:26 -05:00
|
|
|
"github.com/stretchr/testify/mock"
|
2022-05-17 13:52:22 -05:00
|
|
|
|
2022-10-19 08:02:15 -05:00
|
|
|
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
2020-02-29 06:35:15 -06:00
|
|
|
"github.com/grafana/grafana/pkg/models"
|
2022-06-01 13:16:26 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
2022-08-10 04:56:48 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/org"
|
2022-09-20 11:58:04 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
2022-08-10 04:56:48 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/user"
|
2018-04-08 08:06:22 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
type scenarioContext struct {
|
|
|
|
t *testing.T
|
|
|
|
orgRoleScenario string
|
|
|
|
permissionScenario string
|
|
|
|
g DashboardGuardian
|
2022-08-10 04:56:48 -05:00
|
|
|
givenUser *user.SignedInUser
|
2018-04-08 08:06:22 -05:00
|
|
|
givenDashboardID int64
|
2022-07-18 08:14:58 -05:00
|
|
|
givenPermissions []*models.DashboardACLInfoDTO
|
2020-02-29 06:35:15 -06:00
|
|
|
givenTeams []*models.TeamDTO
|
2022-07-18 08:14:58 -05:00
|
|
|
updatePermissions []*models.DashboardACL
|
2018-04-08 08:06:22 -05:00
|
|
|
expectedFlags permissionFlags
|
|
|
|
callerFile string
|
|
|
|
callerLine int
|
|
|
|
}
|
|
|
|
|
|
|
|
type scenarioFunc func(c *scenarioContext)
|
|
|
|
|
2022-08-10 04:56:48 -05:00
|
|
|
func orgRoleScenario(desc string, t *testing.T, role org.RoleType, fn scenarioFunc) {
|
2020-11-24 04:36:00 -06:00
|
|
|
t.Run(desc, func(t *testing.T) {
|
2022-08-10 04:56:48 -05:00
|
|
|
user := &user.SignedInUser{
|
2022-08-11 06:28:55 -05:00
|
|
|
UserID: userID,
|
|
|
|
OrgID: orgID,
|
2020-11-24 04:36:00 -06:00
|
|
|
OrgRole: role,
|
|
|
|
}
|
2022-10-19 08:02:15 -05:00
|
|
|
store := dbtest.NewFakeDB()
|
2022-09-20 11:58:04 -05:00
|
|
|
guard := newDashboardGuardian(context.Background(), dashboardID, orgID, user, store, &dashboards.FakeDashboardService{}, &teamtest.FakeService{})
|
2020-11-24 04:36:00 -06:00
|
|
|
|
|
|
|
sc := &scenarioContext{
|
|
|
|
t: t,
|
|
|
|
orgRoleScenario: desc,
|
|
|
|
givenUser: user,
|
|
|
|
givenDashboardID: dashboardID,
|
|
|
|
g: guard,
|
|
|
|
}
|
2018-06-19 04:10:17 -05:00
|
|
|
fn(sc)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-08-10 04:56:48 -05:00
|
|
|
func apiKeyScenario(desc string, t *testing.T, role org.RoleType, fn scenarioFunc) {
|
2020-11-24 04:36:00 -06:00
|
|
|
t.Run(desc, func(t *testing.T) {
|
2022-08-10 04:56:48 -05:00
|
|
|
user := &user.SignedInUser{
|
2022-08-11 06:28:55 -05:00
|
|
|
UserID: 0,
|
|
|
|
OrgID: orgID,
|
2020-11-24 04:36:00 -06:00
|
|
|
OrgRole: role,
|
2022-08-11 06:28:55 -05:00
|
|
|
ApiKeyID: 10,
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
2022-10-19 08:02:15 -05:00
|
|
|
store := dbtest.NewFakeDB()
|
2022-09-20 11:58:04 -05:00
|
|
|
guard := newDashboardGuardian(context.Background(), dashboardID, orgID, user, store, &dashboards.FakeDashboardService{}, &teamtest.FakeService{})
|
2020-11-24 04:36:00 -06:00
|
|
|
sc := &scenarioContext{
|
|
|
|
t: t,
|
|
|
|
orgRoleScenario: desc,
|
|
|
|
givenUser: user,
|
|
|
|
givenDashboardID: dashboardID,
|
|
|
|
g: guard,
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
2020-11-24 04:36:00 -06:00
|
|
|
fn(sc)
|
2018-04-08 08:06:22 -05:00
|
|
|
})
|
2020-11-24 04:36:00 -06:00
|
|
|
}
|
2018-04-08 08:06:22 -05:00
|
|
|
|
2020-11-24 04:36:00 -06:00
|
|
|
func permissionScenario(desc string, dashboardID int64, sc *scenarioContext,
|
2022-07-18 08:14:58 -05:00
|
|
|
permissions []*models.DashboardACLInfoDTO, fn scenarioFunc) {
|
2020-11-24 04:36:00 -06:00
|
|
|
sc.t.Run(desc, func(t *testing.T) {
|
2022-10-19 08:02:15 -05:00
|
|
|
store := dbtest.NewFakeDB()
|
2020-11-24 04:36:00 -06:00
|
|
|
teams := []*models.TeamDTO{}
|
|
|
|
|
|
|
|
for _, p := range permissions {
|
|
|
|
if p.TeamId > 0 {
|
|
|
|
teams = append(teams, &models.TeamDTO{Id: p.TeamId})
|
|
|
|
}
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
2022-09-20 11:58:04 -05:00
|
|
|
teamSvc := &teamtest.FakeService{ExpectedTeamsByUser: teams}
|
2018-04-08 08:06:22 -05:00
|
|
|
|
2022-06-01 13:16:26 -05:00
|
|
|
dashSvc := dashboards.NewFakeDashboardService(t)
|
2022-07-18 08:14:58 -05:00
|
|
|
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
|
|
|
|
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
|
2022-06-01 13:16:26 -05:00
|
|
|
q.Result = permissions
|
|
|
|
}).Return(nil)
|
|
|
|
|
2020-11-24 04:36:00 -06:00
|
|
|
sc.permissionScenario = desc
|
2022-09-20 11:58:04 -05:00
|
|
|
sc.g = newDashboardGuardian(context.Background(), dashboardID, sc.givenUser.OrgID, sc.givenUser, store, dashSvc, teamSvc)
|
2020-11-24 04:36:00 -06:00
|
|
|
sc.givenDashboardID = dashboardID
|
|
|
|
sc.givenPermissions = permissions
|
|
|
|
sc.givenTeams = teams
|
2018-04-08 08:06:22 -05:00
|
|
|
|
|
|
|
fn(sc)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
type permissionType uint8
|
|
|
|
|
|
|
|
const (
|
|
|
|
USER permissionType = 1 << iota
|
|
|
|
TEAM
|
|
|
|
EDITOR
|
|
|
|
VIEWER
|
|
|
|
)
|
|
|
|
|
|
|
|
func (p permissionType) String() string {
|
|
|
|
names := map[uint8]string{
|
|
|
|
uint8(USER): "user",
|
|
|
|
uint8(TEAM): "team",
|
|
|
|
uint8(EDITOR): "editor role",
|
|
|
|
uint8(VIEWER): "viewer role",
|
|
|
|
}
|
|
|
|
return names[uint8(p)]
|
|
|
|
}
|
|
|
|
|
|
|
|
type permissionFlags uint8
|
|
|
|
|
|
|
|
const (
|
|
|
|
NO_ACCESS permissionFlags = 1 << iota
|
|
|
|
CAN_ADMIN
|
|
|
|
CAN_EDIT
|
|
|
|
CAN_SAVE
|
|
|
|
CAN_VIEW
|
|
|
|
FULL_ACCESS = CAN_ADMIN | CAN_EDIT | CAN_SAVE | CAN_VIEW
|
|
|
|
EDITOR_ACCESS = CAN_EDIT | CAN_SAVE | CAN_VIEW
|
|
|
|
VIEWER_ACCESS = CAN_VIEW
|
|
|
|
)
|
|
|
|
|
2020-11-05 08:37:11 -06:00
|
|
|
func (f permissionFlags) canAdmin() bool {
|
|
|
|
return f&CAN_ADMIN != 0
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
2020-11-05 08:37:11 -06:00
|
|
|
func (f permissionFlags) canEdit() bool {
|
|
|
|
return f&CAN_EDIT != 0
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
2020-11-05 08:37:11 -06:00
|
|
|
func (f permissionFlags) canSave() bool {
|
|
|
|
return f&CAN_SAVE != 0
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
2020-11-05 08:37:11 -06:00
|
|
|
func (f permissionFlags) canView() bool {
|
|
|
|
return f&CAN_VIEW != 0
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
2020-11-05 08:37:11 -06:00
|
|
|
func (f permissionFlags) noAccess() bool {
|
|
|
|
return f&(CAN_ADMIN|CAN_EDIT|CAN_SAVE|CAN_VIEW) == 0
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (f permissionFlags) String() string {
|
|
|
|
r := []string{}
|
|
|
|
|
|
|
|
if f.canAdmin() {
|
|
|
|
r = append(r, "admin")
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.canEdit() {
|
|
|
|
r = append(r, "edit")
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.canSave() {
|
|
|
|
r = append(r, "save")
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.canView() {
|
|
|
|
r = append(r, "view")
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.noAccess() {
|
|
|
|
r = append(r, "<no access>")
|
|
|
|
}
|
|
|
|
|
2020-07-16 07:39:01 -05:00
|
|
|
return strings.Join(r, ", ")
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sc *scenarioContext) reportSuccess() {
|
2020-11-24 04:36:00 -06:00
|
|
|
assert.True(sc.t, true)
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sc *scenarioContext) reportFailure(desc string, expected interface{}, actual interface{}) {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
buf.WriteString("\n")
|
|
|
|
buf.WriteString(sc.orgRoleScenario)
|
|
|
|
buf.WriteString(" ")
|
|
|
|
buf.WriteString(sc.permissionScenario)
|
|
|
|
buf.WriteString("\n ")
|
|
|
|
buf.WriteString(desc)
|
|
|
|
buf.WriteString("\n")
|
|
|
|
buf.WriteString(fmt.Sprintf("Source test: %s:%d\n", sc.callerFile, sc.callerLine))
|
|
|
|
buf.WriteString(fmt.Sprintf("Expected: %v\n", expected))
|
|
|
|
buf.WriteString(fmt.Sprintf("Actual: %v\n", actual))
|
|
|
|
buf.WriteString("Context:")
|
2022-08-11 06:28:55 -05:00
|
|
|
buf.WriteString(fmt.Sprintf("\n Given user: orgRole=%s, id=%d, orgId=%d", sc.givenUser.OrgRole, sc.givenUser.UserID, sc.givenUser.OrgID))
|
2018-04-08 08:06:22 -05:00
|
|
|
buf.WriteString(fmt.Sprintf("\n Given dashboard id: %d", sc.givenDashboardID))
|
|
|
|
|
|
|
|
for i, p := range sc.givenPermissions {
|
|
|
|
r := "<nil>"
|
|
|
|
if p.Role != nil {
|
|
|
|
r = string(*p.Role)
|
|
|
|
}
|
|
|
|
buf.WriteString(fmt.Sprintf("\n Given permission (%d): dashboardId=%d, userId=%d, teamId=%d, role=%v, permission=%s", i, p.DashboardId, p.UserId, p.TeamId, r, p.Permission.String()))
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, t := range sc.givenTeams {
|
|
|
|
buf.WriteString(fmt.Sprintf("\n Given team (%d): id=%d", i, t.Id))
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, p := range sc.updatePermissions {
|
|
|
|
r := "<nil>"
|
|
|
|
if p.Role != nil {
|
|
|
|
r = string(*p.Role)
|
|
|
|
}
|
2020-11-17 10:09:14 -06:00
|
|
|
buf.WriteString(fmt.Sprintf("\n Update permission (%d): dashboardId=%d, userId=%d, teamId=%d, role=%v, permission=%s", i, p.DashboardID, p.UserID, p.TeamID, r, p.Permission.String()))
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
sc.t.Fatalf(buf.String())
|
|
|
|
}
|
|
|
|
|
2022-07-18 08:14:58 -05:00
|
|
|
func newCustomUserPermission(dashboardID int64, userID int64, permission models.PermissionType) *models.DashboardACL {
|
|
|
|
return &models.DashboardACL{OrgID: orgID, DashboardID: dashboardID, UserID: userID, Permission: permission}
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
2022-07-18 08:14:58 -05:00
|
|
|
func newDefaultUserPermission(dashboardID int64, permission models.PermissionType) *models.DashboardACL {
|
2018-04-08 08:06:22 -05:00
|
|
|
return newCustomUserPermission(dashboardID, userID, permission)
|
|
|
|
}
|
|
|
|
|
2022-07-18 08:14:58 -05:00
|
|
|
func newCustomTeamPermission(dashboardID int64, teamID int64, permission models.PermissionType) *models.DashboardACL {
|
|
|
|
return &models.DashboardACL{OrgID: orgID, DashboardID: dashboardID, TeamID: teamID, Permission: permission}
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
2022-07-18 08:14:58 -05:00
|
|
|
func newDefaultTeamPermission(dashboardID int64, permission models.PermissionType) *models.DashboardACL {
|
2018-04-08 08:06:22 -05:00
|
|
|
return newCustomTeamPermission(dashboardID, teamID, permission)
|
|
|
|
}
|
|
|
|
|
2022-07-18 08:14:58 -05:00
|
|
|
func newAdminRolePermission(dashboardID int64, permission models.PermissionType) *models.DashboardACL {
|
|
|
|
return &models.DashboardACL{OrgID: orgID, DashboardID: dashboardID, Role: &adminRole, Permission: permission}
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
2022-07-18 08:14:58 -05:00
|
|
|
func newEditorRolePermission(dashboardID int64, permission models.PermissionType) *models.DashboardACL {
|
|
|
|
return &models.DashboardACL{OrgID: orgID, DashboardID: dashboardID, Role: &editorRole, Permission: permission}
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
2022-07-18 08:14:58 -05:00
|
|
|
func newViewerRolePermission(dashboardID int64, permission models.PermissionType) *models.DashboardACL {
|
|
|
|
return &models.DashboardACL{OrgID: orgID, DashboardID: dashboardID, Role: &viewerRole, Permission: permission}
|
2018-04-08 08:06:22 -05:00
|
|
|
}
|
|
|
|
|
2022-07-18 08:14:58 -05:00
|
|
|
func toDto(acl *models.DashboardACL) *models.DashboardACLInfoDTO {
|
|
|
|
return &models.DashboardACLInfoDTO{
|
2020-11-17 10:09:14 -06:00
|
|
|
OrgId: acl.OrgID,
|
|
|
|
DashboardId: acl.DashboardID,
|
|
|
|
UserId: acl.UserID,
|
|
|
|
TeamId: acl.TeamID,
|
2018-04-08 08:06:22 -05:00
|
|
|
Role: acl.Role,
|
|
|
|
Permission: acl.Permission,
|
|
|
|
PermissionName: acl.Permission.String(),
|
|
|
|
}
|
|
|
|
}
|