Skip to content

Commit b27a3bc

Browse files
authored
feat: support direct assignments in graph (#308)
2 parents 8c12ed5 + 6107b63 commit b27a3bc

File tree

4 files changed

+143
-14
lines changed

4 files changed

+143
-14
lines changed

pkg/go/.golangci.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,7 @@ linters-settings:
6767

6868
issues:
6969
exclude-use-default: true
70+
exclude-rules:
71+
- path: _test.go
72+
linters:
73+
- funlen

pkg/go/Makefile

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
BINARY_NAME=openfga-language
22
DOCKER_BINARY=docker
33
GO_BIN ?= $(shell go env GOPATH)/bin
4+
.DEFAULT_GOAL := help
5+
6+
help: ## Show this help
7+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
8+
49

510
#-----------------------------------------------------------------------------------------------------------------------
611
# Rules (https://www.gnu.org/software/make/manual/html_node/Rule-Introduction.html#Rule-Introduction)
@@ -28,8 +33,8 @@ build:
2833
clean: ## Clean project files
2934
go clean
3035

31-
test: ## Run tests
32-
go test ./... -count=1
36+
test: ## Run Go tests
37+
go test ./... -count=1 -race
3338

3439
lint: $(GO_BIN)/golangci-lint ## Lint Go source files
3540
@echo "==> Linting Go source files"
@@ -43,4 +48,4 @@ format: $(GO_BIN)/gofumpt ## Format Go source files
4348
@echo "==> Formatting project files"
4449
gofumpt -w transformer/ errors/
4550

46-
all-tests: build audit lint test
51+
all-tests: build audit lint test ## Run linting and tests

pkg/go/graph/graph_builder.go

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,7 @@ func parseModel(model *openfgav1.AuthorizationModel) (*multi.DirectedGraph, erro
6161

6262
rewrite := typeDef.GetRelations()[relation]
6363
if _, ok := rewrite.GetUserset().(*openfgav1.Userset_This); ok {
64-
directlyRelated := make([]*openfgav1.RelationReference, 0)
65-
if metadata, ok := typeDef.GetMetadata().GetRelations()[relation]; ok {
66-
directlyRelated = metadata.GetDirectlyRelatedUserTypes()
67-
}
68-
69-
for _, directlyRelatedDef := range directlyRelated {
70-
assignableType := directlyRelatedDef.GetType()
71-
72-
newNode := graphBuilder.GetOrAddNode(assignableType, assignableType, SpecificType)
73-
graphBuilder.AddEdge(newNode, relationNode, DirectEdge)
74-
}
64+
parseThis(typeDef, relation, graphBuilder, relationNode)
7565
}
7666
}
7767
}
@@ -84,6 +74,36 @@ func parseModel(model *openfgav1.AuthorizationModel) (*multi.DirectedGraph, erro
8474
return nil, fmt.Errorf("%w: could not cast to directed graph", ErrBuildingGraph)
8575
}
8676

