mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Zanzana: Evaluate access with Check request (server-side) (#96213)
* Zanzana: Evaluate access with Check request (server-side) * Pass parent folder for checking access * Review suggestions * remove fixme comment
This commit is contained in:
parent
27a0491f30
commit
b1fb581ab1
@ -8,6 +8,8 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel"
|
||||||
|
|
||||||
|
"github.com/grafana/authlib/claims"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||||
@ -116,9 +118,36 @@ func (a *AccessControl) evaluateZanzana(ctx context.Context, user identity.Reque
|
|||||||
eval = evaluator
|
eval = evaluator
|
||||||
}
|
}
|
||||||
|
|
||||||
return eval.EvaluateCustom(func(action, scope string) (bool, error) {
|
return eval.EvaluateCustom(func(action string, scopes ...string) (bool, error) {
|
||||||
// FIXME: Implement using new schema / apis
|
// FIXME: handle action with no scopes
|
||||||
return false, nil
|
if len(scopes) == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceScope := scopes[0]
|
||||||
|
kind, _, identifier := accesscontrol.SplitScope(resourceScope)
|
||||||
|
|
||||||
|
// Parent folder always returned by scope resolver as a second value
|
||||||
|
var parentFolder string
|
||||||
|
if len(scopes) > 1 {
|
||||||
|
_, _, parentFolder = accesscontrol.SplitScope(scopes[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace := claims.OrgNamespaceFormatter(user.GetOrgID())
|
||||||
|
req, ok := zanzana.TranslateToCheckRequest(namespace, action, kind, parentFolder, identifier)
|
||||||
|
if !ok {
|
||||||
|
// unsupported translation
|
||||||
|
return false, errAccessNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
a.log.Debug("evaluating zanzana", "user", user.GetUID(), "namespace", req.Namespace, "verb", req.Verb, "resource", req.Resource, "name", req.Name)
|
||||||
|
res, err := a.zclient.Check(ctx, user, *req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Allowed, nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
var logger = log.New("accesscontrol.evaluator")
|
var logger = log.New("accesscontrol.evaluator")
|
||||||
|
|
||||||
type CheckerFn func(action string, scope string) (bool, error)
|
type CheckerFn func(action string, scopes ...string) (bool, error)
|
||||||
|
|
||||||
type Evaluator interface {
|
type Evaluator interface {
|
||||||
// Evaluate permissions that are grouped by action
|
// Evaluate permissions that are grouped by action
|
||||||
@ -89,18 +89,12 @@ func (p permissionEvaluator) EvaluateCustom(fn CheckerFn) (bool, error) {
|
|||||||
return fn(p.Action, "")
|
return fn(p.Action, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, target := range p.Scopes {
|
matches, err := fn(p.Action, p.Scopes...)
|
||||||
matches, err := fn(p.Action, target)
|
if err != nil {
|
||||||
if err != nil {
|
return false, err
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, nil
|
return matches, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p permissionEvaluator) MutateScopes(ctx context.Context, mutate ScopeAttributeMutator) (Evaluator, error) {
|
func (p permissionEvaluator) MutateScopes(ctx context.Context, mutate ScopeAttributeMutator) (Evaluator, error) {
|
||||||
|
@ -23,12 +23,19 @@ func GetTypeInfo(group, resource string) (TypeInfo, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var VerbMapping = map[string]string{
|
var VerbMapping = map[string]string{
|
||||||
utils.VerbGet: "read",
|
utils.VerbGet: RelationRead,
|
||||||
utils.VerbList: "read",
|
utils.VerbList: RelationRead,
|
||||||
utils.VerbWatch: "read",
|
utils.VerbWatch: RelationRead,
|
||||||
utils.VerbCreate: "create",
|
utils.VerbCreate: RelationCreate,
|
||||||
utils.VerbUpdate: "write",
|
utils.VerbUpdate: RelationWrite,
|
||||||
utils.VerbPatch: "write",
|
utils.VerbPatch: RelationWrite,
|
||||||
utils.VerbDelete: "delete",
|
utils.VerbDelete: RelationDelete,
|
||||||
utils.VerbDeleteCollection: "delete",
|
utils.VerbDeleteCollection: RelationDelete,
|
||||||
|
}
|
||||||
|
|
||||||
|
var RelationToVerbMapping = map[string]string{
|
||||||
|
RelationRead: utils.VerbGet,
|
||||||
|
RelationCreate: utils.VerbCreate,
|
||||||
|
RelationWrite: utils.VerbUpdate,
|
||||||
|
RelationDelete: utils.VerbDelete,
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
|
|
||||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||||
|
|
||||||
|
"github.com/grafana/authlib/authz"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/authz/zanzana/common"
|
"github.com/grafana/grafana/pkg/services/authz/zanzana/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -143,3 +145,32 @@ func MergeFolderResourceTuples(a, b *openfgav1.TupleKey) {
|
|||||||
vb := b.Condition.Context.Fields["group_resources"]
|
vb := b.Condition.Context.Fields["group_resources"]
|
||||||
va.GetListValue().Values = append(va.GetListValue().Values, vb.GetListValue().Values...)
|
va.GetListValue().Values = append(va.GetListValue().Values, vb.GetListValue().Values...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TranslateToCheckRequest(namespace, action, kind, folder, name string) (*authz.CheckRequest, bool) {
|
||||||
|
translation, ok := resourceTranslations[kind]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
m, ok := translation.mapping[action]
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
verb, ok := common.RelationToVerbMapping[m.relation]
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &authz.CheckRequest{
|
||||||
|
Namespace: namespace,
|
||||||
|
Verb: verb,
|
||||||
|
Group: translation.group,
|
||||||
|
Resource: translation.resource,
|
||||||
|
Name: name,
|
||||||
|
Folder: folder,
|
||||||
|
}
|
||||||
|
|
||||||
|
return req, true
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user