Files
grafana/pkg/registry/apis/dashboard/authorizer.go
T
2025-09-03 20:41:37 +00:00

131 lines
5.7 KiB
Go

package dashboard
import (
"context"
"k8s.io/apiserver/pkg/authorization/authorizer"
"github.com/grafana/authlib/types"
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/libraryelements"
)
func newLegacyAuthorizer(ac accesscontrol.AccessControl, l log.Logger) authorizer.Authorizer {
return authorizer.AuthorizerFunc(
func(ctx context.Context, attr authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
// Note that we will return Allow more than expected.
// This is because we do NOT want to hit the RoleAuthorizer that would be evaluated afterwards.
if !attr.IsResourceRequest() {
return authorizer.DecisionDeny, "unexpected non-resource request", nil
}
user, err := identity.GetRequester(ctx)
if err != nil {
return authorizer.DecisionDeny, "error getting requester", err
}
ns := attr.GetNamespace()
if ns == "" {
return authorizer.DecisionDeny, "expected namespace", nil
}
info, err := types.ParseNamespace(attr.GetNamespace())
if err != nil {
return authorizer.DecisionDeny, "error reading org from namespace", err
}
// Validate organization access before we possibly step out here.
if user.GetOrgID() != info.OrgID {
return authorizer.DecisionDeny, "org mismatch", dashboards.ErrUserIsNotSignedInToOrg
}
// Determine if this is a library panel or dashboard resource
resource := attr.GetResource()
isLibraryPanel := resource == dashv0.LIBRARY_PANEL_RESOURCE
if isLibraryPanel {
return authorizeLibraryPanel(ctx, ac, user, attr)
} else {
return authorizeDashboard(ctx, ac, user, attr)
}
})
}
func authorizeLibraryPanel(ctx context.Context, ac accesscontrol.AccessControl, user identity.Requester, attr authorizer.Attributes) (authorizer.Decision, string, error) {
switch attr.GetVerb() {
case "list", "search":
// Detailed read permissions are handled by authz, this just checks whether the user can ready *any* library panel
ok, err := ac.Evaluate(ctx, user, accesscontrol.EvalPermission(libraryelements.ActionLibraryPanelsRead))
if !ok || err != nil {
return authorizer.DecisionDeny, "can not read any library panels", err
}
case "create":
// TODO: uncomment this when we implement create :)
//
// Detailed create permissions are handled by authz, this just checks whether the user can create *any* library panel
// ok, err := ac.Evaluate(ctx, user, accesscontrol.EvalPermission(libraryelements.ActionLibraryPanelsCreate))
// if !ok || err != nil {
// return authorizer.DecisionDeny, "can not create any library panels", err
// }
return authorizer.DecisionDeny, "can not create any library panels", nil
case "get":
ok, err := ac.Evaluate(ctx, user, accesscontrol.EvalPermission(libraryelements.ActionLibraryPanelsRead, libraryelements.ScopeLibraryPanelsProvider.GetResourceScopeUID(attr.GetName())))
if !ok || err != nil {
return authorizer.DecisionDeny, "can not view library panel", err
}
case "update", "patch":
ok, err := ac.Evaluate(ctx, user, accesscontrol.EvalPermission(libraryelements.ActionLibraryPanelsWrite, libraryelements.ScopeLibraryPanelsProvider.GetResourceScopeUID(attr.GetName())))
if !ok || err != nil {
return authorizer.DecisionDeny, "can not edit library panel", err
}
case "delete":
ok, err := ac.Evaluate(ctx, user, accesscontrol.EvalPermission(libraryelements.ActionLibraryPanelsDelete, libraryelements.ScopeLibraryPanelsProvider.GetResourceScopeUID(attr.GetName())))
if !ok || err != nil {
return authorizer.DecisionDeny, "can not delete library panel", err
}
default:
return authorizer.DecisionDeny, "unsupported verb for library panels", nil
}
return authorizer.DecisionAllow, "", nil
}
func authorizeDashboard(ctx context.Context, ac accesscontrol.AccessControl, user identity.Requester, attr authorizer.Attributes) (authorizer.Decision, string, error) {
switch attr.GetVerb() {
case "list", "search":
// Detailed read permissions are handled by authz, this just checks whether the user can ready *any* dashboard
ok, err := ac.Evaluate(ctx, user, accesscontrol.EvalPermission(dashboards.ActionDashboardsRead))
if !ok || err != nil {
return authorizer.DecisionDeny, "can not read any dashboards", err
}
case "create":
// Detailed create permissions are handled by authz, this just checks whether the user can create *any* dashboard
ok, err := ac.Evaluate(ctx, user, accesscontrol.EvalPermission(dashboards.ActionDashboardsCreate))
if !ok || err != nil {
return authorizer.DecisionDeny, "can not create any dashboards", err
}
case "get":
ok, err := ac.Evaluate(ctx, user, accesscontrol.EvalPermission(dashboards.ActionDashboardsRead, dashboards.ScopeDashboardsProvider.GetResourceScopeUID(attr.GetName())))
if !ok || err != nil {
return authorizer.DecisionDeny, "can not view dashboard", err
}
case "update", "patch":
ok, err := ac.Evaluate(ctx, user, accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite, dashboards.ScopeDashboardsProvider.GetResourceScopeUID(attr.GetName())))
if !ok || err != nil {
return authorizer.DecisionDeny, "can not edit dashboard", err
}
case "delete":
ok, err := ac.Evaluate(ctx, user, accesscontrol.EvalPermission(dashboards.ActionDashboardsDelete, dashboards.ScopeDashboardsProvider.GetResourceScopeUID(attr.GetName())))
if !ok || err != nil {
return authorizer.DecisionDeny, "can not delete dashboard", err
}
default:
return authorizer.DecisionDeny, "unsupported verb for dashboards", nil
}
return authorizer.DecisionAllow, "", nil
}