The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/ngalert/migration/permissions_test.go

821 lines
31 KiB

package migration
import (
"context"
"encoding/json"
"fmt"
"sort"
"strings"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
"github.com/grafana/grafana/pkg/services/alerting/models"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
ngModels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/team"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
)
// TestDashAlertPermissionMigration tests the execution of the migration specifically for dashboards with custom permissions.
//
//nolint:gocyclo
func TestDashAlertPermissionMigration(t *testing.T) {
genLegacyAlert := func(name string, dashboardId int64, mutators ...func(*models.Alert)) *models.Alert {
a := createAlert(t, 1, int(dashboardId), 1, name, nil)
if len(mutators) > 0 {
for _, mutator := range mutators {
mutator(a)
}
}
return a
}
genAlert := func(title string, namespaceUID string, dashboardUID string, mutators ...func(*ngModels.AlertRule)) *ngModels.AlertRule {
dashTitle := "Dashboard Title " + dashboardUID
a := &ngModels.AlertRule{
ID: 1,
OrgID: 1,
Title: title,
Condition: "A",
Data: []ngModels.AlertQuery{
{
RefID: "A",
DatasourceUID: "__expr__",
Model: json.RawMessage(`{"conditions":[],"intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"classic_conditions"}`),
},
},
NamespaceUID: namespaceUID,
DashboardUID: &dashboardUID,
RuleGroup: fmt.Sprintf("%s - 1m", dashTitle),
IntervalSeconds: 60,
Version: 1,
PanelID: pointer(int64(1)),
RuleGroupIndex: 1,
NoDataState: ngModels.NoData,
ExecErrState: ngModels.AlertingErrState,
For: 60 * time.Second,
Annotations: map[string]string{
"message": "message",
"__dashboardUid__": dashboardUID,
"__panelId__": "1",
},
Labels: map[string]string{},
IsPaused: false,
}
if len(mutators) > 0 {
for _, mutator := range mutators {
mutator(a)
}
}
return a
}
withPanelId := func(id int64) func(*ngModels.AlertRule) {
return func(a *ngModels.AlertRule) {
a.PanelID = pointer(id)
a.Annotations["__panelId__"] = fmt.Sprintf("%d", id)
}
}
genFolder := func(t *testing.T, id int64, uid string, mutators ...func(f *dashboards.Dashboard)) *dashboards.Dashboard {
d := createFolder(t, id, 1, uid)
d.Title = "Original Folder " + uid
if len(mutators) > 0 {
for _, mutator := range mutators {
mutator(d)
}
}
return d
}
genCreatedFolder := func(t *testing.T, title string, mutators ...func(f *dashboards.Dashboard)) *dashboards.Dashboard {
d := createFolder(t, 1, 1, "") // Leave generated UID blank, so we don't compare.
d.Title = title
d.CreatedBy = -1
d.UpdatedBy = -1
if len(mutators) > 0 {
for _, mutator := range mutators {
mutator(d)
}
}
return d
}
genDashboard := func(t *testing.T, id int64, uid string, folderId int64, mutators ...func(f *dashboards.Dashboard)) *dashboards.Dashboard {
d := createDashboard(t, id, 1, uid, folderId, nil)
d.Title = "Dashboard Title " + uid
if len(mutators) > 0 {
for _, mutator := range mutators {
mutator(d)
}
}
return d
}
genPerms := func(perms ...accesscontrol.SetResourcePermissionCommand) []accesscontrol.SetResourcePermissionCommand {
return perms
}
type expectedAlertMigration struct {
Alert *ngModels.AlertRule
Folder *dashboards.Dashboard
Perms []accesscontrol.SetResourcePermissionCommand
}
type testcase struct {
name string
enterprise bool
folders []*dashboards.Dashboard
folderPerms map[string][]accesscontrol.SetResourcePermissionCommand // UID -> Perms
dashboards []*dashboards.Dashboard
dashboardPerms map[string][]accesscontrol.SetResourcePermissionCommand // UID -> Perms
alerts []*models.Alert
roles map[accesscontrol.Role][]accesscontrol.Permission
expected []expectedAlertMigration
}
// Used to perform the same tests for each of builtins, users, and teams.
splitTestcase := func(raw testcase) []testcase {
permTypes := make(map[string]func(accesscontrol.SetResourcePermissionCommand) accesscontrol.SetResourcePermissionCommand, 3)
permTypes["builtins"] = func(p accesscontrol.SetResourcePermissionCommand) accesscontrol.SetResourcePermissionCommand {
return p
}
mapping := map[string]int64{
string(org.RoleEditor): 1,
string(org.RoleViewer): 2,
}
permTypes["users"] = func(p accesscontrol.SetResourcePermissionCommand) accesscontrol.SetResourcePermissionCommand {
id, ok := mapping[p.BuiltinRole]
if !ok {
return p
}
p.UserID = id
p.BuiltinRole = ""
return p
}
permTypes["teams"] = func(p accesscontrol.SetResourcePermissionCommand) accesscontrol.SetResourcePermissionCommand {
id, ok := mapping[p.BuiltinRole]
if !ok {
return p
}
p.TeamID = id
p.BuiltinRole = ""
return p
}
applyTransform := func(tt testcase, pfunc func(p accesscontrol.SetResourcePermissionCommand) accesscontrol.SetResourcePermissionCommand) testcase {
folderPerms := make(map[string][]accesscontrol.SetResourcePermissionCommand, len(tt.folderPerms))
for _, f := range tt.folders {
perms := make([]accesscontrol.SetResourcePermissionCommand, 0, len(tt.folderPerms[f.UID]))
for _, p := range tt.folderPerms[f.UID] {
perms = append(perms, pfunc(p))
}
folderPerms[f.UID] = perms
}
tt.folderPerms = folderPerms
dashboardPerms := make(map[string][]accesscontrol.SetResourcePermissionCommand, len(tt.dashboardPerms))
for _, d := range tt.dashboards {
perms := make([]accesscontrol.SetResourcePermissionCommand, 0, len(tt.dashboardPerms[d.UID]))
for _, p := range tt.dashboardPerms[d.UID] {
perms = append(perms, pfunc(p))
}
dashboardPerms[d.UID] = perms
}
tt.dashboardPerms = dashboardPerms
expected := make([]expectedAlertMigration, 0, len(tt.expected))
for _, ex := range tt.expected {
permissions := make([]accesscontrol.SetResourcePermissionCommand, 0, len(ex.Perms))
for _, p := range ex.Perms {
permissions = append(permissions, pfunc(p))
}
ex.Perms = permissions
sort.SliceStable(permissions, func(i, j int) bool {
if permissions[i].BuiltinRole != permissions[j].BuiltinRole {
return permissions[i].BuiltinRole < permissions[j].BuiltinRole
}
if permissions[i].UserID != permissions[j].UserID {
return permissions[i].UserID < permissions[j].UserID
}
if permissions[i].TeamID != permissions[j].TeamID {
return permissions[i].TeamID < permissions[j].TeamID
}
return permissions[i].Permission < permissions[j].Permission
})
f := *ex.Folder
if strings.Contains(f.Title, "%s") {
hash, err := createHash(permissions)
require.NoError(t, err)
f.Title = fmt.Sprintf(f.Title, hash)
}
expected = append(expected, expectedAlertMigration{
Alert: ex.Alert,
Folder: &f,
Perms: permissions,
})
}
tt.expected = expected
return tt
}
cases := make([]testcase, 0, 3)
for k, pfunc := range permTypes {
tt := applyTransform(raw, pfunc)
tt.name = k
cases = append(cases, tt)
}
return cases
}
basicFolder := genFolder(t, 1, "f_1")
basicDashboard := genDashboard(t, 2, "d_1", basicFolder.ID)
defaultPerms := genPerms(
accesscontrol.SetResourcePermissionCommand{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
accesscontrol.SetResourcePermissionCommand{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
)
basicAlert1 := genLegacyAlert("alert1", basicDashboard.ID, func(a *models.Alert) { a.PanelID = 1 })
basicAlert2 := genLegacyAlert("alert2", basicDashboard.ID, func(a *models.Alert) { a.PanelID = 2 })
basicPerms := func() map[accesscontrol.Role][]accesscontrol.Permission {
basic := make(map[accesscontrol.Role][]accesscontrol.Permission)
var permissions []accesscontrol.Permission
ts := time.Now()
for _, action := range append(ossaccesscontrol.DashboardAdminActions, ossaccesscontrol.FolderAdminActions...) {
if isDashboardAction := strings.HasPrefix(action, "dashboards"); isDashboardAction {
permissions = append(permissions, accesscontrol.Permission{
Action: action,
Scope: dashboards.ScopeDashboardsAll,
Created: ts,
Updated: ts,
})
}
permissions = append(permissions, accesscontrol.Permission{
Action: action,
Scope: dashboards.ScopeFoldersAll,
Created: ts,
Updated: ts,
})
}
basic[accesscontrol.Role{Name: accesscontrol.BasicRolePrefix + "admin"}] = permissions
return basic
}
tc := []testcase{
{
name: "alerts in dashboard and folder with default permissions migrate to same folder",
folders: []*dashboards.Dashboard{basicFolder},
folderPerms: map[string][]accesscontrol.SetResourcePermissionCommand{basicFolder.UID: defaultPerms},
dashboards: []*dashboards.Dashboard{basicDashboard},
dashboardPerms: map[string][]accesscontrol.SetResourcePermissionCommand{basicDashboard.UID: defaultPerms},
alerts: []*models.Alert{basicAlert1, basicAlert2},
expected: []expectedAlertMigration{
{
Alert: genAlert(basicAlert1.Name, basicFolder.UID, basicDashboard.UID, withPanelId(basicAlert1.PanelID)),
Folder: basicFolder,
Perms: defaultPerms,
},
{
Alert: genAlert(basicAlert2.Name, basicFolder.UID, basicDashboard.UID, withPanelId(basicAlert2.PanelID)),
Folder: basicFolder,
Perms: defaultPerms,
},
},
},
{
name: "dashboard override cannot lessen folder permissions",
folders: []*dashboards.Dashboard{basicFolder},
folderPerms: map[string][]accesscontrol.SetResourcePermissionCommand{basicFolder.UID: defaultPerms},
dashboards: []*dashboards.Dashboard{basicDashboard},
dashboardPerms: map[string][]accesscontrol.SetResourcePermissionCommand{
basicDashboard.UID: {
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_VIEW.String()}, // Change.
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
},
},
alerts: []*models.Alert{basicAlert1},
expected: []expectedAlertMigration{
{
Alert: genAlert(basicAlert1.Name, basicFolder.UID, basicDashboard.UID),
Folder: basicFolder,
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()}, // Inherits from Folder.
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
},
},
},
},
{
name: "dashboard with various permission overrides should create new folder",
folders: []*dashboards.Dashboard{basicFolder},
folderPerms: map[string][]accesscontrol.SetResourcePermissionCommand{basicFolder.UID: defaultPerms},
dashboards: []*dashboards.Dashboard{
genDashboard(t, 2, "d_1", basicFolder.ID),
genDashboard(t, 3, "d_2", basicFolder.ID),
genDashboard(t, 4, "d_3", basicFolder.ID),
genDashboard(t, 5, "d_4", basicFolder.ID),
genDashboard(t, 6, "d_5", basicFolder.ID),
},
dashboardPerms: map[string][]accesscontrol.SetResourcePermissionCommand{
"d_1": {
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_EDIT.String()}, // Change.
},
"d_2": {
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Change.
},
"d_3": {
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Change.
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_EDIT.String()}, // Change.
},
"d_4": {
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Change.
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
},
"d_5": {
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Change.
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Change.
},
},
alerts: []*models.Alert{genLegacyAlert("alert1", 2), genLegacyAlert("alert2", 3), genLegacyAlert("alert3", 4), genLegacyAlert("alert4", 5), genLegacyAlert("alert5", 6)},
expected: []expectedAlertMigration{
{
Alert: genAlert("alert1", "", "d_1"),
Folder: genCreatedFolder(t, "Original Folder f_1 Alerts - %s"),
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_EDIT.String()}, // Change.
},
},
{
Alert: genAlert("alert2", "", "d_2"),
Folder: genCreatedFolder(t, "Original Folder f_1 Alerts - %s"),
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Change.
},
},
{
Alert: genAlert("alert3", "", "d_3"),
Folder: genCreatedFolder(t, "Original Folder f_1 Alerts - %s"),
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Change.
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_EDIT.String()}, // Change.
},
},
{
Alert: genAlert("alert4", "", "d_4"),
Folder: genCreatedFolder(t, "Original Folder f_1 Alerts - %s"),
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Change.
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
},
},
{
Alert: genAlert("alert5", "", "d_5"),
Folder: genCreatedFolder(t, "Original Folder f_1 Alerts - %s"),
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Change.
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Change.
},
},
},
},
{
name: "missing dashboard permission is inherited from folder",
folders: []*dashboards.Dashboard{genFolder(t, 1, "f_1"), genFolder(t, 2, "f_2")},
folderPerms: map[string][]accesscontrol.SetResourcePermissionCommand{
"f_1": {
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_ADMIN.String()},
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_ADMIN.String()},
},
"f_2": {
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_VIEW.String()},
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
},
},
dashboards: []*dashboards.Dashboard{
genDashboard(t, 3, "d_1", 1),
genDashboard(t, 4, "d_2", 1),
genDashboard(t, 5, "d_3", 2),
genDashboard(t, 6, "d_4", 2),
},
dashboardPerms: map[string][]accesscontrol.SetResourcePermissionCommand{
"d_1": {
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
},
"d_2": {
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
},
"d_3": {
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
},
"d_4": {
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
},
},
alerts: []*models.Alert{genLegacyAlert("alert1", 3), genLegacyAlert("alert2", 4), genLegacyAlert("alert3", 5), genLegacyAlert("alert4", 6)},
expected: []expectedAlertMigration{
{
Alert: genAlert("alert1", "f_1", "d_1"),
Folder: genFolder(t, 1, "f_1"), // Original folder since the perms didn't change.
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Inherits from Folder.
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Overrides from Folder.
},
},
{
Alert: genAlert("alert2", "f_1", "d_2"),
Folder: genFolder(t, 1, "f_1"), // Original folder since the perms didn't change.
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Overrides from Folder.
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // Inherits from Folder.
},
},
{
Alert: genAlert("alert3", "f_2", "d_3"),
Folder: genFolder(t, 2, "f_2"), // Original folder since the perms didn't change.
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_VIEW.String()}, // Inherits from Folder.
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
},
},
{
Alert: genAlert("alert4", "", "d_4"),
Folder: genCreatedFolder(t, "Original Folder f_2 Alerts - %s"),
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()}, // Inherits from Folder.
},
},
},
},
{
name: "missing dashboard and folder view permission is still missing",
folders: []*dashboards.Dashboard{basicFolder},
folderPerms: map[string][]accesscontrol.SetResourcePermissionCommand{
basicFolder.UID: {
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
},
},
dashboards: []*dashboards.Dashboard{basicDashboard},
dashboardPerms: map[string][]accesscontrol.SetResourcePermissionCommand{
basicDashboard.UID: {
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_VIEW.String()},
},
},
alerts: []*models.Alert{basicAlert1},
expected: []expectedAlertMigration{
{
Alert: genAlert(basicAlert1.Name, basicFolder.UID, basicDashboard.UID),
Folder: basicFolder,
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
},
},
},
},
// General folder.
{
name: "dashboard in general folder with default permissions migrates to General Alerting subfolder for permission",
dashboards: []*dashboards.Dashboard{genDashboard(t, 1, "d_1", 0)}, // Dashboard in general folder.
dashboardPerms: map[string][]accesscontrol.SetResourcePermissionCommand{
"d_1": defaultPerms,
},
alerts: []*models.Alert{genLegacyAlert("alert1", 1)},
expected: []expectedAlertMigration{
{
Alert: genAlert("alert1", "f_1", "d_1"),
Folder: genCreatedFolder(t, "General Alerting Alerts - %s"),
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()}, // From Dashboard.
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()}, // From Dashboard.
},
},
},
},
{
name: "dashboard in general folder with some perms migrates to General Alerting subfolder with correct permissions",
dashboards: []*dashboards.Dashboard{genDashboard(t, 1, "d_1", 0)}, // Dashboard in general folder.
dashboardPerms: map[string][]accesscontrol.SetResourcePermissionCommand{
"d_1": { // Missing viewer.
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
},
},
alerts: []*models.Alert{genLegacyAlert("alert1", 1)},
expected: []expectedAlertMigration{
{
Alert: genAlert("alert1", "f_1", "d_1"),
Folder: genCreatedFolder(t, "General Alerting Alerts - %s"),
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()}, // From Dashboard.
},
},
},
},
{
name: "dashboard in general folder with empty perms migrates to General Alerting",
dashboards: []*dashboards.Dashboard{genDashboard(t, 1, "d_1", 0)}, // Dashboard in general folder.
alerts: []*models.Alert{genLegacyAlert("alert1", 1)},
expected: []expectedAlertMigration{
{
Alert: genAlert("alert1", "f_1", "d_1"),
Folder: genCreatedFolder(t, "General Alerting"),
Perms: []accesscontrol.SetResourcePermissionCommand{},
},
},
},
// The following tests handled extra requirements of enterprise RBAC in that they include basic, fixed, and custom roles.
{
name: "should handle basic roles the same as managed builtin roles",
enterprise: true,
roles: basicPerms(),
folders: []*dashboards.Dashboard{basicFolder},
folderPerms: map[string][]accesscontrol.SetResourcePermissionCommand{basicFolder.UID: defaultPerms},
dashboards: []*dashboards.Dashboard{
genDashboard(t, 2, "d_1", basicFolder.ID),
},
dashboardPerms: map[string][]accesscontrol.SetResourcePermissionCommand{
"d_1": {
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_EDIT.String()}, // Change.
},
},
alerts: []*models.Alert{genLegacyAlert("alert1", 2)},
expected: []expectedAlertMigration{
{
Alert: genAlert("alert1", "", "d_1"),
Folder: genCreatedFolder(t, "Original Folder f_1 Alerts - %s"),
Perms: []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(org.RoleAdmin), Permission: dashboardaccess.PERMISSION_ADMIN.String()}, // From basic:admin.
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_EDIT.String()}, // Change.
},
},
},
},
{
name: "should ignore fixed roles even if they would affect access",
enterprise: true,
roles: map[accesscontrol.Role][]accesscontrol.Permission{
{Name: "fixed:dashboards:writer"}: {
{Action: dashboards.ActionDashboardsRead, Scope: dashboards.ScopeDashboardsAll},
{Action: dashboards.ActionDashboardsWrite, Scope: dashboards.ScopeDashboardsAll},
{Action: dashboards.ActionDashboardsDelete, Scope: dashboards.ScopeDashboardsAll},
{Action: dashboards.ActionDashboardsCreate, Scope: dashboards.ScopeFoldersAll},
{Action: dashboards.ActionDashboardsPermissionsRead, Scope: dashboards.ScopeDashboardsAll},
{Action: dashboards.ActionDashboardsPermissionsWrite, Scope: dashboards.ScopeDashboardsAll},
},
},
folders: []*dashboards.Dashboard{basicFolder},
folderPerms: map[string][]accesscontrol.SetResourcePermissionCommand{basicFolder.UID: defaultPerms},
dashboards: []*dashboards.Dashboard{basicDashboard},
dashboardPerms: map[string][]accesscontrol.SetResourcePermissionCommand{basicDashboard.UID: defaultPerms},
alerts: []*models.Alert{basicAlert1},
expected: []expectedAlertMigration{ // Expect no new folder.
{
Alert: genAlert(basicAlert1.Name, basicFolder.UID, basicDashboard.UID),
Folder: basicFolder,
Perms: defaultPerms,
},
},
},
{
name: "should ignore custom roles even if they would affect access",
enterprise: true,
roles: map[accesscontrol.Role][]accesscontrol.Permission{
{Name: "custom role"}: {
{Action: dashboards.ActionDashboardsRead, Scope: dashboards.ScopeDashboardsAll},
{Action: dashboards.ActionDashboardsWrite, Scope: dashboards.ScopeDashboardsAll},
{Action: dashboards.ActionDashboardsDelete, Scope: dashboards.ScopeDashboardsAll},
{Action: dashboards.ActionDashboardsCreate, Scope: dashboards.ScopeFoldersAll},
{Action: dashboards.ActionDashboardsPermissionsRead, Scope: dashboards.ScopeDashboardsAll},
{Action: dashboards.ActionDashboardsPermissionsWrite, Scope: dashboards.ScopeDashboardsAll},
},
},
folders: []*dashboards.Dashboard{basicFolder},
folderPerms: map[string][]accesscontrol.SetResourcePermissionCommand{basicFolder.UID: defaultPerms},
dashboards: []*dashboards.Dashboard{basicDashboard},
dashboardPerms: map[string][]accesscontrol.SetResourcePermissionCommand{basicDashboard.UID: defaultPerms},
alerts: []*models.Alert{basicAlert1},
expected: []expectedAlertMigration{ // Expect no new folder.
{
Alert: genAlert(basicAlert1.Name, basicFolder.UID, basicDashboard.UID),
Folder: basicFolder,
Perms: defaultPerms,
},
},
},
}
for _, ttRaw := range tc {
t.Run(ttRaw.name, func(t *testing.T) {
for _, tt := range splitTestcase(ttRaw) {
t.Run(tt.name, func(t *testing.T) {
sqlStore := db.InitTestDB(t)
x := sqlStore.GetEngine()
if tt.enterprise {
createRoles(t, context.Background(), sqlStore, tt.roles)
}
service := NewTestMigrationService(t, sqlStore, &setting.Cfg{})
setupLegacyAlertsTables(t, x, nil, tt.alerts, tt.folders, tt.dashboards)
for i := 1; i < 3; i++ {
_, err := x.Insert(user.User{
ID: int64(i),
OrgID: 1,
Name: fmt.Sprintf("user%v", i),
Login: fmt.Sprintf("user%v", i),
Email: fmt.Sprintf("user%v@example.org", i),
Created: now,
Updated: now,
})
require.NoError(t, err)
}
for i := 1; i < 3; i++ {
_, err := x.Insert(team.Team{
ID: int64(i),
OrgID: 1,
UID: fmt.Sprintf("team%v", i),
Name: fmt.Sprintf("team%v", i),
Created: now,
Updated: now,
})
require.NoError(t, err)
}
for _, f := range tt.folders {
_, err := service.migrationStore.SetFolderPermissions(context.Background(), 1, f.UID, tt.folderPerms[f.UID]...)
require.NoError(t, err)
}
for _, d := range tt.dashboards {
_, err := service.migrationStore.SetDashboardPermissions(context.Background(), 1, d.UID, tt.dashboardPerms[d.UID]...)
require.NoError(t, err)
}
err := service.Run(context.Background())
require.NoError(t, err)
// construct actuals.
orgId := int64(1)
rules := getAlertRules(t, x, orgId)
actual := make([]expectedAlertMigration, 0, len(rules))
for i, r := range rules {
// Remove generated fields.
require.NotEqual(t, r.Labels["rule_uid"], "")
delete(r.Labels, "rule_uid")
require.NotEqual(t, r.Annotations["__alertId__"], "")
delete(r.Annotations, "__alertId__")
folder := getDashboard(t, x, orgId, r.NamespaceUID)
rperms, err := service.migrationStore.GetFolderPermissions(context.Background(), getMigrationUser(orgId), folder.UID)
require.NoError(t, err)
expected := tt.expected[i]
if expected.Folder.UID == "" {
// We're expecting the UID to be generated, so remove it from comparison.
folder.UID = ""
r.NamespaceUID = ""
expected.Alert.NamespaceUID = ""
}
keep := make(map[accesscontrol.SetResourcePermissionCommand]dashboardaccess.PermissionType)
for _, p := range rperms {
if permission := service.migrationStore.MapActions(p); permission != "" {
sp := accesscontrol.SetResourcePermissionCommand{
UserID: p.UserId,
TeamID: p.TeamId,
BuiltinRole: p.BuiltInRole,
}
pType := permissionMap[permission]
current, ok := keep[sp]
if !ok || pType > current {
keep[sp] = pType
}
}
}
perms := make([]accesscontrol.SetResourcePermissionCommand, 0, len(keep))
for p, pType := range keep {
p.Permission = pType.String()
perms = append(perms, p)
}
actual = append(actual, expectedAlertMigration{
Alert: r,
Folder: folder,
Perms: perms,
})
}
cOpt := []cmp.Option{
cmpopts.SortSlices(func(a, b expectedAlertMigration) bool {
return a.Alert.Title < b.Alert.Title
}),
cmpopts.SortSlices(func(a, b accesscontrol.SetResourcePermissionCommand) bool {
if a.BuiltinRole != b.BuiltinRole {
return a.BuiltinRole < b.BuiltinRole
}
if a.UserID != b.UserID {
return a.UserID < b.UserID
}
if a.TeamID != b.TeamID {
return a.TeamID < b.TeamID
}
return a.Permission < b.Permission
}),
cmpopts.IgnoreUnexported(ngModels.AlertRule{}, ngModels.AlertQuery{}),
cmpopts.IgnoreFields(ngModels.AlertRule{}, "ID", "Updated", "UID"),
cmpopts.IgnoreFields(dashboards.Dashboard{}, "ID", "Created", "Updated", "Data", "Slug"),
}
if !cmp.Equal(tt.expected, actual, cOpt...) {
t.Errorf("Unexpected Rule: %v", cmp.Diff(tt.expected, actual, cOpt...))
}
})
}
})
}
}
func createRoles(t testing.TB, ctx context.Context, store db.DB, rolePerms map[accesscontrol.Role][]accesscontrol.Permission) {
_ = store.WithDbSession(ctx, func(sess *db.Session) error {
ts := time.Now()
var roles []accesscontrol.Role
basic := accesscontrol.BuildBasicRoleDefinitions()
var permissions []accesscontrol.Permission
var builtinRoleAssignments []accesscontrol.BuiltinRole
var userRoleAssignments []accesscontrol.UserRole
var teamRoleAssignments []accesscontrol.TeamRole
i := int64(1)
for role, perms := range rolePerms {
if role.IsBasic() {
for roleType, br := range basic {
if br.Name == role.Name {
role = br.Role()
builtinRoleAssignments = append(builtinRoleAssignments, accesscontrol.BuiltinRole{
OrgID: accesscontrol.GlobalOrgID, RoleID: i, Role: roleType, Created: ts, Updated: ts,
})
}
}
} else {
userRoleAssignments = append(userRoleAssignments, accesscontrol.UserRole{
OrgID: accesscontrol.GlobalOrgID, RoleID: i, UserID: 1, Created: ts,
})
teamRoleAssignments = append(teamRoleAssignments, accesscontrol.TeamRole{
OrgID: accesscontrol.GlobalOrgID, RoleID: i, TeamID: 1, Created: ts,
})
}
role.ID = i
role.Created = ts
role.Updated = ts
roles = append(roles, role)
for _, p := range perms {
permissions = append(permissions, accesscontrol.Permission{
RoleID: role.ID, Action: p.Action, Scope: p.Scope, Created: ts, Updated: ts,
})
}
i++
}
_, err := sess.InsertMulti(&roles)
require.NoError(t, err)
_, err = sess.InsertMulti(&permissions)
require.NoError(t, err)
_, err = sess.InsertMulti(&builtinRoleAssignments)
require.NoError(t, err)
_, err = sess.InsertMulti(&userRoleAssignments)
require.NoError(t, err)
_, err = sess.InsertMulti(&teamRoleAssignments)
require.NoError(t, err)
return nil
})
}