Zanzana: fix generic schema (#95648)
* Change schema so that resource checks on a folder walks the tree
This commit is contained in:
@@ -110,7 +110,7 @@ func folderTreeCollector2(store db.DB) legacyTupleCollector {
|
||||
// managedPermissionsCollector collects managed permissions into provided tuple map.
|
||||
// It will only store actions that are supported by our schema. Managed permissions can
|
||||
// be directly mapped to user/team/role without having to write an intermediate role.
|
||||
func managedPermissionsCollector2(store db.DB) legacyTupleCollector {
|
||||
func managedPermissionsCollector2(store db.DB, kind string) legacyTupleCollector {
|
||||
return func(ctx context.Context) (map[string]map[string]*openfgav1.TupleKey, error) {
|
||||
query := `
|
||||
SELECT u.uid as user_uid, t.uid as team_uid, p.action, p.kind, p.identifier, r.org_id
|
||||
@@ -122,6 +122,7 @@ func managedPermissionsCollector2(store db.DB) legacyTupleCollector {
|
||||
LEFT JOIN team t ON tr.team_id = t.id
|
||||
LEFT JOIN builtin_role br ON r.id = br.role_id
|
||||
WHERE r.name LIKE 'managed:%'
|
||||
AND p.kind = ?
|
||||
`
|
||||
type Permission struct {
|
||||
RoleName string `xorm:"role_name"`
|
||||
@@ -135,7 +136,7 @@ func managedPermissionsCollector2(store db.DB) legacyTupleCollector {
|
||||
|
||||
var permissions []Permission
|
||||
err := store.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
return sess.SQL(query).Find(&permissions)
|
||||
return sess.SQL(query, kind).Find(&permissions)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -164,6 +165,19 @@ func managedPermissionsCollector2(store db.DB) legacyTupleCollector {
|
||||
tuples[tuple.Object] = make(map[string]*openfgav1.TupleKey)
|
||||
}
|
||||
|
||||
// For resource actions on folders we need to merge the tuples into one with combined
|
||||
// group_resources.
|
||||
if zanzana.IsFolderResourceTuple(tuple) {
|
||||
key := tupleStringWithoutCondition(tuple)
|
||||
if t, ok := tuples[tuple.Object][key]; ok {
|
||||
zanzana.MergeFolderResourceTuples(t, tuple)
|
||||
} else {
|
||||
tuples[tuple.Object][key] = tuple
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
tuples[tuple.Object][tuple.String()] = tuple
|
||||
}
|
||||
|
||||
@@ -171,6 +185,14 @@ func managedPermissionsCollector2(store db.DB) legacyTupleCollector {
|
||||
}
|
||||
}
|
||||
|
||||
func tupleStringWithoutCondition(tuple *openfgav1.TupleKey) string {
|
||||
c := tuple.Condition
|
||||
tuple.Condition = nil
|
||||
s := tuple.String()
|
||||
tuple.Condition = c
|
||||
return s
|
||||
}
|
||||
|
||||
func zanzanaCollector(client zanzana.Client, relations []string) zanzanaTupleCollector {
|
||||
return func(ctx context.Context, client zanzana.Client, object string) (map[string]*openfgav1.TupleKey, error) {
|
||||
// list will use continuation token to collect all tuples for object and relation
|
||||
@@ -213,7 +235,11 @@ func zanzanaCollector(client zanzana.Client, relations []string) zanzanaTupleCol
|
||||
return nil, err
|
||||
}
|
||||
for _, t := range tuples {
|
||||
out[t.Key.String()] = t.Key
|
||||
if zanzana.IsFolderResourceTuple(t.Key) {
|
||||
out[tupleStringWithoutCondition(t.Key)] = t.Key
|
||||
} else {
|
||||
out[t.Key.String()] = t.Key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,8 +72,14 @@ func NewZanzanaReconciler(client zanzana.Client, store db.DB, lock *serverlock.S
|
||||
client,
|
||||
),
|
||||
newResourceReconciler(
|
||||
"managed permissison",
|
||||
managedPermissionsCollector2(store),
|
||||
"managed folder permissions",
|
||||
managedPermissionsCollector2(store, zanzana.KindFolders),
|
||||
zanzanaCollector(client, zanzana.Folder2Relations),
|
||||
client,
|
||||
),
|
||||
newResourceReconciler(
|
||||
"managed dashboard permissions",
|
||||
managedPermissionsCollector2(store, zanzana.KindDashboards),
|
||||
zanzanaCollector(client, zanzana.ResourceRelations),
|
||||
client,
|
||||
),
|
||||
|
||||
@@ -4,8 +4,9 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/authz/zanzana"
|
||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/authz/zanzana"
|
||||
)
|
||||
|
||||
// legacyTupleCollector collects tuples groupd by object and tupleKey
|
||||
@@ -47,13 +48,25 @@ func (r resourceReconciler) reconcile(ctx context.Context) error {
|
||||
|
||||
// 3. Check if tuples from grafana db exists in zanzana and if not add them to writes
|
||||
for key, t := range tuples {
|
||||
_, ok := zanzanaTuples[key]
|
||||
stored, ok := zanzanaTuples[key]
|
||||
if !ok {
|
||||
writes = append(writes, t)
|
||||
continue
|
||||
}
|
||||
|
||||
// 4. For folder resource tuples we also need to compare the stored group_resources
|
||||
if zanzana.IsFolderResourceTuple(t) && t.String() != stored.String() {
|
||||
deletes = append(deletes, &openfgav1.TupleKeyWithoutCondition{
|
||||
User: t.User,
|
||||
Relation: t.Relation,
|
||||
Object: t.Object,
|
||||
})
|
||||
|
||||
writes = append(writes, t)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Check if tuple from zanzana don't exists in grafana db, if not add them to deletes.
|
||||
// 5. Check if tuple from zanzana don't exists in grafana db, if not add them to deletes.
|
||||
for key, tuple := range zanzanaTuples {
|
||||
_, ok := tuples[key]
|
||||
if !ok {
|
||||
@@ -70,19 +83,6 @@ func (r resourceReconciler) reconcile(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FIXME: batch them together
|
||||
if len(writes) > 0 {
|
||||
err := batch(writes, 100, func(items []*openfgav1.TupleKey) error {
|
||||
return r.client.Write(ctx, &openfgav1.WriteRequest{
|
||||
Writes: &openfgav1.WriteRequestWrites{TupleKeys: items},
|
||||
})
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(deletes) > 0 {
|
||||
err := batch(deletes, 100, func(items []*openfgav1.TupleKeyWithoutCondition) error {
|
||||
return r.client.Write(ctx, &openfgav1.WriteRequest{
|
||||
@@ -95,5 +95,17 @@ func (r resourceReconciler) reconcile(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
if len(writes) > 0 {
|
||||
err := batch(writes, 100, func(items []*openfgav1.TupleKey) error {
|
||||
return r.client.Write(ctx, &openfgav1.WriteRequest{
|
||||
Writes: &openfgav1.WriteRequestWrites{TupleKeys: items},
|
||||
})
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user