Skip to content

Commit cdd7b9f

Browse files
committed
playground work
1 parent 8791c46 commit cdd7b9f

File tree

10 files changed

+1690
-0
lines changed

10 files changed

+1690
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package main
2+
3+
import (
4+
"github.com/coder/coder/v2/coderd/database/spice/policy/playground/relationships"
5+
"gopkg.in/yaml.v3"
6+
7+
"github.com/coder/coder/v2/coderd/database/spice/policy"
8+
)
9+
10+
type PlaygroundYAML struct {
11+
Schema string `yaml:"schema"`
12+
Relationships string `yaml:"relationships"`
13+
Assertions struct {
14+
True []string `yaml:"assertTrue"`
15+
False []string `yaml:"assertFalse"`
16+
} `yaml:"assertions"`
17+
Validation map[string][]string `yaml:"validation"`
18+
}
19+
20+
func PlaygroundExport() string {
21+
relationships.GenerateRelationships()
22+
y := PlaygroundYAML{
23+
Schema: policy.Schema,
24+
Relationships: relationships.AllRelationsToStrings(),
25+
}
26+
out, err := yaml.Marshal(y)
27+
if err != nil {
28+
panic(err)
29+
}
30+
return string(out)
31+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
)
7+
8+
func main() {
9+
if len(os.Args) <= 1 {
10+
usage()
11+
return
12+
}
13+
14+
switch os.Args[1] {
15+
case "export":
16+
fmt.Println(PlaygroundExport())
17+
default:
18+
usage()
19+
}
20+
}
21+
22+
func usage() {
23+
fmt.Println("Usage: policycmd [command]")
24+
fmt.Println("Commands:")
25+
fmt.Println(" export")
26+
fmt.Println(" import")
27+
fmt.Println(" playground")
28+
fmt.Println(" run")
29+
fmt.Println(" test")
30+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package main
2+
3+
import (
4+
_ "embed"
5+
"fmt"
6+
"go/format"
7+
"strings"
8+
"text/template"
9+
10+
v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
11+
core "github.com/authzed/spicedb/pkg/proto/core/v1"
12+
13+
"github.com/authzed/spicedb/pkg/schemadsl/compiler"
14+
"github.com/coder/coder/v2/coderd/database/spice/policy"
15+
)
16+
17+
//go:embed relationships.tmpl
18+
var templateText string
19+
20+
func main() {
21+
fmt.Println(Generate())
22+
}
23+
24+
func capitalize(name string) string {
25+
return strings.ToUpper(string(name[0])) + name[1:]
26+
}
27+
28+
func Generate() string {
29+
var prefix string
30+
compiled, err := compiler.Compile(compiler.InputSchema{
31+
Source: "policy.zed",
32+
SchemaString: policy.Schema,
33+
}, &prefix)
34+
if err != nil {
35+
panic(err)
36+
}
37+
38+
tpl := template.New("zanzobjects").Funcs(template.FuncMap{
39+
"capitalize": capitalize,
40+
})
41+
42+
tpl, err = tpl.Parse(templateText)
43+
if err != nil {
44+
panic(err)
45+
}
46+
47+
var output strings.Builder
48+
output.WriteString(`package relationships`)
49+
output.WriteString("\n")
50+
output.WriteString(`import v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"`)
51+
output.WriteString("\n")
52+
53+
for _, obj := range compiled.ObjectDefinitions {
54+
d := newDef(obj)
55+
var _ = d
56+
err := tpl.Execute(&output, d)
57+
if err != nil {
58+
panic(err)
59+
}
60+
output.WriteString("\n")
61+
}
62+
63+
formatted, err := format.Source([]byte(output.String()))
64+
if err != nil {
65+
panic(err)
66+
}
67+
return string(formatted)
68+
}
69+
70+
type objectDefinition struct {
71+
// The core type
72+
*core.NamespaceDefinition
73+
74+
DirectRelations []objectDirectRelation
75+
}
76+
77+
type objectDirectRelation struct {
78+
RelationName string
79+
FunctionName string
80+
Subject v1.SubjectReference
81+
}
82+
83+
func newDef(obj *core.NamespaceDefinition) objectDefinition {
84+
d := objectDefinition{
85+
NamespaceDefinition: obj,
86+
}
87+
rels := make([]objectDirectRelation, 0)
88+
89+
//if obj.Name == "group" {
90+
// fmt.Println("")
91+
//}
92+
93+
for _, r := range obj.Relation {
94+
if r.UsersetRewrite != nil {
95+
// This is a permission.
96+
continue
97+
}
98+
99+
dedup := 0
100+
multipleSubjects := make([]objectDirectRelation, 0)
101+
// For the "relation" we should write a helper function to create
102+
// this relationship between two objects.
103+
for _, d := range r.TypeInformation.AllowedDirectRelations {
104+
optRel := ""
105+
if d.GetRelation() != "..." {
106+
optRel = d.GetRelation()
107+
}
108+
109+
if d.GetPublicWildcard() != nil {
110+
multipleSubjects = append(multipleSubjects, objectDirectRelation{
111+
RelationName: r.Name,
112+
FunctionName: r.Name,
113+
Subject: v1.SubjectReference{
114+
Object: &v1.ObjectReference{
115+
ObjectType: d.Namespace,
116+
ObjectId: "*",
117+
},
118+
OptionalRelation: optRel,
119+
},
120+
})
121+
continue
122+
}
123+
124+
dedup++
125+
multipleSubjects = append(multipleSubjects, objectDirectRelation{
126+
RelationName: r.Name,
127+
FunctionName: r.Name,
128+
Subject: v1.SubjectReference{
129+
Object: &v1.ObjectReference{
130+
ObjectType: d.Namespace,
131+
ObjectId: "<id>",
132+
},
133+
OptionalRelation: optRel,
134+
},
135+
})
136+
}
137+
138+
if dedup > 1 {
139+
for i := range multipleSubjects {
140+
// Remove method name conflicts
141+
multipleSubjects[i].FunctionName += capitalize(multipleSubjects[i].Subject.Object.ObjectType)
142+
}
143+
}
144+
rels = append(rels, multipleSubjects...)
145+
}
146+
d.DirectRelations = rels
147+
return d
148+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package main_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
8+
"github.com/authzed/spicedb/pkg/tuple"
9+
)
10+
11+
func TestString(t *testing.T) {
12+
rel := v1.Relationship{
13+
Resource: &v1.ObjectReference{
14+
ObjectType: "group",
15+
ObjectId: "everyone",
16+
},
17+
Relation: "member",
18+
Subject: &v1.SubjectReference{
19+
Object: &v1.ObjectReference{
20+
ObjectType: "user",
21+
ObjectId: "*",
22+
},
23+
},
24+
OptionalCaveat: nil,
25+
}
26+
27+
fmt.Println(tuple.MustRelString(&rel))
28+
29+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
type Obj{{ capitalize .Name }} struct {
2+
Obj *v1.ObjectReference
3+
*Relationships
4+
}
5+
6+
func {{ capitalize .Name }}(id string) *Obj{{ capitalize .Name }} {
7+
o := &Obj{{ capitalize .Name }}{
8+
Obj: &v1.ObjectReference{
9+
ObjectType: "{{ .Name }}",
10+
ObjectId: id,
11+
},
12+
Relationships: NewRelationships(),
13+
}
14+
allObjects = append(allObjects, o)
15+
return o
16+
}
17+
18+
{{ $outerName := .Name }}
19+
func (obj *Obj{{ capitalize $outerName }}) Type() string {
20+
return "{{ .Name }}"
21+
}
22+
23+
24+
{{ range $index, $element := .DirectRelations }}
25+
{{ if eq $element.Subject.Object.ObjectId "*" }}
26+
func (obj *Obj{{ capitalize $outerName }}) {{ capitalize $element.RelationName }}Wildcard() *Obj{{ capitalize $outerName }}{
27+
obj.Add(v1.Relationship{
28+
Resource: obj.Obj,
29+
Relation: "{{ $element.RelationName }}",
30+
Subject: &v1.SubjectReference{
31+
Object: &v1.ObjectReference {
32+
ObjectType: "{{ $element.Subject.Object.ObjectType }}",
33+
ObjectId: "*",
34+
},
35+
OptionalRelation: "{{ $element.Subject.OptionalRelation }}",
36+
},
37+
OptionalCaveat: nil,
38+
})
39+
return obj
40+
}
41+
42+
{{ else }}
43+
44+
func (obj *Obj{{ capitalize $outerName }}) {{ capitalize $element.FunctionName }}(subs ...*Obj{{ capitalize $element.Subject.Object.ObjectType }}) *Obj{{ capitalize $outerName }}{
45+
for i := range subs {
46+
sub := subs[i]
47+
obj.Add(v1.Relationship{
48+
Resource: obj.Obj,
49+
Relation: "{{ $element.RelationName }}",
50+
Subject: &v1.SubjectReference{
51+
Object: sub.Obj,
52+
OptionalRelation: "{{ $element.Subject.OptionalRelation }}",
53+
},
54+
OptionalCaveat: nil,
55+
})
56+
}
57+
return obj
58+
}
59+
{{ end }}
60+
61+
{{ end }}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package relationships
2+
3+
import (
4+
"fmt"
5+
"sort"
6+
"strings"
7+
8+
v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
9+
"github.com/authzed/spicedb/pkg/tuple"
10+
)
11+
12+
func NewRelationships() *Relationships {
13+
return &Relationships{
14+
Relations: []v1.Relationship{},
15+
}
16+
}
17+
18+
type Relationships struct {
19+
Relations []v1.Relationship
20+
}
21+
22+
func (r *Relationships) AddRelation(relationship v1.Relationship) {
23+
r.Relations = append(r.Relations, relationship)
24+
}
25+
26+
//func (r *Relationships)
27+
28+
func (r Relationships) AllRelations() []v1.Relationship {
29+
return r.Relations
30+
}
31+
32+
type ObjectWithRelationships interface {
33+
AllRelations() []v1.Relationship
34+
Type() string
35+
}
36+
37+
var allObjects []ObjectWithRelationships
38+
39+
func AllRelationsToStrings() string {
40+
// group all the objects
41+
buckets := make(map[string][]ObjectWithRelationships)
42+
bucketKeys := make([]string, 0)
43+
for _, o := range allObjects {
44+
o := o
45+
if _, ok := buckets[o.Type()]; !ok {
46+
bucketKeys = append(bucketKeys, o.Type())
47+
}
48+
buckets[o.Type()] = append(buckets[o.Type()], o)
49+
}
50+
51+
var allStrings []string
52+
53+
sort.Strings(bucketKeys)
54+
for _, bucketKey := range bucketKeys {
55+
bucket := buckets[bucketKey]
56+
allStrings = append(allStrings, "// All "+bucket[0].Type()+"s")
57+
for _, o := range bucket {
58+
for _, r := range o.AllRelations() {
59+
r := r
60+
rStr, err := tuple.StringRelationship(&r)
61+
if err != nil {
62+
panic(err)
63+
}
64+
allStrings = append(allStrings, rStr)
65+
}
66+
}
67+
}
68+
return strings.Join(allStrings, "\n")
69+
}
70+
71+
func WorkspaceWithDeps(id string) *ObjWorkspace {
72+
workspace := Workspace(id)
73+
build := Workspace_build(fmt.Sprintf("%s/build", id)).
74+
Workspace(workspace)
75+
agent := Workspace_agent(fmt.Sprintf("%s/agent", id)).
76+
Workspace(workspace)
77+
app := Worspace_app(fmt.Sprintf("%s/app", id)).
78+
Workspace(workspace)
79+
resources := Workspace_resources(fmt.Sprintf("%s/resources", id)).
80+
Workspace(workspace)
81+
82+
var _, _, _, _ = build, agent, app, resources
83+
return workspace
84+
}
85+
86+
func (obj *ObjTemplate) Version(id string) *ObjTemplate_version {
87+
// This "/" syntax is not required. We usually use uuids, but strings
88+
// are easier to read, and this helps us intuitively see relations.
89+
return Template_version(fmt.Sprintf("%s/%s", obj.Obj.ObjectId, id)).
90+
Template(obj)
91+
}

0 commit comments

Comments
 (0)