From 75ae3074dc963039971dfb1d917acdc6444b1d86 Mon Sep 17 00:00:00 2001 From: Patrik Date: Wed, 8 Mar 2023 16:08:04 +0100 Subject: [PATCH] feat(parser): allow quoted property access (#1273) --- ...suite=snapshots-quoted_property_names.json | 47 +++++++++++++++++++ internal/schema/parser.go | 22 +++++++-- internal/schema/parser_test.go | 12 ++++- 3 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 internal/schema/.snapshots/TestParser-suite=snapshots-quoted_property_names.json diff --git a/internal/schema/.snapshots/TestParser-suite=snapshots-quoted_property_names.json b/internal/schema/.snapshots/TestParser-suite=snapshots-quoted_property_names.json new file mode 100644 index 000000000..741ea204b --- /dev/null +++ b/internal/schema/.snapshots/TestParser-suite=snapshots-quoted_property_names.json @@ -0,0 +1,47 @@ +{ + "Resource": [ + { + "name": "scope.relation", + "types": [ + { + "namespace": "Resource" + } + ] + }, + { + "name": "scope.action_0", + "rewrite": { + "operator": "or", + "children": [ + { + "relation": "scope.relation", + "computed_subject_set_relation": "scope.action_1" + } + ] + } + }, + { + "name": "scope.action_1", + "rewrite": { + "operator": "or", + "children": [ + { + "relation": "scope.relation", + "computed_subject_set_relation": "scope.relation" + } + ] + } + }, + { + "name": "scope.action_2", + "rewrite": { + "operator": "or", + "children": [ + { + "relation": "scope.action_0" + } + ] + } + } + ] +} diff --git a/internal/schema/parser.go b/internal/schema/parser.go index 06a74785e..36e8dcd04 100644 --- a/internal/schema/parser.go +++ b/internal/schema/parser.go @@ -141,6 +141,8 @@ func (p *parser) match(tokens ...interface{}) (matched bool) { if !token(p) { return false } + default: + panic(fmt.Sprintf("unexpected token type %T", token)) } } return true @@ -414,10 +416,17 @@ func setOperation(typ itemType) ast.Operator { panic("not reached") } +func (p *parser) matchPropertyAccess(propertyName any) bool { + return p.matchIf(is(itemBracketLeft), "[", propertyName, "]") || p.match(".", propertyName) +} + func (p *parser) parsePermissionExpression() (child ast.Child) { var name, verb item - if !p.match("this", ".", &verb, ".", &name) { + if !p.match("this", ".", &verb) { + return + } + if !p.matchPropertyAccess(&name) { return } @@ -444,7 +453,6 @@ func (p *parser) parsePermissionExpression() (child ast.Child) { default: p.addFatal(verb, "expected 'related' or 'permits', got %q", verb.Val) - } return @@ -469,15 +477,21 @@ func (p *parser) parseTupleToSubjectSet(relation item) (rewrite ast.Child) { switch verb.Val { case "related": + if !p.matchPropertyAccess(&subjectSetRel) { + return nil + } p.match( - ".", &subjectSetRel, ".", "includes", "(", "ctx", ".", "subject", + ".", "includes", "(", "ctx", ".", "subject", optional(","), ")", optional(","), ")", ) p.addCheck(checkAllRelationsTypesHaveRelation( &p.namespace, relation, subjectSetRel, )) case "permits": - p.match(".", &subjectSetRel, "(", "ctx", ")", ")") + if !p.matchPropertyAccess(&subjectSetRel) { + return nil + } + p.match("(", "ctx", ")", ")") p.addCheck(checkAllRelationsTypesHaveRelation( &p.namespace, relation, subjectSetRel, )) diff --git a/internal/schema/parser_test.go b/internal/schema/parser_test.go index 1ea6eceab..4a21d8d92 100644 --- a/internal/schema/parser_test.go +++ b/internal/schema/parser_test.go @@ -157,7 +157,17 @@ class Resource implements Namespace { this.related.supervisors.traverse((role) => role.related.member.includes(ctx.subject)), }; } -`}, +`}, {"quoted property names", ` +class Resource implements Namespace { + related: { + "scope.relation": Resource[] + } + permits = { + "scope.action_0": (ctx: Context) => this.related["scope.relation"].traverse((r) => r.permits["scope.action_1"](ctx)), + "scope.action_1": (ctx: Context) => this.related["scope.relation"].traverse((r) => r.related["scope.relation"].includes(ctx.subject)), + "scope.action_2": (ctx: Context) => this.permits["scope.action_0"](ctx), + } +}`}, } func TestParser(t *testing.T) {