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 }