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:
Alexander Zobnin 2024-11-11 16:39:21 +01:00 committed by GitHub
parent 27a0491f30
commit b1fb581ab1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 83 additions and 22 deletions

View File

@ -8,6 +8,8 @@ import (
"github.com/prometheus/client_golang/prometheus"
"go.opentelemetry.io/otel"
"github.com/grafana/authlib/claims"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
@ -116,9 +118,36 @@ func (a *AccessControl) evaluateZanzana(ctx context.Context, user identity.Reque
eval = evaluator
}
return eval.EvaluateCustom(func(action, scope string) (bool, error) {
// FIXME: Implement using new schema / apis
return false, nil
return eval.EvaluateCustom(func(action string, scopes ...string) (bool, error) {
// FIXME: handle action with no scopes
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
})
}

View File

@ -11,7 +11,7 @@ import (
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 {
// Evaluate permissions that are grouped by action
@ -89,18 +89,12 @@ func (p permissionEvaluator) EvaluateCustom(fn CheckerFn) (bool, error) {
return fn(p.Action, "")
}
for _, target := range p.Scopes {
matches, err := fn(p.Action, target)
if err != nil {
return false, err
}
if matches {
return true, nil
}
matches, err := fn(p.Action, p.Scopes...)
if err != nil {
return false, err
}
return false, nil
return matches, nil
}
func (p permissionEvaluator) MutateScopes(ctx context.Context, mutate ScopeAttributeMutator) (Evaluator, error) {

View File

@ -23,12 +23,19 @@ func GetTypeInfo(group, resource string) (TypeInfo, bool) {
}
var VerbMapping = map[string]string{
utils.VerbGet: "read",
utils.VerbList: "read",
utils.VerbWatch: "read",
utils.VerbCreate: "create",
utils.VerbUpdate: "write",
utils.VerbPatch: "write",
utils.VerbDelete: "delete",
utils.VerbDeleteCollection: "delete",
utils.VerbGet: RelationRead,
utils.VerbList: RelationRead,
utils.VerbWatch: RelationRead,
utils.VerbCreate: RelationCreate,
utils.VerbUpdate: RelationWrite,
utils.VerbPatch: RelationWrite,
utils.VerbDelete: RelationDelete,
utils.VerbDeleteCollection: RelationDelete,
}
var RelationToVerbMapping = map[string]string{
RelationRead: utils.VerbGet,
RelationCreate: utils.VerbCreate,
RelationWrite: utils.VerbUpdate,
RelationDelete: utils.VerbDelete,
}

View File

@ -6,6 +6,8 @@ import (
openfgav1 "github.com/openfga/api/proto/openfga/v1"
"github.com/grafana/authlib/authz"
"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"]
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
}