From 69dc5a0b88e3317f692c7beb0a426724edba4d18 Mon Sep 17 00:00:00 2001 From: Gabriel MABILLE Date: Tue, 29 Jul 2025 21:11:06 +0200 Subject: [PATCH] `grafana-iam`: Add resolver for `permissions:type:delegate` (#108789) * `grafana-iam`: Add resolver for `permissions:type:delegate` * roles create -> write --- pkg/services/authz/rbac/mapper.go | 16 +++++++++++++++- pkg/services/authz/rbac/resolver.go | 12 ++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/pkg/services/authz/rbac/mapper.go b/pkg/services/authz/rbac/mapper.go index ded19301c83..28537ad638d 100644 --- a/pkg/services/authz/rbac/mapper.go +++ b/pkg/services/authz/rbac/mapper.go @@ -105,7 +105,21 @@ func NewMapperRegistry() MapperRegistry { // Teams is a special case. We translate user permissions from id to uid based. "teams": newResourceTranslation("teams", "uid", false), "coreroles": newResourceTranslation("roles", "uid", false), - "roles": newResourceTranslation("roles", "uid", false), + "roles": translation{ + resource: "roles", + attribute: "uid", + verbMapping: map[string]string{ + utils.VerbCreate: "roles:write", + utils.VerbGet: "roles:read", + utils.VerbUpdate: "roles:write", + utils.VerbPatch: "roles:write", + utils.VerbDelete: "roles:delete", + utils.VerbDeleteCollection: "roles:delete", + utils.VerbList: "roles:read", + utils.VerbWatch: "roles:read", + }, + folderSupport: false, + }, }, "secret.grafana.app": { "securevalues": newResourceTranslation("secret.securevalues", "uid", false), diff --git a/pkg/services/authz/rbac/resolver.go b/pkg/services/authz/rbac/resolver.go index f8978302c6c..0a774844c29 100644 --- a/pkg/services/authz/rbac/resolver.go +++ b/pkg/services/authz/rbac/resolver.go @@ -81,10 +81,22 @@ func (s *Service) newTeamNameResolver(ctx context.Context, ns types.NamespaceInf }, nil } +func permissionsDelegateResolverFunc(scope string) (string, error) { + if strings.TrimPrefix(scope, "permissions:type:") == "delegate" { + // The permissions:type:delegate scope does not have any discriminating value, + // so we return a wildcard to indicate that it applies to all roles. + return "*", nil + } + return "", fmt.Errorf("unsupported scope: %s", scope) +} + func (s *Service) nameResolver(ctx context.Context, ns types.NamespaceInfo, scopePrefix string) (ScopeResolverFunc, error) { if scopePrefix == "teams:id:" { return s.newTeamNameResolver(ctx, ns) } + if scopePrefix == "permissions:type:" { + return permissionsDelegateResolverFunc, nil + } // No resolver found for the given scope prefix. return nil, nil }