RBAC: Annotation permission migration (#78899)

* add annotation permissions to dashboard managed role and add migrations for annotation permissions

* fix a bug with conditional access level definitions

* add tests

* Update pkg/services/sqlstore/migrations/accesscontrol/dashboard_permissions.go

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>

* apply feedback

* add batching, fix tests and a typo

* add one more test

* undo unneeded change

* undo unwanted change

* only check the default basic permissions for non-OSS instances

* account for all wildcards and simplify the check a bit

* error handling and extra conditionals to avoid test failures

* fix a bug with admin permissions not appearing for folders

* fix the OSS check

---------

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
This commit is contained in:
Ieva 2024-01-26 17:17:29 +00:00 committed by GitHub
parent 138079bbd8
commit 048d1e7c86
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 547 additions and 12 deletions

View File

@ -606,7 +606,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
Group: "Annotations",
Permissions: []ac.Permission{
{Action: ac.ActionAnnotationsRead, Scope: ac.ScopeAnnotationsTypeOrganization},
{Action: ac.ActionAnnotationsRead, Scope: dashboards.ScopeDashboardsAll},
{Action: ac.ActionAnnotationsRead, Scope: dashboards.ScopeFoldersAll},
},
},
Grants: []string{string(org.RoleAdmin)},
@ -620,11 +620,11 @@ func (hs *HTTPServer) declareFixedRoles() error {
Group: "Annotations",
Permissions: []ac.Permission{
{Action: ac.ActionAnnotationsCreate, Scope: ac.ScopeAnnotationsTypeOrganization},
{Action: ac.ActionAnnotationsCreate, Scope: dashboards.ScopeDashboardsAll},
{Action: ac.ActionAnnotationsCreate, Scope: dashboards.ScopeFoldersAll},
{Action: ac.ActionAnnotationsDelete, Scope: ac.ScopeAnnotationsTypeOrganization},
{Action: ac.ActionAnnotationsDelete, Scope: dashboards.ScopeDashboardsAll},
{Action: ac.ActionAnnotationsDelete, Scope: dashboards.ScopeFoldersAll},
{Action: ac.ActionAnnotationsWrite, Scope: ac.ScopeAnnotationsTypeOrganization},
{Action: ac.ActionAnnotationsWrite, Scope: dashboards.ScopeDashboardsAll},
{Action: ac.ActionAnnotationsWrite, Scope: dashboards.ScopeFoldersAll},
},
},
Grants: []string{string(org.RoleAdmin)},

View File

