Zanzana: Use separate store for each org (#96015)

* Move server init into server package

* map store name to id

* refactor model loading

* pass namespace into reconcilers and collectors

* refactor

* Extend authz server with Read and Write methods

* use new read/write in reconciler

* implement server side read and write

* Sync permissions for every org

* handle namespace in check and list

* split read and write

* provide conditions

* Fix client implementation

* fix nil conditions

* remove unused client code

* use lock for store access

* move type translators to common package

* fix folder collector

* fix store creation

* remove unused AuthorizationModelId

* fix server tests

* fix linter
This commit is contained in:
Alexander Zobnin
2024-11-08 14:54:36 +01:00
committed by GitHub
parent 86bc087257
commit 910ec7e7dc
22 changed files with 1497 additions and 396 deletions
@@ -3,13 +3,16 @@ package dualwrite
import (
"context"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/authz/zanzana"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
"github.com/grafana/grafana/pkg/services/authz/zanzana/common"
authzextv1 "github.com/grafana/grafana/pkg/services/authz/zanzana/proto/v1"
)
func teamMembershipCollector(store db.DB) legacyTupleCollector {
return func(ctx context.Context) (map[string]map[string]*openfgav1.TupleKey, error) {
return func(ctx context.Context, orgId int64) (map[string]map[string]*openfgav1.TupleKey, error) {
query := `
SELECT t.uid as team_uid, u.uid as user_uid, tm.permission
FROM team_member tm
@@ -60,7 +63,7 @@ func teamMembershipCollector(store db.DB) legacyTupleCollector {
// folderTreeCollector collects folder tree structure and writes it as relation tuples
func folderTreeCollector(store db.DB) legacyTupleCollector {
return func(ctx context.Context) (map[string]map[string]*openfgav1.TupleKey, error) {
return func(ctx context.Context, orgId int64) (map[string]map[string]*openfgav1.TupleKey, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.migrator.folderTreeCollector")
defer span.End()
@@ -91,9 +94,9 @@ func folderTreeCollector(store db.DB) legacyTupleCollector {
}
tuple = &openfgav1.TupleKey{
Object: zanzana.NewTupleEntry("folder2", f.FolderUID, ""),
Object: zanzana.NewTupleEntry(common.TypeFolder, f.FolderUID, ""),
Relation: zanzana.RelationParent,
User: zanzana.NewTupleEntry("folder2", f.ParentUID, ""),
User: zanzana.NewTupleEntry(common.TypeFolder, f.ParentUID, ""),
}
if tuples[tuple.Object] == nil {
@@ -111,7 +114,7 @@ func folderTreeCollector(store db.DB) legacyTupleCollector {
// 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 managedPermissionsCollector(store db.DB, kind string) legacyTupleCollector {
return func(ctx context.Context) (map[string]map[string]*openfgav1.TupleKey, error) {
return func(ctx context.Context, orgId int64) (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
FROM permission p
@@ -194,11 +197,12 @@ func tupleStringWithoutCondition(tuple *openfgav1.TupleKey) string {
}
func zanzanaCollector(relations []string) zanzanaTupleCollector {
return func(ctx context.Context, client zanzana.Client, object string) (map[string]*openfgav1.TupleKey, error) {
return func(ctx context.Context, client zanzana.Client, object string, namespace string) (map[string]*openfgav1.TupleKey, error) {
// list will use continuation token to collect all tuples for object and relation
list := func(relation string) ([]*openfgav1.Tuple, error) {
first, err := client.Read(ctx, &openfgav1.ReadRequest{
TupleKey: &openfgav1.ReadRequestTupleKey{
first, err := client.Read(ctx, &authzextv1.ReadRequest{
Namespace: namespace,
TupleKey: &authzextv1.ReadRequestTupleKey{
Object: object,
Relation: relation,
},
@@ -211,8 +215,9 @@ func zanzanaCollector(relations []string) zanzanaTupleCollector {
c := first.ContinuationToken
for c != "" {
res, err := client.Read(ctx, &openfgav1.ReadRequest{
TupleKey: &openfgav1.ReadRequestTupleKey{
res, err := client.Read(ctx, &authzextv1.ReadRequest{
Namespace: namespace,
TupleKey: &authzextv1.ReadRequestTupleKey{
Object: object,
Relation: relation,
},
@@ -225,7 +230,7 @@ func zanzanaCollector(relations []string) zanzanaTupleCollector {
first.Tuples = append(first.Tuples, res.Tuples...)
}
return first.Tuples, nil
return common.ToOpenFGATuples(first.Tuples), nil
}
out := make(map[string]*openfgav1.TupleKey)