Files
grafana/pkg/registry/apis/preferences/utils/authorizer.go
T

103 lines
2.9 KiB
Go

package utils
import (
"context"
"slices"
"k8s.io/apiserver/pkg/authorization/authorizer"
"github.com/grafana/authlib/authz"
"github.com/grafana/grafana-app-sdk/logging"
"github.com/grafana/grafana/pkg/apimachinery/identity"
)
type AuthorizeFromName struct {
Teams TeamService
OKNames []string
Resource map[string][]ResourceOwner // may include unknown
}
func (a *AuthorizeFromName) Authorize(ctx context.Context, attr authorizer.Attributes) (authorizer.Decision, string, error) {
user, err := identity.GetRequester(ctx)
if err != nil || user == nil {
return authorizer.DecisionDeny, "valid user is required", err
}
if !attr.IsResourceRequest() {
return authorizer.DecisionNoOpinion, "", nil
}
owners, ok := a.Resource[attr.GetResource()]
if !ok {
return authorizer.DecisionDeny, "missing resource name", nil
}
// Check if the request includes explicit permissions
res := authz.CheckServicePermissions(user, attr.GetAPIGroup(), attr.GetResource(), attr.GetVerb())
if !res.Allowed {
log := logging.FromContext(ctx)
log.Info("calling service lacks required permissions",
"isServiceCall", res.ServiceCall,
"apiGroup", attr.GetAPIGroup(),
"resource", attr.GetResource(),
"verb", attr.GetVerb(),
"permissions", len(res.Permissions),
)
return authorizer.DecisionDeny, "calling service lacks required permissions", nil
}
if attr.GetName() == "" {
if attr.IsReadOnly() {
return authorizer.DecisionAllow, "", nil
}
return authorizer.DecisionDeny, "mutating request without a name", nil
}
// the pseudo sub-resource
if a.OKNames != nil && slices.Contains(a.OKNames, attr.GetName()) {
return authorizer.DecisionAllow, "", nil
}
info, _ := ParseOwnerFromName(attr.GetName())
if !slices.Contains(owners, info.Owner) {
return authorizer.DecisionDeny, "unsupported owner type", nil
}
switch info.Owner {
case NamespaceResourceOwner:
if attr.IsReadOnly() {
// Everyone can see the namespace
return authorizer.DecisionAllow, "", nil
}
if user.GetOrgRole() == identity.RoleAdmin {
return authorizer.DecisionAllow, "", nil
}
return authorizer.DecisionDeny, "must be an org admin to edit", nil
case UserResourceOwner:
if user.GetIdentifier() == info.Identifier {
return authorizer.DecisionAllow, "", nil
}
return authorizer.DecisionDeny, "your are not the owner of the resource", nil
case TeamResourceOwner:
if a.Teams == nil {
return authorizer.DecisionDeny, "team checker not configured", err
}
ok, err := a.Teams.InTeam(ctx, user, info.Identifier, !attr.IsReadOnly())
if err != nil {
return authorizer.DecisionDeny, "error fetching teams", err
}
if ok {
return authorizer.DecisionAllow, "", nil
}
return authorizer.DecisionDeny, "you are not a member of the referenced team", nil
case UnknownResourceOwner:
return authorizer.DecisionAllow, "", nil
}
// the owner was not explicitly allowed
return authorizer.DecisionDeny, "", nil
}