diff --git a/pkg/services/authz/zanzana/client/client.go b/pkg/services/authz/zanzana/client/client.go index cc31cc53d93..670ef845da1 100644 --- a/pkg/services/authz/zanzana/client/client.go +++ b/pkg/services/authz/zanzana/client/client.go @@ -9,6 +9,7 @@ import ( "google.golang.org/protobuf/types/known/wrapperspb" openfgav1 "github.com/openfga/api/proto/openfga/v1" + "github.com/openfga/language/pkg/go/transformer" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/authz/zanzana/schema" @@ -28,16 +29,16 @@ func WithLogger(logger log.Logger) ClientOption { } } -func WithSchema(dsl string) ClientOption { +func WithSchema(modules []transformer.ModuleFile) ClientOption { return func(c *Client) { - c.dsl = dsl + c.modules = modules } } type Client struct { logger log.Logger client openfgav1.OpenFGAServiceClient - dsl string + modules []transformer.ModuleFile tenantID string storeID string modelID string @@ -60,8 +61,8 @@ func New(ctx context.Context, cc grpc.ClientConnInterface, opts ...ClientOption) c.tenantID = "stack-default" } - if c.dsl == "" { - c.dsl = schema.DSL + if c.modules == nil || len(c.modules) == 0 { + c.modules = schema.SchemaModules } store, err := c.getOrCreateStore(ctx, c.tenantID) @@ -71,7 +72,7 @@ func New(ctx context.Context, cc grpc.ClientConnInterface, opts ...ClientOption) c.storeID = store.GetId() - modelID, err := c.loadModel(ctx, c.storeID, c.dsl) + modelID, err := c.loadModel(ctx, c.storeID, c.modules) if err != nil { return nil, err } @@ -151,9 +152,14 @@ func (c *Client) getStore(ctx context.Context, name string) (*openfgav1.Store, e } } -func (c *Client) loadModel(ctx context.Context, storeID string, dsl string) (string, error) { +func (c *Client) loadModel(ctx context.Context, storeID string, modules []transformer.ModuleFile) (string, error) { var continuationToken string + model, err := schema.TransformModulesToModel(modules) + if err != nil { + return "", err + } + for { // ReadAuthorizationModels returns authorization models for a store sorted in descending order of creation. // So with a pageSize of 1 we will get the latest model. @@ -167,16 +173,10 @@ func (c *Client) loadModel(ctx context.Context, storeID string, dsl string) (str return "", fmt.Errorf("failed to load authorization model: %w", err) } - for _, model := range res.GetAuthorizationModels() { - // We need to first convert stored model into dsl and compare it to provided dsl. - storedDSL, err := schema.TransformToDSL(model) - if err != nil { - return "", err - } - + for _, m := range res.GetAuthorizationModels() { // If provided dsl is equal to a stored dsl we use that as the authorization id - if schema.EqualModels(dsl, storedDSL) { - return model.GetId(), nil + if schema.EqualModels(m, model) { + return m.GetId(), nil } } @@ -188,11 +188,6 @@ func (c *Client) loadModel(ctx context.Context, storeID string, dsl string) (str continuationToken = res.GetContinuationToken() } - model, err := schema.TransformToModel(dsl) - if err != nil { - return "", err - } - writeRes, err := c.client.WriteAuthorizationModel(ctx, &openfgav1.WriteAuthorizationModelRequest{ StoreId: c.storeID, TypeDefinitions: model.GetTypeDefinitions(), diff --git a/pkg/services/authz/zanzana/client/client_test.go b/pkg/services/authz/zanzana/client/client_test.go index 523fc665e36..050a0cafcce 100644 --- a/pkg/services/authz/zanzana/client/client_test.go +++ b/pkg/services/authz/zanzana/client/client_test.go @@ -5,13 +5,15 @@ import ( "testing" openfgav1 "github.com/openfga/api/proto/openfga/v1" + "github.com/openfga/language/pkg/go/transformer" "github.com/fullstorydev/grpchan/inprocgrpc" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/tests/testsuite" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" zserver "github.com/grafana/grafana/pkg/services/authz/zanzana/server" zstore "github.com/grafana/grafana/pkg/services/authz/zanzana/store" @@ -63,12 +65,14 @@ func TestIntegrationClient(t *testing.T) { t.Run("should update authorization model if it has new changes", func(t *testing.T) { dsl := ` -model - schema 1.1 +module core type user ` - c, err := New(context.Background(), conn, WithTenantID("new"), WithSchema(dsl)) + modules := []transformer.ModuleFile{ + {Name: "core.fga", Contents: dsl}, + } + c, err := New(context.Background(), conn, WithTenantID("new"), WithSchema(modules)) require.NoError(t, err) assert.Equal(t, prevStoreID, c.storeID) diff --git a/pkg/services/authz/zanzana/schema/core.fga b/pkg/services/authz/zanzana/schema/core.fga new file mode 100644 index 00000000000..9bbfb265b41 --- /dev/null +++ b/pkg/services/authz/zanzana/schema/core.fga @@ -0,0 +1,37 @@ +module core + +type instance + +type user + +type org + relations + define instance: [instance] + define member: [user] + + # team management + define team_create: [role#assignee] + define team_read: [role#assignee] + define team_write: [role#assignee] or team_create + define team_delete: [role#assignee] or team_write + define team_permissions_write: [role#assignee] + define team_permissions_read: [role#assignee] or team_permissions_write + +type role + relations + define org: [org] + define instance: [instance] + define assignee: [user, team#member, role#assignee] + +type team + relations + define org: [org] + define admin: [user] + define member: [user] or admin + + define read: [role#assignee] or member or team_read from org + define write: [role#assignee] or admin or team_write from org + define delete: [role#assignee] or admin or team_delete from org + define permissions_read: [role#assignee] or admin or team_permissions_read from org + define permissions_write: [role#assignee] or admin or team_permissions_write from org + diff --git a/pkg/services/authz/zanzana/schema/dashboard.fga b/pkg/services/authz/zanzana/schema/dashboard.fga new file mode 100644 index 00000000000..6769e3f2bab --- /dev/null +++ b/pkg/services/authz/zanzana/schema/dashboard.fga @@ -0,0 +1,34 @@ +module dashboard + +extend type org + relations + define dashboard_annotations_create: [role#assignee] + define dashboard_annotations_read: [role#assignee] + define dashboard_annotations_write: [role#assignee] + define dashboard_annotations_delete: [role#assignee] + define dashboard_create: [role#assignee] + define dashboard_delete: [role#assignee] + define dashboard_permissions_read: [role#assignee] + define dashboard_permissions_write: [role#assignee] + define dashboard_public_write: [role#assignee] or dashboard_write + define dashboard_read: [role#assignee] + define dashboard_write: [role#assignee] + +type dashboard + relations + define org: [org] + define parent: [folder] + + define read: [user, team#member, role#assignee] or dashboard_read from parent or dashboard_read from org + define write: [user, team#member, role#assignee] or dashboard_write from parent or dashboard_write from org + define delete: [user, team#member, role#assignee] or dashboard_delete from parent or dashboard_delete from org + define create: [user, team#member, role#assignee] or dashboard_create from parent or dashboard_create from org + define permissions_read: [user, team#member, role#assignee] or dashboard_permissions_read from parent or dashboard_permissions_read from org + define permissions_write: [user, team#member, role#assignee] or dashboard_permissions_write from parent or dashboard_permissions_write from org + + define public_write: [user, team#member, role#assignee] or dashboard_public_write from parent or dashboard_public_write from org or write + define annotations_create: [user, team#member, role#assignee] or dashboard_annotations_create from parent or dashboard_annotations_create from org + define annotations_read: [user, team#member, role#assignee] or dashboard_annotations_read from parent or dashboard_annotations_read from org + define annotations_write: [user, team#member, role#assignee] or dashboard_annotations_write from parent or dashboard_annotations_write from org + define annotations_delete: [user, team#member, role#assignee] or dashboard_annotations_delete from parent or dashboard_annotations_delete from org + diff --git a/pkg/services/authz/zanzana/schema/folder.fga b/pkg/services/authz/zanzana/schema/folder.fga new file mode 100644 index 00000000000..4b4587b9fb5 --- /dev/null +++ b/pkg/services/authz/zanzana/schema/folder.fga @@ -0,0 +1,62 @@ +module folder + +extend type org + relations + define folder_create: [role#assignee] + define folder_read: [role#assignee] or folder_delete + define folder_write: [role#assignee] or folder_create + define folder_delete: [role#assignee] or folder_write + define folder_permissions_write: [role#assignee] + define folder_permissions_read: [role#assignee] or folder_permissions_write + + define library_panel_create: [role#assignee] + define library_panel_read: [role#assignee] or library_panel_write + define library_panel_write: [role#assignee] or library_panel_create + define library_panel_delete: [role#assignee] or library_panel_create + + define alert_rule_create: [role#assignee] + define alert_rule_read: [role#assignee] or alert_rule_write + define alert_rule_write: [role#assignee] or alert_rule_create + define alert_rule_delete: [role#assignee] or alert_rule_write + define alert_silence_create: [role#assignee] + define alert_silence_delete: [role#assignee] or alert_silence_write + define alert_silence_read: [role#assignee] or alert_silence_write + define alert_silence_write: [role#assignee] or alert_silence_create + +type folder + relations + define parent: [folder] + define org: [org] + + define create: [user, team#member, role#assignee] or create from parent or folder_create from org + define read: [user, team#member, role#assignee] or read from parent or folder_read from org + define write: [user, team#member, role#assignee] or write from parent or folder_write from org + define delete: [user, team#member, role#assignee] or delete from parent or folder_delete from org + define permissions_read: [user, team#member, role#assignee] or permissions_read from parent or folder_permissions_read from org + define permissions_write: [user, team#member, role#assignee] or permissions_write from parent or folder_permissions_write from org + + define dashboard_create: [user, team#member, role#assignee] or dashboard_create from parent or dashboard_create from org + define dashboard_read: [user, team#member, role#assignee] or dashboard_read from parent or dashboard_read from org + define dashboard_write: [user, team#member, role#assignee] or dashboard_write from parent or dashboard_write from org + define dashboard_delete: [user, team#member, role#assignee] or dashboard_delete from parent or dashboard_delete from org + define dashboard_permissions_read: [user, team#member, role#assignee] or dashboard_permissions_read from parent or dashboard_permissions_read from org + define dashboard_permissions_write: [user, team#member, role#assignee] or dashboard_permissions_write from parent or dashboard_permissions_write from org + define dashboard_public_write: [user, team#member, role#assignee] or dashboard_public_write from parent or dashboard_public_write from org or dashboard_write + define dashboard_annotations_create: [user, team#member, role#assignee] or dashboard_annotations_create from parent or dashboard_annotations_create from org + define dashboard_annotations_read: [user, team#member, role#assignee] or dashboard_annotations_read from parent or dashboard_annotations_read from org + define dashboard_annotations_write: [user, team#member, role#assignee] or dashboard_annotations_write from parent or dashboard_annotations_write from org + define dashboard_annotations_delete: [user, team#member, role#assignee] or dashboard_annotations_delete from parent or dashboard_annotations_delete from org + + define library_panel_create: [user, team#member, role#assignee] or library_panel_create from parent or library_panel_create from org + define library_panel_read: [user, team#member, role#assignee] or library_panel_read from parent or library_panel_read from org or library_panel_write + define library_panel_write: [user, team#member, role#assignee] or library_panel_write from parent or library_panel_write from org or library_panel_create + define library_panel_delete: [user, team#member, role#assignee] or library_panel_delete from parent or library_panel_delete from org or library_panel_create + + define alert_rule_create: [user, team#member, role#assignee] or alert_rule_create from parent or alert_rule_create from org + define alert_rule_read: [user, team#member, role#assignee] or alert_rule_read from parent or alert_rule_read from org or alert_rule_write + define alert_rule_write: [user, team#member, role#assignee] or alert_rule_write from parent or alert_rule_write from org or alert_rule_create + define alert_rule_delete: [user, team#member, role#assignee] or alert_rule_delete from parent or alert_rule_delete from org or alert_rule_write + define alert_silence_create: [user, team#member, role#assignee] or alert_silence_create from parent or alert_silence_create from org + define alert_silence_read: [user, team#member, role#assignee] or alert_silence_read from parent or alert_silence_read from org or alert_silence_write + define alert_silence_write: [user, team#member, role#assignee] or alert_silence_write from parent or alert_silence_write from org or alert_silence_create + diff --git a/pkg/services/authz/zanzana/schema/schema.fga b/pkg/services/authz/zanzana/schema/schema.fga deleted file mode 100644 index 02cc33b4407..00000000000 --- a/pkg/services/authz/zanzana/schema/schema.fga +++ /dev/null @@ -1,126 +0,0 @@ -model - schema 1.1 - -type instance - -type user - -type org - relations - define instance: [instance] - define member: [user] - - # team management - define team_create: [role#assignee] - define team_read: [role#assignee] - define team_write: [role#assignee] or team_create - define team_delete: [role#assignee] or team_write - define team_permissions_write: [role#assignee] - define team_permissions_read: [role#assignee] or team_permissions_write - - define folder_create: [role#assignee] - define folder_read: [role#assignee] or folder_delete - define folder_write: [role#assignee] or folder_create - define folder_delete: [role#assignee] or folder_write - define folder_permissions_write: [role#assignee] - define folder_permissions_read: [role#assignee] or folder_permissions_write - - define dashboard_annotations_create: [role#assignee] - define dashboard_annotations_read: [role#assignee] - define dashboard_annotations_write: [role#assignee] - define dashboard_annotations_delete: [role#assignee] - define dashboard_create: [role#assignee] - define dashboard_delete: [role#assignee] - define dashboard_permissions_read: [role#assignee] - define dashboard_permissions_write: [role#assignee] - define dashboard_public_write: [role#assignee] or dashboard_write - define dashboard_read: [role#assignee] - define dashboard_write: [role#assignee] - - define library_panel_create: [role#assignee] - define library_panel_read: [role#assignee] or library_panel_write - define library_panel_write: [role#assignee] or library_panel_create - define library_panel_delete: [role#assignee] or library_panel_create - - define alert_rule_create: [role#assignee] - define alert_rule_read: [role#assignee] or alert_rule_write - define alert_rule_write: [role#assignee] or alert_rule_create - define alert_rule_delete: [role#assignee] or alert_rule_write - define alert_silence_create: [role#assignee] - define alert_silence_delete: [role#assignee] or alert_silence_write - define alert_silence_read: [role#assignee] or alert_silence_write - define alert_silence_write: [role#assignee] or alert_silence_create - -type role - relations - define org: [org] - define instance: [instance] - define assignee: [user, team#member, role#assignee] - -type team - relations - define org: [org] - define admin: [user] - define member: [user] or admin - - define read: [role#assignee] or member or team_read from org - define write: [role#assignee] or admin or team_write from org - define delete: [role#assignee] or admin or team_delete from org - define permissions_read: [role#assignee] or admin or team_permissions_read from org - define permissions_write: [role#assignee] or admin or team_permissions_write from org - -type folder - relations - define parent: [folder] - define org: [org] - - define create: [user, team#member, role#assignee] or create from parent or folder_create from org - define read: [user, team#member, role#assignee] or read from parent or folder_read from org - define write: [user, team#member, role#assignee] or write from parent or folder_write from org - define delete: [user, team#member, role#assignee] or delete from parent or folder_delete from org - define permissions_read: [user, team#member, role#assignee] or permissions_read from parent or folder_permissions_read from org - define permissions_write: [user, team#member, role#assignee] or permissions_write from parent or folder_permissions_write from org - - define dashboard_create: [user, team#member, role#assignee] or dashboard_create from parent or dashboard_create from org - define dashboard_read: [user, team#member, role#assignee] or dashboard_read from parent or dashboard_read from org - define dashboard_write: [user, team#member, role#assignee] or dashboard_write from parent or dashboard_write from org - define dashboard_delete: [user, team#member, role#assignee] or dashboard_delete from parent or dashboard_delete from org - define dashboard_permissions_read: [user, team#member, role#assignee] or dashboard_permissions_read from parent or dashboard_permissions_read from org - define dashboard_permissions_write: [user, team#member, role#assignee] or dashboard_permissions_write from parent or dashboard_permissions_write from org - define dashboard_public_write: [user, team#member, role#assignee] or dashboard_public_write from parent or dashboard_public_write from org or dashboard_write - define dashboard_annotations_create: [user, team#member, role#assignee] or dashboard_annotations_create from parent or dashboard_annotations_create from org - define dashboard_annotations_read: [user, team#member, role#assignee] or dashboard_annotations_read from parent or dashboard_annotations_read from org - define dashboard_annotations_write: [user, team#member, role#assignee] or dashboard_annotations_write from parent or dashboard_annotations_write from org - define dashboard_annotations_delete: [user, team#member, role#assignee] or dashboard_annotations_delete from parent or dashboard_annotations_delete from org - - define library_panel_create: [user, team#member, role#assignee] or library_panel_create from parent or library_panel_create from org - define library_panel_read: [user, team#member, role#assignee] or library_panel_read from parent or library_panel_read from org or library_panel_write - define library_panel_write: [user, team#member, role#assignee] or library_panel_write from parent or library_panel_write from org or library_panel_create - define library_panel_delete: [user, team#member, role#assignee] or library_panel_delete from parent or library_panel_delete from org or library_panel_create - - define alert_rule_create: [user, team#member, role#assignee] or alert_rule_create from parent or alert_rule_create from org - define alert_rule_read: [user, team#member, role#assignee] or alert_rule_read from parent or alert_rule_read from org or alert_rule_write - define alert_rule_write: [user, team#member, role#assignee] or alert_rule_write from parent or alert_rule_write from org or alert_rule_create - define alert_rule_delete: [user, team#member, role#assignee] or alert_rule_delete from parent or alert_rule_delete from org or alert_rule_write - define alert_silence_create: [user, team#member, role#assignee] or alert_silence_create from parent or alert_silence_create from org - define alert_silence_read: [user, team#member, role#assignee] or alert_silence_read from parent or alert_silence_read from org or alert_silence_write - define alert_silence_write: [user, team#member, role#assignee] or alert_silence_write from parent or alert_silence_write from org or alert_silence_create - -# Dashboard -type dashboard - relations - define org: [org] - define parent: [folder] - - define read: [user, team#member, role#assignee] or dashboard_read from parent or dashboard_read from org - define write: [user, team#member, role#assignee] or dashboard_write from parent or dashboard_write from org - define delete: [user, team#member, role#assignee] or dashboard_delete from parent or dashboard_delete from org - define create: [user, team#member, role#assignee] or dashboard_create from parent or dashboard_create from org - define permissions_read: [user, team#member, role#assignee] or dashboard_permissions_read from parent or dashboard_permissions_read from org - define permissions_write: [user, team#member, role#assignee] or dashboard_permissions_write from parent or dashboard_permissions_write from org - - define public_write: [user, team#member, role#assignee] or dashboard_public_write from parent or dashboard_public_write from org or write - define annotations_create: [user, team#member, role#assignee] or dashboard_annotations_create from parent or dashboard_annotations_create from org - define annotations_read: [user, team#member, role#assignee] or dashboard_annotations_read from parent or dashboard_annotations_read from org - define annotations_write: [user, team#member, role#assignee] or dashboard_annotations_write from parent or dashboard_annotations_write from org - define annotations_delete: [user, team#member, role#assignee] or dashboard_annotations_delete from parent or dashboard_annotations_delete from org diff --git a/pkg/services/authz/zanzana/schema/schema.go b/pkg/services/authz/zanzana/schema/schema.go index ca84e0221a6..9b019e30a0b 100644 --- a/pkg/services/authz/zanzana/schema/schema.go +++ b/pkg/services/authz/zanzana/schema/schema.go @@ -2,7 +2,30 @@ package schema import ( _ "embed" + + "github.com/openfga/language/pkg/go/transformer" ) -//go:embed schema.fga -var DSL string +//go:embed core.fga +var coreDSL string + +//go:embed dashboard.fga +var dashboardDSL string + +//go:embed folder.fga +var folderDSL string + +var SchemaModules = []transformer.ModuleFile{ + { + Name: "core.fga", + Contents: coreDSL, + }, + { + Name: "dashboard.fga", + Contents: dashboardDSL, + }, + { + Name: "folder.fga", + Contents: folderDSL, + }, +} diff --git a/pkg/services/authz/zanzana/schema/transform.go b/pkg/services/authz/zanzana/schema/transform.go index e62237398d9..4f36f331360 100644 --- a/pkg/services/authz/zanzana/schema/transform.go +++ b/pkg/services/authz/zanzana/schema/transform.go @@ -6,9 +6,19 @@ import ( openfgav1 "github.com/openfga/api/proto/openfga/v1" language "github.com/openfga/language/pkg/go/transformer" + "google.golang.org/protobuf/encoding/protojson" ) -func TransformToModel(dsl string) (*openfgav1.AuthorizationModel, error) { +func TransformModulesToModel(modules []language.ModuleFile) (*openfgav1.AuthorizationModel, error) { + parsedAuthModel, err := language.TransformModuleFilesToModel(modules, "1.2") + if err != nil { + return nil, fmt.Errorf("failed to transform dsl to model: %w", err) + } + + return parsedAuthModel, nil +} + +func TransformDSLToModel(dsl string) (*openfgav1.AuthorizationModel, error) { parsedAuthModel, err := language.TransformDSLToProto(dsl) if err != nil { return nil, fmt.Errorf("failed to transform dsl to model: %w", err) @@ -17,27 +27,32 @@ func TransformToModel(dsl string) (*openfgav1.AuthorizationModel, error) { return parsedAuthModel, nil } -func TransformToDSL(model *openfgav1.AuthorizationModel) (string, error) { - return language.TransformJSONProtoToDSL(model) +func TransformToDSL(model *openfgav1.AuthorizationModel, opts ...language.TransformOption) (string, error) { + return language.TransformJSONProtoToDSL(model, opts...) } -// FIXME(kalleep): We need to figure out a better way to compare equality of two different -// authorization model. For now the easiest way I found to comparing different schemas was -// to convert them into their json representation but this requires us to first convert dsl into -// openfgav1.AuthorizationModel and then later parse it as json. -// Comparing parsed authorization model with authorization model from store directly by parsing them as -// as json won't work because stored model will have some fields set such as id that are not present in a parsed -// dsl from disk. -func EqualModels(a, b string) bool { - astr, err := language.TransformDSLToJSON(a) +// EqualModels compares two authorization models. +// Id is not comparing since model loaded from disk doesn't contain Id. +func EqualModels(a, b *openfgav1.AuthorizationModel) bool { + aCopy := openfgav1.AuthorizationModel{ + SchemaVersion: a.SchemaVersion, + TypeDefinitions: a.TypeDefinitions, + Conditions: a.Conditions, + } + aJSONBytes, err := protojson.Marshal(&aCopy) if err != nil { return false } - bstr, err := language.TransformDSLToJSON(b) + bCopy := openfgav1.AuthorizationModel{ + SchemaVersion: b.SchemaVersion, + TypeDefinitions: b.TypeDefinitions, + Conditions: b.Conditions, + } + bJSONBytes, err := protojson.Marshal(&bCopy) if err != nil { return false } - return astr == bstr + return string(aJSONBytes) == string(bJSONBytes) } diff --git a/pkg/services/authz/zanzana/schema/transform_test.go b/pkg/services/authz/zanzana/schema/transform_test.go index f7a3b108fc3..f309d91e963 100644 --- a/pkg/services/authz/zanzana/schema/transform_test.go +++ b/pkg/services/authz/zanzana/schema/transform_test.go @@ -3,6 +3,7 @@ package schema import ( "testing" + "github.com/openfga/language/pkg/go/transformer" "github.com/stretchr/testify/assert" ) @@ -125,7 +126,188 @@ type role for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - assert.Equal(t, tt.expected, EqualModels(tt.a, tt.b)) + modelA, err := transformer.TransformDSLToProto(tt.a) + assert.NoError(t, err) + + modelB, err := transformer.TransformDSLToProto(tt.b) + assert.NoError(t, err) + + assert.Equal(t, tt.expected, EqualModels(modelA, modelB)) + }) + } +} + +func TestModulesEqualModels(t *testing.T) { + type testCase struct { + desc string + a []transformer.ModuleFile + b []transformer.ModuleFile + expected bool + } + + tests := []testCase{ + { + desc: "should be equal", + a: []transformer.ModuleFile{ + { + Name: "core.fga", + Contents: ` +module core + +type instance + +type user + +type org + relations + define instance: [instance] + define member: [user] + define viewer: [user] + +type role + relations + define org: [org] + define instance: [instance] + define assignee: [user, team#member, role#assignee] + `, + }, + { + Name: "team.fga", + Contents: ` +module team + +type team + relations + define org: [org] + define admin: [user] + define member: [user] or org + `, + }, + }, + b: []transformer.ModuleFile{ + { + Name: "core.fga", + Contents: ` +module core + +type instance + +type user + +type org + relations + define instance: [instance] + define member: [user] + define viewer: [user] + +type role + relations + define org: [org] + define instance: [instance] + define assignee: [user, team#member, role#assignee] + `, + }, + { + Name: "team.fga", + Contents: ` +module team + +type team + relations + define org: [org] + define admin: [user] + define member: [user] or org + `, + }, + }, + expected: true, + }, + { + desc: "should not be equal", + a: []transformer.ModuleFile{ + { + Name: "core.fga", + Contents: ` +module core + +type instance + +type user + +type org + relations + define instance: [instance] + define member: [user] + define viewer: [user] + +type role + relations + define org: [org] + define instance: [instance] + define assignee: [user, team#member, role#assignee] + `, + }, + { + Name: "team.fga", + Contents: ` +module team + +type team + relations + define org: [org] + define admin: [user] + define member: [user] or org + `, + }, + }, + b: []transformer.ModuleFile{ + { + Name: "core.fga", + Contents: ` +module core + +type instance + +type user + +type org + relations + define instance: [instance] + define member: [user] + define viewer: [user] + +type role + relations + define org: [org] + define instance: [instance] + define assignee: [user, team#member, role#assignee] + `, + }, + { + Name: "folder.fga", + Contents: ` +module folder + +type folder + relations + define parent: [folder] + define org: [org] + `, + }, + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + modelA, err := TransformModulesToModel(tt.a) + assert.NoError(t, err) + + modelB, err := TransformModulesToModel(tt.b) + assert.NoError(t, err) + + assert.Equal(t, tt.expected, EqualModels(modelA, modelB)) }) } }