AccessControl: Add migration for seeding managed inherited permissions (#49337)
* AccessControl: Add migration for seeding managed inherited permissions Co-authored-by: Karl Persson <kalle.persson@grafana.com> * AccessControl: move to single file * AccessControl: Add tests for managed permission migration Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> * AccessControl: Ensure no duplicate insertion Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> * Remove commented code Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> * Add code migration constant Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> * Ensure DB is clean between tests Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> * Update pkg/services/sqlstore/migrations/accesscontrol/managed_permission_migrator.go Co-authored-by: Karl Persson <kalle.persson@grafana.com> Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
This commit is contained in:
co-authored by
Karl Persson
Gabriel MABILLE
parent
18727f0bf5
commit
3250bf6b2b
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrations"
|
||||
@@ -22,6 +23,16 @@ type rawPermission struct {
|
||||
Action, Scope string
|
||||
}
|
||||
|
||||
func (rp *rawPermission) toPermission(roleID int64, ts time.Time) accesscontrol.Permission {
|
||||
return accesscontrol.Permission{
|
||||
RoleID: roleID,
|
||||
Action: rp.Action,
|
||||
Scope: rp.Scope,
|
||||
Updated: ts,
|
||||
Created: ts,
|
||||
}
|
||||
}
|
||||
|
||||
// Setup users
|
||||
var (
|
||||
now = time.Now()
|
||||
@@ -209,9 +220,7 @@ func setupTestDB(t *testing.T) *xorm.Engine {
|
||||
err = migrator.NewDialect(x).CleanDB()
|
||||
require.NoError(t, err)
|
||||
|
||||
mg := migrator.NewMigrator(x, &setting.Cfg{
|
||||
IsFeatureToggleEnabled: func(key string) bool { return key == "accesscontrol" },
|
||||
})
|
||||
mg := migrator.NewMigrator(x, &setting.Cfg{Logger: log.New("acmigration.test")})
|
||||
migrations := &migrations.OSSMigrations{}
|
||||
migrations.AddMigration(mg)
|
||||
|
||||
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
acmig "github.com/grafana/grafana/pkg/services/sqlstore/migrations/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/require"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func TestManagedPermissionsMigration(t *testing.T) {
|
||||
// Run initial migration to have a working DB
|
||||
x := setupTestDB(t)
|
||||
|
||||
team1Scope := accesscontrol.Scope("teams", "id", "1")
|
||||
team2Scope := accesscontrol.Scope("teams", "id", "2")
|
||||
|
||||
type teamMigrationTestCase struct {
|
||||
desc string
|
||||
putRolePerms map[int64]map[string][]rawPermission
|
||||
wantRolePerms map[int64]map[string][]rawPermission
|
||||
}
|
||||
testCases := []teamMigrationTestCase{
|
||||
{
|
||||
desc: "empty perms",
|
||||
putRolePerms: map[int64]map[string][]rawPermission{},
|
||||
wantRolePerms: map[int64]map[string][]rawPermission{},
|
||||
},
|
||||
{
|
||||
desc: "only unrelated perms",
|
||||
putRolePerms: map[int64]map[string][]rawPermission{
|
||||
1: {
|
||||
"managed:users:1:permissions": {{Action: "teams:read", Scope: team1Scope}},
|
||||
},
|
||||
},
|
||||
wantRolePerms: map[int64]map[string][]rawPermission{
|
||||
1: {
|
||||
"managed:users:1:permissions": {{Action: "teams:read", Scope: team1Scope}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "inherit permissions from managed role",
|
||||
putRolePerms: map[int64]map[string][]rawPermission{
|
||||
1: {
|
||||
"managed:builtins:viewer:permissions": {
|
||||
{Action: "teams.permissions:read", Scope: team1Scope},
|
||||
{Action: "teams.permissions:write", Scope: team2Scope},
|
||||
},
|
||||
"managed:builtins:editor:permissions": {
|
||||
{Action: "teams:delete", Scope: team1Scope},
|
||||
},
|
||||
},
|
||||
2: {
|
||||
"managed:users:1:permissions": {{Action: "teams:read", Scope: team1Scope}},
|
||||
"managed:builtins:viewer:permissions": {
|
||||
{Action: "teams:delete", Scope: team1Scope},
|
||||
{Action: "teams.permissions:read", Scope: team1Scope},
|
||||
{Action: "teams.permissions:write", Scope: team2Scope},
|
||||
},
|
||||
"managed:builtins:editor:permissions": {
|
||||
{Action: "teams.permissions:read", Scope: team1Scope},
|
||||
{Action: "teams.permissions:write", Scope: team2Scope},
|
||||
},
|
||||
"managed:builtins:admin:permissions": {
|
||||
{Action: "teams.permissions:read", Scope: team1Scope},
|
||||
{Action: "teams.permissions:write", Scope: team2Scope},
|
||||
{Action: "teams:write", Scope: team1Scope},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantRolePerms: map[int64]map[string][]rawPermission{
|
||||
1: {
|
||||
"managed:builtins:viewer:permissions": {
|
||||
{Action: "teams.permissions:read", Scope: team1Scope},
|
||||
{Action: "teams.permissions:write", Scope: team2Scope},
|
||||
},
|
||||
"managed:builtins:editor:permissions": {
|
||||
{Action: "teams:delete", Scope: team1Scope},
|
||||
{Action: "teams.permissions:read", Scope: team1Scope},
|
||||
{Action: "teams.permissions:write", Scope: team2Scope},
|
||||
},
|
||||
"managed:builtins:admin:permissions": {
|
||||
{Action: "teams:delete", Scope: team1Scope},
|
||||
{Action: "teams.permissions:read", Scope: team1Scope},
|
||||
{Action: "teams.permissions:write", Scope: team2Scope},
|
||||
},
|
||||
},
|
||||
2: {
|
||||
"managed:users:1:permissions": {{Action: "teams:read", Scope: team1Scope}},
|
||||
"managed:builtins:viewer:permissions": {
|
||||
{Action: "teams:delete", Scope: team1Scope},
|
||||
{Action: "teams.permissions:read", Scope: team1Scope},
|
||||
{Action: "teams.permissions:write", Scope: team2Scope},
|
||||
},
|
||||
"managed:builtins:editor:permissions": {
|
||||
{Action: "teams:delete", Scope: team1Scope},
|
||||
{Action: "teams.permissions:read", Scope: team1Scope},
|
||||
{Action: "teams.permissions:write", Scope: team2Scope},
|
||||
},
|
||||
"managed:builtins:admin:permissions": {
|
||||
{Action: "teams:delete", Scope: team1Scope},
|
||||
{Action: "teams.permissions:read", Scope: team1Scope},
|
||||
{Action: "teams.permissions:write", Scope: team2Scope},
|
||||
{Action: "teams:write", Scope: team1Scope},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
// Remove migration
|
||||
_, errDeleteMig := x.Exec(`DELETE FROM migration_log WHERE migration_id = ?;
|
||||
DELETE FROM permission; DELETE FROM role`, acmig.ManagedPermissionsMigrationID)
|
||||
require.NoError(t, errDeleteMig)
|
||||
|
||||
// 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.AddManagedPermissionsMigration(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, errManagedRoleSearch := x.Table("role").Where("org_id = ? AND name = ?", orgID, roleName).Get(&role)
|
||||
|
||||
require.NoError(t, errManagedRoleSearch)
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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, ":", "_")
|
||||
role := accesscontrol.Role{
|
||||
OrgID: orgID,
|
||||
Version: 1,
|
||||
UID: uid,
|
||||
Name: roleName,
|
||||
Updated: now,
|
||||
Created: now,
|
||||
}
|
||||
roleCount, errInsertRole := x.Insert(&role)
|
||||
require.NoError(t, errInsertRole)
|
||||
require.Equal(t, int64(1), roleCount)
|
||||
|
||||
br := accesscontrol.BuiltinRole{
|
||||
RoleID: role.ID,
|
||||
OrgID: role.OrgID,
|
||||
Role: acmig.ParseRoleFromName(roleName),
|
||||
Updated: now,
|
||||
Created: now,
|
||||
}
|
||||
brCount, err := x.Insert(br)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), brCount)
|
||||
|
||||
permissions := []accesscontrol.Permission{}
|
||||
for _, p := range perms {
|
||||
permissions = append(permissions, p.toPermission(role.ID, now))
|
||||
}
|
||||
permissionsCount, err := x.Insert(permissions)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(len(perms)), permissionsCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user