@ -118,6 +118,27 @@ var DashboardViewActions = []string{dashboards.ActionDashboardsRead}
var DashboardEditActions = append(DashboardViewActions, []string{dashboards.ActionDashboardsWrite, dashboards.ActionDashboardsDelete}...)
var DashboardAdminActions = append(DashboardEditActions, []string{dashboards.ActionDashboardsPermissionsRead, dashboards.ActionDashboardsPermissionsWrite}...)
func getDashboardViewActions(features featuremgmt.FeatureToggles) []string {
if features.IsEnabled(context.Background(), featuremgmt.FlagAnnotationPermissionUpdate) {
return append(DashboardViewActions, accesscontrol.ActionAnnotationsRead)
}
return DashboardViewActions
}
func getDashboardEditActions(features featuremgmt.FeatureToggles) []string {
if features.IsEnabled(context.Background(), featuremgmt.FlagAnnotationPermissionUpdate) {
return append(DashboardEditActions, []string{accesscontrol.ActionAnnotationsRead, accesscontrol.ActionAnnotationsWrite, accesscontrol.ActionAnnotationsDelete, accesscontrol.ActionAnnotationsCreate}...)
}
return DashboardEditActions
}
func getDashboardAdminActions(features featuremgmt.FeatureToggles) []string {
if features.IsEnabled(context.Background(), featuremgmt.FlagAnnotationPermissionUpdate) {
return append(DashboardAdminActions, []string{accesscontrol.ActionAnnotationsRead, accesscontrol.ActionAnnotationsWrite, accesscontrol.ActionAnnotationsDelete, accesscontrol.ActionAnnotationsCreate}...)
}
return DashboardAdminActions
}
func ProvideDashboardPermissions(
cfg *setting.Cfg, features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB, ac accesscontrol.AccessControl,
license licensing.Licensing, dashboardStore dashboards.Store, folderService folder.Service, service accesscontrol.Service,
@ -177,9 +198,9 @@ func ProvideDashboardPermissions(
ServiceAccounts: true,
},
PermissionsToActions: map[string][]string{
"View": DashboardViewActions,
"Edit": DashboardEditActions,
"Admin": DashboardAdminActions,
"View": getDashboardViewActions(features),
"Edit": getDashboardEditActions(features),
"Admin": getDashboardAdminActions(features),
},
ReaderRoleName: "Dashboard permission reader",
WriterRoleName: "Dashboard permission writer",
@ -242,9 +263,9 @@ func ProvideFolderPermissions(
ServiceAccounts: true,
},
PermissionsToActions: map[string][]string{
"View": append(DashboardViewActions, FolderViewActions...),
"Edit": append(DashboardEditActions, FolderEditActions...),
"Admin": append(DashboardAdminActions, FolderAdminActions...),
"View": append(getDashboardViewActions(features), FolderViewActions...),
"Edit": append(getDashboardEditActions(features), FolderEditActions...),
"Admin": append(getDashboardAdminActions(features), FolderAdminActions...),
},
ReaderRoleName: "Folder permission reader",
WriterRoleName: "Folder permission writer",

View File

@ -661,6 +661,171 @@ func (m *managedFolderLibraryPanelActionsMigrator) Exec(sess *xorm.Session, mg *
return nil
}
const ManagedDashboardAnnotationActionsMigratorID = "managed dashboard permissions annotation actions migration"
func AddManagedDashboardAnnotationActionsMigration(mg *migrator.Migrator) {
mg.AddMigration(ManagedDashboardAnnotationActionsMigratorID, &managedDashboardAnnotationActionsMigrator{})
}
type managedDashboardAnnotationActionsMigrator struct {
migrator.MigrationBase
}
func (m *managedDashboardAnnotationActionsMigrator) SQL(dialect migrator.Dialect) string {
return CodeMigrationSQL
}
func (m *managedDashboardAnnotationActionsMigrator) Exec(sess *xorm.Session, mg *migrator.Migrator) error {
// Check if roles have been populated and return early if they haven't - this avoids logging a warning from hasDefaultAnnotationPermissions
roleCount := 0
_, err := sess.SQL(`SELECT COUNT( DISTINCT r.uid ) FROM role AS r INNER JOIN permission AS p ON r.id = p.role_id WHERE r.uid IN (?, ?, ?)`, "basic_viewer", "basic_editor", "basic_admin").Get(&roleCount)
if err != nil {
return fmt.Errorf("failed to check if basic roles have been populated: %w", err)
}
// Role count will be 0 either for new Grafana installations (in that case no managed roles will exist either, and the next conditional will return nil)
// or for OSS instances, for which basic role permissions can't be changed, so we don't need to run the default permission check in that case.
if roleCount != 0 {
// Check that default annotation permissions are assigned to basic roles. If that is not the case, skip the migration.
if hasDefaultPerms, err := m.hasDefaultAnnotationPermissions(sess, mg); err != nil || !hasDefaultPerms {
return err
}
}
var ids []any
if err := sess.SQL("SELECT id FROM role WHERE name LIKE 'managed:%'").Find(&ids); err != nil {
return err
}
if len(ids) == 0 {
return nil
}
var permissions []ac.Permission
roleQueryBatchSize := 100
err = batch(len(ids), roleQueryBatchSize, func(start, end int) error {
var batchPermissions []ac.Permission
if err := sess.SQL("SELECT role_id, action, scope FROM permission WHERE role_id IN(?"+strings.Repeat(" ,?", len(ids[start:end])-1)+") AND (scope LIKE 'folders:%' or scope LIKE 'dashboards:%')", ids[start:end]...).Find(&batchPermissions); err != nil {
return err
}
permissions = append(permissions, batchPermissions...)
return nil
})
if err != nil {
return err
}
mapped := make(map[int64]map[string]map[string]bool, len(ids)-1)
for _, p := range permissions {
if mapped[p.RoleID] == nil {
mapped[p.RoleID] = make(map[string]map[string]bool)
}
if mapped[p.RoleID][p.Scope] == nil {
mapped[p.RoleID][p.Scope] = make(map[string]bool)
}
mapped[p.RoleID][p.Scope][p.Action] = true
}
var toAdd []ac.Permission
now := time.Now()
for roleId, mappedPermissions := range mapped {
for scope, roleActions := range mappedPermissions {
if roleActions[dashboards.ActionDashboardsRead] {
if !roleActions[ac.ActionAnnotationsRead] {
toAdd = append(toAdd, ac.Permission{
RoleID: roleId,
Updated: now,
Created: now,
Scope: scope,
Action: ac.ActionAnnotationsRead,
})
}
}
if roleActions[dashboards.ActionDashboardsWrite] {
if !roleActions[ac.ActionAnnotationsCreate] {
toAdd = append(toAdd, ac.Permission{
RoleID: roleId,
Updated: now,
Created: now,
Scope: scope,
Action: ac.ActionAnnotationsCreate,
})
}
if !roleActions[ac.ActionAnnotationsDelete] {
toAdd = append(toAdd, ac.Permission{
RoleID: roleId,
Updated: now,
Created: now,
Scope: scope,
Action: ac.ActionAnnotationsDelete,
})
}
if !roleActions[ac.ActionAnnotationsWrite] {
toAdd = append(toAdd, ac.Permission{
RoleID: roleId,
Updated: now,
Created: now,
Scope: scope,
Action: ac.ActionAnnotationsWrite,
})
}
}
}
}
if len(toAdd) == 0 {
return nil
}
return batch(len(toAdd), batchSize, func(start, end int) error {
_, err := sess.InsertMulti(toAdd[start:end])
return err
})
}
func (m *managedDashboardAnnotationActionsMigrator) hasDefaultAnnotationPermissions(sess *xorm.Session, mg *migrator.Migrator) (bool, error) {
type basicRolePermission struct {
Uid string
Action string
Scope string
}
var basicRolePermissions []basicRolePermission
basicRoleUIDs := []any{"basic_viewer", "basic_editor", "basic_admin"}
query := `SELECT r.uid, p.action, p.scope FROM role r
LEFT OUTER JOIN permission p ON p.role_id = r.id
WHERE r.uid IN (?, ?, ?) AND p.action LIKE 'annotations:%' AND p.scope IN ('*', 'annotations:*', 'annotations:type:*', 'annotations:type:dashboard')
`
if err := sess.SQL(query, basicRoleUIDs...).Find(&basicRolePermissions); err != nil {
return false, fmt.Errorf("failed to list basic role permissions: %w", err)
}
mappedBasicRolePerms := make(map[string]map[string]bool, 0)
for _, p := range basicRolePermissions {
if mappedBasicRolePerms[p.Uid] == nil {
mappedBasicRolePerms[p.Uid] = make(map[string]bool)
}
mappedBasicRolePerms[p.Uid][p.Action] = true
}
expectedAnnotationActions := []string{ac.ActionAnnotationsRead, ac.ActionAnnotationsCreate, ac.ActionAnnotationsDelete, ac.ActionAnnotationsWrite}
for _, uid := range basicRoleUIDs {
if mappedBasicRolePerms[uid.(string)] == nil {
mg.Logger.Warn("basic role permissions missing annotation permissions, skipping annotation permission migration", "uid", uid)
return false, nil
}
for _, action := range expectedAnnotationActions {
if !mappedBasicRolePerms[uid.(string)][action] {
mg.Logger.Warn("basic role permissions missing annotation permissions, skipping annotation permission migration", "uid", uid, "action", action)
return false, nil
}
}
}
return true, nil
}
func hasFolderAdmin(permissions []ac.Permission) bool {
return hasActions(folderPermissionTranslation[dashboardaccess.PERMISSION_ADMIN], permissions)
}

View File

@ -262,6 +262,9 @@ func setupTestDB(t *testing.T) *xorm.Engine {
mg := migrator.NewMigrator(x, &setting.Cfg{
Logger: log.New("acmigration.test"),
Raw: ini.Empty(),
IsFeatureToggleEnabled: func(key string) bool {
return true
},
})
migrations := &migrations.OSSMigrations{}
migrations.AddMigration(mg)

View File

@ -0,0 +1,335 @@
package test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboards"
acmig "github.com/grafana/grafana/pkg/services/sqlstore/migrations/accesscontrol"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/setting"
)
type testCase struct {
desc string
putRolePerms map[int64]map[string][]rawPermission
wantRolePerms map[int64]map[string][]rawPermission
}
func testCases() []testCase {
allAnnotationPermissions := []rawPermission{
{Action: accesscontrol.ActionAnnotationsRead, Scope: accesscontrol.ScopeAnnotationsTypeDashboard},
{Action: accesscontrol.ActionAnnotationsCreate, Scope: accesscontrol.ScopeAnnotationsTypeDashboard},
{Action: accesscontrol.ActionAnnotationsDelete, Scope: accesscontrol.ScopeAnnotationsTypeDashboard},
{Action: accesscontrol.ActionAnnotationsWrite, Scope: accesscontrol.ScopeAnnotationsTypeDashboard},
}
onlyOrgAnnotations := []rawPermission{
{Action: accesscontrol.ActionAnnotationsRead, Scope: accesscontrol.ScopeAnnotationsTypeOrganization},
{Action: accesscontrol.ActionAnnotationsCreate, Scope: accesscontrol.ScopeAnnotationsTypeOrganization},
{Action: accesscontrol.ActionAnnotationsDelete, Scope: accesscontrol.ScopeAnnotationsTypeOrganization},
{Action: accesscontrol.ActionAnnotationsWrite, Scope: accesscontrol.ScopeAnnotationsTypeOrganization},
}
wildcardAnnotationPermissions := []rawPermission{
{Action: accesscontrol.ActionAnnotationsRead, Scope: "*"},
{Action: accesscontrol.ActionAnnotationsCreate, Scope: "annotations:*"},
{Action: accesscontrol.ActionAnnotationsDelete, Scope: "annotations:type:*"},
{Action: accesscontrol.ActionAnnotationsWrite, Scope: accesscontrol.ScopeAnnotationsAll},
}
return []testCase{
{
desc: "empty permissions lead to empty permissions",
putRolePerms: map[int64]map[string][]rawPermission{},
wantRolePerms: map[int64]map[string][]rawPermission{},
},
{
desc: "adds new permissions for instances without basic roles (should only be OSS instances)",
putRolePerms: map[int64]map[string][]rawPermission{
1: {
"managed:users:1:permissions": {{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test"}},
},
},
wantRolePerms: map[int64]map[string][]rawPermission{
1: {
"managed:users:1:permissions": {
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test"},
{Action: accesscontrol.ActionAnnotationsRead, Scope: "dashboards:uid:test"},
},
},
},
},
{
desc: "doesn't add any new permissions if has default annotation permissions on basic roles but no dashboard or folder permissions",
putRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": allAnnotationPermissions,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
},
},
wantRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": allAnnotationPermissions,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
},
},
},
{
desc: "adds new permissions if has default annotation permissions on basic roles and dashboard read permissions",
putRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": allAnnotationPermissions,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
"managed:users:1:permissions": {{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test"}},
},
},
wantRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": allAnnotationPermissions,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
"managed:users:1:permissions": {
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test"},
{Action: accesscontrol.ActionAnnotationsRead, Scope: "dashboards:uid:test"},
},
},
},
},
{
desc: "adds new permissions if has default annotation permissions on basic roles and dashboard write permissions",
putRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": allAnnotationPermissions,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
"managed:users:1:permissions": {
{Action: dashboards.ActionDashboardsWrite, Scope: "dashboards:uid:test"},
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test"},
},
},
},
wantRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": allAnnotationPermissions,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
"managed:users:1:permissions": {
{Action: dashboards.ActionDashboardsWrite, Scope: "dashboards:uid:test"},
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test"},
{Action: accesscontrol.ActionAnnotationsRead, Scope: "dashboards:uid:test"},
{Action: accesscontrol.ActionAnnotationsWrite, Scope: "dashboards:uid:test"},
{Action: accesscontrol.ActionAnnotationsDelete, Scope: "dashboards:uid:test"},
{Action: accesscontrol.ActionAnnotationsCreate, Scope: "dashboards:uid:test"},
},
},
},
},
{
desc: "adds new permissions if has default annotation permissions on basic roles and folder read permissions",
putRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": allAnnotationPermissions,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
"managed:users:1:permissions": {{Action: dashboards.ActionDashboardsRead, Scope: "folders:uid:test"}},
},
},
wantRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": allAnnotationPermissions,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
"managed:users:1:permissions": {
{Action: dashboards.ActionDashboardsRead, Scope: "folders:uid:test"},
{Action: accesscontrol.ActionAnnotationsRead, Scope: "folders:uid:test"},
},
},
},
},
{
desc: "adds new permissions if has default annotation permissions on basic roles and folder write permissions",
putRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": allAnnotationPermissions,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
"managed:users:1:permissions": {
{Action: dashboards.ActionDashboardsWrite, Scope: "folders:uid:test"},
{Action: dashboards.ActionDashboardsRead, Scope: "folders:uid:test"},
},
},
},
wantRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": allAnnotationPermissions,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
"managed:users:1:permissions": {
{Action: dashboards.ActionDashboardsWrite, Scope: "folders:uid:test"},
{Action: dashboards.ActionDashboardsRead, Scope: "folders:uid:test"},
{Action: accesscontrol.ActionAnnotationsRead, Scope: "folders:uid:test"},
{Action: accesscontrol.ActionAnnotationsWrite, Scope: "folders:uid:test"},
{Action: accesscontrol.ActionAnnotationsDelete, Scope: "folders:uid:test"},
{Action: accesscontrol.ActionAnnotationsCreate, Scope: "folders:uid:test"},
},
},
},
},
{
desc: "adds new permissions to several managed roles if has default annotation permissions on basic roles and dashboard read permissions",
putRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": allAnnotationPermissions,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
"managed:users:1:permissions": {{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test"}},
"managed:teams:1:permissions": {{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test2"}},
},
},
wantRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": allAnnotationPermissions,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
"managed:users:1:permissions": {
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test"},
{Action: accesscontrol.ActionAnnotationsRead, Scope: "dashboards:uid:test"},
},
"managed:teams:1:permissions": {
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test2"},
{Action: accesscontrol.ActionAnnotationsRead, Scope: "dashboards:uid:test2"},
},
},
},
},
{
desc: "doesn't add any new permissions if annotation permissions are missing from the basic roles",
putRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
"managed:users:1:permissions": {
{Action: dashboards.ActionDashboardsWrite, Scope: "dashboards:uid:test"},
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test"},
},
},
},
wantRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
},
},
},
{
desc: "doesn't add any new permissions if annotation permissions from the basic roles don't have the dashboard scope",
putRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": onlyOrgAnnotations,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
"managed:users:1:permissions": {
{Action: dashboards.ActionDashboardsWrite, Scope: "dashboards:uid:test"},
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test"},
},
},
},
wantRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": onlyOrgAnnotations,
"basic:editor": allAnnotationPermissions,
"basic:admin": allAnnotationPermissions,
},
},
},
{
desc: "adds new permissions if has default annotation permissions with different wildcard scopes",
putRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": wildcardAnnotationPermissions,
"basic:editor": wildcardAnnotationPermissions,
"basic:admin": wildcardAnnotationPermissions,
"managed:users:1:permissions": {{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test"}},
},
},
wantRolePerms: map[int64]map[string][]rawPermission{
1: {
"basic:viewer": wildcardAnnotationPermissions,
"basic:editor": wildcardAnnotationPermissions,
"basic:admin": wildcardAnnotationPermissions,
"managed:users:1:permissions": {
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:test"},
{Action: accesscontrol.ActionAnnotationsRead, Scope: "dashboards:uid:test"},
},
},
},
},
}
}
func TestAnnotationActionMigration(t *testing.T) {
// Run initial migration to have a working DB
x := setupTestDB(t)
for _, tc := range testCases() {
t.Run(tc.desc, func(t *testing.T) {
// Remove migration
_, errDeleteMig := x.Exec(`DELETE FROM migration_log WHERE migration_id LIKE ?`, acmig.ManagedDashboardAnnotationActionsMigratorID)
require.NoError(t, errDeleteMig)
_, errDeletePerm := x.Exec(`DELETE FROM permission`)
require.NoError(t, errDeletePerm)
_, errDeleteRole := x.Exec(`DELETE FROM role`)
require.NoError(t, errDeleteRole)
// Test running the migrations twice to make sure they don't conflict
for i := 0; i < 2; i++ {
if i == 0 {
// put permissions
putTestPermissions(t, x, tc.putRolePerms)
}
// Run accesscontrol migration (permissions insertion should not have conflicted)
acmigrator := migrator.NewMigrator(x, &setting.Cfg{Logger: log.New("acmigration.test")})
acmig.AddManagedDashboardAnnotationActionsMigration(acmigrator)
errRunningMig := acmigrator.Start(false, 0)
require.NoError(t, errRunningMig)
// verify got == want
for orgID, roles := range tc.wantRolePerms {
for roleName := range roles {
// Check managed roles exist
role := accesscontrol.Role{}
hasRole, errRoleSearch := x.Table("role").Where("org_id = ? AND name = ?", orgID, roleName).Get(&role)
require.NoError(t, errRoleSearch)
require.True(t, hasRole, "expected role to exist", "orgID", orgID, "role", roleName)
// Check permissions associated with each role
perms := []accesscontrol.Permission{}
count, errManagedPermsSearch := x.Table("permission").Where("role_id = ?", role.ID).FindAndCount(&perms)
require.NoError(t, errManagedPermsSearch)
require.Equal(t, int64(len(tc.wantRolePerms[orgID][roleName])), count, "expected role to be tied to permissions", "orgID", orgID, "role", roleName)
gotRawPerms := convertToRawPermissions(perms)
require.ElementsMatch(t, gotRawPerms, tc.wantRolePerms[orgID][roleName], "expected role to have permissions", "orgID", orgID, "role", roleName)
// Check assignment of the roles
br := accesscontrol.BuiltinRole{}
has, errAssignmentSearch := x.Table("builtin_role").Where("role_id = ? AND role = ? AND org_id = ?", role.ID, acmig.ParseRoleFromName(roleName), orgID).Get(&br)
require.NoError(t, errAssignmentSearch)
require.True(t, has, "expected assignment of role to builtin role", "orgID", orgID, "role", roleName)
}
}
}
})
}
}

