From 71538f2e6edfa96631d2b7d72d6f2fd0a1595091 Mon Sep 17 00:00:00 2001 From: Jake Moshenko Date: Fri, 22 Oct 2021 14:42:11 -0400 Subject: [PATCH 1/4] Add a v1 API example Signed-off-by: Jake Moshenko --- examples/v1/example.go | 160 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 examples/v1/example.go diff --git a/examples/v1/example.go b/examples/v1/example.go new file mode 100644 index 0000000..81ccc4c --- /dev/null +++ b/examples/v1/example.go @@ -0,0 +1,160 @@ +package main + +import ( + "context" + "log" + + "github.com/authzed/grpcutil" + + v1 "github.com/authzed/authzed-go/proto/authzed/api/v1" + "github.com/authzed/authzed-go/v1" +) + +const ( + documentNS = "yourtenant/document" + userNS = "yourtenant/user" + + schema = ` + definition yourtenant/user {} + + definition yourtenant/document { + relation viewer: yourtenant/user + relation contributor: yourtenant/user + relation owner: yourtenant/user + + permission read = viewer + contributor + owner + permission write = contributor + owner + permission delete = owner + } + ` +) + +func main() { + // Create an Authzed client. + client, err := authzed.NewClient( + "grpc.authzed.com:443", + grpcutil.WithInsecureBearerToken("t_your_token_here"), + grpcutil.WithSystemCerts(grpcutil.VerifyCA), + ) + if err != nil { + log.Fatalf("unable to initialize client: %s", err) + } + + // Uncomment this block to run against a local SpiceDB. + // client, err = authzed.NewClient( + // "localhost:50051", + // grpcutil.WithInsecureBearerToken("testtesttesttest"), + // grpc.WithInsecure(), + // ) + // if err != nil { + // log.Fatalf("unable to initialize client: %s", err) + // } + + // Write the schema to the permissions system + _, err = client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{ + Schema: schema, + }) + if err != nil { + log.Fatalf("unable to write schema: %s", err) + } + + // Create some objects that will be protected by Authzed. + aDoc := object(documentNS, "doc1") + anOwner := subject(userNS, "theowner") + anEditor := subject(userNS, "userwhocanedit") + aViewer := subject(userNS, "viewonlyuser") + + // Create some relationships that represent roles granted between users and objects. + resp, err := client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{ + Updates: []*v1.RelationshipUpdate{ + createRelationship(relationship(aDoc, "owner", anOwner)), + createRelationship(relationship(aDoc, "contributor", anEditor)), + createRelationship(relationship(aDoc, "viewer", aViewer)), + }, + }) + if err != nil { + log.Fatalf("unable to write tuples: %s", err) + } + + // Save the ZedToken from the Write for future requests in order to enforce + // that responses are at least as fresh as our last write. + // + // We recommend saving this from any call to WriteRelationships and storing it + // alongside the object referenced in the write or check (in this case aDoc)" + // + // For more info see: + // https://github.com/authzed/spicedb/blob/main/docs/zedtokens-and-zookies.md + whenPermsChanged := resp.WrittenAt + + // Run some permission checks on the written data. + aNobody := subject(userNS, "randomnobody") + expected := []struct { + resource *v1.ObjectReference + permission string + subject *v1.SubjectReference + hasAccess bool + }{ + {aDoc, "read", anOwner, true}, + {aDoc, "write", anOwner, true}, + {aDoc, "delete", anOwner, true}, + {aDoc, "read", anEditor, true}, + {aDoc, "write", anEditor, true}, + {aDoc, "delete", anEditor, false}, + {aDoc, "read", aViewer, true}, + {aDoc, "write", aViewer, false}, + {aDoc, "delete", aViewer, false}, + {aDoc, "read", aNobody, false}, + {aDoc, "write", aNobody, false}, + {aDoc, "delete", aNobody, false}, + } + + for _, test := range expected { + testResp, err := client.CheckPermission(context.Background(), &v1.CheckPermissionRequest{ + // Guarantee checks occur on data fresher than the write. + Consistency: &v1.Consistency{ + Requirement: &v1.Consistency_AtLeastAsFresh{ + AtLeastAsFresh: whenPermsChanged, + }, + }, + Resource: test.resource, + Permission: test.permission, + Subject: test.subject, + }) + if err != nil { + log.Fatalf("unable to run check request: %s", err) + } + + hasAccess := testResp.Permissionship == v1.CheckPermissionResponse_PERMISSIONSHIP_HAS_PERMISSION + if hasAccess != test.hasAccess { + log.Fatalf("check returned the wrong result: %#v", test) + } + } +} + +func object(namespace, objectID string) *v1.ObjectReference { + return &v1.ObjectReference{ + ObjectType: namespace, + ObjectId: objectID, + } +} + +func subject(namespace, objectID string) *v1.SubjectReference { + return &v1.SubjectReference{ + Object: object(namespace, objectID), + } +} + +func relationship(resource *v1.ObjectReference, relation string, subject *v1.SubjectReference) *v1.Relationship { + return &v1.Relationship{ + Resource: resource, + Relation: relation, + Subject: subject, + } +} + +func createRelationship(relationship *v1.Relationship) *v1.RelationshipUpdate { + return &v1.RelationshipUpdate{ + Operation: v1.RelationshipUpdate_OPERATION_CREATE, + Relationship: relationship, + } +} From b4795255a4413eb5f12435e5a1ed49322d6ebe95 Mon Sep 17 00:00:00 2001 From: Jake Moshenko Date: Fri, 22 Oct 2021 14:42:25 -0400 Subject: [PATCH 2/4] remove the v0 and v1alpha1 examples Signed-off-by: Jake Moshenko --- examples/v0/example.go | 123 ---------------------- examples/v0/yourtenant.yaml | 71 ------------- examples/v1alpha1/schema_write_example.go | 37 ------- 3 files changed, 231 deletions(-) delete mode 100644 examples/v0/example.go delete mode 100644 examples/v0/yourtenant.yaml delete mode 100644 examples/v1alpha1/schema_write_example.go diff --git a/examples/v0/example.go b/examples/v0/example.go deleted file mode 100644 index a5b5109..0000000 --- a/examples/v0/example.go +++ /dev/null @@ -1,123 +0,0 @@ -package main - -import ( - "context" - "log" - - v0 "github.com/authzed/authzed-go/proto/authzed/api/v0" - "github.com/authzed/authzed-go/v0" -) - -const ( - documentNS = "yourtenant/document" - userNS = "yourtenant/user" -) - -func main() { - // Create an Authzed client. - client, err := authzed.NewClient( - "grpc.authzed.com:443", - authzed.Token("t_your_token_here_1234567deadbeef"), - authzed.SystemCerts(authzed.VerifyCA), - ) - if err != nil { - log.Fatalf("unable to initialize client: %s", err) - } - - // Create some objects that will be protected by Authzed. - aDoc := createObject(documentNS, "doc1") - anOwner := createObject(userNS, "theowner")("...") - anEditor := createObject(userNS, "userwhocanedit")("...") - aViewer := createObject(userNS, "viewonlyuser")("...") - - // Create some tuples that represent roles granted between users and objects. - resp, err := client.Write(context.Background(), &v0.WriteRequest{ - Updates: []*v0.RelationTupleUpdate{ - createTuple(tuple(aDoc("owner"), anOwner)), - createTuple(tuple(aDoc("contributor"), anEditor)), - createTuple(tuple(aDoc("viewer"), aViewer)), - }, - }) - if err != nil { - log.Fatalf("unable to write tuples: %s", err) - } - - // Save the revision from the Write for future requests in order to enforce - // that responses are at least as fresh as our last write. - // - // We recommend saving this from any call to Write or ContentChangeCheck, - // and storing it alongside the object referenced in the write or check (in this case aDoc)" - // - // For more info see: - // https://docs.authzed.com/authz/new-enemy - whenPermsChanged := resp.Revision - - // Run some permission checks on the written data. - aNobody := createObject(userNS, "randomnobody")("...") - expected := []checkData{ - {permission: aDoc("read"), user: anOwner, hasAccess: true}, - {permission: aDoc("write"), user: anOwner, hasAccess: true}, - {permission: aDoc("delete"), user: anOwner, hasAccess: true}, - {permission: aDoc("read"), user: anEditor, hasAccess: true}, - {permission: aDoc("write"), user: anEditor, hasAccess: true}, - {permission: aDoc("delete"), user: anEditor, hasAccess: false}, - {permission: aDoc("read"), user: aViewer, hasAccess: true}, - {permission: aDoc("write"), user: aViewer, hasAccess: false}, - {permission: aDoc("delete"), user: aViewer, hasAccess: false}, - {permission: aDoc("read"), user: aNobody, hasAccess: true}, - {permission: aDoc("write"), user: aNobody, hasAccess: false}, - {permission: aDoc("delete"), user: aNobody, hasAccess: false}, - } - - for _, test := range expected { - testResp, err := client.Check(context.Background(), &v0.CheckRequest{ - TestUserset: test.permission, - User: &v0.User{UserOneof: &v0.User_Userset{ - Userset: test.user, - }}, - AtRevision: whenPermsChanged, // Guarantee checks occur on data fresher than the write. - }) - if err != nil { - log.Fatalf("unable to run check request: %s", err) - } - - hasAccess := testResp.GetMembership() == v0.CheckResponse_MEMBER - if hasAccess != test.hasAccess { - log.Fatalf("check returned the wrong result: %v", test) - } - } -} - -type checkData struct { - permission *v0.ObjectAndRelation - user *v0.ObjectAndRelation - hasAccess bool -} - -func createObject(namespace, objectID string) func(string) *v0.ObjectAndRelation { - return func(relation string) *v0.ObjectAndRelation { - return &v0.ObjectAndRelation{ - Namespace: namespace, - ObjectId: objectID, - Relation: relation, - } - } -} - -func tuple(onr *v0.ObjectAndRelation, userset *v0.ObjectAndRelation) *v0.RelationTuple { - return &v0.RelationTuple{ - ObjectAndRelation: onr, - User: &v0.User{ - UserOneof: &v0.User_Userset{ - Userset: userset, - }, - }, - } -} - -func createTuple(tpl *v0.RelationTuple) *v0.RelationTupleUpdate { - return &v0.RelationTupleUpdate{ - Operation: v0.RelationTupleUpdate_CREATE, - Tuple: tpl, - } -} diff --git a/examples/v0/yourtenant.yaml b/examples/v0/yourtenant.yaml deleted file mode 100644 index 44355e9..0000000 --- a/examples/v0/yourtenant.yaml +++ /dev/null @@ -1,71 +0,0 @@ -# This file contains the namespace configuration for basic example. - -# This section contains the tenant configuration itself. -namespace_configs: -- | - name: "yourtenant/user" - -- | - name: "yourtenant/document" - - relation { - name: "read" - - userset_rewrite { - union { - child { computed_userset { relation: "viewer" } } - child { computed_userset { relation: "contributor" } } - child { computed_userset { relation: "owner" } } - } - } - } - relation { - name: "write" - - userset_rewrite { - union { - child { computed_userset { relation: "contributor" } } - child { computed_userset { relation: "owner" } } - } - } - } - relation { - name: "delete" - - userset_rewrite { - union { - child { computed_userset { relation: "owner" } } - } - } - } - - relation { - name: "viewer" - } - relation { - name: "contributor" - } - relation { - name: "owner" - } - -# This section contains example tuples that will be written to the tenant, -# which can be used to validate the tenant configuration offline. -validation_tuples: -- yourtenant/document:doc1#owner@yourtenant/user:theowner#... -- yourtenant/document:doc1#contributor@yourtenant/user:userwhocanedit#... -- yourtenant/document:doc1#viewer@yourtenant/user:viewonlyuser#... - -# This section contains validation blocks that check the specific membership -# of the various grantable permissions. Validation will fail if the list of -# entities computed to have access differs in any way from the list declared. -validation: - yourtenant/document:doc1#read: - - '[yourtenant/user:viewonlyuser#...] is ' - - '[yourtenant/user:userwhocanedit#...] is ' - - '[yourtenant/user:theowner#...] is ' - yourtenant/document:doc1#write: - - '[yourtenant/user:userwhocanedit#...] is ' - - '[yourtenant/user:theowner#...] is ' - yourtenant/document:doc1#delete: - - '[yourtenant/user:theowner#...] is ' diff --git a/examples/v1alpha1/schema_write_example.go b/examples/v1alpha1/schema_write_example.go deleted file mode 100644 index 5c80cac..0000000 --- a/examples/v1alpha1/schema_write_example.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "context" - "log" - - pb "github.com/authzed/authzed-go/proto/authzed/api/v1alpha1" - authzed "github.com/authzed/authzed-go/v1alpha1" - "github.com/authzed/grpcutil" -) - -const schema = `definition blog/user {} - -definition blog/post { - relation reader: blog/user - relation writer: blog/user - - permission read = reader + writer - permission write = writer -}` - -func main() { - client, err := authzed.NewClient( - "grpc.authzed.com:443", - grpcutil.WithBearerToken("t_your_token_here_1234567deadbeef"), - grpcutil.WithSystemCerts(false), - ) - if err != nil { - log.Fatalf("unable to initialize client: %s", err) - } - - request := &pb.WriteSchemaRequest{Schema: schema} - _, err = client.WriteSchema(context.Background(), request) - if err != nil { - log.Fatalf("failed to write schema: %s", err) - } -} From ddf64fd94f5b1f3cf5fff2250d0180bcd634025e Mon Sep 17 00:00:00 2001 From: Jake Moshenko Date: Fri, 22 Oct 2021 15:22:03 -0400 Subject: [PATCH 3/4] improve github workflow to only run on master pushes Signed-off-by: Jake Moshenko --- .github/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 42c5931..77dd962 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -2,10 +2,10 @@ name: "build" on: push: branches: - - "!dependabot/*" - - "*" + - "master" pull_request: branches: + - "!dependabot/*" - "*" jobs: lint: From 2edc56ccfc54c743a459e5ea461a6863c6581a03 Mon Sep 17 00:00:00 2001 From: Jake Moshenko Date: Fri, 22 Oct 2021 15:37:25 -0400 Subject: [PATCH 4/4] upgrade to buf 1.0.0-rc6 and regenerate protos Signed-off-by: Jake Moshenko --- .github/workflows/build.yaml | 2 +- proto/authzed/api/v0/acl_service.pb.go | 2 +- proto/authzed/api/v0/core.pb.go | 2 +- proto/authzed/api/v0/developer.pb.go | 2 +- proto/authzed/api/v0/namespace.pb.go | 2 +- proto/authzed/api/v0/namespace_service.pb.go | 2 +- proto/authzed/api/v0/watch_service.pb.go | 2 +- proto/authzed/api/v1/core.pb.go | 2 +- proto/authzed/api/v1/openapi.pb.go | 2 +- proto/authzed/api/v1/permission_service.pb.go | 2 +- proto/authzed/api/v1/schema_service.pb.go | 2 +- proto/authzed/api/v1/watch_service.pb.go | 2 +- proto/authzed/api/v1alpha1/schema.pb.go | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 77dd962..c9047d4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -62,6 +62,6 @@ jobs: - uses: "actions/checkout@v2" - uses: "bufbuild/buf-setup-action@v0.5.0" with: - version: "0.56.0" + version: "1.0.0-rc6" - name: "Generate & Diff Protos" run: "./buf.gen.yaml && git diff && bash -c '[ $(git status --porcelain | tee /dev/fd/2 | wc -c) -eq 0 ]'" diff --git a/proto/authzed/api/v0/acl_service.pb.go b/proto/authzed/api/v0/acl_service.pb.go index 85fcdd7..faee475 100644 --- a/proto/authzed/api/v0/acl_service.pb.go +++ b/proto/authzed/api/v0/acl_service.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.18.1 +// protoc v3.19.0 // source: authzed/api/v0/acl_service.proto package v0 diff --git a/proto/authzed/api/v0/core.pb.go b/proto/authzed/api/v0/core.pb.go index e248ceb..3c927be 100644 --- a/proto/authzed/api/v0/core.pb.go +++ b/proto/authzed/api/v0/core.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.18.1 +// protoc v3.19.0 // source: authzed/api/v0/core.proto package v0 diff --git a/proto/authzed/api/v0/developer.pb.go b/proto/authzed/api/v0/developer.pb.go index 2c07964..f120934 100644 --- a/proto/authzed/api/v0/developer.pb.go +++ b/proto/authzed/api/v0/developer.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.18.1 +// protoc v3.19.0 // source: authzed/api/v0/developer.proto package v0 diff --git a/proto/authzed/api/v0/namespace.pb.go b/proto/authzed/api/v0/namespace.pb.go index d89b9a4..d89c575 100644 --- a/proto/authzed/api/v0/namespace.pb.go +++ b/proto/authzed/api/v0/namespace.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.18.1 +// protoc v3.19.0 // source: authzed/api/v0/namespace.proto package v0 diff --git a/proto/authzed/api/v0/namespace_service.pb.go b/proto/authzed/api/v0/namespace_service.pb.go index 4edd177..5c4e770 100644 --- a/proto/authzed/api/v0/namespace_service.pb.go +++ b/proto/authzed/api/v0/namespace_service.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.18.1 +// protoc v3.19.0 // source: authzed/api/v0/namespace_service.proto package v0 diff --git a/proto/authzed/api/v0/watch_service.pb.go b/proto/authzed/api/v0/watch_service.pb.go index 8c88a02..54fc7cf 100644 --- a/proto/authzed/api/v0/watch_service.pb.go +++ b/proto/authzed/api/v0/watch_service.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.18.1 +// protoc v3.19.0 // source: authzed/api/v0/watch_service.proto package v0 diff --git a/proto/authzed/api/v1/core.pb.go b/proto/authzed/api/v1/core.pb.go index c7f2942..a91b4ef 100644 --- a/proto/authzed/api/v1/core.pb.go +++ b/proto/authzed/api/v1/core.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.18.1 +// protoc v3.19.0 // source: authzed/api/v1/core.proto package v1 diff --git a/proto/authzed/api/v1/openapi.pb.go b/proto/authzed/api/v1/openapi.pb.go index 84314c8..ba2dff2 100644 --- a/proto/authzed/api/v1/openapi.pb.go +++ b/proto/authzed/api/v1/openapi.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.18.1 +// protoc v3.19.0 // source: authzed/api/v1/openapi.proto package v1 diff --git a/proto/authzed/api/v1/permission_service.pb.go b/proto/authzed/api/v1/permission_service.pb.go index 070336b..87c9e7a 100644 --- a/proto/authzed/api/v1/permission_service.pb.go +++ b/proto/authzed/api/v1/permission_service.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.18.1 +// protoc v3.19.0 // source: authzed/api/v1/permission_service.proto package v1 diff --git a/proto/authzed/api/v1/schema_service.pb.go b/proto/authzed/api/v1/schema_service.pb.go index d2f5bc9..2c23d85 100644 --- a/proto/authzed/api/v1/schema_service.pb.go +++ b/proto/authzed/api/v1/schema_service.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.18.1 +// protoc v3.19.0 // source: authzed/api/v1/schema_service.proto package v1 diff --git a/proto/authzed/api/v1/watch_service.pb.go b/proto/authzed/api/v1/watch_service.pb.go index eb40e98..9218338 100644 --- a/proto/authzed/api/v1/watch_service.pb.go +++ b/proto/authzed/api/v1/watch_service.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.18.1 +// protoc v3.19.0 // source: authzed/api/v1/watch_service.proto package v1 diff --git a/proto/authzed/api/v1alpha1/schema.pb.go b/proto/authzed/api/v1alpha1/schema.pb.go index df9ffc4..922a640 100644 --- a/proto/authzed/api/v1alpha1/schema.pb.go +++ b/proto/authzed/api/v1alpha1/schema.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.18.1 +// protoc v3.19.0 // source: authzed/api/v1alpha1/schema.proto package v1alpha1