Authorization bypass / Access control

HIGH
grafana/grafana
Commit: 6d05d927a7fd
Affected: < 12.4.0
2026-05-14 19:16 UTC

Description

The commit fixes an authorization check bug in K8s Dashboards folder permission verification. Previously the code checked the folder access subresource and interpreted CanEdit (mapping to folders:write) as the gate for creating dashboards. This excluded users who only had dashboards:create permission on the folder but lacked folders:write, effectively denying legitimate dashboard creation for those RBAC configurations. The patch replaces the folder subresource check with a direct dashboards:create permission check scoped to the target folder via the accessClient.Check mechanism. This aligns permission semantics across enterprise custom RBAC and OSS resource permission levels and ensures that dashboard creation is allowed when dashboards:create is granted, even if folders:write is not.

Proof of Concept

PoC outline (before vs after fix): Prerequisites: Grafana instance with K8s Dashboards feature enabled; a folder with UID FOLDER_UID; a user U granted dashboards:create on the folder but not folders:write. Pre-fix behavior (buggy path): - User U attempts to create a dashboard in FOLDER_UID via Grafana API. - The server checks the folder access subresource CanEdit (folders:write) and denies access if folders:write is not granted. - Result: HTTP 403 Forbidden, dashboard creation blocked despite dashboards:create being granted. Post-fix behavior (patched path): - User U attempts the same create operation. - The server performs a direct dashboards:create check scoped to the folder (instead of CanEdit on the folder subresource). - If dashboards:create is granted, the operation succeeds. - Result: HTTP 201 Created (or equivalent success response). Concrete illustrative curl (illustrative only): - Payload for creating a dashboard in a folder: curl -X POST https://grafana.example/api/dashboards -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{"dashboard":{"title":"Poc Test","uid":"poc-test-123","folderUid":"FOLDER_UID"}}' - Expected outcome: - If pre-fix: 403 Forbidden (due to CanEdit check failing). - If post-fix: 201 Created (assuming dashboards:create is granted). Notes: This PoC assumes the K8s Dashboards feature and RBAC configured to grant dashboards:create on the folder but not folders:write. The fix ensures permissions are evaluated against dashboards:create rather than the broader folders:write capability.

Commit Details

Author: Mihai Turdean

Date: 2026-05-14 18:55 UTC

Message:

K8s Dashboards: Fix folder permission check to use dashboards:create (#124612) * K8s Dashboards: Fix folder permission check to use dashboards:create The verifyFolderAccessPermissions method was checking CanEdit on the folder access subresource, which maps to folders:write. This meant enterprise users with custom RBAC roles granting only dashboards:create + dashboards:write on a folder were denied dashboard creation, because they lacked folders:write. Replace the folder access subresource fetch with a direct accessClient.Check() call for VerbCreate on the dashboards resource scoped to the target folder. This checks the semantically correct permission (dashboards:create) and works for both enterprise custom roles and OSS resource permission levels (Edit grants the folders:edit action set which includes dashboards:create). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * K8s Dashboards: Simplify verifyFolderAccessPermissions signature Both call sites pass a single folder UID, so the variadic parameter is unnecessary. Take a single folderID string instead. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Triage Assessment

Vulnerability Type: Authorization bypass / Access control

Confidence: HIGH

Reasoning:

The commit changes the permission check from relying on a folder subresource (CanEdit) to a direct dashboards:create check, ensuring enterprise custom RBAC and OSS permissions correctly gate dashboard creation. This addresses an authorization bypass where users could be denied creation despite having dashboards:create on the folder. It fixes access control logic rather than introducing new features.

Verification Assessment

Vulnerability Type: Authorization bypass / Access control

Confidence: HIGH

Affected Versions: < 12.4.0

Code Diff

diff --git a/pkg/registry/apis/dashboard/register.go b/pkg/registry/apis/dashboard/register.go index 3a9ea8a266df..144cc6772136 100644 --- a/pkg/registry/apis/dashboard/register.go +++ b/pkg/registry/apis/dashboard/register.go @@ -1102,34 +1102,24 @@ func (b *DashboardsAPIBuilder) GetAuthorizer() authorizer.Authorizer { }) } -func (b *DashboardsAPIBuilder) verifyFolderAccessPermissions(ctx context.Context, user identity.Requester, folderIds ...string) error { +func (b *DashboardsAPIBuilder) verifyFolderAccessPermissions(ctx context.Context, user identity.Requester, folderID string) error { ns, err := request.NamespaceInfoFrom(ctx, false) if err != nil { return err } - folderClient := b.folderClientProvider.GetOrCreateHandler(ns.Value) - - for _, folderId := range folderIds { - resp, err := folderClient.Get(ctx, folderId, ns.OrgID, metav1.GetOptions{}, "access") - if err != nil { - if apierrors.IsNotFound(err) { - return apierrors.NewNotFound(folders.FolderResourceInfo.GroupResource(), folderId) - } - if apierrors.IsForbidden(err) { - return apierrors.NewForbidden(folders.FolderResourceInfo.GroupResource(), folderId, folder.ErrAccessDenied) - } - return err - } - var accessInfo folders.FolderAccessInfo - err = runtime.DefaultUnstructuredConverter.FromUnstructured(resp.Object, &accessInfo) - if err != nil { - logging.FromContext(ctx).Error("Failed to convert folder access response", "error", err) - return err - } - if !accessInfo.CanEdit { - return apierrors.NewForbidden(folders.FolderResourceInfo.GroupResource(), folderId, folder.ErrAccessDenied) - } + gvr := dashv1.DashboardResourceInfo.GroupVersionResource() + resp, err := b.accessClient.Check(ctx, user, authlib.CheckRequest{ + Verb: utils.VerbCreate, + Group: gvr.Group, + Resource: gvr.Resource, + Namespace: ns.Value, + }, folderID) + if err != nil { + return err + } + if !resp.Allowed { + return apierrors.NewForbidden(folders.FolderResourceInfo.GroupResource(), folderID, folder.ErrAccessDenied) } return nil
← Back to Alerts View on GitHub →