View File

@ -2,7 +2,6 @@ package test
import (
"fmt"
"strconv"
"strings"
"testing"
@ -256,7 +255,10 @@ func TestManagedPermissionsMigrationRunTwice(t *testing.T) {
func putTestPermissions(t *testing.T, x *xorm.Engine, rolePerms map[int64]map[string][]rawPermission) {
for orgID, roles := range rolePerms {
for roleName, perms := range roles {
uid := strconv.FormatInt(orgID, 10) + strings.ReplaceAll(roleName, ":", "_")
uid := strings.ReplaceAll(roleName, ":", "_")
if !strings.HasPrefix(roleName, "basic") {
uid = fmt.Sprintf("%d_%s", orgID, uid)
}
role := accesscontrol.Role{
OrgID: orgID,
Version: 1,

View File

@ -114,6 +114,15 @@ func (*OSSMigrations) AddMigration(mg *Migrator) {
ualert.CreateOrgMigratedKVStoreEntries(mg)
// https://github.com/grafana/identity-access-team/issues/546: tracks removal of the feature toggle from the annotation permission migration
// nolint:staticcheck
if mg.Cfg != nil && mg.Cfg.IsFeatureToggleEnabled != nil {
// nolint:staticcheck
if mg.Cfg.IsFeatureToggleEnabled(featuremgmt.FlagAnnotationPermissionUpdate) {
accesscontrol.AddManagedDashboardAnnotationActionsMigration(mg)
}
}
addKVStoreMySQLValueTypeLongTextMigration(mg)
}