From 75df97bd246df0d0ab7b7ffb65a593564d313a91 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Fri, 22 Jan 2021 23:00:10 +0200 Subject: [PATCH] doc/privacy: add mutation filtering example to website --- doc/md/privacy.md | 44 +++++++++++++++++++++++++- examples/privacytenant/example_test.go | 2 +- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/doc/md/privacy.md b/doc/md/privacy.md index 82f1ea253..1ca8584d9 100644 --- a/doc/md/privacy.md +++ b/doc/md/privacy.md @@ -379,7 +379,7 @@ are not connected to the same tenant. ```go // FilterTenantRule is a query rule that filters out entities that are not in the tenant. -func FilterTenantRule() privacy.QueryRule { +func FilterTenantRule() privacy.QueryMutationRule { type TeamsFilter interface { WhereHasTenantWith(...predicate.Tenant) } @@ -520,6 +520,48 @@ func Do(ctx context.Context, client *ent.Client) error { } ``` +In some cases, we want to reject user operations on entities that don't belong to their tenant **without loading +these entities from the database** (unlike the `DenyMismatchedTenants` example above). To achieve this, we can use the +`FilterTenantRule` rule for mutations as well, but limit it to specific operations as follows: + +```go +// Policy defines the privacy policy of the Group. +func (Group) Policy() ent.Policy { + return privacy.Policy{ + Mutation: privacy.MutationPolicy{ + rule.DenyMismatchedTenants(), + // Limit the FilterTenantRule only for + // UpdateOne and DeleteOne operations. + privacy.OnMutationOperation( + rule.FilterTenantRule(), + ent.OpUpdateOne|ent.OpDeleteOne, + ), + }, + } +} +``` + +Then, we expect the privacy-rules to take effect on the client operations. + +```go +func Do(ctx context.Context, client *ent.Client) error { + // A continuation of the code-block above. + + // Expect operation to fail, because the FilterTenantRule rule makes sure + // that tenants can update and delete only their groups. + err = entgo.Update().SetName("fail.go").Exec(labView) + if !ent.IsNotFound(err) { + return fmt.Errorf("expect operation to fail, since the group (entgo) is managed by a different tenant (hub)") + } + entgo, err = entgo.Update().SetName("entgo").Save(hubView) + if err != nil { + return fmt.Errorf("expect operation to pass, but got %v", err) + } + fmt.Println(entgo) + return nil +} +``` + The full example exists in [GitHub](https://github.com/facebook/ent/tree/master/examples/privacytenant). Please note that this documentation is under active development. \ No newline at end of file diff --git a/examples/privacytenant/example_test.go b/examples/privacytenant/example_test.go index 4237607d7..c3fce2cd7 100644 --- a/examples/privacytenant/example_test.go +++ b/examples/privacytenant/example_test.go @@ -102,7 +102,7 @@ func Do(ctx context.Context, client *ent.Client) error { fmt.Println(entgo) // Expect operation to fail, because the FilterTenantRule rule makes sure - // that tenants can update and delete their groups. + // that tenants can update and delete only their groups. err = entgo.Update().SetName("fail.go").Exec(labView) if !ent.IsNotFound(err) { return fmt.Errorf("expect operation to fail, since the group (entgo) is managed by a different tenant (hub)")