/
example.go
123 lines (109 loc) · 3.73 KB
/
example.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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,
}
}