77+
func parseThis(typeDef *openfgav1.TypeDefinition, relation string, graphBuilder *AuthorizationModelGraphBuilder, relationNode *AuthorizationModelNode) {
78+
directlyRelated := make([]*openfgav1.RelationReference, 0)
79+
if relationMetadata, ok := typeDef.GetMetadata().GetRelations()[relation]; ok {
80+
directlyRelated = relationMetadata.GetDirectlyRelatedUserTypes()
81+
}
82+
83+
for _, directlyRelatedDef := range directlyRelated {
84+
if directlyRelatedDef.GetRelationOrWildcard() == nil {
85+
// direct assignment to concrete type
86+
assignableType := directlyRelatedDef.GetType()
87+
newNode := graphBuilder.GetOrAddNode(assignableType, assignableType, SpecificType)
88+
graphBuilder.AddEdge(newNode, relationNode, DirectEdge)
89+
}
90+
91+
if directlyRelatedDef.GetWildcard() != nil {
92+
// direct assignment to wildcard
93+
assignableWildcard := directlyRelatedDef.GetType() + ":*"
94+
newNode := graphBuilder.GetOrAddNode(assignableWildcard, assignableWildcard, SpecificType)
95+
graphBuilder.AddEdge(newNode, relationNode, DirectEdge)
96+
}
97+
98+
if directlyRelatedDef.GetRelation() != "" {
99+
// direct assignment to userset
100+
assignableUserset := directlyRelatedDef.GetType() + "#" + directlyRelatedDef.GetRelation()
101+
newNode := graphBuilder.GetOrAddNode(assignableUserset, assignableUserset, SpecificTypeAndRelation)
102+
graphBuilder.AddEdge(newNode, relationNode, DirectEdge)
103+
}
104+
}
105+
}
106+
87107
func (g *AuthorizationModelGraphBuilder) GetOrAddNode(uniqueLabel, label string, nodeType NodeType) *AuthorizationModelNode {
88108
if existingNode := g.GetNodeFor(uniqueLabel); existingNode != nil {
89109
return existingNode

pkg/go/graph/graph_builder_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,106 @@ rankdir=BT
3737
1 [label="folder#viewer"];
3838
2 [label=user];
3939
40+
// Edge definitions.
41+
2 -> 1 [label=direct];
42+
}`,
43+
},
44+
`direct_assignment_with_wildcard`: {
45+
model: `
46+
model
47+
schema 1.1
48+
type folder
49+
relations
50+
define viewer: [user:*]
51+
type user`,
52+
expectedOutput: `digraph {
53+
graph [
54+
rankdir=BT
55+
];
56+
57+
// Node definitions.
58+
0 [label=folder];
59+
1 [label="folder#viewer"];
60+
2 [label="user:*"];
61+
3 [label=user];
62+
63+
// Edge definitions.
64+
2 -> 1 [label=direct];
65+
}`,
66+
},
67+
`direct_assignment_with_wildcard_and_type`: {
68+
model: `
69+
model
70+
schema 1.1
71+
type folder
72+
relations
73+
define viewer: [user:*, user]
74+
type user`,
75+
expectedOutput: `digraph {
76+
graph [
77+
rankdir=BT
78+
];
79+
80+
// Node definitions.
81+
0 [label=folder];
82+
1 [label="folder#viewer"];
83+
2 [label="user:*"];
84+
3 [label=user];
85+
86+
// Edge definitions.
87+
2 -> 1 [label=direct];
88+
3 -> 1 [label=direct];
89+
}`,
90+
},
91+
`direct_assignment_with_usersets`: {
92+
model: `
93+
model
94+
schema 1.1
95+
type folder
96+
relations
97+
define viewer: [group#member]
98+
type group
99+
relations
100+
define member: [user]
101+
type user`,
102+
expectedOutput: `digraph {
103+
graph [
104+
rankdir=BT
105+
];
106+
107+
// Node definitions.
108+
0 [label=folder];
109+
1 [label="folder#viewer"];
110+
2 [label="group#member"];
111+
3 [label=group];
112+
4 [label=user];
113+
114+
// Edge definitions.
115+
2 -> 1 [label=direct];
116+
4 -> 2 [label=direct];
117+
}`,
118+
},
119+
`direct_assignment_with_conditions`: { // conditions are not represented
120+
model: `
121+
model
122+
schema 1.1
123+
type user
124+
type folder
125+
relations
126+
define viewer: [user with condX]
127+
condition condX (x:int) {
128+
x > 0
129+
}`,
130+
expectedOutput: `digraph {
131+
graph [
132+
rankdir=BT
133+
];
134+
135+
// Node definitions.
136+
0 [label=folder];
137+
1 [label="folder#viewer"];
138+
2 [label=user];
139+
40140
// Edge definitions.
41141
2 -> 1 [label=direct];
42142
}`,

0 commit comments

Comments
 (0)