diff --git a/pkg/services/accesscontrol/resourcepermissions/store.go b/pkg/services/accesscontrol/resourcepermissions/store.go index 371b4a80756..3c619fab5dc 100644 --- a/pkg/services/accesscontrol/resourcepermissions/store.go +++ b/pkg/services/accesscontrol/resourcepermissions/store.go @@ -39,11 +39,13 @@ type flatResourcePermission struct { } func (p *flatResourcePermission) IsManaged(scope string) bool { - return strings.HasPrefix(p.RoleName, accesscontrol.ManagedRolePrefix) && !p.IsInherited(scope) + return strings.HasPrefix(p.RoleName, accesscontrol.ManagedRolePrefix) && p.Scope == scope } +// IsInherited returns true for scopes from managed permissions that don't directly match the required scope +// (ie, managed permissions on a parent resource) func (p *flatResourcePermission) IsInherited(scope string) bool { - return !strings.HasPrefix(p.Scope, strings.Split(strings.ReplaceAll(scope, "*", ""), ":")[0]) + return strings.HasPrefix(p.RoleName, accesscontrol.ManagedRolePrefix) && p.Scope != scope } func (s *store) SetUserResourcePermission( @@ -412,10 +414,12 @@ func groupPermissionsByAssignment(permissions []flatResourcePermission) (map[int } func flatPermissionsToResourcePermissions(scope string, permissions []flatResourcePermission) []accesscontrol.ResourcePermission { - var managed, provisioned []flatResourcePermission + var managed, inherited, provisioned []flatResourcePermission for _, p := range permissions { if p.IsManaged(scope) { managed = append(managed, p) + } else if p.IsInherited(scope) { + inherited = append(inherited, p) } else { provisioned = append(provisioned, p) } @@ -425,6 +429,9 @@ func flatPermissionsToResourcePermissions(scope string, permissions []flatResour if g := flatPermissionsToResourcePermission(scope, managed); g != nil { result = append(result, *g) } + if g := flatPermissionsToResourcePermission(scope, inherited); g != nil { + result = append(result, *g) + } if g := flatPermissionsToResourcePermission(scope, provisioned); g != nil { result = append(result, *g) } diff --git a/pkg/services/accesscontrol/resourcepermissions/store_test.go b/pkg/services/accesscontrol/resourcepermissions/store_test.go index 0598e8c9190..b568e57a46b 100644 --- a/pkg/services/accesscontrol/resourcepermissions/store_test.go +++ b/pkg/services/accesscontrol/resourcepermissions/store_test.go @@ -11,6 +11,7 @@ import ( "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org/orgimpl" "github.com/grafana/grafana/pkg/services/quota/quotatest" @@ -520,3 +521,58 @@ func setupTestEnv(t testing.TB) (*store, *sqlstore.SQLStore) { sql := db.InitTestDB(t) return NewStore(sql), sql } + +func TestStore_IsInherited(t *testing.T) { + type testCase struct { + description string + permission *flatResourcePermission + requiredScope string + expected bool + } + + testCases := []testCase{ + { + description: "same scope is not inherited", + permission: &flatResourcePermission{ + Scope: dashboards.ScopeDashboardsProvider.GetResourceScopeUID("some_uid"), + RoleName: fmt.Sprintf("%stest_role", accesscontrol.ManagedRolePrefix), + }, + requiredScope: dashboards.ScopeDashboardsProvider.GetResourceScopeUID("some_uid"), + expected: false, + }, + { + description: "specific folder scope for dashboards is inherited", + permission: &flatResourcePermission{ + Scope: dashboards.ScopeFoldersProvider.GetResourceScopeUID("parent"), + RoleName: fmt.Sprintf("%stest_role", accesscontrol.ManagedRolePrefix), + }, + requiredScope: dashboards.ScopeDashboardsProvider.GetResourceScopeUID("some_uid"), + expected: true, + }, + { + description: "wildcard scope from a fixed role is not inherited", + permission: &flatResourcePermission{ + Scope: dashboards.ScopeDashboardsAll, + RoleName: fmt.Sprintf("%sfixed_role", accesscontrol.FixedRolePrefix), + }, + requiredScope: dashboards.ScopeDashboardsProvider.GetResourceScopeUID("some_uid"), + expected: false, + }, + { + description: "parent folder scope for nested folders is inherited", + permission: &flatResourcePermission{ + Scope: dashboards.ScopeFoldersProvider.GetResourceScopeUID("parent"), + RoleName: fmt.Sprintf("%stest_role", accesscontrol.ManagedRolePrefix), + }, + requiredScope: dashboards.ScopeFoldersProvider.GetResourceScopeUID("some_folder"), + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + isInherited := tc.permission.IsInherited(tc.requiredScope) + assert.Equal(t, tc.expected, isInherited) + }) + } +}