The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/registry/apis/folders/authorizer.go

106 lines
3.4 KiB

package folders
import (
"context"
"errors"
"slices"
"k8s.io/apiserver/pkg/authorization/authorizer"
"github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboards"
)
// newLegacyAuthorizer creates an authorizer using legacy access control, this is only usable for single tenant api.
func newLegacyAuthorizer(ac accesscontrol.AccessControl) authorizer.Authorizer {
return authorizer.AuthorizerFunc(func(ctx context.Context, attr authorizer.Attributes) (authorizer.Decision, string, error) {
in, err := authorizerFunc(ctx, attr)
if err != nil {
if errors.Is(err, errNoUser) {
return authorizer.DecisionDeny, "", nil
}
return authorizer.DecisionNoOpinion, "", nil
}
ok, err := ac.Evaluate(ctx, in.user, in.evaluator)
if ok {
return authorizer.DecisionAllow, "", nil
}
return authorizer.DecisionDeny, "folder", err
})
}
func authorizerFunc(ctx context.Context, attr authorizer.Attributes) (*authorizerParams, error) {
allowedVerbs := []string{utils.VerbCreate, utils.VerbDelete, utils.VerbList}
verb := attr.GetVerb()
name := attr.GetName()
if (!attr.IsResourceRequest()) || (name == "" && verb != utils.VerbCreate && slices.Contains(allowedVerbs, verb)) {
return nil, errNoResource
}
// require a user
user, err := identity.GetRequester(ctx)
if err != nil {
return nil, errNoUser
}
scope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(name)
var eval accesscontrol.Evaluator
// "get" is used for sub-resources with GET http (parents, access, count)
switch verb {
case utils.VerbCreate:
eval = accesscontrol.EvalPermission(dashboards.ActionFoldersCreate)
case utils.VerbPatch:
fallthrough
case utils.VerbUpdate:
eval = accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, scope)
case utils.VerbDeleteCollection:
fallthrough
case utils.VerbDelete:
eval = accesscontrol.EvalPermission(dashboards.ActionFoldersDelete, scope)
case utils.VerbList:
eval = accesscontrol.EvalPermission(dashboards.ActionFoldersRead)
default:
eval = accesscontrol.EvalPermission(dashboards.ActionFoldersRead, scope)
}
return &authorizerParams{evaluator: eval, user: user}, nil
}
// newMultiTenantAuthorizer creates an authorizer sutiable to multi-tenant setup.
// For now it only allow authorization of access tokens.
func newMultiTenantAuthorizer(ac types.AccessClient) authorizer.Authorizer {
return authorizer.AuthorizerFunc(func(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) {
info, ok := types.AuthInfoFrom(ctx)
if !ok {
return authorizer.DecisionDeny, "missing auth info", nil
}
// For now we only allow access policy to authorize with multi-tenant setup
if !types.IsIdentityType(info.GetIdentityType(), types.TypeAccessPolicy) {
return authorizer.DecisionDeny, "permission denied", nil
}
res, err := ac.Check(ctx, info, types.CheckRequest{
Verb: a.GetVerb(),
Group: a.GetAPIGroup(),
Resource: a.GetResource(),
Name: a.GetName(),
Namespace: a.GetNamespace(),
Subresource: a.GetSubresource(),
})
if err != nil {
return authorizer.DecisionDeny, "faild to perform authorization", err
}
if !res.Allowed {
return authorizer.DecisionDeny, "permission denied", nil
}
return authorizer.DecisionAllow, "", nil
})
}