From ff3289a061460e6792d7650a776289dbdf52ac72 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Tue, 11 Jun 2024 20:34:09 -0400 Subject: [PATCH 1/3] Add support for new syntax to schema parser --- pkg/schemadsl/dslshape/dslshape.go | 7 ++ pkg/schemadsl/lexer/lex_test.go | 10 ++ pkg/schemadsl/parser/parser.go | 37 ++++++- pkg/schemadsl/parser/parser_impl.go | 19 ++++ pkg/schemadsl/parser/parser_test.go | 4 + .../parser/tests/arrowillegalfunc.zed | 3 + .../tests/arrowillegalfunc.zed.expected | 47 ++++++++ .../parser/tests/arrowillegalops.zed | 3 + .../parser/tests/arrowillegalops.zed.expected | 41 +++++++ pkg/schemadsl/parser/tests/arrowops.zed | 4 + .../parser/tests/arrowops.zed.expected | 103 ++++++++++++++++++ .../parser/tests/caveatwithkeywordparam.zed | 3 + .../tests/caveatwithkeywordparam.zed.expected | 52 +++++++++ 13 files changed, 331 insertions(+), 2 deletions(-) create mode 100644 pkg/schemadsl/parser/tests/arrowillegalfunc.zed create mode 100644 pkg/schemadsl/parser/tests/arrowillegalfunc.zed.expected create mode 100644 pkg/schemadsl/parser/tests/arrowillegalops.zed create mode 100644 pkg/schemadsl/parser/tests/arrowillegalops.zed.expected create mode 100644 pkg/schemadsl/parser/tests/arrowops.zed create mode 100644 pkg/schemadsl/parser/tests/arrowops.zed.expected create mode 100644 pkg/schemadsl/parser/tests/caveatwithkeywordparam.zed create mode 100644 pkg/schemadsl/parser/tests/caveatwithkeywordparam.zed.expected diff --git a/pkg/schemadsl/dslshape/dslshape.go b/pkg/schemadsl/dslshape/dslshape.go index c6dc850a67..2421f59690 100644 --- a/pkg/schemadsl/dslshape/dslshape.go +++ b/pkg/schemadsl/dslshape/dslshape.go @@ -169,6 +169,13 @@ const ( // The expression to compute the permission. NodePermissionPredicateComputeExpression = "compute-expression" + // + // NodeTypeArrowExpression + // + + // The name of the function in the arrow expression. + NodeArrowExpressionFunctionName = "function-name" + // // NodeTypeIdentifer // diff --git a/pkg/schemadsl/lexer/lex_test.go b/pkg/schemadsl/lexer/lex_test.go index c310e74083..a223cc2253 100644 --- a/pkg/schemadsl/lexer/lex_test.go +++ b/pkg/schemadsl/lexer/lex_test.go @@ -247,6 +247,16 @@ var lexerTests = []lexerTest{ }, }, }, + + {"dot access", "foo.all(something)", []Lexeme{ + {TokenTypeIdentifier, 0, "foo", ""}, + {TokenTypePeriod, 0, ".", ""}, + {TokenTypeIdentifier, 0, "all", ""}, + {TokenTypeLeftParen, 0, "(", ""}, + {TokenTypeIdentifier, 0, "something", ""}, + {TokenTypeRightParen, 0, ")", ""}, + tEOF, + }}, } func TestLexer(t *testing.T) { diff --git a/pkg/schemadsl/parser/parser.go b/pkg/schemadsl/parser/parser.go index b65eeba41d..6806420bbe 100644 --- a/pkg/schemadsl/parser/parser.go +++ b/pkg/schemadsl/parser/parser.go @@ -478,7 +478,40 @@ func (p *sourceParser) tryConsumeComputeExpression(subTryExprFn tryParserFn, bin // ```foo->bar->baz->meh``` func (p *sourceParser) tryConsumeArrowExpression() (AstNode, bool) { rightNodeBuilder := func(leftNode AstNode, operatorToken lexer.Lexeme) (AstNode, bool) { - rightNode, ok := p.tryConsumeBaseExpression() + // Check for an arrow function. + if operatorToken.Kind == lexer.TokenTypePeriod { + functionName, ok := p.consumeIdentifier() + if !ok { + return nil, false + } + + // TODO(jschorr): Change to keywords in schema v2. + if functionName != "any" && functionName != "all" { + p.emitErrorf("Expected 'any' or 'all' for arrow function, found: %s", functionName) + return nil, false + } + + if _, ok := p.consume(lexer.TokenTypeLeftParen); !ok { + return nil, false + } + + rightNode, ok := p.tryConsumeIdentifierLiteral() + if !ok { + return nil, false + } + + if _, ok := p.consume(lexer.TokenTypeRightParen); !ok { + return nil, false + } + + exprNode := p.createNode(dslshape.NodeTypeArrowExpression) + exprNode.Connect(dslshape.NodeExpressionPredicateLeftExpr, leftNode) + exprNode.Connect(dslshape.NodeExpressionPredicateRightExpr, rightNode) + exprNode.MustDecorate(dslshape.NodeArrowExpressionFunctionName, functionName) + return exprNode, true + } + + rightNode, ok := p.tryConsumeIdentifierLiteral() if !ok { return nil, false } @@ -489,7 +522,7 @@ func (p *sourceParser) tryConsumeArrowExpression() (AstNode, bool) { exprNode.Connect(dslshape.NodeExpressionPredicateRightExpr, rightNode) return exprNode, true } - return p.performLeftRecursiveParsing(p.tryConsumeIdentifierLiteral, rightNodeBuilder, nil, lexer.TokenTypeRightArrow) + return p.performLeftRecursiveParsing(p.tryConsumeIdentifierLiteral, rightNodeBuilder, nil, lexer.TokenTypeRightArrow, lexer.TokenTypePeriod) } // tryConsumeBaseExpression attempts to consume base compute expressions (identifiers, parenthesis). diff --git a/pkg/schemadsl/parser/parser_impl.go b/pkg/schemadsl/parser/parser_impl.go index 76f2bf3839..318e2be7d6 100644 --- a/pkg/schemadsl/parser/parser_impl.go +++ b/pkg/schemadsl/parser/parser_impl.go @@ -187,6 +187,25 @@ func (p *sourceParser) consumeKeyword(keyword string) bool { return true } +// consumeKeywords consumes an expected keyword token(s) or adds an error node. +func (p *sourceParser) consumeKeywords(keywords ...string) (string, bool) { + keyword, ok := p.tryConsumeKeywords(keywords...) + if !ok { + p.emitErrorf("Expected one of: %v, found: %v", keywords, p.currentToken.Kind) + } + return keyword, ok +} + +// tryConsumeKeywords consumes an expected keyword token(s) or adds an error node. +func (p *sourceParser) tryConsumeKeywords(keywords ...string) (string, bool) { + for _, keyword := range keywords { + if p.tryConsumeKeyword(keyword) { + return keyword, true + } + } + return "", false +} + // tryConsumeKeyword attempts to consume an expected keyword token. func (p *sourceParser) tryConsumeKeyword(keyword string) bool { if !p.isKeyword(keyword) { diff --git a/pkg/schemadsl/parser/parser_test.go b/pkg/schemadsl/parser/parser_test.go index af64f26014..453f89c0c9 100644 --- a/pkg/schemadsl/parser/parser_test.go +++ b/pkg/schemadsl/parser/parser_test.go @@ -118,6 +118,10 @@ func TestParser(t *testing.T) { {"super large test", "superlarge"}, {"invalid permission name test", "invalid_perm_name"}, {"union positions test", "unionpos"}, + {"arrow operations test", "arrowops"}, + {"arrow illegal operations test", "arrowillegalops"}, + {"arrow illegal function test", "arrowillegalfunc"}, + {"caveat with keyword parameter test", "caveatwithkeywordparam"}, } for _, test := range parserTests { diff --git a/pkg/schemadsl/parser/tests/arrowillegalfunc.zed b/pkg/schemadsl/parser/tests/arrowillegalfunc.zed new file mode 100644 index 0000000000..f86b51a905 --- /dev/null +++ b/pkg/schemadsl/parser/tests/arrowillegalfunc.zed @@ -0,0 +1,3 @@ +definition resource { + permission view = a.foo(bar) +} \ No newline at end of file diff --git a/pkg/schemadsl/parser/tests/arrowillegalfunc.zed.expected b/pkg/schemadsl/parser/tests/arrowillegalfunc.zed.expected new file mode 100644 index 0000000000..803106a8e2 --- /dev/null +++ b/pkg/schemadsl/parser/tests/arrowillegalfunc.zed.expected @@ -0,0 +1,47 @@ +NodeTypeFile + end-rune = 48 + input-source = arrow illegal function test + start-rune = 0 + child-node => + NodeTypeDefinition + definition-name = resource + end-rune = 48 + input-source = arrow illegal function test + start-rune = 0 + child-node => + NodeTypePermission + end-rune = 48 + input-source = arrow illegal function test + relation-name = view + start-rune = 26 + child-node => + NodeTypeError + end-rune = 48 + error-message = Expected 'any' or 'all' for arrow function, found: foo + error-source = ( + input-source = arrow illegal function test + start-rune = 49 + NodeTypeError + end-rune = 48 + error-message = Expected right hand expression, found: TokenTypeLeftParen + error-source = ( + input-source = arrow illegal function test + start-rune = 49 + compute-expression => + NodeTypeIdentifier + end-rune = 44 + identifier-value = a + input-source = arrow illegal function test + start-rune = 44 + NodeTypeError + end-rune = 48 + error-message = Expected end of statement or definition, found: TokenTypeLeftParen + error-source = ( + input-source = arrow illegal function test + start-rune = 49 + NodeTypeError + end-rune = 48 + error-message = Unexpected token at root level: TokenTypeLeftParen + error-source = ( + input-source = arrow illegal function test + start-rune = 49 \ No newline at end of file diff --git a/pkg/schemadsl/parser/tests/arrowillegalops.zed b/pkg/schemadsl/parser/tests/arrowillegalops.zed new file mode 100644 index 0000000000..7cc9c91284 --- /dev/null +++ b/pkg/schemadsl/parser/tests/arrowillegalops.zed @@ -0,0 +1,3 @@ +definition resource { + permission view = a->(b) +} \ No newline at end of file diff --git a/pkg/schemadsl/parser/tests/arrowillegalops.zed.expected b/pkg/schemadsl/parser/tests/arrowillegalops.zed.expected new file mode 100644 index 0000000000..155b2210fb --- /dev/null +++ b/pkg/schemadsl/parser/tests/arrowillegalops.zed.expected @@ -0,0 +1,41 @@ +NodeTypeFile + end-rune = 46 + input-source = arrow illegal operations test + start-rune = 0 + child-node => + NodeTypeDefinition + definition-name = resource + end-rune = 46 + input-source = arrow illegal operations test + start-rune = 0 + child-node => + NodeTypePermission + end-rune = 46 + input-source = arrow illegal operations test + relation-name = view + start-rune = 26 + child-node => + NodeTypeError + end-rune = 46 + error-message = Expected right hand expression, found: TokenTypeLeftParen + error-source = ( + input-source = arrow illegal operations test + start-rune = 47 + compute-expression => + NodeTypeIdentifier + end-rune = 44 + identifier-value = a + input-source = arrow illegal operations test + start-rune = 44 + NodeTypeError + end-rune = 46 + error-message = Expected end of statement or definition, found: TokenTypeLeftParen + error-source = ( + input-source = arrow illegal operations test + start-rune = 47 + NodeTypeError + end-rune = 46 + error-message = Unexpected token at root level: TokenTypeLeftParen + error-source = ( + input-source = arrow illegal operations test + start-rune = 47 \ No newline at end of file diff --git a/pkg/schemadsl/parser/tests/arrowops.zed b/pkg/schemadsl/parser/tests/arrowops.zed new file mode 100644 index 0000000000..1d048a0018 --- /dev/null +++ b/pkg/schemadsl/parser/tests/arrowops.zed @@ -0,0 +1,4 @@ +definition resource { + permission view = parent1.any(member1) + parent2.all(member2) + permission chained = parent1.any(member1).all(member2)->member3 +} \ No newline at end of file diff --git a/pkg/schemadsl/parser/tests/arrowops.zed.expected b/pkg/schemadsl/parser/tests/arrowops.zed.expected new file mode 100644 index 0000000000..96e5ee7de5 --- /dev/null +++ b/pkg/schemadsl/parser/tests/arrowops.zed.expected @@ -0,0 +1,103 @@ +NodeTypeFile + end-rune = 156 + input-source = arrow operations test + start-rune = 0 + child-node => + NodeTypeDefinition + definition-name = resource + end-rune = 156 + input-source = arrow operations test + start-rune = 0 + child-node => + NodeTypePermission + end-rune = 86 + input-source = arrow operations test + relation-name = view + start-rune = 26 + compute-expression => + NodeTypeUnionExpression + end-rune = 86 + input-source = arrow operations test + start-rune = 44 + left-expr => + NodeTypeArrowExpression + end-rune = 63 + function-name = any + input-source = arrow operations test + start-rune = 44 + left-expr => + NodeTypeIdentifier + end-rune = 50 + identifier-value = parent1 + input-source = arrow operations test + start-rune = 44 + right-expr => + NodeTypeIdentifier + end-rune = 62 + identifier-value = member1 + input-source = arrow operations test + start-rune = 56 + right-expr => + NodeTypeArrowExpression + end-rune = 86 + function-name = all + input-source = arrow operations test + start-rune = 67 + left-expr => + NodeTypeIdentifier + end-rune = 73 + identifier-value = parent2 + input-source = arrow operations test + start-rune = 67 + right-expr => + NodeTypeIdentifier + end-rune = 85 + identifier-value = member2 + input-source = arrow operations test + start-rune = 79 + NodeTypePermission + end-rune = 154 + input-source = arrow operations test + relation-name = chained + start-rune = 92 + compute-expression => + NodeTypeArrowExpression + end-rune = 154 + input-source = arrow operations test + start-rune = 113 + left-expr => + NodeTypeArrowExpression + end-rune = 145 + function-name = all + input-source = arrow operations test + start-rune = 113 + left-expr => + NodeTypeArrowExpression + end-rune = 132 + function-name = any + input-source = arrow operations test + start-rune = 113 + left-expr => + NodeTypeIdentifier + end-rune = 119 + identifier-value = parent1 + input-source = arrow operations test + start-rune = 113 + right-expr => + NodeTypeIdentifier + end-rune = 131 + identifier-value = member1 + input-source = arrow operations test + start-rune = 125 + right-expr => + NodeTypeIdentifier + end-rune = 144 + identifier-value = member2 + input-source = arrow operations test + start-rune = 138 + right-expr => + NodeTypeIdentifier + end-rune = 154 + identifier-value = member3 + input-source = arrow operations test + start-rune = 148 \ No newline at end of file diff --git a/pkg/schemadsl/parser/tests/caveatwithkeywordparam.zed b/pkg/schemadsl/parser/tests/caveatwithkeywordparam.zed new file mode 100644 index 0000000000..95b8683eef --- /dev/null +++ b/pkg/schemadsl/parser/tests/caveatwithkeywordparam.zed @@ -0,0 +1,3 @@ +caveat something(someMap map, anotherMap map) { + someMap.isSubtreeOf(anotherMap) +} \ No newline at end of file diff --git a/pkg/schemadsl/parser/tests/caveatwithkeywordparam.zed.expected b/pkg/schemadsl/parser/tests/caveatwithkeywordparam.zed.expected new file mode 100644 index 0000000000..6defad57c2 --- /dev/null +++ b/pkg/schemadsl/parser/tests/caveatwithkeywordparam.zed.expected @@ -0,0 +1,52 @@ +NodeTypeFile + end-rune = 94 + input-source = caveat with keyword parameter test + start-rune = 0 + child-node => + NodeTypeCaveatDefinition + caveat-definition-name = something + end-rune = 94 + input-source = caveat with keyword parameter test + start-rune = 0 + caveat-definition-expression => + NodeTypeCaveatExpression + caveat-expression-expressionstr = someMap.isSubtreeOf(anotherMap) + + end-rune = 93 + input-source = caveat with keyword parameter test + start-rune = 62 + parameters => + NodeTypeCaveatParameter + caveat-parameter-name = someMap + end-rune = 32 + input-source = caveat with keyword parameter test + start-rune = 17 + caveat-parameter-type => + NodeTypeCaveatTypeReference + end-rune = 32 + input-source = caveat with keyword parameter test + start-rune = 25 + type-name = map + child-types => + NodeTypeCaveatTypeReference + end-rune = 31 + input-source = caveat with keyword parameter test + start-rune = 29 + type-name = any + NodeTypeCaveatParameter + caveat-parameter-name = anotherMap + end-rune = 53 + input-source = caveat with keyword parameter test + start-rune = 35 + caveat-parameter-type => + NodeTypeCaveatTypeReference + end-rune = 53 + input-source = caveat with keyword parameter test + start-rune = 46 + type-name = map + child-types => + NodeTypeCaveatTypeReference + end-rune = 52 + input-source = caveat with keyword parameter test + start-rune = 50 + type-name = any \ No newline at end of file From 7aa323252141bf149cc587ee8e5ff14230195cb0 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 12 Jun 2024 13:55:16 -0400 Subject: [PATCH 2/3] Add translator and generator support for new functioned arrows --- pkg/namespace/builder.go | 33 + pkg/proto/core/v1/core.pb.go | 868 ++++++++++++++-------- pkg/proto/core/v1/core.pb.validate.go | 419 +++++++++++ pkg/proto/core/v1/core_vtproto.pb.go | 608 ++++++++++++++- pkg/schemadsl/compiler/compiler_test.go | 34 + pkg/schemadsl/compiler/translator.go | 9 + pkg/schemadsl/generator/generator.go | 36 +- pkg/schemadsl/generator/generator_test.go | 9 + proto/internal/core/v1/core.proto | 21 + 9 files changed, 1718 insertions(+), 319 deletions(-) diff --git a/pkg/namespace/builder.go b/pkg/namespace/builder.go index 92a0d1afd4..66e4755606 100644 --- a/pkg/namespace/builder.go +++ b/pkg/namespace/builder.go @@ -264,6 +264,39 @@ func TupleToUserset(tuplesetRelation, usersetRelation string) *core.SetOperation } } +// MustFunctionedTupleToUserset creates a child which first loads all tuples with the specific relation, +// and then applies the function to all children on the usersets found by following a relation on those loaded +// tuples. +func MustFunctionedTupleToUserset(tuplesetRelation, functionName, usersetRelation string) *core.SetOperation_Child { + function := core.FunctionedTupleToUserset_FUNCTION_ANY + + switch functionName { + case "any": + function = core.FunctionedTupleToUserset_FUNCTION_ANY + + case "all": + function = core.FunctionedTupleToUserset_FUNCTION_ALL + + default: + panic(spiceerrors.MustBugf("unknown function name: %s", functionName)) + } + + return &core.SetOperation_Child{ + ChildType: &core.SetOperation_Child_FunctionedTupleToUserset{ + FunctionedTupleToUserset: &core.FunctionedTupleToUserset{ + Function: function, + Tupleset: &core.FunctionedTupleToUserset_Tupleset{ + Relation: tuplesetRelation, + }, + ComputedUserset: &core.ComputedUserset{ + Relation: usersetRelation, + Object: core.ComputedUserset_TUPLE_USERSET_OBJECT, + }, + }, + }, + } +} + // Rewrite wraps a rewrite as a set operation child of another rewrite. func Rewrite(rewrite *core.UsersetRewrite) *core.SetOperation_Child { return &core.SetOperation_Child{ diff --git a/pkg/proto/core/v1/core.pb.go b/pkg/proto/core/v1/core.pb.go index b22109b804..2ea1d3550c 100644 --- a/pkg/proto/core/v1/core.pb.go +++ b/pkg/proto/core/v1/core.pb.go @@ -238,6 +238,55 @@ func (ReachabilityEntrypoint_EntrypointResultStatus) EnumDescriptor() ([]byte, [ return file_core_v1_core_proto_rawDescGZIP(), []int{17, 1} } +type FunctionedTupleToUserset_Function int32 + +const ( + FunctionedTupleToUserset_FUNCTION_UNSPECIFIED FunctionedTupleToUserset_Function = 0 + FunctionedTupleToUserset_FUNCTION_ANY FunctionedTupleToUserset_Function = 1 + FunctionedTupleToUserset_FUNCTION_ALL FunctionedTupleToUserset_Function = 2 +) + +// Enum value maps for FunctionedTupleToUserset_Function. +var ( + FunctionedTupleToUserset_Function_name = map[int32]string{ + 0: "FUNCTION_UNSPECIFIED", + 1: "FUNCTION_ANY", + 2: "FUNCTION_ALL", + } + FunctionedTupleToUserset_Function_value = map[string]int32{ + "FUNCTION_UNSPECIFIED": 0, + "FUNCTION_ANY": 1, + "FUNCTION_ALL": 2, + } +) + +func (x FunctionedTupleToUserset_Function) Enum() *FunctionedTupleToUserset_Function { + p := new(FunctionedTupleToUserset_Function) + *p = x + return p +} + +func (x FunctionedTupleToUserset_Function) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FunctionedTupleToUserset_Function) Descriptor() protoreflect.EnumDescriptor { + return file_core_v1_core_proto_enumTypes[4].Descriptor() +} + +func (FunctionedTupleToUserset_Function) Type() protoreflect.EnumType { + return &file_core_v1_core_proto_enumTypes[4] +} + +func (x FunctionedTupleToUserset_Function) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use FunctionedTupleToUserset_Function.Descriptor instead. +func (FunctionedTupleToUserset_Function) EnumDescriptor() ([]byte, []int) { + return file_core_v1_core_proto_rawDescGZIP(), []int{24, 0} +} + type ComputedUserset_Object int32 const ( @@ -268,11 +317,11 @@ func (x ComputedUserset_Object) String() string { } func (ComputedUserset_Object) Descriptor() protoreflect.EnumDescriptor { - return file_core_v1_core_proto_enumTypes[4].Descriptor() + return file_core_v1_core_proto_enumTypes[5].Descriptor() } func (ComputedUserset_Object) Type() protoreflect.EnumType { - return &file_core_v1_core_proto_enumTypes[4] + return &file_core_v1_core_proto_enumTypes[5] } func (x ComputedUserset_Object) Number() protoreflect.EnumNumber { @@ -281,7 +330,7 @@ func (x ComputedUserset_Object) Number() protoreflect.EnumNumber { // Deprecated: Use ComputedUserset_Object.Descriptor instead. func (ComputedUserset_Object) EnumDescriptor() ([]byte, []int) { - return file_core_v1_core_proto_rawDescGZIP(), []int{24, 0} + return file_core_v1_core_proto_rawDescGZIP(), []int{25, 0} } type CaveatOperation_Operation int32 @@ -320,11 +369,11 @@ func (x CaveatOperation_Operation) String() string { } func (CaveatOperation_Operation) Descriptor() protoreflect.EnumDescriptor { - return file_core_v1_core_proto_enumTypes[5].Descriptor() + return file_core_v1_core_proto_enumTypes[6].Descriptor() } func (CaveatOperation_Operation) Type() protoreflect.EnumType { - return &file_core_v1_core_proto_enumTypes[5] + return &file_core_v1_core_proto_enumTypes[6] } func (x CaveatOperation_Operation) Number() protoreflect.EnumNumber { @@ -333,7 +382,7 @@ func (x CaveatOperation_Operation) Number() protoreflect.EnumNumber { // Deprecated: Use CaveatOperation_Operation.Descriptor instead. func (CaveatOperation_Operation) EnumDescriptor() ([]byte, []int) { - return file_core_v1_core_proto_rawDescGZIP(), []int{27, 0} + return file_core_v1_core_proto_rawDescGZIP(), []int{28, 0} } type RelationTuple struct { @@ -1992,6 +2041,77 @@ func (x *TupleToUserset) GetSourcePosition() *SourcePosition { return nil } +type FunctionedTupleToUserset struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Function FunctionedTupleToUserset_Function `protobuf:"varint,1,opt,name=function,proto3,enum=core.v1.FunctionedTupleToUserset_Function" json:"function,omitempty"` + Tupleset *FunctionedTupleToUserset_Tupleset `protobuf:"bytes,2,opt,name=tupleset,proto3" json:"tupleset,omitempty"` + ComputedUserset *ComputedUserset `protobuf:"bytes,3,opt,name=computed_userset,json=computedUserset,proto3" json:"computed_userset,omitempty"` + SourcePosition *SourcePosition `protobuf:"bytes,4,opt,name=source_position,json=sourcePosition,proto3" json:"source_position,omitempty"` +} + +func (x *FunctionedTupleToUserset) Reset() { + *x = FunctionedTupleToUserset{} + if protoimpl.UnsafeEnabled { + mi := &file_core_v1_core_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FunctionedTupleToUserset) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FunctionedTupleToUserset) ProtoMessage() {} + +func (x *FunctionedTupleToUserset) ProtoReflect() protoreflect.Message { + mi := &file_core_v1_core_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FunctionedTupleToUserset.ProtoReflect.Descriptor instead. +func (*FunctionedTupleToUserset) Descriptor() ([]byte, []int) { + return file_core_v1_core_proto_rawDescGZIP(), []int{24} +} + +func (x *FunctionedTupleToUserset) GetFunction() FunctionedTupleToUserset_Function { + if x != nil { + return x.Function + } + return FunctionedTupleToUserset_FUNCTION_UNSPECIFIED +} + +func (x *FunctionedTupleToUserset) GetTupleset() *FunctionedTupleToUserset_Tupleset { + if x != nil { + return x.Tupleset + } + return nil +} + +func (x *FunctionedTupleToUserset) GetComputedUserset() *ComputedUserset { + if x != nil { + return x.ComputedUserset + } + return nil +} + +func (x *FunctionedTupleToUserset) GetSourcePosition() *SourcePosition { + if x != nil { + return x.SourcePosition + } + return nil +} + type ComputedUserset struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2005,7 +2125,7 @@ type ComputedUserset struct { func (x *ComputedUserset) Reset() { *x = ComputedUserset{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_core_proto_msgTypes[24] + mi := &file_core_v1_core_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2018,7 +2138,7 @@ func (x *ComputedUserset) String() string { func (*ComputedUserset) ProtoMessage() {} func (x *ComputedUserset) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_core_proto_msgTypes[24] + mi := &file_core_v1_core_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2031,7 +2151,7 @@ func (x *ComputedUserset) ProtoReflect() protoreflect.Message { // Deprecated: Use ComputedUserset.ProtoReflect.Descriptor instead. func (*ComputedUserset) Descriptor() ([]byte, []int) { - return file_core_v1_core_proto_rawDescGZIP(), []int{24} + return file_core_v1_core_proto_rawDescGZIP(), []int{25} } func (x *ComputedUserset) GetObject() ComputedUserset_Object { @@ -2067,7 +2187,7 @@ type SourcePosition struct { func (x *SourcePosition) Reset() { *x = SourcePosition{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_core_proto_msgTypes[25] + mi := &file_core_v1_core_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2080,7 +2200,7 @@ func (x *SourcePosition) String() string { func (*SourcePosition) ProtoMessage() {} func (x *SourcePosition) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_core_proto_msgTypes[25] + mi := &file_core_v1_core_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2093,7 +2213,7 @@ func (x *SourcePosition) ProtoReflect() protoreflect.Message { // Deprecated: Use SourcePosition.ProtoReflect.Descriptor instead. func (*SourcePosition) Descriptor() ([]byte, []int) { - return file_core_v1_core_proto_rawDescGZIP(), []int{25} + return file_core_v1_core_proto_rawDescGZIP(), []int{26} } func (x *SourcePosition) GetZeroIndexedLineNumber() uint64 { @@ -2125,7 +2245,7 @@ type CaveatExpression struct { func (x *CaveatExpression) Reset() { *x = CaveatExpression{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_core_proto_msgTypes[26] + mi := &file_core_v1_core_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2138,7 +2258,7 @@ func (x *CaveatExpression) String() string { func (*CaveatExpression) ProtoMessage() {} func (x *CaveatExpression) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_core_proto_msgTypes[26] + mi := &file_core_v1_core_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2151,7 +2271,7 @@ func (x *CaveatExpression) ProtoReflect() protoreflect.Message { // Deprecated: Use CaveatExpression.ProtoReflect.Descriptor instead. func (*CaveatExpression) Descriptor() ([]byte, []int) { - return file_core_v1_core_proto_rawDescGZIP(), []int{26} + return file_core_v1_core_proto_rawDescGZIP(), []int{27} } func (m *CaveatExpression) GetOperationOrCaveat() isCaveatExpression_OperationOrCaveat { @@ -2203,7 +2323,7 @@ type CaveatOperation struct { func (x *CaveatOperation) Reset() { *x = CaveatOperation{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_core_proto_msgTypes[27] + mi := &file_core_v1_core_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2216,7 +2336,7 @@ func (x *CaveatOperation) String() string { func (*CaveatOperation) ProtoMessage() {} func (x *CaveatOperation) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_core_proto_msgTypes[27] + mi := &file_core_v1_core_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2229,7 +2349,7 @@ func (x *CaveatOperation) ProtoReflect() protoreflect.Message { // Deprecated: Use CaveatOperation.ProtoReflect.Descriptor instead. func (*CaveatOperation) Descriptor() ([]byte, []int) { - return file_core_v1_core_proto_rawDescGZIP(), []int{27} + return file_core_v1_core_proto_rawDescGZIP(), []int{28} } func (x *CaveatOperation) GetOp() CaveatOperation_Operation { @@ -2269,7 +2389,7 @@ type RelationshipFilter struct { func (x *RelationshipFilter) Reset() { *x = RelationshipFilter{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_core_proto_msgTypes[28] + mi := &file_core_v1_core_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2282,7 +2402,7 @@ func (x *RelationshipFilter) String() string { func (*RelationshipFilter) ProtoMessage() {} func (x *RelationshipFilter) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_core_proto_msgTypes[28] + mi := &file_core_v1_core_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2295,7 +2415,7 @@ func (x *RelationshipFilter) ProtoReflect() protoreflect.Message { // Deprecated: Use RelationshipFilter.ProtoReflect.Descriptor instead. func (*RelationshipFilter) Descriptor() ([]byte, []int) { - return file_core_v1_core_proto_rawDescGZIP(), []int{28} + return file_core_v1_core_proto_rawDescGZIP(), []int{29} } func (x *RelationshipFilter) GetResourceType() string { @@ -2350,7 +2470,7 @@ type SubjectFilter struct { func (x *SubjectFilter) Reset() { *x = SubjectFilter{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_core_proto_msgTypes[29] + mi := &file_core_v1_core_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2363,7 +2483,7 @@ func (x *SubjectFilter) String() string { func (*SubjectFilter) ProtoMessage() {} func (x *SubjectFilter) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_core_proto_msgTypes[29] + mi := &file_core_v1_core_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2376,7 +2496,7 @@ func (x *SubjectFilter) ProtoReflect() protoreflect.Message { // Deprecated: Use SubjectFilter.ProtoReflect.Descriptor instead. func (*SubjectFilter) Descriptor() ([]byte, []int) { - return file_core_v1_core_proto_rawDescGZIP(), []int{29} + return file_core_v1_core_proto_rawDescGZIP(), []int{30} } func (x *SubjectFilter) GetSubjectType() string { @@ -2409,7 +2529,7 @@ type AllowedRelation_PublicWildcard struct { func (x *AllowedRelation_PublicWildcard) Reset() { *x = AllowedRelation_PublicWildcard{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_core_proto_msgTypes[33] + mi := &file_core_v1_core_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2422,7 +2542,7 @@ func (x *AllowedRelation_PublicWildcard) String() string { func (*AllowedRelation_PublicWildcard) ProtoMessage() {} func (x *AllowedRelation_PublicWildcard) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_core_proto_msgTypes[33] + mi := &file_core_v1_core_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2449,6 +2569,7 @@ type SetOperation_Child struct { // *SetOperation_Child_ComputedUserset // *SetOperation_Child_TupleToUserset // *SetOperation_Child_UsersetRewrite + // *SetOperation_Child_FunctionedTupleToUserset // *SetOperation_Child_XNil ChildType isSetOperation_Child_ChildType `protobuf_oneof:"child_type"` SourcePosition *SourcePosition `protobuf:"bytes,5,opt,name=source_position,json=sourcePosition,proto3" json:"source_position,omitempty"` @@ -2463,7 +2584,7 @@ type SetOperation_Child struct { func (x *SetOperation_Child) Reset() { *x = SetOperation_Child{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_core_proto_msgTypes[34] + mi := &file_core_v1_core_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2476,7 +2597,7 @@ func (x *SetOperation_Child) String() string { func (*SetOperation_Child) ProtoMessage() {} func (x *SetOperation_Child) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_core_proto_msgTypes[34] + mi := &file_core_v1_core_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2527,6 +2648,13 @@ func (x *SetOperation_Child) GetUsersetRewrite() *UsersetRewrite { return nil } +func (x *SetOperation_Child) GetFunctionedTupleToUserset() *FunctionedTupleToUserset { + if x, ok := x.GetChildType().(*SetOperation_Child_FunctionedTupleToUserset); ok { + return x.FunctionedTupleToUserset + } + return nil +} + func (x *SetOperation_Child) GetXNil() *SetOperation_Child_Nil { if x, ok := x.GetChildType().(*SetOperation_Child_XNil); ok { return x.XNil @@ -2568,6 +2696,10 @@ type SetOperation_Child_UsersetRewrite struct { UsersetRewrite *UsersetRewrite `protobuf:"bytes,4,opt,name=userset_rewrite,json=usersetRewrite,proto3,oneof"` } +type SetOperation_Child_FunctionedTupleToUserset struct { + FunctionedTupleToUserset *FunctionedTupleToUserset `protobuf:"bytes,8,opt,name=functioned_tuple_to_userset,json=functionedTupleToUserset,proto3,oneof"` +} + type SetOperation_Child_XNil struct { XNil *SetOperation_Child_Nil `protobuf:"bytes,6,opt,name=_nil,json=Nil,proto3,oneof"` } @@ -2580,6 +2712,8 @@ func (*SetOperation_Child_TupleToUserset) isSetOperation_Child_ChildType() {} func (*SetOperation_Child_UsersetRewrite) isSetOperation_Child_ChildType() {} +func (*SetOperation_Child_FunctionedTupleToUserset) isSetOperation_Child_ChildType() {} + func (*SetOperation_Child_XNil) isSetOperation_Child_ChildType() {} type SetOperation_Child_This struct { @@ -2591,7 +2725,7 @@ type SetOperation_Child_This struct { func (x *SetOperation_Child_This) Reset() { *x = SetOperation_Child_This{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_core_proto_msgTypes[35] + mi := &file_core_v1_core_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2604,7 +2738,7 @@ func (x *SetOperation_Child_This) String() string { func (*SetOperation_Child_This) ProtoMessage() {} func (x *SetOperation_Child_This) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_core_proto_msgTypes[35] + mi := &file_core_v1_core_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2629,7 +2763,7 @@ type SetOperation_Child_Nil struct { func (x *SetOperation_Child_Nil) Reset() { *x = SetOperation_Child_Nil{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_core_proto_msgTypes[36] + mi := &file_core_v1_core_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2642,7 +2776,7 @@ func (x *SetOperation_Child_Nil) String() string { func (*SetOperation_Child_Nil) ProtoMessage() {} func (x *SetOperation_Child_Nil) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_core_proto_msgTypes[36] + mi := &file_core_v1_core_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2669,7 +2803,7 @@ type TupleToUserset_Tupleset struct { func (x *TupleToUserset_Tupleset) Reset() { *x = TupleToUserset_Tupleset{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_core_proto_msgTypes[37] + mi := &file_core_v1_core_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2682,7 +2816,7 @@ func (x *TupleToUserset_Tupleset) String() string { func (*TupleToUserset_Tupleset) ProtoMessage() {} func (x *TupleToUserset_Tupleset) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_core_proto_msgTypes[37] + mi := &file_core_v1_core_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2705,6 +2839,53 @@ func (x *TupleToUserset_Tupleset) GetRelation() string { return "" } +type FunctionedTupleToUserset_Tupleset struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Relation string `protobuf:"bytes,1,opt,name=relation,proto3" json:"relation,omitempty"` +} + +func (x *FunctionedTupleToUserset_Tupleset) Reset() { + *x = FunctionedTupleToUserset_Tupleset{} + if protoimpl.UnsafeEnabled { + mi := &file_core_v1_core_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FunctionedTupleToUserset_Tupleset) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FunctionedTupleToUserset_Tupleset) ProtoMessage() {} + +func (x *FunctionedTupleToUserset_Tupleset) ProtoReflect() protoreflect.Message { + mi := &file_core_v1_core_proto_msgTypes[39] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FunctionedTupleToUserset_Tupleset.ProtoReflect.Descriptor instead. +func (*FunctionedTupleToUserset_Tupleset) Descriptor() ([]byte, []int) { + return file_core_v1_core_proto_rawDescGZIP(), []int{24, 0} +} + +func (x *FunctionedTupleToUserset_Tupleset) GetRelation() string { + if x != nil { + return x.Relation + } + return "" +} + type SubjectFilter_RelationFilter struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2716,7 +2897,7 @@ type SubjectFilter_RelationFilter struct { func (x *SubjectFilter_RelationFilter) Reset() { *x = SubjectFilter_RelationFilter{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_core_proto_msgTypes[38] + mi := &file_core_v1_core_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2729,7 +2910,7 @@ func (x *SubjectFilter_RelationFilter) String() string { func (*SubjectFilter_RelationFilter) ProtoMessage() {} func (x *SubjectFilter_RelationFilter) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_core_proto_msgTypes[38] + mi := &file_core_v1_core_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2742,7 +2923,7 @@ func (x *SubjectFilter_RelationFilter) ProtoReflect() protoreflect.Message { // Deprecated: Use SubjectFilter_RelationFilter.ProtoReflect.Descriptor instead. func (*SubjectFilter_RelationFilter) Descriptor() ([]byte, []int) { - return file_core_v1_core_proto_rawDescGZIP(), []int{29, 0} + return file_core_v1_core_proto_rawDescGZIP(), []int{30, 0} } func (x *SubjectFilter_RelationFilter) GetRelation() string { @@ -3107,12 +3288,12 @@ var file_core_v1_core_proto_rawDesc = []byte{ 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x18, 0x0a, 0x11, 0x72, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x22, 0xc4, 0x04, 0x0a, 0x0c, 0x53, 0x65, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x22, 0xb2, 0x05, 0x0a, 0x0c, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x05, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x42, 0x0f, 0xfa, 0x42, 0x0c, 0x92, 0x01, 0x09, 0x08, 0x01, 0x22, - 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x05, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x1a, 0xef, 0x03, + 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x05, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x1a, 0xdd, 0x04, 0x0a, 0x05, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x12, 0x37, 0x0a, 0x05, 0x5f, 0x74, 0x68, 0x69, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x68, @@ -3131,151 +3312,189 @@ var file_core_v1_core_proto_rawDesc = []byte{ 0x69, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x0e, - 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, 0x34, - 0x0a, 0x04, 0x5f, 0x6e, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x2e, 0x4e, 0x69, 0x6c, 0x48, 0x00, 0x52, - 0x03, 0x4e, 0x69, 0x6c, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x1a, 0x06, 0x0a, - 0x04, 0x54, 0x68, 0x69, 0x73, 0x1a, 0x05, 0x0a, 0x03, 0x4e, 0x69, 0x6c, 0x42, 0x11, 0x0a, 0x0a, - 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x22, - 0xba, 0x02, 0x0a, 0x0e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, - 0x65, 0x74, 0x12, 0x46, 0x0a, 0x08, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x2e, 0x54, 0x75, - 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, - 0x52, 0x08, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x12, 0x4d, 0x0a, 0x10, 0x63, 0x6f, - 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x42, 0x08, - 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, - 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x4f, 0x0a, 0x08, 0x54, - 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x12, 0x43, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xfa, 0x42, 0x24, 0x72, 0x22, - 0x28, 0x40, 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, - 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, - 0x5d, 0x24, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x91, 0x02, 0x0a, - 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, - 0x12, 0x41, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, - 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, 0x6f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x12, 0x43, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xfa, 0x42, 0x24, 0x72, 0x22, 0x28, 0x40, 0x32, 0x1e, - 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, - 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x08, - 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x34, 0x0a, 0x06, 0x4f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x55, 0x50, 0x4c, 0x45, 0x5f, 0x4f, 0x42, - 0x4a, 0x45, 0x43, 0x54, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x55, 0x50, 0x4c, 0x45, 0x5f, - 0x55, 0x53, 0x45, 0x52, 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x01, - 0x22, 0x8a, 0x01, 0x0a, 0x0e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x18, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x7a, 0x65, 0x72, 0x6f, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x1c, - 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, - 0x75, 0x6d, 0x6e, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x19, 0x7a, 0x65, 0x72, 0x6f, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x43, - 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x9c, 0x01, - 0x0a, 0x10, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, - 0x00, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x06, - 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, - 0x61, 0x76, 0x65, 0x61, 0x74, 0x42, 0x15, 0x0a, 0x13, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x6f, 0x72, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x22, 0xb0, 0x01, 0x0a, - 0x0f, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x32, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x4f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x02, 0x6f, 0x70, 0x12, 0x35, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, 0x32, 0x0a, 0x09, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, - 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x07, 0x0a, - 0x03, 0x41, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x4e, 0x4f, 0x54, 0x10, 0x03, 0x22, - 0xee, 0x03, 0x0a, 0x12, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x70, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x4b, 0xfa, - 0x42, 0x48, 0x72, 0x46, 0x28, 0x80, 0x01, 0x32, 0x41, 0x5e, 0x28, 0x28, 0x5b, 0x61, 0x2d, 0x7a, - 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x31, 0x7d, - 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, - 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, - 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x57, 0x0a, 0x14, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x25, 0xfa, 0x42, 0x22, 0x72, 0x20, 0x28, 0x80, 0x08, - 0x32, 0x1b, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x2f, 0x5f, - 0x7c, 0x5c, 0x2d, 0x3d, 0x2b, 0x5d, 0x7b, 0x31, 0x2c, 0x7d, 0x29, 0x3f, 0x24, 0x52, 0x12, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, - 0x64, 0x12, 0x64, 0x0a, 0x1b, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x25, 0xfa, 0x42, 0x22, 0x72, 0x20, 0x28, 0x80, 0x08, - 0x32, 0x1b, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x2f, 0x5f, - 0x7c, 0x5c, 0x2d, 0x3d, 0x2b, 0x5d, 0x7b, 0x31, 0x2c, 0x7d, 0x29, 0x3f, 0x24, 0x52, 0x18, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, - 0x64, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x57, 0x0a, 0x11, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x2a, 0xfa, 0x42, 0x27, 0x72, 0x25, 0x28, 0x40, 0x32, 0x21, 0x5e, 0x28, 0x5b, - 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, - 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x52, 0x10, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x4e, 0x0a, 0x17, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x15, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x61, 0x6c, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, - 0x22, 0x86, 0x03, 0x0a, 0x0d, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x12, 0x6b, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x48, 0xfa, 0x42, 0x45, 0x72, 0x43, 0x28, - 0x80, 0x01, 0x32, 0x3e, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, - 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x31, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, - 0x39, 0x5d, 0x2f, 0x29, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, - 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, - 0x5d, 0x24, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x5a, 0x0a, 0x13, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2a, 0xfa, 0x42, - 0x27, 0x72, 0x25, 0x28, 0x80, 0x08, 0x32, 0x20, 0x5e, 0x28, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, - 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x2f, 0x5f, 0x7c, 0x5c, 0x2d, 0x3d, 0x2b, 0x5d, 0x7b, 0x31, 0x2c, - 0x7d, 0x29, 0x7c, 0x5c, 0x2a, 0x29, 0x3f, 0x24, 0x52, 0x11, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x61, 0x6c, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x52, 0x0a, 0x11, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x52, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x10, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, - 0x58, 0x0a, 0x0e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x12, 0x46, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x2a, 0xfa, 0x42, 0x27, 0x72, 0x25, 0x28, 0x40, 0x32, 0x21, 0x5e, 0x28, - 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, - 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x52, - 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x8a, 0x01, 0x0a, 0x0b, 0x63, 0x6f, - 0x6d, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x43, 0x6f, 0x72, 0x65, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x7a, 0x65, 0x64, 0x2f, 0x73, 0x70, 0x69, 0x63, 0x65, - 0x64, 0x62, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x72, - 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x72, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x58, - 0x58, 0xaa, 0x02, 0x07, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x07, 0x43, 0x6f, - 0x72, 0x65, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x13, 0x43, 0x6f, 0x72, 0x65, 0x5c, 0x56, 0x31, 0x5c, - 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, 0x43, 0x6f, - 0x72, 0x65, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, 0x6c, + 0x0a, 0x1b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x5f, 0x74, 0x75, 0x70, + 0x6c, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, + 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, + 0x48, 0x00, 0x52, 0x18, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x54, 0x75, + 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x34, 0x0a, 0x04, + 0x5f, 0x6e, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x2e, 0x4e, 0x69, 0x6c, 0x48, 0x00, 0x52, 0x03, 0x4e, + 0x69, 0x6c, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x6f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x1a, 0x06, 0x0a, 0x04, 0x54, + 0x68, 0x69, 0x73, 0x1a, 0x05, 0x0a, 0x03, 0x4e, 0x69, 0x6c, 0x42, 0x11, 0x0a, 0x0a, 0x63, 0x68, + 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x22, 0xba, 0x02, + 0x0a, 0x0e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, + 0x12, 0x46, 0x0a, 0x08, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x75, 0x70, + 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x2e, 0x54, 0x75, 0x70, 0x6c, + 0x65, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, + 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x12, 0x4d, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x70, + 0x75, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, + 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, + 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, + 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x4f, 0x0a, 0x08, 0x54, 0x75, 0x70, + 0x6c, 0x65, 0x73, 0x65, 0x74, 0x12, 0x43, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xfa, 0x42, 0x24, 0x72, 0x22, 0x28, 0x40, + 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, + 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, + 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xec, 0x03, 0x0a, 0x18, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, + 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x52, 0x0a, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x54, 0x75, + 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x2e, 0x46, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0xfa, 0x42, 0x07, 0x82, 0x01, 0x04, 0x10, 0x01, 0x20, + 0x00, 0x52, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x50, 0x0a, 0x08, 0x74, + 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x65, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, + 0x2e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, + 0x02, 0x10, 0x01, 0x52, 0x08, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x12, 0x4d, 0x0a, + 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, + 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x63, 0x6f, 0x6d, + 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x40, 0x0a, 0x0f, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x4f, + 0x0a, 0x08, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x12, 0x43, 0x0a, 0x08, 0x72, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xfa, 0x42, + 0x24, 0x72, 0x22, 0x28, 0x40, 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, + 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, + 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x48, 0x0a, 0x08, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x14, 0x46, + 0x55, 0x4e, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x55, 0x4e, 0x43, 0x54, 0x49, 0x4f, + 0x4e, 0x5f, 0x41, 0x4e, 0x59, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x55, 0x4e, 0x43, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x02, 0x22, 0x91, 0x02, 0x0a, 0x0f, 0x43, 0x6f, + 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x41, 0x0a, + 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, + 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x08, + 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x12, 0x43, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x27, 0xfa, 0x42, 0x24, 0x72, 0x22, 0x28, 0x40, 0x32, 0x1e, 0x5e, 0x5b, 0x61, + 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, + 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x08, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x34, 0x0a, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x55, 0x50, 0x4c, 0x45, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, + 0x54, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x55, 0x50, 0x4c, 0x45, 0x5f, 0x55, 0x53, 0x45, + 0x52, 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x01, 0x22, 0x8a, 0x01, + 0x0a, 0x0e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x37, 0x0a, 0x18, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, + 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x15, 0x7a, 0x65, 0x72, 0x6f, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x4c, + 0x69, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x1c, 0x7a, 0x65, 0x72, + 0x6f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, + 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x19, 0x7a, 0x65, 0x72, 0x6f, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x75, + 0x6d, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x9c, 0x01, 0x0a, 0x10, 0x43, + 0x61, 0x76, 0x65, 0x61, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x38, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x76, + 0x65, 0x61, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x09, + 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x06, 0x63, 0x61, 0x76, + 0x65, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x64, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x76, 0x65, + 0x61, 0x74, 0x42, 0x15, 0x0a, 0x13, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x6f, 0x72, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x22, 0xb0, 0x01, 0x0a, 0x0f, 0x43, 0x61, + 0x76, 0x65, 0x61, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, + 0x02, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x02, 0x6f, + 0x70, 0x12, 0x35, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, + 0x76, 0x65, 0x61, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, + 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, 0x32, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4e, + 0x44, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x4e, 0x4f, 0x54, 0x10, 0x03, 0x22, 0xee, 0x03, 0x0a, + 0x12, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x12, 0x70, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x4b, 0xfa, 0x42, 0x48, 0x72, + 0x46, 0x28, 0x80, 0x01, 0x32, 0x41, 0x5e, 0x28, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, + 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x31, 0x7d, 0x5b, 0x61, 0x2d, + 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, + 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, + 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x57, 0x0a, 0x14, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x25, 0xfa, 0x42, 0x22, 0x72, 0x20, 0x28, 0x80, 0x08, 0x32, 0x1b, 0x5e, + 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x2f, 0x5f, 0x7c, 0x5c, 0x2d, + 0x3d, 0x2b, 0x5d, 0x7b, 0x31, 0x2c, 0x7d, 0x29, 0x3f, 0x24, 0x52, 0x12, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x64, + 0x0a, 0x1b, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x25, 0xfa, 0x42, 0x22, 0x72, 0x20, 0x28, 0x80, 0x08, 0x32, 0x1b, 0x5e, + 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x2f, 0x5f, 0x7c, 0x5c, 0x2d, + 0x3d, 0x2b, 0x5d, 0x7b, 0x31, 0x2c, 0x7d, 0x29, 0x3f, 0x24, 0x52, 0x18, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x50, 0x72, + 0x65, 0x66, 0x69, 0x78, 0x12, 0x57, 0x0a, 0x11, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x2a, 0xfa, 0x42, 0x27, 0x72, 0x25, 0x28, 0x40, 0x32, 0x21, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, + 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, + 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x52, 0x10, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, + 0x17, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x15, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x86, 0x03, + 0x0a, 0x0d, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, + 0x6b, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x48, 0xfa, 0x42, 0x45, 0x72, 0x43, 0x28, 0x80, 0x01, 0x32, + 0x3e, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, + 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x31, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, + 0x29, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, + 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, + 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x5a, 0x0a, 0x13, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2a, 0xfa, 0x42, 0x27, 0x72, 0x25, + 0x28, 0x80, 0x08, 0x32, 0x20, 0x5e, 0x28, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, + 0x2d, 0x39, 0x2f, 0x5f, 0x7c, 0x5c, 0x2d, 0x3d, 0x2b, 0x5d, 0x7b, 0x31, 0x2c, 0x7d, 0x29, 0x7c, + 0x5c, 0x2a, 0x29, 0x3f, 0x24, 0x52, 0x11, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, + 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x52, 0x0a, 0x11, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x10, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x58, 0x0a, 0x0e, + 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x46, + 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x2a, 0xfa, 0x42, 0x27, 0x72, 0x25, 0x28, 0x40, 0x32, 0x21, 0x5e, 0x28, 0x5b, 0x61, 0x2d, + 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, + 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x52, 0x08, 0x72, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x8a, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x43, 0x6f, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x50, 0x01, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x61, 0x75, 0x74, 0x68, 0x7a, 0x65, 0x64, 0x2f, 0x73, 0x70, 0x69, 0x63, 0x65, 0x64, 0x62, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, + 0x31, 0x3b, 0x63, 0x6f, 0x72, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x58, 0x58, 0xaa, 0x02, + 0x07, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x07, 0x43, 0x6f, 0x72, 0x65, 0x5c, + 0x56, 0x31, 0xe2, 0x02, 0x13, 0x43, 0x6f, 0x72, 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, 0x43, 0x6f, 0x72, 0x65, 0x3a, + 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3290,126 +3509,134 @@ func file_core_v1_core_proto_rawDescGZIP() []byte { return file_core_v1_core_proto_rawDescData } -var file_core_v1_core_proto_enumTypes = make([]protoimpl.EnumInfo, 6) -var file_core_v1_core_proto_msgTypes = make([]protoimpl.MessageInfo, 39) +var file_core_v1_core_proto_enumTypes = make([]protoimpl.EnumInfo, 7) +var file_core_v1_core_proto_msgTypes = make([]protoimpl.MessageInfo, 41) var file_core_v1_core_proto_goTypes = []interface{}{ (RelationTupleUpdate_Operation)(0), // 0: core.v1.RelationTupleUpdate.Operation (SetOperationUserset_Operation)(0), // 1: core.v1.SetOperationUserset.Operation (ReachabilityEntrypoint_ReachabilityEntrypointKind)(0), // 2: core.v1.ReachabilityEntrypoint.ReachabilityEntrypointKind (ReachabilityEntrypoint_EntrypointResultStatus)(0), // 3: core.v1.ReachabilityEntrypoint.EntrypointResultStatus - (ComputedUserset_Object)(0), // 4: core.v1.ComputedUserset.Object - (CaveatOperation_Operation)(0), // 5: core.v1.CaveatOperation.Operation - (*RelationTuple)(nil), // 6: core.v1.RelationTuple - (*ContextualizedCaveat)(nil), // 7: core.v1.ContextualizedCaveat - (*CaveatDefinition)(nil), // 8: core.v1.CaveatDefinition - (*CaveatTypeReference)(nil), // 9: core.v1.CaveatTypeReference - (*ObjectAndRelation)(nil), // 10: core.v1.ObjectAndRelation - (*RelationReference)(nil), // 11: core.v1.RelationReference - (*Zookie)(nil), // 12: core.v1.Zookie - (*RelationTupleUpdate)(nil), // 13: core.v1.RelationTupleUpdate - (*RelationTupleTreeNode)(nil), // 14: core.v1.RelationTupleTreeNode - (*SetOperationUserset)(nil), // 15: core.v1.SetOperationUserset - (*DirectSubject)(nil), // 16: core.v1.DirectSubject - (*DirectSubjects)(nil), // 17: core.v1.DirectSubjects - (*Metadata)(nil), // 18: core.v1.Metadata - (*NamespaceDefinition)(nil), // 19: core.v1.NamespaceDefinition - (*Relation)(nil), // 20: core.v1.Relation - (*ReachabilityGraph)(nil), // 21: core.v1.ReachabilityGraph - (*ReachabilityEntrypoints)(nil), // 22: core.v1.ReachabilityEntrypoints - (*ReachabilityEntrypoint)(nil), // 23: core.v1.ReachabilityEntrypoint - (*TypeInformation)(nil), // 24: core.v1.TypeInformation - (*AllowedRelation)(nil), // 25: core.v1.AllowedRelation - (*AllowedCaveat)(nil), // 26: core.v1.AllowedCaveat - (*UsersetRewrite)(nil), // 27: core.v1.UsersetRewrite - (*SetOperation)(nil), // 28: core.v1.SetOperation - (*TupleToUserset)(nil), // 29: core.v1.TupleToUserset - (*ComputedUserset)(nil), // 30: core.v1.ComputedUserset - (*SourcePosition)(nil), // 31: core.v1.SourcePosition - (*CaveatExpression)(nil), // 32: core.v1.CaveatExpression - (*CaveatOperation)(nil), // 33: core.v1.CaveatOperation - (*RelationshipFilter)(nil), // 34: core.v1.RelationshipFilter - (*SubjectFilter)(nil), // 35: core.v1.SubjectFilter - nil, // 36: core.v1.CaveatDefinition.ParameterTypesEntry - nil, // 37: core.v1.ReachabilityGraph.EntrypointsBySubjectTypeEntry - nil, // 38: core.v1.ReachabilityGraph.EntrypointsBySubjectRelationEntry - (*AllowedRelation_PublicWildcard)(nil), // 39: core.v1.AllowedRelation.PublicWildcard - (*SetOperation_Child)(nil), // 40: core.v1.SetOperation.Child - (*SetOperation_Child_This)(nil), // 41: core.v1.SetOperation.Child.This - (*SetOperation_Child_Nil)(nil), // 42: core.v1.SetOperation.Child.Nil - (*TupleToUserset_Tupleset)(nil), // 43: core.v1.TupleToUserset.Tupleset - (*SubjectFilter_RelationFilter)(nil), // 44: core.v1.SubjectFilter.RelationFilter - (*structpb.Struct)(nil), // 45: google.protobuf.Struct - (*anypb.Any)(nil), // 46: google.protobuf.Any + (FunctionedTupleToUserset_Function)(0), // 4: core.v1.FunctionedTupleToUserset.Function + (ComputedUserset_Object)(0), // 5: core.v1.ComputedUserset.Object + (CaveatOperation_Operation)(0), // 6: core.v1.CaveatOperation.Operation + (*RelationTuple)(nil), // 7: core.v1.RelationTuple + (*ContextualizedCaveat)(nil), // 8: core.v1.ContextualizedCaveat + (*CaveatDefinition)(nil), // 9: core.v1.CaveatDefinition + (*CaveatTypeReference)(nil), // 10: core.v1.CaveatTypeReference + (*ObjectAndRelation)(nil), // 11: core.v1.ObjectAndRelation + (*RelationReference)(nil), // 12: core.v1.RelationReference + (*Zookie)(nil), // 13: core.v1.Zookie + (*RelationTupleUpdate)(nil), // 14: core.v1.RelationTupleUpdate + (*RelationTupleTreeNode)(nil), // 15: core.v1.RelationTupleTreeNode + (*SetOperationUserset)(nil), // 16: core.v1.SetOperationUserset + (*DirectSubject)(nil), // 17: core.v1.DirectSubject + (*DirectSubjects)(nil), // 18: core.v1.DirectSubjects + (*Metadata)(nil), // 19: core.v1.Metadata + (*NamespaceDefinition)(nil), // 20: core.v1.NamespaceDefinition + (*Relation)(nil), // 21: core.v1.Relation + (*ReachabilityGraph)(nil), // 22: core.v1.ReachabilityGraph + (*ReachabilityEntrypoints)(nil), // 23: core.v1.ReachabilityEntrypoints + (*ReachabilityEntrypoint)(nil), // 24: core.v1.ReachabilityEntrypoint + (*TypeInformation)(nil), // 25: core.v1.TypeInformation + (*AllowedRelation)(nil), // 26: core.v1.AllowedRelation + (*AllowedCaveat)(nil), // 27: core.v1.AllowedCaveat + (*UsersetRewrite)(nil), // 28: core.v1.UsersetRewrite + (*SetOperation)(nil), // 29: core.v1.SetOperation + (*TupleToUserset)(nil), // 30: core.v1.TupleToUserset + (*FunctionedTupleToUserset)(nil), // 31: core.v1.FunctionedTupleToUserset + (*ComputedUserset)(nil), // 32: core.v1.ComputedUserset + (*SourcePosition)(nil), // 33: core.v1.SourcePosition + (*CaveatExpression)(nil), // 34: core.v1.CaveatExpression + (*CaveatOperation)(nil), // 35: core.v1.CaveatOperation + (*RelationshipFilter)(nil), // 36: core.v1.RelationshipFilter + (*SubjectFilter)(nil), // 37: core.v1.SubjectFilter + nil, // 38: core.v1.CaveatDefinition.ParameterTypesEntry + nil, // 39: core.v1.ReachabilityGraph.EntrypointsBySubjectTypeEntry + nil, // 40: core.v1.ReachabilityGraph.EntrypointsBySubjectRelationEntry + (*AllowedRelation_PublicWildcard)(nil), // 41: core.v1.AllowedRelation.PublicWildcard + (*SetOperation_Child)(nil), // 42: core.v1.SetOperation.Child + (*SetOperation_Child_This)(nil), // 43: core.v1.SetOperation.Child.This + (*SetOperation_Child_Nil)(nil), // 44: core.v1.SetOperation.Child.Nil + (*TupleToUserset_Tupleset)(nil), // 45: core.v1.TupleToUserset.Tupleset + (*FunctionedTupleToUserset_Tupleset)(nil), // 46: core.v1.FunctionedTupleToUserset.Tupleset + (*SubjectFilter_RelationFilter)(nil), // 47: core.v1.SubjectFilter.RelationFilter + (*structpb.Struct)(nil), // 48: google.protobuf.Struct + (*anypb.Any)(nil), // 49: google.protobuf.Any } var file_core_v1_core_proto_depIdxs = []int32{ - 10, // 0: core.v1.RelationTuple.resource_and_relation:type_name -> core.v1.ObjectAndRelation - 10, // 1: core.v1.RelationTuple.subject:type_name -> core.v1.ObjectAndRelation - 7, // 2: core.v1.RelationTuple.caveat:type_name -> core.v1.ContextualizedCaveat - 45, // 3: core.v1.ContextualizedCaveat.context:type_name -> google.protobuf.Struct - 36, // 4: core.v1.CaveatDefinition.parameter_types:type_name -> core.v1.CaveatDefinition.ParameterTypesEntry - 18, // 5: core.v1.CaveatDefinition.metadata:type_name -> core.v1.Metadata - 31, // 6: core.v1.CaveatDefinition.source_position:type_name -> core.v1.SourcePosition - 9, // 7: core.v1.CaveatTypeReference.child_types:type_name -> core.v1.CaveatTypeReference + 11, // 0: core.v1.RelationTuple.resource_and_relation:type_name -> core.v1.ObjectAndRelation + 11, // 1: core.v1.RelationTuple.subject:type_name -> core.v1.ObjectAndRelation + 8, // 2: core.v1.RelationTuple.caveat:type_name -> core.v1.ContextualizedCaveat + 48, // 3: core.v1.ContextualizedCaveat.context:type_name -> google.protobuf.Struct + 38, // 4: core.v1.CaveatDefinition.parameter_types:type_name -> core.v1.CaveatDefinition.ParameterTypesEntry + 19, // 5: core.v1.CaveatDefinition.metadata:type_name -> core.v1.Metadata + 33, // 6: core.v1.CaveatDefinition.source_position:type_name -> core.v1.SourcePosition + 10, // 7: core.v1.CaveatTypeReference.child_types:type_name -> core.v1.CaveatTypeReference 0, // 8: core.v1.RelationTupleUpdate.operation:type_name -> core.v1.RelationTupleUpdate.Operation - 6, // 9: core.v1.RelationTupleUpdate.tuple:type_name -> core.v1.RelationTuple - 15, // 10: core.v1.RelationTupleTreeNode.intermediate_node:type_name -> core.v1.SetOperationUserset - 17, // 11: core.v1.RelationTupleTreeNode.leaf_node:type_name -> core.v1.DirectSubjects - 10, // 12: core.v1.RelationTupleTreeNode.expanded:type_name -> core.v1.ObjectAndRelation - 32, // 13: core.v1.RelationTupleTreeNode.caveat_expression:type_name -> core.v1.CaveatExpression + 7, // 9: core.v1.RelationTupleUpdate.tuple:type_name -> core.v1.RelationTuple + 16, // 10: core.v1.RelationTupleTreeNode.intermediate_node:type_name -> core.v1.SetOperationUserset + 18, // 11: core.v1.RelationTupleTreeNode.leaf_node:type_name -> core.v1.DirectSubjects + 11, // 12: core.v1.RelationTupleTreeNode.expanded:type_name -> core.v1.ObjectAndRelation + 34, // 13: core.v1.RelationTupleTreeNode.caveat_expression:type_name -> core.v1.CaveatExpression 1, // 14: core.v1.SetOperationUserset.operation:type_name -> core.v1.SetOperationUserset.Operation - 14, // 15: core.v1.SetOperationUserset.child_nodes:type_name -> core.v1.RelationTupleTreeNode - 10, // 16: core.v1.DirectSubject.subject:type_name -> core.v1.ObjectAndRelation - 32, // 17: core.v1.DirectSubject.caveat_expression:type_name -> core.v1.CaveatExpression - 16, // 18: core.v1.DirectSubjects.subjects:type_name -> core.v1.DirectSubject - 46, // 19: core.v1.Metadata.metadata_message:type_name -> google.protobuf.Any - 20, // 20: core.v1.NamespaceDefinition.relation:type_name -> core.v1.Relation - 18, // 21: core.v1.NamespaceDefinition.metadata:type_name -> core.v1.Metadata - 31, // 22: core.v1.NamespaceDefinition.source_position:type_name -> core.v1.SourcePosition - 27, // 23: core.v1.Relation.userset_rewrite:type_name -> core.v1.UsersetRewrite - 24, // 24: core.v1.Relation.type_information:type_name -> core.v1.TypeInformation - 18, // 25: core.v1.Relation.metadata:type_name -> core.v1.Metadata - 31, // 26: core.v1.Relation.source_position:type_name -> core.v1.SourcePosition - 37, // 27: core.v1.ReachabilityGraph.entrypoints_by_subject_type:type_name -> core.v1.ReachabilityGraph.EntrypointsBySubjectTypeEntry - 38, // 28: core.v1.ReachabilityGraph.entrypoints_by_subject_relation:type_name -> core.v1.ReachabilityGraph.EntrypointsBySubjectRelationEntry - 23, // 29: core.v1.ReachabilityEntrypoints.entrypoints:type_name -> core.v1.ReachabilityEntrypoint - 11, // 30: core.v1.ReachabilityEntrypoints.subject_relation:type_name -> core.v1.RelationReference + 15, // 15: core.v1.SetOperationUserset.child_nodes:type_name -> core.v1.RelationTupleTreeNode + 11, // 16: core.v1.DirectSubject.subject:type_name -> core.v1.ObjectAndRelation + 34, // 17: core.v1.DirectSubject.caveat_expression:type_name -> core.v1.CaveatExpression + 17, // 18: core.v1.DirectSubjects.subjects:type_name -> core.v1.DirectSubject + 49, // 19: core.v1.Metadata.metadata_message:type_name -> google.protobuf.Any + 21, // 20: core.v1.NamespaceDefinition.relation:type_name -> core.v1.Relation + 19, // 21: core.v1.NamespaceDefinition.metadata:type_name -> core.v1.Metadata + 33, // 22: core.v1.NamespaceDefinition.source_position:type_name -> core.v1.SourcePosition + 28, // 23: core.v1.Relation.userset_rewrite:type_name -> core.v1.UsersetRewrite + 25, // 24: core.v1.Relation.type_information:type_name -> core.v1.TypeInformation + 19, // 25: core.v1.Relation.metadata:type_name -> core.v1.Metadata + 33, // 26: core.v1.Relation.source_position:type_name -> core.v1.SourcePosition + 39, // 27: core.v1.ReachabilityGraph.entrypoints_by_subject_type:type_name -> core.v1.ReachabilityGraph.EntrypointsBySubjectTypeEntry + 40, // 28: core.v1.ReachabilityGraph.entrypoints_by_subject_relation:type_name -> core.v1.ReachabilityGraph.EntrypointsBySubjectRelationEntry + 24, // 29: core.v1.ReachabilityEntrypoints.entrypoints:type_name -> core.v1.ReachabilityEntrypoint + 12, // 30: core.v1.ReachabilityEntrypoints.subject_relation:type_name -> core.v1.RelationReference 2, // 31: core.v1.ReachabilityEntrypoint.kind:type_name -> core.v1.ReachabilityEntrypoint.ReachabilityEntrypointKind - 11, // 32: core.v1.ReachabilityEntrypoint.target_relation:type_name -> core.v1.RelationReference + 12, // 32: core.v1.ReachabilityEntrypoint.target_relation:type_name -> core.v1.RelationReference 3, // 33: core.v1.ReachabilityEntrypoint.result_status:type_name -> core.v1.ReachabilityEntrypoint.EntrypointResultStatus - 25, // 34: core.v1.TypeInformation.allowed_direct_relations:type_name -> core.v1.AllowedRelation - 39, // 35: core.v1.AllowedRelation.public_wildcard:type_name -> core.v1.AllowedRelation.PublicWildcard - 31, // 36: core.v1.AllowedRelation.source_position:type_name -> core.v1.SourcePosition - 26, // 37: core.v1.AllowedRelation.required_caveat:type_name -> core.v1.AllowedCaveat - 28, // 38: core.v1.UsersetRewrite.union:type_name -> core.v1.SetOperation - 28, // 39: core.v1.UsersetRewrite.intersection:type_name -> core.v1.SetOperation - 28, // 40: core.v1.UsersetRewrite.exclusion:type_name -> core.v1.SetOperation - 31, // 41: core.v1.UsersetRewrite.source_position:type_name -> core.v1.SourcePosition - 40, // 42: core.v1.SetOperation.child:type_name -> core.v1.SetOperation.Child - 43, // 43: core.v1.TupleToUserset.tupleset:type_name -> core.v1.TupleToUserset.Tupleset - 30, // 44: core.v1.TupleToUserset.computed_userset:type_name -> core.v1.ComputedUserset - 31, // 45: core.v1.TupleToUserset.source_position:type_name -> core.v1.SourcePosition - 4, // 46: core.v1.ComputedUserset.object:type_name -> core.v1.ComputedUserset.Object - 31, // 47: core.v1.ComputedUserset.source_position:type_name -> core.v1.SourcePosition - 33, // 48: core.v1.CaveatExpression.operation:type_name -> core.v1.CaveatOperation - 7, // 49: core.v1.CaveatExpression.caveat:type_name -> core.v1.ContextualizedCaveat - 5, // 50: core.v1.CaveatOperation.op:type_name -> core.v1.CaveatOperation.Operation - 32, // 51: core.v1.CaveatOperation.children:type_name -> core.v1.CaveatExpression - 35, // 52: core.v1.RelationshipFilter.optional_subject_filter:type_name -> core.v1.SubjectFilter - 44, // 53: core.v1.SubjectFilter.optional_relation:type_name -> core.v1.SubjectFilter.RelationFilter - 9, // 54: core.v1.CaveatDefinition.ParameterTypesEntry.value:type_name -> core.v1.CaveatTypeReference - 22, // 55: core.v1.ReachabilityGraph.EntrypointsBySubjectTypeEntry.value:type_name -> core.v1.ReachabilityEntrypoints - 22, // 56: core.v1.ReachabilityGraph.EntrypointsBySubjectRelationEntry.value:type_name -> core.v1.ReachabilityEntrypoints - 41, // 57: core.v1.SetOperation.Child._this:type_name -> core.v1.SetOperation.Child.This - 30, // 58: core.v1.SetOperation.Child.computed_userset:type_name -> core.v1.ComputedUserset - 29, // 59: core.v1.SetOperation.Child.tuple_to_userset:type_name -> core.v1.TupleToUserset - 27, // 60: core.v1.SetOperation.Child.userset_rewrite:type_name -> core.v1.UsersetRewrite - 42, // 61: core.v1.SetOperation.Child._nil:type_name -> core.v1.SetOperation.Child.Nil - 31, // 62: core.v1.SetOperation.Child.source_position:type_name -> core.v1.SourcePosition - 63, // [63:63] is the sub-list for method output_type - 63, // [63:63] is the sub-list for method input_type - 63, // [63:63] is the sub-list for extension type_name - 63, // [63:63] is the sub-list for extension extendee - 0, // [0:63] is the sub-list for field type_name + 26, // 34: core.v1.TypeInformation.allowed_direct_relations:type_name -> core.v1.AllowedRelation + 41, // 35: core.v1.AllowedRelation.public_wildcard:type_name -> core.v1.AllowedRelation.PublicWildcard + 33, // 36: core.v1.AllowedRelation.source_position:type_name -> core.v1.SourcePosition + 27, // 37: core.v1.AllowedRelation.required_caveat:type_name -> core.v1.AllowedCaveat + 29, // 38: core.v1.UsersetRewrite.union:type_name -> core.v1.SetOperation + 29, // 39: core.v1.UsersetRewrite.intersection:type_name -> core.v1.SetOperation + 29, // 40: core.v1.UsersetRewrite.exclusion:type_name -> core.v1.SetOperation + 33, // 41: core.v1.UsersetRewrite.source_position:type_name -> core.v1.SourcePosition + 42, // 42: core.v1.SetOperation.child:type_name -> core.v1.SetOperation.Child + 45, // 43: core.v1.TupleToUserset.tupleset:type_name -> core.v1.TupleToUserset.Tupleset + 32, // 44: core.v1.TupleToUserset.computed_userset:type_name -> core.v1.ComputedUserset + 33, // 45: core.v1.TupleToUserset.source_position:type_name -> core.v1.SourcePosition + 4, // 46: core.v1.FunctionedTupleToUserset.function:type_name -> core.v1.FunctionedTupleToUserset.Function + 46, // 47: core.v1.FunctionedTupleToUserset.tupleset:type_name -> core.v1.FunctionedTupleToUserset.Tupleset + 32, // 48: core.v1.FunctionedTupleToUserset.computed_userset:type_name -> core.v1.ComputedUserset + 33, // 49: core.v1.FunctionedTupleToUserset.source_position:type_name -> core.v1.SourcePosition + 5, // 50: core.v1.ComputedUserset.object:type_name -> core.v1.ComputedUserset.Object + 33, // 51: core.v1.ComputedUserset.source_position:type_name -> core.v1.SourcePosition + 35, // 52: core.v1.CaveatExpression.operation:type_name -> core.v1.CaveatOperation + 8, // 53: core.v1.CaveatExpression.caveat:type_name -> core.v1.ContextualizedCaveat + 6, // 54: core.v1.CaveatOperation.op:type_name -> core.v1.CaveatOperation.Operation + 34, // 55: core.v1.CaveatOperation.children:type_name -> core.v1.CaveatExpression + 37, // 56: core.v1.RelationshipFilter.optional_subject_filter:type_name -> core.v1.SubjectFilter + 47, // 57: core.v1.SubjectFilter.optional_relation:type_name -> core.v1.SubjectFilter.RelationFilter + 10, // 58: core.v1.CaveatDefinition.ParameterTypesEntry.value:type_name -> core.v1.CaveatTypeReference + 23, // 59: core.v1.ReachabilityGraph.EntrypointsBySubjectTypeEntry.value:type_name -> core.v1.ReachabilityEntrypoints + 23, // 60: core.v1.ReachabilityGraph.EntrypointsBySubjectRelationEntry.value:type_name -> core.v1.ReachabilityEntrypoints + 43, // 61: core.v1.SetOperation.Child._this:type_name -> core.v1.SetOperation.Child.This + 32, // 62: core.v1.SetOperation.Child.computed_userset:type_name -> core.v1.ComputedUserset + 30, // 63: core.v1.SetOperation.Child.tuple_to_userset:type_name -> core.v1.TupleToUserset + 28, // 64: core.v1.SetOperation.Child.userset_rewrite:type_name -> core.v1.UsersetRewrite + 31, // 65: core.v1.SetOperation.Child.functioned_tuple_to_userset:type_name -> core.v1.FunctionedTupleToUserset + 44, // 66: core.v1.SetOperation.Child._nil:type_name -> core.v1.SetOperation.Child.Nil + 33, // 67: core.v1.SetOperation.Child.source_position:type_name -> core.v1.SourcePosition + 68, // [68:68] is the sub-list for method output_type + 68, // [68:68] is the sub-list for method input_type + 68, // [68:68] is the sub-list for extension type_name + 68, // [68:68] is the sub-list for extension extendee + 0, // [0:68] is the sub-list for field type_name } func init() { file_core_v1_core_proto_init() } @@ -3707,7 +3934,7 @@ func file_core_v1_core_proto_init() { } } file_core_v1_core_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ComputedUserset); i { + switch v := v.(*FunctionedTupleToUserset); i { case 0: return &v.state case 1: @@ -3719,7 +3946,7 @@ func file_core_v1_core_proto_init() { } } file_core_v1_core_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SourcePosition); i { + switch v := v.(*ComputedUserset); i { case 0: return &v.state case 1: @@ -3731,7 +3958,7 @@ func file_core_v1_core_proto_init() { } } file_core_v1_core_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CaveatExpression); i { + switch v := v.(*SourcePosition); i { case 0: return &v.state case 1: @@ -3743,7 +3970,7 @@ func file_core_v1_core_proto_init() { } } file_core_v1_core_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CaveatOperation); i { + switch v := v.(*CaveatExpression); i { case 0: return &v.state case 1: @@ -3755,7 +3982,7 @@ func file_core_v1_core_proto_init() { } } file_core_v1_core_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RelationshipFilter); i { + switch v := v.(*CaveatOperation); i { case 0: return &v.state case 1: @@ -3767,6 +3994,18 @@ func file_core_v1_core_proto_init() { } } file_core_v1_core_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RelationshipFilter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_core_v1_core_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SubjectFilter); i { case 0: return &v.state @@ -3778,7 +4017,7 @@ func file_core_v1_core_proto_init() { return nil } } - file_core_v1_core_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + file_core_v1_core_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AllowedRelation_PublicWildcard); i { case 0: return &v.state @@ -3790,7 +4029,7 @@ func file_core_v1_core_proto_init() { return nil } } - file_core_v1_core_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + file_core_v1_core_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetOperation_Child); i { case 0: return &v.state @@ -3802,7 +4041,7 @@ func file_core_v1_core_proto_init() { return nil } } - file_core_v1_core_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + file_core_v1_core_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetOperation_Child_This); i { case 0: return &v.state @@ -3814,7 +4053,7 @@ func file_core_v1_core_proto_init() { return nil } } - file_core_v1_core_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + file_core_v1_core_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetOperation_Child_Nil); i { case 0: return &v.state @@ -3826,7 +4065,7 @@ func file_core_v1_core_proto_init() { return nil } } - file_core_v1_core_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + file_core_v1_core_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TupleToUserset_Tupleset); i { case 0: return &v.state @@ -3838,7 +4077,19 @@ func file_core_v1_core_proto_init() { return nil } } - file_core_v1_core_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + file_core_v1_core_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FunctionedTupleToUserset_Tupleset); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_core_v1_core_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SubjectFilter_RelationFilter); i { case 0: return &v.state @@ -3864,15 +4115,16 @@ func file_core_v1_core_proto_init() { (*UsersetRewrite_Intersection)(nil), (*UsersetRewrite_Exclusion)(nil), } - file_core_v1_core_proto_msgTypes[26].OneofWrappers = []interface{}{ + file_core_v1_core_proto_msgTypes[27].OneofWrappers = []interface{}{ (*CaveatExpression_Operation)(nil), (*CaveatExpression_Caveat)(nil), } - file_core_v1_core_proto_msgTypes[34].OneofWrappers = []interface{}{ + file_core_v1_core_proto_msgTypes[35].OneofWrappers = []interface{}{ (*SetOperation_Child_XThis)(nil), (*SetOperation_Child_ComputedUserset)(nil), (*SetOperation_Child_TupleToUserset)(nil), (*SetOperation_Child_UsersetRewrite)(nil), + (*SetOperation_Child_FunctionedTupleToUserset)(nil), (*SetOperation_Child_XNil)(nil), } type x struct{} @@ -3880,8 +4132,8 @@ func file_core_v1_core_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_core_v1_core_proto_rawDesc, - NumEnums: 6, - NumMessages: 39, + NumEnums: 7, + NumMessages: 41, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/proto/core/v1/core.pb.validate.go b/pkg/proto/core/v1/core.pb.validate.go index a040974c59..66bdbdd411 100644 --- a/pkg/proto/core/v1/core.pb.validate.go +++ b/pkg/proto/core/v1/core.pb.validate.go @@ -4334,6 +4334,243 @@ var _ interface { ErrorName() string } = TupleToUsersetValidationError{} +// Validate checks the field values on FunctionedTupleToUserset with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *FunctionedTupleToUserset) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on FunctionedTupleToUserset with the +// rules defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// FunctionedTupleToUsersetMultiError, or nil if none found. +func (m *FunctionedTupleToUserset) ValidateAll() error { + return m.validate(true) +} + +func (m *FunctionedTupleToUserset) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if _, ok := _FunctionedTupleToUserset_Function_NotInLookup[m.GetFunction()]; ok { + err := FunctionedTupleToUsersetValidationError{ + field: "Function", + reason: "value must not be in list [FUNCTION_UNSPECIFIED]", + } + if !all { + return err + } + errors = append(errors, err) + } + + if _, ok := FunctionedTupleToUserset_Function_name[int32(m.GetFunction())]; !ok { + err := FunctionedTupleToUsersetValidationError{ + field: "Function", + reason: "value must be one of the defined enum values", + } + if !all { + return err + } + errors = append(errors, err) + } + + if m.GetTupleset() == nil { + err := FunctionedTupleToUsersetValidationError{ + field: "Tupleset", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetTupleset()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, FunctionedTupleToUsersetValidationError{ + field: "Tupleset", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, FunctionedTupleToUsersetValidationError{ + field: "Tupleset", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetTupleset()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return FunctionedTupleToUsersetValidationError{ + field: "Tupleset", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if m.GetComputedUserset() == nil { + err := FunctionedTupleToUsersetValidationError{ + field: "ComputedUserset", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetComputedUserset()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, FunctionedTupleToUsersetValidationError{ + field: "ComputedUserset", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, FunctionedTupleToUsersetValidationError{ + field: "ComputedUserset", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetComputedUserset()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return FunctionedTupleToUsersetValidationError{ + field: "ComputedUserset", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetSourcePosition()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, FunctionedTupleToUsersetValidationError{ + field: "SourcePosition", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, FunctionedTupleToUsersetValidationError{ + field: "SourcePosition", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetSourcePosition()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return FunctionedTupleToUsersetValidationError{ + field: "SourcePosition", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return FunctionedTupleToUsersetMultiError(errors) + } + + return nil +} + +// FunctionedTupleToUsersetMultiError is an error wrapping multiple validation +// errors returned by FunctionedTupleToUserset.ValidateAll() if the designated +// constraints aren't met. +type FunctionedTupleToUsersetMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m FunctionedTupleToUsersetMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m FunctionedTupleToUsersetMultiError) AllErrors() []error { return m } + +// FunctionedTupleToUsersetValidationError is the validation error returned by +// FunctionedTupleToUserset.Validate if the designated constraints aren't met. +type FunctionedTupleToUsersetValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e FunctionedTupleToUsersetValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e FunctionedTupleToUsersetValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e FunctionedTupleToUsersetValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e FunctionedTupleToUsersetValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e FunctionedTupleToUsersetValidationError) ErrorName() string { + return "FunctionedTupleToUsersetValidationError" +} + +// Error satisfies the builtin error interface +func (e FunctionedTupleToUsersetValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sFunctionedTupleToUserset.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = FunctionedTupleToUsersetValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = FunctionedTupleToUsersetValidationError{} + +var _FunctionedTupleToUserset_Function_NotInLookup = map[FunctionedTupleToUserset_Function]struct{}{ + 0: {}, +} + // Validate checks the field values on ComputedUserset with the rules defined // in the proto definition for this message. If any rules are violated, the // first error encountered is returned, or nil if there are no violations. @@ -5686,6 +5923,59 @@ func (m *SetOperation_Child) validate(all bool) error { } } + case *SetOperation_Child_FunctionedTupleToUserset: + if v == nil { + err := SetOperation_ChildValidationError{ + field: "ChildType", + reason: "oneof value cannot be a typed-nil", + } + if !all { + return err + } + errors = append(errors, err) + } + oneofChildTypePresent = true + + if m.GetFunctionedTupleToUserset() == nil { + err := SetOperation_ChildValidationError{ + field: "FunctionedTupleToUserset", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetFunctionedTupleToUserset()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, SetOperation_ChildValidationError{ + field: "FunctionedTupleToUserset", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, SetOperation_ChildValidationError{ + field: "FunctionedTupleToUserset", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetFunctionedTupleToUserset()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return SetOperation_ChildValidationError{ + field: "FunctionedTupleToUserset", + reason: "embedded message failed validation", + cause: err, + } + } + } + case *SetOperation_Child_XNil: if v == nil { err := SetOperation_ChildValidationError{ @@ -6152,6 +6442,135 @@ var _ interface { var _TupleToUserset_Tupleset_Relation_Pattern = regexp.MustCompile("^[a-z][a-z0-9_]{1,62}[a-z0-9]$") +// Validate checks the field values on FunctionedTupleToUserset_Tupleset with +// the rules defined in the proto definition for this message. If any rules +// are violated, the first error encountered is returned, or nil if there are +// no violations. +func (m *FunctionedTupleToUserset_Tupleset) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on FunctionedTupleToUserset_Tupleset +// with the rules defined in the proto definition for this message. If any +// rules are violated, the result is a list of violation errors wrapped in +// FunctionedTupleToUserset_TuplesetMultiError, or nil if none found. +func (m *FunctionedTupleToUserset_Tupleset) ValidateAll() error { + return m.validate(true) +} + +func (m *FunctionedTupleToUserset_Tupleset) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if len(m.GetRelation()) > 64 { + err := FunctionedTupleToUserset_TuplesetValidationError{ + field: "Relation", + reason: "value length must be at most 64 bytes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if !_FunctionedTupleToUserset_Tupleset_Relation_Pattern.MatchString(m.GetRelation()) { + err := FunctionedTupleToUserset_TuplesetValidationError{ + field: "Relation", + reason: "value does not match regex pattern \"^[a-z][a-z0-9_]{1,62}[a-z0-9]$\"", + } + if !all { + return err + } + errors = append(errors, err) + } + + if len(errors) > 0 { + return FunctionedTupleToUserset_TuplesetMultiError(errors) + } + + return nil +} + +// FunctionedTupleToUserset_TuplesetMultiError is an error wrapping multiple +// validation errors returned by +// FunctionedTupleToUserset_Tupleset.ValidateAll() if the designated +// constraints aren't met. +type FunctionedTupleToUserset_TuplesetMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m FunctionedTupleToUserset_TuplesetMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m FunctionedTupleToUserset_TuplesetMultiError) AllErrors() []error { return m } + +// FunctionedTupleToUserset_TuplesetValidationError is the validation error +// returned by FunctionedTupleToUserset_Tupleset.Validate if the designated +// constraints aren't met. +type FunctionedTupleToUserset_TuplesetValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e FunctionedTupleToUserset_TuplesetValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e FunctionedTupleToUserset_TuplesetValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e FunctionedTupleToUserset_TuplesetValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e FunctionedTupleToUserset_TuplesetValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e FunctionedTupleToUserset_TuplesetValidationError) ErrorName() string { + return "FunctionedTupleToUserset_TuplesetValidationError" +} + +// Error satisfies the builtin error interface +func (e FunctionedTupleToUserset_TuplesetValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sFunctionedTupleToUserset_Tupleset.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = FunctionedTupleToUserset_TuplesetValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = FunctionedTupleToUserset_TuplesetValidationError{} + +var _FunctionedTupleToUserset_Tupleset_Relation_Pattern = regexp.MustCompile("^[a-z][a-z0-9_]{1,62}[a-z0-9]$") + // Validate checks the field values on SubjectFilter_RelationFilter with the // rules defined in the proto definition for this message. If any rules are // violated, the first error encountered is returned, or nil if there are no violations. diff --git a/pkg/proto/core/v1/core_vtproto.pb.go b/pkg/proto/core/v1/core_vtproto.pb.go index a0a24a7191..b3b90c601c 100644 --- a/pkg/proto/core/v1/core_vtproto.pb.go +++ b/pkg/proto/core/v1/core_vtproto.pb.go @@ -682,6 +682,15 @@ func (m *SetOperation_Child_UsersetRewrite) CloneVT() isSetOperation_Child_Child return r } +func (m *SetOperation_Child_FunctionedTupleToUserset) CloneVT() isSetOperation_Child_ChildType { + if m == nil { + return (*SetOperation_Child_FunctionedTupleToUserset)(nil) + } + r := new(SetOperation_Child_FunctionedTupleToUserset) + r.FunctionedTupleToUserset = m.FunctionedTupleToUserset.CloneVT() + return r +} + func (m *SetOperation_Child_XNil) CloneVT() isSetOperation_Child_ChildType { if m == nil { return (*SetOperation_Child_XNil)(nil) @@ -750,6 +759,43 @@ func (m *TupleToUserset) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *FunctionedTupleToUserset_Tupleset) CloneVT() *FunctionedTupleToUserset_Tupleset { + if m == nil { + return (*FunctionedTupleToUserset_Tupleset)(nil) + } + r := new(FunctionedTupleToUserset_Tupleset) + r.Relation = m.Relation + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *FunctionedTupleToUserset_Tupleset) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *FunctionedTupleToUserset) CloneVT() *FunctionedTupleToUserset { + if m == nil { + return (*FunctionedTupleToUserset)(nil) + } + r := new(FunctionedTupleToUserset) + r.Function = m.Function + r.Tupleset = m.Tupleset.CloneVT() + r.ComputedUserset = m.ComputedUserset.CloneVT() + r.SourcePosition = m.SourcePosition.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *FunctionedTupleToUserset) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (m *ComputedUserset) CloneVT() *ComputedUserset { if m == nil { return (*ComputedUserset)(nil) @@ -1986,6 +2032,31 @@ func (this *SetOperation_Child_XNil) EqualVT(thatIface isSetOperation_Child_Chil return true } +func (this *SetOperation_Child_FunctionedTupleToUserset) EqualVT(thatIface isSetOperation_Child_ChildType) bool { + that, ok := thatIface.(*SetOperation_Child_FunctionedTupleToUserset) + if !ok { + return false + } + if this == that { + return true + } + if this == nil && that != nil || this != nil && that == nil { + return false + } + if p, q := this.FunctionedTupleToUserset, that.FunctionedTupleToUserset; p != q { + if p == nil { + p = &FunctionedTupleToUserset{} + } + if q == nil { + q = &FunctionedTupleToUserset{} + } + if !p.EqualVT(q) { + return false + } + } + return true +} + func (this *SetOperation) EqualVT(that *SetOperation) bool { if this == that { return true @@ -2063,6 +2134,53 @@ func (this *TupleToUserset) EqualMessageVT(thatMsg proto.Message) bool { } return this.EqualVT(that) } +func (this *FunctionedTupleToUserset_Tupleset) EqualVT(that *FunctionedTupleToUserset_Tupleset) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Relation != that.Relation { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *FunctionedTupleToUserset_Tupleset) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*FunctionedTupleToUserset_Tupleset) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *FunctionedTupleToUserset) EqualVT(that *FunctionedTupleToUserset) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Function != that.Function { + return false + } + if !this.Tupleset.EqualVT(that.Tupleset) { + return false + } + if !this.ComputedUserset.EqualVT(that.ComputedUserset) { + return false + } + if !this.SourcePosition.EqualVT(that.SourcePosition) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *FunctionedTupleToUserset) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*FunctionedTupleToUserset) + if !ok { + return false + } + return this.EqualVT(that) +} func (this *ComputedUserset) EqualVT(that *ComputedUserset) bool { if this == that { return true @@ -3962,6 +4080,25 @@ func (m *SetOperation_Child_XNil) MarshalToSizedBufferVT(dAtA []byte) (int, erro } return len(dAtA) - i, nil } +func (m *SetOperation_Child_FunctionedTupleToUserset) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SetOperation_Child_FunctionedTupleToUserset) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + i := len(dAtA) + if m.FunctionedTupleToUserset != nil { + size, err := m.FunctionedTupleToUserset.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x42 + } + return len(dAtA) - i, nil +} func (m *SetOperation) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -4110,6 +4247,114 @@ func (m *TupleToUserset) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *FunctionedTupleToUserset_Tupleset) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FunctionedTupleToUserset_Tupleset) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *FunctionedTupleToUserset_Tupleset) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Relation) > 0 { + i -= len(m.Relation) + copy(dAtA[i:], m.Relation) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Relation))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *FunctionedTupleToUserset) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FunctionedTupleToUserset) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *FunctionedTupleToUserset) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.SourcePosition != nil { + size, err := m.SourcePosition.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x22 + } + if m.ComputedUserset != nil { + size, err := m.ComputedUserset.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x1a + } + if m.Tupleset != nil { + size, err := m.Tupleset.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if m.Function != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Function)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *ComputedUserset) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -5184,6 +5429,18 @@ func (m *SetOperation_Child_XNil) SizeVT() (n int) { } return n } +func (m *SetOperation_Child_FunctionedTupleToUserset) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FunctionedTupleToUserset != nil { + l = m.FunctionedTupleToUserset.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + return n +} func (m *SetOperation) SizeVT() (n int) { if m == nil { return 0 @@ -5236,6 +5493,45 @@ func (m *TupleToUserset) SizeVT() (n int) { return n } +func (m *FunctionedTupleToUserset_Tupleset) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Relation) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *FunctionedTupleToUserset) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Function != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Function)) + } + if m.Tupleset != nil { + l = m.Tupleset.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.ComputedUserset != nil { + l = m.ComputedUserset.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.SourcePosition != nil { + l = m.SourcePosition.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + func (m *ComputedUserset) SizeVT() (n int) { if m == nil { return 0 @@ -9365,11 +9661,52 @@ func (m *SetOperation_Child) UnmarshalVT(dAtA []byte) error { } else { return fmt.Errorf("proto: wrong wireType = %d for field OperationPath", wireType) } - default: - iNdEx = preIndex - skippy, err := protohelpers.Skip(dAtA[iNdEx:]) - if err != nil { - return err + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FunctionedTupleToUserset", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if oneof, ok := m.ChildType.(*SetOperation_Child_FunctionedTupleToUserset); ok { + if err := oneof.FunctionedTupleToUserset.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + } else { + v := &FunctionedTupleToUserset{} + if err := v.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.ChildType = &SetOperation_Child_FunctionedTupleToUserset{FunctionedTupleToUserset: v} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength @@ -9714,6 +10051,267 @@ func (m *TupleToUserset) UnmarshalVT(dAtA []byte) error { } return nil } +func (m *FunctionedTupleToUserset_Tupleset) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FunctionedTupleToUserset_Tupleset: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FunctionedTupleToUserset_Tupleset: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Relation", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Relation = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FunctionedTupleToUserset) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FunctionedTupleToUserset: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FunctionedTupleToUserset: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Function", wireType) + } + m.Function = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Function |= FunctionedTupleToUserset_Function(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tupleset", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Tupleset == nil { + m.Tupleset = &FunctionedTupleToUserset_Tupleset{} + } + if err := m.Tupleset.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ComputedUserset", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ComputedUserset == nil { + m.ComputedUserset = &ComputedUserset{} + } + if err := m.ComputedUserset.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourcePosition", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SourcePosition == nil { + m.SourcePosition = &SourcePosition{} + } + if err := m.SourcePosition.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *ComputedUserset) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/pkg/schemadsl/compiler/compiler_test.go b/pkg/schemadsl/compiler/compiler_test.go index 44dacb6d7b..f060f78f6c 100644 --- a/pkg/schemadsl/compiler/compiler_test.go +++ b/pkg/schemadsl/compiler/compiler_test.go @@ -938,6 +938,40 @@ func TestCompile(t *testing.T) { ), }, }, + { + "any arrow", + withTenantPrefix, + `definition simple { + permission foo = bar.any(baz) + }`, + "", + []SchemaDefinition{ + namespace.Namespace("sometenant/simple", + namespace.MustRelation("foo", + namespace.Union( + namespace.MustFunctionedTupleToUserset("bar", "any", "baz"), + ), + ), + ), + }, + }, + { + "all arrow", + withTenantPrefix, + `definition simple { + permission foo = bar.all(baz) + }`, + "", + []SchemaDefinition{ + namespace.Namespace("sometenant/simple", + namespace.MustRelation("foo", + namespace.Union( + namespace.MustFunctionedTupleToUserset("bar", "all", "baz"), + ), + ), + ), + }, + }, } for _, test := range tests { diff --git a/pkg/schemadsl/compiler/translator.go b/pkg/schemadsl/compiler/translator.go index de5a780ca6..4c0f491f51 100644 --- a/pkg/schemadsl/compiler/translator.go +++ b/pkg/schemadsl/compiler/translator.go @@ -520,6 +520,15 @@ func translateExpressionOperationDirect(tctx translationContext, expressionOpNod return nil, err } + if expressionOpNode.Has(dslshape.NodeArrowExpressionFunctionName) { + functionName, err := expressionOpNode.GetString(dslshape.NodeArrowExpressionFunctionName) + if err != nil { + return nil, err + } + + return namespace.MustFunctionedTupleToUserset(tuplesetRelation, functionName, usersetRelation), nil + } + return namespace.TupleToUserset(tuplesetRelation, usersetRelation), nil case dslshape.NodeTypeUnionExpression: diff --git a/pkg/schemadsl/generator/generator.go b/pkg/schemadsl/generator/generator.go index 6fbc95b53c..de8da899bd 100644 --- a/pkg/schemadsl/generator/generator.go +++ b/pkg/schemadsl/generator/generator.go @@ -221,7 +221,7 @@ func (sg *sourceGenerator) emitRelation(relation *core.Relation) error { if relation.UsersetRewrite != nil { sg.append(" = ") - sg.emitRewrite(relation.UsersetRewrite) + sg.mustEmitRewrite(relation.UsersetRewrite) } sg.appendLine() @@ -243,7 +243,7 @@ func (sg *sourceGenerator) emitAllowedRelation(allowedRelation *core.AllowedRela } } -func (sg *sourceGenerator) emitRewrite(rewrite *core.UsersetRewrite) { +func (sg *sourceGenerator) mustEmitRewrite(rewrite *core.UsersetRewrite) { switch rw := rewrite.RewriteOperation.(type) { case *core.UsersetRewrite_Union: sg.emitRewriteOps(rw.Union, "+") @@ -251,6 +251,8 @@ func (sg *sourceGenerator) emitRewrite(rewrite *core.UsersetRewrite) { sg.emitRewriteOps(rw.Intersection, "&") case *core.UsersetRewrite_Exclusion: sg.emitRewriteOps(rw.Exclusion, "-") + default: + panic(spiceerrors.MustBugf("unknown rewrite operation %T", rw)) } } @@ -260,7 +262,7 @@ func (sg *sourceGenerator) emitRewriteOps(setOp *core.SetOperation, op string) { sg.append(" " + op + " ") } - sg.emitSetOpChild(child) + sg.mustEmitSetOpChild(child) } } @@ -283,16 +285,16 @@ func (sg *sourceGenerator) isAllUnion(rewrite *core.UsersetRewrite) bool { } } -func (sg *sourceGenerator) emitSetOpChild(setOpChild *core.SetOperation_Child) { +func (sg *sourceGenerator) mustEmitSetOpChild(setOpChild *core.SetOperation_Child) { switch child := setOpChild.ChildType.(type) { case *core.SetOperation_Child_UsersetRewrite: if sg.isAllUnion(child.UsersetRewrite) { - sg.emitRewrite(child.UsersetRewrite) + sg.mustEmitRewrite(child.UsersetRewrite) break } sg.append("(") - sg.emitRewrite(child.UsersetRewrite) + sg.mustEmitRewrite(child.UsersetRewrite) sg.append(")") case *core.SetOperation_Child_XThis: @@ -308,6 +310,28 @@ func (sg *sourceGenerator) emitSetOpChild(setOpChild *core.SetOperation_Child) { sg.append(child.TupleToUserset.Tupleset.Relation) sg.append("->") sg.append(child.TupleToUserset.ComputedUserset.Relation) + + case *core.SetOperation_Child_FunctionedTupleToUserset: + sg.append(child.FunctionedTupleToUserset.Tupleset.Relation) + sg.append(".") + + switch child.FunctionedTupleToUserset.Function { + case core.FunctionedTupleToUserset_FUNCTION_ALL: + sg.append("all") + + case core.FunctionedTupleToUserset_FUNCTION_ANY: + sg.append("any") + + default: + panic(spiceerrors.MustBugf("unknown function %v", child.FunctionedTupleToUserset.Function)) + } + + sg.append("(") + sg.append(child.FunctionedTupleToUserset.ComputedUserset.Relation) + sg.append(")") + + default: + panic(spiceerrors.MustBugf("unknown child type %T", child)) } } diff --git a/pkg/schemadsl/generator/generator_test.go b/pkg/schemadsl/generator/generator_test.go index 3e08ae58b0..943d2a9462 100644 --- a/pkg/schemadsl/generator/generator_test.go +++ b/pkg/schemadsl/generator/generator_test.go @@ -351,6 +351,15 @@ definition foos/document { permission read = reader + writer + another permission write = writer permission minus = (rela - relb) - relc +}`, + }, + { + "different kinds of arrows", + `definition document{ + permission first = rela->relb + relc.any(reld) + rele.all(relf) +}`, + `definition document { + permission first = rela->relb + relc.any(reld) + rele.all(relf) }`, }, } diff --git a/proto/internal/core/v1/core.proto b/proto/internal/core/v1/core.proto index 880b4bed18..5fa55ea94f 100644 --- a/proto/internal/core/v1/core.proto +++ b/proto/internal/core/v1/core.proto @@ -427,6 +427,7 @@ message SetOperation { ComputedUserset computed_userset = 2 [(validate.rules).message.required = true]; TupleToUserset tuple_to_userset = 3 [(validate.rules).message.required = true]; UsersetRewrite userset_rewrite = 4 [(validate.rules).message.required = true]; + FunctionedTupleToUserset functioned_tuple_to_userset = 8 [(validate.rules).message.required = true]; Nil _nil = 6; } @@ -460,6 +461,26 @@ message TupleToUserset { SourcePosition source_position = 3; } +message FunctionedTupleToUserset { + enum Function { + FUNCTION_UNSPECIFIED = 0; + FUNCTION_ANY = 1; + FUNCTION_ALL = 2; + } + + message Tupleset { + string relation = 1 [(validate.rules).string = { + pattern: "^[a-z][a-z0-9_]{1,62}[a-z0-9]$", + max_bytes: 64, + }]; + } + + Function function = 1 [ (validate.rules).enum = {defined_only: true, not_in: [0]} ]; + Tupleset tupleset = 2 [(validate.rules).message.required = true]; + ComputedUserset computed_userset = 3 [(validate.rules).message.required = true]; + SourcePosition source_position = 4; +} + message ComputedUserset { enum Object { TUPLE_OBJECT = 0; From 263823bbf684d5d19aa09c1ea1e46c2655e9706e Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 13 Jun 2024 14:42:26 -0400 Subject: [PATCH 3/3] Add dispatching support for intersection arrows Fixes #597 --- internal/caveats/builder.go | 20 + internal/dispatch/graph/check_test.go | 715 +++++++++++++++++- internal/dispatch/graph/expand_test.go | 176 ++++- .../dispatch/graph/lookupresources_test.go | 40 + .../dispatch/graph/lookupsubjects_test.go | 283 ++++++- internal/graph/check.go | 252 +++++- internal/graph/expand.go | 32 +- internal/graph/graph.go | 8 + internal/graph/lookupsubjects.go | 220 +++++- internal/graph/membershipset.go | 30 + internal/namespace/canonicalization.go | 54 +- internal/namespace/canonicalization_test.go | 43 ++ .../caveatedintersectionarrow.yaml | 55 ++ .../testconfigs/intersectionarrow.yaml | 49 ++ internal/testutil/subjects.go | 2 +- pkg/graph/walker.go | 14 +- pkg/namespace/builder.go | 2 +- pkg/schemadsl/parser/parser_impl.go | 19 - pkg/typesystem/reachabilitygraph_test.go | 166 ++++ pkg/typesystem/reachabilitygraphbuilder.go | 136 ++-- pkg/typesystem/typesystem.go | 65 +- 21 files changed, 2258 insertions(+), 123 deletions(-) create mode 100644 internal/services/integrationtesting/testconfigs/caveatedintersectionarrow.yaml create mode 100644 internal/services/integrationtesting/testconfigs/intersectionarrow.yaml diff --git a/internal/caveats/builder.go b/internal/caveats/builder.go index f5a46fb572..faa4413158 100644 --- a/internal/caveats/builder.go +++ b/internal/caveats/builder.go @@ -1,6 +1,8 @@ package caveats import ( + "google.golang.org/protobuf/types/known/structpb" + core "github.com/authzed/spicedb/pkg/proto/core/v1" ) @@ -34,6 +36,24 @@ func CaveatExprForTesting(name string) *core.CaveatExpression { } } +// CaveatExprForTesting returns a CaveatExpression referencing a caveat with the given name and +// empty context. +func MustCaveatExprForTestingWithContext(name string, context map[string]any) *core.CaveatExpression { + contextStruct, err := structpb.NewStruct(context) + if err != nil { + panic(err) + } + + return &core.CaveatExpression{ + OperationOrCaveat: &core.CaveatExpression_Caveat{ + Caveat: &core.ContextualizedCaveat{ + CaveatName: name, + Context: contextStruct, + }, + }, + } +} + // ShortcircuitedOr combines two caveat expressions via an `||`. If one of the expressions is nil, // then the entire expression is *short-circuited*, and a nil is returned. func ShortcircuitedOr(first *core.CaveatExpression, second *core.CaveatExpression) *core.CaveatExpression { diff --git a/internal/dispatch/graph/check_test.go b/internal/dispatch/graph/check_test.go index 59cff585e4..e5ba8b0039 100644 --- a/internal/dispatch/graph/check_test.go +++ b/internal/dispatch/graph/check_test.go @@ -22,6 +22,7 @@ import ( "github.com/authzed/spicedb/pkg/genutil/mapz" core "github.com/authzed/spicedb/pkg/proto/core/v1" v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" + "github.com/authzed/spicedb/pkg/testutil" "github.com/authzed/spicedb/pkg/tuple" ) @@ -291,13 +292,6 @@ func TestCheckMetadata(t *testing.T) { } } -func addFrame(trace *v1.CheckDebugTrace, foundFrames *mapz.Set[string]) { - foundFrames.Insert(fmt.Sprintf("%s:%s#%s", trace.Request.ResourceRelation.Namespace, strings.Join(trace.Request.ResourceIds, ","), trace.Request.ResourceRelation.Relation)) - for _, subTrace := range trace.SubProblems { - addFrame(subTrace, foundFrames) - } -} - func TestCheckPermissionOverSchema(t *testing.T) { testCases := []struct { name string @@ -306,6 +300,7 @@ func TestCheckPermissionOverSchema(t *testing.T) { resource *core.ObjectAndRelation subject *core.ObjectAndRelation expectedPermissionship v1.ResourceCheckResult_Membership + expectedCaveat *core.CaveatExpression }{ { "basic union", @@ -322,6 +317,7 @@ func TestCheckPermissionOverSchema(t *testing.T) { ONR("document", "first", "view"), ONR("user", "tom", "..."), v1.ResourceCheckResult_MEMBER, + nil, }, { "basic intersection", @@ -339,6 +335,7 @@ func TestCheckPermissionOverSchema(t *testing.T) { ONR("document", "first", "view"), ONR("user", "tom", "..."), v1.ResourceCheckResult_MEMBER, + nil, }, { "basic exclusion", @@ -355,6 +352,7 @@ func TestCheckPermissionOverSchema(t *testing.T) { ONR("document", "first", "view"), ONR("user", "tom", "..."), v1.ResourceCheckResult_MEMBER, + nil, }, { "basic union, multiple branches", @@ -372,6 +370,7 @@ func TestCheckPermissionOverSchema(t *testing.T) { ONR("document", "first", "view"), ONR("user", "tom", "..."), v1.ResourceCheckResult_MEMBER, + nil, }, { "basic union no permission", @@ -386,6 +385,7 @@ func TestCheckPermissionOverSchema(t *testing.T) { ONR("document", "first", "view"), ONR("user", "tom", "..."), v1.ResourceCheckResult_NOT_MEMBER, + nil, }, { "basic intersection no permission", @@ -402,6 +402,7 @@ func TestCheckPermissionOverSchema(t *testing.T) { ONR("document", "first", "view"), ONR("user", "tom", "..."), v1.ResourceCheckResult_NOT_MEMBER, + nil, }, { "basic exclusion no permission", @@ -419,6 +420,7 @@ func TestCheckPermissionOverSchema(t *testing.T) { ONR("document", "first", "view"), ONR("user", "tom", "..."), v1.ResourceCheckResult_NOT_MEMBER, + nil, }, { "exclusion with multiple branches", @@ -444,6 +446,7 @@ func TestCheckPermissionOverSchema(t *testing.T) { ONR("document", "first", "view"), ONR("user", "tom", "..."), v1.ResourceCheckResult_MEMBER, + nil, }, { "intersection with multiple branches", @@ -469,6 +472,7 @@ func TestCheckPermissionOverSchema(t *testing.T) { ONR("document", "first", "view"), ONR("user", "tom", "..."), v1.ResourceCheckResult_MEMBER, + nil, }, { "exclusion with multiple branches no permission", @@ -495,6 +499,7 @@ func TestCheckPermissionOverSchema(t *testing.T) { ONR("document", "first", "view"), ONR("user", "tom", "..."), v1.ResourceCheckResult_NOT_MEMBER, + nil, }, { "intersection with multiple branches no permission", @@ -519,6 +524,690 @@ func TestCheckPermissionOverSchema(t *testing.T) { ONR("document", "first", "view"), ONR("user", "tom", "..."), v1.ResourceCheckResult_NOT_MEMBER, + nil, + }, + { + "basic arrow", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation orgs: organization + permission view = orgs->member + }`, + []*core.RelationTuple{ + tuple.MustParse("document:first#orgs@organization:first"), + tuple.MustParse("document:first#orgs@organization:second"), + tuple.MustParse("organization:second#member@user:tom"), + }, + ONR("document", "first", "view"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_MEMBER, + nil, + }, + { + "basic any arrow", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation orgs: organization + permission view = orgs.any(member) + }`, + []*core.RelationTuple{ + tuple.MustParse("document:first#orgs@organization:first"), + tuple.MustParse("document:first#orgs@organization:second"), + tuple.MustParse("organization:second#member@user:tom"), + }, + ONR("document", "first", "view"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_MEMBER, + nil, + }, + { + "basic all arrow negative", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation orgs: organization + permission view = orgs.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse("document:first#orgs@organization:first"), + tuple.MustParse("document:first#orgs@organization:second"), + tuple.MustParse("organization:second#member@user:tom"), + }, + ONR("document", "first", "view"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_NOT_MEMBER, + nil, + }, + { + "basic all arrow positive", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation orgs: organization + permission view = orgs.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse("document:first#orgs@organization:first"), + tuple.MustParse("document:first#orgs@organization:second"), + tuple.MustParse("organization:first#member@user:tom"), + tuple.MustParse("organization:second#member@user:tom"), + }, + ONR("document", "first", "view"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_MEMBER, + nil, + }, + { + "basic all arrow positive with different types", + `definition user {} + + definition organization { + relation member: user + } + + definition someotherresource { + relation member: user + } + + definition document { + relation orgs: organization | someotherresource + permission view = orgs.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse("document:first#orgs@organization:first"), + tuple.MustParse("document:first#orgs@organization:second"), + tuple.MustParse("organization:first#member@user:tom"), + tuple.MustParse("organization:second#member@user:tom"), + }, + ONR("document", "first", "view"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_MEMBER, + nil, + }, + { + "basic all arrow negative over different types", + `definition user {} + + definition organization { + relation member: user + } + + definition someotherresource { + relation member: user + } + + definition document { + relation orgs: organization | someotherresource + permission view = orgs.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse("document:first#orgs@organization:first"), + tuple.MustParse("document:first#orgs@organization:second"), + tuple.MustParse("document:first#orgs@someotherresource:other"), + tuple.MustParse("organization:first#member@user:tom"), + tuple.MustParse("organization:second#member@user:tom"), + }, + ONR("document", "first", "view"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_NOT_MEMBER, + nil, + }, + { + "basic all arrow positive over different types", + `definition user {} + + definition organization { + relation member: user + } + + definition someotherresource { + relation member: user + } + + definition document { + relation orgs: organization | someotherresource + permission view = orgs.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse("document:first#orgs@organization:first"), + tuple.MustParse("document:first#orgs@organization:second"), + tuple.MustParse("document:first#orgs@someotherresource:other"), + tuple.MustParse("organization:first#member@user:tom"), + tuple.MustParse("organization:second#member@user:tom"), + tuple.MustParse("someotherresource:other#member@user:tom"), + }, + ONR("document", "first", "view"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_MEMBER, + nil, + }, + { + "all arrow for single org", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation orgs: organization + permission view = orgs.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse("document:first#orgs@organization:first"), + tuple.MustParse("organization:first#member@user:tom"), + }, + ONR("document", "first", "view"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_MEMBER, + nil, + }, + { + "all arrow for no orgs", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation orgs: organization + permission view = orgs.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse("organization:first#member@user:tom"), + }, + ONR("document", "first", "view"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_NOT_MEMBER, + nil, + }, + { + "view_by_all negative", + ` definition user {} + + definition team { + relation direct_member: user + permission member = direct_member + } + + definition resource { + relation team: team + permission view_by_all = team.all(member) + permission view_by_any = team.any(member) + }`, + []*core.RelationTuple{ + tuple.MustParse("team:first#direct_member@user:tom"), + tuple.MustParse("team:first#direct_member@user:fred"), + tuple.MustParse("team:first#direct_member@user:sarah"), + tuple.MustParse("team:second#direct_member@user:fred"), + tuple.MustParse("team:second#direct_member@user:sarah"), + tuple.MustParse("team:third#direct_member@user:sarah"), + tuple.MustParse("resource:oneteam#team@team:first"), + tuple.MustParse("resource:twoteams#team@team:first"), + tuple.MustParse("resource:twoteams#team@team:second"), + tuple.MustParse("resource:threeteams#team@team:first"), + tuple.MustParse("resource:threeteams#team@team:second"), + tuple.MustParse("resource:threeteams#team@team:third"), + }, + ONR("resource", "threeteams", "view_by_all"), + ONR("user", "fred", "..."), + v1.ResourceCheckResult_NOT_MEMBER, + nil, + }, + { + "view_by_any positive", + ` definition user {} + + definition team { + relation direct_member: user + permission member = direct_member + } + + definition resource { + relation team: team + relation viewer: user + permission view_by_all = team.all(member) + viewer + permission view_by_any = team.any(member) + viewer + }`, + []*core.RelationTuple{ + tuple.MustParse("team:first#direct_member@user:tom"), + tuple.MustParse("team:first#direct_member@user:fred"), + tuple.MustParse("team:first#direct_member@user:sarah"), + tuple.MustParse("team:second#direct_member@user:fred"), + tuple.MustParse("team:second#direct_member@user:sarah"), + tuple.MustParse("team:third#direct_member@user:sarah"), + tuple.MustParse("resource:oneteam#team@team:first"), + tuple.MustParse("resource:twoteams#team@team:first"), + tuple.MustParse("resource:twoteams#team@team:second"), + tuple.MustParse("resource:threeteams#team@team:first"), + tuple.MustParse("resource:threeteams#team@team:second"), + tuple.MustParse("resource:threeteams#team@team:third"), + tuple.MustParse("resource:oneteam#viewer@user:rachel"), + }, + ONR("resource", "threeteams", "view_by_any"), + ONR("user", "fred", "..."), + v1.ResourceCheckResult_MEMBER, + nil, + }, + { + "view_by_any positive directly", + ` definition user {} + + definition team { + relation direct_member: user + permission member = direct_member + } + + definition resource { + relation team: team + relation viewer: user + permission view_by_all = team.all(member) + viewer + permission view_by_any = team.any(member) + viewer + }`, + []*core.RelationTuple{ + tuple.MustParse("team:first#direct_member@user:tom"), + tuple.MustParse("team:first#direct_member@user:fred"), + tuple.MustParse("team:first#direct_member@user:sarah"), + tuple.MustParse("team:second#direct_member@user:fred"), + tuple.MustParse("team:second#direct_member@user:sarah"), + tuple.MustParse("team:third#direct_member@user:sarah"), + tuple.MustParse("resource:oneteam#team@team:first"), + tuple.MustParse("resource:twoteams#team@team:first"), + tuple.MustParse("resource:twoteams#team@team:second"), + tuple.MustParse("resource:threeteams#team@team:first"), + tuple.MustParse("resource:threeteams#team@team:second"), + tuple.MustParse("resource:threeteams#team@team:third"), + tuple.MustParse("resource:oneteam#viewer@user:rachel"), + }, + ONR("resource", "oneteam", "view_by_any"), + ONR("user", "rachel", "..."), + v1.ResourceCheckResult_MEMBER, + nil, + }, + { + "caveated intersection arrow", + ` definition user {} + + definition team { + relation direct_member: user + permission member = direct_member + } + + caveat somecaveat(someparam int) { + someparam == 42 + } + + definition resource { + relation team: team with somecaveat + permission view_by_all = team.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse("team:first#direct_member@user:tom"), + tuple.MustParse("resource:oneteam#team@team:first[somecaveat]"), + }, + ONR("resource", "oneteam", "view_by_all"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_CAVEATED_MEMBER, + caveatAndCtx("somecaveat", nil), + }, + { + "intersection arrow with caveated member", + ` definition user {} + + definition team { + relation direct_member: user with somecaveat + permission member = direct_member + } + + caveat somecaveat(someparam int) { + someparam == 42 + } + + definition resource { + relation team: team + permission view_by_all = team.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse("team:first#direct_member@user:tom[somecaveat]"), + tuple.MustParse("resource:oneteam#team@team:first"), + }, + ONR("resource", "oneteam", "view_by_all"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_CAVEATED_MEMBER, + caveatAndCtx("somecaveat", nil), + }, + { + "caveated intersection arrow with caveated member", + ` definition user {} + + definition team { + relation direct_member: user with somecaveat + permission member = direct_member + } + + caveat somecaveat(someparam int) { + someparam == 42 + } + + definition resource { + relation team: team with somecaveat + permission view_by_all = team.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse("team:first#direct_member@user:tom[somecaveat]"), + tuple.MustParse("resource:oneteam#team@team:first[somecaveat]"), + }, + ONR("resource", "oneteam", "view_by_all"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_CAVEATED_MEMBER, + caveatAndCtx("somecaveat", nil), + }, + { + "caveated intersection arrow with caveated member, different context", + `definition user {} + + definition team { + relation direct_member: user with anothercaveat + permission member = direct_member + } + + caveat anothercaveat(someparam int) { + someparam == 43 + } + + caveat somecaveat(someparam int) { + someparam == 42 + } + + definition resource { + relation team: team with somecaveat + permission view_by_all = team.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse(`team:first#direct_member@user:tom[anothercaveat:{"someparam": 43}]`), + tuple.MustParse(`resource:oneteam#team@team:first[somecaveat:{"someparam": 42}]`), + }, + ONR("resource", "oneteam", "view_by_all"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_CAVEATED_MEMBER, + caveatAnd( + caveatAndCtx("anothercaveat", map[string]any{"someparam": int64(43)}), + caveatAndCtx("somecaveat", map[string]any{"someparam": int64(42)}), + ), + }, + { + "caveated intersection arrow with multiple caveated branches", + `definition user {} + + definition team { + relation direct_member: user + permission member = direct_member + } + + caveat somecaveat(someparam int) { + someparam >= 42 + } + + definition resource { + relation team: team with somecaveat + permission view_by_all = team.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse(`resource:someresource#team@team:first[somecaveat:{"someparam": 41}]`), + tuple.MustParse(`resource:someresource#team@team:second[somecaveat:{"someparam": 42}]`), + tuple.MustParse(`team:first#direct_member@user:tom`), + tuple.MustParse(`team:second#direct_member@user:tom`), + }, + ONR("resource", "someresource", "view_by_all"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_CAVEATED_MEMBER, + caveatAnd( + caveatAndCtx("somecaveat", map[string]any{"someparam": int64(41)}), + caveatAndCtx("somecaveat", map[string]any{"someparam": int64(42)}), + ), + }, + + { + "caveated intersection arrow with multiple caveated members", + `definition user {} + + definition team { + relation direct_member: user with somecaveat + permission member = direct_member + } + + caveat somecaveat(someparam int) { + someparam >= 42 + } + + definition resource { + relation team: team + permission view_by_all = team.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse(`resource:someresource#team@team:first`), + tuple.MustParse(`resource:someresource#team@team:second`), + tuple.MustParse(`team:first#direct_member@user:tom[somecaveat:{"someparam": 41}]`), + tuple.MustParse(`team:second#direct_member@user:tom[somecaveat:{"someparam": 42}]`), + }, + ONR("resource", "someresource", "view_by_all"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_CAVEATED_MEMBER, + caveatAnd( + caveatAndCtx("somecaveat", map[string]any{"someparam": int64(41)}), + caveatAndCtx("somecaveat", map[string]any{"someparam": int64(42)}), + ), + }, + { + "caveated intersection arrow with one caveated branch", + `definition user {} + + definition team { + relation direct_member: user + permission member = direct_member + } + + caveat somecaveat(someparam int) { + someparam >= 42 + } + + definition resource { + relation team: team with somecaveat | team + permission view_by_all = team.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse(`resource:someresource#team@team:first`), + tuple.MustParse(`resource:someresource#team@team:second[somecaveat:{"someparam": 42}]`), + tuple.MustParse(`team:first#direct_member@user:tom`), + tuple.MustParse(`team:second#direct_member@user:tom`), + }, + ONR("resource", "someresource", "view_by_all"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_CAVEATED_MEMBER, + caveatAndCtx("somecaveat", map[string]any{"someparam": int64(42)}), + }, + { + "caveated intersection arrow with one caveated member", + `definition user {} + + definition team { + relation direct_member: user with somecaveat + permission member = direct_member + } + + caveat somecaveat(someparam int) { + someparam >= 42 + } + + definition resource { + relation team: team + permission view_by_all = team.all(member) + }`, + []*core.RelationTuple{ + tuple.MustParse(`resource:someresource#team@team:first`), + tuple.MustParse(`resource:someresource#team@team:second`), + tuple.MustParse(`team:first#direct_member@user:tom`), + tuple.MustParse(`team:second#direct_member@user:tom[somecaveat:{"someparam": 42}]`), + }, + ONR("resource", "someresource", "view_by_all"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_CAVEATED_MEMBER, + caveatAndCtx("somecaveat", map[string]any{"someparam": int64(42)}), + }, + { + "caveated intersection arrow multiple paths to the same subject", + `definition user {} + + definition team { + relation direct_member: user + permission member = direct_member + } + + caveat somecaveat(someparam int) { + someparam >= 42 + } + + definition resource { + relation team: team with somecaveat | team#direct_member with somecaveat + permission view_by_all = team.all(member) // Note: this points to the same team twice + }`, + []*core.RelationTuple{ + tuple.MustParse(`resource:someresource#team@team:first`), + tuple.MustParse(`resource:someresource#team@team:first#direct_member[somecaveat]`), + tuple.MustParse(`team:first#direct_member@user:tom`), + }, + ONR("resource", "someresource", "view_by_all"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_CAVEATED_MEMBER, + caveatAndCtx("somecaveat", nil), + }, + { + "recursive all arrow positive result", + `definition user {} + + definition folder { + relation parent: folder + relation owner: user + + permission view = parent.all(owner) + } + + definition document { + relation folder: folder + permission view = folder.all(view) + }`, + []*core.RelationTuple{ + tuple.MustParse("folder:root1#owner@user:tom"), + tuple.MustParse("folder:root1#owner@user:fred"), + tuple.MustParse("folder:root1#owner@user:sarah"), + tuple.MustParse("folder:root2#owner@user:fred"), + tuple.MustParse("folder:root2#owner@user:sarah"), + + tuple.MustParse("folder:child1#parent@folder:root1"), + tuple.MustParse("folder:child1#parent@folder:root2"), + + tuple.MustParse("folder:child2#parent@folder:root1"), + tuple.MustParse("folder:child2#parent@folder:root2"), + + tuple.MustParse("document:doc1#folder@folder:child1"), + tuple.MustParse("document:doc1#folder@folder:child2"), + }, + ONR("document", "doc1", "view"), + ONR("user", "fred", "..."), + v1.ResourceCheckResult_MEMBER, + nil, + }, + { + "recursive all arrow negative result", + `definition user {} + + definition folder { + relation parent: folder + relation owner: user + + permission view = parent.all(owner) + } + + definition document { + relation folder: folder + permission view = folder.all(view) + }`, + []*core.RelationTuple{ + tuple.MustParse("folder:root1#owner@user:tom"), + tuple.MustParse("folder:root1#owner@user:fred"), + tuple.MustParse("folder:root1#owner@user:sarah"), + tuple.MustParse("folder:root2#owner@user:fred"), + tuple.MustParse("folder:root2#owner@user:sarah"), + + tuple.MustParse("folder:child1#parent@folder:root1"), + tuple.MustParse("folder:child1#parent@folder:root2"), + + tuple.MustParse("folder:child2#parent@folder:root1"), + tuple.MustParse("folder:child2#parent@folder:root2"), + + tuple.MustParse("document:doc1#folder@folder:child1"), + tuple.MustParse("document:doc1#folder@folder:child2"), + }, + ONR("document", "doc1", "view"), + ONR("user", "tom", "..."), + v1.ResourceCheckResult_NOT_MEMBER, + nil, + }, + { + "recursive all arrow negative result due to recursion missing a folder", + `definition user {} + + definition folder { + relation parent: folder + relation owner: user + + permission view = parent.all(owner) + } + + definition document { + relation folder: folder + permission view = folder.all(view) + }`, + []*core.RelationTuple{ + tuple.MustParse("folder:root1#owner@user:tom"), + tuple.MustParse("folder:root1#owner@user:fred"), + tuple.MustParse("folder:root1#owner@user:sarah"), + tuple.MustParse("folder:root2#owner@user:fred"), + tuple.MustParse("folder:root2#owner@user:sarah"), + + tuple.MustParse("folder:child1#parent@folder:root1"), + tuple.MustParse("folder:child1#parent@folder:root2"), + tuple.MustParse("folder:child1#parent@folder:root3"), + + tuple.MustParse("folder:child2#parent@folder:root1"), + tuple.MustParse("folder:child2#parent@folder:root2"), + + tuple.MustParse("document:doc1#folder@folder:child1"), + tuple.MustParse("document:doc1#folder@folder:child2"), + }, + ONR("document", "doc1", "view"), + ONR("user", "fred", "..."), + v1.ResourceCheckResult_NOT_MEMBER, + nil, }, } @@ -555,10 +1244,22 @@ func TestCheckPermissionOverSchema(t *testing.T) { } require.Equal(tc.expectedPermissionship, membership) + + if tc.expectedCaveat != nil { + require.NotEmpty(resp.ResultsByResourceId[tc.resource.ObjectId].Expression) + testutil.RequireProtoEqual(t, tc.expectedCaveat, resp.ResultsByResourceId[tc.resource.ObjectId].Expression, "mismatch in caveat") + } }) } } +func addFrame(trace *v1.CheckDebugTrace, foundFrames *mapz.Set[string]) { + foundFrames.Insert(fmt.Sprintf("%s:%s#%s", trace.Request.ResourceRelation.Namespace, strings.Join(trace.Request.ResourceIds, ","), trace.Request.ResourceRelation.Relation)) + for _, subTrace := range trace.SubProblems { + addFrame(subTrace, foundFrames) + } +} + func TestCheckDebugging(t *testing.T) { type expectedFrame struct { resourceType *core.RelationReference diff --git a/internal/dispatch/graph/expand_test.go b/internal/dispatch/graph/expand_test.go index 466e83f482..99b26526f3 100644 --- a/internal/dispatch/graph/expand_test.go +++ b/internal/dispatch/graph/expand_test.go @@ -305,7 +305,7 @@ func TestMaxDepthExpand(t *testing.T) { require.Error(err) } -func TestCaveatedExpand(t *testing.T) { +func TestExpandOverSchema(t *testing.T) { defer goleak.VerifyNone(t, goleakIgnores...) testCases := []struct { @@ -318,6 +318,180 @@ func TestCaveatedExpand(t *testing.T) { expansionMode v1.DispatchExpandRequest_ExpansionMode expectedTreeText string }{ + { + "basic any arrow", + ` + definition user {} + + definition folder { + relation viewer: user + } + + definition document { + relation folder: folder + permission view = folder->viewer + }`, + []*core.RelationTuple{ + tuple.MustParse("document:testdoc#folder@folder:testfolder1"), + tuple.MustParse("document:testdoc#folder@folder:testfolder2"), + tuple.MustParse("folder:testfolder1#viewer@user:tom"), + tuple.MustParse("folder:testfolder1#viewer@user:fred"), + tuple.MustParse("folder:testfolder2#viewer@user:sarah"), + }, + + tuple.ParseONR("document:testdoc#view"), + + v1.DispatchExpandRequest_SHALLOW, + `intermediate_node: { + operation: UNION + child_nodes: { + intermediate_node: { + operation: UNION + child_nodes: { + leaf_node: { + subjects: { + subject: { + namespace: "user" + object_id: "fred" + relation: "..." + } + } + subjects: { + subject: { + namespace: "user" + object_id: "tom" + relation: "..." + } + } + } + expanded: { + namespace: "folder" + object_id: "testfolder1" + relation: "viewer" + } + } + child_nodes: { + leaf_node: { + subjects: { + subject: { + namespace: "user" + object_id: "sarah" + relation: "..." + } + } + } + expanded: { + namespace: "folder" + object_id: "testfolder2" + relation: "viewer" + } + } + } + expanded: { + namespace: "document" + object_id: "testdoc" + relation: "view" + } + } + } + expanded: { + namespace: "document" + object_id: "testdoc" + relation: "view" + }`, + }, + { + "basic all arrow", + ` + definition user {} + + definition folder { + relation viewer: user + } + + definition document { + relation folder: folder + permission view = folder.all(viewer) + }`, + []*core.RelationTuple{ + tuple.MustParse("document:testdoc#folder@folder:testfolder1"), + tuple.MustParse("document:testdoc#folder@folder:testfolder2"), + tuple.MustParse("folder:testfolder1#viewer@user:tom"), + tuple.MustParse("folder:testfolder1#viewer@user:fred"), + tuple.MustParse("folder:testfolder2#viewer@user:tom"), + tuple.MustParse("folder:testfolder2#viewer@user:sarah"), + }, + + tuple.ParseONR("document:testdoc#view"), + + v1.DispatchExpandRequest_SHALLOW, + ` + intermediate_node: { + operation: UNION + child_nodes: { + intermediate_node: { + operation: INTERSECTION + child_nodes: { + leaf_node: { + subjects: { + subject: { + namespace: "user" + object_id: "fred" + relation: "..." + } + } + subjects: { + subject: { + namespace: "user" + object_id: "tom" + relation: "..." + } + } + } + expanded: { + namespace: "folder" + object_id: "testfolder1" + relation: "viewer" + } + } + child_nodes: { + leaf_node: { + subjects: { + subject: { + namespace: "user" + object_id: "sarah" + relation: "..." + } + } + subjects: { + subject: { + namespace: "user" + object_id: "tom" + relation: "..." + } + } + } + expanded: { + namespace: "folder" + object_id: "testfolder2" + relation: "viewer" + } + } + } + expanded: { + namespace: "document" + object_id: "testdoc" + relation: "view" + } + } + } + expanded: { + namespace: "document" + object_id: "testdoc" + relation: "view" + } + `, + }, { "basic caveated subject", ` diff --git a/internal/dispatch/graph/lookupresources_test.go b/internal/dispatch/graph/lookupresources_test.go index fbc73287b4..d7077a50af 100644 --- a/internal/dispatch/graph/lookupresources_test.go +++ b/internal/dispatch/graph/lookupresources_test.go @@ -544,6 +544,46 @@ func TestLookupResourcesOverSchemaWithCursors(t *testing.T) { ONR("user", "tom", "..."), genResourceIds("document", 15100), }, + { + "all arrow", + `definition user {} + + definition folder { + relation viewer: user + } + + definition document { + relation parent: folder + relation viewer: user + permission view = parent.all(viewer) + viewer + }`, + []*core.RelationTuple{ + tuple.MustParse("document:doc0#parent@folder:folder0"), + tuple.MustParse("folder:folder0#viewer@user:tom"), + + tuple.MustParse("document:doc1#parent@folder:folder1-1"), + tuple.MustParse("document:doc1#parent@folder:folder1-2"), + tuple.MustParse("document:doc1#parent@folder:folder1-3"), + tuple.MustParse("folder:folder1-1#viewer@user:tom"), + tuple.MustParse("folder:folder1-2#viewer@user:tom"), + tuple.MustParse("folder:folder1-3#viewer@user:tom"), + + tuple.MustParse("document:doc2#parent@folder:folder2-1"), + tuple.MustParse("document:doc2#parent@folder:folder2-2"), + tuple.MustParse("document:doc2#parent@folder:folder2-3"), + tuple.MustParse("folder:folder2-1#viewer@user:tom"), + tuple.MustParse("folder:folder2-2#viewer@user:tom"), + + tuple.MustParse("document:doc3#parent@folder:folder3-1"), + + tuple.MustParse("document:doc4#viewer@user:tom"), + + tuple.MustParse("document:doc5#viewer@user:fred"), + }, + RR("document", "view"), + ONR("user", "tom", "..."), + []string{"doc0", "doc1", "doc4"}, + }, } for _, tc := range testCases { diff --git a/internal/dispatch/graph/lookupsubjects_test.go b/internal/dispatch/graph/lookupsubjects_test.go index 3aabce3592..15ade5e692 100644 --- a/internal/dispatch/graph/lookupsubjects_test.go +++ b/internal/dispatch/graph/lookupsubjects_test.go @@ -24,6 +24,7 @@ import ( var ( caveatexpr = caveats.CaveatExprForTesting + caveatAndCtx = caveats.MustCaveatExprForTestingWithContext caveatAnd = caveats.And caveatInvert = caveats.Invert ) @@ -275,7 +276,7 @@ func TestLookupSubjectsDispatchCount(t *testing.T) { } } -func TestCaveatedLookupSubjects(t *testing.T) { +func TestLookupSubjectsOverSchema(t *testing.T) { testCases := []struct { name string schema string @@ -707,6 +708,286 @@ func TestCaveatedLookupSubjects(t *testing.T) { }, }, }, + { + "simple arrow", + `definition user {} + + definition folder { + relation viewer: user + permission view = viewer + } + + definition document { + relation folder: folder + permission view = folder->view + }`, + []*corev1.RelationTuple{ + tuple.MustParse("folder:folder1#viewer@user:tom"), + tuple.MustParse("folder:folder1#viewer@user:fred"), + tuple.MustParse("document:somedoc#folder@folder:folder1"), + }, + ONR("document", "somedoc", "view"), + RR("user", "..."), + []*v1.FoundSubject{ + { + SubjectId: "tom", + }, + { + SubjectId: "fred", + }, + }, + }, + { + "simple any arrow", + `definition user {} + + definition folder { + relation viewer: user + permission view = viewer + } + + definition document { + relation folder: folder + permission view = folder.any(view) + }`, + []*corev1.RelationTuple{ + tuple.MustParse("folder:folder1#viewer@user:tom"), + tuple.MustParse("folder:folder1#viewer@user:fred"), + tuple.MustParse("document:somedoc#folder@folder:folder1"), + }, + ONR("document", "somedoc", "view"), + RR("user", "..."), + []*v1.FoundSubject{ + { + SubjectId: "tom", + }, + { + SubjectId: "fred", + }, + }, + }, + { + "simple all arrow", + `definition user {} + + definition folder { + relation viewer: user + permission view = viewer + } + + definition document { + relation folder: folder + permission view = folder.all(view) + }`, + []*corev1.RelationTuple{ + tuple.MustParse("folder:folder1#viewer@user:tom"), + tuple.MustParse("folder:folder1#viewer@user:fred"), + tuple.MustParse("document:somedoc#folder@folder:folder1"), + }, + ONR("document", "somedoc", "view"), + RR("user", "..."), + []*v1.FoundSubject{ + { + SubjectId: "tom", + }, + { + SubjectId: "fred", + }, + }, + }, + { + "all arrow multiple", + `definition user {} + + definition folder { + relation viewer: user + permission view = viewer + } + + definition document { + relation folder: folder + permission view = folder.all(view) + }`, + []*corev1.RelationTuple{ + tuple.MustParse("folder:folder1#viewer@user:tom"), + tuple.MustParse("folder:folder1#viewer@user:fred"), + tuple.MustParse("folder:folder2#viewer@user:fred"), + tuple.MustParse("document:somedoc#folder@folder:folder1"), + tuple.MustParse("document:somedoc#folder@folder:folder2"), + }, + ONR("document", "somedoc", "view"), + RR("user", "..."), + []*v1.FoundSubject{ + { + SubjectId: "fred", + }, + }, + }, + { + "all arrow over multiple resource IDs", + `definition user {} + + definition organization { + relation member: user + } + + definition folder { + relation parent: organization + permission view = parent.all(member) + } + + definition document { + relation folder: folder + permission view = folder->view + }`, + []*corev1.RelationTuple{ + tuple.MustParse("document:somedoc#folder@folder:folder1"), + tuple.MustParse("document:somedoc#folder@folder:folder2"), + tuple.MustParse("folder:folder1#parent@organization:org1"), + tuple.MustParse("folder:folder2#parent@organization:org2"), + tuple.MustParse("folder:folder2#parent@organization:org3"), + tuple.MustParse("organization:org1#member@user:fred"), + tuple.MustParse("organization:org2#member@user:tom"), + tuple.MustParse("organization:org3#member@user:tom"), + tuple.MustParse("organization:org2#member@user:sarah"), + }, + ONR("document", "somedoc", "view"), + RR("user", "..."), + []*v1.FoundSubject{ + { + SubjectId: "tom", + }, + { + SubjectId: "fred", + }, + }, + }, + { + "intersection arrow over caveated teams", + `definition user {} + + definition team { + relation member: user | user with anothercaveat + } + + caveat caveat1(someparam1 int) { + someparam1 == 42 + } + + caveat caveat2(someparam2 int) { + someparam2 == 42 + } + + caveat anothercaveat(anotherparam int) { + anotherparam == 43 + } + + definition document { + relation team: team with caveat1 | team with caveat2 + permission view = team.all(member) + }`, + []*corev1.RelationTuple{ + tuple.MustParse(`document:somedoc#team@team:team1[caveat1:{":someparam1":42}]`), + tuple.MustParse(`document:somedoc#team@team:team2[caveat2:{":someparam2":43}]`), + tuple.MustParse(`team:team1#member@user:tom`), + tuple.MustParse(`team:team2#member@user:tom`), + tuple.MustParse(`team:team1#member@user:fred`), + tuple.MustParse(`team:team2#member@user:fred[anothercaveat:{":anotherparam":43}]`), + tuple.MustParse(`team:team1#member@user:sarah`), + }, + ONR("document", "somedoc", "view"), + RR("user", "..."), + []*v1.FoundSubject{ + { + SubjectId: "tom", + CaveatExpression: caveatAnd( + caveatAndCtx("caveat1", map[string]interface{}{"someparam1": 42}), + caveatAndCtx("caveat2", map[string]interface{}{"someparam2": 43}), + ), + }, + { + SubjectId: "fred", + CaveatExpression: caveatAnd( + caveatAnd( + caveatAndCtx("caveat1", map[string]interface{}{"someparam1": 42}), + caveatAndCtx("caveat2", map[string]interface{}{"someparam2": 43}), + ), + caveatAndCtx("anothercaveat", map[string]interface{}{"anotherparam": 43}), + ), + }, + }, + }, + { + "all arrow minus banned", + `definition user {} + + definition folder { + relation viewer: user + permission view = viewer + } + + definition document { + relation banned: user + relation folder: folder + permission view = folder.all(view) - banned + }`, + []*corev1.RelationTuple{ + tuple.MustParse("folder:folder1#viewer@user:tom"), + tuple.MustParse("folder:folder1#viewer@user:fred"), + tuple.MustParse("document:somedoc#folder@folder:folder1"), + tuple.MustParse("document:somedoc#banned@user:fred"), + tuple.MustParse("document:somedoc#banned@user:sarah"), + }, + ONR("document", "somedoc", "view"), + RR("user", "..."), + []*v1.FoundSubject{ + { + SubjectId: "tom", + }, + }, + }, + { + "recursive all arrow ", + `definition user {} + + definition folder { + relation parent: folder + relation owner: user + + permission view = parent.all(owner) + } + + definition document { + relation folder: folder + permission view = folder.all(view) + }`, + []*corev1.RelationTuple{ + tuple.MustParse("folder:root1#owner@user:tom"), + tuple.MustParse("folder:root1#owner@user:fred"), + tuple.MustParse("folder:root1#owner@user:sarah"), + tuple.MustParse("folder:root2#owner@user:fred"), + tuple.MustParse("folder:root2#owner@user:sarah"), + + tuple.MustParse("folder:child1#parent@folder:root1"), + tuple.MustParse("folder:child1#parent@folder:root2"), + + tuple.MustParse("folder:child2#parent@folder:root1"), + tuple.MustParse("folder:child2#parent@folder:root2"), + + tuple.MustParse("document:doc1#folder@folder:child1"), + tuple.MustParse("document:doc1#folder@folder:child2"), + }, + ONR("document", "doc1", "view"), + RR("user", "..."), + []*v1.FoundSubject{ + { + SubjectId: "fred", + }, + { + SubjectId: "sarah", + }, + }, + }, } for _, tc := range testCases { diff --git a/internal/graph/check.go b/internal/graph/check.go index 606d03ddc6..7432512a8f 100644 --- a/internal/graph/check.go +++ b/internal/graph/check.go @@ -484,11 +484,23 @@ func (cc *ConcurrentChecker) runSetOperation(ctx context.Context, crc currentReq case *core.SetOperation_Child_UsersetRewrite: return cc.checkUsersetRewrite(ctx, crc, child.UsersetRewrite) case *core.SetOperation_Child_TupleToUserset: - return cc.checkTupleToUserset(ctx, crc, child.TupleToUserset) + return checkTupleToUserset(ctx, cc, crc, child.TupleToUserset) + case *core.SetOperation_Child_FunctionedTupleToUserset: + switch child.FunctionedTupleToUserset.Function { + case core.FunctionedTupleToUserset_FUNCTION_ANY: + return checkTupleToUserset(ctx, cc, crc, child.FunctionedTupleToUserset) + + case core.FunctionedTupleToUserset_FUNCTION_ALL: + return checkIntersectionTupleToUserset(ctx, cc, crc, child.FunctionedTupleToUserset) + + default: + return checkResultError(spiceerrors.MustBugf("unknown userset function `%s`", child.FunctionedTupleToUserset.Function), emptyMetadata) + } + case *core.SetOperation_Child_XNil: return noMembers() default: - return checkResultError(fmt.Errorf("unknown set operation child `%T` in check", child), emptyMetadata) + return checkResultError(spiceerrors.MustBugf("unknown set operation child `%T` in check", child), emptyMetadata) } } @@ -576,8 +588,186 @@ func removeIndexFromSlice[T any](s []T, index int) []T { return append(cpy, s[index+1:]...) } -func (cc *ConcurrentChecker) checkTupleToUserset(ctx context.Context, crc currentRequestContext, ttu *core.TupleToUserset) CheckResult { - ctx, span := tracer.Start(ctx, ttu.Tupleset.Relation+"->"+ttu.ComputedUserset.Relation) +type relation interface { + GetRelation() string +} + +type ttu[T relation] interface { + GetComputedUserset() *core.ComputedUserset + GetTupleset() T +} + +type checkResultWithType struct { + CheckResult + + relationType *core.RelationReference +} + +func checkIntersectionTupleToUserset( + ctx context.Context, + cc *ConcurrentChecker, + crc currentRequestContext, + ttu *core.FunctionedTupleToUserset, +) CheckResult { + ctx, span := tracer.Start(ctx, ttu.GetTupleset().GetRelation()+"-(all)->"+ttu.GetComputedUserset().Relation) + defer span.End() + + // Query for the subjects over which to walk the TTU. + log.Ctx(ctx).Trace().Object("intersectionttu", crc.parentReq).Send() + ds := datastoremw.MustFromContext(ctx).SnapshotReader(crc.parentReq.Revision) + it, err := ds.QueryRelationships(ctx, datastore.RelationshipsFilter{ + OptionalResourceType: crc.parentReq.ResourceRelation.Namespace, + OptionalResourceIds: crc.filteredResourceIDs, + OptionalResourceRelation: ttu.GetTupleset().GetRelation(), + }) + if err != nil { + return checkResultError(NewCheckFailureErr(err), emptyMetadata) + } + defer it.Close() + + subjectsToDispatch := tuple.NewONRByTypeSet() + relationshipsBySubjectONR := mapz.NewMultiMap[string, *core.RelationTuple]() + subjectsByResourceID := mapz.NewMultiMap[string, *core.ObjectAndRelation]() + for tpl := it.Next(); tpl != nil; tpl = it.Next() { + if it.Err() != nil { + return checkResultError(NewCheckFailureErr(it.Err()), emptyMetadata) + } + + subjectsToDispatch.Add(tpl.Subject) + relationshipsBySubjectONR.Add(tuple.StringONR(tpl.Subject), tpl) + subjectsByResourceID.Add(tpl.ResourceAndRelation.ObjectId, tpl.Subject) + } + it.Close() + + // Convert the subjects into batched requests. + // To simplify the logic, +1 is added to account for the situation where + // the number of elements is less than the chunk size, and spare us some annoying code. + expectedNumberOfChunks := uint16(subjectsToDispatch.ValueLen())/crc.maxDispatchCount + 1 + toDispatch := make([]directDispatch, 0, expectedNumberOfChunks) + subjectsToDispatch.ForEachType(func(rr *core.RelationReference, resourceIds []string) { + chunkCount := 0.0 + slicez.ForEachChunk(resourceIds, crc.maxDispatchCount, func(resourceIdChunk []string) { + chunkCount++ + toDispatch = append(toDispatch, directDispatch{ + resourceType: rr, + resourceIds: resourceIdChunk, + }) + }) + dispatchChunkCountHistogram.Observe(chunkCount) + }) + + if subjectsToDispatch.IsEmpty() { + return noMembers() + } + + // Run the dispatch for all the chunks. Unlike a standard TTU, we do *not* perform mapping here, + // as we need to access the results on a per subject basis. Instead, we keep each result and map + // by the relation type of the dispatched subject. + chunkResults, err := run( + ctx, + currentRequestContext{ + parentReq: crc.parentReq, + filteredResourceIDs: crc.filteredResourceIDs, + resultsSetting: v1.DispatchCheckRequest_REQUIRE_ALL_RESULTS, + maxDispatchCount: crc.maxDispatchCount, + }, + toDispatch, + func(ctx context.Context, crc currentRequestContext, dd directDispatch) checkResultWithType { + childResult := cc.checkComputedUserset(ctx, crc, ttu.GetComputedUserset(), dd.resourceType, dd.resourceIds) + return checkResultWithType{ + CheckResult: childResult, + relationType: dd.resourceType, + } + }, + cc.concurrencyLimit, + ) + if err != nil { + return checkResultError(err, emptyMetadata) + } + + // Create a membership set per-subject-type, representing the membership for each of the dispatched subjects. + resultsByDispatchedSubject := map[string]*MembershipSet{} + combinedMetadata := emptyMetadata + for _, result := range chunkResults { + if result.Err != nil { + return checkResultError(result.Err, emptyMetadata) + } + + typeKey := tuple.StringRR(result.relationType) + if _, ok := resultsByDispatchedSubject[typeKey]; !ok { + resultsByDispatchedSubject[typeKey] = NewMembershipSet() + } + + resultsByDispatchedSubject[typeKey].UnionWith(result.Resp.ResultsByResourceId) + combinedMetadata = combineResponseMetadata(combinedMetadata, result.Resp.Metadata) + } + + // For each resource ID, check that there exist some sort of permission for *each* subject. If not, then the + // intersection for that resource fails. If all subjects have some sort of permission, then the resource ID is + // a member, perhaps caveated. + resourcesFound := NewMembershipSet() + for _, resourceID := range subjectsByResourceID.Keys() { + subjects, _ := subjectsByResourceID.Get(resourceID) + if len(subjects) == 0 { + return checkResultError(spiceerrors.MustBugf("no subjects found for resource ID %s", resourceID), emptyMetadata) + } + + hasAllSubjects := true + caveats := make([]*core.CaveatExpression, 0, len(subjects)) + + // Check each of the subjects found for the resource ID and ensure that membership (at least caveated) + // was found for each. If any are not found, then the resource ID is not a member. + // We also collect up the caveats for each subject, as they will be added to the final result. + for _, subject := range subjects { + subjectTypeKey := tuple.StringRR(&core.RelationReference{ + Namespace: subject.Namespace, + Relation: subject.Relation, + }) + + results, ok := resultsByDispatchedSubject[subjectTypeKey] + if !ok { + hasAllSubjects = false + break + } + + hasMembership, caveat := results.GetResourceID(subject.ObjectId) + if !hasMembership { + hasAllSubjects = false + break + } + + if caveat != nil { + caveats = append(caveats, caveat) + } + + // Add any caveats on the subject from the starting relationship(s) as well. + subjectKey := tuple.StringONR(subject) + tuples, _ := relationshipsBySubjectONR.Get(subjectKey) + for _, relationTuple := range tuples { + if relationTuple.Caveat != nil { + caveats = append(caveats, wrapCaveat(relationTuple.Caveat)) + } + } + } + + if !hasAllSubjects { + continue + } + + // Add the member to the membership set, with the caveats for each (if any). + resourcesFound.AddMemberWithOptionalCaveats(resourceID, caveats) + } + + return checkResultsForMembership(resourcesFound, combinedMetadata) +} + +func checkTupleToUserset[T relation]( + ctx context.Context, + cc *ConcurrentChecker, + crc currentRequestContext, + ttu ttu[T], +) CheckResult { + ctx, span := tracer.Start(ctx, ttu.GetTupleset().GetRelation()+"->"+ttu.GetComputedUserset().Relation) defer span.End() log.Ctx(ctx).Trace().Object("ttu", crc.parentReq).Send() @@ -585,7 +775,7 @@ func (cc *ConcurrentChecker) checkTupleToUserset(ctx context.Context, crc curren it, err := ds.QueryRelationships(ctx, datastore.RelationshipsFilter{ OptionalResourceType: crc.parentReq.ResourceRelation.Namespace, OptionalResourceIds: crc.filteredResourceIDs, - OptionalResourceRelation: ttu.Tupleset.Relation, + OptionalResourceRelation: ttu.GetTupleset().GetRelation(), }) if err != nil { return checkResultError(NewCheckFailureErr(err), emptyMetadata) @@ -607,7 +797,7 @@ func (cc *ConcurrentChecker) checkTupleToUserset(ctx context.Context, crc curren // Convert the subjects into batched requests. // To simplify the logic, +1 is added to account for the situation where // the number of elements is less than the chunk size, and spare us some annoying code. - expectedNumberOfChunks := subjectsToDispatch.ValueLen()/int(crc.maxDispatchCount) + 1 + expectedNumberOfChunks := uint16(subjectsToDispatch.ValueLen())/crc.maxDispatchCount + 1 toDispatch := make([]directDispatch, 0, expectedNumberOfChunks) subjectsToDispatch.ForEachType(func(rr *core.RelationReference, resourceIds []string) { chunkCount := 0.0 @@ -626,7 +816,7 @@ func (cc *ConcurrentChecker) checkTupleToUserset(ctx context.Context, crc curren crc, toDispatch, func(ctx context.Context, crc currentRequestContext, dd directDispatch) CheckResult { - childResult := cc.checkComputedUserset(ctx, crc, ttu.ComputedUserset, dd.resourceType, dd.resourceIds) + childResult := cc.checkComputedUserset(ctx, crc, ttu.GetComputedUserset(), dd.resourceType, dd.resourceIds) if childResult.Err != nil { return childResult } @@ -648,6 +838,42 @@ func withDistinctMetadata(result CheckResult) CheckResult { } } +// run runs all the children in parallel and returns the full set of results. +func run[T any, R withError]( + ctx context.Context, + crc currentRequestContext, + children []T, + handler func(ctx context.Context, crc currentRequestContext, child T) R, + concurrencyLimit uint16, +) ([]R, error) { + if len(children) == 0 { + return nil, nil + } + + if len(children) == 1 { + return []R{handler(ctx, crc, children[0])}, nil + } + + resultChan := make(chan R, len(children)) + childCtx, cancelFn := context.WithCancel(ctx) + dispatchAllAsync(childCtx, crc, children, handler, resultChan, concurrencyLimit) + defer cancelFn() + + results := make([]R, 0, len(children)) + for i := 0; i < len(children); i++ { + select { + case result := <-resultChan: + results = append(results, result) + + case <-ctx.Done(): + log.Ctx(ctx).Trace().Msg("anyCanceled") + return nil, ctx.Err() + } + } + + return results, nil +} + // union returns whether any one of the lazy checks pass, and is used for union. func union[T any]( ctx context.Context, @@ -832,12 +1058,16 @@ func difference[T any]( return checkResultsForMembership(membershipSet, responseMetadata) } -func dispatchAllAsync[T any]( +type withError interface { + ResultError() error +} + +func dispatchAllAsync[T any, R withError]( ctx context.Context, crc currentRequestContext, children []T, - handler func(ctx context.Context, crc currentRequestContext, child T) CheckResult, - resultChan chan<- CheckResult, + handler func(ctx context.Context, crc currentRequestContext, child T) R, + resultChan chan<- R, concurrencyLimit uint16, ) { tr := taskrunner.NewPreloadedTaskRunner(ctx, concurrencyLimit, len(children)) @@ -846,7 +1076,7 @@ func dispatchAllAsync[T any]( tr.Add(func(ctx context.Context) error { result := handler(ctx, crc, currentChild) resultChan <- result - return result.Err + return result.ResultError() }) } diff --git a/internal/graph/expand.go b/internal/graph/expand.go index 99924c1d42..937486d921 100644 --- a/internal/graph/expand.go +++ b/internal/graph/expand.go @@ -3,7 +3,6 @@ package graph import ( "context" "errors" - "fmt" "github.com/authzed/spicedb/internal/caveats" @@ -191,11 +190,22 @@ func (ce *ConcurrentExpander) expandSetOperation(ctx context.Context, req Valida case *core.SetOperation_Child_UsersetRewrite: requests = append(requests, ce.expandUsersetRewrite(ctx, req, child.UsersetRewrite)) case *core.SetOperation_Child_TupleToUserset: - requests = append(requests, ce.expandTupleToUserset(ctx, req, child.TupleToUserset)) + requests = append(requests, expandTupleToUserset(ctx, ce, req, child.TupleToUserset, expandAny)) + case *core.SetOperation_Child_FunctionedTupleToUserset: + switch child.FunctionedTupleToUserset.Function { + case core.FunctionedTupleToUserset_FUNCTION_ANY: + requests = append(requests, expandTupleToUserset(ctx, ce, req, child.FunctionedTupleToUserset, expandAny)) + + case core.FunctionedTupleToUserset_FUNCTION_ALL: + requests = append(requests, expandTupleToUserset(ctx, ce, req, child.FunctionedTupleToUserset, expandAll)) + + default: + return expandError(spiceerrors.MustBugf("unknown function `%s` in expand", child.FunctionedTupleToUserset.Function)) + } case *core.SetOperation_Child_XNil: requests = append(requests, emptyExpansion(req.ResourceAndRelation)) default: - return expandError(fmt.Errorf("unknown set operation child `%T` in expand", child)) + return expandError(spiceerrors.MustBugf("unknown set operation child `%T` in expand", child)) } } return func(ctx context.Context, resultChan chan<- ExpandResult) { @@ -253,13 +263,21 @@ func (ce *ConcurrentExpander) expandComputedUserset(ctx context.Context, req Val }) } -func (ce *ConcurrentExpander) expandTupleToUserset(_ context.Context, req ValidatedExpandRequest, ttu *core.TupleToUserset) ReduceableExpandFunc { +type expandFunc func(ctx context.Context, start *core.ObjectAndRelation, requests []ReduceableExpandFunc) ExpandResult + +func expandTupleToUserset[T relation]( + _ context.Context, + ce *ConcurrentExpander, + req ValidatedExpandRequest, + ttu ttu[T], + expandFunc expandFunc, +) ReduceableExpandFunc { return func(ctx context.Context, resultChan chan<- ExpandResult) { ds := datastoremw.MustFromContext(ctx).SnapshotReader(req.Revision) it, err := ds.QueryRelationships(ctx, datastore.RelationshipsFilter{ OptionalResourceType: req.ResourceAndRelation.Namespace, OptionalResourceIds: []string{req.ResourceAndRelation.ObjectId}, - OptionalResourceRelation: ttu.Tupleset.Relation, + OptionalResourceRelation: ttu.GetTupleset().GetRelation(), }) if err != nil { resultChan <- expandResultError(NewExpansionFailureErr(err), emptyMetadata) @@ -274,12 +292,12 @@ func (ce *ConcurrentExpander) expandTupleToUserset(_ context.Context, req Valida return } - toDispatch := ce.expandComputedUserset(ctx, req, ttu.ComputedUserset, tpl) + toDispatch := ce.expandComputedUserset(ctx, req, ttu.GetComputedUserset(), tpl) requestsToDispatch = append(requestsToDispatch, decorateWithCaveatIfNecessary(toDispatch, caveats.CaveatAsExpr(tpl.Caveat))) } it.Close() - resultChan <- expandAny(ctx, req.ResourceAndRelation, requestsToDispatch) + resultChan <- expandFunc(ctx, req.ResourceAndRelation, requestsToDispatch) } } diff --git a/internal/graph/graph.go b/internal/graph/graph.go index 232d8641c6..e05074f89d 100644 --- a/internal/graph/graph.go +++ b/internal/graph/graph.go @@ -38,12 +38,20 @@ type CheckResult struct { Err error } +func (cr CheckResult) ResultError() error { + return cr.Err +} + // ExpandResult is the data that is returned by a single expand or sub-expand. type ExpandResult struct { Resp *v1.DispatchExpandResponse Err error } +func (er ExpandResult) ResultError() error { + return er.Err +} + // ReduceableExpandFunc is a function that can be bound to a execution context. type ReduceableExpandFunc func(ctx context.Context, resultChan chan<- ExpandResult) diff --git a/internal/graph/lookupsubjects.go b/internal/graph/lookupsubjects.go index e7c2663018..510b82ce52 100644 --- a/internal/graph/lookupsubjects.go +++ b/internal/graph/lookupsubjects.go @@ -4,19 +4,24 @@ import ( "context" "errors" "fmt" + "sync" "golang.org/x/sync/errgroup" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "github.com/authzed/spicedb/internal/datasets" "github.com/authzed/spicedb/internal/dispatch" log "github.com/authzed/spicedb/internal/logging" datastoremw "github.com/authzed/spicedb/internal/middleware/datastore" "github.com/authzed/spicedb/internal/namespace" + "github.com/authzed/spicedb/internal/taskrunner" "github.com/authzed/spicedb/pkg/datastore" "github.com/authzed/spicedb/pkg/genutil/mapz" "github.com/authzed/spicedb/pkg/genutil/slicez" core "github.com/authzed/spicedb/pkg/proto/core/v1" v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" + "github.com/authzed/spicedb/pkg/spiceerrors" "github.com/authzed/spicedb/pkg/tuple" ) @@ -188,16 +193,199 @@ func (cl *ConcurrentLookupSubjects) lookupViaComputed( }, stream) } -func (cl *ConcurrentLookupSubjects) lookupViaTupleToUserset( +type resourceDispatchTracker struct { + ctx context.Context + cancelDispatch context.CancelFunc + resourceID string + + subjectsSet datasets.SubjectSet + metadata *v1.ResponseMeta + + isFirstUpdate bool + wasCanceled bool + + lock sync.Mutex +} + +func lookupViaIntersectionTupleToUserset( + ctx context.Context, + cl *ConcurrentLookupSubjects, + parentRequest ValidatedLookupSubjectsRequest, + parentStream dispatch.LookupSubjectsStream, + ttu *core.FunctionedTupleToUserset, +) error { + ds := datastoremw.MustFromContext(ctx).SnapshotReader(parentRequest.Revision) + it, err := ds.QueryRelationships(ctx, datastore.RelationshipsFilter{ + OptionalResourceType: parentRequest.ResourceRelation.Namespace, + OptionalResourceRelation: ttu.GetTupleset().GetRelation(), + OptionalResourceIds: parentRequest.ResourceIds, + }) + if err != nil { + return err + } + defer it.Close() + + // TODO(jschorr): Find a means of doing this without dispatching per subject, per resource. Perhaps + // there is a way we can still dispatch to all the subjects at once, and then intersect the results + // afterwards. + resourceDispatchTrackerByResourceID := make(map[string]*resourceDispatchTracker) + + cancelCtx, checkCancel := context.WithCancel(ctx) + defer checkCancel() + + // For each found tuple, dispatch a lookup subjects request and collect its results. + // We need to intersect between *all* the found subjects for each resource ID. + var ttuCaveat *core.CaveatExpression + taskrunner := taskrunner.NewPreloadedTaskRunner(cancelCtx, cl.concurrencyLimit, 1) + for tpl := it.Next(); tpl != nil; tpl = it.Next() { + if it.Err() != nil { + return it.Err() + } + + // If the relationship has a caveat, add it to the overall TTU caveat. Since this is an intersection + // of *all* branches, the caveat will be applied to all found subjects, so this is a safe approach. + if tpl.Caveat != nil { + ttuCaveat = caveatAnd(ttuCaveat, wrapCaveat(tpl.Caveat)) + } + + if err := namespace.CheckNamespaceAndRelation(ctx, tpl.Subject.Namespace, ttu.GetComputedUserset().Relation, false, ds); err != nil { + if !errors.As(err, &namespace.ErrRelationNotFound{}) { + return err + } + + continue + } + + // Create a data structure to track the intersection of subjects for the particular resource. If the resource's subject set + // ends up empty anywhere along the way, the dispatches for *that resource* will be canceled early. + resourceID := tpl.ResourceAndRelation.ObjectId + dispatchInfoForResource, ok := resourceDispatchTrackerByResourceID[resourceID] + if !ok { + dispatchCtx, cancelDispatch := context.WithCancel(cancelCtx) + dispatchInfoForResource = &resourceDispatchTracker{ + ctx: dispatchCtx, + cancelDispatch: cancelDispatch, + resourceID: resourceID, + subjectsSet: datasets.NewSubjectSet(), + metadata: emptyMetadata, + isFirstUpdate: true, + lock: sync.Mutex{}, + } + resourceDispatchTrackerByResourceID[resourceID] = dispatchInfoForResource + } + + tpl := tpl + taskrunner.Add(func(ctx context.Context) error { + // Collect all results for this branch of the resource ID. + // TODO(jschorr): once LS has cursoring (and thus, ordering), we can move to not collecting everything up before intersecting + // for this branch of the resource ID. + collectingStream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupSubjectsResponse](dispatchInfoForResource.ctx) + err := cl.d.DispatchLookupSubjects(&v1.DispatchLookupSubjectsRequest{ + ResourceRelation: &core.RelationReference{ + Namespace: tpl.Subject.Namespace, + Relation: ttu.GetComputedUserset().Relation, + }, + ResourceIds: []string{tpl.Subject.ObjectId}, + SubjectRelation: parentRequest.SubjectRelation, + Metadata: &v1.ResolverMeta{ + AtRevision: parentRequest.Revision.String(), + DepthRemaining: parentRequest.Metadata.DepthRemaining - 1, + }, + }, collectingStream) + if err != nil { + // Check if the dispatches for the resource were canceled, and if so, return nil to stop the task. + dispatchInfoForResource.lock.Lock() + wasCanceled := dispatchInfoForResource.wasCanceled + dispatchInfoForResource.lock.Unlock() + + if wasCanceled { + if errors.Is(err, context.Canceled) { + return nil + } + + errStatus, ok := status.FromError(err) + if ok && errStatus.Code() == codes.Canceled { + return nil + } + } + + return err + } + + // Collect the results into a subject set. + results := datasets.NewSubjectSet() + collectedMetadata := emptyMetadata + for _, result := range collectingStream.Results() { + collectedMetadata = combineResponseMetadata(collectedMetadata, result.Metadata) + for _, foundSubjects := range result.FoundSubjectsByResourceId { + if err := results.UnionWith(foundSubjects.FoundSubjects); err != nil { + return fmt.Errorf("failed to UnionWith under lookupSubjectsIntersection: %w", err) + } + } + } + + dispatchInfoForResource.lock.Lock() + defer dispatchInfoForResource.lock.Unlock() + + dispatchInfoForResource.metadata = combineResponseMetadata(dispatchInfoForResource.metadata, collectedMetadata) + + // If the first update for the resource, set the subjects set to the results. + if dispatchInfoForResource.isFirstUpdate { + dispatchInfoForResource.isFirstUpdate = false + dispatchInfoForResource.subjectsSet = results + } else { + // Otherwise, intersect the results with the existing subjects set. + err := dispatchInfoForResource.subjectsSet.IntersectionDifference(results) + if err != nil { + return err + } + } + + // If the subjects set is empty, cancel the dispatch for any further results for this resource ID. + if dispatchInfoForResource.subjectsSet.IsEmpty() { + dispatchInfoForResource.wasCanceled = true + dispatchInfoForResource.cancelDispatch() + } + + return nil + }) + } + it.Close() + + // Wait for all dispatched operations to complete. + if err := taskrunner.StartAndWait(); err != nil { + return err + } + + // For each resource ID, intersect the found subjects from each stream. + metadata := emptyMetadata + currentSubjectsByResourceID := map[string]*v1.FoundSubjects{} + + for incomingResourceID, tracker := range resourceDispatchTrackerByResourceID { + currentSubjects := tracker.subjectsSet + currentSubjects = currentSubjects.WithParentCaveatExpression(ttuCaveat) + currentSubjectsByResourceID[incomingResourceID] = currentSubjects.AsFoundSubjects() + + metadata = combineResponseMetadata(metadata, tracker.metadata) + } + + return parentStream.Publish(&v1.DispatchLookupSubjectsResponse{ + FoundSubjectsByResourceId: currentSubjectsByResourceID, + Metadata: metadata, + }) +} + +func lookupViaTupleToUserset[T relation]( ctx context.Context, + cl *ConcurrentLookupSubjects, parentRequest ValidatedLookupSubjectsRequest, parentStream dispatch.LookupSubjectsStream, - ttu *core.TupleToUserset, + ttu ttu[T], ) error { ds := datastoremw.MustFromContext(ctx).SnapshotReader(parentRequest.Revision) it, err := ds.QueryRelationships(ctx, datastore.RelationshipsFilter{ OptionalResourceType: parentRequest.ResourceRelation.Namespace, - OptionalResourceRelation: ttu.Tupleset.Relation, + OptionalResourceRelation: ttu.GetTupleset().GetRelation(), OptionalResourceIds: parentRequest.ResourceIds, }) if err != nil { @@ -223,14 +411,14 @@ func (cl *ConcurrentLookupSubjects) lookupViaTupleToUserset( relationshipsBySubjectONR.Add(tuple.StringONR(&core.ObjectAndRelation{ Namespace: tpl.Subject.Namespace, ObjectId: tpl.Subject.ObjectId, - Relation: ttu.ComputedUserset.Relation, + Relation: ttu.GetComputedUserset().Relation, }), tpl) } it.Close() // Map the found subject types by the computed userset relation, so that we dispatch to it. toDispatchByComputedRelationType, err := toDispatchByTuplesetType.Map(func(resourceType *core.RelationReference) (*core.RelationReference, error) { - if err := namespace.CheckNamespaceAndRelation(ctx, resourceType.Namespace, ttu.ComputedUserset.Relation, false, ds); err != nil { + if err := namespace.CheckNamespaceAndRelation(ctx, resourceType.Namespace, ttu.GetComputedUserset().Relation, false, ds); err != nil { if errors.As(err, &namespace.ErrRelationNotFound{}) { return nil, nil } @@ -240,7 +428,7 @@ func (cl *ConcurrentLookupSubjects) lookupViaTupleToUserset( return &core.RelationReference{ Namespace: resourceType.Namespace, - Relation: ttu.ComputedUserset.Relation, + Relation: ttu.GetComputedUserset().Relation, }, nil }) if err != nil { @@ -302,15 +490,31 @@ func (cl *ConcurrentLookupSubjects) lookupSetOperation( case *core.SetOperation_Child_TupleToUserset: g.Go(func() error { - return cl.lookupViaTupleToUserset(subCtx, req, stream, child.TupleToUserset) + return lookupViaTupleToUserset(subCtx, cl, req, stream, child.TupleToUserset) }) + case *core.SetOperation_Child_FunctionedTupleToUserset: + switch child.FunctionedTupleToUserset.Function { + case core.FunctionedTupleToUserset_FUNCTION_ANY: + g.Go(func() error { + return lookupViaTupleToUserset(subCtx, cl, req, stream, child.FunctionedTupleToUserset) + }) + + case core.FunctionedTupleToUserset_FUNCTION_ALL: + g.Go(func() error { + return lookupViaIntersectionTupleToUserset(subCtx, cl, req, stream, child.FunctionedTupleToUserset) + }) + + default: + return spiceerrors.MustBugf("unknown function in lookup subjects: %v", child.FunctionedTupleToUserset.Function) + } + case *core.SetOperation_Child_XNil: // Purposely do nothing. continue default: - return fmt.Errorf("unknown set operation child `%T` in expand", child) + return spiceerrors.MustBugf("unknown set operation child `%T` in lookup subjects", child) } } diff --git a/internal/graph/membershipset.go b/internal/graph/membershipset.go index 24665de9e9..7ec263e0e1 100644 --- a/internal/graph/membershipset.go +++ b/internal/graph/membershipset.go @@ -62,6 +62,25 @@ func (ms *MembershipSet) AddMemberViaRelationship( ms.addMember(resourceID, intersection) } +// AddMemberWithOptionalCaveats adds the given resource ID as a member with the optional caveats combined +// via intersection. +func (ms *MembershipSet) AddMemberWithOptionalCaveats( + resourceID string, + caveats []*core.CaveatExpression, +) { + if len(caveats) == 0 { + ms.addMember(resourceID, nil) + return + } + + intersection := caveats[0] + for _, caveat := range caveats[1:] { + intersection = caveatAnd(intersection, caveat) + } + + ms.addMember(resourceID, intersection) +} + func (ms *MembershipSet) addMember(resourceID string, caveatExpr *core.CaveatExpression) { existing, ok := ms.membersByID[resourceID] if !ok { @@ -153,6 +172,17 @@ func (ms *MembershipSet) HasConcreteResourceID(resourceID string) bool { return ok && found == nil } +// GetResourceID returns a bool indicating whether the resource is found in the set and the +// associated caveat expression, if any. +func (ms *MembershipSet) GetResourceID(resourceID string) (bool, *core.CaveatExpression) { + if ms == nil { + return false, nil + } + + caveat, ok := ms.membersByID[resourceID] + return ok, caveat +} + // Size returns the number of elements in the membership set. func (ms *MembershipSet) Size() int { if ms == nil { diff --git a/internal/namespace/canonicalization.go b/internal/namespace/canonicalization.go index b1b142c759..5de8ff0897 100644 --- a/internal/namespace/canonicalization.go +++ b/internal/namespace/canonicalization.go @@ -37,7 +37,7 @@ const computedKeyPrefix = "%" // expressions in the namespace: // // definition somenamespace { -// relation first: ... +// relation first: ... // ^ index 0 // relation second: ... // ^ index 1 @@ -155,6 +155,28 @@ func convertToBdd(relation *core.Relation, bdd *rudd.BDD, so *core.SetOperation, values = append(values, builder(index, arrowIndex)) + case *core.SetOperation_Child_FunctionedTupleToUserset: + switch child.FunctionedTupleToUserset.Function { + case core.FunctionedTupleToUserset_FUNCTION_ANY: + arrowIndex, err := varMap.GetArrow(child.FunctionedTupleToUserset.Tupleset.Relation, child.FunctionedTupleToUserset.ComputedUserset.Relation) + if err != nil { + return nil, err + } + + values = append(values, builder(index, arrowIndex)) + + case core.FunctionedTupleToUserset_FUNCTION_ALL: + arrowIndex, err := varMap.GetIntersectionArrow(child.FunctionedTupleToUserset.Tupleset.Relation, child.FunctionedTupleToUserset.ComputedUserset.Relation) + if err != nil { + return nil, err + } + + values = append(values, builder(index, arrowIndex)) + + default: + return nil, spiceerrors.MustBugf("unknown function %v", child.FunctionedTupleToUserset.Function) + } + case *core.SetOperation_Child_XNil: values = append(values, builder(index, varMap.Nil())) @@ -179,6 +201,15 @@ func (bvm bddVarMap) GetArrow(tuplesetName string, relName string) (int, error) return index, nil } +func (bvm bddVarMap) GetIntersectionArrow(tuplesetName string, relName string) (int, error) { + key := tuplesetName + "-(all)->" + relName + index, ok := bvm.varMap[key] + if !ok { + return -1, spiceerrors.MustBugf("missing intersection arrow key %s in varMap", key) + } + return index, nil +} + func (bvm bddVarMap) Nil() int { return len(bvm.varMap) } @@ -213,15 +244,32 @@ func buildBddVarMap(relations []*core.Relation, aliasMap map[string]string) (bdd continue } - _, err := graph.WalkRewrite(rewrite, func(childOneof *core.SetOperation_Child) interface{} { + _, err := graph.WalkRewrite(rewrite, func(childOneof *core.SetOperation_Child) (interface{}, error) { switch child := childOneof.ChildType.(type) { case *core.SetOperation_Child_TupleToUserset: key := child.TupleToUserset.Tupleset.Relation + "->" + child.TupleToUserset.ComputedUserset.Relation + if _, ok := varMap[key]; !ok { + varMap[key] = len(varMap) + } + case *core.SetOperation_Child_FunctionedTupleToUserset: + key := child.FunctionedTupleToUserset.Tupleset.Relation + "->" + child.FunctionedTupleToUserset.ComputedUserset.Relation + + switch child.FunctionedTupleToUserset.Function { + case core.FunctionedTupleToUserset_FUNCTION_ANY: + // Use the key. + + case core.FunctionedTupleToUserset_FUNCTION_ALL: + key = child.FunctionedTupleToUserset.Tupleset.Relation + "-(all)->" + child.FunctionedTupleToUserset.ComputedUserset.Relation + + default: + return nil, spiceerrors.MustBugf("unknown function %v", child.FunctionedTupleToUserset.Function) + } + if _, ok := varMap[key]; !ok { varMap[key] = len(varMap) } } - return nil + return nil, nil }) if err != nil { return bddVarMap{}, err diff --git a/internal/namespace/canonicalization_test.go b/internal/namespace/canonicalization_test.go index 5fc84e26d8..3250faf2e1 100644 --- a/internal/namespace/canonicalization_test.go +++ b/internal/namespace/canonicalization_test.go @@ -375,6 +375,49 @@ func TestCanonicalization(t *testing.T) { "second": computedKeyPrefix + "bfc8d945d7030961", }, }, + { + "canonicalization with functioned arrow expressions", + ns.Namespace( + "document", + ns.MustRelation("owner", nil), + ns.MustRelation("viewer", nil), + ns.MustRelation("first", ns.Union( + ns.TupleToUserset("owner", "something"), + )), + ns.MustRelation("second", ns.Union( + ns.TupleToUserset("owner", "something"), + )), + ns.MustRelation("difftuple", ns.Union( + ns.TupleToUserset("viewer", "something"), + )), + ns.MustRelation("third", ns.Union( + ns.MustFunctionedTupleToUserset("owner", "any", "something"), + )), + ns.MustRelation("thirdwithall", ns.Union( + ns.MustFunctionedTupleToUserset("owner", "all", "something"), + )), + ns.MustRelation("allplusanother", ns.Union( + ns.MustFunctionedTupleToUserset("owner", "all", "something"), + ns.ComputedUserset("owner"), + )), + ns.MustRelation("anotherplusall", ns.Union( + ns.ComputedUserset("owner"), + ns.MustFunctionedTupleToUserset("owner", "all", "something"), + )), + ), + "", + map[string]string{ + "owner": "owner", + "viewer": "viewer", + "first": computedKeyPrefix + "9fd2b03cabeb2e42", + "second": computedKeyPrefix + "9fd2b03cabeb2e42", + "third": computedKeyPrefix + "9fd2b03cabeb2e42", + "thirdwithall": computedKeyPrefix + "eafa2f3f2d970680", + "difftuple": computedKeyPrefix + "dddc650e89a7bf1a", + "allplusanother": computedKeyPrefix + "8b68ba1711b32ca4", + "anotherplusall": computedKeyPrefix + "8b68ba1711b32ca4", + }, + }, } for _, tc := range testCases { diff --git a/internal/services/integrationtesting/testconfigs/caveatedintersectionarrow.yaml b/internal/services/integrationtesting/testconfigs/caveatedintersectionarrow.yaml new file mode 100644 index 0000000000..1beb928621 --- /dev/null +++ b/internal/services/integrationtesting/testconfigs/caveatedintersectionarrow.yaml @@ -0,0 +1,55 @@ +--- +schema: |+ + definition user {} + + definition team { + relation direct_member: user with membercaveat + permission member = direct_member + } + + caveat membercaveat(memberparam int) { + memberparam >= 42 + } + + caveat teamcaveat(teamparam int) { + teamparam < 42 + } + + definition resource { + relation team: team with teamcaveat + permission view_by_all = team.all(member) + } + +relationships: |- + team:first#direct_member@user:tom[membercaveat] + resource:firstresource#team@team:first[teamcaveat] + + team:team2-1#direct_member@user:tom[membercaveat:{"memberparam": 43}] + team:team2-2#direct_member@user:tom[membercaveat:{"memberparam": 44}] + resource:positivecaveated#team@team:team2-1[teamcaveat:{"teamparam": 1}] + resource:positivecaveated#team@team:team2-2[teamcaveat:{"teamparam": 2}] + + team:team3-1#direct_member@user:tom[membercaveat:{"memberparam": 43}] + team:team3-2#direct_member@user:tom[membercaveat:{"memberparam": 44}] + resource:negativeteam#team@team:team3-1[teamcaveat:{"teamparam": 100}] + resource:negativeteam#team@team:team3-2[teamcaveat:{"teamparam": 2}] + + team:team4-1#direct_member@user:tom[membercaveat:{"memberparam": 3}] + team:team4-2#direct_member@user:tom[membercaveat:{"memberparam": 44}] + resource:negativemember#team@team:team4-1[teamcaveat:{"teamparam": 1}] + resource:negativemember#team@team:team4-2[teamcaveat:{"teamparam": 2}] + +assertions: + assertTrue: + - 'resource:firstresource#view_by_all@user:tom with {"memberparam": 42, "teamparam": 41}' + - "resource:positivecaveated#view_by_all@user:tom" + assertCaveated: + - "resource:firstresource#view_by_all@user:tom" + - 'resource:firstresource#view_by_all@user:tom with {"memberparam": 42}' + - 'resource:firstresource#view_by_all@user:tom with {"teamparam": 41}' + assertFalse: + - 'resource:firstresource#view_by_all@user:tom with {"memberparam": 1, "teamparam": 41}' + - 'resource:firstresource#view_by_all@user:tom with {"memberparam": 42, "teamparam": 100}' + - 'resource:firstresource#view_by_all@user:tom with {"memberparam": 1, "teamparam": 100}' + - "resource:negativeteam#view_by_all@user:tom" + - "resource:negativemember#view_by_all@user:tom" diff --git a/internal/services/integrationtesting/testconfigs/intersectionarrow.yaml b/internal/services/integrationtesting/testconfigs/intersectionarrow.yaml new file mode 100644 index 0000000000..446d63c401 --- /dev/null +++ b/internal/services/integrationtesting/testconfigs/intersectionarrow.yaml @@ -0,0 +1,49 @@ +--- +schema: |+ + definition user {} + + definition team { + relation direct_member: user + permission member = direct_member + } + + definition resource { + relation team: team + permission view_by_all = team.all(member) + permission view_by_any = team.any(member) + } + +relationships: |- + team:first#direct_member@user:tom + team:first#direct_member@user:fred + team:first#direct_member@user:sarah + team:second#direct_member@user:fred + team:second#direct_member@user:sarah + team:third#direct_member@user:sarah + resource:oneteam#team@team:first + resource:twoteams#team@team:first + resource:twoteams#team@team:second + resource:threeteams#team@team:first + resource:threeteams#team@team:second + resource:threeteams#team@team:third + +assertions: + assertTrue: + - "resource:oneteam#view_by_all@user:tom" + - "resource:oneteam#view_by_all@user:fred" + - "resource:oneteam#view_by_all@user:sarah" + - "resource:twoteams#view_by_all@user:fred" + - "resource:threeteams#view_by_all@user:sarah" + - "resource:oneteam#view_by_any@user:tom" + - "resource:oneteam#view_by_any@user:fred" + - "resource:oneteam#view_by_any@user:sarah" + - "resource:twoteams#view_by_any@user:tom" + - "resource:twoteams#view_by_any@user:fred" + - "resource:twoteams#view_by_any@user:sarah" + - "resource:threeteams#view_by_any@user:tom" + - "resource:threeteams#view_by_any@user:fred" + - "resource:threeteams#view_by_any@user:sarah" + assertFalse: + - "resource:twoteams#view_by_all@user:tom" + - "resource:threeteams#view_by_all@user:tom" + - "resource:threeteams#view_by_all@user:fred" diff --git a/internal/testutil/subjects.go b/internal/testutil/subjects.go index 9e6694b72f..be6b51a00f 100644 --- a/internal/testutil/subjects.go +++ b/internal/testutil/subjects.go @@ -70,7 +70,7 @@ func Wildcard(exclusions ...string) *v1.FoundSubject { func RequireEquivalentSets(t *testing.T, expected []*v1.FoundSubject, found []*v1.FoundSubject) { t.Helper() err := CheckEquivalentSets(expected, found) - require.NoError(t, err, "found different subject sets: %v", err) + require.NoError(t, err, "found different subject sets: %v \n %v", err, found) } // RequireExpectedSubject requires that the given expected and produced subjects match. diff --git a/pkg/graph/walker.go b/pkg/graph/walker.go index 2a6a9c6502..3ae8b6bddf 100644 --- a/pkg/graph/walker.go +++ b/pkg/graph/walker.go @@ -7,7 +7,7 @@ import ( // WalkHandler is a function invoked for each node in the rewrite tree. If it returns non-nil, // that value is returned from the walk. Otherwise, the walk continues. -type WalkHandler func(childOneof *core.SetOperation_Child) interface{} +type WalkHandler func(childOneof *core.SetOperation_Child) (interface{}, error) // WalkRewrite walks a userset rewrite tree, invoking the handler found on each node of the tree // until the handler returns a non-nil value, which is in turn returned from this function. Returns @@ -32,12 +32,12 @@ func WalkRewrite(rewrite *core.UsersetRewrite, handler WalkHandler) (interface{} // HasThis returns true if there exists a `_this` node anywhere within the given rewrite. If // the rewrite is nil, returns false. func HasThis(rewrite *core.UsersetRewrite) (bool, error) { - result, err := WalkRewrite(rewrite, func(childOneof *core.SetOperation_Child) interface{} { + result, err := WalkRewrite(rewrite, func(childOneof *core.SetOperation_Child) (interface{}, error) { switch childOneof.ChildType.(type) { case *core.SetOperation_Child_XThis: - return true + return true, nil default: - return nil + return nil, nil } }) return result != nil && result.(bool), err @@ -45,7 +45,11 @@ func HasThis(rewrite *core.UsersetRewrite) (bool, error) { func walkRewriteChildren(so *core.SetOperation, handler WalkHandler) (interface{}, error) { for _, childOneof := range so.Child { - vle := handler(childOneof) + vle, err := handler(childOneof) + if err != nil { + return nil, err + } + if vle != nil { return vle, nil } diff --git a/pkg/namespace/builder.go b/pkg/namespace/builder.go index 66e4755606..03c81c97a9 100644 --- a/pkg/namespace/builder.go +++ b/pkg/namespace/builder.go @@ -272,7 +272,7 @@ func MustFunctionedTupleToUserset(tuplesetRelation, functionName, usersetRelatio switch functionName { case "any": - function = core.FunctionedTupleToUserset_FUNCTION_ANY + // already set to any case "all": function = core.FunctionedTupleToUserset_FUNCTION_ALL diff --git a/pkg/schemadsl/parser/parser_impl.go b/pkg/schemadsl/parser/parser_impl.go index 318e2be7d6..76f2bf3839 100644 --- a/pkg/schemadsl/parser/parser_impl.go +++ b/pkg/schemadsl/parser/parser_impl.go @@ -187,25 +187,6 @@ func (p *sourceParser) consumeKeyword(keyword string) bool { return true } -// consumeKeywords consumes an expected keyword token(s) or adds an error node. -func (p *sourceParser) consumeKeywords(keywords ...string) (string, bool) { - keyword, ok := p.tryConsumeKeywords(keywords...) - if !ok { - p.emitErrorf("Expected one of: %v, found: %v", keywords, p.currentToken.Kind) - } - return keyword, ok -} - -// tryConsumeKeywords consumes an expected keyword token(s) or adds an error node. -func (p *sourceParser) tryConsumeKeywords(keywords ...string) (string, bool) { - for _, keyword := range keywords { - if p.tryConsumeKeyword(keyword) { - return keyword, true - } - } - return "", false -} - // tryConsumeKeyword attempts to consume an expected keyword token. func (p *sourceParser) tryConsumeKeyword(keyword string) bool { if !p.isKeyword(keyword) { diff --git a/pkg/typesystem/reachabilitygraph_test.go b/pkg/typesystem/reachabilitygraph_test.go index 1dfaf2de92..bd0d9d1bee 100644 --- a/pkg/typesystem/reachabilitygraph_test.go +++ b/pkg/typesystem/reachabilitygraph_test.go @@ -115,6 +115,38 @@ func TestRelationsEncounteredForSubject(t *testing.T) { "admin", []string{"document#view"}, }, + { + "simple any arrow", + `definition user {} + + definition organization { + relation admin: user + } + + definition document { + relation org: organization + permission view = org.any(admin) + }`, + "organization", + "admin", + []string{"document#view"}, + }, + { + "simple all arrow", + `definition user {} + + definition organization { + relation admin: user + } + + definition document { + relation org: organization + permission view = org.all(admin) + }`, + "organization", + "admin", + []string{"document#view"}, + }, { "complex schema", `definition user {} @@ -321,6 +353,44 @@ func TestRelationsEncounteredForResource(t *testing.T) { "view", []string{"document#viewer", "document#owner", "document#org", "document#view", "organization#admin"}, }, + { + "permission with any arrow", + `definition user {} + + definition organization { + relation admin: user + } + + definition document { + relation org: organization + relation viewer: user + relation owner: user + + permission view = viewer + owner + org.any(admin) + }`, + "document", + "view", + []string{"document#viewer", "document#owner", "document#org", "document#view", "organization#admin"}, + }, + { + "permission with all arrow", + `definition user {} + + definition organization { + relation admin: user + } + + definition document { + relation org: organization + relation viewer: user + relation owner: user + + permission view = viewer + owner + org.all(admin) + }`, + "document", + "view", + []string{"document#viewer", "document#owner", "document#org", "document#view", "organization#admin"}, + }, { "permission with subrelation", `definition user {} @@ -683,6 +753,60 @@ func TestReachabilityGraph(t *testing.T) { rrt("organization", "admin", true), }, }, + { + "permission with any arrow", + `definition user {} + + definition organization { + relation admin: user + } + + definition document { + relation org: organization + relation viewer: user + relation owner: user + permission view = viewer + owner + org.any(admin) + }`, + rr("document", "view"), + rr("user", "..."), + []rrtStruct{ + rrt("document", "owner", true), + rrt("document", "viewer", true), + rrt("organization", "admin", true), + }, + []rrtStruct{ + rrt("document", "owner", true), + rrt("document", "viewer", true), + rrt("organization", "admin", true), + }, + }, + { + "permission with all arrow", + `definition user {} + + definition organization { + relation admin: user + } + + definition document { + relation org: organization + relation viewer: user + relation owner: user + permission view = viewer + owner + org.all(admin) + }`, + rr("document", "view"), + rr("user", "..."), + []rrtStruct{ + rrt("document", "owner", true), + rrt("document", "viewer", true), + rrt("organization", "admin", true), + }, + []rrtStruct{ + rrt("document", "owner", true), + rrt("document", "viewer", true), + rrt("organization", "admin", true), + }, + }, { "permission with multi-level arrows", `definition user {} @@ -1013,6 +1137,48 @@ func TestReachabilityGraph(t *testing.T) { rrt("organization", "viewer", true), }, }, + { + "optimized reachability with any arrow", + `definition user {} + + definition organization { + relation admin: user + } + + definition document { + relation org: organization + permission view = org.any(admin) + }`, + rr("document", "view"), + rr("organization", "admin"), + []rrtStruct{ + rrt("document", "view", true), + }, + []rrtStruct{ + rrt("document", "view", true), + }, + }, + { + "optimized reachability with all arrow", + `definition user {} + + definition organization { + relation admin: user + } + + definition document { + relation org: organization + permission view = org.all(admin) + }`, + rr("document", "view"), + rr("organization", "admin"), + []rrtStruct{ + rrt("document", "view", false), + }, + []rrtStruct{ + rrt("document", "view", false), + }, + }, } for _, tc := range testCases { diff --git a/pkg/typesystem/reachabilitygraphbuilder.go b/pkg/typesystem/reachabilitygraphbuilder.go index 97f5657bf9..1abbf5297f 100644 --- a/pkg/typesystem/reachabilitygraphbuilder.go +++ b/pkg/typesystem/reachabilitygraphbuilder.go @@ -98,59 +98,29 @@ func computeRewriteOpReachability(ctx context.Context, children []*core.SetOpera case *core.SetOperation_Child_TupleToUserset: tuplesetRelation := child.TupleToUserset.Tupleset.Relation - directRelationTypes, err := ts.AllowedDirectRelationsAndWildcards(tuplesetRelation) - if err != nil { + computedUsersetRelation := child.TupleToUserset.ComputedUserset.Relation + if err := computeTTUReachability(ctx, graph, tuplesetRelation, computedUsersetRelation, operationResultState, rr, ts); err != nil { return err } - computedUsersetRelation := child.TupleToUserset.ComputedUserset.Relation - for _, allowedRelationType := range directRelationTypes { - // For each namespace allowed to be found on the right hand side of the - // tupleset relation, include the *computed userset* relation as an entrypoint. - // - // For example, given a schema: - // - // ``` - // definition user {} - // - // definition parent1 { - // relation somerel: user - // } - // - // definition parent2 { - // relation somerel: user - // } - // - // definition child { - // relation parent: parent1 | parent2 - // permission someperm = parent->somerel - // } - // ``` - // - // We will add an entrypoint for the arrow itself, keyed to the relation type - // included from the computed userset. - // - // Using the above example, this will add entrypoints for `parent1#somerel` - // and `parent2#somerel`, which are the subjects reached after resolving the - // right side of the arrow. - - // Check if the relation does exist on the allowed type, and only add the entrypoint if present. - relTypeSystem, err := ts.TypeSystemForNamespace(ctx, allowedRelationType.Namespace) - if err != nil { - return err - } + case *core.SetOperation_Child_FunctionedTupleToUserset: + tuplesetRelation := child.FunctionedTupleToUserset.Tupleset.Relation + computedUsersetRelation := child.FunctionedTupleToUserset.ComputedUserset.Relation - if relTypeSystem.HasRelation(computedUsersetRelation) { - err := addSubjectEntrypoint(graph, allowedRelationType.Namespace, computedUsersetRelation, &core.ReachabilityEntrypoint{ - Kind: core.ReachabilityEntrypoint_TUPLESET_TO_USERSET_ENTRYPOINT, - TargetRelation: rr, - ResultStatus: operationResultState, - TuplesetRelation: tuplesetRelation, - }) - if err != nil { - return err - } - } + switch child.FunctionedTupleToUserset.Function { + case core.FunctionedTupleToUserset_FUNCTION_ANY: + // Nothing to change. + + case core.FunctionedTupleToUserset_FUNCTION_ALL: + // Mark as a conditional result. + operationResultState = core.ReachabilityEntrypoint_REACHABLE_CONDITIONAL_RESULT + + default: + return spiceerrors.MustBugf("unknown function type `%T` in reachability graph building", child.FunctionedTupleToUserset.Function) + } + + if err := computeTTUReachability(ctx, graph, tuplesetRelation, computedUsersetRelation, operationResultState, rr, ts); err != nil { + return err } case *core.SetOperation_Child_XNil: @@ -158,7 +128,73 @@ func computeRewriteOpReachability(ctx context.Context, children []*core.SetOpera return nil default: - return fmt.Errorf("unknown set operation child `%T` in reachability graph building", child) + return spiceerrors.MustBugf("unknown set operation child `%T` in reachability graph building", child) + } + } + + return nil +} + +func computeTTUReachability( + ctx context.Context, + graph *core.ReachabilityGraph, + tuplesetRelation string, + computedUsersetRelation string, + operationResultState core.ReachabilityEntrypoint_EntrypointResultStatus, + rr *core.RelationReference, + ts *TypeSystem, +) error { + directRelationTypes, err := ts.AllowedDirectRelationsAndWildcards(tuplesetRelation) + if err != nil { + return err + } + + for _, allowedRelationType := range directRelationTypes { + // For each namespace allowed to be found on the right hand side of the + // tupleset relation, include the *computed userset* relation as an entrypoint. + // + // For example, given a schema: + // + // ``` + // definition user {} + // + // definition parent1 { + // relation somerel: user + // } + // + // definition parent2 { + // relation somerel: user + // } + // + // definition child { + // relation parent: parent1 | parent2 + // permission someperm = parent->somerel + // } + // ``` + // + // We will add an entrypoint for the arrow itself, keyed to the relation type + // included from the computed userset. + // + // Using the above example, this will add entrypoints for `parent1#somerel` + // and `parent2#somerel`, which are the subjects reached after resolving the + // right side of the arrow. + + // Check if the relation does exist on the allowed type, and only add the entrypoint if present. + relTypeSystem, err := ts.TypeSystemForNamespace(ctx, allowedRelationType.Namespace) + if err != nil { + return err + } + + if relTypeSystem.HasRelation(computedUsersetRelation) { + err := addSubjectEntrypoint(graph, allowedRelationType.Namespace, computedUsersetRelation, &core.ReachabilityEntrypoint{ + Kind: core.ReachabilityEntrypoint_TUPLESET_TO_USERSET_ENTRYPOINT, + TargetRelation: rr, + ResultStatus: operationResultState, + TuplesetRelation: tuplesetRelation, + }) + if err != nil { + return err + } } } diff --git a/pkg/typesystem/typesystem.go b/pkg/typesystem/typesystem.go index 2c43831b8a..0a909d7c2d 100644 --- a/pkg/typesystem/typesystem.go +++ b/pkg/typesystem/typesystem.go @@ -407,7 +407,7 @@ func (nts *TypeSystem) Validate(ctx context.Context) (*ValidatedNamespaceTypeSys // Validate the usersets's. usersetRewrite := relation.GetUsersetRewrite() - rerr, err := graph.WalkRewrite(usersetRewrite, func(childOneof *core.SetOperation_Child) interface{} { + rerr, err := graph.WalkRewrite(usersetRewrite, func(childOneof *core.SetOperation_Child) (interface{}, error) { switch child := childOneof.ChildType.(type) { case *core.SetOperation_Child_ComputedUserset: relationName := child.ComputedUserset.GetRelation() @@ -417,17 +417,18 @@ func (nts *TypeSystem) Validate(ctx context.Context) (*ValidatedNamespaceTypeSys NewRelationNotFoundErr(nts.nsDef.Name, relationName), childOneof, relationName, - ) + ), nil } + case *core.SetOperation_Child_TupleToUserset: ttu := child.TupleToUserset if ttu == nil { - return nil + return nil, nil } tupleset := ttu.GetTupleset() if tupleset == nil { - return nil + return nil, nil } relationName := tupleset.GetRelation() @@ -437,19 +438,19 @@ func (nts *TypeSystem) Validate(ctx context.Context) (*ValidatedNamespaceTypeSys NewRelationNotFoundErr(nts.nsDef.Name, relationName), childOneof, relationName, - ) + ), nil } if nspkg.GetRelationKind(found) == iv1.RelationMetadata_PERMISSION { return NewTypeErrorWithSource( NewPermissionUsedOnLeftOfArrowErr(nts.nsDef.Name, relation.Name, relationName), - childOneof, relationName) + childOneof, relationName), nil } // Ensure the tupleset relation doesn't itself import wildcard. referencedWildcard, err := nts.referencesWildcardType(ctx, relationName) if err != nil { - return err + return err, nil } if referencedWildcard != nil { @@ -462,10 +463,56 @@ func (nts *TypeSystem) Validate(ctx context.Context) (*ValidatedNamespaceTypeSys tuple.StringRR(referencedWildcard.ReferencingRelation), ), childOneof, relationName, - ) + ), nil + } + + case *core.SetOperation_Child_FunctionedTupleToUserset: + ttu := child.FunctionedTupleToUserset + if ttu == nil { + return nil, nil + } + + tupleset := ttu.GetTupleset() + if tupleset == nil { + return nil, nil + } + + relationName := tupleset.GetRelation() + found, ok := nts.relationMap[relationName] + if !ok { + return NewTypeErrorWithSource( + NewRelationNotFoundErr(nts.nsDef.Name, relationName), + childOneof, + relationName, + ), nil + } + + if nspkg.GetRelationKind(found) == iv1.RelationMetadata_PERMISSION { + return NewTypeErrorWithSource( + NewPermissionUsedOnLeftOfArrowErr(nts.nsDef.Name, relation.Name, relationName), + childOneof, relationName), nil + } + + // Ensure the tupleset relation doesn't itself import wildcard. + referencedWildcard, err := nts.referencesWildcardType(ctx, relationName) + if err != nil { + return err, nil + } + + if referencedWildcard != nil { + return NewTypeErrorWithSource( + NewWildcardUsedInArrowErr( + nts.nsDef.Name, + relation.Name, + relationName, + referencedWildcard.WildcardType.GetNamespace(), + tuple.StringRR(referencedWildcard.ReferencingRelation), + ), + childOneof, relationName, + ), nil } } - return nil + return nil, nil }) if rerr != nil { return nil, asTypeError(rerr.(error))