diff --git a/internal/caveats/run.go b/internal/caveats/run.go index 23339f6171..4d602a3cd8 100644 --- a/internal/caveats/run.go +++ b/internal/caveats/run.go @@ -63,9 +63,10 @@ type ExpressionResult interface { } type syntheticResult struct { - value bool - contextValues map[string]any - exprString string + value bool + contextValues map[string]any + exprString string + missingContextParams []string } func (sr syntheticResult) Value() bool { @@ -73,11 +74,15 @@ func (sr syntheticResult) Value() bool { } func (sr syntheticResult) IsPartial() bool { - return false + return len(sr.missingContextParams) > 0 } func (sr syntheticResult) MissingVarNames() ([]string, error) { - return nil, fmt.Errorf("not a partial value") + if len(sr.missingContextParams) == 0 { + return nil, fmt.Errorf("not a partial value") + } + + return sr.missingContextParams, nil } func (sr syntheticResult) ContextValues() map[string]any { @@ -156,6 +161,14 @@ func (lc loadedCaveats) Get(caveatDefName string) (*core.CaveatDefinition, *cave return caveat, justDeserialized, nil } +func isFalseResult(result ExpressionResult) bool { + return !result.Value() && !result.IsPartial() +} + +func isTrueResult(result ExpressionResult) bool { + return result.Value() && !result.IsPartial() +} + func runExpressionWithCaveats( ctx context.Context, env *caveats.Environment, @@ -203,15 +216,30 @@ func runExpressionWithCaveats( } cop := expr.GetOperation() - boolResult := false - if cop.Op == core.CaveatOperation_AND { - boolResult = true - } var contextValues map[string]any var exprStringPieces []string + var currentResult ExpressionResult = syntheticResult{ + value: false, + contextValues: nil, + exprString: "", + missingContextParams: nil, + } + if cop.Op == core.CaveatOperation_AND { + currentResult = syntheticResult{ + value: true, + contextValues: nil, + exprString: "", + missingContextParams: nil, + } + } + buildExprString := func() (string, error) { + if debugOption != RunCaveatExpressionWithDebugInformation { + return "", nil + } + switch cop.Op { case core.CaveatOperation_AND: return strings.Join(exprStringPieces, " && "), nil @@ -227,90 +255,174 @@ func runExpressionWithCaveats( } } - for _, child := range cop.Children { - childResult, err := runExpressionWithCaveats(ctx, env, child, context, loadedCaveats, debugOption) - if err != nil { + addDebugInfo := func(found ExpressionResult) error { + if debugOption == RunCaveatExpressionWithDebugInformation { + contextValues = combineMaps(contextValues, found.ContextValues()) + exprString, err := found.ExpressionString() + if err != nil { + return err + } + + exprStringPieces = append(exprStringPieces, exprString) + } + + return nil + } + + and := func(existing ExpressionResult, found ExpressionResult) (ExpressionResult, error) { + if err := addDebugInfo(found); err != nil { return nil, err } - if childResult.IsPartial() { - return childResult, nil + var missingContextParams []string + if existing.IsPartial() { + params, err := existing.MissingVarNames() + if err != nil { + return nil, err + } + + missingContextParams = params + } else if !existing.Value() { + return existing, nil } - switch cop.Op { - case core.CaveatOperation_AND: - boolResult = boolResult && childResult.Value() + if found.IsPartial() { + params, err := found.MissingVarNames() + if err != nil { + return nil, err + } - if debugOption == RunCaveatExpressionWithDebugInformation { - contextValues = combineMaps(contextValues, childResult.ContextValues()) - exprString, err := childResult.ExpressionString() - if err != nil { - return nil, err - } + missingContextParams = append(missingContextParams, params...) + } else if !found.Value() { + return found, nil + } - exprStringPieces = append(exprStringPieces, exprString) + exprString, err := buildExprString() + if err != nil { + return nil, err + } + + return syntheticResult{ + value: existing.Value() && found.Value(), + contextValues: contextValues, + exprString: exprString, + missingContextParams: missingContextParams, + }, nil + } + + or := func(existing ExpressionResult, found ExpressionResult) (ExpressionResult, error) { + if err := addDebugInfo(found); err != nil { + return nil, err + } + + var missingContextParams []string + if existing.IsPartial() { + params, err := existing.MissingVarNames() + if err != nil { + return nil, err } - if !boolResult { - built, err := buildExprString() - if err != nil { - return nil, err - } + missingContextParams = params + } else if existing.Value() { + return existing, nil + } - return syntheticResult{false, contextValues, built}, nil + if found.IsPartial() { + params, err := found.MissingVarNames() + if err != nil { + return nil, err } - case core.CaveatOperation_OR: - boolResult = boolResult || childResult.Value() + missingContextParams = append(missingContextParams, params...) + } else if found.Value() { + return found, nil + } + + exprString, err := buildExprString() + if err != nil { + return nil, err + } - if debugOption == RunCaveatExpressionWithDebugInformation { - contextValues = combineMaps(contextValues, childResult.ContextValues()) - exprString, err := childResult.ExpressionString() - if err != nil { - return nil, err - } + return syntheticResult{ + value: existing.Value() || found.Value(), + contextValues: contextValues, + exprString: exprString, + missingContextParams: missingContextParams, + }, nil + } - exprStringPieces = append(exprStringPieces, exprString) + invert := func(existing ExpressionResult) (ExpressionResult, error) { + if debugOption == RunCaveatExpressionWithDebugInformation { + contextValues = combineMaps(contextValues, existing.ContextValues()) + exprString, err := existing.ExpressionString() + if err != nil { + return nil, err } - if boolResult { - built, err := buildExprString() - if err != nil { - return nil, err - } + exprStringPieces = append(exprStringPieces, "!("+exprString+")") + } + + value := !existing.Value() + + var missingContextParams []string + if existing.IsPartial() { + value = false // partials always have a value of false. + missingContextParams, _ = existing.MissingVarNames() + } - return syntheticResult{true, contextValues, built}, nil + exprString, err := buildExprString() + if err != nil { + return nil, err + } + + return syntheticResult{ + value: value, + contextValues: contextValues, + exprString: exprString, + missingContextParams: missingContextParams, + }, nil + } + + for _, child := range cop.Children { + childResult, err := runExpressionWithCaveats(ctx, env, child, context, loadedCaveats, debugOption) + if err != nil { + return nil, err + } + + switch cop.Op { + case core.CaveatOperation_AND: + cr, err := and(currentResult, childResult) + if err != nil { + return nil, err } - case core.CaveatOperation_NOT: - if debugOption == RunCaveatExpressionWithDebugInformation { - contextValues = combineMaps(contextValues, childResult.ContextValues()) - exprString, err := childResult.ExpressionString() - if err != nil { - return nil, err - } - - exprStringPieces = append(exprStringPieces, "!("+exprString+")") + currentResult = cr + + if isFalseResult(currentResult) { + return currentResult, nil } - built, err := buildExprString() + case core.CaveatOperation_OR: + cr, err := or(currentResult, childResult) if err != nil { return nil, err } - return syntheticResult{!childResult.Value(), contextValues, built}, nil + currentResult = cr + + if isTrueResult(currentResult) { + return currentResult, nil + } + + case core.CaveatOperation_NOT: + return invert(childResult) default: return nil, spiceerrors.MustBugf("unknown caveat operation: %v", cop.Op) } } - built, err := buildExprString() - if err != nil { - return nil, err - } - - return syntheticResult{boolResult, contextValues, built}, nil + return currentResult, nil } func combineMaps(first map[string]any, second map[string]any) map[string]any { @@ -318,6 +430,10 @@ func combineMaps(first map[string]any, second map[string]any) map[string]any { first = make(map[string]any, len(second)) } + if second == nil { + return first + } + cloned := maps.Clone(first) maps.Copy(cloned, second) return cloned diff --git a/internal/caveats/run_test.go b/internal/caveats/run_test.go index dbabd97c3b..0bb718e759 100644 --- a/internal/caveats/run_test.go +++ b/internal/caveats/run_test.go @@ -23,10 +23,12 @@ var ( func TestRunCaveatExpressions(t *testing.T) { tcs := []struct { - name string - expression *core.CaveatExpression - context map[string]any - expectedValue bool + name string + expression *core.CaveatExpression + context map[string]any + expectedValue bool + expectedMissingContext []string + expectedExprString string }{ { "basic", @@ -35,6 +37,8 @@ func TestRunCaveatExpressions(t *testing.T) { "first": "42", }, true, + nil, + "first == 42", }, { "another basic", @@ -43,6 +47,8 @@ func TestRunCaveatExpressions(t *testing.T) { "first": "12", }, false, + nil, + "first == 42", }, { "short circuited or", @@ -54,6 +60,8 @@ func TestRunCaveatExpressions(t *testing.T) { "first": "42", }, true, + nil, + "first == 42", }, { "non-short circuited or", @@ -66,6 +74,8 @@ func TestRunCaveatExpressions(t *testing.T) { "second": "hello", }, true, + nil, + `second == "hello"`, }, { "false or", @@ -78,6 +88,8 @@ func TestRunCaveatExpressions(t *testing.T) { "second": "hi", }, false, + nil, + `first == 42 || second == "hello"`, }, { "short circuited and", @@ -89,6 +101,8 @@ func TestRunCaveatExpressions(t *testing.T) { "first": "12", }, false, + nil, + "first == 42", }, { "non-short circuited and", @@ -101,6 +115,8 @@ func TestRunCaveatExpressions(t *testing.T) { "second": "hello", }, false, + nil, + "first == 42", }, { "false or", @@ -113,6 +129,8 @@ func TestRunCaveatExpressions(t *testing.T) { "second": "hi", }, false, + nil, + `second == "hello"`, }, { "inversion", @@ -123,6 +141,8 @@ func TestRunCaveatExpressions(t *testing.T) { "first": "12", }, true, + nil, + `!(first == 42)`, }, { "nested", @@ -141,6 +161,8 @@ func TestRunCaveatExpressions(t *testing.T) { "third": false, }, false, + nil, + `first == 42 || second == "hello"`, }, { "nested true", @@ -159,6 +181,264 @@ func TestRunCaveatExpressions(t *testing.T) { "third": false, }, true, + nil, + `first == 42 && !(third)`, + }, + { + "missing context on left side of and branch, right branch is true", + caveatAnd( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hello", + }, + false, + []string{"first"}, + `first == 42 && second == "hello"`, + }, + { + "missing context on right side of and branch, left branch is true", + caveatAnd( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "first": "42", + }, + false, + []string{"second"}, + `first == 42 && second == "hello"`, + }, + { + "missing context on left side of or branch, right branch is true", + caveatOr( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hello", + }, + true, + nil, + `second == "hello"`, + }, + { + "missing context on right side of or branch, left branch is true", + caveatOr( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "first": "42", + }, + true, + nil, + `first == 42`, + }, + { + "missing context on left side of and branch, right branch is false", + caveatAnd( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hi", + }, + false, + nil, + `second == "hello"`, + }, + { + "missing context on right side of and branch, left branch is false", + caveatAnd( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "first": "41", + }, + false, + nil, + `first == 42`, + }, + { + "missing context on left side of or branch, right branch is false", + caveatOr( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hi", + }, + false, + []string{"first"}, + `first == 42 || second == "hello"`, + }, + { + "missing context on right side of or branch, left branch is false", + caveatOr( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "first": "41", + }, + false, + []string{"second"}, + `first == 42 || second == "hello"`, + }, + { + "missing context on both sides of and branch", + caveatAnd( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{}, + false, + []string{"first", "second"}, + `first == 42 && second == "hello"`, + }, + { + "missing context on both sides of or branch", + caveatOr( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{}, + false, + []string{"first", "second"}, + `first == 42 || second == "hello"`, + }, + { + "missing context under invert", + caveatInvert( + caveatexpr("firstCaveat"), + ), + map[string]any{}, + false, + []string{"first"}, + `!(first == 42)`, + }, + { + "missing context with invert under and with true right branch", + caveatAnd( + caveatInvert( + caveatexpr("firstCaveat"), + ), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hello", + }, + false, + []string{"first"}, + `!(first == 42) && second == "hello"`, + }, + { + "missing context with invert under and with true left branch", + caveatAnd( + caveatexpr("secondCaveat"), + caveatInvert( + caveatexpr("firstCaveat"), + ), + ), + map[string]any{ + "second": "hello", + }, + false, + []string{"first"}, + `second == "hello" && !(first == 42)`, + }, + { + "missing context with invert under and with false right branch", + caveatAnd( + caveatInvert( + caveatexpr("firstCaveat"), + ), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hi", + }, + false, + nil, + `second == "hello"`, + }, + { + "missing context with invert under and with false left branch", + caveatAnd( + caveatexpr("secondCaveat"), + caveatInvert( + caveatexpr("firstCaveat"), + ), + ), + map[string]any{ + "second": "hi", + }, + false, + nil, + `second == "hello"`, + }, + { + "missing context with invert under or with true right branch", + caveatOr( + caveatInvert( + caveatexpr("firstCaveat"), + ), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hello", + }, + true, + nil, + `second == "hello"`, + }, + { + "missing context with invert under or with true left branch", + caveatOr( + caveatexpr("secondCaveat"), + caveatInvert( + caveatexpr("firstCaveat"), + ), + ), + map[string]any{ + "second": "hello", + }, + true, + nil, + `second == "hello"`, + }, + { + "missing context with invert under or with false right branch", + caveatOr( + caveatInvert( + caveatexpr("firstCaveat"), + ), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hi", + }, + false, + []string{"first"}, + `!(first == 42) || second == "hello"`, + }, + { + "missing context with invert under or with false left branch", + caveatOr( + caveatexpr("secondCaveat"), + caveatInvert( + caveatexpr("firstCaveat"), + ), + ), + map[string]any{ + "second": "hi", + }, + false, + []string{"first"}, + `second == "hello" || !(first == 42)`, }, } @@ -199,6 +479,30 @@ func TestRunCaveatExpressions(t *testing.T) { result, err := caveats.RunCaveatExpression(context.Background(), tc.expression, tc.context, reader, debugOption) req.NoError(err) req.Equal(tc.expectedValue, result.Value()) + + if len(tc.expectedMissingContext) > 0 { + require.False(t, result.Value()) + require.True(t, result.IsPartial()) + + missingFields, err := result.MissingVarNames() + require.NoError(t, err) + require.ElementsMatch(t, tc.expectedMissingContext, missingFields) + } else { + require.False(t, result.IsPartial()) + } + + _, err = result.ContextStruct() + require.NoError(t, err) + + _, err = result.ExpressionString() + require.NoError(t, err) + + if debugOption == caveats.RunCaveatExpressionWithDebugInformation { + exprString, err := result.ExpressionString() + require.NoError(t, err) + require.NotEmpty(t, exprString) + require.Equal(t, tc.expectedExprString, exprString) + } }) } }) diff --git a/internal/dispatch/caching/caching.go b/internal/dispatch/caching/caching.go index 49cfd7bff4..5428502190 100644 --- a/internal/dispatch/caching/caching.go +++ b/internal/dispatch/caching/caching.go @@ -292,6 +292,70 @@ func sliceSize(xs []byte) int64 { return int64(int(unsafe.Sizeof(xs)) + len(xs)) } +func (cd *Dispatcher) DispatchLookupResources2(req *v1.DispatchLookupResources2Request, stream dispatch.LookupResources2Stream) error { + cd.lookupResourcesTotalCounter.Inc() + + requestKey, err := cd.keyHandler.LookupResources2CacheKey(stream.Context(), req) + if err != nil { + return err + } + + if cachedResultRaw, found := cd.c.Get(requestKey); found { + cd.lookupResourcesFromCacheCounter.Inc() + for _, slice := range cachedResultRaw.([][]byte) { + var response v1.DispatchLookupResources2Response + if err := response.UnmarshalVT(slice); err != nil { + return err + } + if err := stream.Publish(&response); err != nil { + // don't wrap error with additional context, as it may be a grpc status.Status. + // status.FromError() is unable to unwrap status.Status values, and as a consequence + // the Dispatcher wouldn't properly propagate the gRPC error code + return err + } + } + return nil + } + + var ( + mu sync.Mutex + toCacheResults [][]byte + ) + wrapped := &dispatch.WrappedDispatchStream[*v1.DispatchLookupResources2Response]{ + Stream: stream, + Ctx: stream.Context(), + Processor: func(result *v1.DispatchLookupResources2Response) (*v1.DispatchLookupResources2Response, bool, error) { + adjustedResult := result.CloneVT() + adjustedResult.Metadata.CachedDispatchCount = adjustedResult.Metadata.DispatchCount + adjustedResult.Metadata.DispatchCount = 0 + adjustedResult.Metadata.DebugInfo = nil + + adjustedBytes, err := adjustedResult.MarshalVT() + if err != nil { + return &v1.DispatchLookupResources2Response{Metadata: &v1.ResponseMeta{}}, false, err + } + + mu.Lock() + toCacheResults = append(toCacheResults, adjustedBytes) + mu.Unlock() + + return result, true, nil + }, + } + + if err := cd.d.DispatchLookupResources2(req, wrapped); err != nil { + return err + } + + var size int64 + for _, slice := range toCacheResults { + size += sliceSize(slice) + } + + cd.c.Set(requestKey, toCacheResults, size) + return nil +} + // DispatchLookupResources implements dispatch.LookupResources interface. func (cd *Dispatcher) DispatchLookupResources(req *v1.DispatchLookupResourcesRequest, stream dispatch.LookupResourcesStream) error { cd.lookupResourcesTotalCounter.Inc() diff --git a/internal/dispatch/caching/cachingdispatch_test.go b/internal/dispatch/caching/cachingdispatch_test.go index 1d4066c531..8eee3d5ee5 100644 --- a/internal/dispatch/caching/cachingdispatch_test.go +++ b/internal/dispatch/caching/cachingdispatch_test.go @@ -167,6 +167,10 @@ func (ddm delegateDispatchMock) DispatchLookupResources(_ *v1.DispatchLookupReso return nil } +func (ddm delegateDispatchMock) DispatchLookupResources2(_ *v1.DispatchLookupResources2Request, _ dispatch.LookupResources2Stream) error { + return nil +} + func (ddm delegateDispatchMock) DispatchLookupSubjects(_ *v1.DispatchLookupSubjectsRequest, _ dispatch.LookupSubjectsStream) error { return nil } diff --git a/internal/dispatch/caching/delegate.go b/internal/dispatch/caching/delegate.go index 7c42529688..399742c4e5 100644 --- a/internal/dispatch/caching/delegate.go +++ b/internal/dispatch/caching/delegate.go @@ -36,6 +36,10 @@ func (fd fakeDelegate) DispatchLookupResources(_ *v1.DispatchLookupResourcesRequ return spiceerrors.MustBugf(errMessage) } +func (fd fakeDelegate) DispatchLookupResources2(_ *v1.DispatchLookupResources2Request, _ dispatch.LookupResources2Stream) error { + return spiceerrors.MustBugf(errMessage) +} + func (fd fakeDelegate) DispatchLookupSubjects(_ *v1.DispatchLookupSubjectsRequest, _ dispatch.LookupSubjectsStream) error { return spiceerrors.MustBugf(errMessage) } diff --git a/internal/dispatch/dispatch.go b/internal/dispatch/dispatch.go index 8ed2ee4b9a..b997aaa4f1 100644 --- a/internal/dispatch/dispatch.go +++ b/internal/dispatch/dispatch.go @@ -26,6 +26,7 @@ type Dispatcher interface { ReachableResources LookupResources LookupSubjects + LookupResources2 // Close closes the dispatcher. Close() error @@ -70,6 +71,15 @@ type LookupResources interface { ) error } +type LookupResources2Stream = Stream[*v1.DispatchLookupResources2Response] + +type LookupResources2 interface { + DispatchLookupResources2( + req *v1.DispatchLookupResources2Request, + stream LookupResources2Stream, + ) error +} + // LookupSubjectsStream is an alias for the stream to which found subjects will be written. type LookupSubjectsStream = Stream[*v1.DispatchLookupSubjectsResponse] diff --git a/internal/dispatch/graph/check_test.go b/internal/dispatch/graph/check_test.go index e5ba8b0039..955f0e0e53 100644 --- a/internal/dispatch/graph/check_test.go +++ b/internal/dispatch/graph/check_test.go @@ -15,6 +15,7 @@ import ( "github.com/authzed/spicedb/internal/dispatch/caching" "github.com/authzed/spicedb/internal/dispatch/keys" "github.com/authzed/spicedb/internal/graph" + "github.com/authzed/spicedb/internal/graph/hints" log "github.com/authzed/spicedb/internal/logging" datastoremw "github.com/authzed/spicedb/internal/middleware/datastore" "github.com/authzed/spicedb/internal/testfixtures" @@ -1372,6 +1373,472 @@ func TestCheckDebugging(t *testing.T) { } } +func TestCheckWithHints(t *testing.T) { + testCases := []struct { + name string + schema string + relationships []*core.RelationTuple + resource *core.ObjectAndRelation + subject *core.ObjectAndRelation + hints []*v1.CheckHint + expectedPermissionship bool + }{ + { + "no relationships", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer + editor + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + nil, + false, + }, + { + "no relationships with matching check hint", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer + editor + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{hints.CheckHintForComputedUserset("document", "somedoc", "viewer", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + })}, + true, + }, + { + "no relationships with unrelated check hint", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer + editor + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{hints.CheckHintForComputedUserset("document", "anotherdoc", "viewer", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + })}, + false, + }, + { + "no relationships with unrelated check hint due to subject", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer + editor + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{hints.CheckHintForComputedUserset("document", "somedoc", "viewer", ONR("user", "anotheruser", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + })}, + false, + }, + { + "no relationships with matching arrow check hint", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation org: organization + permission view = org->member + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{hints.CheckHintForArrow("document", "somedoc", "org", "member", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + })}, + true, + }, + { + "no relationships with non matching tupleset arrow check hint", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation org: organization + permission view = org->member + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{hints.CheckHintForArrow("document", "somedoc", "anotherrel", "member", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + })}, + false, + }, + { + "no relationships with non matching computed userset arrow check hint", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation org: organization + permission view = org->member + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{hints.CheckHintForArrow("document", "somedoc", "org", "membersssss", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + })}, + false, + }, + { + "check hint over part of an intersection but missing other branch in rels", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer & editor + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{hints.CheckHintForComputedUserset("document", "somedoc", "viewer", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + })}, + false, + }, + { + "check hint over part of an intersection with other branch in rels", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer & editor + }`, + []*core.RelationTuple{ + tuple.MustParse("document:somedoc#editor@user:tom"), + }, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{hints.CheckHintForComputedUserset("document", "somedoc", "viewer", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + })}, + true, + }, + { + "check hint of wrong type over part of an intersection with other branch in rels", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer & editor + }`, + []*core.RelationTuple{ + tuple.MustParse("document:somedoc#editor@user:tom"), + }, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{hints.CheckHintForArrow("document", "somedoc", "viewer", "something", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + })}, + false, + }, + { + "check hint over part of an exclusion with other branch not in rels", + `definition user {} + + definition document { + relation banned: user + relation viewer: user + permission view = viewer - banned + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{hints.CheckHintForComputedUserset("document", "somedoc", "viewer", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + })}, + true, + }, + { + "check hint for wildcard", + `definition user {} + + definition document { + relation viewer: user:* + permission view = viewer + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{hints.CheckHintForComputedUserset("document", "somedoc", "viewer", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + })}, + true, + }, + { + "check hint for wildcard, negative", + `definition user {} + + definition document { + relation viewer: user:* + permission view = viewer + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{hints.CheckHintForComputedUserset("document", "somedoc", "viewer", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_NOT_MEMBER, + })}, + false, + }, + { + "check hint for each branch of an intersection", + `definition user {} + + definition document { + relation viewer: user + relation editor: user + permission view = viewer & editor + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{ + hints.CheckHintForComputedUserset("document", "somedoc", "viewer", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + }), + hints.CheckHintForComputedUserset("document", "somedoc", "editor", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + }), + }, + true, + }, + { + "check hint for each branch of an exclusion", + `definition user {} + + definition document { + relation viewer: user + relation banned: user + permission view = viewer - banned + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{ + hints.CheckHintForComputedUserset("document", "somedoc", "viewer", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + }), + hints.CheckHintForComputedUserset("document", "somedoc", "banned", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + }), + }, + false, + }, + { + "check hint for each branch of an exclusion, allowed", + `definition user {} + + definition document { + relation viewer: user + relation banned: user + permission view = viewer - banned + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{ + hints.CheckHintForComputedUserset("document", "somedoc", "viewer", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + }), + hints.CheckHintForComputedUserset("document", "somedoc", "banned", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_NOT_MEMBER, + }), + }, + true, + }, + { + "check hint for each branch of an exclusion, not member on either branch", + `definition user {} + + definition document { + relation viewer: user + relation banned: user + permission view = viewer - banned + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + []*v1.CheckHint{ + hints.CheckHintForComputedUserset("document", "somedoc", "viewer", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_NOT_MEMBER, + }), + hints.CheckHintForComputedUserset("document", "somedoc", "banned", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_NOT_MEMBER, + }), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + require := require.New(t) + + dispatcher := NewLocalOnlyDispatcher(10) + + ds, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.DatastoreFromSchemaAndTestRelationships(ds, tc.schema, tc.relationships, require) + + ctx := datastoremw.ContextWithHandle(context.Background()) + require.NoError(datastoremw.SetInContext(ctx, ds)) + + resp, err := dispatcher.DispatchCheck(ctx, &v1.DispatchCheckRequest{ + ResourceRelation: RR(tc.resource.Namespace, tc.resource.Relation), + ResourceIds: []string{tc.resource.ObjectId}, + Subject: tc.subject, + ResultsSetting: v1.DispatchCheckRequest_ALLOW_SINGLE_RESULT, + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + CheckHints: tc.hints, + }) + require.NoError(err) + + _, ok := resp.ResultsByResourceId[tc.resource.ObjectId] + if tc.expectedPermissionship { + require.True(ok) + require.Equal(v1.ResourceCheckResult_MEMBER, resp.ResultsByResourceId[tc.resource.ObjectId].Membership) + } else { + if ok { + require.Equal(v1.ResourceCheckResult_NOT_MEMBER, resp.ResultsByResourceId[tc.resource.ObjectId].Membership) + } + } + }) + } +} + +func TestCheckHintsPartialApplication(t *testing.T) { + require := require.New(t) + + dispatcher := NewLocalOnlyDispatcher(10) + + ds, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.DatastoreFromSchemaAndTestRelationships(ds, ` + definition user {} + + definition document { + relation viewer: user + permission view = viewer + } + + `, []*core.RelationTuple{ + tuple.MustParse("document:somedoc#viewer@user:tom"), + }, require) + + ctx := datastoremw.ContextWithHandle(context.Background()) + require.NoError(datastoremw.SetInContext(ctx, ds)) + + resp, err := dispatcher.DispatchCheck(ctx, &v1.DispatchCheckRequest{ + ResourceRelation: RR("document", "view"), + ResourceIds: []string{"somedoc", "anotherdoc", "thirddoc"}, + Subject: ONR("user", "tom", graph.Ellipsis), + ResultsSetting: v1.DispatchCheckRequest_REQUIRE_ALL_RESULTS, + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + CheckHints: []*v1.CheckHint{ + hints.CheckHintForComputedUserset("document", "anotherdoc", "viewer", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + }), + }, + }) + require.NoError(err) + + require.Len(resp.ResultsByResourceId, 2) + require.Equal(v1.ResourceCheckResult_MEMBER, resp.ResultsByResourceId["somedoc"].Membership) + require.Equal(v1.ResourceCheckResult_MEMBER, resp.ResultsByResourceId["anotherdoc"].Membership) +} + +func TestCheckHintsPartialApplicationOverArrow(t *testing.T) { + require := require.New(t) + + dispatcher := NewLocalOnlyDispatcher(10) + + ds, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.DatastoreFromSchemaAndTestRelationships(ds, ` + definition user {} + + definition organization { + relation member: user + } + + definition document { + relation org: organization + permission view = org->member + } + + `, []*core.RelationTuple{ + tuple.MustParse("document:somedoc#org@organization:someorg"), + tuple.MustParse("organization:someorg#member@user:tom"), + }, require) + + ctx := datastoremw.ContextWithHandle(context.Background()) + require.NoError(datastoremw.SetInContext(ctx, ds)) + + resp, err := dispatcher.DispatchCheck(ctx, &v1.DispatchCheckRequest{ + ResourceRelation: RR("document", "view"), + ResourceIds: []string{"somedoc", "anotherdoc", "thirddoc"}, + Subject: ONR("user", "tom", graph.Ellipsis), + ResultsSetting: v1.DispatchCheckRequest_REQUIRE_ALL_RESULTS, + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + CheckHints: []*v1.CheckHint{ + hints.CheckHintForArrow("document", "anotherdoc", "org", "member", ONR("user", "tom", graph.Ellipsis), &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + }), + }, + }) + require.NoError(err) + + require.Len(resp.ResultsByResourceId, 2) + require.Equal(v1.ResourceCheckResult_MEMBER, resp.ResultsByResourceId["somedoc"].Membership) + require.Equal(v1.ResourceCheckResult_MEMBER, resp.ResultsByResourceId["anotherdoc"].Membership) +} + func newLocalDispatcherWithConcurrencyLimit(t testing.TB, concurrencyLimit uint16) (context.Context, dispatch.Dispatcher, datastore.Revision) { rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) require.NoError(t, err) diff --git a/internal/dispatch/graph/graph.go b/internal/dispatch/graph/graph.go index 72a8d9093a..0dd5cde2d6 100644 --- a/internal/dispatch/graph/graph.go +++ b/internal/dispatch/graph/graph.go @@ -94,6 +94,7 @@ func NewLocalOnlyDispatcherWithLimits(concurrencyLimits ConcurrencyLimits) dispa d.reachableResourcesHandler = graph.NewCursoredReachableResources(d, concurrencyLimits.ReachableResources) d.lookupResourcesHandler = graph.NewCursoredLookupResources(d, d, concurrencyLimits.LookupResources) d.lookupSubjectsHandler = graph.NewConcurrentLookupSubjects(d, concurrencyLimits.LookupSubjects) + d.lookupResourcesHandler2 = graph.NewCursoredLookupResources2(d, d, concurrencyLimits.LookupResources) return d } @@ -108,6 +109,7 @@ func NewDispatcher(redispatcher dispatch.Dispatcher, concurrencyLimits Concurren reachableResourcesHandler := graph.NewCursoredReachableResources(redispatcher, concurrencyLimits.ReachableResources) lookupResourcesHandler := graph.NewCursoredLookupResources(redispatcher, redispatcher, concurrencyLimits.LookupResources) lookupSubjectsHandler := graph.NewConcurrentLookupSubjects(redispatcher, concurrencyLimits.LookupSubjects) + lookupResourcesHandler2 := graph.NewCursoredLookupResources2(redispatcher, redispatcher, concurrencyLimits.LookupResources) return &localDispatcher{ checker: checker, @@ -115,6 +117,7 @@ func NewDispatcher(redispatcher dispatch.Dispatcher, concurrencyLimits Concurren reachableResourcesHandler: reachableResourcesHandler, lookupResourcesHandler: lookupResourcesHandler, lookupSubjectsHandler: lookupSubjectsHandler, + lookupResourcesHandler2: lookupResourcesHandler2, } } @@ -124,6 +127,7 @@ type localDispatcher struct { reachableResourcesHandler *graph.CursoredReachableResources lookupResourcesHandler *graph.CursoredLookupResources lookupSubjectsHandler *graph.ConcurrentLookupSubjects + lookupResourcesHandler2 *graph.CursoredLookupResources2 } func (ld *localDispatcher) loadNamespace(ctx context.Context, nsName string, revision datastore.Revision) (*core.NamespaceDefinition, error) { @@ -228,8 +232,10 @@ func (ld *localDispatcher) DispatchCheck(ctx context.Context, req *v1.DispatchCh Subject: req.Subject, Metadata: req.Metadata, Debug: req.Debug, + CheckHints: req.CheckHints, }, - Revision: revision, + Revision: revision, + OriginalRelationName: req.ResourceRelation.Relation, } resp, err := ld.checker.Check(ctx, validatedReq, relation) @@ -337,6 +343,34 @@ func (ld *localDispatcher) DispatchLookupResources( ) } +func (ld *localDispatcher) DispatchLookupResources2( + req *v1.DispatchLookupResources2Request, + stream dispatch.LookupResources2Stream, +) error { + ctx, span := tracer.Start(stream.Context(), "DispatchLookupResources2", trace.WithAttributes( + attribute.String("resource-type", tuple.StringRR(req.ResourceRelation)), + attribute.String("subject", tuple.StringONR(req.TerminalSubject)), + )) + defer span.End() + + if err := dispatch.CheckDepth(ctx, req); err != nil { + return err + } + + revision, err := ld.parseRevision(ctx, req.Metadata.AtRevision) + if err != nil { + return err + } + + return ld.lookupResourcesHandler2.LookupResources2( + graph.ValidatedLookupResources2Request{ + DispatchLookupResources2Request: req, + Revision: revision, + }, + dispatch.StreamWithContext(ctx, stream), + ) +} + // DispatchLookupSubjects implements dispatch.LookupSubjects interface func (ld *localDispatcher) DispatchLookupSubjects( req *v1.DispatchLookupSubjectsRequest, diff --git a/internal/dispatch/graph/lookupresources2_test.go b/internal/dispatch/graph/lookupresources2_test.go new file mode 100644 index 0000000000..0b1f5be41d --- /dev/null +++ b/internal/dispatch/graph/lookupresources2_test.go @@ -0,0 +1,933 @@ +package graph + +import ( + "context" + "fmt" + "slices" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.uber.org/goleak" + + "github.com/authzed/spicedb/internal/datastore/memdb" + "github.com/authzed/spicedb/internal/dispatch" + datastoremw "github.com/authzed/spicedb/internal/middleware/datastore" + "github.com/authzed/spicedb/internal/testfixtures" + "github.com/authzed/spicedb/pkg/datastore" + "github.com/authzed/spicedb/pkg/datastore/options" + "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/tuple" +) + +func TestSimpleLookupResources2(t *testing.T) { + defer goleak.VerifyNone(t, goleakIgnores...) + + testCases := []struct { + start *core.RelationReference + target *core.ObjectAndRelation + expectedResources []*v1.PossibleResource + expectedDispatchCount uint32 + expectedDepthRequired uint32 + }{ + { + RR("document", "view"), + ONR("user", "unknown", "..."), + []*v1.PossibleResource{}, + 0, + 0, + }, + { + RR("document", "view"), + ONR("user", "eng_lead", "..."), + []*v1.PossibleResource{ + possibleRes("masterplan"), + }, + 2, + 1, + }, + { + RR("document", "owner"), + ONR("user", "product_manager", "..."), + []*v1.PossibleResource{ + possibleRes("masterplan"), + }, + 2, + 0, + }, + { + RR("document", "view"), + ONR("user", "legal", "..."), + []*v1.PossibleResource{ + possibleRes("companyplan"), + possibleRes("masterplan"), + }, + 6, + 3, + }, + { + RR("document", "view_and_edit"), + ONR("user", "multiroleguy", "..."), + []*v1.PossibleResource{ + possibleRes("specialplan"), + }, + 7, + 5, + }, + { + RR("folder", "view"), + ONR("user", "owner", "..."), + []*v1.PossibleResource{ + possibleRes("strategy"), + possibleRes("company"), + }, + 8, + 4, + }, + } + + for _, tc := range testCases { + name := fmt.Sprintf( + "%s#%s->%s", + tc.start.Namespace, + tc.start.Relation, + tuple.StringONR(tc.target), + ) + + tc := tc + t.Run(name, func(t *testing.T) { + defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) + + require := require.New(t) + ctx, dispatcher, revision := newLocalDispatcher(t) + defer dispatcher.Close() + + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + err := dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: tc.start, + SubjectRelation: RR(tc.target.Namespace, tc.target.Relation), + SubjectIds: []string{tc.target.ObjectId}, + TerminalSubject: tc.target, + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalLimit: veryLargeLimit, + }, stream) + + require.NoError(err) + + foundResources, maxDepthRequired, maxDispatchCount, maxCachedDispatchCount := processResults2(stream) + require.ElementsMatch(tc.expectedResources, foundResources, "Found: %v, Expected: %v", foundResources, tc.expectedResources) + require.Equal(tc.expectedDepthRequired, maxDepthRequired, "Depth required mismatch") + require.LessOrEqual(maxDispatchCount, tc.expectedDispatchCount, "Found dispatch count greater than expected") + require.Equal(uint32(0), maxCachedDispatchCount) + + // We have to sleep a while to let the cache converge: + // https://github.com/outcaste-io/ristretto/blob/01b9f37dd0fd453225e042d6f3a27cd14f252cd0/cache_test.go#L17 + time.Sleep(10 * time.Millisecond) + + // Run again with the cache available. + stream = dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: tc.start, + SubjectRelation: RR(tc.target.Namespace, tc.target.Relation), + SubjectIds: []string{tc.target.ObjectId}, + TerminalSubject: tc.target, + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalLimit: veryLargeLimit, + }, stream) + dispatcher.Close() + + require.NoError(err) + + foundResources, maxDepthRequired, maxDispatchCount, maxCachedDispatchCount = processResults2(stream) + require.ElementsMatch(tc.expectedResources, foundResources, "Found: %v, Expected: %v", foundResources, tc.expectedResources) + require.Equal(tc.expectedDepthRequired, maxDepthRequired, "Depth required mismatch") + require.LessOrEqual(maxCachedDispatchCount, tc.expectedDispatchCount, "Found dispatch count greater than expected") + require.Equal(uint32(0), maxDispatchCount) + }) + } +} + +func TestSimpleLookupResourcesWithCursor2(t *testing.T) { + defer goleak.VerifyNone(t, goleakIgnores...) + + for _, tc := range []struct { + subject string + expectedFirst []string + expectedSecond []string + }{ + { + subject: "owner", + expectedFirst: []string{"ownerplan"}, + expectedSecond: []string{"companyplan", "masterplan", "ownerplan"}, + }, + { + subject: "chief_financial_officer", + expectedFirst: []string{"healthplan"}, + expectedSecond: []string{"healthplan", "masterplan"}, + }, + { + subject: "auditor", + expectedFirst: []string{"companyplan"}, + expectedSecond: []string{"companyplan", "masterplan"}, + }, + } { + tc := tc + t.Run(tc.subject, func(t *testing.T) { + require := require.New(t) + ctx, dispatcher, revision := newLocalDispatcher(t) + defer dispatcher.Close() + + found := mapz.NewSet[string]() + + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + err := dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{tc.subject}, + TerminalSubject: ONR("user", tc.subject, "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalLimit: 1, + }, stream) + + require.NoError(err) + + require.Equal(1, len(stream.Results())) + + found.Insert(stream.Results()[0].Resource.ResourceId) + require.Equal(tc.expectedFirst, found.AsSlice()) + + cursor := stream.Results()[0].AfterResponseCursor + require.NotNil(cursor) + + stream = dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{tc.subject}, + TerminalSubject: ONR("user", tc.subject, "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalCursor: cursor, + OptionalLimit: 2, + }, stream) + + require.NoError(err) + + for _, result := range stream.Results() { + found.Insert(result.Resource.ResourceId) + } + + foundResults := found.AsSlice() + slices.Sort(foundResults) + + require.Equal(tc.expectedSecond, foundResults) + }) + } +} + +func TestLookupResourcesCursorStability2(t *testing.T) { + defer goleak.VerifyNone(t, goleakIgnores...) + + require := require.New(t) + ctx, dispatcher, revision := newLocalDispatcher(t) + defer dispatcher.Close() + + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + + // Make the first first request. + err := dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"owner"}, + TerminalSubject: ONR("user", "owner", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalLimit: 2, + }, stream) + + require.NoError(err) + require.Equal(2, len(stream.Results())) + + cursor := stream.Results()[1].AfterResponseCursor + require.NotNil(cursor) + + // Make the same request and ensure the cursor has not changed. + stream = dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"owner"}, + TerminalSubject: ONR("user", "owner", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalLimit: 2, + }, stream) + + require.NoError(err) + + require.NoError(err) + require.Equal(2, len(stream.Results())) + + cursorAgain := stream.Results()[1].AfterResponseCursor + require.NotNil(cursor) + require.Equal(cursor, cursorAgain) +} + +func processResults2(stream *dispatch.CollectingDispatchStream[*v1.DispatchLookupResources2Response]) ([]*v1.PossibleResource, uint32, uint32, uint32) { + foundResources := []*v1.PossibleResource{} + var maxDepthRequired uint32 + var maxDispatchCount uint32 + var maxCachedDispatchCount uint32 + for _, result := range stream.Results() { + result.Resource.ForSubjectIds = nil + foundResources = append(foundResources, result.Resource) + maxDepthRequired = max(maxDepthRequired, result.Metadata.DepthRequired) + maxDispatchCount = max(maxDispatchCount, result.Metadata.DispatchCount) + maxCachedDispatchCount = max(maxCachedDispatchCount, result.Metadata.CachedDispatchCount) + } + return foundResources, maxDepthRequired, maxDispatchCount, maxCachedDispatchCount +} + +func TestMaxDepthLookup2(t *testing.T) { + require := require.New(t) + + rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.StandardDatastoreWithData(rawDS, require) + + dispatcher := NewLocalOnlyDispatcher(10) + defer dispatcher.Close() + + ctx := datastoremw.ContextWithHandle(context.Background()) + require.NoError(datastoremw.SetInContext(ctx, ds)) + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"legal"}, + TerminalSubject: ONR("user", "legal", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 0, + }, + }, stream) + + require.Error(err) +} + +func TestLookupResources2OverSchemaWithCursors(t *testing.T) { + testCases := []struct { + name string + schema string + relationships []*core.RelationTuple + permission *core.RelationReference + subject *core.ObjectAndRelation + expectedResourceIDs []string + }{ + { + "basic union", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer + editor + }`, + joinTuples( + genTuples("document", "viewer", "user", "tom", 1510), + genTuples("document", "editor", "user", "tom", 1510), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 1510), + }, + { + "basic exclusion", + `definition user {} + + definition document { + relation banned: user + relation viewer: user + permission view = viewer - banned + }`, + genTuples("document", "viewer", "user", "tom", 1010), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 1010), + }, + { + "basic intersection", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer & editor + }`, + joinTuples( + genTuples("document", "viewer", "user", "tom", 510), + genTuples("document", "editor", "user", "tom", 510), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 510), + }, + { + "union and excluded union", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + relation banned: user + permission can_view = viewer - banned + permission view = can_view + editor + }`, + joinTuples( + genTuples("document", "viewer", "user", "tom", 1310), + genTuplesWithOffset("document", "editor", "user", "tom", 1250, 1200), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 2450), + }, + { + "basic caveats", + `definition user {} + + caveat somecaveat(somecondition int) { + somecondition == 42 + } + + definition document { + relation viewer: user with somecaveat + permission view = viewer + }`, + genTuplesWithCaveat("document", "viewer", "user", "tom", "somecaveat", map[string]any{"somecondition": 42}, 0, 2450), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 2450), + }, + { + "excluded items", + `definition user {} + + definition document { + relation banned: user + relation viewer: user + permission view = viewer - banned + }`, + joinTuples( + genTuples("document", "viewer", "user", "tom", 1310), + genTuplesWithOffset("document", "banned", "user", "tom", 1210, 100), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 1210), + }, + { + "basic caveats with missing field", + `definition user {} + + caveat somecaveat(somecondition int) { + somecondition == 42 + } + + definition document { + relation viewer: user with somecaveat + permission view = viewer + }`, + genTuplesWithCaveat("document", "viewer", "user", "tom", "somecaveat", map[string]any{}, 0, 2450), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 2450), + }, + { + "larger arrow dispatch", + `definition user {} + + definition folder { + relation viewer: user + } + + definition document { + relation folder: folder + permission view = folder->viewer + }`, + joinTuples( + genTuples("folder", "viewer", "user", "tom", 150), + genSubjectTuples("document", "folder", "folder", "...", 150), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 150), + }, + { + "big", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer + editor + }`, + joinTuples( + genTuples("document", "viewer", "user", "tom", 15100), + genTuples("document", "editor", "user", "tom", 15100), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 15100), + }, + { + "arrow under intersection", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation org: organization + relation viewer: user + permission view = org->member & viewer + }`, + joinTuples( + genTuples("document", "viewer", "user", "tom", 510), + joinTuples( + genTuples("document", "org", "organization", "someorg", 510), + []*core.RelationTuple{ + tuple.MustParse("organization:someorg#member@user:tom"), + }, + ), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 510), + }, + { + "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 { + tc := tc + t.Run(tc.name, func(t *testing.T) { + for _, pageSize := range []int{0, 104, 1023} { + pageSize := pageSize + t.Run(fmt.Sprintf("ps-%d_", pageSize), func(t *testing.T) { + require := require.New(t) + + dispatcher := NewLocalOnlyDispatcher(10) + + ds, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.DatastoreFromSchemaAndTestRelationships(ds, tc.schema, tc.relationships, require) + + ctx := datastoremw.ContextWithHandle(context.Background()) + require.NoError(datastoremw.SetInContext(ctx, ds)) + + var currentCursor *v1.Cursor + foundResourceIDs := mapz.NewSet[string]() + for { + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: tc.permission, + SubjectRelation: RR(tc.subject.Namespace, "..."), + SubjectIds: []string{tc.subject.ObjectId}, + TerminalSubject: tc.subject, + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalLimit: uint32(pageSize), + OptionalCursor: currentCursor, + }, stream) + require.NoError(err) + + if pageSize > 0 { + require.LessOrEqual(len(stream.Results()), pageSize) + } + + for _, result := range stream.Results() { + foundResourceIDs.Insert(result.Resource.ResourceId) + currentCursor = result.AfterResponseCursor + } + + if pageSize == 0 || len(stream.Results()) < pageSize { + break + } + } + + foundResourceIDsSlice := foundResourceIDs.AsSlice() + slices.Sort(foundResourceIDsSlice) + slices.Sort(tc.expectedResourceIDs) + + require.Equal(tc.expectedResourceIDs, foundResourceIDsSlice) + }) + } + }) + } +} + +func TestLookupResources2ImmediateTimeout(t *testing.T) { + defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) + + require := require.New(t) + + rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.StandardDatastoreWithData(rawDS, require) + + dispatcher := NewLocalOnlyDispatcher(10) + defer dispatcher.Close() + + ctx := datastoremw.ContextWithHandle(context.Background()) + cctx, cancel := context.WithTimeout(ctx, 1*time.Nanosecond) + defer cancel() + + require.NoError(datastoremw.SetInContext(cctx, ds)) + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](cctx) + + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"legal"}, + TerminalSubject: ONR("user", "legal", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 10, + }, + }, stream) + + require.ErrorIs(err, context.DeadlineExceeded) + require.ErrorContains(err, "context deadline exceeded") +} + +func TestLookupResources2WithError(t *testing.T) { + defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) + + require := require.New(t) + + rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.StandardDatastoreWithData(rawDS, require) + + dispatcher := NewLocalOnlyDispatcher(10) + defer dispatcher.Close() + + ctx := datastoremw.ContextWithHandle(context.Background()) + cctx, cancel := context.WithTimeout(ctx, 1*time.Nanosecond) + defer cancel() + + require.NoError(datastoremw.SetInContext(cctx, ds)) + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](cctx) + + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"legal"}, + TerminalSubject: ONR("user", "legal", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 10, + }, + }, stream) + + require.ErrorIs(err, context.DeadlineExceeded) + require.ErrorContains(err, "context deadline exceeded") +} + +func TestLookupResources2EnsureCheckHints(t *testing.T) { + defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) + + tcs := []struct { + name string + schema string + relationships []*core.RelationTuple + + resourceRelation *core.RelationReference + subject *core.ObjectAndRelation + + disallowedQueries []*core.RelationReference + expectedResources []string + expectedError string + }{ + { + name: "basic intersection", + schema: `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer & editor + }`, + relationships: []*core.RelationTuple{ + tuple.MustParse("document:masterplan#viewer@user:tom"), + tuple.MustParse("document:masterplan#editor@user:tom"), + tuple.MustParse("document:anotherplan#viewer@user:tom"), + tuple.MustParse("document:anotherplan#editor@user:tom"), + }, + resourceRelation: RR("document", "view"), + subject: ONR("user", "tom", "..."), + disallowedQueries: []*core.RelationReference{ + RR("document", "viewer"), + }, + expectedResources: []string{"masterplan", "anotherplan"}, + }, + { + name: "basic arrow", + schema: `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation org: organization + relation editor: user + permission view = org->member & editor + }`, + relationships: []*core.RelationTuple{ + tuple.MustParse("document:masterplan#org@organization:someorg"), + tuple.MustParse("organization:someorg#member@user:tom"), + tuple.MustParse("document:masterplan#editor@user:tom"), + }, + resourceRelation: RR("document", "view"), + subject: ONR("user", "tom", "..."), + disallowedQueries: []*core.RelationReference{ + RR("organization", "member"), + }, + expectedResources: []string{"masterplan"}, + }, + { + name: "basic intersection with disallowed query (sanity check to ensure test is working)", + schema: `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer & editor + }`, + relationships: []*core.RelationTuple{ + tuple.MustParse("document:masterplan#viewer@user:tom"), + tuple.MustParse("document:masterplan#editor@user:tom"), + }, + resourceRelation: RR("document", "view"), + subject: ONR("user", "tom", "..."), + disallowedQueries: []*core.RelationReference{ + RR("document", "editor"), + }, + expectedError: "disallowed query: document#editor", + }, + { + name: "indirect result without alias", + schema: `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission indirect_viewer = viewer + nil + permission indirect_editor = editor + permission view = indirect_viewer & indirect_editor + }`, + relationships: []*core.RelationTuple{ + tuple.MustParse("document:masterplan#viewer@user:tom"), + tuple.MustParse("document:masterplan#editor@user:tom"), + }, + resourceRelation: RR("document", "view"), + subject: ONR("user", "tom", "..."), + disallowedQueries: []*core.RelationReference{ + RR("document", "viewer"), + }, + expectedResources: []string{"masterplan"}, + }, + { + name: "indirect result with alias", + schema: `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission indirect_viewer = viewer + permission indirect_editor = editor + permission view = indirect_viewer & indirect_editor + }`, + relationships: []*core.RelationTuple{ + tuple.MustParse("document:masterplan#viewer@user:tom"), + tuple.MustParse("document:masterplan#editor@user:tom"), + }, + resourceRelation: RR("document", "view"), + subject: ONR("user", "tom", "..."), + disallowedQueries: []*core.RelationReference{ + RR("document", "viewer"), + }, + expectedResources: []string{"masterplan"}, + }, + { + name: "public document", + schema: `definition user {} + + definition document { + relation editor: user + relation viewer: user | user:* + permission view = viewer & editor + }`, + relationships: []*core.RelationTuple{ + tuple.MustParse("document:masterplan#viewer@user:*"), + tuple.MustParse("document:masterplan#editor@user:tom"), + }, + resourceRelation: RR("document", "view"), + subject: ONR("user", "tom", "..."), + disallowedQueries: []*core.RelationReference{ + RR("document", "viewer"), + }, + expectedResources: []string{"masterplan"}, + }, + } + + for _, tc := range tcs { + tc := tc + t.Run(tc.name, func(t *testing.T) { + require := require.New(t) + + rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.DatastoreFromSchemaAndTestRelationships(rawDS, tc.schema, tc.relationships, require) + + checkingDS := disallowedWrapper{ds, tc.disallowedQueries} + + dispatcher := NewLocalOnlyDispatcher(10) + defer dispatcher.Close() + + ctx := datastoremw.ContextWithHandle(context.Background()) + cctx, cancel := context.WithTimeout(ctx, 1*time.Minute) + defer cancel() + + require.NoError(datastoremw.SetInContext(cctx, checkingDS)) + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](cctx) + + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: tc.resourceRelation, + SubjectRelation: RR(tc.subject.Namespace, tc.subject.Relation), + SubjectIds: []string{tc.subject.ObjectId}, + TerminalSubject: tc.subject, + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + }, stream) + if tc.expectedError != "" { + require.Error(err) + require.Contains(err.Error(), tc.expectedError) + return + } + + require.NoError(err) + + foundResourceIDs := mapz.NewSet[string]() + for _, result := range stream.Results() { + foundResourceIDs.Insert(result.Resource.ResourceId) + } + + foundResourceIDsSlice := foundResourceIDs.AsSlice() + slices.Sort(foundResourceIDsSlice) + + require.ElementsMatch(tc.expectedResources, foundResourceIDsSlice) + }) + } +} + +type disallowedWrapper struct { + datastore.Datastore + disallowedQueries []*core.RelationReference +} + +func (dw disallowedWrapper) SnapshotReader(rev datastore.Revision) datastore.Reader { + return disallowedReader{dw.Datastore.SnapshotReader(rev), dw.disallowedQueries} +} + +type disallowedReader struct { + datastore.Reader + disallowedQueries []*core.RelationReference +} + +func (dr disallowedReader) QueryRelationships( + ctx context.Context, + filter datastore.RelationshipsFilter, + options ...options.QueryOptionsOption, +) (datastore.RelationshipIterator, error) { + for _, disallowedQuery := range dr.disallowedQueries { + if disallowedQuery.Namespace == filter.OptionalResourceType && disallowedQuery.Relation == filter.OptionalResourceRelation { + return nil, fmt.Errorf("disallowed query: %s", tuple.StringRR(disallowedQuery)) + } + } + + return dr.Reader.QueryRelationships(ctx, filter, options...) +} + +func possibleRes(resourceID string) *v1.PossibleResource { + return &v1.PossibleResource{ + ResourceId: resourceID, + } +} diff --git a/internal/dispatch/keys/computed.go b/internal/dispatch/keys/computed.go index 38c9a3a732..a9794d2f22 100644 --- a/internal/dispatch/keys/computed.go +++ b/internal/dispatch/keys/computed.go @@ -95,6 +95,19 @@ func lookupResourcesRequestToKey(req *v1.DispatchLookupResourcesRequest, option ) } +// lookupResourcesRequest2ToKey converts a lookup request into a cache key +func lookupResourcesRequest2ToKey(req *v1.DispatchLookupResources2Request, option dispatchCacheKeyHashComputeOption) DispatchCacheKey { + return dispatchCacheKeyHash(lookupPrefix, req.Metadata.AtRevision, option, + hashableRelationReference{req.ResourceRelation}, + hashableRelationReference{req.SubjectRelation}, + hashableIds(req.SubjectIds), + hashableOnr{req.TerminalSubject}, + hashableContext{HashableContext: caveats.HashableContext{Struct: req.Context}}, // NOTE: context is included here because lookup does a single dispatch + hashableCursor{req.OptionalCursor}, + hashableLimit(req.OptionalLimit), + ) +} + // lookupSubjectsRequestToKey converts a lookup subjects request into a cache key func lookupSubjectsRequestToKey(req *v1.DispatchLookupSubjectsRequest, option dispatchCacheKeyHashComputeOption) DispatchCacheKey { return dispatchCacheKeyHash(lookupSubjectsPrefix, req.Metadata.AtRevision, option, diff --git a/internal/dispatch/keys/computed_test.go b/internal/dispatch/keys/computed_test.go index 8af141e99f..9069fecfe2 100644 --- a/internal/dispatch/keys/computed_test.go +++ b/internal/dispatch/keys/computed_test.go @@ -405,6 +405,266 @@ func TestStableCacheKeys(t *testing.T) { }, "d699c5b5d3a6dfade601", }, + { + "lookup resources 2", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + }, computeBothHashes) + }, + "f49fbafef9c489abe601", + }, + { + "lookup resources 2 with zero limit", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalLimit: 0, + }, computeBothHashes) + }, + "f49fbafef9c489abe601", + }, + { + "lookup resources 2 with non-zero limit", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalLimit: 42, + }, computeBothHashes) + }, + "ea88adb1c1dfa6ebab01", + }, + { + "lookup resources 2 with nil context", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + Context: nil, + }, computeBothHashes) + }, + "f49fbafef9c489abe601", + }, + { + "lookup resources 2 with empty context", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + Context: func() *structpb.Struct { + v, _ := structpb.NewStruct(map[string]any{}) + return v + }(), + }, computeBothHashes) + }, + "f49fbafef9c489abe601", + }, + { + "lookup resources 2 with context", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + Context: func() *structpb.Struct { + v, _ := structpb.NewStruct(map[string]any{ + "foo": 1, + "bar": true, + }) + return v + }(), + }, computeBothHashes) + }, + "bbd78884eff9edbebd01", + }, + { + "lookup resources 2 with different context", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + Context: func() *structpb.Struct { + v, _ := structpb.NewStruct(map[string]any{ + "foo": 2, + "bar": true, + }) + return v + }(), + }, computeBothHashes) + }, + "94d0faa5feaaa4dc17", + }, + { + "lookup resources 2 with escaped string", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + Context: func() *structpb.Struct { + v, _ := structpb.NewStruct(map[string]any{ + "foo": "this is an `escaped` string\nhi", + }) + return v + }(), + }, computeBothHashes) + }, + "cdffc895cb9299b9da01", + }, + { + "lookup resources 2 with nested context", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + Context: func() *structpb.Struct { + v, _ := structpb.NewStruct(map[string]any{ + "foo": 1, + "bar": map[string]any{ + "meh": "hiya", + "baz": "yo", + }, + }) + return v + }(), + }, computeBothHashes) + }, + "84d8d38ff9fadbb36d", + }, + { + "lookup resources 2 with empty cursor", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalCursor: &v1.Cursor{}, + }, computeBothHashes) + }, + "f49fbafef9c489abe601", + }, + { + "lookup resources 2 with non-empty cursor", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalCursor: &v1.Cursor{ + Sections: []string{"foo"}, + }, + }, computeBothHashes) + }, + "f09894c0d9a0b8ff7f", + }, + { + "lookup resources 2 with different cursor", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalCursor: &v1.Cursor{ + Sections: []string{"foo", "bar"}, + }, + }, computeBothHashes) + }, + "badfd7c59cdbcef671", + }, + { + "lookup resources 2 with different terminal subject", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "sarah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalCursor: &v1.Cursor{ + Sections: []string{"foo", "bar"}, + }, + }, computeBothHashes) + }, + "8787b685e9cea993a901", + }, + { + "lookup resources 2 with different subject IDs", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah", "tom"}, + TerminalSubject: ONR("user", "sarah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalCursor: &v1.Cursor{ + Sections: []string{"foo", "bar"}, + }, + }, computeBothHashes) + }, + "f4d6d884eaa5e4b4ed01", + }, } for _, tc := range tcs { diff --git a/internal/dispatch/keys/keys.go b/internal/dispatch/keys/keys.go index fa384eb626..7c45846c92 100644 --- a/internal/dispatch/keys/keys.go +++ b/internal/dispatch/keys/keys.go @@ -16,6 +16,9 @@ type Handler interface { // LookupResourcesCacheKey computes the caching key for a LookupResources operation. LookupResourcesCacheKey(ctx context.Context, req *v1.DispatchLookupResourcesRequest) (DispatchCacheKey, error) + // LookupResources2CacheKey computes the caching key for a LookupResources2 operation. + LookupResources2CacheKey(ctx context.Context, req *v1.DispatchLookupResources2Request) (DispatchCacheKey, error) + // LookupSubjectsCacheKey computes the caching key for a LookupSubjects operation. LookupSubjectsCacheKey(ctx context.Context, req *v1.DispatchLookupSubjectsRequest) (DispatchCacheKey, error) @@ -31,6 +34,9 @@ type Handler interface { // LookupResourcesDispatchKey computes the dispatch key for a LookupResources operation. LookupResourcesDispatchKey(ctx context.Context, req *v1.DispatchLookupResourcesRequest) ([]byte, error) + // LookupResources2DispatchKey computes the dispatch key for a LookupResources2 operation. + LookupResources2DispatchKey(ctx context.Context, req *v1.DispatchLookupResources2Request) ([]byte, error) + // LookupSubjectsDispatchKey computes the key for a LookupSubjects operation. LookupSubjectsDispatchKey(ctx context.Context, req *v1.DispatchLookupSubjectsRequest) ([]byte, error) @@ -47,6 +53,10 @@ func (b baseKeyHandler) LookupResourcesCacheKey(_ context.Context, req *v1.Dispa return lookupResourcesRequestToKey(req, computeBothHashes), nil } +func (b baseKeyHandler) LookupResources2CacheKey(_ context.Context, req *v1.DispatchLookupResources2Request) (DispatchCacheKey, error) { + return lookupResourcesRequest2ToKey(req, computeBothHashes), nil +} + func (b baseKeyHandler) LookupSubjectsCacheKey(_ context.Context, req *v1.DispatchLookupSubjectsRequest) (DispatchCacheKey, error) { return lookupSubjectsRequestToKey(req, computeBothHashes), nil } @@ -67,6 +77,10 @@ func (b baseKeyHandler) LookupResourcesDispatchKey(_ context.Context, req *v1.Di return lookupResourcesRequestToKey(req, computeOnlyStableHash).StableSumAsBytes(), nil } +func (b baseKeyHandler) LookupResources2DispatchKey(_ context.Context, req *v1.DispatchLookupResources2Request) ([]byte, error) { + return lookupResourcesRequest2ToKey(req, computeOnlyStableHash).StableSumAsBytes(), nil +} + func (b baseKeyHandler) LookupSubjectsDispatchKey(_ context.Context, req *v1.DispatchLookupSubjectsRequest) ([]byte, error) { return lookupSubjectsRequestToKey(req, computeOnlyStableHash).StableSumAsBytes(), nil } diff --git a/internal/dispatch/remote/cluster.go b/internal/dispatch/remote/cluster.go index 5cee5f57c7..52305a1a11 100644 --- a/internal/dispatch/remote/cluster.go +++ b/internal/dispatch/remote/cluster.go @@ -38,6 +38,7 @@ type ClusterClient interface { DispatchExpand(ctx context.Context, req *v1.DispatchExpandRequest, opts ...grpc.CallOption) (*v1.DispatchExpandResponse, error) DispatchReachableResources(ctx context.Context, in *v1.DispatchReachableResourcesRequest, opts ...grpc.CallOption) (v1.DispatchService_DispatchReachableResourcesClient, error) DispatchLookupResources(ctx context.Context, in *v1.DispatchLookupResourcesRequest, opts ...grpc.CallOption) (v1.DispatchService_DispatchLookupResourcesClient, error) + DispatchLookupResources2(ctx context.Context, in *v1.DispatchLookupResources2Request, opts ...grpc.CallOption) (v1.DispatchService_DispatchLookupResources2Client, error) DispatchLookupSubjects(ctx context.Context, in *v1.DispatchLookupSubjectsRequest, opts ...grpc.CallOption) (v1.DispatchService_DispatchLookupSubjectsClient, error) } @@ -353,6 +354,56 @@ func (cr *clusterDispatcher) DispatchLookupResources( } } +func (cr *clusterDispatcher) DispatchLookupResources2( + req *v1.DispatchLookupResources2Request, + stream dispatch.LookupResources2Stream, +) error { + requestKey, err := cr.keyHandler.LookupResources2DispatchKey(stream.Context(), req) + if err != nil { + return err + } + + ctx := context.WithValue(stream.Context(), consistent.CtxKey, requestKey) + stream = dispatch.StreamWithContext(ctx, stream) + + if err := dispatch.CheckDepth(ctx, req); err != nil { + return err + } + + withTimeout, cancelFn := context.WithTimeout(ctx, cr.dispatchOverallTimeout) + defer cancelFn() + + client, err := cr.clusterClient.DispatchLookupResources2(withTimeout, req) + if err != nil { + return err + } + + for { + select { + case <-withTimeout.Done(): + return withTimeout.Err() + + default: + result, err := client.Recv() + if errors.Is(err, io.EOF) { + return nil + } else if err != nil { + return err + } + + merr := adjustMetadataForDispatch(result.Metadata) + if merr != nil { + return merr + } + + serr := stream.Publish(result) + if serr != nil { + return serr + } + } + } +} + func (cr *clusterDispatcher) DispatchLookupSubjects( req *v1.DispatchLookupSubjectsRequest, stream dispatch.LookupSubjectsStream, diff --git a/internal/dispatch/singleflight/singleflight.go b/internal/dispatch/singleflight/singleflight.go index 39fc8ea5c8..ad98dd31c7 100644 --- a/internal/dispatch/singleflight/singleflight.go +++ b/internal/dispatch/singleflight/singleflight.go @@ -143,6 +143,10 @@ func (d *Dispatcher) DispatchLookupResources(req *v1.DispatchLookupResourcesRequ return d.delegate.DispatchLookupResources(req, stream) } +func (d *Dispatcher) DispatchLookupResources2(req *v1.DispatchLookupResources2Request, stream dispatch.LookupResources2Stream) error { + return d.delegate.DispatchLookupResources2(req, stream) +} + func (d *Dispatcher) DispatchLookupSubjects(req *v1.DispatchLookupSubjectsRequest, stream dispatch.LookupSubjectsStream) error { return d.delegate.DispatchLookupSubjects(req, stream) } diff --git a/internal/dispatch/singleflight/singleflight_test.go b/internal/dispatch/singleflight/singleflight_test.go index 55f1e4095d..6612b42806 100644 --- a/internal/dispatch/singleflight/singleflight_test.go +++ b/internal/dispatch/singleflight/singleflight_test.go @@ -373,6 +373,10 @@ func (m mockDispatcher) DispatchLookupResources(_ *v1.DispatchLookupResourcesReq return nil } +func (m mockDispatcher) DispatchLookupResources2(_ *v1.DispatchLookupResources2Request, _ dispatch.LookupResources2Stream) error { + return nil +} + func (m mockDispatcher) DispatchLookupSubjects(_ *v1.DispatchLookupSubjectsRequest, _ dispatch.LookupSubjectsStream) error { return nil } diff --git a/internal/graph/check.go b/internal/graph/check.go index 7432512a8f..9ccd659402 100644 --- a/internal/graph/check.go +++ b/internal/graph/check.go @@ -13,6 +13,7 @@ import ( "google.golang.org/protobuf/types/known/durationpb" "github.com/authzed/spicedb/internal/dispatch" + "github.com/authzed/spicedb/internal/graph/hints" log "github.com/authzed/spicedb/internal/logging" datastoremw "github.com/authzed/spicedb/internal/middleware/datastore" "github.com/authzed/spicedb/internal/namespace" @@ -42,6 +43,8 @@ var directDispatchQueryHistogram = prometheus.NewHistogram(prometheus.HistogramO Buckets: []float64{1, 2}, }) +const noOriginalRelation = "" + func init() { prometheus.MustRegister(directDispatchQueryHistogram) prometheus.MustRegister(dispatchChunkCountHistogram) @@ -64,6 +67,10 @@ type ConcurrentChecker struct { type ValidatedCheckRequest struct { *v1.DispatchCheckRequest Revision datastore.Revision + + // OriginalRelationName is the original relation/permission name that was used in the request, + // before being changed due to aliasing. + OriginalRelationName string } // currentRequestContext holds context information for the current request being @@ -173,8 +180,28 @@ func (cc *ConcurrentChecker) checkInternal(ctx context.Context, req ValidatedChe return checkResultsForMembership(membershipSet, emptyMetadata) } + // Filter for check hints, if any. + if len(req.CheckHints) > 0 { + filteredResourcesIdsSet := mapz.NewSet(filteredResourcesIds...) + for _, checkHint := range req.CheckHints { + resourceID, ok := hints.AsCheckHintForComputedUserset(checkHint, req.ResourceRelation.Namespace, req.ResourceRelation.Relation, req.Subject) + if ok { + filteredResourcesIdsSet.Delete(resourceID) + continue + } + + if req.OriginalRelationName != "" { + resourceID, ok = hints.AsCheckHintForComputedUserset(checkHint, req.ResourceRelation.Namespace, req.OriginalRelationName, req.Subject) + if ok { + filteredResourcesIdsSet.Delete(resourceID) + } + } + } + filteredResourcesIds = filteredResourcesIdsSet.AsSlice() + } + if len(filteredResourcesIds) == 0 { - return noMembers() + return combineWithCheckHints(combineResultWithFoundResources(noMembers(), membershipSet), req) } // NOTE: We can always allow a single result if we're only trying to find the results for a @@ -196,10 +223,66 @@ func (cc *ConcurrentChecker) checkInternal(ctx context.Context, req ValidatedChe } if relation.UsersetRewrite == nil { - return combineResultWithFoundResources(cc.checkDirect(ctx, crc, relation), membershipSet) + return combineWithCheckHints(combineResultWithFoundResources(cc.checkDirect(ctx, crc, relation), membershipSet), req) + } + + return combineWithCheckHints(combineResultWithFoundResources(cc.checkUsersetRewrite(ctx, crc, relation.UsersetRewrite), membershipSet), req) +} + +func combineWithComputedHints(result CheckResult, hints map[string]*v1.ResourceCheckResult) CheckResult { + if len(hints) == 0 { + return result + } + + for resourceID, hint := range hints { + if _, ok := result.Resp.ResultsByResourceId[resourceID]; ok { + return checkResultError( + spiceerrors.MustBugf("check hint for resource ID %q, which already exists", resourceID), + emptyMetadata, + ) + } + + if result.Resp.ResultsByResourceId == nil { + result.Resp.ResultsByResourceId = make(map[string]*v1.ResourceCheckResult) + } + result.Resp.ResultsByResourceId[resourceID] = hint + } + + return result +} + +func combineWithCheckHints(result CheckResult, req ValidatedCheckRequest) CheckResult { + if len(req.CheckHints) == 0 { + return result + } + + for _, checkHint := range req.CheckHints { + resourceID, ok := hints.AsCheckHintForComputedUserset(checkHint, req.ResourceRelation.Namespace, req.ResourceRelation.Relation, req.Subject) + if !ok { + if req.OriginalRelationName != "" { + resourceID, ok = hints.AsCheckHintForComputedUserset(checkHint, req.ResourceRelation.Namespace, req.OriginalRelationName, req.Subject) + } + + if !ok { + continue + } + } + + if result.Resp.ResultsByResourceId == nil { + result.Resp.ResultsByResourceId = make(map[string]*v1.ResourceCheckResult) + } + + if _, ok := result.Resp.ResultsByResourceId[resourceID]; ok { + return checkResultError( + spiceerrors.MustBugf("check hint for resource ID %q, which already exists", resourceID), + emptyMetadata, + ) + } + + result.Resp.ResultsByResourceId[resourceID] = checkHint.Result } - return combineResultWithFoundResources(cc.checkUsersetRewrite(ctx, crc, relation.UsersetRewrite), membershipSet) + return result } type directDispatch struct { @@ -408,10 +491,12 @@ func (cc *ConcurrentChecker) checkDirect(ctx context.Context, crc currentRequest Subject: crc.parentReq.Subject, ResultsSetting: crc.resultsSetting, - Metadata: decrementDepth(crc.parentReq.Metadata), - Debug: crc.parentReq.Debug, + Metadata: decrementDepth(crc.parentReq.Metadata), + Debug: crc.parentReq.Debug, + CheckHints: crc.parentReq.CheckHints, }, crc.parentReq.Revision, + noOriginalRelation, }) if childResult.Err != nil { @@ -560,8 +645,10 @@ func (cc *ConcurrentChecker) checkComputedUserset(ctx context.Context, crc curre ResultsSetting: crc.resultsSetting, Metadata: decrementDepth(crc.parentReq.Metadata), Debug: crc.parentReq.Debug, + CheckHints: crc.parentReq.CheckHints, }, crc.parentReq.Revision, + noOriginalRelation, }) return combineResultWithFoundResources(result, membershipSet) } @@ -609,6 +696,7 @@ func checkIntersectionTupleToUserset( crc currentRequestContext, ttu *core.FunctionedTupleToUserset, ) CheckResult { + // TODO(jschorr): use check hints here ctx, span := tracer.Start(ctx, ttu.GetTupleset().GetRelation()+"-(all)->"+ttu.GetComputedUserset().Relation) defer span.End() @@ -767,6 +855,34 @@ func checkTupleToUserset[T relation]( crc currentRequestContext, ttu ttu[T], ) CheckResult { + filteredResourceIDs := crc.filteredResourceIDs + hintsToReturn := make(map[string]*v1.ResourceCheckResult, len(crc.parentReq.CheckHints)) + if len(crc.parentReq.CheckHints) > 0 { + filteredResourcesIdsSet := mapz.NewSet(crc.filteredResourceIDs...) + + for _, checkHint := range crc.parentReq.CheckHints { + resourceID, ok := hints.AsCheckHintForArrow( + checkHint, + crc.parentReq.ResourceRelation.Namespace, + ttu.GetTupleset().GetRelation(), + ttu.GetComputedUserset().Relation, + crc.parentReq.Subject, + ) + if !ok { + continue + } + + filteredResourcesIdsSet.Delete(resourceID) + hintsToReturn[resourceID] = checkHint.Result + } + + filteredResourceIDs = filteredResourcesIdsSet.AsSlice() + } + + if len(filteredResourceIDs) == 0 { + return combineWithComputedHints(noMembers(), hintsToReturn) + } + ctx, span := tracer.Start(ctx, ttu.GetTupleset().GetRelation()+"->"+ttu.GetComputedUserset().Relation) defer span.End() @@ -774,7 +890,7 @@ func checkTupleToUserset[T relation]( ds := datastoremw.MustFromContext(ctx).SnapshotReader(crc.parentReq.Revision) it, err := ds.QueryRelationships(ctx, datastore.RelationshipsFilter{ OptionalResourceType: crc.parentReq.ResourceRelation.Namespace, - OptionalResourceIds: crc.filteredResourceIDs, + OptionalResourceIds: filteredResourceIDs, OptionalResourceRelation: ttu.GetTupleset().GetRelation(), }) if err != nil { @@ -811,7 +927,7 @@ func checkTupleToUserset[T relation]( dispatchChunkCountHistogram.Observe(chunkCount) }) - return union( + return combineWithComputedHints(union( ctx, crc, toDispatch, @@ -824,7 +940,7 @@ func checkTupleToUserset[T relation]( return mapFoundResources(childResult, dd.resourceType, relationshipsBySubjectONR) }, cc.concurrencyLimit, - ) + ), hintsToReturn) } func withDistinctMetadata(result CheckResult) CheckResult { diff --git a/internal/graph/computed/computecheck.go b/internal/graph/computed/computecheck.go index 2f6c9cbebd..d5d921af89 100644 --- a/internal/graph/computed/computecheck.go +++ b/internal/graph/computed/computecheck.go @@ -46,6 +46,7 @@ type CheckParameters struct { AtRevision datastore.Revision MaximumDepth uint32 DebugOption DebugOption + CheckHints []*v1.CheckHint } // ComputeCheck computes a check result for the given resource and subject, computing any @@ -118,7 +119,8 @@ func computeCheck(ctx context.Context, DepthRemaining: params.MaximumDepth, TraversalBloom: bf, }, - Debug: debugging, + Debug: debugging, + CheckHints: params.CheckHints, }) if len(resourceIDs) == 1 { diff --git a/internal/graph/computed/computecheck_test.go b/internal/graph/computed/computecheck_test.go index 36623db76b..557cb96218 100644 --- a/internal/graph/computed/computecheck_test.go +++ b/internal/graph/computed/computecheck_test.go @@ -87,7 +87,7 @@ func TestComputeCheckWithCaveats(t *testing.T) { "document:foo#view@user:sarah", nil, v1.ResourceCheckResult_CAVEATED_MEMBER, - []string{"anothercondition"}, + []string{"anothercondition", "somecondition", "somebool"}, "", }, { @@ -96,7 +96,7 @@ func TestComputeCheckWithCaveats(t *testing.T) { "somecondition": "42", }, v1.ResourceCheckResult_CAVEATED_MEMBER, - []string{"anothercondition"}, + []string{"anothercondition", "somebool"}, "", }, { diff --git a/internal/graph/hints/checkhints.go b/internal/graph/hints/checkhints.go new file mode 100644 index 0000000000..a1e05a7e19 --- /dev/null +++ b/internal/graph/hints/checkhints.go @@ -0,0 +1,95 @@ +package hints + +import ( + 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/typesystem" +) + +// CheckHintForComputedUserset creates a CheckHint for a relation and a subject. +func CheckHintForComputedUserset(resourceType string, resourceID string, relation string, subject *core.ObjectAndRelation, result *v1.ResourceCheckResult) *v1.CheckHint { + return &v1.CheckHint{ + Resource: &core.ObjectAndRelation{ + Namespace: resourceType, + ObjectId: resourceID, + Relation: relation, + }, + Subject: subject, + Result: result, + } +} + +// CheckHintForArrow creates a CheckHint for an arrow and a subject. +func CheckHintForArrow(resourceType string, resourceID string, tuplesetRelation string, computedUsersetRelation string, subject *core.ObjectAndRelation, result *v1.ResourceCheckResult) *v1.CheckHint { + return &v1.CheckHint{ + Resource: &core.ObjectAndRelation{ + Namespace: resourceType, + ObjectId: resourceID, + Relation: tuplesetRelation, + }, + TtuComputedUsersetRelation: computedUsersetRelation, + Subject: subject, + Result: result, + } +} + +// AsCheckHintForComputedUserset returns the resourceID if the checkHint is for the given relation and subject. +func AsCheckHintForComputedUserset(checkHint *v1.CheckHint, resourceType string, relationName string, subject *core.ObjectAndRelation) (string, bool) { + if checkHint.TtuComputedUsersetRelation != "" { + return "", false + } + + if checkHint.Resource.Namespace == resourceType && checkHint.Resource.Relation == relationName && checkHint.Subject.EqualVT(subject) { + return checkHint.Resource.ObjectId, true + } + + return "", false +} + +// AsCheckHintForArrow returns the resourceID if the checkHint is for the given arrow and subject. +func AsCheckHintForArrow(checkHint *v1.CheckHint, resourceType string, tuplesetRelation string, computedUsersetRelation string, subject *core.ObjectAndRelation) (string, bool) { + if checkHint.TtuComputedUsersetRelation != computedUsersetRelation { + return "", false + } + + if checkHint.Resource.Namespace == resourceType && checkHint.Resource.Relation == tuplesetRelation && checkHint.Subject.EqualVT(subject) { + return checkHint.Resource.ObjectId, true + } + + return "", false +} + +// HintForEntrypoint returns a CheckHint for the given reachability graph entrypoint and associated subject and result. +func HintForEntrypoint(re typesystem.ReachabilityEntrypoint, resourceID string, subject *core.ObjectAndRelation, result *v1.ResourceCheckResult) (*v1.CheckHint, error) { + switch re.EntrypointKind() { + case core.ReachabilityEntrypoint_RELATION_ENTRYPOINT: + return nil, spiceerrors.MustBugf("cannot call CheckHintForResource for kind %v", re.EntrypointKind()) + + case core.ReachabilityEntrypoint_TUPLESET_TO_USERSET_ENTRYPOINT: + namespace := re.TargetNamespace() + tuplesetRelation, err := re.TuplesetRelation() + if err != nil { + return nil, err + } + + computedUsersetRelation, err := re.ComputedUsersetRelation() + if err != nil { + return nil, err + } + + return CheckHintForArrow(namespace, resourceID, tuplesetRelation, computedUsersetRelation, subject, result), nil + + case core.ReachabilityEntrypoint_COMPUTED_USERSET_ENTRYPOINT: + namespace := re.TargetNamespace() + relation, err := re.ComputedUsersetRelation() + if err != nil { + return nil, err + } + + return CheckHintForComputedUserset(namespace, resourceID, relation, subject, result), nil + + default: + return nil, spiceerrors.MustBugf("unknown relation entrypoint kind") + } +} diff --git a/internal/graph/hints/checkhints_test.go b/internal/graph/hints/checkhints_test.go new file mode 100644 index 0000000000..5b9ba12676 --- /dev/null +++ b/internal/graph/hints/checkhints_test.go @@ -0,0 +1,362 @@ +package hints + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/authzed/spicedb/internal/datastore/memdb" + datastoremw "github.com/authzed/spicedb/internal/middleware/datastore" + "github.com/authzed/spicedb/pkg/datastore" + core "github.com/authzed/spicedb/pkg/proto/core/v1" + v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" + "github.com/authzed/spicedb/pkg/schemadsl/compiler" + "github.com/authzed/spicedb/pkg/schemadsl/input" + "github.com/authzed/spicedb/pkg/tuple" + "github.com/authzed/spicedb/pkg/typesystem" +) + +func TestHintForEntrypoint(t *testing.T) { + tcs := []struct { + name string + schema string + subjectType string + subjectRelation string + expectedHints []*v1.CheckHint + }{ + { + "computed userset entrypoint", + ` + definition org { + relation member: user + permission is_member = member + } + + definition resource { + relation org: org + permission view = org->is_member + }`, + "org", + "member", + []*v1.CheckHint{ + CheckHintForComputedUserset("org", "someid", "member", tuple.ParseSubjectONR("user:tom"), &v1.ResourceCheckResult{}), + }, + }, + { + "arrow entrypoint", + ` + definition org { + relation member: user + permission is_member = member + } + + definition resource { + relation org: org + permission view = org->is_member + }`, + "org", + "is_member", + []*v1.CheckHint{ + CheckHintForArrow("resource", "someid", "org", "is_member", tuple.ParseSubjectONR("user:tom"), &v1.ResourceCheckResult{}), + }, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + rg := buildReachabilityGraph(t, tc.schema) + subject := tuple.ParseSubjectONR("user:tom") + + entrypoints, err := rg.OptimizedEntrypointsForSubjectToResource(context.Background(), &core.RelationReference{ + Namespace: tc.subjectType, + Relation: tc.subjectRelation, + }, &core.RelationReference{ + Namespace: "resource", + Relation: "view", + }) + require.NoError(t, err) + + hints := make([]*v1.CheckHint, 0, len(entrypoints)) + for _, ep := range entrypoints { + if ep.EntrypointKind() == core.ReachabilityEntrypoint_RELATION_ENTRYPOINT { + continue + } + + hint, err := HintForEntrypoint(ep, "someid", subject, &v1.ResourceCheckResult{}) + require.NoError(t, err) + + hints = append(hints, hint) + } + + require.Equal(t, tc.expectedHints, hints) + }) + } +} + +func buildReachabilityGraph(t *testing.T, schema string) *typesystem.ReachabilityGraph { + require := require.New(t) + + ds, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ctx := datastoremw.ContextWithDatastore(context.Background(), ds) + + compiled, err := compiler.Compile(compiler.InputSchema{ + Source: input.Source("schema"), + SchemaString: schema, + }, compiler.AllowUnprefixedObjectType()) + require.NoError(err) + + // Write the schema. + _, err = ds.ReadWriteTx(context.Background(), func(ctx context.Context, tx datastore.ReadWriteTransaction) error { + for _, nsDef := range compiled.ObjectDefinitions { + if err := tx.WriteNamespaces(ctx, nsDef); err != nil { + return err + } + } + + return nil + }) + require.NoError(err) + + lastRevision, err := ds.HeadRevision(context.Background()) + require.NoError(err) + + reader := ds.SnapshotReader(lastRevision) + + _, vts, err := typesystem.ReadNamespaceAndTypes(ctx, "resource", reader) + require.NoError(err) + + rg := typesystem.ReachabilityGraphFor(vts) + return rg +} + +func TestCheckHintForComputedUserset(t *testing.T) { + resourceType := "resourceType" + resourceID := "resourceID" + relation := "relation" + subject := &core.ObjectAndRelation{ + Namespace: "subjectNamespace", + ObjectId: "subjectObjectId", + Relation: "subjectRelation", + } + result := &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + } + + checkHint := CheckHintForComputedUserset(resourceType, resourceID, relation, subject, result) + + require.Equal(t, resourceType, checkHint.Resource.Namespace) + require.Equal(t, resourceID, checkHint.Resource.ObjectId) + require.Equal(t, relation, checkHint.Resource.Relation) + require.Equal(t, subject, checkHint.Subject) + require.Equal(t, result, checkHint.Result) + require.Empty(t, checkHint.TtuComputedUsersetRelation) + + resourceID, ok := AsCheckHintForComputedUserset(checkHint, resourceType, relation, subject) + require.True(t, ok) + require.Equal(t, "resourceID", resourceID) +} + +func TestCheckHintForArrow(t *testing.T) { + resourceType := "resourceType" + resourceID := "resourceID" + tuplesetRelation := "tuplesetRelation" + computedUsersetRelation := "computedUsersetRelation" + subject := &core.ObjectAndRelation{ + Namespace: "subjectNamespace", + ObjectId: "subjectObjectId", + Relation: "subjectRelation", + } + result := &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + } + + checkHint := CheckHintForArrow(resourceType, resourceID, tuplesetRelation, computedUsersetRelation, subject, result) + + require.Equal(t, resourceType, checkHint.Resource.Namespace) + require.Equal(t, resourceID, checkHint.Resource.ObjectId) + require.Equal(t, tuplesetRelation, checkHint.Resource.Relation) + require.Equal(t, subject, checkHint.Subject) + require.Equal(t, result, checkHint.Result) + require.Equal(t, computedUsersetRelation, checkHint.TtuComputedUsersetRelation) + + resourceID, ok := AsCheckHintForArrow(checkHint, resourceType, tuplesetRelation, computedUsersetRelation, subject) + require.True(t, ok) + require.Equal(t, "resourceID", resourceID) +} + +func TestAsCheckHintForComputedUserset(t *testing.T) { + tcs := []struct { + name string + checkHint *v1.CheckHint + handler func(*v1.CheckHint) (string, bool) + expectedResult string + }{ + { + "matching resource and subject", + CheckHintForComputedUserset("resourceType", "resourceID", "relation", tuple.ParseSubjectONR("user:tom"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForComputedUserset(ch, "resourceType", "relation", tuple.ParseSubjectONR("user:tom")) + }, + "resourceID", + }, + { + "mismatch subject ID", + CheckHintForComputedUserset("resourceType", "resourceID", "relation", tuple.ParseSubjectONR("user:anothersubject"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForComputedUserset(ch, "resourceType", "relation", tuple.ParseSubjectONR("user:tom")) + }, + "", + }, + { + "mismatch subject type", + CheckHintForComputedUserset("resourceType", "resourceID", "relation", tuple.ParseSubjectONR("githubuser:tom"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForComputedUserset(ch, "resourceType", "relation", tuple.ParseSubjectONR("user:tom")) + }, + "", + }, + { + "mismatch subject relation", + CheckHintForComputedUserset("resourceType", "resourceID", "relation", tuple.ParseSubjectONR("user:tom#foo"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForComputedUserset(ch, "resourceType", "relation", tuple.ParseSubjectONR("user:tom")) + }, + "", + }, + { + "mismatch resource type", + CheckHintForComputedUserset("anotherType", "resourceID", "relation", tuple.ParseSubjectONR("user:tom"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForComputedUserset(ch, "resourceType", "relation", tuple.ParseSubjectONR("user:tom")) + }, + "", + }, + { + "mismatch resource relation", + CheckHintForComputedUserset("resourceType", "resourceID", "anotherRelation", tuple.ParseSubjectONR("user:tom"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForComputedUserset(ch, "resourceType", "relation", tuple.ParseSubjectONR("user:tom#...")) + }, + "", + }, + { + "mismatch kind", + CheckHintForArrow("resourceType", "resourceID", "ttu", "clu", tuple.ParseSubjectONR("user:tom"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForComputedUserset(ch, "resourceType", "relation", tuple.ParseSubjectONR("user:tom#...")) + }, + "", + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + resourceID, ok := tc.handler(tc.checkHint) + if tc.expectedResult == "" { + require.False(t, ok) + return + } + + require.Equal(t, tc.expectedResult, resourceID) + require.True(t, ok) + }) + } +} + +func TestAsCheckHintForArrow(t *testing.T) { + tcs := []struct { + name string + checkHint *v1.CheckHint + handler func(*v1.CheckHint) (string, bool) + expectedResult string + }{ + { + "matching resource and subject", + CheckHintForArrow("resourceType", "resourceID", "ttu", "cur", tuple.ParseSubjectONR("user:tom"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForArrow(ch, "resourceType", "ttu", "cur", tuple.ParseSubjectONR("user:tom")) + }, + "resourceID", + }, + { + "mismatch TTU", + CheckHintForArrow("resourceType", "resourceID", "anotherttu", "cur", tuple.ParseSubjectONR("user:tom"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForArrow(ch, "resourceType", "ttu", "cur", tuple.ParseSubjectONR("user:tom")) + }, + "", + }, + { + "mismatch computeduserset", + CheckHintForArrow("resourceType", "resourceID", "ttu", "anothercur", tuple.ParseSubjectONR("user:tom"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForArrow(ch, "resourceType", "ttu", "cur", tuple.ParseSubjectONR("user:tom")) + }, + "", + }, + { + "mismatch subject ID", + CheckHintForArrow("resourceType", "resourceID", "ttu", "cur", tuple.ParseSubjectONR("user:anothersubject"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForArrow(ch, "resourceType", "ttu", "cur", tuple.ParseSubjectONR("user:tom")) + }, + "", + }, + { + "mismatch subject type", + CheckHintForArrow("resourceType", "resourceID", "ttu", "cur", tuple.ParseSubjectONR("githubuser:tom"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForArrow(ch, "resourceType", "ttu", "cur", tuple.ParseSubjectONR("user:tom")) + }, + "", + }, + { + "mismatch subject relation", + CheckHintForArrow("resourceType", "resourceID", "ttu", "cur", tuple.ParseSubjectONR("user:tom#something"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForArrow(ch, "resourceType", "ttu", "cur", tuple.ParseSubjectONR("user:tom")) + }, + "", + }, + { + "mismatch resource type", + CheckHintForArrow("anotherType", "resourceID", "ttu", "cur", tuple.ParseSubjectONR("user:tom"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForArrow(ch, "resourceType", "ttu", "cur", tuple.ParseSubjectONR("user:tom")) + }, + "", + }, + { + "mismatch resource relation", + CheckHintForArrow("resourceType", "resourceID", "anotherttu", "cur", tuple.ParseSubjectONR("user:tom"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForArrow(ch, "resourceType", "ttu", "cur", tuple.ParseSubjectONR("user:tom")) + }, + "", + }, + { + "mismatch kind", + CheckHintForComputedUserset("resourceType", "resourceID", "relation", tuple.ParseSubjectONR("user:tom"), &v1.ResourceCheckResult{}), + func(ch *v1.CheckHint) (string, bool) { + return AsCheckHintForArrow(ch, "resourceType", "ttu", "cur", tuple.ParseSubjectONR("user:tom")) + }, + "", + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + resourceID, ok := tc.handler(tc.checkHint) + if tc.expectedResult == "" { + require.False(t, ok) + return + } + + require.Equal(t, tc.expectedResult, resourceID) + require.True(t, ok) + }) + } +} diff --git a/internal/graph/lookupresources2.go b/internal/graph/lookupresources2.go new file mode 100644 index 0000000000..bed84e512e --- /dev/null +++ b/internal/graph/lookupresources2.go @@ -0,0 +1,632 @@ +package graph + +import ( + "context" + "slices" + "sort" + + "github.com/authzed/spicedb/internal/caveats" + "github.com/authzed/spicedb/internal/dispatch" + "github.com/authzed/spicedb/internal/graph/computed" + "github.com/authzed/spicedb/internal/graph/hints" + datastoremw "github.com/authzed/spicedb/internal/middleware/datastore" + "github.com/authzed/spicedb/pkg/datastore" + "github.com/authzed/spicedb/pkg/datastore/options" + "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/spiceerrors" + "github.com/authzed/spicedb/pkg/tuple" + "github.com/authzed/spicedb/pkg/typesystem" +) + +func NewCursoredLookupResources2(dl dispatch.LookupResources2, dc dispatch.Check, concurrencyLimit uint16) *CursoredLookupResources2 { + return &CursoredLookupResources2{dl, dc, concurrencyLimit} +} + +type CursoredLookupResources2 struct { + dl dispatch.LookupResources2 + dc dispatch.Check + concurrencyLimit uint16 +} + +type ValidatedLookupResources2Request struct { + *v1.DispatchLookupResources2Request + Revision datastore.Revision +} + +func (crr *CursoredLookupResources2) LookupResources2( + req ValidatedLookupResources2Request, + stream dispatch.LookupResources2Stream, +) error { + if req.TerminalSubject == nil { + return spiceerrors.MustBugf("no terminal subject given to lookup resources dispatch") + } + + if slices.Contains(req.SubjectIds, tuple.PublicWildcard) { + return NewWildcardNotAllowedErr("cannot perform lookup resources on wildcard", "subject_id") + } + + if len(req.SubjectIds) == 0 { + return spiceerrors.MustBugf("no subjects ids given to lookup resources dispatch") + } + + // Sort for stability. + if len(req.SubjectIds) > 1 { + sort.Strings(req.SubjectIds) + } + + ctx := stream.Context() + limits := newLimitTracker(req.OptionalLimit) + ci, err := newCursorInformation(req.OptionalCursor, limits, dispatchVersion) + if err != nil { + return err + } + + return withSubsetInCursor(ci, + func(currentOffset int, nextCursorWith afterResponseCursor) error { + // If the resource type matches the subject type, yield directly as a one-to-one result + // for each subjectID. + if req.SubjectRelation.Namespace == req.ResourceRelation.Namespace && + req.SubjectRelation.Relation == req.ResourceRelation.Relation { + for index, subjectID := range req.SubjectIds { + if index < currentOffset { + continue + } + + if !ci.limits.prepareForPublishing() { + return nil + } + + err := stream.Publish(&v1.DispatchLookupResources2Response{ + Resource: &v1.PossibleResource{ + ResourceId: subjectID, + ForSubjectIds: []string{subjectID}, + }, + Metadata: emptyMetadata, + AfterResponseCursor: nextCursorWith(index + 1), + }) + if err != nil { + return err + } + } + } + return nil + }, func(ci cursorInformation) error { + // Once done checking for the matching subject type, yield by dispatching over entrypoints. + return crr.afterSameType(ctx, ci, req, stream) + }) +} + +func (crr *CursoredLookupResources2) afterSameType( + ctx context.Context, + ci cursorInformation, + req ValidatedLookupResources2Request, + parentStream dispatch.LookupResources2Stream, +) error { + dispatched := &syncONRSet{} + + // Load the type system and reachability graph to find the entrypoints for the reachability. + ds := datastoremw.MustFromContext(ctx) + reader := ds.SnapshotReader(req.Revision) + _, typeSystem, err := typesystem.ReadNamespaceAndTypes(ctx, req.ResourceRelation.Namespace, reader) + if err != nil { + return err + } + + rg := typesystem.ReachabilityGraphFor(typeSystem) + entrypoints, err := rg.OptimizedEntrypointsForSubjectToResource(ctx, &core.RelationReference{ + Namespace: req.SubjectRelation.Namespace, + Relation: req.SubjectRelation.Relation, + }, req.ResourceRelation) + if err != nil { + return err + } + + // For each entrypoint, load the necessary data and re-dispatch if a subproblem was found. + return withParallelizedStreamingIterableInCursor(ctx, ci, entrypoints, parentStream, crr.concurrencyLimit, + func(ctx context.Context, ci cursorInformation, entrypoint typesystem.ReachabilityEntrypoint, stream dispatch.LookupResources2Stream) error { + switch entrypoint.EntrypointKind() { + case core.ReachabilityEntrypoint_RELATION_ENTRYPOINT: + return crr.lookupRelationEntrypoint(ctx, ci, entrypoint, rg, reader, req, stream, dispatched) + + case core.ReachabilityEntrypoint_COMPUTED_USERSET_ENTRYPOINT: + containingRelation := entrypoint.ContainingRelationOrPermission() + rewrittenSubjectRelation := &core.RelationReference{ + Namespace: containingRelation.Namespace, + Relation: containingRelation.Relation, + } + + rsm := subjectIDsToResourcesMap2(rewrittenSubjectRelation, req.SubjectIds) + drsm := rsm.asReadOnly() + + return crr.redispatchOrReport( + ctx, + ci, + rewrittenSubjectRelation, + drsm, + rg, + entrypoint, + stream, + req, + dispatched, + ) + + case core.ReachabilityEntrypoint_TUPLESET_TO_USERSET_ENTRYPOINT: + return crr.lookupTTUEntrypoint(ctx, ci, entrypoint, rg, reader, req, stream, dispatched) + + default: + return spiceerrors.MustBugf("Unknown kind of entrypoint: %v", entrypoint.EntrypointKind()) + } + }) +} + +func (crr *CursoredLookupResources2) lookupRelationEntrypoint( + ctx context.Context, + ci cursorInformation, + entrypoint typesystem.ReachabilityEntrypoint, + rg *typesystem.ReachabilityGraph, + reader datastore.Reader, + req ValidatedLookupResources2Request, + stream dispatch.LookupResources2Stream, + dispatched *syncONRSet, +) error { + relationReference, err := entrypoint.DirectRelation() + if err != nil { + return err + } + + _, relTypeSystem, err := typesystem.ReadNamespaceAndTypes(ctx, relationReference.Namespace, reader) + if err != nil { + return err + } + + // Build the list of subjects to lookup based on the type information available. + isDirectAllowed, err := relTypeSystem.IsAllowedDirectRelation( + relationReference.Relation, + req.SubjectRelation.Namespace, + req.SubjectRelation.Relation, + ) + if err != nil { + return err + } + + subjectIds := make([]string, 0, len(req.SubjectIds)+1) + if isDirectAllowed == typesystem.DirectRelationValid { + subjectIds = append(subjectIds, req.SubjectIds...) + } + + if req.SubjectRelation.Relation == tuple.Ellipsis { + isWildcardAllowed, err := relTypeSystem.IsAllowedPublicNamespace(relationReference.Relation, req.SubjectRelation.Namespace) + if err != nil { + return err + } + + if isWildcardAllowed == typesystem.PublicSubjectAllowed { + subjectIds = append(subjectIds, "*") + } + } + + // Lookup the subjects and then redispatch/report results. + relationFilter := datastore.SubjectRelationFilter{ + NonEllipsisRelation: req.SubjectRelation.Relation, + } + + if req.SubjectRelation.Relation == tuple.Ellipsis { + relationFilter = datastore.SubjectRelationFilter{ + IncludeEllipsisRelation: true, + } + } + + subjectsFilter := datastore.SubjectsFilter{ + SubjectType: req.SubjectRelation.Namespace, + OptionalSubjectIds: subjectIds, + RelationFilter: relationFilter, + } + + return crr.redispatchOrReportOverDatabaseQuery( + ctx, + redispatchOverDatabaseConfig2{ + ci: ci, + reader: reader, + subjectsFilter: subjectsFilter, + sourceResourceType: relationReference, + foundResourceType: relationReference, + entrypoint: entrypoint, + rg: rg, + concurrencyLimit: crr.concurrencyLimit, + parentStream: stream, + parentRequest: req, + dispatched: dispatched, + }, + ) +} + +type redispatchOverDatabaseConfig2 struct { + ci cursorInformation + + reader datastore.Reader + + subjectsFilter datastore.SubjectsFilter + sourceResourceType *core.RelationReference + foundResourceType *core.RelationReference + + entrypoint typesystem.ReachabilityEntrypoint + rg *typesystem.ReachabilityGraph + + concurrencyLimit uint16 + parentStream dispatch.LookupResources2Stream + parentRequest ValidatedLookupResources2Request + dispatched *syncONRSet +} + +func (crr *CursoredLookupResources2) redispatchOrReportOverDatabaseQuery( + ctx context.Context, + config redispatchOverDatabaseConfig2, +) error { + return withDatastoreCursorInCursor(ctx, config.ci, config.parentStream, config.concurrencyLimit, + // Find the target resources for the subject. + func(queryCursor options.Cursor) ([]itemAndPostCursor[dispatchableResourcesSubjectMap2], error) { + it, err := config.reader.ReverseQueryRelationships( + ctx, + config.subjectsFilter, + options.WithResRelation(&options.ResourceRelation{ + Namespace: config.sourceResourceType.Namespace, + Relation: config.sourceResourceType.Relation, + }), + options.WithSortForReverse(options.BySubject), + options.WithAfterForReverse(queryCursor), + ) + if err != nil { + return nil, err + } + defer it.Close() + + // Chunk based on the FilterMaximumIDCount, to ensure we never send more than that amount of + // results to a downstream dispatch. + rsm := newResourcesSubjectMap2WithCapacity(config.sourceResourceType, uint32(datastore.FilterMaximumIDCount)) + toBeHandled := make([]itemAndPostCursor[dispatchableResourcesSubjectMap2], 0) + currentCursor := queryCursor + + for tpl := it.Next(); tpl != nil; tpl = it.Next() { + if it.Err() != nil { + return nil, it.Err() + } + + var missingContextParameters []string + + // If a caveat exists on the relationship, run it and filter the results, marking those that have missing context. + if tpl.Caveat != nil && tpl.Caveat.CaveatName != "" { + caveatExpr := caveats.CaveatAsExpr(tpl.Caveat) + runResult, err := caveats.RunCaveatExpression(ctx, caveatExpr, config.parentRequest.Context.AsMap(), config.reader, caveats.RunCaveatExpressionNoDebugging) + if err != nil { + return nil, err + } + + // If a partial result is returned, collect the missing context parameters. + if runResult.IsPartial() { + missingNames, err := runResult.MissingVarNames() + if err != nil { + return nil, err + } + + missingContextParameters = missingNames + } else if !runResult.Value() { + // If the run result shows the caveat does not apply, skip. This shears the tree of results early. + continue + } + } + + if err := rsm.addRelationship(tpl, missingContextParameters); err != nil { + return nil, err + } + + if rsm.len() == int(datastore.FilterMaximumIDCount) { + toBeHandled = append(toBeHandled, itemAndPostCursor[dispatchableResourcesSubjectMap2]{ + item: rsm.asReadOnly(), + cursor: currentCursor, + }) + rsm = newResourcesSubjectMap2WithCapacity(config.sourceResourceType, uint32(datastore.FilterMaximumIDCount)) + currentCursor = tpl + } + } + it.Close() + + if rsm.len() > 0 { + toBeHandled = append(toBeHandled, itemAndPostCursor[dispatchableResourcesSubjectMap2]{ + item: rsm.asReadOnly(), + cursor: currentCursor, + }) + } + + return toBeHandled, nil + }, + + // Redispatch or report the results. + func( + ctx context.Context, + ci cursorInformation, + drsm dispatchableResourcesSubjectMap2, + currentStream dispatch.LookupResources2Stream, + ) error { + return crr.redispatchOrReport( + ctx, + ci, + config.foundResourceType, + drsm, + config.rg, + config.entrypoint, + currentStream, + config.parentRequest, + config.dispatched, + ) + }, + ) +} + +func (crr *CursoredLookupResources2) lookupTTUEntrypoint(ctx context.Context, + ci cursorInformation, + entrypoint typesystem.ReachabilityEntrypoint, + rg *typesystem.ReachabilityGraph, + reader datastore.Reader, + req ValidatedLookupResources2Request, + stream dispatch.LookupResources2Stream, + dispatched *syncONRSet, +) error { + containingRelation := entrypoint.ContainingRelationOrPermission() + + _, ttuTypeSystem, err := typesystem.ReadNamespaceAndTypes(ctx, containingRelation.Namespace, reader) + if err != nil { + return err + } + + tuplesetRelation, err := entrypoint.TuplesetRelation() + if err != nil { + return err + } + + // Determine whether this TTU should be followed, which will be the case if the subject relation's namespace + // is allowed in any form on the relation; since arrows ignore the subject's relation (if any), we check + // for the subject namespace as a whole. + allowedRelations, err := ttuTypeSystem.GetAllowedDirectNamespaceSubjectRelations(tuplesetRelation, req.SubjectRelation.Namespace) + if err != nil { + return err + } + + if allowedRelations == nil { + return nil + } + + // Search for the resolved subjects in the tupleset of the TTU. + subjectsFilter := datastore.SubjectsFilter{ + SubjectType: req.SubjectRelation.Namespace, + OptionalSubjectIds: req.SubjectIds, + } + + // Optimization: if there is a single allowed relation, pass it as a subject relation filter to make things faster + // on querying. + if allowedRelations.Len() == 1 { + allowedRelationName := allowedRelations.AsSlice()[0] + subjectsFilter.RelationFilter = datastore.SubjectRelationFilter{}.WithRelation(allowedRelationName) + } + + tuplesetRelationReference := &core.RelationReference{ + Namespace: containingRelation.Namespace, + Relation: tuplesetRelation, + } + + return crr.redispatchOrReportOverDatabaseQuery( + ctx, + redispatchOverDatabaseConfig2{ + ci: ci, + reader: reader, + subjectsFilter: subjectsFilter, + sourceResourceType: tuplesetRelationReference, + foundResourceType: containingRelation, + entrypoint: entrypoint, + rg: rg, + parentStream: stream, + parentRequest: req, + dispatched: dispatched, + }, + ) +} + +type possibleResourceAndIndex struct { + resource *v1.PossibleResource + index int +} + +// redispatchOrReport checks if further redispatching is necessary for the found resource +// type. If not, and the found resource type+relation matches the target resource type+relation, +// the resource is reported to the parent stream. +func (crr *CursoredLookupResources2) redispatchOrReport( + ctx context.Context, + ci cursorInformation, + foundResourceType *core.RelationReference, + foundResources dispatchableResourcesSubjectMap2, + rg *typesystem.ReachabilityGraph, + entrypoint typesystem.ReachabilityEntrypoint, + parentStream dispatch.LookupResources2Stream, + parentRequest ValidatedLookupResources2Request, + dispatched *syncONRSet, +) error { + if foundResources.isEmpty() { + // Nothing more to do. + return nil + } + + // Check for entrypoints for the new found resource type. + hasResourceEntrypoints, err := rg.HasOptimizedEntrypointsForSubjectToResource(ctx, foundResourceType, parentRequest.ResourceRelation) + if err != nil { + return err + } + + return withSubsetInCursor(ci, + func(currentOffset int, nextCursorWith afterResponseCursor) error { + if !hasResourceEntrypoints { + // If the found resource matches the target resource type and relation, potentially yield the resource. + if foundResourceType.Namespace == parentRequest.ResourceRelation.Namespace && foundResourceType.Relation == parentRequest.ResourceRelation.Relation { + resources := foundResources.asPossibleResources() + if len(resources) == 0 { + return nil + } + + if currentOffset >= len(resources) { + return nil + } + + offsetted := resources[currentOffset:] + if len(offsetted) == 0 { + return nil + } + + filtered := make([]possibleResourceAndIndex, 0, len(offsetted)) + for index, resource := range offsetted { + filtered = append(filtered, possibleResourceAndIndex{ + resource: resource, + index: index, + }) + } + + metadata := emptyMetadata + + // If the entrypoint is not a direct result, issue a check to further filter the results on the intersection or exclusion. + if !entrypoint.IsDirectResult() { + resourceIDs := make([]string, 0, len(offsetted)) + checkHints := make([]*v1.CheckHint, 0, len(offsetted)) + for _, resource := range offsetted { + resourceIDs = append(resourceIDs, resource.ResourceId) + + checkHint, err := hints.HintForEntrypoint( + entrypoint, + resource.ResourceId, + parentRequest.TerminalSubject, + &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + }) + if err != nil { + return err + } + checkHints = append(checkHints, checkHint) + } + + resultsByResourceID, checkMetadata, err := computed.ComputeBulkCheck(ctx, crr.dc, computed.CheckParameters{ + ResourceType: parentRequest.ResourceRelation, + Subject: parentRequest.TerminalSubject, + CaveatContext: parentRequest.Context.AsMap(), + AtRevision: parentRequest.Revision, + MaximumDepth: parentRequest.Metadata.DepthRemaining - 1, + DebugOption: computed.NoDebugging, + CheckHints: checkHints, + }, resourceIDs) + if err != nil { + return err + } + + metadata = addCallToResponseMetadata(checkMetadata) + + filtered = make([]possibleResourceAndIndex, 0, len(offsetted)) + for index, resource := range offsetted { + result, ok := resultsByResourceID[resource.ResourceId] + if !ok { + continue + } + + switch result.Membership { + case v1.ResourceCheckResult_MEMBER: + filtered = append(filtered, possibleResourceAndIndex{ + resource: resource, + index: index, + }) + + case v1.ResourceCheckResult_CAVEATED_MEMBER: + missingContextParams := mapz.NewSet(result.MissingExprFields...) + missingContextParams.Extend(resource.MissingContextParams) + + filtered = append(filtered, possibleResourceAndIndex{ + resource: &v1.PossibleResource{ + ResourceId: resource.ResourceId, + ForSubjectIds: resource.ForSubjectIds, + MissingContextParams: missingContextParams.AsSlice(), + }, + index: index, + }) + + case v1.ResourceCheckResult_NOT_MEMBER: + // Skip. + + default: + return spiceerrors.MustBugf("unexpected result from check: %v", result.Membership) + } + } + } + + for _, resourceAndIndex := range filtered { + if !ci.limits.prepareForPublishing() { + return nil + } + + err := parentStream.Publish(&v1.DispatchLookupResources2Response{ + Resource: resourceAndIndex.resource, + Metadata: metadata, + AfterResponseCursor: nextCursorWith(currentOffset + resourceAndIndex.index + 1), + }) + if err != nil { + return err + } + + metadata = emptyMetadata + } + return nil + } + } + return nil + }, func(ci cursorInformation) error { + if !hasResourceEntrypoints { + return nil + } + + // The new subject type for dispatching was the found type of the *resource*. + newSubjectType := foundResourceType + + // To avoid duplicate work, remove any subjects already dispatched. + filteredSubjectIDs := foundResources.filterSubjectIDsToDispatch(dispatched, newSubjectType) + if len(filteredSubjectIDs) == 0 { + return nil + } + + // If the entrypoint is a direct result then we can simply dispatch directly and map + // all found results, as no further filtering will be needed. + if entrypoint.IsDirectResult() { + stream := unfilteredLookupResourcesDispatchStreamForEntrypoint(ctx, foundResources, parentStream, ci) + return crr.dl.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: parentRequest.ResourceRelation, + SubjectRelation: newSubjectType, + SubjectIds: filteredSubjectIDs, + TerminalSubject: parentRequest.TerminalSubject, + Metadata: &v1.ResolverMeta{ + AtRevision: parentRequest.Revision.String(), + DepthRemaining: parentRequest.Metadata.DepthRemaining - 1, + }, + OptionalCursor: ci.currentCursor, + OptionalLimit: parentRequest.OptionalLimit, + }, stream) + } + + // Otherwise, we need to dispatch and filter results by batch checking along the way. + return runDispatchAndChecker( + ctx, + parentRequest, + foundResources, + ci, + parentStream, + newSubjectType, + filteredSubjectIDs, + entrypoint, + crr.dl, + crr.dc, + crr.concurrencyLimit, + ) + }) +} diff --git a/internal/graph/lr2streams.go b/internal/graph/lr2streams.go new file mode 100644 index 0000000000..7d50dbf038 --- /dev/null +++ b/internal/graph/lr2streams.go @@ -0,0 +1,290 @@ +package graph + +import ( + "context" + "sync" + + "github.com/authzed/spicedb/internal/dispatch" + "github.com/authzed/spicedb/internal/graph/computed" + "github.com/authzed/spicedb/internal/graph/hints" + "github.com/authzed/spicedb/internal/taskrunner" + "github.com/authzed/spicedb/pkg/datastore" + "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/spiceerrors" + "github.com/authzed/spicedb/pkg/typesystem" +) + +// runDispatchAndChecker runs the dispatch and checker for a lookup resources call, and publishes +// the results to the parent stream. This function is responsible for handling the dispatching +// of the lookup resources call, and then checking the results to filter them. +func runDispatchAndChecker( + ctx context.Context, + parentReq ValidatedLookupResources2Request, + foundResources dispatchableResourcesSubjectMap2, + ci cursorInformation, + parentStream dispatch.LookupResources2Stream, + newSubjectType *core.RelationReference, + filteredSubjectIDs []string, + entrypoint typesystem.ReachabilityEntrypoint, + lrDispatcher dispatch.LookupResources2, + checkDispatcher dispatch.Check, + concurrencyLimit uint16, +) error { + // Only allow max one dispatcher and one checker to run concurrently. + concurrencyLimit = min(concurrencyLimit, 2) + + rdc := &dispatchAndCheckRunner{ + parentRequest: parentReq, + foundResources: foundResources, + ci: ci, + parentStream: parentStream, + newSubjectType: newSubjectType, + filteredSubjectIDs: filteredSubjectIDs, + entrypoint: entrypoint, + lrDispatcher: lrDispatcher, + checkDispatcher: checkDispatcher, + taskrunner: taskrunner.NewTaskRunner(ctx, concurrencyLimit), + lock: &sync.Mutex{}, + } + + return rdc.runAndWait() +} + +type dispatchAndCheckRunner struct { + parentRequest ValidatedLookupResources2Request + foundResources dispatchableResourcesSubjectMap2 + ci cursorInformation + parentStream dispatch.LookupResources2Stream + newSubjectType *core.RelationReference + filteredSubjectIDs []string + entrypoint typesystem.ReachabilityEntrypoint + lrDispatcher dispatch.LookupResources2 + checkDispatcher dispatch.Check + + taskrunner *taskrunner.TaskRunner + + lock *sync.Mutex +} + +func (rdc *dispatchAndCheckRunner) dispatchAndCollect(ctx context.Context, cursor *v1.Cursor) ([]*v1.DispatchLookupResources2Response, error) { + collectingStream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + err := rdc.lrDispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: rdc.parentRequest.ResourceRelation, + SubjectRelation: rdc.newSubjectType, + SubjectIds: rdc.filteredSubjectIDs, + TerminalSubject: rdc.parentRequest.TerminalSubject, + Metadata: &v1.ResolverMeta{ + AtRevision: rdc.parentRequest.Revision.String(), + DepthRemaining: rdc.parentRequest.Metadata.DepthRemaining - 1, + }, + OptionalCursor: cursor, + OptionalLimit: uint32(datastore.FilterMaximumIDCount), + }, collectingStream) + return collectingStream.Results(), err +} + +func (rdc *dispatchAndCheckRunner) runDispatch(ctx context.Context, cursor *v1.Cursor) error { + rdc.lock.Lock() + if rdc.ci.limits.hasExhaustedLimit() { + rdc.lock.Unlock() + return nil + } + rdc.lock.Unlock() + + collected, err := rdc.dispatchAndCollect(ctx, cursor) + if err != nil { + return err + } + + if len(collected) == 0 { + return nil + } + + // Kick off a worker to filter the results via a check and then publish what was found. + rdc.taskrunner.Schedule(func(ctx context.Context) error { + return rdc.runChecker(ctx, collected) + }) + + // Start another dispatch at the cursor of the last response, to run in the background + // and collect more results for filtering while the checker is running. + rdc.taskrunner.Schedule(func(ctx context.Context) error { + return rdc.runDispatch(ctx, collected[len(collected)-1].AfterResponseCursor) + }) + + return nil +} + +func (rdc *dispatchAndCheckRunner) runChecker(ctx context.Context, collected []*v1.DispatchLookupResources2Response) error { + rdc.lock.Lock() + if rdc.ci.limits.hasExhaustedLimit() { + rdc.lock.Unlock() + return nil + } + rdc.lock.Unlock() + + checkHints := make([]*v1.CheckHint, 0, len(collected)) + resourceIDsToCheck := make([]string, 0, len(collected)) + for _, resource := range collected { + resourceIDsToCheck = append(resourceIDsToCheck, resource.Resource.ResourceId) + + checkHint, err := hints.HintForEntrypoint( + rdc.entrypoint, + resource.Resource.ResourceId, + rdc.parentRequest.TerminalSubject, + &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + }) + if err != nil { + return err + } + checkHints = append(checkHints, checkHint) + } + + // Batch check the results to filter to those visible and then publish just the visible resources. + resultsByResourceID, checkMetadata, err := computed.ComputeBulkCheck(ctx, rdc.checkDispatcher, computed.CheckParameters{ + ResourceType: rdc.parentRequest.ResourceRelation, + Subject: rdc.parentRequest.TerminalSubject, + CaveatContext: rdc.parentRequest.Context.AsMap(), + AtRevision: rdc.parentRequest.Revision, + MaximumDepth: rdc.parentRequest.Metadata.DepthRemaining - 1, + DebugOption: computed.NoDebugging, + CheckHints: checkHints, + }, resourceIDsToCheck) + if err != nil { + return err + } + + // Publish any resources that are visible. + isFirstPublishCall := true + for _, resource := range collected { + result, ok := resultsByResourceID[resource.Resource.ResourceId] + if !ok { + continue + } + + switch result.Membership { + case v1.ResourceCheckResult_MEMBER: + fallthrough + + case v1.ResourceCheckResult_CAVEATED_MEMBER: + rdc.lock.Lock() + if err := publishResultToParentStream(resource, rdc.ci, rdc.foundResources, result.MissingExprFields, isFirstPublishCall, checkMetadata, rdc.parentStream); err != nil { + rdc.lock.Unlock() + return err + } + + isFirstPublishCall = false + + if rdc.ci.limits.hasExhaustedLimit() { + rdc.lock.Unlock() + return nil + } + rdc.lock.Unlock() + + case v1.ResourceCheckResult_NOT_MEMBER: + // Skip. + continue + + default: + return spiceerrors.MustBugf("unexpected result from check: %v", result.Membership) + } + } + + return nil +} + +func (rdc *dispatchAndCheckRunner) runAndWait() error { + currentCursor := rdc.ci.currentCursor + + // Kick off a dispatch at the current cursor. + rdc.taskrunner.Schedule(func(ctx context.Context) error { + return rdc.runDispatch(ctx, currentCursor) + }) + + return rdc.taskrunner.Wait() +} + +// unfilteredLookupResourcesDispatchStreamForEntrypoint creates a new dispatch stream that wraps +// the parent stream, and publishes the results of the lookup resources call to the parent stream, +// mapped via foundResources. +func unfilteredLookupResourcesDispatchStreamForEntrypoint( + ctx context.Context, + foundResources dispatchableResourcesSubjectMap2, + parentStream dispatch.LookupResources2Stream, + ci cursorInformation, +) dispatch.LookupResources2Stream { + isFirstPublishCall := true + + wrappedStream := dispatch.NewHandlingDispatchStream(ctx, func(result *v1.DispatchLookupResources2Response) error { + select { + case <-ctx.Done(): + return ctx.Err() + + default: + } + + if err := publishResultToParentStream(result, ci, foundResources, nil, isFirstPublishCall, emptyMetadata, parentStream); err != nil { + return err + } + isFirstPublishCall = false + return nil + }) + + return wrappedStream +} + +// publishResultToParentStream publishes the result of a lookup resources call to the parent stream, +// mapped via foundResources. +func publishResultToParentStream( + result *v1.DispatchLookupResources2Response, + ci cursorInformation, + foundResources dispatchableResourcesSubjectMap2, + additionalMissingContext []string, + isFirstPublishCall bool, + additionalMetadata *v1.ResponseMeta, + parentStream dispatch.LookupResources2Stream, +) error { + // Map the found resources via the subject+resources used for dispatching, to determine + // if any need to be made conditional due to caveats. + mappedResource, err := foundResources.mapPossibleResource(result.Resource) + if err != nil { + return err + } + + if !ci.limits.prepareForPublishing() { + return nil + } + + // The cursor for the response is that of the parent response + the cursor from the result itself. + afterResponseCursor, err := combineCursors( + ci.responsePartialCursor(), + result.AfterResponseCursor, + ) + if err != nil { + return err + } + + metadata := result.Metadata + if isFirstPublishCall { + metadata = addCallToResponseMetadata(metadata) + metadata = combineResponseMetadata(metadata, additionalMetadata) + } else { + metadata = addAdditionalDepthRequired(metadata) + } + + missingContextParameters := mapz.NewSet(mappedResource.MissingContextParams...) + missingContextParameters.Extend(result.Resource.MissingContextParams) + missingContextParameters.Extend(additionalMissingContext) + + mappedResource.MissingContextParams = missingContextParameters.AsSlice() + + resp := &v1.DispatchLookupResources2Response{ + Resource: mappedResource, + Metadata: metadata, + AfterResponseCursor: afterResponseCursor, + } + + return parentStream.Publish(resp) +} diff --git a/internal/graph/membershipset.go b/internal/graph/membershipset.go index 7ec263e0e1..1eacf1fd86 100644 --- a/internal/graph/membershipset.go +++ b/internal/graph/membershipset.go @@ -110,7 +110,9 @@ func (ms *MembershipSet) addMember(resourceID string, caveatExpr *core.CaveatExp // The changes are made in-place. func (ms *MembershipSet) UnionWith(resultsMap CheckResultsMap) { for resourceID, details := range resultsMap { - ms.addMember(resourceID, details.Expression) + if details.Membership != v1.ResourceCheckResult_NOT_MEMBER { + ms.addMember(resourceID, details.Expression) + } } } @@ -118,7 +120,7 @@ func (ms *MembershipSet) UnionWith(resultsMap CheckResultsMap) { // The changes are made in-place. func (ms *MembershipSet) IntersectWith(resultsMap CheckResultsMap) { for resourceID := range ms.membersByID { - if _, ok := resultsMap[resourceID]; !ok { + if details, ok := resultsMap[resourceID]; !ok || details.Membership == v1.ResourceCheckResult_NOT_MEMBER { delete(ms.membersByID, resourceID) } } @@ -126,7 +128,7 @@ func (ms *MembershipSet) IntersectWith(resultsMap CheckResultsMap) { ms.hasDeterminedMember = false for resourceID, details := range resultsMap { existing, ok := ms.membersByID[resourceID] - if !ok { + if !ok || details.Membership == v1.ResourceCheckResult_NOT_MEMBER { continue } if existing == nil && details.Expression == nil { @@ -143,7 +145,7 @@ func (ms *MembershipSet) IntersectWith(resultsMap CheckResultsMap) { func (ms *MembershipSet) Subtract(resultsMap CheckResultsMap) { ms.hasDeterminedMember = false for resourceID, expression := range ms.membersByID { - if details, ok := resultsMap[resourceID]; ok { + if details, ok := resultsMap[resourceID]; ok && details.Membership != v1.ResourceCheckResult_NOT_MEMBER { // If the incoming member has no caveat, then this removal is absolute. if details.Expression == nil { delete(ms.membersByID, resourceID) diff --git a/internal/graph/membershipset_test.go b/internal/graph/membershipset_test.go index 43fd0c8afc..e86c885400 100644 --- a/internal/graph/membershipset_test.go +++ b/internal/graph/membershipset_test.go @@ -1,13 +1,16 @@ package graph import ( + "sort" "testing" "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" "google.golang.org/protobuf/types/known/structpb" "github.com/authzed/spicedb/internal/caveats" core "github.com/authzed/spicedb/pkg/proto/core/v1" + v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" "github.com/authzed/spicedb/pkg/tuple" ) @@ -748,6 +751,64 @@ func TestMembershipSetSubtract(t *testing.T) { } } +func TestMembershipSetUnionWithNonMemberEntries(t *testing.T) { + ms := NewMembershipSet() + ms.addMember("resource1", nil) + ms.addMember("resource2", nil) + + ms.UnionWith(CheckResultsMap{ + "resource3": &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_NOT_MEMBER, + }, + }) + + keys := maps.Keys(ms.membersByID) + sort.Strings(keys) + + require.Equal(t, 2, ms.Size()) + require.True(t, ms.HasDeterminedMember()) + require.Equal(t, []string{"resource1", "resource2"}, keys) +} + +func TestMembershipSetIntersectWithNonMemberEntries(t *testing.T) { + ms := NewMembershipSet() + ms.addMember("resource1", nil) + ms.addMember("resource2", nil) + + ms.IntersectWith(CheckResultsMap{ + "resource1": &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_NOT_MEMBER, + }, + "resource2": &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + }, + }) + + require.Equal(t, 1, ms.Size()) + require.True(t, ms.HasDeterminedMember()) + require.Equal(t, []string{"resource2"}, maps.Keys(ms.membersByID)) +} + +func TestMembershipSetSubtractWithNonMemberEntries(t *testing.T) { + ms := NewMembershipSet() + ms.addMember("resource1", nil) + ms.addMember("resource2", nil) + + // Subtracting a set with a non-member entry should not change the set. + ms.Subtract(CheckResultsMap{ + "resource1": &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_NOT_MEMBER, + }, + "resource2": &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + }, + }) + + require.Equal(t, 1, ms.Size()) + require.True(t, ms.HasDeterminedMember()) + require.Equal(t, []string{"resource1"}, maps.Keys(ms.membersByID)) +} + func unwrapCaveat(ce *core.CaveatExpression) *core.ContextualizedCaveat { if ce == nil { return nil diff --git a/internal/graph/resourcesubjectsmap.go b/internal/graph/resourcesubjectsmap.go index 62a3076098..667a249c77 100644 --- a/internal/graph/resourcesubjectsmap.go +++ b/internal/graph/resourcesubjectsmap.go @@ -31,7 +31,8 @@ type resourcesSubjectMap struct { // subjectInfo is the information about a subject contained in a resourcesSubjectMap. type subjectInfo struct { - subjectID string + subjectID string + isCaveated bool } @@ -142,6 +143,7 @@ func (rsm dispatchableResourcesSubjectMap) asReachableResources(isDirectEntrypoi for _, info := range subjectInfos { subjectIDs = append(subjectIDs, info.subjectID) + if !info.isCaveated { allCaveated = false nonCaveatedSubjectIDs = append(nonCaveatedSubjectIDs, info.subjectID) @@ -197,6 +199,7 @@ func (rsm dispatchableResourcesSubjectMap) mapFoundResource(foundResource *v1.Re for _, info := range infos { forSubjectIDs.Insert(info.subjectID) + if !info.isCaveated { nonCaveatedSubjectIDs.Insert(info.subjectID) } diff --git a/internal/graph/resourcesubjectsmap2.go b/internal/graph/resourcesubjectsmap2.go new file mode 100644 index 0000000000..bde1b85d02 --- /dev/null +++ b/internal/graph/resourcesubjectsmap2.go @@ -0,0 +1,197 @@ +package graph + +import ( + "sort" + + "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/spiceerrors" +) + +// resourcesSubjectMap2 is a multimap which tracks mappings from found resource IDs +// to the subject IDs (may be more than one) for each, as well as whether the mapping +// is conditional due to the use of a caveat on the relationship which formed the mapping. +type resourcesSubjectMap2 struct { + resourceType *core.RelationReference + resourcesAndSubjects *mapz.MultiMap[string, subjectInfo2] +} + +// subjectInfo2 is the information about a subject contained in a resourcesSubjectMap2. +type subjectInfo2 struct { + subjectID string + missingContextParameters []string +} + +func newResourcesSubjectMap2(resourceType *core.RelationReference) resourcesSubjectMap2 { + return resourcesSubjectMap2{ + resourceType: resourceType, + resourcesAndSubjects: mapz.NewMultiMap[string, subjectInfo2](), + } +} + +func newResourcesSubjectMap2WithCapacity(resourceType *core.RelationReference, capacity uint32) resourcesSubjectMap2 { + return resourcesSubjectMap2{ + resourceType: resourceType, + resourcesAndSubjects: mapz.NewMultiMapWithCap[string, subjectInfo2](capacity), + } +} + +func subjectIDsToResourcesMap2(resourceType *core.RelationReference, subjectIDs []string) resourcesSubjectMap2 { + rsm := newResourcesSubjectMap2(resourceType) + for _, subjectID := range subjectIDs { + rsm.addSubjectIDAsFoundResourceID(subjectID) + } + return rsm +} + +// addRelationship adds the relationship to the resource subject map, recording a mapping from +// the resource of the relationship to the subject, as well as whether the relationship was caveated. +func (rsm resourcesSubjectMap2) addRelationship(rel *core.RelationTuple, missingContextParameters []string) error { + if rel.ResourceAndRelation.Namespace != rsm.resourceType.Namespace || + rel.ResourceAndRelation.Relation != rsm.resourceType.Relation { + return spiceerrors.MustBugf("invalid relationship for addRelationship. expected: %v, found: %v", rsm.resourceType, rel.ResourceAndRelation) + } + + if len(missingContextParameters) > 0 && rel.Caveat == nil { + return spiceerrors.MustBugf("missing caveat for caveated relationship") + } + + rsm.resourcesAndSubjects.Add(rel.ResourceAndRelation.ObjectId, subjectInfo2{rel.Subject.ObjectId, missingContextParameters}) + return nil +} + +// addSubjectIDAsFoundResourceID adds a subject ID directly as a found subject for itself as the resource, +// with no associated caveat. +func (rsm resourcesSubjectMap2) addSubjectIDAsFoundResourceID(subjectID string) { + rsm.resourcesAndSubjects.Add(subjectID, subjectInfo2{subjectID, nil}) +} + +// asReadOnly returns a read-only dispatchableResourcesSubjectMap2 for dispatching for the +// resources in this map (if any). +func (rsm resourcesSubjectMap2) asReadOnly() dispatchableResourcesSubjectMap2 { + return dispatchableResourcesSubjectMap2{rsm.resourceType, rsm.resourcesAndSubjects.AsReadOnly()} +} + +func (rsm resourcesSubjectMap2) len() int { + return rsm.resourcesAndSubjects.Len() +} + +// dispatchableResourcesSubjectMap2 is a read-only, frozen version of the resourcesSubjectMap2 that +// can be used for mapping conditionals once calls have been dispatched. This is read-only due to +// its use by concurrent callers. +type dispatchableResourcesSubjectMap2 struct { + resourceType *core.RelationReference + resourcesAndSubjects mapz.ReadOnlyMultimap[string, subjectInfo2] +} + +func (rsm dispatchableResourcesSubjectMap2) isEmpty() bool { + return rsm.resourcesAndSubjects.IsEmpty() +} + +func (rsm dispatchableResourcesSubjectMap2) resourceIDs() []string { + return rsm.resourcesAndSubjects.Keys() +} + +// filterSubjectIDsToDispatch returns the set of subject IDs that have not yet been +// dispatched, by adding them to the dispatched set. +func (rsm dispatchableResourcesSubjectMap2) filterSubjectIDsToDispatch(dispatched *syncONRSet, dispatchSubjectType *core.RelationReference) []string { + resourceIDs := rsm.resourceIDs() + filtered := make([]string, 0, len(resourceIDs)) + for _, resourceID := range resourceIDs { + if dispatched.Add(&core.ObjectAndRelation{ + Namespace: dispatchSubjectType.Namespace, + ObjectId: resourceID, + Relation: dispatchSubjectType.Relation, + }) { + filtered = append(filtered, resourceID) + } + } + + return filtered +} + +func (rsm dispatchableResourcesSubjectMap2) asPossibleResources() []*v1.PossibleResource { + resources := make([]*v1.PossibleResource, 0, rsm.resourcesAndSubjects.Len()) + + // Sort for stability. + sortedResourceIds := rsm.resourcesAndSubjects.Keys() + sort.Strings(sortedResourceIds) + + for _, resourceID := range sortedResourceIds { + subjectInfo2s, _ := rsm.resourcesAndSubjects.Get(resourceID) + subjectIDs := make([]string, 0, len(subjectInfo2s)) + allCaveated := true + nonCaveatedSubjectIDs := make([]string, 0, len(subjectInfo2s)) + missingContextParameters := mapz.NewSet[string]() + + for _, info := range subjectInfo2s { + subjectIDs = append(subjectIDs, info.subjectID) + if len(info.missingContextParameters) == 0 { + allCaveated = false + nonCaveatedSubjectIDs = append(nonCaveatedSubjectIDs, info.subjectID) + } else { + missingContextParameters.Extend(info.missingContextParameters) + } + } + + // Sort for stability. + sort.Strings(subjectIDs) + + // If all the incoming edges are caveated, then the entire status has to be marked as a check + // is required. Otherwise, if there is at least *one* non-caveated incoming edge, then we can + // return the existing status as a short-circuit for those non-caveated found subjects. + if allCaveated { + resources = append(resources, &v1.PossibleResource{ + ResourceId: resourceID, + ForSubjectIds: subjectIDs, + MissingContextParams: missingContextParameters.AsSlice(), + }) + } else { + resources = append(resources, &v1.PossibleResource{ + ResourceId: resourceID, + ForSubjectIds: nonCaveatedSubjectIDs, + }) + } + } + return resources +} + +func (rsm dispatchableResourcesSubjectMap2) mapPossibleResource(foundResource *v1.PossibleResource) (*v1.PossibleResource, error) { + forSubjectIDs := mapz.NewSet[string]() + nonCaveatedSubjectIDs := mapz.NewSet[string]() + missingContextParameters := mapz.NewSet[string]() + + for _, forSubjectID := range foundResource.ForSubjectIds { + // Map from the incoming subject ID to the subject ID(s) that caused the dispatch. + infos, ok := rsm.resourcesAndSubjects.Get(forSubjectID) + if !ok { + return nil, spiceerrors.MustBugf("missing for subject ID") + } + + for _, info := range infos { + forSubjectIDs.Insert(info.subjectID) + if len(info.missingContextParameters) == 0 { + nonCaveatedSubjectIDs.Insert(info.subjectID) + } else { + missingContextParameters.Extend(info.missingContextParameters) + } + } + } + + // If there are some non-caveated IDs, return those and mark as the parent status. + if nonCaveatedSubjectIDs.Len() > 0 { + return &v1.PossibleResource{ + ResourceId: foundResource.ResourceId, + ForSubjectIds: nonCaveatedSubjectIDs.AsSlice(), + }, nil + } + + // Otherwise, everything is caveated, so return the full set of subject IDs and mark + // as a check is required. + return &v1.PossibleResource{ + ResourceId: foundResource.ResourceId, + ForSubjectIds: forSubjectIDs.AsSlice(), + MissingContextParams: missingContextParameters.AsSlice(), + }, nil +} diff --git a/internal/graph/resourcesubjectsmap2_test.go b/internal/graph/resourcesubjectsmap2_test.go new file mode 100644 index 0000000000..7f1b18ecfb --- /dev/null +++ b/internal/graph/resourcesubjectsmap2_test.go @@ -0,0 +1,225 @@ +package graph + +import ( + "sort" + "strings" + "testing" + + "github.com/stretchr/testify/require" + + 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" +) + +func TestResourcesSubjectsMap2Basic(t *testing.T) { + rsm := newResourcesSubjectMap2(&core.RelationReference{ + Namespace: "document", + Relation: "view", + }) + + require.Equal(t, rsm.resourceType.Namespace, "document") + require.Equal(t, rsm.resourceType.Relation, "view") + require.Equal(t, 0, rsm.len()) + + rsm.addSubjectIDAsFoundResourceID("first") + require.Equal(t, 1, rsm.len()) + + rsm.addSubjectIDAsFoundResourceID("second") + require.Equal(t, 2, rsm.len()) + + err := rsm.addRelationship(tuple.MustParse("document:third#view@user:tom"), nil) + require.NoError(t, err) + require.Equal(t, 3, rsm.len()) + + err = rsm.addRelationship(tuple.MustParse("document:fourth#view@user:sarah[somecaveat]"), []string{"somecontext"}) + require.NoError(t, err) + require.Equal(t, 4, rsm.len()) + + locked := rsm.asReadOnly() + require.False(t, locked.isEmpty()) + + directAsResources := locked.asPossibleResources() + testutil.RequireProtoSlicesEqual(t, []*v1.PossibleResource{ + { + ResourceId: "first", + ForSubjectIds: []string{"first"}, + }, + { + ResourceId: "fourth", + ForSubjectIds: []string{"sarah"}, + MissingContextParams: []string{"somecontext"}, + }, + { + ResourceId: "second", + ForSubjectIds: []string{"second"}, + }, + { + ResourceId: "third", + ForSubjectIds: []string{"tom"}, + }, + }, directAsResources, nil, "different resources") +} + +type relAndMissingContext struct { + rel *core.RelationTuple + missingContext []string +} + +func TestResourcesSubjectsMap2MapFoundResources(t *testing.T) { + tcs := []struct { + name string + rels []relAndMissingContext + foundResources []*v1.PossibleResource + expected []*v1.PossibleResource + }{ + { + "empty", + []relAndMissingContext{}, + []*v1.PossibleResource{}, + []*v1.PossibleResource{}, + }, + { + "basic no caveats", + []relAndMissingContext{ + {rel: tuple.MustParse("group:firstgroup#member@organization:foo")}, + {rel: tuple.MustParse("group:firstgroup#member@organization:bar")}, + }, + []*v1.PossibleResource{ + { + ResourceId: "first", + ForSubjectIds: []string{"firstgroup"}, + }, + }, + []*v1.PossibleResource{ + { + ResourceId: "first", + ForSubjectIds: []string{"foo", "bar"}, + }, + }, + }, + { + "caveated all found", + []relAndMissingContext{ + {rel: tuple.MustParse("group:firstgroup#member@organization:foo[somecaveat]")}, + {rel: tuple.MustParse("group:firstgroup#member@organization:bar[somecvaeat]")}, + }, + []*v1.PossibleResource{ + { + ResourceId: "first", + ForSubjectIds: []string{"firstgroup"}, + }, + }, + []*v1.PossibleResource{ + { + ResourceId: "first", + ForSubjectIds: []string{"bar", "foo"}, + }, + }, + }, + { + "multi-input all caveated", + []relAndMissingContext{ + {rel: tuple.MustParse("group:firstgroup#member@organization:bar[anothercaveat]"), missingContext: []string{"somecontext"}}, + {rel: tuple.MustParse("group:secondgroup#member@organization:foo[somecaveat]"), missingContext: []string{"anothercaveat"}}, + }, + []*v1.PossibleResource{ + { + ResourceId: "somedoc", + ForSubjectIds: []string{"firstgroup", "secondgroup"}, + MissingContextParams: []string{"somecontext"}, + }, + }, + []*v1.PossibleResource{ + { + ResourceId: "somedoc", + ForSubjectIds: []string{"bar", "foo"}, + MissingContextParams: []string{"anothercaveat", "somecontext"}, + }, + }, + }, + } + + for _, tc := range tcs { + tc := tc + t.Run(tc.name, func(t *testing.T) { + rsm := newResourcesSubjectMap2(&core.RelationReference{ + Namespace: "group", + Relation: "member", + }) + + for _, rel := range tc.rels { + err := rsm.addRelationship(rel.rel, rel.missingContext) + require.NoError(t, err) + } + + expected := make([]*v1.PossibleResource, 0, len(tc.expected)) + for _, expectedResource := range tc.expected { + cloned := expectedResource.CloneVT() + sort.Strings(cloned.ForSubjectIds) + expected = append(expected, cloned) + } + + locked := rsm.asReadOnly() + + resources := make([]*v1.PossibleResource, 0, len(tc.foundResources)) + for _, resource := range tc.foundResources { + r, err := locked.mapPossibleResource(resource) + require.NoError(t, err) + resources = append(resources, r) + } + + for _, r := range resources { + sort.Strings(r.ForSubjectIds) + sort.Strings(r.MissingContextParams) + } + + testutil.RequireProtoSlicesEqual(t, expected, resources, sortPossibleByResource, "different resources") + }) + } +} + +func TestFilterSubjectIDsToDispatch(t *testing.T) { + rsm := newResourcesSubjectMap2(&core.RelationReference{ + Namespace: "group", + Relation: "member", + }) + + err := rsm.addRelationship(tuple.MustParse("group:firstgroup#member@organization:foo"), nil) + require.NoError(t, err) + + err = rsm.addRelationship(tuple.MustParse("group:firstgroup#member@organization:bar"), nil) + require.NoError(t, err) + + err = rsm.addRelationship(tuple.MustParse("group:secondgroup#member@organization:foo"), nil) + require.NoError(t, err) + + locked := rsm.asReadOnly() + onrSet := &syncONRSet{} + filtered := locked.filterSubjectIDsToDispatch(onrSet, &core.RelationReference{ + Namespace: "group", + Relation: "...", + }) + + sort.Strings(filtered) + require.Equal(t, []string{"firstgroup", "secondgroup"}, filtered) + + filtered2 := locked.filterSubjectIDsToDispatch(onrSet, &core.RelationReference{ + Namespace: "group", + Relation: "...", + }) + require.Empty(t, filtered2) + + filtered3 := locked.filterSubjectIDsToDispatch(onrSet, &core.RelationReference{ + Namespace: "somethingelse", + Relation: "...", + }) + + sort.Strings(filtered3) + require.Equal(t, []string{"firstgroup", "secondgroup"}, filtered3) +} + +func sortPossibleByResource(first *v1.PossibleResource, second *v1.PossibleResource) int { + return strings.Compare(first.ResourceId, second.ResourceId) +} diff --git a/internal/services/dispatch/v1/acl.go b/internal/services/dispatch/v1/acl.go index 64dbe071c6..68d2740929 100644 --- a/internal/services/dispatch/v1/acl.go +++ b/internal/services/dispatch/v1/acl.go @@ -69,6 +69,14 @@ func (ds *dispatchServer) DispatchLookupResources( dispatch.WrapGRPCStream[*dispatchv1.DispatchLookupResourcesResponse](resp)) } +func (ds *dispatchServer) DispatchLookupResources2( + req *dispatchv1.DispatchLookupResources2Request, + resp dispatchv1.DispatchService_DispatchLookupResources2Server, +) error { + return ds.localDispatch.DispatchLookupResources2(req, + dispatch.WrapGRPCStream[*dispatchv1.DispatchLookupResources2Response](resp)) +} + func (ds *dispatchServer) DispatchLookupSubjects( req *dispatchv1.DispatchLookupSubjectsRequest, resp dispatchv1.DispatchService_DispatchLookupSubjectsServer, diff --git a/internal/services/integrationtesting/consistency_test.go b/internal/services/integrationtesting/consistency_test.go index 0316140731..2ca87c9c4f 100644 --- a/internal/services/integrationtesting/consistency_test.go +++ b/internal/services/integrationtesting/consistency_test.go @@ -23,6 +23,7 @@ import ( "github.com/authzed/spicedb/internal/dispatch" "github.com/authzed/spicedb/internal/graph" "github.com/authzed/spicedb/internal/services/integrationtesting/consistencytestutil" + "github.com/authzed/spicedb/pkg/cmd/server" "github.com/authzed/spicedb/pkg/datastore" "github.com/authzed/spicedb/pkg/development" "github.com/authzed/spicedb/pkg/genutil/mapz" @@ -62,16 +63,26 @@ func TestConsistency(t *testing.T) { dispatcherKind := dispatcherKind t.Run(dispatcherKind, func(t *testing.T) { - t.Parallel() - runConsistencyTestSuiteForFile(t, filePath, dispatcherKind == "caching") + for _, useLRV2 := range []bool{false, true} { + useLRV2 := useLRV2 + t.Run(fmt.Sprintf("lrv2-%t", useLRV2), func(t *testing.T) { + t.Parallel() + runConsistencyTestSuiteForFile(t, filePath, dispatcherKind == "caching", useLRV2) + }) + } }) } }) } } -func runConsistencyTestSuiteForFile(t *testing.T, filePath string, useCachingDispatcher bool) { - cad := consistencytestutil.LoadDataAndCreateClusterForTesting(t, filePath, testTimedelta) +func runConsistencyTestSuiteForFile(t *testing.T, filePath string, useCachingDispatcher bool, useLRV2 bool) { + options := []server.ConfigOption{} + if useLRV2 { + options = append(options, server.WithEnableExperimentalLookupResources(true)) + } + + cad := consistencytestutil.LoadDataAndCreateClusterForTesting(t, filePath, testTimedelta, options...) // Validate the type system for each namespace. headRevision, err := cad.DataStore.HeadRevision(cad.Ctx) diff --git a/internal/services/integrationtesting/consistencytestutil/clusteranddata.go b/internal/services/integrationtesting/consistencytestutil/clusteranddata.go index 637d5ead9c..eae495a202 100644 --- a/internal/services/integrationtesting/consistencytestutil/clusteranddata.go +++ b/internal/services/integrationtesting/consistencytestutil/clusteranddata.go @@ -16,6 +16,7 @@ import ( "github.com/authzed/spicedb/internal/dispatch/keys" datastoremw "github.com/authzed/spicedb/internal/middleware/datastore" "github.com/authzed/spicedb/internal/testserver" + "github.com/authzed/spicedb/pkg/cmd/server" "github.com/authzed/spicedb/pkg/datastore" "github.com/authzed/spicedb/pkg/typesystem" "github.com/authzed/spicedb/pkg/validationfile" @@ -34,24 +35,24 @@ type ConsistencyClusterAndData struct { // LoadDataAndCreateClusterForTesting loads the data found in a consistency test file, // builds a cluster for it, and returns both the data and cluster. -func LoadDataAndCreateClusterForTesting(t *testing.T, consistencyTestFilePath string, revisionDelta time.Duration) ConsistencyClusterAndData { +func LoadDataAndCreateClusterForTesting(t *testing.T, consistencyTestFilePath string, revisionDelta time.Duration, additionalServerOptions ...server.ConfigOption) ConsistencyClusterAndData { require := require.New(t) ds, err := memdb.NewMemdbDatastore(0, revisionDelta, memdb.DisableGC) require.NoError(err) - return BuildDataAndCreateClusterForTesting(t, consistencyTestFilePath, ds) + return BuildDataAndCreateClusterForTesting(t, consistencyTestFilePath, ds, additionalServerOptions...) } // BuildDataAndCreateClusterForTesting loads the data found in a consistency test file, // builds a cluster for it, and returns both the data and cluster. -func BuildDataAndCreateClusterForTesting(t *testing.T, consistencyTestFilePath string, ds datastore.Datastore) ConsistencyClusterAndData { +func BuildDataAndCreateClusterForTesting(t *testing.T, consistencyTestFilePath string, ds datastore.Datastore, additionalServerOptions ...server.ConfigOption) ConsistencyClusterAndData { require := require.New(t) populated, revision, err := validationfile.PopulateFromFiles(context.Background(), ds, []string{consistencyTestFilePath}) require.NoError(err) - connections, cleanup := testserver.TestClusterWithDispatch(t, 1, ds) + connections, cleanup := testserver.TestClusterWithDispatch(t, 1, ds, additionalServerOptions...) t.Cleanup(cleanup) dsCtx := datastoremw.ContextWithHandle(context.Background()) diff --git a/internal/services/integrationtesting/testconfigs/caveatarrow.yaml b/internal/services/integrationtesting/testconfigs/caveatarrow.yaml index e59ce8e7fd..4b742138b5 100644 --- a/internal/services/integrationtesting/testconfigs/caveatarrow.yaml +++ b/internal/services/integrationtesting/testconfigs/caveatarrow.yaml @@ -51,11 +51,11 @@ assertions: - "document:caveatedorgdoc#view@user:tom" - "document:staticorgdoc#view@user:sarah" - "document:caveatedorgdoc#view@user:sarah" - - "document:caveatedorgdoc#view@user:fred" - 'document:caveatedorgdoc#view@user:sarah with {"anothercondition": "hello world"}' assertFalse: - "document:directorgdoc#view@user:fred" - "document:staticnoorgdoc#view@user:tom" - "document:staticorgdoc#view@user:fred" + - "document:caveatedorgdoc#view@user:fred" - 'document:caveatedorgdoc#view@user:tom with {"anothercondition": "nope"}' - 'document:caveatedorgdoc#view@user:sarah with {"somecondition": 41, "anothercondition": "hello world"}' diff --git a/internal/services/integrationtesting/testconfigs/directandindirect.yaml b/internal/services/integrationtesting/testconfigs/directandindirect.yaml new file mode 100644 index 0000000000..35263a3765 --- /dev/null +++ b/internal/services/integrationtesting/testconfigs/directandindirect.yaml @@ -0,0 +1,45 @@ +--- +schema: |+ + definition user {} + + definition organization { + relation admin: user + relation member: user + permission is_member = member + admin + } + + definition document { + relation banned: user + relation viewer: user | user:* + relation org: organization + + permission edit = org->is_member + permission view = (viewer - banned) + edit + permission vsb = viewer - banned + permission indirect_view = vsb + edit + } + +relationships: | + document:firstdoc#viewer@user:tom + document:firstdoc#banned@user:fred + document:firstdoc#org@organization:someorg + organization:someorg#member@user:fred + + document:publicdoc#viewer@user:* + document:publicdoc#banned@user:fred + document:publicdoc#org@organization:someorg +assertions: + assertTrue: + - "document:firstdoc#view@user:tom" + - "document:firstdoc#view@user:fred" + - "document:firstdoc#indirect_view@user:tom" + - "document:firstdoc#indirect_view@user:fred" + - "document:firstdoc#vsb@user:tom" + - "document:publicdoc#view@user:tom" + - "document:publicdoc#view@user:fred" + - "document:publicdoc#indirect_view@user:tom" + - "document:publicdoc#indirect_view@user:fred" + - "document:publicdoc#vsb@user:tom" + assertFalse: + - "document:firstdoc#vsb@user:fred" + - "document:publicdoc#vsb@user:fred" diff --git a/internal/services/steelthreadtesting/definitions.go b/internal/services/steelthreadtesting/definitions.go index 011f2ffc85..c95a6db16e 100644 --- a/internal/services/steelthreadtesting/definitions.go +++ b/internal/services/steelthreadtesting/definitions.go @@ -209,6 +209,54 @@ var steelThreadTestCases = []steelThreadTestCase{ }, resultsFileName: "basic-lookup-resources-cursored-lookup-resources-for-fred-page-size-100-results.yaml", }, + { + name: "indirect without other permission, page size 5", + operationName: "cursoredLookupResources", + arguments: map[string]any{ + "resource_type": "document", + "permission": "vsb", + "subject_type": "user", + "subject_object_id": "fred", + "page_size": 5, + }, + resultsFileName: "basic-lookup-resources-indirect-without-other-permission-page-size-5-results.yaml", + }, + { + name: "indirect without other permission, page size 16", + operationName: "cursoredLookupResources", + arguments: map[string]any{ + "resource_type": "document", + "permission": "vsb", + "subject_type": "user", + "subject_object_id": "fred", + "page_size": 16, + }, + resultsFileName: "basic-lookup-resources-indirect-without-other-permission-page-size-16-results.yaml", + }, + { + name: "vsb_plus_nil, page size 16", + operationName: "cursoredLookupResources", + arguments: map[string]any{ + "resource_type": "document", + "permission": "vsb_plus_nil", + "subject_type": "user", + "subject_object_id": "fred", + "page_size": 16, + }, + resultsFileName: "basic-lookup-resources-vsb-plus-nil-page-size-16-results.yaml", + }, + { + name: "edit, page size 16", + operationName: "cursoredLookupResources", + arguments: map[string]any{ + "resource_type": "document", + "permission": "edit", + "subject_type": "user", + "subject_object_id": "fred", + "page_size": 16, + }, + resultsFileName: "basic-lookup-resources-edit-page-size-16-results.yaml", + }, }, }, { @@ -227,4 +275,56 @@ var steelThreadTestCases = []steelThreadTestCase{ }, }, }, + { + name: "lookup resources with intersection", + datafile: "document-with-intersect-resources.yaml", + operations: []steelThreadOperationCase{ + { + name: "uncursored lookup resources for user:fred", + operationName: "lookupResources", + arguments: map[string]any{ + "resource_type": "document", + "permission": "view", + "subject_type": "user", + "subject_object_id": "fred", + }, + resultsFileName: "lookup-resources-with-intersection-uncursored-indirect-lookup-resources-for-user-fred-results.yaml", + }, + { + name: "uncursored indirect lookup resources for user:fred", + operationName: "lookupResources", + arguments: map[string]any{ + "resource_type": "document", + "permission": "indirect_view", + "subject_type": "user", + "subject_object_id": "fred", + }, + resultsFileName: "lookup-resources-with-intersection-uncursored-indirect-lookup-resources-for-user-fred-results.yaml", + }, + { + name: "cursored lookup resources for user:fred", + operationName: "cursoredLookupResources", + arguments: map[string]any{ + "resource_type": "document", + "permission": "view", + "subject_type": "user", + "subject_object_id": "fred", + "page_size": 18, + }, + resultsFileName: "lookup-resources-with-intersection-cursored-lookup-resources-for-user-fred-results.yaml", + }, + { + name: "cursored indirect lookup resources for user:fred", + operationName: "cursoredLookupResources", + arguments: map[string]any{ + "resource_type": "document", + "permission": "indirect_view", + "subject_type": "user", + "subject_object_id": "fred", + "page_size": 18, + }, + resultsFileName: "lookup-resources-with-intersection-cursored-lookup-resources-for-user-fred-results.yaml", + }, + }, + }, } diff --git a/internal/services/steelthreadtesting/operations.go b/internal/services/steelthreadtesting/operations.go index 00b9acc4d7..5537b72a4d 100644 --- a/internal/services/steelthreadtesting/operations.go +++ b/internal/services/steelthreadtesting/operations.go @@ -6,6 +6,7 @@ package steelthreadtesting import ( "context" "errors" + "fmt" "io" "sort" "strings" @@ -147,6 +148,7 @@ func cursoredLookupResources(parameters map[string]any, client v1.PermissionsSer var currentCursor *v1.Cursor nodeSets := make([][]yaml.Node, 0) + resultCounts := make([]int, 0) for { r, err := client.LookupResources(ctx, &v1.LookupResourcesRequest{ ResourceObjectType: parameters["resource_type"].(string), @@ -171,6 +173,7 @@ func cursoredLookupResources(parameters map[string]any, client v1.PermissionsSer } foundResources := mapz.NewSet[string]() + resultCount := 0 for { resp, err := r.Recv() if err != nil { @@ -183,12 +186,15 @@ func cursoredLookupResources(parameters map[string]any, client v1.PermissionsSer foundResources.Add(formatResolvedResource(resp)) currentCursor = resp.AfterResultCursor + resultCount++ } if foundResources.IsEmpty() { break } + resultCounts = append(resultCounts, resultCount) + foundResourcesSlice := foundResources.AsSlice() sort.Strings(foundResourcesSlice) @@ -204,6 +210,16 @@ func cursoredLookupResources(parameters map[string]any, client v1.PermissionsSer nodeSets = append(nodeSets, yamlNodes) } + for index, count := range resultCounts { + if index == len(resultCounts)-1 { + continue + } + + if count != parameters["page_size"].(int) { + return nil, fmt.Errorf("expected full page size of %d for page #%d (of %d), got %d\npage sizes: %v", parameters["page_size"].(int), index, len(resultCounts), count, resultCounts) + } + } + return nodeSets, nil } diff --git a/internal/services/steelthreadtesting/steelresults/basic-lookup-resources-edit-page-size-16-results.yaml b/internal/services/steelthreadtesting/steelresults/basic-lookup-resources-edit-page-size-16-results.yaml new file mode 100644 index 0000000000..1b7c06d064 --- /dev/null +++ b/internal/services/steelthreadtesting/steelresults/basic-lookup-resources-edit-page-size-16-results.yaml @@ -0,0 +1,11 @@ +--- +- - 'doc-90' + - 'doc-91' + - 'doc-92' + - 'doc-93' + - 'doc-94' + - 'doc-95' + - 'doc-96' + - 'doc-97' + - 'doc-98' + - 'doc-99' diff --git a/internal/services/steelthreadtesting/steelresults/basic-lookup-resources-indirect-without-other-permission-page-size-16-results.yaml b/internal/services/steelthreadtesting/steelresults/basic-lookup-resources-indirect-without-other-permission-page-size-16-results.yaml new file mode 100644 index 0000000000..0292a6a3a2 --- /dev/null +++ b/internal/services/steelthreadtesting/steelresults/basic-lookup-resources-indirect-without-other-permission-page-size-16-results.yaml @@ -0,0 +1,84 @@ +--- +- - 'doc-0' + - 'doc-1' + - 'doc-10' + - 'doc-11' + - 'doc-12' + - 'doc-13' + - 'doc-14' + - 'doc-15' + - 'doc-16' + - 'doc-17' + - 'doc-18' + - 'doc-19' + - 'doc-2' + - 'doc-20' + - 'doc-21' + - 'doc-22' +- - 'doc-23' + - 'doc-24' + - 'doc-25' + - 'doc-26' + - 'doc-27' + - 'doc-28' + - 'doc-29' + - 'doc-3' + - 'doc-30' + - 'doc-31' + - 'doc-32' + - 'doc-33' + - 'doc-34' + - 'doc-35' + - 'doc-36' + - 'doc-37' +- - 'doc-38' + - 'doc-39' + - 'doc-4' + - 'doc-40' + - 'doc-41' + - 'doc-42' + - 'doc-43' + - 'doc-44' + - 'doc-45' + - 'doc-46' + - 'doc-47' + - 'doc-48' + - 'doc-49' + - 'doc-5' + - 'doc-50' + - 'doc-51' +- - 'doc-52' + - 'doc-53' + - 'doc-54' + - 'doc-55' + - 'doc-56' + - 'doc-57' + - 'doc-58' + - 'doc-59' + - 'doc-6' + - 'doc-60' + - 'doc-61' + - 'doc-62' + - 'doc-63' + - 'doc-64' + - 'doc-65' + - 'doc-66' +- - 'doc-67' + - 'doc-68' + - 'doc-69' + - 'doc-7' + - 'doc-70' + - 'doc-71' + - 'doc-72' + - 'doc-73' + - 'doc-74' + - 'doc-75' + - 'doc-76' + - 'doc-77' + - 'doc-78' + - 'doc-79' + - 'doc-8' + - 'doc-9' +- - 'public-doc-0' + - 'public-doc-1' + - 'public-doc-3' diff --git a/internal/services/steelthreadtesting/steelresults/basic-lookup-resources-indirect-without-other-permission-page-size-5-results.yaml b/internal/services/steelthreadtesting/steelresults/basic-lookup-resources-indirect-without-other-permission-page-size-5-results.yaml new file mode 100644 index 0000000000..9378e5b804 --- /dev/null +++ b/internal/services/steelthreadtesting/steelresults/basic-lookup-resources-indirect-without-other-permission-page-size-5-results.yaml @@ -0,0 +1,84 @@ +--- +- - 'doc-0' + - 'doc-1' + - 'doc-10' + - 'doc-11' + - 'doc-12' +- - 'doc-13' + - 'doc-14' + - 'doc-15' + - 'doc-16' + - 'doc-17' +- - 'doc-18' + - 'doc-19' + - 'doc-2' + - 'doc-20' + - 'doc-21' +- - 'doc-22' + - 'doc-23' + - 'doc-24' + - 'doc-25' + - 'doc-26' +- - 'doc-27' + - 'doc-28' + - 'doc-29' + - 'doc-3' + - 'doc-30' +- - 'doc-31' + - 'doc-32' + - 'doc-33' + - 'doc-34' + - 'doc-35' +- - 'doc-36' + - 'doc-37' + - 'doc-38' + - 'doc-39' + - 'doc-4' +- - 'doc-40' + - 'doc-41' + - 'doc-42' + - 'doc-43' + - 'doc-44' +- - 'doc-45' + - 'doc-46' + - 'doc-47' + - 'doc-48' + - 'doc-49' +- - 'doc-5' + - 'doc-50' + - 'doc-51' + - 'doc-52' + - 'doc-53' +- - 'doc-54' + - 'doc-55' + - 'doc-56' + - 'doc-57' + - 'doc-58' +- - 'doc-59' + - 'doc-6' + - 'doc-60' + - 'doc-61' + - 'doc-62' +- - 'doc-63' + - 'doc-64' + - 'doc-65' + - 'doc-66' + - 'doc-67' +- - 'doc-68' + - 'doc-69' + - 'doc-7' + - 'doc-70' + - 'doc-71' +- - 'doc-72' + - 'doc-73' + - 'doc-74' + - 'doc-75' + - 'doc-76' +- - 'doc-77' + - 'doc-78' + - 'doc-79' + - 'doc-8' + - 'doc-9' +- - 'public-doc-0' + - 'public-doc-1' + - 'public-doc-3' diff --git a/internal/services/steelthreadtesting/steelresults/basic-lookup-resources-vsb-plus-nil-page-size-16-results.yaml b/internal/services/steelthreadtesting/steelresults/basic-lookup-resources-vsb-plus-nil-page-size-16-results.yaml new file mode 100644 index 0000000000..0292a6a3a2 --- /dev/null +++ b/internal/services/steelthreadtesting/steelresults/basic-lookup-resources-vsb-plus-nil-page-size-16-results.yaml @@ -0,0 +1,84 @@ +--- +- - 'doc-0' + - 'doc-1' + - 'doc-10' + - 'doc-11' + - 'doc-12' + - 'doc-13' + - 'doc-14' + - 'doc-15' + - 'doc-16' + - 'doc-17' + - 'doc-18' + - 'doc-19' + - 'doc-2' + - 'doc-20' + - 'doc-21' + - 'doc-22' +- - 'doc-23' + - 'doc-24' + - 'doc-25' + - 'doc-26' + - 'doc-27' + - 'doc-28' + - 'doc-29' + - 'doc-3' + - 'doc-30' + - 'doc-31' + - 'doc-32' + - 'doc-33' + - 'doc-34' + - 'doc-35' + - 'doc-36' + - 'doc-37' +- - 'doc-38' + - 'doc-39' + - 'doc-4' + - 'doc-40' + - 'doc-41' + - 'doc-42' + - 'doc-43' + - 'doc-44' + - 'doc-45' + - 'doc-46' + - 'doc-47' + - 'doc-48' + - 'doc-49' + - 'doc-5' + - 'doc-50' + - 'doc-51' +- - 'doc-52' + - 'doc-53' + - 'doc-54' + - 'doc-55' + - 'doc-56' + - 'doc-57' + - 'doc-58' + - 'doc-59' + - 'doc-6' + - 'doc-60' + - 'doc-61' + - 'doc-62' + - 'doc-63' + - 'doc-64' + - 'doc-65' + - 'doc-66' +- - 'doc-67' + - 'doc-68' + - 'doc-69' + - 'doc-7' + - 'doc-70' + - 'doc-71' + - 'doc-72' + - 'doc-73' + - 'doc-74' + - 'doc-75' + - 'doc-76' + - 'doc-77' + - 'doc-78' + - 'doc-79' + - 'doc-8' + - 'doc-9' +- - 'public-doc-0' + - 'public-doc-1' + - 'public-doc-3' diff --git a/internal/services/steelthreadtesting/steelresults/lookup-resources-with-intersection-cursored-lookup-resources-for-user-fred-results.yaml b/internal/services/steelthreadtesting/steelresults/lookup-resources-with-intersection-cursored-lookup-resources-for-user-fred-results.yaml new file mode 100644 index 0000000000..c215832620 --- /dev/null +++ b/internal/services/steelthreadtesting/steelresults/lookup-resources-with-intersection-cursored-lookup-resources-for-user-fred-results.yaml @@ -0,0 +1,151 @@ +--- +- - 'doc-150' + - 'doc-151' + - 'doc-152' + - 'doc-153' + - 'doc-154' + - 'doc-155' + - 'doc-156' + - 'doc-157' + - 'doc-158' + - 'doc-159' + - 'doc-160' + - 'doc-161' + - 'doc-162' + - 'doc-163' + - 'doc-164' + - 'doc-165' + - 'doc-166' + - 'doc-167' +- - 'doc-168' + - 'doc-169' + - 'doc-170' + - 'doc-171' + - 'doc-172' + - 'doc-173' + - 'doc-174' + - 'doc-175' + - 'doc-176' + - 'doc-177' + - 'doc-178' + - 'doc-179' + - 'doc-180' + - 'doc-181' + - 'doc-182' + - 'doc-183' + - 'doc-184' + - 'doc-185' +- - 'doc-186' + - 'doc-187' + - 'doc-188' + - 'doc-189' + - 'doc-190' + - 'doc-191' + - 'doc-192' + - 'doc-193' + - 'doc-194' + - 'doc-195' + - 'doc-196' + - 'doc-197' + - 'doc-198' + - 'doc-199' + - 'doc-200' + - 'doc-201' + - 'doc-202' + - 'doc-203' +- - 'doc-204' + - 'doc-205' + - 'doc-206' + - 'doc-207' + - 'doc-208' + - 'doc-209' + - 'doc-210' + - 'doc-211' + - 'doc-212' + - 'doc-213' + - 'doc-214' + - 'doc-215' + - 'doc-216' + - 'doc-217' + - 'doc-218' + - 'doc-219' + - 'doc-220' + - 'doc-221' +- - 'doc-222' + - 'doc-223' + - 'doc-224' + - 'doc-225' + - 'doc-226' + - 'doc-227' + - 'doc-228' + - 'doc-229' + - 'doc-230' + - 'doc-231' + - 'doc-232' + - 'doc-233' + - 'doc-234' + - 'doc-235' + - 'doc-236' + - 'doc-237' + - 'doc-238' + - 'doc-239' +- - 'doc-240' + - 'doc-241' + - 'doc-242' + - 'doc-243' + - 'doc-244' + - 'doc-245' + - 'doc-246' + - 'doc-247' + - 'doc-248' + - 'doc-249' + - 'doc-250' + - 'doc-251' + - 'doc-252' + - 'doc-253' + - 'doc-254' + - 'doc-255' + - 'doc-256' + - 'doc-257' +- - 'doc-258' + - 'doc-259' + - 'doc-260' + - 'doc-261' + - 'doc-262' + - 'doc-263' + - 'doc-264' + - 'doc-265' + - 'doc-266' + - 'doc-267' + - 'doc-268' + - 'doc-269' + - 'doc-270' + - 'doc-271' + - 'doc-272' + - 'doc-273' + - 'doc-274' + - 'doc-275' +- - 'doc-276' + - 'doc-277' + - 'doc-278' + - 'doc-279' + - 'doc-280' + - 'doc-281' + - 'doc-282' + - 'doc-283' + - 'doc-284' + - 'doc-285' + - 'doc-286' + - 'doc-287' + - 'doc-288' + - 'doc-289' + - 'doc-290' + - 'doc-291' + - 'doc-292' + - 'doc-293' +- - 'doc-294' + - 'doc-295' + - 'doc-296' + - 'doc-297' + - 'doc-298' + - 'doc-299' diff --git a/internal/services/steelthreadtesting/steelresults/lookup-resources-with-intersection-uncursored-indirect-lookup-resources-for-user-fred-results.yaml b/internal/services/steelthreadtesting/steelresults/lookup-resources-with-intersection-uncursored-indirect-lookup-resources-for-user-fred-results.yaml new file mode 100644 index 0000000000..f0a775f0fa --- /dev/null +++ b/internal/services/steelthreadtesting/steelresults/lookup-resources-with-intersection-uncursored-indirect-lookup-resources-for-user-fred-results.yaml @@ -0,0 +1,151 @@ +--- +- 'doc-150' +- 'doc-151' +- 'doc-152' +- 'doc-153' +- 'doc-154' +- 'doc-155' +- 'doc-156' +- 'doc-157' +- 'doc-158' +- 'doc-159' +- 'doc-160' +- 'doc-161' +- 'doc-162' +- 'doc-163' +- 'doc-164' +- 'doc-165' +- 'doc-166' +- 'doc-167' +- 'doc-168' +- 'doc-169' +- 'doc-170' +- 'doc-171' +- 'doc-172' +- 'doc-173' +- 'doc-174' +- 'doc-175' +- 'doc-176' +- 'doc-177' +- 'doc-178' +- 'doc-179' +- 'doc-180' +- 'doc-181' +- 'doc-182' +- 'doc-183' +- 'doc-184' +- 'doc-185' +- 'doc-186' +- 'doc-187' +- 'doc-188' +- 'doc-189' +- 'doc-190' +- 'doc-191' +- 'doc-192' +- 'doc-193' +- 'doc-194' +- 'doc-195' +- 'doc-196' +- 'doc-197' +- 'doc-198' +- 'doc-199' +- 'doc-200' +- 'doc-201' +- 'doc-202' +- 'doc-203' +- 'doc-204' +- 'doc-205' +- 'doc-206' +- 'doc-207' +- 'doc-208' +- 'doc-209' +- 'doc-210' +- 'doc-211' +- 'doc-212' +- 'doc-213' +- 'doc-214' +- 'doc-215' +- 'doc-216' +- 'doc-217' +- 'doc-218' +- 'doc-219' +- 'doc-220' +- 'doc-221' +- 'doc-222' +- 'doc-223' +- 'doc-224' +- 'doc-225' +- 'doc-226' +- 'doc-227' +- 'doc-228' +- 'doc-229' +- 'doc-230' +- 'doc-231' +- 'doc-232' +- 'doc-233' +- 'doc-234' +- 'doc-235' +- 'doc-236' +- 'doc-237' +- 'doc-238' +- 'doc-239' +- 'doc-240' +- 'doc-241' +- 'doc-242' +- 'doc-243' +- 'doc-244' +- 'doc-245' +- 'doc-246' +- 'doc-247' +- 'doc-248' +- 'doc-249' +- 'doc-250' +- 'doc-251' +- 'doc-252' +- 'doc-253' +- 'doc-254' +- 'doc-255' +- 'doc-256' +- 'doc-257' +- 'doc-258' +- 'doc-259' +- 'doc-260' +- 'doc-261' +- 'doc-262' +- 'doc-263' +- 'doc-264' +- 'doc-265' +- 'doc-266' +- 'doc-267' +- 'doc-268' +- 'doc-269' +- 'doc-270' +- 'doc-271' +- 'doc-272' +- 'doc-273' +- 'doc-274' +- 'doc-275' +- 'doc-276' +- 'doc-277' +- 'doc-278' +- 'doc-279' +- 'doc-280' +- 'doc-281' +- 'doc-282' +- 'doc-283' +- 'doc-284' +- 'doc-285' +- 'doc-286' +- 'doc-287' +- 'doc-288' +- 'doc-289' +- 'doc-290' +- 'doc-291' +- 'doc-292' +- 'doc-293' +- 'doc-294' +- 'doc-295' +- 'doc-296' +- 'doc-297' +- 'doc-298' +- 'doc-299' diff --git a/internal/services/steelthreadtesting/testdata/document-with-intersect-resources.yaml b/internal/services/steelthreadtesting/testdata/document-with-intersect-resources.yaml new file mode 100644 index 0000000000..57e1932ac7 --- /dev/null +++ b/internal/services/steelthreadtesting/testdata/document-with-intersect-resources.yaml @@ -0,0 +1,622 @@ +--- +schema: |+ + definition user {} + + definition document { + relation viewer1: user + relation viewer2: user + permission view = viewer1 & viewer2 + + permission indirect_viewer1 = viewer1 + permission indirect_viewer2 = viewer2 + permission indirect_view = indirect_viewer1 & indirect_viewer2 + } + +relationships: | + // user:fred as viewer1 for 300 documents + // for docid in range(0, 300): + // document:doc-{docid}#viewer1@user:fred + document:doc-0#viewer1@user:fred + document:doc-1#viewer1@user:fred + document:doc-2#viewer1@user:fred + document:doc-3#viewer1@user:fred + document:doc-4#viewer1@user:fred + document:doc-5#viewer1@user:fred + document:doc-6#viewer1@user:fred + document:doc-7#viewer1@user:fred + document:doc-8#viewer1@user:fred + document:doc-9#viewer1@user:fred + document:doc-10#viewer1@user:fred + document:doc-11#viewer1@user:fred + document:doc-12#viewer1@user:fred + document:doc-13#viewer1@user:fred + document:doc-14#viewer1@user:fred + document:doc-15#viewer1@user:fred + document:doc-16#viewer1@user:fred + document:doc-17#viewer1@user:fred + document:doc-18#viewer1@user:fred + document:doc-19#viewer1@user:fred + document:doc-20#viewer1@user:fred + document:doc-21#viewer1@user:fred + document:doc-22#viewer1@user:fred + document:doc-23#viewer1@user:fred + document:doc-24#viewer1@user:fred + document:doc-25#viewer1@user:fred + document:doc-26#viewer1@user:fred + document:doc-27#viewer1@user:fred + document:doc-28#viewer1@user:fred + document:doc-29#viewer1@user:fred + document:doc-30#viewer1@user:fred + document:doc-31#viewer1@user:fred + document:doc-32#viewer1@user:fred + document:doc-33#viewer1@user:fred + document:doc-34#viewer1@user:fred + document:doc-35#viewer1@user:fred + document:doc-36#viewer1@user:fred + document:doc-37#viewer1@user:fred + document:doc-38#viewer1@user:fred + document:doc-39#viewer1@user:fred + document:doc-40#viewer1@user:fred + document:doc-41#viewer1@user:fred + document:doc-42#viewer1@user:fred + document:doc-43#viewer1@user:fred + document:doc-44#viewer1@user:fred + document:doc-45#viewer1@user:fred + document:doc-46#viewer1@user:fred + document:doc-47#viewer1@user:fred + document:doc-48#viewer1@user:fred + document:doc-49#viewer1@user:fred + document:doc-50#viewer1@user:fred + document:doc-51#viewer1@user:fred + document:doc-52#viewer1@user:fred + document:doc-53#viewer1@user:fred + document:doc-54#viewer1@user:fred + document:doc-55#viewer1@user:fred + document:doc-56#viewer1@user:fred + document:doc-57#viewer1@user:fred + document:doc-58#viewer1@user:fred + document:doc-59#viewer1@user:fred + document:doc-60#viewer1@user:fred + document:doc-61#viewer1@user:fred + document:doc-62#viewer1@user:fred + document:doc-63#viewer1@user:fred + document:doc-64#viewer1@user:fred + document:doc-65#viewer1@user:fred + document:doc-66#viewer1@user:fred + document:doc-67#viewer1@user:fred + document:doc-68#viewer1@user:fred + document:doc-69#viewer1@user:fred + document:doc-70#viewer1@user:fred + document:doc-71#viewer1@user:fred + document:doc-72#viewer1@user:fred + document:doc-73#viewer1@user:fred + document:doc-74#viewer1@user:fred + document:doc-75#viewer1@user:fred + document:doc-76#viewer1@user:fred + document:doc-77#viewer1@user:fred + document:doc-78#viewer1@user:fred + document:doc-79#viewer1@user:fred + document:doc-80#viewer1@user:fred + document:doc-81#viewer1@user:fred + document:doc-82#viewer1@user:fred + document:doc-83#viewer1@user:fred + document:doc-84#viewer1@user:fred + document:doc-85#viewer1@user:fred + document:doc-86#viewer1@user:fred + document:doc-87#viewer1@user:fred + document:doc-88#viewer1@user:fred + document:doc-89#viewer1@user:fred + document:doc-90#viewer1@user:fred + document:doc-91#viewer1@user:fred + document:doc-92#viewer1@user:fred + document:doc-93#viewer1@user:fred + document:doc-94#viewer1@user:fred + document:doc-95#viewer1@user:fred + document:doc-96#viewer1@user:fred + document:doc-97#viewer1@user:fred + document:doc-98#viewer1@user:fred + document:doc-99#viewer1@user:fred + document:doc-100#viewer1@user:fred + document:doc-101#viewer1@user:fred + document:doc-102#viewer1@user:fred + document:doc-103#viewer1@user:fred + document:doc-104#viewer1@user:fred + document:doc-105#viewer1@user:fred + document:doc-106#viewer1@user:fred + document:doc-107#viewer1@user:fred + document:doc-108#viewer1@user:fred + document:doc-109#viewer1@user:fred + document:doc-110#viewer1@user:fred + document:doc-111#viewer1@user:fred + document:doc-112#viewer1@user:fred + document:doc-113#viewer1@user:fred + document:doc-114#viewer1@user:fred + document:doc-115#viewer1@user:fred + document:doc-116#viewer1@user:fred + document:doc-117#viewer1@user:fred + document:doc-118#viewer1@user:fred + document:doc-119#viewer1@user:fred + document:doc-120#viewer1@user:fred + document:doc-121#viewer1@user:fred + document:doc-122#viewer1@user:fred + document:doc-123#viewer1@user:fred + document:doc-124#viewer1@user:fred + document:doc-125#viewer1@user:fred + document:doc-126#viewer1@user:fred + document:doc-127#viewer1@user:fred + document:doc-128#viewer1@user:fred + document:doc-129#viewer1@user:fred + document:doc-130#viewer1@user:fred + document:doc-131#viewer1@user:fred + document:doc-132#viewer1@user:fred + document:doc-133#viewer1@user:fred + document:doc-134#viewer1@user:fred + document:doc-135#viewer1@user:fred + document:doc-136#viewer1@user:fred + document:doc-137#viewer1@user:fred + document:doc-138#viewer1@user:fred + document:doc-139#viewer1@user:fred + document:doc-140#viewer1@user:fred + document:doc-141#viewer1@user:fred + document:doc-142#viewer1@user:fred + document:doc-143#viewer1@user:fred + document:doc-144#viewer1@user:fred + document:doc-145#viewer1@user:fred + document:doc-146#viewer1@user:fred + document:doc-147#viewer1@user:fred + document:doc-148#viewer1@user:fred + document:doc-149#viewer1@user:fred + document:doc-150#viewer1@user:fred + document:doc-151#viewer1@user:fred + document:doc-152#viewer1@user:fred + document:doc-153#viewer1@user:fred + document:doc-154#viewer1@user:fred + document:doc-155#viewer1@user:fred + document:doc-156#viewer1@user:fred + document:doc-157#viewer1@user:fred + document:doc-158#viewer1@user:fred + document:doc-159#viewer1@user:fred + document:doc-160#viewer1@user:fred + document:doc-161#viewer1@user:fred + document:doc-162#viewer1@user:fred + document:doc-163#viewer1@user:fred + document:doc-164#viewer1@user:fred + document:doc-165#viewer1@user:fred + document:doc-166#viewer1@user:fred + document:doc-167#viewer1@user:fred + document:doc-168#viewer1@user:fred + document:doc-169#viewer1@user:fred + document:doc-170#viewer1@user:fred + document:doc-171#viewer1@user:fred + document:doc-172#viewer1@user:fred + document:doc-173#viewer1@user:fred + document:doc-174#viewer1@user:fred + document:doc-175#viewer1@user:fred + document:doc-176#viewer1@user:fred + document:doc-177#viewer1@user:fred + document:doc-178#viewer1@user:fred + document:doc-179#viewer1@user:fred + document:doc-180#viewer1@user:fred + document:doc-181#viewer1@user:fred + document:doc-182#viewer1@user:fred + document:doc-183#viewer1@user:fred + document:doc-184#viewer1@user:fred + document:doc-185#viewer1@user:fred + document:doc-186#viewer1@user:fred + document:doc-187#viewer1@user:fred + document:doc-188#viewer1@user:fred + document:doc-189#viewer1@user:fred + document:doc-190#viewer1@user:fred + document:doc-191#viewer1@user:fred + document:doc-192#viewer1@user:fred + document:doc-193#viewer1@user:fred + document:doc-194#viewer1@user:fred + document:doc-195#viewer1@user:fred + document:doc-196#viewer1@user:fred + document:doc-197#viewer1@user:fred + document:doc-198#viewer1@user:fred + document:doc-199#viewer1@user:fred + document:doc-200#viewer1@user:fred + document:doc-201#viewer1@user:fred + document:doc-202#viewer1@user:fred + document:doc-203#viewer1@user:fred + document:doc-204#viewer1@user:fred + document:doc-205#viewer1@user:fred + document:doc-206#viewer1@user:fred + document:doc-207#viewer1@user:fred + document:doc-208#viewer1@user:fred + document:doc-209#viewer1@user:fred + document:doc-210#viewer1@user:fred + document:doc-211#viewer1@user:fred + document:doc-212#viewer1@user:fred + document:doc-213#viewer1@user:fred + document:doc-214#viewer1@user:fred + document:doc-215#viewer1@user:fred + document:doc-216#viewer1@user:fred + document:doc-217#viewer1@user:fred + document:doc-218#viewer1@user:fred + document:doc-219#viewer1@user:fred + document:doc-220#viewer1@user:fred + document:doc-221#viewer1@user:fred + document:doc-222#viewer1@user:fred + document:doc-223#viewer1@user:fred + document:doc-224#viewer1@user:fred + document:doc-225#viewer1@user:fred + document:doc-226#viewer1@user:fred + document:doc-227#viewer1@user:fred + document:doc-228#viewer1@user:fred + document:doc-229#viewer1@user:fred + document:doc-230#viewer1@user:fred + document:doc-231#viewer1@user:fred + document:doc-232#viewer1@user:fred + document:doc-233#viewer1@user:fred + document:doc-234#viewer1@user:fred + document:doc-235#viewer1@user:fred + document:doc-236#viewer1@user:fred + document:doc-237#viewer1@user:fred + document:doc-238#viewer1@user:fred + document:doc-239#viewer1@user:fred + document:doc-240#viewer1@user:fred + document:doc-241#viewer1@user:fred + document:doc-242#viewer1@user:fred + document:doc-243#viewer1@user:fred + document:doc-244#viewer1@user:fred + document:doc-245#viewer1@user:fred + document:doc-246#viewer1@user:fred + document:doc-247#viewer1@user:fred + document:doc-248#viewer1@user:fred + document:doc-249#viewer1@user:fred + document:doc-250#viewer1@user:fred + document:doc-251#viewer1@user:fred + document:doc-252#viewer1@user:fred + document:doc-253#viewer1@user:fred + document:doc-254#viewer1@user:fred + document:doc-255#viewer1@user:fred + document:doc-256#viewer1@user:fred + document:doc-257#viewer1@user:fred + document:doc-258#viewer1@user:fred + document:doc-259#viewer1@user:fred + document:doc-260#viewer1@user:fred + document:doc-261#viewer1@user:fred + document:doc-262#viewer1@user:fred + document:doc-263#viewer1@user:fred + document:doc-264#viewer1@user:fred + document:doc-265#viewer1@user:fred + document:doc-266#viewer1@user:fred + document:doc-267#viewer1@user:fred + document:doc-268#viewer1@user:fred + document:doc-269#viewer1@user:fred + document:doc-270#viewer1@user:fred + document:doc-271#viewer1@user:fred + document:doc-272#viewer1@user:fred + document:doc-273#viewer1@user:fred + document:doc-274#viewer1@user:fred + document:doc-275#viewer1@user:fred + document:doc-276#viewer1@user:fred + document:doc-277#viewer1@user:fred + document:doc-278#viewer1@user:fred + document:doc-279#viewer1@user:fred + document:doc-280#viewer1@user:fred + document:doc-281#viewer1@user:fred + document:doc-282#viewer1@user:fred + document:doc-283#viewer1@user:fred + document:doc-284#viewer1@user:fred + document:doc-285#viewer1@user:fred + document:doc-286#viewer1@user:fred + document:doc-287#viewer1@user:fred + document:doc-288#viewer1@user:fred + document:doc-289#viewer1@user:fred + document:doc-290#viewer1@user:fred + document:doc-291#viewer1@user:fred + document:doc-292#viewer1@user:fred + document:doc-293#viewer1@user:fred + document:doc-294#viewer1@user:fred + document:doc-295#viewer1@user:fred + document:doc-296#viewer1@user:fred + document:doc-297#viewer1@user:fred + document:doc-298#viewer1@user:fred + document:doc-299#viewer1@user:fred + + // user:fred as viewer2 for 300 documents, starting at doc 150 + // for docid in range(150, 450): + // document:doc-{docid}#viewer2@user:fred + document:doc-150#viewer2@user:fred + document:doc-151#viewer2@user:fred + document:doc-152#viewer2@user:fred + document:doc-153#viewer2@user:fred + document:doc-154#viewer2@user:fred + document:doc-155#viewer2@user:fred + document:doc-156#viewer2@user:fred + document:doc-157#viewer2@user:fred + document:doc-158#viewer2@user:fred + document:doc-159#viewer2@user:fred + document:doc-160#viewer2@user:fred + document:doc-161#viewer2@user:fred + document:doc-162#viewer2@user:fred + document:doc-163#viewer2@user:fred + document:doc-164#viewer2@user:fred + document:doc-165#viewer2@user:fred + document:doc-166#viewer2@user:fred + document:doc-167#viewer2@user:fred + document:doc-168#viewer2@user:fred + document:doc-169#viewer2@user:fred + document:doc-170#viewer2@user:fred + document:doc-171#viewer2@user:fred + document:doc-172#viewer2@user:fred + document:doc-173#viewer2@user:fred + document:doc-174#viewer2@user:fred + document:doc-175#viewer2@user:fred + document:doc-176#viewer2@user:fred + document:doc-177#viewer2@user:fred + document:doc-178#viewer2@user:fred + document:doc-179#viewer2@user:fred + document:doc-180#viewer2@user:fred + document:doc-181#viewer2@user:fred + document:doc-182#viewer2@user:fred + document:doc-183#viewer2@user:fred + document:doc-184#viewer2@user:fred + document:doc-185#viewer2@user:fred + document:doc-186#viewer2@user:fred + document:doc-187#viewer2@user:fred + document:doc-188#viewer2@user:fred + document:doc-189#viewer2@user:fred + document:doc-190#viewer2@user:fred + document:doc-191#viewer2@user:fred + document:doc-192#viewer2@user:fred + document:doc-193#viewer2@user:fred + document:doc-194#viewer2@user:fred + document:doc-195#viewer2@user:fred + document:doc-196#viewer2@user:fred + document:doc-197#viewer2@user:fred + document:doc-198#viewer2@user:fred + document:doc-199#viewer2@user:fred + document:doc-200#viewer2@user:fred + document:doc-201#viewer2@user:fred + document:doc-202#viewer2@user:fred + document:doc-203#viewer2@user:fred + document:doc-204#viewer2@user:fred + document:doc-205#viewer2@user:fred + document:doc-206#viewer2@user:fred + document:doc-207#viewer2@user:fred + document:doc-208#viewer2@user:fred + document:doc-209#viewer2@user:fred + document:doc-210#viewer2@user:fred + document:doc-211#viewer2@user:fred + document:doc-212#viewer2@user:fred + document:doc-213#viewer2@user:fred + document:doc-214#viewer2@user:fred + document:doc-215#viewer2@user:fred + document:doc-216#viewer2@user:fred + document:doc-217#viewer2@user:fred + document:doc-218#viewer2@user:fred + document:doc-219#viewer2@user:fred + document:doc-220#viewer2@user:fred + document:doc-221#viewer2@user:fred + document:doc-222#viewer2@user:fred + document:doc-223#viewer2@user:fred + document:doc-224#viewer2@user:fred + document:doc-225#viewer2@user:fred + document:doc-226#viewer2@user:fred + document:doc-227#viewer2@user:fred + document:doc-228#viewer2@user:fred + document:doc-229#viewer2@user:fred + document:doc-230#viewer2@user:fred + document:doc-231#viewer2@user:fred + document:doc-232#viewer2@user:fred + document:doc-233#viewer2@user:fred + document:doc-234#viewer2@user:fred + document:doc-235#viewer2@user:fred + document:doc-236#viewer2@user:fred + document:doc-237#viewer2@user:fred + document:doc-238#viewer2@user:fred + document:doc-239#viewer2@user:fred + document:doc-240#viewer2@user:fred + document:doc-241#viewer2@user:fred + document:doc-242#viewer2@user:fred + document:doc-243#viewer2@user:fred + document:doc-244#viewer2@user:fred + document:doc-245#viewer2@user:fred + document:doc-246#viewer2@user:fred + document:doc-247#viewer2@user:fred + document:doc-248#viewer2@user:fred + document:doc-249#viewer2@user:fred + document:doc-250#viewer2@user:fred + document:doc-251#viewer2@user:fred + document:doc-252#viewer2@user:fred + document:doc-253#viewer2@user:fred + document:doc-254#viewer2@user:fred + document:doc-255#viewer2@user:fred + document:doc-256#viewer2@user:fred + document:doc-257#viewer2@user:fred + document:doc-258#viewer2@user:fred + document:doc-259#viewer2@user:fred + document:doc-260#viewer2@user:fred + document:doc-261#viewer2@user:fred + document:doc-262#viewer2@user:fred + document:doc-263#viewer2@user:fred + document:doc-264#viewer2@user:fred + document:doc-265#viewer2@user:fred + document:doc-266#viewer2@user:fred + document:doc-267#viewer2@user:fred + document:doc-268#viewer2@user:fred + document:doc-269#viewer2@user:fred + document:doc-270#viewer2@user:fred + document:doc-271#viewer2@user:fred + document:doc-272#viewer2@user:fred + document:doc-273#viewer2@user:fred + document:doc-274#viewer2@user:fred + document:doc-275#viewer2@user:fred + document:doc-276#viewer2@user:fred + document:doc-277#viewer2@user:fred + document:doc-278#viewer2@user:fred + document:doc-279#viewer2@user:fred + document:doc-280#viewer2@user:fred + document:doc-281#viewer2@user:fred + document:doc-282#viewer2@user:fred + document:doc-283#viewer2@user:fred + document:doc-284#viewer2@user:fred + document:doc-285#viewer2@user:fred + document:doc-286#viewer2@user:fred + document:doc-287#viewer2@user:fred + document:doc-288#viewer2@user:fred + document:doc-289#viewer2@user:fred + document:doc-290#viewer2@user:fred + document:doc-291#viewer2@user:fred + document:doc-292#viewer2@user:fred + document:doc-293#viewer2@user:fred + document:doc-294#viewer2@user:fred + document:doc-295#viewer2@user:fred + document:doc-296#viewer2@user:fred + document:doc-297#viewer2@user:fred + document:doc-298#viewer2@user:fred + document:doc-299#viewer2@user:fred + document:doc-300#viewer2@user:fred + document:doc-301#viewer2@user:fred + document:doc-302#viewer2@user:fred + document:doc-303#viewer2@user:fred + document:doc-304#viewer2@user:fred + document:doc-305#viewer2@user:fred + document:doc-306#viewer2@user:fred + document:doc-307#viewer2@user:fred + document:doc-308#viewer2@user:fred + document:doc-309#viewer2@user:fred + document:doc-310#viewer2@user:fred + document:doc-311#viewer2@user:fred + document:doc-312#viewer2@user:fred + document:doc-313#viewer2@user:fred + document:doc-314#viewer2@user:fred + document:doc-315#viewer2@user:fred + document:doc-316#viewer2@user:fred + document:doc-317#viewer2@user:fred + document:doc-318#viewer2@user:fred + document:doc-319#viewer2@user:fred + document:doc-320#viewer2@user:fred + document:doc-321#viewer2@user:fred + document:doc-322#viewer2@user:fred + document:doc-323#viewer2@user:fred + document:doc-324#viewer2@user:fred + document:doc-325#viewer2@user:fred + document:doc-326#viewer2@user:fred + document:doc-327#viewer2@user:fred + document:doc-328#viewer2@user:fred + document:doc-329#viewer2@user:fred + document:doc-330#viewer2@user:fred + document:doc-331#viewer2@user:fred + document:doc-332#viewer2@user:fred + document:doc-333#viewer2@user:fred + document:doc-334#viewer2@user:fred + document:doc-335#viewer2@user:fred + document:doc-336#viewer2@user:fred + document:doc-337#viewer2@user:fred + document:doc-338#viewer2@user:fred + document:doc-339#viewer2@user:fred + document:doc-340#viewer2@user:fred + document:doc-341#viewer2@user:fred + document:doc-342#viewer2@user:fred + document:doc-343#viewer2@user:fred + document:doc-344#viewer2@user:fred + document:doc-345#viewer2@user:fred + document:doc-346#viewer2@user:fred + document:doc-347#viewer2@user:fred + document:doc-348#viewer2@user:fred + document:doc-349#viewer2@user:fred + document:doc-350#viewer2@user:fred + document:doc-351#viewer2@user:fred + document:doc-352#viewer2@user:fred + document:doc-353#viewer2@user:fred + document:doc-354#viewer2@user:fred + document:doc-355#viewer2@user:fred + document:doc-356#viewer2@user:fred + document:doc-357#viewer2@user:fred + document:doc-358#viewer2@user:fred + document:doc-359#viewer2@user:fred + document:doc-360#viewer2@user:fred + document:doc-361#viewer2@user:fred + document:doc-362#viewer2@user:fred + document:doc-363#viewer2@user:fred + document:doc-364#viewer2@user:fred + document:doc-365#viewer2@user:fred + document:doc-366#viewer2@user:fred + document:doc-367#viewer2@user:fred + document:doc-368#viewer2@user:fred + document:doc-369#viewer2@user:fred + document:doc-370#viewer2@user:fred + document:doc-371#viewer2@user:fred + document:doc-372#viewer2@user:fred + document:doc-373#viewer2@user:fred + document:doc-374#viewer2@user:fred + document:doc-375#viewer2@user:fred + document:doc-376#viewer2@user:fred + document:doc-377#viewer2@user:fred + document:doc-378#viewer2@user:fred + document:doc-379#viewer2@user:fred + document:doc-380#viewer2@user:fred + document:doc-381#viewer2@user:fred + document:doc-382#viewer2@user:fred + document:doc-383#viewer2@user:fred + document:doc-384#viewer2@user:fred + document:doc-385#viewer2@user:fred + document:doc-386#viewer2@user:fred + document:doc-387#viewer2@user:fred + document:doc-388#viewer2@user:fred + document:doc-389#viewer2@user:fred + document:doc-390#viewer2@user:fred + document:doc-391#viewer2@user:fred + document:doc-392#viewer2@user:fred + document:doc-393#viewer2@user:fred + document:doc-394#viewer2@user:fred + document:doc-395#viewer2@user:fred + document:doc-396#viewer2@user:fred + document:doc-397#viewer2@user:fred + document:doc-398#viewer2@user:fred + document:doc-399#viewer2@user:fred + document:doc-400#viewer2@user:fred + document:doc-401#viewer2@user:fred + document:doc-402#viewer2@user:fred + document:doc-403#viewer2@user:fred + document:doc-404#viewer2@user:fred + document:doc-405#viewer2@user:fred + document:doc-406#viewer2@user:fred + document:doc-407#viewer2@user:fred + document:doc-408#viewer2@user:fred + document:doc-409#viewer2@user:fred + document:doc-410#viewer2@user:fred + document:doc-411#viewer2@user:fred + document:doc-412#viewer2@user:fred + document:doc-413#viewer2@user:fred + document:doc-414#viewer2@user:fred + document:doc-415#viewer2@user:fred + document:doc-416#viewer2@user:fred + document:doc-417#viewer2@user:fred + document:doc-418#viewer2@user:fred + document:doc-419#viewer2@user:fred + document:doc-420#viewer2@user:fred + document:doc-421#viewer2@user:fred + document:doc-422#viewer2@user:fred + document:doc-423#viewer2@user:fred + document:doc-424#viewer2@user:fred + document:doc-425#viewer2@user:fred + document:doc-426#viewer2@user:fred + document:doc-427#viewer2@user:fred + document:doc-428#viewer2@user:fred + document:doc-429#viewer2@user:fred + document:doc-430#viewer2@user:fred + document:doc-431#viewer2@user:fred + document:doc-432#viewer2@user:fred + document:doc-433#viewer2@user:fred + document:doc-434#viewer2@user:fred + document:doc-435#viewer2@user:fred + document:doc-436#viewer2@user:fred + document:doc-437#viewer2@user:fred + document:doc-438#viewer2@user:fred + document:doc-439#viewer2@user:fred + document:doc-440#viewer2@user:fred + document:doc-441#viewer2@user:fred + document:doc-442#viewer2@user:fred + document:doc-443#viewer2@user:fred + document:doc-444#viewer2@user:fred + document:doc-445#viewer2@user:fred + document:doc-446#viewer2@user:fred + document:doc-447#viewer2@user:fred + document:doc-448#viewer2@user:fred + document:doc-449#viewer2@user:fred diff --git a/internal/services/steelthreadtesting/testdata/document-with-many-resources.yaml b/internal/services/steelthreadtesting/testdata/document-with-many-resources.yaml index 1a2f344cae..05a54c4e01 100644 --- a/internal/services/steelthreadtesting/testdata/document-with-many-resources.yaml +++ b/internal/services/steelthreadtesting/testdata/document-with-many-resources.yaml @@ -17,6 +17,7 @@ schema: |+ permission view = (viewer - banned) + edit permission vsb = viewer - banned permission indirect_view = vsb + edit + permission vsb_plus_nil = vsb + nil } relationships: | diff --git a/internal/services/v1/debug_test.go b/internal/services/v1/debug_test.go index dea12bb669..0b68f95295 100644 --- a/internal/services/v1/debug_test.go +++ b/internal/services/v1/debug_test.go @@ -402,7 +402,7 @@ func TestCheckPermissionWithDebug(t *testing.T) { }, v1.CheckPermissionResponse_PERMISSIONSHIP_NO_PERMISSION, 1, - []rda{expectDebugFrames("member"), expectCaveat(`anothercondition == "hello world" && somecondition == 42`)}, + []rda{expectDebugFrames("member"), expectCaveat(`somecondition == 42`)}, }, { "sarah as partially conditional viewer", @@ -433,7 +433,7 @@ func TestCheckPermissionWithDebug(t *testing.T) { 1, []rda{ expectDebugFrames("member"), - expectMissingContext("anothercondition"), + expectMissingContext("anothercondition", "somecondition"), }, }, }, diff --git a/internal/services/v1/permissions.go b/internal/services/v1/permissions.go index 97c4a08b60..c29d0e7678 100644 --- a/internal/services/v1/permissions.go +++ b/internal/services/v1/permissions.go @@ -376,6 +376,14 @@ func TranslateExpansionTree(node *core.RelationTupleTreeNode) *v1.PermissionRela } func (ps *permissionServer) LookupResources(req *v1.LookupResourcesRequest, resp v1.PermissionsService_LookupResourcesServer) error { + if ps.config.UseExperimentalLookupResources2 { + return ps.lookupResources2(req, resp) + } + + return ps.lookupResources1(req, resp) +} + +func (ps *permissionServer) lookupResources1(req *v1.LookupResourcesRequest, resp v1.PermissionsService_LookupResourcesServer) error { if req.OptionalLimit > 0 && req.OptionalLimit > ps.config.MaxLookupResourcesLimit { return ps.rewriteError(resp.Context(), NewExceedsMaximumLimitErr(uint64(req.OptionalLimit), uint64(ps.config.MaxLookupResourcesLimit))) } @@ -503,6 +511,140 @@ func (ps *permissionServer) LookupResources(req *v1.LookupResourcesRequest, resp return nil } +func (ps *permissionServer) lookupResources2(req *v1.LookupResourcesRequest, resp v1.PermissionsService_LookupResourcesServer) error { + if req.OptionalLimit > 0 && req.OptionalLimit > ps.config.MaxLookupResourcesLimit { + return ps.rewriteError(resp.Context(), NewExceedsMaximumLimitErr(uint64(req.OptionalLimit), uint64(ps.config.MaxLookupResourcesLimit))) + } + + ctx := resp.Context() + + atRevision, revisionReadAt, err := consistency.RevisionFromContext(ctx) + if err != nil { + return ps.rewriteError(ctx, err) + } + + ds := datastoremw.MustFromContext(ctx).SnapshotReader(atRevision) + + if err := namespace.CheckNamespaceAndRelations(ctx, + []namespace.TypeAndRelationToCheck{ + { + NamespaceName: req.ResourceObjectType, + RelationName: req.Permission, + AllowEllipsis: false, + }, + { + NamespaceName: req.Subject.Object.ObjectType, + RelationName: normalizeSubjectRelation(req.Subject), + AllowEllipsis: true, + }, + }, ds); err != nil { + return ps.rewriteError(ctx, err) + } + + respMetadata := &dispatch.ResponseMeta{ + DispatchCount: 1, + CachedDispatchCount: 0, + DepthRequired: 1, + DebugInfo: nil, + } + usagemetrics.SetInContext(ctx, respMetadata) + + var currentCursor *dispatch.Cursor + + lrRequestHash, err := computeLRRequestHash(req) + if err != nil { + return ps.rewriteError(ctx, err) + } + + if req.OptionalCursor != nil { + decodedCursor, err := cursor.DecodeToDispatchCursor(req.OptionalCursor, lrRequestHash) + if err != nil { + return ps.rewriteError(ctx, err) + } + currentCursor = decodedCursor + } + + alreadyPublishedPermissionedResourceIds := map[string]struct{}{} + + stream := dispatchpkg.NewHandlingDispatchStream(ctx, func(result *dispatch.DispatchLookupResources2Response) error { + found := result.Resource + + dispatchpkg.AddResponseMetadata(respMetadata, result.Metadata) + currentCursor = result.AfterResponseCursor + + var partial *v1.PartialCaveatInfo + permissionship := v1.LookupPermissionship_LOOKUP_PERMISSIONSHIP_HAS_PERMISSION + if len(found.MissingContextParams) > 0 { + permissionship = v1.LookupPermissionship_LOOKUP_PERMISSIONSHIP_CONDITIONAL_PERMISSION + partial = &v1.PartialCaveatInfo{ + MissingRequiredContext: found.MissingContextParams, + } + } else if req.OptionalLimit == 0 { + if _, ok := alreadyPublishedPermissionedResourceIds[found.ResourceId]; ok { + // Skip publishing the duplicate. + return nil + } + + // TODO(jschorr): Investigate something like a Trie here for better memory efficiency. + alreadyPublishedPermissionedResourceIds[found.ResourceId] = struct{}{} + } + + encodedCursor, err := cursor.EncodeFromDispatchCursor(result.AfterResponseCursor, lrRequestHash, atRevision) + if err != nil { + return ps.rewriteError(ctx, err) + } + + err = resp.Send(&v1.LookupResourcesResponse{ + LookedUpAt: revisionReadAt, + ResourceObjectId: found.ResourceId, + Permissionship: permissionship, + PartialCaveatInfo: partial, + AfterResultCursor: encodedCursor, + }) + if err != nil { + return err + } + return nil + }) + + bf, err := dispatch.NewTraversalBloomFilter(uint(ps.config.MaximumAPIDepth)) + if err != nil { + return err + } + + err = ps.dispatch.DispatchLookupResources2( + &dispatch.DispatchLookupResources2Request{ + Metadata: &dispatch.ResolverMeta{ + AtRevision: atRevision.String(), + DepthRemaining: ps.config.MaximumAPIDepth, + TraversalBloom: bf, + }, + ResourceRelation: &core.RelationReference{ + Namespace: req.ResourceObjectType, + Relation: req.Permission, + }, + SubjectRelation: &core.RelationReference{ + Namespace: req.Subject.Object.ObjectType, + Relation: normalizeSubjectRelation(req.Subject), + }, + SubjectIds: []string{req.Subject.Object.ObjectId}, + TerminalSubject: &core.ObjectAndRelation{ + Namespace: req.Subject.Object.ObjectType, + ObjectId: req.Subject.Object.ObjectId, + Relation: normalizeSubjectRelation(req.Subject), + }, + Context: req.Context, + OptionalCursor: currentCursor, + OptionalLimit: req.OptionalLimit, + }, + stream) + if err != nil { + return ps.rewriteError(ctx, err) + } + + return nil +} + func (ps *permissionServer) LookupSubjects(req *v1.LookupSubjectsRequest, resp v1.PermissionsService_LookupSubjectsServer) error { ctx := resp.Context() diff --git a/internal/services/v1/permissions_test.go b/internal/services/v1/permissions_test.go index 4702cd7127..e04445044d 100644 --- a/internal/services/v1/permissions_test.go +++ b/internal/services/v1/permissions_test.go @@ -556,7 +556,7 @@ func TestLookupResources(t *testing.T) { []string{"specialplan"}, codes.OK, 6, - 7, + 8, }, { "document", "view_and_edit", @@ -622,53 +622,71 @@ func TestLookupResources(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(fmt.Sprintf("%s::%s from %s:%s#%s", tc.objectType, tc.permission, tc.subject.Object.ObjectType, tc.subject.Object.ObjectId, tc.subject.OptionalRelation), func(t *testing.T) { - require := require.New(t) - conn, cleanup, _, revision := testserver.NewTestServer(require, delta, memdb.DisableGC, true, tf.StandardDatastoreWithData) - client := v1.NewPermissionsServiceClient(conn) - t.Cleanup(func() { - goleak.VerifyNone(t, goleak.IgnoreCurrent()) - }) - t.Cleanup(cleanup) - - var trailer metadata.MD - lookupClient, err := client.LookupResources(context.Background(), &v1.LookupResourcesRequest{ - ResourceObjectType: tc.objectType, - Permission: tc.permission, - Subject: tc.subject, - Consistency: &v1.Consistency{ - Requirement: &v1.Consistency_AtLeastAsFresh{ - AtLeastAsFresh: zedtoken.MustNewFromRevision(revision), - }, - }, - }, grpc.Trailer(&trailer)) + for _, useV2 := range []bool{false, true} { + useV2 := useV2 + t.Run(fmt.Sprintf("v2:%v", useV2), func(t *testing.T) { + require := require.New(t) + conn, cleanup, _, revision := testserver.NewTestServerWithConfig( + require, + delta, + memdb.DisableGC, + true, + testserver.ServerConfig{ + MaxUpdatesPerWrite: 1000, + MaxPreconditionsCount: 1000, + StreamingAPITimeout: 30 * time.Second, + MaxRelationshipContextSize: 25000, + UseExperimentalLookupResources2: useV2, + }, + tf.StandardDatastoreWithData, + ) + client := v1.NewPermissionsServiceClient(conn) + t.Cleanup(func() { + goleak.VerifyNone(t, goleak.IgnoreCurrent()) + }) + t.Cleanup(cleanup) - require.NoError(err) - if tc.expectedErrorCode == codes.OK { - var resolvedObjectIds []string - for { - resp, err := lookupClient.Recv() - if errors.Is(err, io.EOF) { - break - } + var trailer metadata.MD + lookupClient, err := client.LookupResources(context.Background(), &v1.LookupResourcesRequest{ + ResourceObjectType: tc.objectType, + Permission: tc.permission, + Subject: tc.subject, + Consistency: &v1.Consistency{ + Requirement: &v1.Consistency_AtLeastAsFresh{ + AtLeastAsFresh: zedtoken.MustNewFromRevision(revision), + }, + }, + }, grpc.Trailer(&trailer)) require.NoError(err) + if tc.expectedErrorCode == codes.OK { + var resolvedObjectIds []string + for { + resp, err := lookupClient.Recv() + if errors.Is(err, io.EOF) { + break + } - resolvedObjectIds = append(resolvedObjectIds, resp.ResourceObjectId) - } + require.NoError(err) - slices.Sort(tc.expectedObjectIds) - slices.Sort(resolvedObjectIds) + resolvedObjectIds = append(resolvedObjectIds, resp.ResourceObjectId) + } - require.Equal(tc.expectedObjectIds, resolvedObjectIds) + slices.Sort(tc.expectedObjectIds) + slices.Sort(resolvedObjectIds) - dispatchCount, err := responsemeta.GetIntResponseTrailerMetadata(trailer, responsemeta.DispatchedOperationsCount) - require.NoError(err) - require.GreaterOrEqual(dispatchCount, 0) - require.LessOrEqual(dispatchCount, tc.maximumDispatchCount) - require.GreaterOrEqual(dispatchCount, tc.minimumDispatchCount) - } else { - _, err := lookupClient.Recv() - grpcutil.RequireStatus(t, tc.expectedErrorCode, err) + require.Equal(tc.expectedObjectIds, resolvedObjectIds) + + dispatchCount, err := responsemeta.GetIntResponseTrailerMetadata(trailer, responsemeta.DispatchedOperationsCount) + require.NoError(err) + require.GreaterOrEqual(dispatchCount, 0) + require.LessOrEqual(dispatchCount, tc.maximumDispatchCount) + require.GreaterOrEqual(dispatchCount, tc.minimumDispatchCount) + } else { + _, err := lookupClient.Recv() + grpcutil.RequireStatus(t, tc.expectedErrorCode, err) + } + }) } }) } diff --git a/internal/services/v1/relationships.go b/internal/services/v1/relationships.go index 4f77c0fa11..0be7131154 100644 --- a/internal/services/v1/relationships.go +++ b/internal/services/v1/relationships.go @@ -89,6 +89,9 @@ type PermissionsServerConfig struct { // MaxBulkExportRelationshipsLimit defines the maximum number of relationships that can be // exported in a single BulkExportRelationships call. MaxBulkExportRelationshipsLimit uint32 + + // UseExperimentalLookupResources2 enables the experimental LookupResources2 API. + UseExperimentalLookupResources2 bool } // NewPermissionsServer creates a PermissionsServiceServer instance. @@ -108,6 +111,7 @@ func NewPermissionsServer( MaxDeleteRelationshipsLimit: defaultIfZero(config.MaxDeleteRelationshipsLimit, 1_000), MaxLookupResourcesLimit: defaultIfZero(config.MaxLookupResourcesLimit, 1_000), MaxBulkExportRelationshipsLimit: defaultIfZero(config.MaxBulkExportRelationshipsLimit, 100_000), + UseExperimentalLookupResources2: config.UseExperimentalLookupResources2, } return &permissionServer{ diff --git a/internal/testserver/cluster.go b/internal/testserver/cluster.go index 49265c944f..da08cfc628 100644 --- a/internal/testserver/cluster.go +++ b/internal/testserver/cluster.go @@ -137,12 +137,12 @@ func (r *SafeManualResolver) Close() {} // TestClusterWithDispatch creates a cluster with `size` nodes // The cluster has a real dispatch stack that uses bufconn grpc connections -func TestClusterWithDispatch(t testing.TB, size uint, ds datastore.Datastore) ([]*grpc.ClientConn, func()) { - return TestClusterWithDispatchAndCacheConfig(t, size, ds) +func TestClusterWithDispatch(t testing.TB, size uint, ds datastore.Datastore, additionalServerOptions ...server.ConfigOption) ([]*grpc.ClientConn, func()) { + return TestClusterWithDispatchAndCacheConfig(t, size, ds, additionalServerOptions...) } // TestClusterWithDispatchAndCacheConfig creates a cluster with `size` nodes and with cache toggled. -func TestClusterWithDispatchAndCacheConfig(t testing.TB, size uint, ds datastore.Datastore) ([]*grpc.ClientConn, func()) { +func TestClusterWithDispatchAndCacheConfig(t testing.TB, size uint, ds datastore.Datastore, additionalServerOptions ...server.ConfigOption) ([]*grpc.ClientConn, func()) { // each cluster gets a unique prefix since grpc resolution is process-global prefix := getPrefix(t) @@ -212,6 +212,7 @@ func TestClusterWithDispatchAndCacheConfig(t testing.TB, size uint, ds datastore }), server.WithDispatchClusterMetricsPrefix(fmt.Sprintf("%s_%d_dispatch", prefix, i)), } + serverOptions = append(serverOptions, additionalServerOptions...) ctx, cancel := context.WithCancel(context.Background()) srv, err := server.NewConfigWithOptions(serverOptions...).Complete(ctx) diff --git a/internal/testserver/server.go b/internal/testserver/server.go index 5ac971353b..547288e2f3 100644 --- a/internal/testserver/server.go +++ b/internal/testserver/server.go @@ -21,17 +21,19 @@ import ( // ServerConfig is configuration for the test server. type ServerConfig struct { - MaxUpdatesPerWrite uint16 - MaxPreconditionsCount uint16 - MaxRelationshipContextSize int - StreamingAPITimeout time.Duration + MaxUpdatesPerWrite uint16 + MaxPreconditionsCount uint16 + MaxRelationshipContextSize int + StreamingAPITimeout time.Duration + UseExperimentalLookupResources2 bool } var DefaultTestServerConfig = ServerConfig{ - MaxUpdatesPerWrite: 1000, - MaxPreconditionsCount: 1000, - StreamingAPITimeout: 30 * time.Second, - MaxRelationshipContextSize: 25000, + MaxUpdatesPerWrite: 1000, + MaxPreconditionsCount: 1000, + StreamingAPITimeout: 30 * time.Second, + MaxRelationshipContextSize: 25000, + UseExperimentalLookupResources2: false, } // NewTestServer creates a new test server, using defaults for the config. @@ -90,6 +92,7 @@ func NewTestServerWithConfigAndDatastore(require *require.Assertions, server.WithHTTPGateway(util.HTTPServerConfig{HTTPEnabled: false}), server.WithMetricsAPI(util.HTTPServerConfig{HTTPEnabled: false}), server.WithDispatchServer(util.GRPCServerConfig{Enabled: false}), + server.WithEnableExperimentalLookupResources(config.UseExperimentalLookupResources2), server.SetUnaryMiddlewareModification([]server.MiddlewareModification[grpc.UnaryServerInterceptor]{ { Operation: server.OperationReplaceAllUnsafe, diff --git a/pkg/cmd/serve.go b/pkg/cmd/serve.go index f0dbc2cc5d..bfc705260c 100644 --- a/pkg/cmd/serve.go +++ b/pkg/cmd/serve.go @@ -137,6 +137,7 @@ func RegisterServeFlags(cmd *cobra.Command, config *server.Config) error { cmd.Flags().Uint32Var(&config.MaxDeleteRelationshipsLimit, "max-delete-relationships-limit", 1000, "maximum number of relationships that can be deleted in a single request") cmd.Flags().Uint32Var(&config.MaxLookupResourcesLimit, "max-lookup-resources-limit", 1000, "maximum number of resources that can be looked up in a single request") cmd.Flags().Uint32Var(&config.MaxBulkExportRelationshipsLimit, "max-bulk-export-relationships-limit", 10_000, "maximum number of relationships that can be exported in a single request") + cmd.Flags().BoolVar(&config.EnableExperimentalLookupResources, "enable-experimental-lookup-resources", false, "enables the experimental version of the lookup resources API") cmd.Flags().BoolVar(&config.V1SchemaAdditiveOnly, "testing-only-schema-additive-writes", false, "append new definitions to the existing schema, rather than overwriting it") if err := cmd.Flags().MarkHidden("testing-only-schema-additive-writes"); err != nil { diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index c471592676..3abf6049f0 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -104,17 +104,18 @@ type Config struct { ClusterDispatchCacheConfig CacheConfig `debugmap:"visible"` // API Behavior - DisableV1SchemaAPI bool `debugmap:"visible"` - V1SchemaAdditiveOnly bool `debugmap:"visible"` - MaximumUpdatesPerWrite uint16 `debugmap:"visible"` - MaximumPreconditionCount uint16 `debugmap:"visible"` - MaxDatastoreReadPageSize uint64 `debugmap:"visible"` - StreamingAPITimeout time.Duration `debugmap:"visible"` - WatchHeartbeat time.Duration `debugmap:"visible"` - MaxReadRelationshipsLimit uint32 `debugmap:"visible"` - MaxDeleteRelationshipsLimit uint32 `debugmap:"visible"` - MaxLookupResourcesLimit uint32 `debugmap:"visible"` - MaxBulkExportRelationshipsLimit uint32 `debugmap:"visible"` + DisableV1SchemaAPI bool `debugmap:"visible"` + V1SchemaAdditiveOnly bool `debugmap:"visible"` + MaximumUpdatesPerWrite uint16 `debugmap:"visible"` + MaximumPreconditionCount uint16 `debugmap:"visible"` + MaxDatastoreReadPageSize uint64 `debugmap:"visible"` + StreamingAPITimeout time.Duration `debugmap:"visible"` + WatchHeartbeat time.Duration `debugmap:"visible"` + MaxReadRelationshipsLimit uint32 `debugmap:"visible"` + MaxDeleteRelationshipsLimit uint32 `debugmap:"visible"` + MaxLookupResourcesLimit uint32 `debugmap:"visible"` + MaxBulkExportRelationshipsLimit uint32 `debugmap:"visible"` + EnableExperimentalLookupResources bool `debugmap:"visible"` // Additional Services MetricsAPI util.HTTPServerConfig `debugmap:"visible"` @@ -417,6 +418,7 @@ func (c *Config) Complete(ctx context.Context) (RunnableServer, error) { MaxDeleteRelationshipsLimit: c.MaxDeleteRelationshipsLimit, MaxLookupResourcesLimit: c.MaxLookupResourcesLimit, MaxBulkExportRelationshipsLimit: c.MaxBulkExportRelationshipsLimit, + UseExperimentalLookupResources2: c.EnableExperimentalLookupResources, } healthManager := health.NewHealthManager(dispatcher, ds) diff --git a/pkg/cmd/server/zz_generated.options.go b/pkg/cmd/server/zz_generated.options.go index 801978c731..b367c97fc2 100644 --- a/pkg/cmd/server/zz_generated.options.go +++ b/pkg/cmd/server/zz_generated.options.go @@ -85,6 +85,7 @@ func (c *Config) ToOption() ConfigOption { to.MaxDeleteRelationshipsLimit = c.MaxDeleteRelationshipsLimit to.MaxLookupResourcesLimit = c.MaxLookupResourcesLimit to.MaxBulkExportRelationshipsLimit = c.MaxBulkExportRelationshipsLimit + to.EnableExperimentalLookupResources = c.EnableExperimentalLookupResources to.MetricsAPI = c.MetricsAPI to.UnaryMiddlewareModification = c.UnaryMiddlewareModification to.StreamingMiddlewareModification = c.StreamingMiddlewareModification @@ -150,6 +151,7 @@ func (c Config) DebugMap() map[string]any { debugMap["MaxDeleteRelationshipsLimit"] = helpers.DebugValue(c.MaxDeleteRelationshipsLimit, false) debugMap["MaxLookupResourcesLimit"] = helpers.DebugValue(c.MaxLookupResourcesLimit, false) debugMap["MaxBulkExportRelationshipsLimit"] = helpers.DebugValue(c.MaxBulkExportRelationshipsLimit, false) + debugMap["EnableExperimentalLookupResources"] = helpers.DebugValue(c.EnableExperimentalLookupResources, false) debugMap["MetricsAPI"] = helpers.DebugValue(c.MetricsAPI, false) debugMap["SilentlyDisableTelemetry"] = helpers.DebugValue(c.SilentlyDisableTelemetry, false) debugMap["TelemetryCAOverridePath"] = helpers.DebugValue(c.TelemetryCAOverridePath, false) @@ -534,6 +536,13 @@ func WithMaxBulkExportRelationshipsLimit(maxBulkExportRelationshipsLimit uint32) } } +// WithEnableExperimentalLookupResources returns an option that can set EnableExperimentalLookupResources on a Config +func WithEnableExperimentalLookupResources(enableExperimentalLookupResources bool) ConfigOption { + return func(c *Config) { + c.EnableExperimentalLookupResources = enableExperimentalLookupResources + } +} + // WithMetricsAPI returns an option that can set MetricsAPI on a Config func WithMetricsAPI(metricsAPI util.HTTPServerConfig) ConfigOption { return func(c *Config) { diff --git a/pkg/proto/core/v1/core.pb.go b/pkg/proto/core/v1/core.pb.go index 2ea1d3550c..35f49d65be 100644 --- a/pkg/proto/core/v1/core.pb.go +++ b/pkg/proto/core/v1/core.pb.go @@ -1549,6 +1549,10 @@ type ReachabilityEntrypoint struct { // tupleset_relation is the name of the tupleset relation on the TupleToUserset this entrypoint // represents, if applicable. TuplesetRelation string `protobuf:"bytes,5,opt,name=tupleset_relation,json=tuplesetRelation,proto3" json:"tupleset_relation,omitempty"` + // * + // computed_userset_relation is the name of the computed userset relation on the ComputedUserset + // this entrypoint represents, if applicable. + ComputedUsersetRelation string `protobuf:"bytes,6,opt,name=computed_userset_relation,json=computedUsersetRelation,proto3" json:"computed_userset_relation,omitempty"` } func (x *ReachabilityEntrypoint) Reset() { @@ -1611,6 +1615,13 @@ func (x *ReachabilityEntrypoint) GetTuplesetRelation() string { return "" } +func (x *ReachabilityEntrypoint) GetComputedUsersetRelation() string { + if x != nil { + return x.ComputedUsersetRelation + } + return "" +} + // * // TypeInformation defines the allowed types for a relation. type TypeInformation struct { @@ -3197,7 +3208,7 @@ var file_core_v1_core_proto_rawDesc = []byte{ 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0f, 0x73, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x92, 0x04, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xce, 0x04, 0x0a, 0x16, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x4e, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, @@ -3217,284 +3228,288 @@ var file_core_v1_core_proto_rawDesc = []byte{ 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x52, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x7a, 0x0a, 0x1a, 0x52, 0x65, 0x61, 0x63, 0x68, - 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x4c, 0x41, 0x54, 0x49, 0x4f, - 0x4e, 0x5f, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x1f, - 0x0a, 0x1b, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x45, 0x44, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x53, - 0x45, 0x54, 0x5f, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x01, 0x12, - 0x22, 0x0a, 0x1e, 0x54, 0x55, 0x50, 0x4c, 0x45, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, 0x5f, 0x55, - 0x53, 0x45, 0x52, 0x53, 0x45, 0x54, 0x5f, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x50, 0x4f, 0x49, 0x4e, - 0x54, 0x10, 0x02, 0x22, 0x57, 0x0a, 0x16, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, - 0x1c, 0x52, 0x45, 0x41, 0x43, 0x48, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x44, 0x49, - 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, - 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, - 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x01, 0x4a, 0x04, 0x08, 0x03, - 0x10, 0x04, 0x22, 0x65, 0x0a, 0x0f, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x18, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, - 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x16, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, - 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xca, 0x03, 0x0a, 0x0f, 0x41, 0x6c, - 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x66, 0x0a, - 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 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, 0x09, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x4e, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x30, 0xfa, 0x42, 0x2d, 0x72, 0x2b, 0x28, 0x40, - 0x32, 0x27, 0x5e, 0x28, 0x5c, 0x2e, 0x5c, 0x2e, 0x5c, 0x2e, 0x7c, 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, 0x24, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x0f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, - 0x77, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, - 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x57, - 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 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, 0x3f, 0x0a, 0x0f, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, - 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x52, 0x0e, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x1a, 0x10, 0x0a, 0x0e, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x42, 0x16, - 0x0a, 0x14, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x72, 0x5f, 0x77, 0x69, - 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x22, 0x30, 0x0a, 0x0d, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, - 0x64, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x61, 0x76, 0x65, 0x61, - 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x61, - 0x76, 0x65, 0x61, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xad, 0x02, 0x0a, 0x0e, 0x55, 0x73, 0x65, - 0x72, 0x73, 0x65, 0x74, 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x75, - 0x6e, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x05, 0x75, - 0x6e, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x63, 0x6f, 0x6d, 0x70, + 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x7a, 0x0a, 0x1a, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x4b, 0x69, 0x6e, + 0x64, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x4e, + 0x54, 0x52, 0x59, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x1f, 0x0a, 0x1b, 0x43, 0x4f, + 0x4d, 0x50, 0x55, 0x54, 0x45, 0x44, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x53, 0x45, 0x54, 0x5f, 0x45, + 0x4e, 0x54, 0x52, 0x59, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x22, 0x0a, 0x1e, 0x54, + 0x55, 0x50, 0x4c, 0x45, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x53, + 0x45, 0x54, 0x5f, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x02, 0x22, + 0x57, 0x0a, 0x16, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x41, + 0x43, 0x48, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, + 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x44, + 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x01, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x65, + 0x0a, 0x0f, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x52, 0x0a, 0x18, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x64, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, + 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x16, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xca, 0x03, 0x0a, 0x0f, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, + 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x66, 0x0a, 0x09, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 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, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x12, 0x4e, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x30, 0xfa, 0x42, 0x2d, 0x72, 0x2b, 0x28, 0x40, 0x32, 0x27, 0x5e, 0x28, + 0x5c, 0x2e, 0x5c, 0x2e, 0x5c, 0x2e, 0x7c, 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, 0x24, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x52, 0x0a, 0x0f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x77, 0x69, 0x6c, 0x64, + 0x63, 0x61, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x57, 0x69, 0x6c, 0x64, 0x63, + 0x61, 0x72, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x57, 0x69, 0x6c, + 0x64, 0x63, 0x61, 0x72, 0x64, 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, 0x3f, 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, + 0x65, 0x64, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x52, 0x0e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x64, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x1a, 0x10, 0x0a, 0x0e, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x42, 0x16, 0x0a, 0x14, 0x72, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x72, 0x5f, 0x77, 0x69, 0x6c, 0x64, 0x63, 0x61, + 0x72, 0x64, 0x22, 0x30, 0x0a, 0x0d, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x43, 0x61, 0x76, + 0x65, 0x61, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xad, 0x02, 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, + 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x75, 0x6e, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x05, 0x75, 0x6e, 0x69, 0x6f, 0x6e, + 0x12, 0x45, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x09, 0x65, 0x78, 0x63, 0x6c, 0x75, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x0c, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x09, 0x65, - 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, - 0x00, 0x52, 0x09, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x0f, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x09, 0x65, + 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 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, 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, 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, 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, 0x69, 0x6c, 0x64, 0x2e, + 0x54, 0x68, 0x69, 0x73, 0x48, 0x00, 0x52, 0x04, 0x54, 0x68, 0x69, 0x73, 0x12, 0x4f, 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, 0x48, 0x00, 0x52, 0x0f, 0x63, 0x6f, + 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x4d, 0x0a, + 0x10, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 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, 0x0e, 0x74, 0x75, + 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x4c, 0x0a, 0x0f, + 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 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, 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, 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, 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, + 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, 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, 0x54, 0x68, 0x69, 0x73, 0x48, 0x00, 0x52, 0x04, 0x54, 0x68, 0x69, 0x73, - 0x12, 0x4f, 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, 0x48, 0x00, - 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, - 0x74, 0x12, 0x4d, 0x0a, 0x10, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x73, - 0x65, 0x72, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 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, 0x0e, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, - 0x12, 0x4c, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x77, 0x72, - 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, 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, + 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, 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, + 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, 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, + 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, 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, + 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 ( diff --git a/pkg/proto/core/v1/core.pb.validate.go b/pkg/proto/core/v1/core.pb.validate.go index 66bdbdd411..1c5d9896ed 100644 --- a/pkg/proto/core/v1/core.pb.validate.go +++ b/pkg/proto/core/v1/core.pb.validate.go @@ -3086,6 +3086,8 @@ func (m *ReachabilityEntrypoint) validate(all bool) error { // no validation rules for TuplesetRelation + // no validation rules for ComputedUsersetRelation + if len(errors) > 0 { return ReachabilityEntrypointMultiError(errors) } diff --git a/pkg/proto/core/v1/core_vtproto.pb.go b/pkg/proto/core/v1/core_vtproto.pb.go index b3b90c601c..cb4c57d1d6 100644 --- a/pkg/proto/core/v1/core_vtproto.pb.go +++ b/pkg/proto/core/v1/core_vtproto.pb.go @@ -429,6 +429,7 @@ func (m *ReachabilityEntrypoint) CloneVT() *ReachabilityEntrypoint { r.TargetRelation = m.TargetRelation.CloneVT() r.ResultStatus = m.ResultStatus r.TuplesetRelation = m.TuplesetRelation + r.ComputedUsersetRelation = m.ComputedUsersetRelation if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -1572,6 +1573,9 @@ func (this *ReachabilityEntrypoint) EqualVT(that *ReachabilityEntrypoint) bool { if this.TuplesetRelation != that.TuplesetRelation { return false } + if this.ComputedUsersetRelation != that.ComputedUsersetRelation { + return false + } return string(this.unknownFields) == string(that.unknownFields) } @@ -3488,6 +3492,13 @@ func (m *ReachabilityEntrypoint) MarshalToSizedBufferVT(dAtA []byte) (int, error i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.ComputedUsersetRelation) > 0 { + i -= len(m.ComputedUsersetRelation) + copy(dAtA[i:], m.ComputedUsersetRelation) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ComputedUsersetRelation))) + i-- + dAtA[i] = 0x32 + } if len(m.TuplesetRelation) > 0 { i -= len(m.TuplesetRelation) copy(dAtA[i:], m.TuplesetRelation) @@ -5181,6 +5192,10 @@ func (m *ReachabilityEntrypoint) SizeVT() (n int) { if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } + l = len(m.ComputedUsersetRelation) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } n += len(m.unknownFields) return n } @@ -8534,6 +8549,38 @@ func (m *ReachabilityEntrypoint) UnmarshalVT(dAtA []byte) error { } m.TuplesetRelation = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ComputedUsersetRelation", 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.ComputedUsersetRelation = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/pkg/proto/dispatch/v1/00_zerolog.go b/pkg/proto/dispatch/v1/00_zerolog.go index be0041f0b9..cbd71671a2 100644 --- a/pkg/proto/dispatch/v1/00_zerolog.go +++ b/pkg/proto/dispatch/v1/00_zerolog.go @@ -45,6 +45,15 @@ func (lr *DispatchLookupResourcesRequest) MarshalZerologObject(e *zerolog.Event) e.Interface("context", lr.Context) } +// MarshalZerologObject implements zerolog object marshalling. +func (lr *DispatchLookupResources2Request) MarshalZerologObject(e *zerolog.Event) { + e.Object("metadata", lr.Metadata) + e.Str("resource", tuple.StringRR(lr.ResourceRelation)) + e.Str("subject", tuple.StringRR(lr.SubjectRelation)) + e.Array("subject-ids", strArray(lr.SubjectIds)) + e.Interface("context", lr.Context) +} + // MarshalZerologObject implements zerolog object marshalling. func (lr *DispatchReachableResourcesRequest) MarshalZerologObject(e *zerolog.Event) { e.Object("metadata", lr.Metadata) diff --git a/pkg/proto/dispatch/v1/dispatch.pb.go b/pkg/proto/dispatch/v1/dispatch.pb.go index 4b7ac5bddd..1e75f7dbea 100644 --- a/pkg/proto/dispatch/v1/dispatch.pb.go +++ b/pkg/proto/dispatch/v1/dispatch.pb.go @@ -168,7 +168,7 @@ func (x ResourceCheckResult_Membership) Number() protoreflect.EnumNumber { // Deprecated: Use ResourceCheckResult_Membership.Descriptor instead. func (ResourceCheckResult_Membership) EnumDescriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{2, 0} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{3, 0} } type DispatchExpandRequest_ExpansionMode int32 @@ -214,7 +214,7 @@ func (x DispatchExpandRequest_ExpansionMode) Number() protoreflect.EnumNumber { // Deprecated: Use DispatchExpandRequest_ExpansionMode.Descriptor instead. func (DispatchExpandRequest_ExpansionMode) EnumDescriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{3, 0} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{4, 0} } type ReachableResource_ResultStatus int32 @@ -266,7 +266,7 @@ func (x ReachableResource_ResultStatus) Number() protoreflect.EnumNumber { // Deprecated: Use ReachableResource_ResultStatus.Descriptor instead. func (ReachableResource_ResultStatus) EnumDescriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{7, 0} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{11, 0} } type ResolvedResource_Permissionship int32 @@ -315,7 +315,7 @@ func (x ResolvedResource_Permissionship) Number() protoreflect.EnumNumber { // Deprecated: Use ResolvedResource_Permissionship.Descriptor instead. func (ResolvedResource_Permissionship) EnumDescriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{10, 0} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{14, 0} } type CheckDebugTrace_RelationType int32 @@ -364,7 +364,7 @@ func (x CheckDebugTrace_RelationType) Number() protoreflect.EnumNumber { // Deprecated: Use CheckDebugTrace_RelationType.Descriptor instead. func (CheckDebugTrace_RelationType) EnumDescriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{19, 0} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{23, 0} } type DispatchCheckRequest struct { @@ -378,6 +378,13 @@ type DispatchCheckRequest struct { Subject *v1.ObjectAndRelation `protobuf:"bytes,4,opt,name=subject,proto3" json:"subject,omitempty"` ResultsSetting DispatchCheckRequest_ResultsSetting `protobuf:"varint,5,opt,name=results_setting,json=resultsSetting,proto3,enum=dispatch.v1.DispatchCheckRequest_ResultsSetting" json:"results_setting,omitempty"` Debug DispatchCheckRequest_DebugSetting `protobuf:"varint,6,opt,name=debug,proto3,enum=dispatch.v1.DispatchCheckRequest_DebugSetting" json:"debug,omitempty"` + // * + // check_hints are hints provided to the check call to help the resolver optimize the check + // by skipping calculations for the provided checks. The string key is the fully qualified + // "relationtuple"-string for the problem, e.g. `document:example#relation@user:someuser`. + // It is up to the caller to *ensure* that the hints provided are correct; if incorrect, + // the resolver may return incorrect results which will in turn be cached. + CheckHints []*CheckHint `protobuf:"bytes,7,rep,name=check_hints,json=checkHints,proto3" json:"check_hints,omitempty"` } func (x *DispatchCheckRequest) Reset() { @@ -454,6 +461,84 @@ func (x *DispatchCheckRequest) GetDebug() DispatchCheckRequest_DebugSetting { return DispatchCheckRequest_NO_DEBUG } +func (x *DispatchCheckRequest) GetCheckHints() []*CheckHint { + if x != nil { + return x.CheckHints + } + return nil +} + +type CheckHint struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Resource *v1.ObjectAndRelation `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` + Subject *v1.ObjectAndRelation `protobuf:"bytes,2,opt,name=subject,proto3" json:"subject,omitempty"` + TtuComputedUsersetRelation string `protobuf:"bytes,3,opt,name=ttu_computed_userset_relation,json=ttuComputedUsersetRelation,proto3" json:"ttu_computed_userset_relation,omitempty"` + Result *ResourceCheckResult `protobuf:"bytes,4,opt,name=result,proto3" json:"result,omitempty"` +} + +func (x *CheckHint) Reset() { + *x = CheckHint{} + if protoimpl.UnsafeEnabled { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CheckHint) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CheckHint) ProtoMessage() {} + +func (x *CheckHint) ProtoReflect() protoreflect.Message { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[1] + 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 CheckHint.ProtoReflect.Descriptor instead. +func (*CheckHint) Descriptor() ([]byte, []int) { + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{1} +} + +func (x *CheckHint) GetResource() *v1.ObjectAndRelation { + if x != nil { + return x.Resource + } + return nil +} + +func (x *CheckHint) GetSubject() *v1.ObjectAndRelation { + if x != nil { + return x.Subject + } + return nil +} + +func (x *CheckHint) GetTtuComputedUsersetRelation() string { + if x != nil { + return x.TtuComputedUsersetRelation + } + return "" +} + +func (x *CheckHint) GetResult() *ResourceCheckResult { + if x != nil { + return x.Result + } + return nil +} + type DispatchCheckResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -466,7 +551,7 @@ type DispatchCheckResponse struct { func (x *DispatchCheckResponse) Reset() { *x = DispatchCheckResponse{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[1] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -479,7 +564,7 @@ func (x *DispatchCheckResponse) String() string { func (*DispatchCheckResponse) ProtoMessage() {} func (x *DispatchCheckResponse) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[1] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -492,7 +577,7 @@ func (x *DispatchCheckResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DispatchCheckResponse.ProtoReflect.Descriptor instead. func (*DispatchCheckResponse) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{1} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{2} } func (x *DispatchCheckResponse) GetMetadata() *ResponseMeta { @@ -522,7 +607,7 @@ type ResourceCheckResult struct { func (x *ResourceCheckResult) Reset() { *x = ResourceCheckResult{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[2] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -535,7 +620,7 @@ func (x *ResourceCheckResult) String() string { func (*ResourceCheckResult) ProtoMessage() {} func (x *ResourceCheckResult) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[2] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -548,7 +633,7 @@ func (x *ResourceCheckResult) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceCheckResult.ProtoReflect.Descriptor instead. func (*ResourceCheckResult) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{2} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{3} } func (x *ResourceCheckResult) GetMembership() ResourceCheckResult_Membership { @@ -585,7 +670,7 @@ type DispatchExpandRequest struct { func (x *DispatchExpandRequest) Reset() { *x = DispatchExpandRequest{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[3] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -598,7 +683,7 @@ func (x *DispatchExpandRequest) String() string { func (*DispatchExpandRequest) ProtoMessage() {} func (x *DispatchExpandRequest) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[3] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -611,7 +696,7 @@ func (x *DispatchExpandRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DispatchExpandRequest.ProtoReflect.Descriptor instead. func (*DispatchExpandRequest) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{3} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{4} } func (x *DispatchExpandRequest) GetMetadata() *ResolverMeta { @@ -647,7 +732,7 @@ type DispatchExpandResponse struct { func (x *DispatchExpandResponse) Reset() { *x = DispatchExpandResponse{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[4] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -660,7 +745,7 @@ func (x *DispatchExpandResponse) String() string { func (*DispatchExpandResponse) ProtoMessage() {} func (x *DispatchExpandResponse) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[4] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -673,7 +758,7 @@ func (x *DispatchExpandResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DispatchExpandResponse.ProtoReflect.Descriptor instead. func (*DispatchExpandResponse) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{4} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{5} } func (x *DispatchExpandResponse) GetMetadata() *ResponseMeta { @@ -702,7 +787,7 @@ type Cursor struct { func (x *Cursor) Reset() { *x = Cursor{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[5] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -715,7 +800,7 @@ func (x *Cursor) String() string { func (*Cursor) ProtoMessage() {} func (x *Cursor) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[5] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -728,7 +813,7 @@ func (x *Cursor) ProtoReflect() protoreflect.Message { // Deprecated: Use Cursor.ProtoReflect.Descriptor instead. func (*Cursor) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{5} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{6} } func (x *Cursor) GetSections() []string { @@ -745,6 +830,235 @@ func (x *Cursor) GetDispatchVersion() uint32 { return 0 } +type DispatchLookupResources2Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Metadata *ResolverMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + ResourceRelation *v1.RelationReference `protobuf:"bytes,2,opt,name=resource_relation,json=resourceRelation,proto3" json:"resource_relation,omitempty"` + SubjectRelation *v1.RelationReference `protobuf:"bytes,3,opt,name=subject_relation,json=subjectRelation,proto3" json:"subject_relation,omitempty"` + SubjectIds []string `protobuf:"bytes,4,rep,name=subject_ids,json=subjectIds,proto3" json:"subject_ids,omitempty"` + TerminalSubject *v1.ObjectAndRelation `protobuf:"bytes,5,opt,name=terminal_subject,json=terminalSubject,proto3" json:"terminal_subject,omitempty"` + Context *structpb.Struct `protobuf:"bytes,6,opt,name=context,proto3" json:"context,omitempty"` + OptionalCursor *Cursor `protobuf:"bytes,7,opt,name=optional_cursor,json=optionalCursor,proto3" json:"optional_cursor,omitempty"` + OptionalLimit uint32 `protobuf:"varint,8,opt,name=optional_limit,json=optionalLimit,proto3" json:"optional_limit,omitempty"` +} + +func (x *DispatchLookupResources2Request) Reset() { + *x = DispatchLookupResources2Request{} + if protoimpl.UnsafeEnabled { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DispatchLookupResources2Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DispatchLookupResources2Request) ProtoMessage() {} + +func (x *DispatchLookupResources2Request) ProtoReflect() protoreflect.Message { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[7] + 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 DispatchLookupResources2Request.ProtoReflect.Descriptor instead. +func (*DispatchLookupResources2Request) Descriptor() ([]byte, []int) { + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{7} +} + +func (x *DispatchLookupResources2Request) GetMetadata() *ResolverMeta { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *DispatchLookupResources2Request) GetResourceRelation() *v1.RelationReference { + if x != nil { + return x.ResourceRelation + } + return nil +} + +func (x *DispatchLookupResources2Request) GetSubjectRelation() *v1.RelationReference { + if x != nil { + return x.SubjectRelation + } + return nil +} + +func (x *DispatchLookupResources2Request) GetSubjectIds() []string { + if x != nil { + return x.SubjectIds + } + return nil +} + +func (x *DispatchLookupResources2Request) GetTerminalSubject() *v1.ObjectAndRelation { + if x != nil { + return x.TerminalSubject + } + return nil +} + +func (x *DispatchLookupResources2Request) GetContext() *structpb.Struct { + if x != nil { + return x.Context + } + return nil +} + +func (x *DispatchLookupResources2Request) GetOptionalCursor() *Cursor { + if x != nil { + return x.OptionalCursor + } + return nil +} + +func (x *DispatchLookupResources2Request) GetOptionalLimit() uint32 { + if x != nil { + return x.OptionalLimit + } + return 0 +} + +type PossibleResource struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + ForSubjectIds []string `protobuf:"bytes,2,rep,name=for_subject_ids,json=forSubjectIds,proto3" json:"for_subject_ids,omitempty"` + MissingContextParams []string `protobuf:"bytes,3,rep,name=missing_context_params,json=missingContextParams,proto3" json:"missing_context_params,omitempty"` +} + +func (x *PossibleResource) Reset() { + *x = PossibleResource{} + if protoimpl.UnsafeEnabled { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PossibleResource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PossibleResource) ProtoMessage() {} + +func (x *PossibleResource) ProtoReflect() protoreflect.Message { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[8] + 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 PossibleResource.ProtoReflect.Descriptor instead. +func (*PossibleResource) Descriptor() ([]byte, []int) { + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{8} +} + +func (x *PossibleResource) GetResourceId() string { + if x != nil { + return x.ResourceId + } + return "" +} + +func (x *PossibleResource) GetForSubjectIds() []string { + if x != nil { + return x.ForSubjectIds + } + return nil +} + +func (x *PossibleResource) GetMissingContextParams() []string { + if x != nil { + return x.MissingContextParams + } + return nil +} + +type DispatchLookupResources2Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Resource *PossibleResource `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` + Metadata *ResponseMeta `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` + AfterResponseCursor *Cursor `protobuf:"bytes,3,opt,name=after_response_cursor,json=afterResponseCursor,proto3" json:"after_response_cursor,omitempty"` +} + +func (x *DispatchLookupResources2Response) Reset() { + *x = DispatchLookupResources2Response{} + if protoimpl.UnsafeEnabled { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DispatchLookupResources2Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DispatchLookupResources2Response) ProtoMessage() {} + +func (x *DispatchLookupResources2Response) ProtoReflect() protoreflect.Message { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[9] + 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 DispatchLookupResources2Response.ProtoReflect.Descriptor instead. +func (*DispatchLookupResources2Response) Descriptor() ([]byte, []int) { + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{9} +} + +func (x *DispatchLookupResources2Response) GetResource() *PossibleResource { + if x != nil { + return x.Resource + } + return nil +} + +func (x *DispatchLookupResources2Response) GetMetadata() *ResponseMeta { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *DispatchLookupResources2Response) GetAfterResponseCursor() *Cursor { + if x != nil { + return x.AfterResponseCursor + } + return nil +} + type DispatchReachableResourcesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -764,7 +1078,7 @@ type DispatchReachableResourcesRequest struct { func (x *DispatchReachableResourcesRequest) Reset() { *x = DispatchReachableResourcesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[6] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -777,7 +1091,7 @@ func (x *DispatchReachableResourcesRequest) String() string { func (*DispatchReachableResourcesRequest) ProtoMessage() {} func (x *DispatchReachableResourcesRequest) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[6] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -790,7 +1104,7 @@ func (x *DispatchReachableResourcesRequest) ProtoReflect() protoreflect.Message // Deprecated: Use DispatchReachableResourcesRequest.ProtoReflect.Descriptor instead. func (*DispatchReachableResourcesRequest) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{6} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{10} } func (x *DispatchReachableResourcesRequest) GetMetadata() *ResolverMeta { @@ -848,7 +1162,7 @@ type ReachableResource struct { func (x *ReachableResource) Reset() { *x = ReachableResource{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[7] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -861,7 +1175,7 @@ func (x *ReachableResource) String() string { func (*ReachableResource) ProtoMessage() {} func (x *ReachableResource) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[7] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -874,7 +1188,7 @@ func (x *ReachableResource) ProtoReflect() protoreflect.Message { // Deprecated: Use ReachableResource.ProtoReflect.Descriptor instead. func (*ReachableResource) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{7} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{11} } func (x *ReachableResource) GetResourceId() string { @@ -911,7 +1225,7 @@ type DispatchReachableResourcesResponse struct { func (x *DispatchReachableResourcesResponse) Reset() { *x = DispatchReachableResourcesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[8] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -924,7 +1238,7 @@ func (x *DispatchReachableResourcesResponse) String() string { func (*DispatchReachableResourcesResponse) ProtoMessage() {} func (x *DispatchReachableResourcesResponse) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[8] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -937,7 +1251,7 @@ func (x *DispatchReachableResourcesResponse) ProtoReflect() protoreflect.Message // Deprecated: Use DispatchReachableResourcesResponse.ProtoReflect.Descriptor instead. func (*DispatchReachableResourcesResponse) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{8} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{12} } func (x *DispatchReachableResourcesResponse) GetResource() *ReachableResource { @@ -980,7 +1294,7 @@ type DispatchLookupResourcesRequest struct { func (x *DispatchLookupResourcesRequest) Reset() { *x = DispatchLookupResourcesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[9] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -993,7 +1307,7 @@ func (x *DispatchLookupResourcesRequest) String() string { func (*DispatchLookupResourcesRequest) ProtoMessage() {} func (x *DispatchLookupResourcesRequest) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[9] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1006,7 +1320,7 @@ func (x *DispatchLookupResourcesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DispatchLookupResourcesRequest.ProtoReflect.Descriptor instead. func (*DispatchLookupResourcesRequest) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{9} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{13} } func (x *DispatchLookupResourcesRequest) GetMetadata() *ResolverMeta { @@ -1064,7 +1378,7 @@ type ResolvedResource struct { func (x *ResolvedResource) Reset() { *x = ResolvedResource{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[10] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1077,7 +1391,7 @@ func (x *ResolvedResource) String() string { func (*ResolvedResource) ProtoMessage() {} func (x *ResolvedResource) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[10] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1090,7 +1404,7 @@ func (x *ResolvedResource) ProtoReflect() protoreflect.Message { // Deprecated: Use ResolvedResource.ProtoReflect.Descriptor instead. func (*ResolvedResource) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{10} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{14} } func (x *ResolvedResource) GetResourceId() string { @@ -1127,7 +1441,7 @@ type DispatchLookupResourcesResponse struct { func (x *DispatchLookupResourcesResponse) Reset() { *x = DispatchLookupResourcesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[11] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1140,7 +1454,7 @@ func (x *DispatchLookupResourcesResponse) String() string { func (*DispatchLookupResourcesResponse) ProtoMessage() {} func (x *DispatchLookupResourcesResponse) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[11] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1153,7 +1467,7 @@ func (x *DispatchLookupResourcesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DispatchLookupResourcesResponse.ProtoReflect.Descriptor instead. func (*DispatchLookupResourcesResponse) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{11} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{15} } func (x *DispatchLookupResourcesResponse) GetMetadata() *ResponseMeta { @@ -1191,7 +1505,7 @@ type DispatchLookupSubjectsRequest struct { func (x *DispatchLookupSubjectsRequest) Reset() { *x = DispatchLookupSubjectsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[12] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1204,7 +1518,7 @@ func (x *DispatchLookupSubjectsRequest) String() string { func (*DispatchLookupSubjectsRequest) ProtoMessage() {} func (x *DispatchLookupSubjectsRequest) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[12] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1217,7 +1531,7 @@ func (x *DispatchLookupSubjectsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DispatchLookupSubjectsRequest.ProtoReflect.Descriptor instead. func (*DispatchLookupSubjectsRequest) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{12} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{16} } func (x *DispatchLookupSubjectsRequest) GetMetadata() *ResolverMeta { @@ -1261,7 +1575,7 @@ type FoundSubject struct { func (x *FoundSubject) Reset() { *x = FoundSubject{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[13] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1274,7 +1588,7 @@ func (x *FoundSubject) String() string { func (*FoundSubject) ProtoMessage() {} func (x *FoundSubject) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[13] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1287,7 +1601,7 @@ func (x *FoundSubject) ProtoReflect() protoreflect.Message { // Deprecated: Use FoundSubject.ProtoReflect.Descriptor instead. func (*FoundSubject) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{13} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{17} } func (x *FoundSubject) GetSubjectId() string { @@ -1322,7 +1636,7 @@ type FoundSubjects struct { func (x *FoundSubjects) Reset() { *x = FoundSubjects{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[14] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1335,7 +1649,7 @@ func (x *FoundSubjects) String() string { func (*FoundSubjects) ProtoMessage() {} func (x *FoundSubjects) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[14] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1348,7 +1662,7 @@ func (x *FoundSubjects) ProtoReflect() protoreflect.Message { // Deprecated: Use FoundSubjects.ProtoReflect.Descriptor instead. func (*FoundSubjects) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{14} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{18} } func (x *FoundSubjects) GetFoundSubjects() []*FoundSubject { @@ -1370,7 +1684,7 @@ type DispatchLookupSubjectsResponse struct { func (x *DispatchLookupSubjectsResponse) Reset() { *x = DispatchLookupSubjectsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[15] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1383,7 +1697,7 @@ func (x *DispatchLookupSubjectsResponse) String() string { func (*DispatchLookupSubjectsResponse) ProtoMessage() {} func (x *DispatchLookupSubjectsResponse) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[15] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1396,7 +1710,7 @@ func (x *DispatchLookupSubjectsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DispatchLookupSubjectsResponse.ProtoReflect.Descriptor instead. func (*DispatchLookupSubjectsResponse) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{15} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{19} } func (x *DispatchLookupSubjectsResponse) GetFoundSubjectsByResourceId() map[string]*FoundSubjects { @@ -1428,7 +1742,7 @@ type ResolverMeta struct { func (x *ResolverMeta) Reset() { *x = ResolverMeta{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[16] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1441,7 +1755,7 @@ func (x *ResolverMeta) String() string { func (*ResolverMeta) ProtoMessage() {} func (x *ResolverMeta) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[16] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1454,7 +1768,7 @@ func (x *ResolverMeta) ProtoReflect() protoreflect.Message { // Deprecated: Use ResolverMeta.ProtoReflect.Descriptor instead. func (*ResolverMeta) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{16} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{20} } func (x *ResolverMeta) GetAtRevision() string { @@ -1500,7 +1814,7 @@ type ResponseMeta struct { func (x *ResponseMeta) Reset() { *x = ResponseMeta{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[17] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1513,7 +1827,7 @@ func (x *ResponseMeta) String() string { func (*ResponseMeta) ProtoMessage() {} func (x *ResponseMeta) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[17] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1526,7 +1840,7 @@ func (x *ResponseMeta) ProtoReflect() protoreflect.Message { // Deprecated: Use ResponseMeta.ProtoReflect.Descriptor instead. func (*ResponseMeta) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{17} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{21} } func (x *ResponseMeta) GetDispatchCount() uint32 { @@ -1568,7 +1882,7 @@ type DebugInformation struct { func (x *DebugInformation) Reset() { *x = DebugInformation{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[18] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1581,7 +1895,7 @@ func (x *DebugInformation) String() string { func (*DebugInformation) ProtoMessage() {} func (x *DebugInformation) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[18] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1594,7 +1908,7 @@ func (x *DebugInformation) ProtoReflect() protoreflect.Message { // Deprecated: Use DebugInformation.ProtoReflect.Descriptor instead. func (*DebugInformation) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{18} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{22} } func (x *DebugInformation) GetCheck() *CheckDebugTrace { @@ -1620,7 +1934,7 @@ type CheckDebugTrace struct { func (x *CheckDebugTrace) Reset() { *x = CheckDebugTrace{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[19] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1633,7 +1947,7 @@ func (x *CheckDebugTrace) String() string { func (*CheckDebugTrace) ProtoMessage() {} func (x *CheckDebugTrace) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[19] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1646,7 +1960,7 @@ func (x *CheckDebugTrace) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckDebugTrace.ProtoReflect.Descriptor instead. func (*CheckDebugTrace) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{19} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{23} } func (x *CheckDebugTrace) GetRequest() *DispatchCheckRequest { @@ -1703,7 +2017,7 @@ var file_dispatch_v1_dispatch_proto_rawDesc = []byte{ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc8, 0x04, 0x0a, 0x14, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x81, 0x05, 0x0a, 0x14, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, @@ -1730,89 +2044,108 @@ var file_dispatch_v1_dispatch_proto_rawDesc = []byte{ 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x05, - 0x64, 0x65, 0x62, 0x75, 0x67, 0x22, 0x54, 0x0a, 0x0c, 0x44, 0x65, 0x62, 0x75, 0x67, 0x53, 0x65, - 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x4f, 0x5f, 0x44, 0x45, 0x42, 0x55, - 0x47, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x42, 0x41, - 0x53, 0x49, 0x43, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x47, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, - 0x1a, 0x0a, 0x16, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45, 0x5f, - 0x44, 0x45, 0x42, 0x55, 0x47, 0x47, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x22, 0x42, 0x0a, 0x0e, 0x52, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, - 0x13, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x5f, 0x41, 0x4c, 0x4c, 0x5f, 0x52, 0x45, 0x53, - 0x55, 0x4c, 0x54, 0x53, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, - 0x53, 0x49, 0x4e, 0x47, 0x4c, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x01, 0x22, - 0xaa, 0x02, 0x0a, 0x15, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x70, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x3b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x49, 0x64, 0x1a, 0x68, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x99, 0x02, 0x0a, - 0x13, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x12, 0x4b, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, - 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, - 0x70, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 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, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x5f, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x45, 0x78, 0x70, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x4a, 0x0a, 0x0a, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, - 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x4f, 0x54, 0x5f, 0x4d, - 0x45, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x45, 0x4d, 0x42, 0x45, - 0x52, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x43, 0x41, 0x56, 0x45, 0x41, 0x54, 0x45, 0x44, 0x5f, - 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x03, 0x22, 0xb8, 0x02, 0x0a, 0x15, 0x44, 0x69, 0x73, - 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x42, - 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x58, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x61, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, - 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x57, 0x0a, - 0x0e, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, - 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, - 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x2b, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x48, 0x41, 0x4c, 0x4c, - 0x4f, 0x57, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x45, 0x43, 0x55, 0x52, 0x53, 0x49, 0x56, - 0x45, 0x10, 0x01, 0x22, 0x8c, 0x01, 0x0a, 0x16, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, - 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, - 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3b, 0x0a, 0x09, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x6e, 0x6f, - 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x75, 0x70, 0x6c, 0x65, - 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x08, 0x74, 0x72, 0x65, 0x65, 0x4e, 0x6f, - 0x64, 0x65, 0x22, 0x55, 0x0a, 0x06, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, - 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, - 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x69, 0x73, 0x70, - 0x61, 0x74, 0x63, 0x68, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x8e, 0x03, 0x0a, 0x21, 0x44, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x37, 0x0a, 0x0b, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x68, + 0x69, 0x6e, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x48, 0x69, + 0x6e, 0x74, 0x52, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x54, + 0x0a, 0x0c, 0x44, 0x65, 0x62, 0x75, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x0c, + 0x0a, 0x08, 0x4e, 0x4f, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, + 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x42, 0x41, 0x53, 0x49, 0x43, 0x5f, 0x44, 0x45, 0x42, + 0x55, 0x47, 0x47, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x4e, 0x41, 0x42, + 0x4c, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x47, 0x49, + 0x4e, 0x47, 0x10, 0x02, 0x22, 0x42, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, + 0x45, 0x5f, 0x41, 0x4c, 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x53, 0x10, 0x00, 0x12, + 0x17, 0x0a, 0x13, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x49, 0x4e, 0x47, 0x4c, 0x45, 0x5f, + 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x01, 0x22, 0xf6, 0x01, 0x0a, 0x09, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x48, 0x69, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x34, + 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x41, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x75, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, 0x74, 0x74, 0x75, 0x5f, 0x63, 0x6f, 0x6d, 0x70, + 0x75, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x74, 0x74, 0x75, + 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x52, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x22, 0xaa, 0x02, 0x0a, 0x15, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x12, 0x70, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5f, 0x62, 0x79, + 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x13, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x49, 0x64, 0x1a, 0x68, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, + 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x99, + 0x02, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x4b, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x73, 0x68, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x64, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, + 0x68, 0x69, 0x70, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 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, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e, + 0x0a, 0x13, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x5f, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x45, 0x78, 0x70, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x4a, + 0x0a, 0x0a, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x0b, 0x0a, 0x07, + 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x4f, 0x54, + 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x45, 0x4d, + 0x42, 0x45, 0x52, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x43, 0x41, 0x56, 0x45, 0x41, 0x54, 0x45, + 0x44, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x03, 0x22, 0xb8, 0x02, 0x0a, 0x15, 0x44, + 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, + 0x61, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x58, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x13, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x57, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, + 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x70, 0x61, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x61, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x2b, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x61, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x48, 0x41, + 0x4c, 0x4c, 0x4f, 0x57, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x45, 0x43, 0x55, 0x52, 0x53, + 0x49, 0x56, 0x45, 0x10, 0x01, 0x22, 0x8c, 0x01, 0x0a, 0x16, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3b, 0x0a, 0x09, 0x74, 0x72, 0x65, 0x65, 0x5f, + 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x75, 0x70, + 0x6c, 0x65, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x08, 0x74, 0x72, 0x65, 0x65, + 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x55, 0x0a, 0x06, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x1a, + 0x0a, 0x08, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x08, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x90, 0x04, 0x0a, 0x1f, + 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, @@ -1829,269 +2162,334 @@ var file_dispatch_v1_dispatch_proto_rawDesc = []byte{ 0x02, 0x10, 0x01, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, 0x3c, 0x0a, 0x0f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, - 0x73, 0x6f, 0x72, 0x52, 0x0e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x75, 0x72, - 0x73, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0xe6, 0x01, 0x0a, 0x11, 0x52, + 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, 0x4f, 0x0a, 0x10, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, + 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x41, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, + 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x53, + 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x31, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x3c, 0x0a, 0x0f, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x52, 0x0e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0d, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x91, + 0x01, 0x0a, 0x10, 0x50, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x66, 0x6f, 0x72, 0x5f, 0x73, 0x75, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x66, + 0x6f, 0x72, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, 0x34, 0x0a, 0x16, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x22, 0xdd, 0x01, 0x0a, 0x20, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, + 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x32, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x47, 0x0a, 0x15, 0x61, 0x66, 0x74, + 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x63, 0x75, 0x72, 0x73, + 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x52, 0x13, 0x61, + 0x66, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x75, 0x72, 0x73, + 0x6f, 0x72, 0x22, 0x8e, 0x03, 0x0a, 0x21, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, - 0x64, 0x12, 0x50, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x66, 0x6f, 0x72, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x66, 0x6f, - 0x72, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x22, 0x36, 0x0a, 0x0c, 0x52, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x0e, 0x52, - 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x53, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x00, 0x12, - 0x12, 0x0a, 0x0e, 0x48, 0x41, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, - 0x4e, 0x10, 0x01, 0x22, 0xe0, 0x01, 0x0a, 0x22, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x72, 0x4d, 0x65, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x51, 0x0a, 0x11, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x10, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4f, 0x0a, 0x10, + 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x73, 0x75, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, + 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, 0x3c, + 0x0a, 0x0f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, + 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x52, 0x0e, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0e, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x22, 0xe6, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, + 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x50, 0x0a, 0x0d, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x64, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x68, - 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, - 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x47, 0x0a, - 0x15, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, - 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, - 0x72, 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0x88, 0x03, 0x0a, 0x1e, 0x44, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, + 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0c, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x0a, 0x0f, + 0x66, 0x6f, 0x72, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x66, 0x6f, 0x72, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x49, 0x64, 0x73, 0x22, 0x36, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x53, + 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x48, 0x41, 0x53, 0x5f, + 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x22, 0xe0, 0x01, 0x0a, + 0x22, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, + 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, + 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x47, 0x0a, 0x15, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, + 0x88, 0x03, 0x0a, 0x1e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, + 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x42, + 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x12, 0x4d, 0x0a, 0x0f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, + 0x10, 0x01, 0x52, 0x0e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, + 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x12, 0x31, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x3c, 0x0a, 0x0f, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x52, 0x0e, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0x98, 0x02, 0x0a, 0x10, 0x52, + 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, + 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, + 0x12, 0x54, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x68, + 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x52, 0x0e, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x12, 0x38, 0x0a, 0x18, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, + 0x67, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x22, 0x53, 0x0a, 0x0e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x68, + 0x69, 0x70, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, + 0x12, 0x0a, 0x0e, 0x48, 0x41, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, + 0x4e, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, + 0x41, 0x4c, 0x4c, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, + 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x22, 0xed, 0x01, 0x0a, 0x1f, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x4a, 0x0a, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, - 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4d, 0x0a, 0x0f, 0x6f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x10, 0x72, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x15, + 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x63, + 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, + 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, + 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0xa7, 0x02, 0x0a, 0x1d, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, + 0x4d, 0x65, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x51, 0x0a, 0x11, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, - 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x6f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x07, 0x73, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x6e, 0x64, 0x52, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, - 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x31, 0x0a, 0x07, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x78, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x25, 0x0a, 0x0e, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4c, 0x69, - 0x6d, 0x69, 0x74, 0x12, 0x3c, 0x0a, 0x0f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, - 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, - 0x72, 0x52, 0x0e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x75, 0x72, 0x73, 0x6f, - 0x72, 0x22, 0x98, 0x02, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x54, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x2c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x52, 0x0e, 0x70, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x12, 0x38, 0x0a, - 0x18, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x16, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, - 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x53, 0x0a, 0x0e, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, - 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x48, 0x41, 0x53, 0x5f, 0x50, 0x45, - 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x43, 0x4f, - 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x4c, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x5f, - 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x22, 0xed, 0x01, 0x0a, - 0x1f, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, + 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x73, 0x12, 0x4f, + 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0f, + 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0xbd, 0x01, 0x0a, 0x0c, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, + 0x46, 0x0a, 0x11, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 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, 0x10, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x45, 0x78, 0x70, + 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4a, 0x0a, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x6c, - 0x76, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x15, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0xa7, 0x02, 0x0a, - 0x1d, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, - 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, 0x05, - 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x51, 0x0a, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, - 0x52, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, - 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x49, 0x64, 0x73, 0x12, 0x4f, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, - 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xbd, 0x01, 0x0a, 0x0c, 0x46, 0x6f, 0x75, 0x6e, 0x64, - 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x46, 0x0a, 0x11, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, - 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 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, 0x10, 0x63, 0x61, - 0x76, 0x65, 0x61, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x46, - 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, - 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x53, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0x51, 0x0a, 0x0d, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x40, 0x0a, 0x0e, 0x66, 0x6f, 0x75, 0x6e, 0x64, - 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, - 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x0d, 0x66, 0x6f, 0x75, 0x6e, - 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0xd0, 0x02, 0x0a, 0x1e, 0x44, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8c, 0x01, 0x0a, - 0x1d, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x5f, - 0x62, 0x79, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4a, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, - 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, - 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x19, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, - 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x35, 0x0a, 0x08, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x1a, 0x68, 0x0a, 0x1e, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, - 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc1, 0x01, 0x0a, - 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x29, 0x0a, - 0x0b, 0x61, 0x74, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0x28, 0x80, 0x08, 0x52, 0x0a, 0x61, 0x74, - 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x0f, 0x64, 0x65, 0x70, 0x74, - 0x68, 0x5f, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x74, - 0x68, 0x52, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x21, 0x0a, 0x0a, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, - 0x18, 0x01, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x31, 0x0a, - 0x0f, 0x74, 0x72, 0x61, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x6f, 0x6d, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, 0x08, - 0x52, 0x0e, 0x74, 0x72, 0x61, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, - 0x22, 0xda, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, - 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x70, 0x74, - 0x68, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0d, 0x64, 0x65, 0x70, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, - 0x32, 0x0a, 0x15, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0a, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, - 0x6f, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x46, 0x0a, - 0x10, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, - 0x63, 0x68, 0x65, 0x63, 0x6b, 0x22, 0xaf, 0x04, 0x0a, 0x0f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, - 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x69, 0x73, + 0x2e, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x10, 0x65, + 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, + 0x51, 0x0a, 0x0d, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x12, 0x40, 0x0a, 0x0e, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x52, 0x0d, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x22, 0xd0, 0x02, 0x0a, 0x1e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, + 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8c, 0x01, 0x0a, 0x1d, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x5f, + 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4a, 0x2e, + 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x6f, 0x75, 0x6e, 0x64, + 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x19, 0x66, 0x6f, 0x75, 0x6e, 0x64, + 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x49, 0x64, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, + 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x68, 0x0a, 0x1e, 0x46, + 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x75, + 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc1, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, + 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x29, 0x0a, 0x0b, 0x61, 0x74, 0x5f, 0x72, 0x65, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, + 0x72, 0x03, 0x28, 0x80, 0x08, 0x52, 0x0a, 0x61, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x30, 0x0a, 0x0f, 0x64, 0x65, 0x70, 0x74, 0x68, 0x5f, 0x72, 0x65, 0x6d, 0x61, 0x69, + 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, + 0x02, 0x20, 0x00, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x74, 0x68, 0x52, 0x65, 0x6d, 0x61, 0x69, 0x6e, + 0x69, 0x6e, 0x67, 0x12, 0x21, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x6f, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, + 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, 0x08, 0x52, 0x0e, 0x74, 0x72, 0x61, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x6c, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, 0x22, 0xda, 0x01, 0x0a, 0x0c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x70, 0x74, 0x68, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x64, 0x65, 0x70, 0x74, 0x68, + 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x64, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x44, + 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0a, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, + 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x09, 0x64, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, + 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x46, 0x0a, 0x10, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, + 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x64, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, + 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x22, 0xaf, + 0x04, 0x0a, 0x0f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, + 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x5f, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x29, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x52, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x14, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x43, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x2e, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x73, 0x5f, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0e, 0x69, 0x73, 0x43, 0x61, 0x63, 0x68, 0x65, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, + 0x3f, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x6c, 0x65, 0x6d, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, + 0x61, 0x63, 0x65, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x50, 0x72, 0x6f, 0x62, 0x6c, 0x65, 0x6d, 0x73, + 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x5c, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x39, 0x0a, 0x0c, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, + 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, + 0x32, 0xba, 0x05, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x21, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, + 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, + 0x12, 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, + 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x81, 0x01, 0x0a, 0x1a, + 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, + 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2e, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5f, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, - 0x72, 0x61, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, - 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x28, 0x0a, 0x10, - 0x69, 0x73, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x43, 0x61, 0x63, 0x68, 0x65, 0x64, - 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x3f, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x5f, 0x70, 0x72, - 0x6f, 0x62, 0x6c, 0x65, 0x6d, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x64, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x50, - 0x72, 0x6f, 0x62, 0x6c, 0x65, 0x6d, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x5c, - 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x39, 0x0a, 0x0c, - 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, - 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x4c, - 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x45, 0x52, 0x4d, 0x49, - 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x32, 0xbd, 0x04, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x70, - 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x44, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x21, 0x2e, 0x64, + 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x64, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, + 0x78, 0x0a, 0x17, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, + 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2b, 0x2e, 0x64, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, + 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x75, 0x0a, 0x16, 0x44, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x73, 0x12, 0x2a, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, + 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, + 0x12, 0x7b, 0x0a, 0x18, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, + 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x32, 0x12, 0x2c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x12, 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, - 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x81, 0x01, 0x0a, 0x1a, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, - 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x12, 0x2e, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, - 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2f, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, - 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x78, 0x0a, 0x17, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x12, 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, - 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, - 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, - 0x12, 0x75, 0x0a, 0x16, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, - 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x2a, 0x2e, 0x64, 0x69, 0x73, + 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, - 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x42, 0xaa, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, - 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x44, 0x69, 0x73, - 0x70, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3b, 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, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2f, 0x76, 0x31, 0x3b, 0x64, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x44, 0x58, 0x58, 0xaa, - 0x02, 0x0b, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0b, - 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x17, 0x44, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, - 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x42, 0xaa, 0x01, + 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, + 0x31, 0x42, 0x0d, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x50, 0x01, 0x5a, 0x3b, 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, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x2f, 0x76, 0x31, 0x3b, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x76, 0x31, 0xa2, + 0x02, 0x03, 0x44, 0x58, 0x58, 0xaa, 0x02, 0x0b, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0b, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5c, 0x56, + 0x31, 0xe2, 0x02, 0x17, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5c, 0x56, 0x31, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x44, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -2107,7 +2505,7 @@ func file_dispatch_v1_dispatch_proto_rawDescGZIP() []byte { } var file_dispatch_v1_dispatch_proto_enumTypes = make([]protoimpl.EnumInfo, 7) -var file_dispatch_v1_dispatch_proto_msgTypes = make([]protoimpl.MessageInfo, 23) +var file_dispatch_v1_dispatch_proto_msgTypes = make([]protoimpl.MessageInfo, 27) var file_dispatch_v1_dispatch_proto_goTypes = []interface{}{ (DispatchCheckRequest_DebugSetting)(0), // 0: dispatch.v1.DispatchCheckRequest.DebugSetting (DispatchCheckRequest_ResultsSetting)(0), // 1: dispatch.v1.DispatchCheckRequest.ResultsSetting @@ -2117,100 +2515,119 @@ var file_dispatch_v1_dispatch_proto_goTypes = []interface{}{ (ResolvedResource_Permissionship)(0), // 5: dispatch.v1.ResolvedResource.Permissionship (CheckDebugTrace_RelationType)(0), // 6: dispatch.v1.CheckDebugTrace.RelationType (*DispatchCheckRequest)(nil), // 7: dispatch.v1.DispatchCheckRequest - (*DispatchCheckResponse)(nil), // 8: dispatch.v1.DispatchCheckResponse - (*ResourceCheckResult)(nil), // 9: dispatch.v1.ResourceCheckResult - (*DispatchExpandRequest)(nil), // 10: dispatch.v1.DispatchExpandRequest - (*DispatchExpandResponse)(nil), // 11: dispatch.v1.DispatchExpandResponse - (*Cursor)(nil), // 12: dispatch.v1.Cursor - (*DispatchReachableResourcesRequest)(nil), // 13: dispatch.v1.DispatchReachableResourcesRequest - (*ReachableResource)(nil), // 14: dispatch.v1.ReachableResource - (*DispatchReachableResourcesResponse)(nil), // 15: dispatch.v1.DispatchReachableResourcesResponse - (*DispatchLookupResourcesRequest)(nil), // 16: dispatch.v1.DispatchLookupResourcesRequest - (*ResolvedResource)(nil), // 17: dispatch.v1.ResolvedResource - (*DispatchLookupResourcesResponse)(nil), // 18: dispatch.v1.DispatchLookupResourcesResponse - (*DispatchLookupSubjectsRequest)(nil), // 19: dispatch.v1.DispatchLookupSubjectsRequest - (*FoundSubject)(nil), // 20: dispatch.v1.FoundSubject - (*FoundSubjects)(nil), // 21: dispatch.v1.FoundSubjects - (*DispatchLookupSubjectsResponse)(nil), // 22: dispatch.v1.DispatchLookupSubjectsResponse - (*ResolverMeta)(nil), // 23: dispatch.v1.ResolverMeta - (*ResponseMeta)(nil), // 24: dispatch.v1.ResponseMeta - (*DebugInformation)(nil), // 25: dispatch.v1.DebugInformation - (*CheckDebugTrace)(nil), // 26: dispatch.v1.CheckDebugTrace - nil, // 27: dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry - nil, // 28: dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry - nil, // 29: dispatch.v1.CheckDebugTrace.ResultsEntry - (*v1.RelationReference)(nil), // 30: core.v1.RelationReference - (*v1.ObjectAndRelation)(nil), // 31: core.v1.ObjectAndRelation - (*v1.CaveatExpression)(nil), // 32: core.v1.CaveatExpression - (*v1.RelationTupleTreeNode)(nil), // 33: core.v1.RelationTupleTreeNode - (*structpb.Struct)(nil), // 34: google.protobuf.Struct - (*durationpb.Duration)(nil), // 35: google.protobuf.Duration + (*CheckHint)(nil), // 8: dispatch.v1.CheckHint + (*DispatchCheckResponse)(nil), // 9: dispatch.v1.DispatchCheckResponse + (*ResourceCheckResult)(nil), // 10: dispatch.v1.ResourceCheckResult + (*DispatchExpandRequest)(nil), // 11: dispatch.v1.DispatchExpandRequest + (*DispatchExpandResponse)(nil), // 12: dispatch.v1.DispatchExpandResponse + (*Cursor)(nil), // 13: dispatch.v1.Cursor + (*DispatchLookupResources2Request)(nil), // 14: dispatch.v1.DispatchLookupResources2Request + (*PossibleResource)(nil), // 15: dispatch.v1.PossibleResource + (*DispatchLookupResources2Response)(nil), // 16: dispatch.v1.DispatchLookupResources2Response + (*DispatchReachableResourcesRequest)(nil), // 17: dispatch.v1.DispatchReachableResourcesRequest + (*ReachableResource)(nil), // 18: dispatch.v1.ReachableResource + (*DispatchReachableResourcesResponse)(nil), // 19: dispatch.v1.DispatchReachableResourcesResponse + (*DispatchLookupResourcesRequest)(nil), // 20: dispatch.v1.DispatchLookupResourcesRequest + (*ResolvedResource)(nil), // 21: dispatch.v1.ResolvedResource + (*DispatchLookupResourcesResponse)(nil), // 22: dispatch.v1.DispatchLookupResourcesResponse + (*DispatchLookupSubjectsRequest)(nil), // 23: dispatch.v1.DispatchLookupSubjectsRequest + (*FoundSubject)(nil), // 24: dispatch.v1.FoundSubject + (*FoundSubjects)(nil), // 25: dispatch.v1.FoundSubjects + (*DispatchLookupSubjectsResponse)(nil), // 26: dispatch.v1.DispatchLookupSubjectsResponse + (*ResolverMeta)(nil), // 27: dispatch.v1.ResolverMeta + (*ResponseMeta)(nil), // 28: dispatch.v1.ResponseMeta + (*DebugInformation)(nil), // 29: dispatch.v1.DebugInformation + (*CheckDebugTrace)(nil), // 30: dispatch.v1.CheckDebugTrace + nil, // 31: dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry + nil, // 32: dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry + nil, // 33: dispatch.v1.CheckDebugTrace.ResultsEntry + (*v1.RelationReference)(nil), // 34: core.v1.RelationReference + (*v1.ObjectAndRelation)(nil), // 35: core.v1.ObjectAndRelation + (*v1.CaveatExpression)(nil), // 36: core.v1.CaveatExpression + (*v1.RelationTupleTreeNode)(nil), // 37: core.v1.RelationTupleTreeNode + (*structpb.Struct)(nil), // 38: google.protobuf.Struct + (*durationpb.Duration)(nil), // 39: google.protobuf.Duration } var file_dispatch_v1_dispatch_proto_depIdxs = []int32{ - 23, // 0: dispatch.v1.DispatchCheckRequest.metadata:type_name -> dispatch.v1.ResolverMeta - 30, // 1: dispatch.v1.DispatchCheckRequest.resource_relation:type_name -> core.v1.RelationReference - 31, // 2: dispatch.v1.DispatchCheckRequest.subject:type_name -> core.v1.ObjectAndRelation + 27, // 0: dispatch.v1.DispatchCheckRequest.metadata:type_name -> dispatch.v1.ResolverMeta + 34, // 1: dispatch.v1.DispatchCheckRequest.resource_relation:type_name -> core.v1.RelationReference + 35, // 2: dispatch.v1.DispatchCheckRequest.subject:type_name -> core.v1.ObjectAndRelation 1, // 3: dispatch.v1.DispatchCheckRequest.results_setting:type_name -> dispatch.v1.DispatchCheckRequest.ResultsSetting 0, // 4: dispatch.v1.DispatchCheckRequest.debug:type_name -> dispatch.v1.DispatchCheckRequest.DebugSetting - 24, // 5: dispatch.v1.DispatchCheckResponse.metadata:type_name -> dispatch.v1.ResponseMeta - 27, // 6: dispatch.v1.DispatchCheckResponse.results_by_resource_id:type_name -> dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry - 2, // 7: dispatch.v1.ResourceCheckResult.membership:type_name -> dispatch.v1.ResourceCheckResult.Membership - 32, // 8: dispatch.v1.ResourceCheckResult.expression:type_name -> core.v1.CaveatExpression - 23, // 9: dispatch.v1.DispatchExpandRequest.metadata:type_name -> dispatch.v1.ResolverMeta - 31, // 10: dispatch.v1.DispatchExpandRequest.resource_and_relation:type_name -> core.v1.ObjectAndRelation - 3, // 11: dispatch.v1.DispatchExpandRequest.expansion_mode:type_name -> dispatch.v1.DispatchExpandRequest.ExpansionMode - 24, // 12: dispatch.v1.DispatchExpandResponse.metadata:type_name -> dispatch.v1.ResponseMeta - 33, // 13: dispatch.v1.DispatchExpandResponse.tree_node:type_name -> core.v1.RelationTupleTreeNode - 23, // 14: dispatch.v1.DispatchReachableResourcesRequest.metadata:type_name -> dispatch.v1.ResolverMeta - 30, // 15: dispatch.v1.DispatchReachableResourcesRequest.resource_relation:type_name -> core.v1.RelationReference - 30, // 16: dispatch.v1.DispatchReachableResourcesRequest.subject_relation:type_name -> core.v1.RelationReference - 12, // 17: dispatch.v1.DispatchReachableResourcesRequest.optional_cursor:type_name -> dispatch.v1.Cursor - 4, // 18: dispatch.v1.ReachableResource.result_status:type_name -> dispatch.v1.ReachableResource.ResultStatus - 14, // 19: dispatch.v1.DispatchReachableResourcesResponse.resource:type_name -> dispatch.v1.ReachableResource - 24, // 20: dispatch.v1.DispatchReachableResourcesResponse.metadata:type_name -> dispatch.v1.ResponseMeta - 12, // 21: dispatch.v1.DispatchReachableResourcesResponse.after_response_cursor:type_name -> dispatch.v1.Cursor - 23, // 22: dispatch.v1.DispatchLookupResourcesRequest.metadata:type_name -> dispatch.v1.ResolverMeta - 30, // 23: dispatch.v1.DispatchLookupResourcesRequest.object_relation:type_name -> core.v1.RelationReference - 31, // 24: dispatch.v1.DispatchLookupResourcesRequest.subject:type_name -> core.v1.ObjectAndRelation - 34, // 25: dispatch.v1.DispatchLookupResourcesRequest.context:type_name -> google.protobuf.Struct - 12, // 26: dispatch.v1.DispatchLookupResourcesRequest.optional_cursor:type_name -> dispatch.v1.Cursor - 5, // 27: dispatch.v1.ResolvedResource.permissionship:type_name -> dispatch.v1.ResolvedResource.Permissionship - 24, // 28: dispatch.v1.DispatchLookupResourcesResponse.metadata:type_name -> dispatch.v1.ResponseMeta - 17, // 29: dispatch.v1.DispatchLookupResourcesResponse.resolved_resource:type_name -> dispatch.v1.ResolvedResource - 12, // 30: dispatch.v1.DispatchLookupResourcesResponse.after_response_cursor:type_name -> dispatch.v1.Cursor - 23, // 31: dispatch.v1.DispatchLookupSubjectsRequest.metadata:type_name -> dispatch.v1.ResolverMeta - 30, // 32: dispatch.v1.DispatchLookupSubjectsRequest.resource_relation:type_name -> core.v1.RelationReference - 30, // 33: dispatch.v1.DispatchLookupSubjectsRequest.subject_relation:type_name -> core.v1.RelationReference - 32, // 34: dispatch.v1.FoundSubject.caveat_expression:type_name -> core.v1.CaveatExpression - 20, // 35: dispatch.v1.FoundSubject.excluded_subjects:type_name -> dispatch.v1.FoundSubject - 20, // 36: dispatch.v1.FoundSubjects.found_subjects:type_name -> dispatch.v1.FoundSubject - 28, // 37: dispatch.v1.DispatchLookupSubjectsResponse.found_subjects_by_resource_id:type_name -> dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry - 24, // 38: dispatch.v1.DispatchLookupSubjectsResponse.metadata:type_name -> dispatch.v1.ResponseMeta - 25, // 39: dispatch.v1.ResponseMeta.debug_info:type_name -> dispatch.v1.DebugInformation - 26, // 40: dispatch.v1.DebugInformation.check:type_name -> dispatch.v1.CheckDebugTrace - 7, // 41: dispatch.v1.CheckDebugTrace.request:type_name -> dispatch.v1.DispatchCheckRequest - 6, // 42: dispatch.v1.CheckDebugTrace.resource_relation_type:type_name -> dispatch.v1.CheckDebugTrace.RelationType - 29, // 43: dispatch.v1.CheckDebugTrace.results:type_name -> dispatch.v1.CheckDebugTrace.ResultsEntry - 26, // 44: dispatch.v1.CheckDebugTrace.sub_problems:type_name -> dispatch.v1.CheckDebugTrace - 35, // 45: dispatch.v1.CheckDebugTrace.duration:type_name -> google.protobuf.Duration - 9, // 46: dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry.value:type_name -> dispatch.v1.ResourceCheckResult - 21, // 47: dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry.value:type_name -> dispatch.v1.FoundSubjects - 9, // 48: dispatch.v1.CheckDebugTrace.ResultsEntry.value:type_name -> dispatch.v1.ResourceCheckResult - 7, // 49: dispatch.v1.DispatchService.DispatchCheck:input_type -> dispatch.v1.DispatchCheckRequest - 10, // 50: dispatch.v1.DispatchService.DispatchExpand:input_type -> dispatch.v1.DispatchExpandRequest - 13, // 51: dispatch.v1.DispatchService.DispatchReachableResources:input_type -> dispatch.v1.DispatchReachableResourcesRequest - 16, // 52: dispatch.v1.DispatchService.DispatchLookupResources:input_type -> dispatch.v1.DispatchLookupResourcesRequest - 19, // 53: dispatch.v1.DispatchService.DispatchLookupSubjects:input_type -> dispatch.v1.DispatchLookupSubjectsRequest - 8, // 54: dispatch.v1.DispatchService.DispatchCheck:output_type -> dispatch.v1.DispatchCheckResponse - 11, // 55: dispatch.v1.DispatchService.DispatchExpand:output_type -> dispatch.v1.DispatchExpandResponse - 15, // 56: dispatch.v1.DispatchService.DispatchReachableResources:output_type -> dispatch.v1.DispatchReachableResourcesResponse - 18, // 57: dispatch.v1.DispatchService.DispatchLookupResources:output_type -> dispatch.v1.DispatchLookupResourcesResponse - 22, // 58: dispatch.v1.DispatchService.DispatchLookupSubjects:output_type -> dispatch.v1.DispatchLookupSubjectsResponse - 54, // [54:59] is the sub-list for method output_type - 49, // [49:54] is the sub-list for method input_type - 49, // [49:49] is the sub-list for extension type_name - 49, // [49:49] is the sub-list for extension extendee - 0, // [0:49] is the sub-list for field type_name + 8, // 5: dispatch.v1.DispatchCheckRequest.check_hints:type_name -> dispatch.v1.CheckHint + 35, // 6: dispatch.v1.CheckHint.resource:type_name -> core.v1.ObjectAndRelation + 35, // 7: dispatch.v1.CheckHint.subject:type_name -> core.v1.ObjectAndRelation + 10, // 8: dispatch.v1.CheckHint.result:type_name -> dispatch.v1.ResourceCheckResult + 28, // 9: dispatch.v1.DispatchCheckResponse.metadata:type_name -> dispatch.v1.ResponseMeta + 31, // 10: dispatch.v1.DispatchCheckResponse.results_by_resource_id:type_name -> dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry + 2, // 11: dispatch.v1.ResourceCheckResult.membership:type_name -> dispatch.v1.ResourceCheckResult.Membership + 36, // 12: dispatch.v1.ResourceCheckResult.expression:type_name -> core.v1.CaveatExpression + 27, // 13: dispatch.v1.DispatchExpandRequest.metadata:type_name -> dispatch.v1.ResolverMeta + 35, // 14: dispatch.v1.DispatchExpandRequest.resource_and_relation:type_name -> core.v1.ObjectAndRelation + 3, // 15: dispatch.v1.DispatchExpandRequest.expansion_mode:type_name -> dispatch.v1.DispatchExpandRequest.ExpansionMode + 28, // 16: dispatch.v1.DispatchExpandResponse.metadata:type_name -> dispatch.v1.ResponseMeta + 37, // 17: dispatch.v1.DispatchExpandResponse.tree_node:type_name -> core.v1.RelationTupleTreeNode + 27, // 18: dispatch.v1.DispatchLookupResources2Request.metadata:type_name -> dispatch.v1.ResolverMeta + 34, // 19: dispatch.v1.DispatchLookupResources2Request.resource_relation:type_name -> core.v1.RelationReference + 34, // 20: dispatch.v1.DispatchLookupResources2Request.subject_relation:type_name -> core.v1.RelationReference + 35, // 21: dispatch.v1.DispatchLookupResources2Request.terminal_subject:type_name -> core.v1.ObjectAndRelation + 38, // 22: dispatch.v1.DispatchLookupResources2Request.context:type_name -> google.protobuf.Struct + 13, // 23: dispatch.v1.DispatchLookupResources2Request.optional_cursor:type_name -> dispatch.v1.Cursor + 15, // 24: dispatch.v1.DispatchLookupResources2Response.resource:type_name -> dispatch.v1.PossibleResource + 28, // 25: dispatch.v1.DispatchLookupResources2Response.metadata:type_name -> dispatch.v1.ResponseMeta + 13, // 26: dispatch.v1.DispatchLookupResources2Response.after_response_cursor:type_name -> dispatch.v1.Cursor + 27, // 27: dispatch.v1.DispatchReachableResourcesRequest.metadata:type_name -> dispatch.v1.ResolverMeta + 34, // 28: dispatch.v1.DispatchReachableResourcesRequest.resource_relation:type_name -> core.v1.RelationReference + 34, // 29: dispatch.v1.DispatchReachableResourcesRequest.subject_relation:type_name -> core.v1.RelationReference + 13, // 30: dispatch.v1.DispatchReachableResourcesRequest.optional_cursor:type_name -> dispatch.v1.Cursor + 4, // 31: dispatch.v1.ReachableResource.result_status:type_name -> dispatch.v1.ReachableResource.ResultStatus + 18, // 32: dispatch.v1.DispatchReachableResourcesResponse.resource:type_name -> dispatch.v1.ReachableResource + 28, // 33: dispatch.v1.DispatchReachableResourcesResponse.metadata:type_name -> dispatch.v1.ResponseMeta + 13, // 34: dispatch.v1.DispatchReachableResourcesResponse.after_response_cursor:type_name -> dispatch.v1.Cursor + 27, // 35: dispatch.v1.DispatchLookupResourcesRequest.metadata:type_name -> dispatch.v1.ResolverMeta + 34, // 36: dispatch.v1.DispatchLookupResourcesRequest.object_relation:type_name -> core.v1.RelationReference + 35, // 37: dispatch.v1.DispatchLookupResourcesRequest.subject:type_name -> core.v1.ObjectAndRelation + 38, // 38: dispatch.v1.DispatchLookupResourcesRequest.context:type_name -> google.protobuf.Struct + 13, // 39: dispatch.v1.DispatchLookupResourcesRequest.optional_cursor:type_name -> dispatch.v1.Cursor + 5, // 40: dispatch.v1.ResolvedResource.permissionship:type_name -> dispatch.v1.ResolvedResource.Permissionship + 28, // 41: dispatch.v1.DispatchLookupResourcesResponse.metadata:type_name -> dispatch.v1.ResponseMeta + 21, // 42: dispatch.v1.DispatchLookupResourcesResponse.resolved_resource:type_name -> dispatch.v1.ResolvedResource + 13, // 43: dispatch.v1.DispatchLookupResourcesResponse.after_response_cursor:type_name -> dispatch.v1.Cursor + 27, // 44: dispatch.v1.DispatchLookupSubjectsRequest.metadata:type_name -> dispatch.v1.ResolverMeta + 34, // 45: dispatch.v1.DispatchLookupSubjectsRequest.resource_relation:type_name -> core.v1.RelationReference + 34, // 46: dispatch.v1.DispatchLookupSubjectsRequest.subject_relation:type_name -> core.v1.RelationReference + 36, // 47: dispatch.v1.FoundSubject.caveat_expression:type_name -> core.v1.CaveatExpression + 24, // 48: dispatch.v1.FoundSubject.excluded_subjects:type_name -> dispatch.v1.FoundSubject + 24, // 49: dispatch.v1.FoundSubjects.found_subjects:type_name -> dispatch.v1.FoundSubject + 32, // 50: dispatch.v1.DispatchLookupSubjectsResponse.found_subjects_by_resource_id:type_name -> dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry + 28, // 51: dispatch.v1.DispatchLookupSubjectsResponse.metadata:type_name -> dispatch.v1.ResponseMeta + 29, // 52: dispatch.v1.ResponseMeta.debug_info:type_name -> dispatch.v1.DebugInformation + 30, // 53: dispatch.v1.DebugInformation.check:type_name -> dispatch.v1.CheckDebugTrace + 7, // 54: dispatch.v1.CheckDebugTrace.request:type_name -> dispatch.v1.DispatchCheckRequest + 6, // 55: dispatch.v1.CheckDebugTrace.resource_relation_type:type_name -> dispatch.v1.CheckDebugTrace.RelationType + 33, // 56: dispatch.v1.CheckDebugTrace.results:type_name -> dispatch.v1.CheckDebugTrace.ResultsEntry + 30, // 57: dispatch.v1.CheckDebugTrace.sub_problems:type_name -> dispatch.v1.CheckDebugTrace + 39, // 58: dispatch.v1.CheckDebugTrace.duration:type_name -> google.protobuf.Duration + 10, // 59: dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry.value:type_name -> dispatch.v1.ResourceCheckResult + 25, // 60: dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry.value:type_name -> dispatch.v1.FoundSubjects + 10, // 61: dispatch.v1.CheckDebugTrace.ResultsEntry.value:type_name -> dispatch.v1.ResourceCheckResult + 7, // 62: dispatch.v1.DispatchService.DispatchCheck:input_type -> dispatch.v1.DispatchCheckRequest + 11, // 63: dispatch.v1.DispatchService.DispatchExpand:input_type -> dispatch.v1.DispatchExpandRequest + 17, // 64: dispatch.v1.DispatchService.DispatchReachableResources:input_type -> dispatch.v1.DispatchReachableResourcesRequest + 20, // 65: dispatch.v1.DispatchService.DispatchLookupResources:input_type -> dispatch.v1.DispatchLookupResourcesRequest + 23, // 66: dispatch.v1.DispatchService.DispatchLookupSubjects:input_type -> dispatch.v1.DispatchLookupSubjectsRequest + 14, // 67: dispatch.v1.DispatchService.DispatchLookupResources2:input_type -> dispatch.v1.DispatchLookupResources2Request + 9, // 68: dispatch.v1.DispatchService.DispatchCheck:output_type -> dispatch.v1.DispatchCheckResponse + 12, // 69: dispatch.v1.DispatchService.DispatchExpand:output_type -> dispatch.v1.DispatchExpandResponse + 19, // 70: dispatch.v1.DispatchService.DispatchReachableResources:output_type -> dispatch.v1.DispatchReachableResourcesResponse + 22, // 71: dispatch.v1.DispatchService.DispatchLookupResources:output_type -> dispatch.v1.DispatchLookupResourcesResponse + 26, // 72: dispatch.v1.DispatchService.DispatchLookupSubjects:output_type -> dispatch.v1.DispatchLookupSubjectsResponse + 16, // 73: dispatch.v1.DispatchService.DispatchLookupResources2:output_type -> dispatch.v1.DispatchLookupResources2Response + 68, // [68:74] is the sub-list for method output_type + 62, // [62:68] is the sub-list for method input_type + 62, // [62:62] is the sub-list for extension type_name + 62, // [62:62] is the sub-list for extension extendee + 0, // [0:62] is the sub-list for field type_name } func init() { file_dispatch_v1_dispatch_proto_init() } @@ -2232,7 +2649,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchCheckResponse); i { + switch v := v.(*CheckHint); i { case 0: return &v.state case 1: @@ -2244,7 +2661,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResourceCheckResult); i { + switch v := v.(*DispatchCheckResponse); i { case 0: return &v.state case 1: @@ -2256,7 +2673,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchExpandRequest); i { + switch v := v.(*ResourceCheckResult); i { case 0: return &v.state case 1: @@ -2268,7 +2685,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchExpandResponse); i { + switch v := v.(*DispatchExpandRequest); i { case 0: return &v.state case 1: @@ -2280,7 +2697,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Cursor); i { + switch v := v.(*DispatchExpandResponse); i { case 0: return &v.state case 1: @@ -2292,7 +2709,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchReachableResourcesRequest); i { + switch v := v.(*Cursor); i { case 0: return &v.state case 1: @@ -2304,7 +2721,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReachableResource); i { + switch v := v.(*DispatchLookupResources2Request); i { case 0: return &v.state case 1: @@ -2316,7 +2733,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchReachableResourcesResponse); i { + switch v := v.(*PossibleResource); i { case 0: return &v.state case 1: @@ -2328,7 +2745,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchLookupResourcesRequest); i { + switch v := v.(*DispatchLookupResources2Response); i { case 0: return &v.state case 1: @@ -2340,7 +2757,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResolvedResource); i { + switch v := v.(*DispatchReachableResourcesRequest); i { case 0: return &v.state case 1: @@ -2352,7 +2769,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchLookupResourcesResponse); i { + switch v := v.(*ReachableResource); i { case 0: return &v.state case 1: @@ -2364,7 +2781,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchLookupSubjectsRequest); i { + switch v := v.(*DispatchReachableResourcesResponse); i { case 0: return &v.state case 1: @@ -2376,7 +2793,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FoundSubject); i { + switch v := v.(*DispatchLookupResourcesRequest); i { case 0: return &v.state case 1: @@ -2388,7 +2805,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FoundSubjects); i { + switch v := v.(*ResolvedResource); i { case 0: return &v.state case 1: @@ -2400,7 +2817,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchLookupSubjectsResponse); i { + switch v := v.(*DispatchLookupResourcesResponse); i { case 0: return &v.state case 1: @@ -2412,7 +2829,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResolverMeta); i { + switch v := v.(*DispatchLookupSubjectsRequest); i { case 0: return &v.state case 1: @@ -2424,7 +2841,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResponseMeta); i { + switch v := v.(*FoundSubject); i { case 0: return &v.state case 1: @@ -2436,7 +2853,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DebugInformation); i { + switch v := v.(*FoundSubjects); i { case 0: return &v.state case 1: @@ -2448,6 +2865,54 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DispatchLookupSubjectsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_dispatch_v1_dispatch_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResolverMeta); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_dispatch_v1_dispatch_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResponseMeta); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_dispatch_v1_dispatch_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DebugInformation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_dispatch_v1_dispatch_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CheckDebugTrace); i { case 0: return &v.state @@ -2466,7 +2931,7 @@ func file_dispatch_v1_dispatch_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_dispatch_v1_dispatch_proto_rawDesc, NumEnums: 7, - NumMessages: 23, + NumMessages: 27, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/proto/dispatch/v1/dispatch.pb.validate.go b/pkg/proto/dispatch/v1/dispatch.pb.validate.go index bfe4243241..0b0a38ab6d 100644 --- a/pkg/proto/dispatch/v1/dispatch.pb.validate.go +++ b/pkg/proto/dispatch/v1/dispatch.pb.validate.go @@ -181,6 +181,40 @@ func (m *DispatchCheckRequest) validate(all bool) error { // no validation rules for Debug + for idx, item := range m.GetCheckHints() { + _, _ = idx, item + + if all { + switch v := interface{}(item).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchCheckRequestValidationError{ + field: fmt.Sprintf("CheckHints[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchCheckRequestValidationError{ + field: fmt.Sprintf("CheckHints[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchCheckRequestValidationError{ + field: fmt.Sprintf("CheckHints[%v]", idx), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + if len(errors) > 0 { return DispatchCheckRequestMultiError(errors) } @@ -261,6 +295,194 @@ var _ interface { ErrorName() string } = DispatchCheckRequestValidationError{} +// Validate checks the field values on CheckHint 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 *CheckHint) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on CheckHint 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 CheckHintMultiError, or nil +// if none found. +func (m *CheckHint) ValidateAll() error { + return m.validate(true) +} + +func (m *CheckHint) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if all { + switch v := interface{}(m.GetResource()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, CheckHintValidationError{ + field: "Resource", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, CheckHintValidationError{ + field: "Resource", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetResource()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return CheckHintValidationError{ + field: "Resource", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetSubject()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, CheckHintValidationError{ + field: "Subject", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, CheckHintValidationError{ + field: "Subject", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetSubject()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return CheckHintValidationError{ + field: "Subject", + reason: "embedded message failed validation", + cause: err, + } + } + } + + // no validation rules for TtuComputedUsersetRelation + + if all { + switch v := interface{}(m.GetResult()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, CheckHintValidationError{ + field: "Result", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, CheckHintValidationError{ + field: "Result", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetResult()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return CheckHintValidationError{ + field: "Result", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return CheckHintMultiError(errors) + } + + return nil +} + +// CheckHintMultiError is an error wrapping multiple validation errors returned +// by CheckHint.ValidateAll() if the designated constraints aren't met. +type CheckHintMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m CheckHintMultiError) 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 CheckHintMultiError) AllErrors() []error { return m } + +// CheckHintValidationError is the validation error returned by +// CheckHint.Validate if the designated constraints aren't met. +type CheckHintValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e CheckHintValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e CheckHintValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e CheckHintValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e CheckHintValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e CheckHintValidationError) ErrorName() string { return "CheckHintValidationError" } + +// Error satisfies the builtin error interface +func (e CheckHintValidationError) 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 %sCheckHint.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = CheckHintValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = CheckHintValidationError{} + // Validate checks the field values on DispatchCheckResponse 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. @@ -1015,6 +1237,623 @@ var _ interface { ErrorName() string } = CursorValidationError{} +// Validate checks the field values on DispatchLookupResources2Request 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 *DispatchLookupResources2Request) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on DispatchLookupResources2Request 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 +// DispatchLookupResources2RequestMultiError, or nil if none found. +func (m *DispatchLookupResources2Request) ValidateAll() error { + return m.validate(true) +} + +func (m *DispatchLookupResources2Request) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if m.GetMetadata() == nil { + err := DispatchLookupResources2RequestValidationError{ + field: "Metadata", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetMetadata()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetMetadata()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2RequestValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if m.GetResourceRelation() == nil { + err := DispatchLookupResources2RequestValidationError{ + field: "ResourceRelation", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetResourceRelation()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "ResourceRelation", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "ResourceRelation", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetResourceRelation()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2RequestValidationError{ + field: "ResourceRelation", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if m.GetSubjectRelation() == nil { + err := DispatchLookupResources2RequestValidationError{ + field: "SubjectRelation", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetSubjectRelation()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "SubjectRelation", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "SubjectRelation", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetSubjectRelation()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2RequestValidationError{ + field: "SubjectRelation", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if m.GetTerminalSubject() == nil { + err := DispatchLookupResources2RequestValidationError{ + field: "TerminalSubject", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetTerminalSubject()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "TerminalSubject", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "TerminalSubject", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetTerminalSubject()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2RequestValidationError{ + field: "TerminalSubject", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetContext()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "Context", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "Context", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetContext()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2RequestValidationError{ + field: "Context", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetOptionalCursor()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "OptionalCursor", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "OptionalCursor", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetOptionalCursor()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2RequestValidationError{ + field: "OptionalCursor", + reason: "embedded message failed validation", + cause: err, + } + } + } + + // no validation rules for OptionalLimit + + if len(errors) > 0 { + return DispatchLookupResources2RequestMultiError(errors) + } + + return nil +} + +// DispatchLookupResources2RequestMultiError is an error wrapping multiple +// validation errors returned by DispatchLookupResources2Request.ValidateAll() +// if the designated constraints aren't met. +type DispatchLookupResources2RequestMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m DispatchLookupResources2RequestMultiError) 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 DispatchLookupResources2RequestMultiError) AllErrors() []error { return m } + +// DispatchLookupResources2RequestValidationError is the validation error +// returned by DispatchLookupResources2Request.Validate if the designated +// constraints aren't met. +type DispatchLookupResources2RequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e DispatchLookupResources2RequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e DispatchLookupResources2RequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e DispatchLookupResources2RequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e DispatchLookupResources2RequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e DispatchLookupResources2RequestValidationError) ErrorName() string { + return "DispatchLookupResources2RequestValidationError" +} + +// Error satisfies the builtin error interface +func (e DispatchLookupResources2RequestValidationError) 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 %sDispatchLookupResources2Request.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = DispatchLookupResources2RequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = DispatchLookupResources2RequestValidationError{} + +// Validate checks the field values on PossibleResource 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 *PossibleResource) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PossibleResource 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 +// PossibleResourceMultiError, or nil if none found. +func (m *PossibleResource) ValidateAll() error { + return m.validate(true) +} + +func (m *PossibleResource) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for ResourceId + + if len(errors) > 0 { + return PossibleResourceMultiError(errors) + } + + return nil +} + +// PossibleResourceMultiError is an error wrapping multiple validation errors +// returned by PossibleResource.ValidateAll() if the designated constraints +// aren't met. +type PossibleResourceMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PossibleResourceMultiError) 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 PossibleResourceMultiError) AllErrors() []error { return m } + +// PossibleResourceValidationError is the validation error returned by +// PossibleResource.Validate if the designated constraints aren't met. +type PossibleResourceValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PossibleResourceValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PossibleResourceValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PossibleResourceValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PossibleResourceValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PossibleResourceValidationError) ErrorName() string { return "PossibleResourceValidationError" } + +// Error satisfies the builtin error interface +func (e PossibleResourceValidationError) 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 %sPossibleResource.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PossibleResourceValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PossibleResourceValidationError{} + +// Validate checks the field values on DispatchLookupResources2Response 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 *DispatchLookupResources2Response) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on DispatchLookupResources2Response 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 +// DispatchLookupResources2ResponseMultiError, or nil if none found. +func (m *DispatchLookupResources2Response) ValidateAll() error { + return m.validate(true) +} + +func (m *DispatchLookupResources2Response) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if all { + switch v := interface{}(m.GetResource()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2ResponseValidationError{ + field: "Resource", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2ResponseValidationError{ + field: "Resource", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetResource()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2ResponseValidationError{ + field: "Resource", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetMetadata()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2ResponseValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2ResponseValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetMetadata()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2ResponseValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetAfterResponseCursor()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2ResponseValidationError{ + field: "AfterResponseCursor", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2ResponseValidationError{ + field: "AfterResponseCursor", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetAfterResponseCursor()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2ResponseValidationError{ + field: "AfterResponseCursor", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return DispatchLookupResources2ResponseMultiError(errors) + } + + return nil +} + +// DispatchLookupResources2ResponseMultiError is an error wrapping multiple +// validation errors returned by +// DispatchLookupResources2Response.ValidateAll() if the designated +// constraints aren't met. +type DispatchLookupResources2ResponseMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m DispatchLookupResources2ResponseMultiError) 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 DispatchLookupResources2ResponseMultiError) AllErrors() []error { return m } + +// DispatchLookupResources2ResponseValidationError is the validation error +// returned by DispatchLookupResources2Response.Validate if the designated +// constraints aren't met. +type DispatchLookupResources2ResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e DispatchLookupResources2ResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e DispatchLookupResources2ResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e DispatchLookupResources2ResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e DispatchLookupResources2ResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e DispatchLookupResources2ResponseValidationError) ErrorName() string { + return "DispatchLookupResources2ResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e DispatchLookupResources2ResponseValidationError) 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 %sDispatchLookupResources2Response.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = DispatchLookupResources2ResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = DispatchLookupResources2ResponseValidationError{} + // Validate checks the field values on DispatchReachableResourcesRequest 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 diff --git a/pkg/proto/dispatch/v1/dispatch_grpc.pb.go b/pkg/proto/dispatch/v1/dispatch_grpc.pb.go index 14a476ee31..3d6a8ce734 100644 --- a/pkg/proto/dispatch/v1/dispatch_grpc.pb.go +++ b/pkg/proto/dispatch/v1/dispatch_grpc.pb.go @@ -24,6 +24,7 @@ const ( DispatchService_DispatchReachableResources_FullMethodName = "/dispatch.v1.DispatchService/DispatchReachableResources" DispatchService_DispatchLookupResources_FullMethodName = "/dispatch.v1.DispatchService/DispatchLookupResources" DispatchService_DispatchLookupSubjects_FullMethodName = "/dispatch.v1.DispatchService/DispatchLookupSubjects" + DispatchService_DispatchLookupResources2_FullMethodName = "/dispatch.v1.DispatchService/DispatchLookupResources2" ) // DispatchServiceClient is the client API for DispatchService service. @@ -35,6 +36,7 @@ type DispatchServiceClient interface { DispatchReachableResources(ctx context.Context, in *DispatchReachableResourcesRequest, opts ...grpc.CallOption) (DispatchService_DispatchReachableResourcesClient, error) DispatchLookupResources(ctx context.Context, in *DispatchLookupResourcesRequest, opts ...grpc.CallOption) (DispatchService_DispatchLookupResourcesClient, error) DispatchLookupSubjects(ctx context.Context, in *DispatchLookupSubjectsRequest, opts ...grpc.CallOption) (DispatchService_DispatchLookupSubjectsClient, error) + DispatchLookupResources2(ctx context.Context, in *DispatchLookupResources2Request, opts ...grpc.CallOption) (DispatchService_DispatchLookupResources2Client, error) } type dispatchServiceClient struct { @@ -159,6 +161,38 @@ func (x *dispatchServiceDispatchLookupSubjectsClient) Recv() (*DispatchLookupSub return m, nil } +func (c *dispatchServiceClient) DispatchLookupResources2(ctx context.Context, in *DispatchLookupResources2Request, opts ...grpc.CallOption) (DispatchService_DispatchLookupResources2Client, error) { + stream, err := c.cc.NewStream(ctx, &DispatchService_ServiceDesc.Streams[3], DispatchService_DispatchLookupResources2_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &dispatchServiceDispatchLookupResources2Client{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type DispatchService_DispatchLookupResources2Client interface { + Recv() (*DispatchLookupResources2Response, error) + grpc.ClientStream +} + +type dispatchServiceDispatchLookupResources2Client struct { + grpc.ClientStream +} + +func (x *dispatchServiceDispatchLookupResources2Client) Recv() (*DispatchLookupResources2Response, error) { + m := new(DispatchLookupResources2Response) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // DispatchServiceServer is the server API for DispatchService service. // All implementations must embed UnimplementedDispatchServiceServer // for forward compatibility @@ -168,6 +202,7 @@ type DispatchServiceServer interface { DispatchReachableResources(*DispatchReachableResourcesRequest, DispatchService_DispatchReachableResourcesServer) error DispatchLookupResources(*DispatchLookupResourcesRequest, DispatchService_DispatchLookupResourcesServer) error DispatchLookupSubjects(*DispatchLookupSubjectsRequest, DispatchService_DispatchLookupSubjectsServer) error + DispatchLookupResources2(*DispatchLookupResources2Request, DispatchService_DispatchLookupResources2Server) error mustEmbedUnimplementedDispatchServiceServer() } @@ -190,6 +225,9 @@ func (UnimplementedDispatchServiceServer) DispatchLookupResources(*DispatchLooku func (UnimplementedDispatchServiceServer) DispatchLookupSubjects(*DispatchLookupSubjectsRequest, DispatchService_DispatchLookupSubjectsServer) error { return status.Errorf(codes.Unimplemented, "method DispatchLookupSubjects not implemented") } +func (UnimplementedDispatchServiceServer) DispatchLookupResources2(*DispatchLookupResources2Request, DispatchService_DispatchLookupResources2Server) error { + return status.Errorf(codes.Unimplemented, "method DispatchLookupResources2 not implemented") +} func (UnimplementedDispatchServiceServer) mustEmbedUnimplementedDispatchServiceServer() {} // UnsafeDispatchServiceServer may be embedded to opt out of forward compatibility for this service. @@ -302,6 +340,27 @@ func (x *dispatchServiceDispatchLookupSubjectsServer) Send(m *DispatchLookupSubj return x.ServerStream.SendMsg(m) } +func _DispatchService_DispatchLookupResources2_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(DispatchLookupResources2Request) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(DispatchServiceServer).DispatchLookupResources2(m, &dispatchServiceDispatchLookupResources2Server{stream}) +} + +type DispatchService_DispatchLookupResources2Server interface { + Send(*DispatchLookupResources2Response) error + grpc.ServerStream +} + +type dispatchServiceDispatchLookupResources2Server struct { + grpc.ServerStream +} + +func (x *dispatchServiceDispatchLookupResources2Server) Send(m *DispatchLookupResources2Response) error { + return x.ServerStream.SendMsg(m) +} + // DispatchService_ServiceDesc is the grpc.ServiceDesc for DispatchService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -334,6 +393,11 @@ var DispatchService_ServiceDesc = grpc.ServiceDesc{ Handler: _DispatchService_DispatchLookupSubjects_Handler, ServerStreams: true, }, + { + StreamName: "DispatchLookupResources2", + Handler: _DispatchService_DispatchLookupResources2_Handler, + ServerStreams: true, + }, }, Metadata: "dispatch/v1/dispatch.proto", } diff --git a/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go b/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go index 78aac4308f..70fa90489f 100644 --- a/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go +++ b/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go @@ -51,6 +51,13 @@ func (m *DispatchCheckRequest) CloneVT() *DispatchCheckRequest { r.Subject = proto.Clone(rhs).(*v1.ObjectAndRelation) } } + if rhs := m.CheckHints; rhs != nil { + tmpContainer := make([]*CheckHint, len(rhs)) + for k, v := range rhs { + tmpContainer[k] = v.CloneVT() + } + r.CheckHints = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -62,6 +69,38 @@ func (m *DispatchCheckRequest) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *CheckHint) CloneVT() *CheckHint { + if m == nil { + return (*CheckHint)(nil) + } + r := new(CheckHint) + r.TtuComputedUsersetRelation = m.TtuComputedUsersetRelation + r.Result = m.Result.CloneVT() + if rhs := m.Resource; rhs != nil { + if vtpb, ok := interface{}(rhs).(interface{ CloneVT() *v1.ObjectAndRelation }); ok { + r.Resource = vtpb.CloneVT() + } else { + r.Resource = proto.Clone(rhs).(*v1.ObjectAndRelation) + } + } + if rhs := m.Subject; rhs != nil { + if vtpb, ok := interface{}(rhs).(interface{ CloneVT() *v1.ObjectAndRelation }); ok { + r.Subject = vtpb.CloneVT() + } else { + r.Subject = proto.Clone(rhs).(*v1.ObjectAndRelation) + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *CheckHint) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (m *DispatchCheckResponse) CloneVT() *DispatchCheckResponse { if m == nil { return (*DispatchCheckResponse)(nil) @@ -188,6 +227,98 @@ func (m *Cursor) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *DispatchLookupResources2Request) CloneVT() *DispatchLookupResources2Request { + if m == nil { + return (*DispatchLookupResources2Request)(nil) + } + r := new(DispatchLookupResources2Request) + r.Metadata = m.Metadata.CloneVT() + r.Context = (*structpb.Struct)((*structpb1.Struct)(m.Context).CloneVT()) + r.OptionalCursor = m.OptionalCursor.CloneVT() + r.OptionalLimit = m.OptionalLimit + if rhs := m.ResourceRelation; rhs != nil { + if vtpb, ok := interface{}(rhs).(interface{ CloneVT() *v1.RelationReference }); ok { + r.ResourceRelation = vtpb.CloneVT() + } else { + r.ResourceRelation = proto.Clone(rhs).(*v1.RelationReference) + } + } + if rhs := m.SubjectRelation; rhs != nil { + if vtpb, ok := interface{}(rhs).(interface{ CloneVT() *v1.RelationReference }); ok { + r.SubjectRelation = vtpb.CloneVT() + } else { + r.SubjectRelation = proto.Clone(rhs).(*v1.RelationReference) + } + } + if rhs := m.SubjectIds; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.SubjectIds = tmpContainer + } + if rhs := m.TerminalSubject; rhs != nil { + if vtpb, ok := interface{}(rhs).(interface{ CloneVT() *v1.ObjectAndRelation }); ok { + r.TerminalSubject = vtpb.CloneVT() + } else { + r.TerminalSubject = proto.Clone(rhs).(*v1.ObjectAndRelation) + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *DispatchLookupResources2Request) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *PossibleResource) CloneVT() *PossibleResource { + if m == nil { + return (*PossibleResource)(nil) + } + r := new(PossibleResource) + r.ResourceId = m.ResourceId + if rhs := m.ForSubjectIds; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.ForSubjectIds = tmpContainer + } + if rhs := m.MissingContextParams; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.MissingContextParams = tmpContainer + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *PossibleResource) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *DispatchLookupResources2Response) CloneVT() *DispatchLookupResources2Response { + if m == nil { + return (*DispatchLookupResources2Response)(nil) + } + r := new(DispatchLookupResources2Response) + r.Resource = m.Resource.CloneVT() + r.Metadata = m.Metadata.CloneVT() + r.AfterResponseCursor = m.AfterResponseCursor.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *DispatchLookupResources2Response) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (m *DispatchReachableResourcesRequest) CloneVT() *DispatchReachableResourcesRequest { if m == nil { return (*DispatchReachableResourcesRequest)(nil) @@ -595,6 +726,23 @@ func (this *DispatchCheckRequest) EqualVT(that *DispatchCheckRequest) bool { if this.Debug != that.Debug { return false } + if len(this.CheckHints) != len(that.CheckHints) { + return false + } + for i, vx := range this.CheckHints { + vy := that.CheckHints[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &CheckHint{} + } + if q == nil { + q = &CheckHint{} + } + if !p.EqualVT(q) { + return false + } + } + } return string(this.unknownFields) == string(that.unknownFields) } @@ -605,6 +753,46 @@ func (this *DispatchCheckRequest) EqualMessageVT(thatMsg proto.Message) bool { } return this.EqualVT(that) } +func (this *CheckHint) EqualVT(that *CheckHint) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if equal, ok := interface{}(this.Resource).(interface { + EqualVT(*v1.ObjectAndRelation) bool + }); ok { + if !equal.EqualVT(that.Resource) { + return false + } + } else if !proto.Equal(this.Resource, that.Resource) { + return false + } + if equal, ok := interface{}(this.Subject).(interface { + EqualVT(*v1.ObjectAndRelation) bool + }); ok { + if !equal.EqualVT(that.Subject) { + return false + } + } else if !proto.Equal(this.Subject, that.Subject) { + return false + } + if this.TtuComputedUsersetRelation != that.TtuComputedUsersetRelation { + return false + } + if !this.Result.EqualVT(that.Result) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *CheckHint) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*CheckHint) + if !ok { + return false + } + return this.EqualVT(that) +} func (this *DispatchCheckResponse) EqualVT(that *DispatchCheckResponse) bool { if this == that { return true @@ -768,7 +956,7 @@ func (this *Cursor) EqualMessageVT(thatMsg proto.Message) bool { } return this.EqualVT(that) } -func (this *DispatchReachableResourcesRequest) EqualVT(that *DispatchReachableResourcesRequest) bool { +func (this *DispatchLookupResources2Request) EqualVT(that *DispatchLookupResources2Request) bool { if this == that { return true } else if this == nil || that == nil { @@ -804,6 +992,18 @@ func (this *DispatchReachableResourcesRequest) EqualVT(that *DispatchReachableRe return false } } + if equal, ok := interface{}(this.TerminalSubject).(interface { + EqualVT(*v1.ObjectAndRelation) bool + }); ok { + if !equal.EqualVT(that.TerminalSubject) { + return false + } + } else if !proto.Equal(this.TerminalSubject, that.TerminalSubject) { + return false + } + if !(*structpb1.Struct)(this.Context).EqualVT((*structpb1.Struct)(that.Context)) { + return false + } if !this.OptionalCursor.EqualVT(that.OptionalCursor) { return false } @@ -813,14 +1013,14 @@ func (this *DispatchReachableResourcesRequest) EqualVT(that *DispatchReachableRe return string(this.unknownFields) == string(that.unknownFields) } -func (this *DispatchReachableResourcesRequest) EqualMessageVT(thatMsg proto.Message) bool { - that, ok := thatMsg.(*DispatchReachableResourcesRequest) +func (this *DispatchLookupResources2Request) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*DispatchLookupResources2Request) if !ok { return false } return this.EqualVT(that) } -func (this *ReachableResource) EqualVT(that *ReachableResource) bool { +func (this *PossibleResource) EqualVT(that *PossibleResource) bool { if this == that { return true } else if this == nil || that == nil { @@ -829,9 +1029,6 @@ func (this *ReachableResource) EqualVT(that *ReachableResource) bool { if this.ResourceId != that.ResourceId { return false } - if this.ResultStatus != that.ResultStatus { - return false - } if len(this.ForSubjectIds) != len(that.ForSubjectIds) { return false } @@ -841,17 +1038,26 @@ func (this *ReachableResource) EqualVT(that *ReachableResource) bool { return false } } + if len(this.MissingContextParams) != len(that.MissingContextParams) { + return false + } + for i, vx := range this.MissingContextParams { + vy := that.MissingContextParams[i] + if vx != vy { + return false + } + } return string(this.unknownFields) == string(that.unknownFields) } -func (this *ReachableResource) EqualMessageVT(thatMsg proto.Message) bool { - that, ok := thatMsg.(*ReachableResource) +func (this *PossibleResource) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*PossibleResource) if !ok { return false } return this.EqualVT(that) } -func (this *DispatchReachableResourcesResponse) EqualVT(that *DispatchReachableResourcesResponse) bool { +func (this *DispatchLookupResources2Response) EqualVT(that *DispatchLookupResources2Response) bool { if this == that { return true } else if this == nil || that == nil { @@ -869,14 +1075,14 @@ func (this *DispatchReachableResourcesResponse) EqualVT(that *DispatchReachableR return string(this.unknownFields) == string(that.unknownFields) } -func (this *DispatchReachableResourcesResponse) EqualMessageVT(thatMsg proto.Message) bool { - that, ok := thatMsg.(*DispatchReachableResourcesResponse) +func (this *DispatchLookupResources2Response) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*DispatchLookupResources2Response) if !ok { return false } return this.EqualVT(that) } -func (this *DispatchLookupResourcesRequest) EqualVT(that *DispatchLookupResourcesRequest) bool { +func (this *DispatchReachableResourcesRequest) EqualVT(that *DispatchReachableResourcesRequest) bool { if this == that { return true } else if this == nil || that == nil { @@ -885,44 +1091,50 @@ func (this *DispatchLookupResourcesRequest) EqualVT(that *DispatchLookupResource if !this.Metadata.EqualVT(that.Metadata) { return false } - if equal, ok := interface{}(this.ObjectRelation).(interface { + if equal, ok := interface{}(this.ResourceRelation).(interface { EqualVT(*v1.RelationReference) bool }); ok { - if !equal.EqualVT(that.ObjectRelation) { + if !equal.EqualVT(that.ResourceRelation) { return false } - } else if !proto.Equal(this.ObjectRelation, that.ObjectRelation) { + } else if !proto.Equal(this.ResourceRelation, that.ResourceRelation) { return false } - if equal, ok := interface{}(this.Subject).(interface { - EqualVT(*v1.ObjectAndRelation) bool + if equal, ok := interface{}(this.SubjectRelation).(interface { + EqualVT(*v1.RelationReference) bool }); ok { - if !equal.EqualVT(that.Subject) { + if !equal.EqualVT(that.SubjectRelation) { return false } - } else if !proto.Equal(this.Subject, that.Subject) { + } else if !proto.Equal(this.SubjectRelation, that.SubjectRelation) { return false } - if this.OptionalLimit != that.OptionalLimit { + if len(this.SubjectIds) != len(that.SubjectIds) { return false } - if !(*structpb1.Struct)(this.Context).EqualVT((*structpb1.Struct)(that.Context)) { - return false + for i, vx := range this.SubjectIds { + vy := that.SubjectIds[i] + if vx != vy { + return false + } } if !this.OptionalCursor.EqualVT(that.OptionalCursor) { return false } + if this.OptionalLimit != that.OptionalLimit { + return false + } return string(this.unknownFields) == string(that.unknownFields) } -func (this *DispatchLookupResourcesRequest) EqualMessageVT(thatMsg proto.Message) bool { - that, ok := thatMsg.(*DispatchLookupResourcesRequest) +func (this *DispatchReachableResourcesRequest) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*DispatchReachableResourcesRequest) if !ok { return false } return this.EqualVT(that) } -func (this *ResolvedResource) EqualVT(that *ResolvedResource) bool { +func (this *ReachableResource) EqualVT(that *ReachableResource) bool { if this == that { return true } else if this == nil || that == nil { @@ -931,14 +1143,14 @@ func (this *ResolvedResource) EqualVT(that *ResolvedResource) bool { if this.ResourceId != that.ResourceId { return false } - if this.Permissionship != that.Permissionship { + if this.ResultStatus != that.ResultStatus { return false } - if len(this.MissingRequiredContext) != len(that.MissingRequiredContext) { + if len(this.ForSubjectIds) != len(that.ForSubjectIds) { return false } - for i, vx := range this.MissingRequiredContext { - vy := that.MissingRequiredContext[i] + for i, vx := range this.ForSubjectIds { + vy := that.ForSubjectIds[i] if vx != vy { return false } @@ -946,23 +1158,23 @@ func (this *ResolvedResource) EqualVT(that *ResolvedResource) bool { return string(this.unknownFields) == string(that.unknownFields) } -func (this *ResolvedResource) EqualMessageVT(thatMsg proto.Message) bool { - that, ok := thatMsg.(*ResolvedResource) +func (this *ReachableResource) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*ReachableResource) if !ok { return false } return this.EqualVT(that) } -func (this *DispatchLookupResourcesResponse) EqualVT(that *DispatchLookupResourcesResponse) bool { +func (this *DispatchReachableResourcesResponse) EqualVT(that *DispatchReachableResourcesResponse) bool { if this == that { return true } else if this == nil || that == nil { return false } - if !this.Metadata.EqualVT(that.Metadata) { + if !this.Resource.EqualVT(that.Resource) { return false } - if !this.ResolvedResource.EqualVT(that.ResolvedResource) { + if !this.Metadata.EqualVT(that.Metadata) { return false } if !this.AfterResponseCursor.EqualVT(that.AfterResponseCursor) { @@ -971,14 +1183,14 @@ func (this *DispatchLookupResourcesResponse) EqualVT(that *DispatchLookupResourc return string(this.unknownFields) == string(that.unknownFields) } -func (this *DispatchLookupResourcesResponse) EqualMessageVT(thatMsg proto.Message) bool { - that, ok := thatMsg.(*DispatchLookupResourcesResponse) +func (this *DispatchReachableResourcesResponse) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*DispatchReachableResourcesResponse) if !ok { return false } return this.EqualVT(that) } -func (this *DispatchLookupSubjectsRequest) EqualVT(that *DispatchLookupSubjectsRequest) bool { +func (this *DispatchLookupResourcesRequest) EqualVT(that *DispatchLookupResourcesRequest) bool { if this == that { return true } else if this == nil || that == nil { @@ -987,35 +1199,137 @@ func (this *DispatchLookupSubjectsRequest) EqualVT(that *DispatchLookupSubjectsR if !this.Metadata.EqualVT(that.Metadata) { return false } - if equal, ok := interface{}(this.ResourceRelation).(interface { + if equal, ok := interface{}(this.ObjectRelation).(interface { EqualVT(*v1.RelationReference) bool }); ok { - if !equal.EqualVT(that.ResourceRelation) { + if !equal.EqualVT(that.ObjectRelation) { return false } - } else if !proto.Equal(this.ResourceRelation, that.ResourceRelation) { - return false - } - if len(this.ResourceIds) != len(that.ResourceIds) { + } else if !proto.Equal(this.ObjectRelation, that.ObjectRelation) { return false } - for i, vx := range this.ResourceIds { - vy := that.ResourceIds[i] - if vx != vy { - return false - } - } - if equal, ok := interface{}(this.SubjectRelation).(interface { - EqualVT(*v1.RelationReference) bool + if equal, ok := interface{}(this.Subject).(interface { + EqualVT(*v1.ObjectAndRelation) bool }); ok { - if !equal.EqualVT(that.SubjectRelation) { + if !equal.EqualVT(that.Subject) { return false } - } else if !proto.Equal(this.SubjectRelation, that.SubjectRelation) { + } else if !proto.Equal(this.Subject, that.Subject) { return false } - return string(this.unknownFields) == string(that.unknownFields) -} + if this.OptionalLimit != that.OptionalLimit { + return false + } + if !(*structpb1.Struct)(this.Context).EqualVT((*structpb1.Struct)(that.Context)) { + return false + } + if !this.OptionalCursor.EqualVT(that.OptionalCursor) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *DispatchLookupResourcesRequest) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*DispatchLookupResourcesRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *ResolvedResource) EqualVT(that *ResolvedResource) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.ResourceId != that.ResourceId { + return false + } + if this.Permissionship != that.Permissionship { + return false + } + if len(this.MissingRequiredContext) != len(that.MissingRequiredContext) { + return false + } + for i, vx := range this.MissingRequiredContext { + vy := that.MissingRequiredContext[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *ResolvedResource) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*ResolvedResource) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *DispatchLookupResourcesResponse) EqualVT(that *DispatchLookupResourcesResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if !this.Metadata.EqualVT(that.Metadata) { + return false + } + if !this.ResolvedResource.EqualVT(that.ResolvedResource) { + return false + } + if !this.AfterResponseCursor.EqualVT(that.AfterResponseCursor) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *DispatchLookupResourcesResponse) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*DispatchLookupResourcesResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *DispatchLookupSubjectsRequest) EqualVT(that *DispatchLookupSubjectsRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if !this.Metadata.EqualVT(that.Metadata) { + return false + } + if equal, ok := interface{}(this.ResourceRelation).(interface { + EqualVT(*v1.RelationReference) bool + }); ok { + if !equal.EqualVT(that.ResourceRelation) { + return false + } + } else if !proto.Equal(this.ResourceRelation, that.ResourceRelation) { + return false + } + if len(this.ResourceIds) != len(that.ResourceIds) { + return false + } + for i, vx := range this.ResourceIds { + vy := that.ResourceIds[i] + if vx != vy { + return false + } + } + if equal, ok := interface{}(this.SubjectRelation).(interface { + EqualVT(*v1.RelationReference) bool + }); ok { + if !equal.EqualVT(that.SubjectRelation) { + return false + } + } else if !proto.Equal(this.SubjectRelation, that.SubjectRelation) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} func (this *DispatchLookupSubjectsRequest) EqualMessageVT(thatMsg proto.Message) bool { that, ok := thatMsg.(*DispatchLookupSubjectsRequest) @@ -1311,6 +1625,18 @@ func (m *DispatchCheckRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.CheckHints) > 0 { + for iNdEx := len(m.CheckHints) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.CheckHints[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x3a + } + } if m.Debug != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Debug)) i-- @@ -1387,6 +1713,100 @@ func (m *DispatchCheckRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *CheckHint) 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 *CheckHint) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *CheckHint) 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.Result != nil { + size, err := m.Result.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x22 + } + if len(m.TtuComputedUsersetRelation) > 0 { + i -= len(m.TtuComputedUsersetRelation) + copy(dAtA[i:], m.TtuComputedUsersetRelation) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.TtuComputedUsersetRelation))) + i-- + dAtA[i] = 0x1a + } + if m.Subject != nil { + if vtmsg, ok := interface{}(m.Subject).(interface { + MarshalToSizedBufferVT([]byte) (int, error) + }); ok { + size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + } else { + encoded, err := proto.Marshal(m.Subject) + if err != nil { + return 0, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) + } + i-- + dAtA[i] = 0x12 + } + if m.Resource != nil { + if vtmsg, ok := interface{}(m.Resource).(interface { + MarshalToSizedBufferVT([]byte) (int, error) + }); ok { + size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + } else { + encoded, err := proto.Marshal(m.Resource) + if err != nil { + return 0, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *DispatchCheckResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -1703,7 +2123,7 @@ func (m *Cursor) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *DispatchReachableResourcesRequest) MarshalVT() (dAtA []byte, err error) { +func (m *DispatchLookupResources2Request) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -1716,12 +2136,12 @@ func (m *DispatchReachableResourcesRequest) MarshalVT() (dAtA []byte, err error) return dAtA[:n], nil } -func (m *DispatchReachableResourcesRequest) MarshalToVT(dAtA []byte) (int, error) { +func (m *DispatchLookupResources2Request) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *DispatchReachableResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *DispatchLookupResources2Request) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -1736,7 +2156,7 @@ func (m *DispatchReachableResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) if m.OptionalLimit != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.OptionalLimit)) i-- - dAtA[i] = 0x30 + dAtA[i] = 0x40 } if m.OptionalCursor != nil { size, err := m.OptionalCursor.MarshalToSizedBufferVT(dAtA[:i]) @@ -1746,6 +2166,38 @@ func (m *DispatchReachableResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- + dAtA[i] = 0x3a + } + if m.Context != nil { + size, err := (*structpb1.Struct)(m.Context).MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x32 + } + if m.TerminalSubject != nil { + if vtmsg, ok := interface{}(m.TerminalSubject).(interface { + MarshalToSizedBufferVT([]byte) (int, error) + }); ok { + size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + } else { + encoded, err := proto.Marshal(m.TerminalSubject) + if err != nil { + return 0, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) + } + i-- dAtA[i] = 0x2a } if len(m.SubjectIds) > 0 { @@ -1814,7 +2266,7 @@ func (m *DispatchReachableResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) return len(dAtA) - i, nil } -func (m *ReachableResource) MarshalVT() (dAtA []byte, err error) { +func (m *PossibleResource) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -1827,12 +2279,12 @@ func (m *ReachableResource) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *ReachableResource) MarshalToVT(dAtA []byte) (int, error) { +func (m *PossibleResource) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *ReachableResource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *PossibleResource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -1844,20 +2296,24 @@ func (m *ReachableResource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.MissingContextParams) > 0 { + for iNdEx := len(m.MissingContextParams) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.MissingContextParams[iNdEx]) + copy(dAtA[i:], m.MissingContextParams[iNdEx]) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.MissingContextParams[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } if len(m.ForSubjectIds) > 0 { for iNdEx := len(m.ForSubjectIds) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ForSubjectIds[iNdEx]) copy(dAtA[i:], m.ForSubjectIds[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ForSubjectIds[iNdEx]))) i-- - dAtA[i] = 0x1a + dAtA[i] = 0x12 } } - if m.ResultStatus != 0 { - i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ResultStatus)) - i-- - dAtA[i] = 0x10 - } if len(m.ResourceId) > 0 { i -= len(m.ResourceId) copy(dAtA[i:], m.ResourceId) @@ -1868,7 +2324,7 @@ func (m *ReachableResource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *DispatchReachableResourcesResponse) MarshalVT() (dAtA []byte, err error) { +func (m *DispatchLookupResources2Response) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -1881,12 +2337,12 @@ func (m *DispatchReachableResourcesResponse) MarshalVT() (dAtA []byte, err error return dAtA[:n], nil } -func (m *DispatchReachableResourcesResponse) MarshalToVT(dAtA []byte) (int, error) { +func (m *DispatchLookupResources2Response) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *DispatchReachableResourcesResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *DispatchLookupResources2Response) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -1931,7 +2387,7 @@ func (m *DispatchReachableResourcesResponse) MarshalToSizedBufferVT(dAtA []byte) return len(dAtA) - i, nil } -func (m *DispatchLookupResourcesRequest) MarshalVT() (dAtA []byte, err error) { +func (m *DispatchReachableResourcesRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -1944,7 +2400,235 @@ func (m *DispatchLookupResourcesRequest) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *DispatchLookupResourcesRequest) MarshalToVT(dAtA []byte) (int, error) { +func (m *DispatchReachableResourcesRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DispatchReachableResourcesRequest) 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.OptionalLimit != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.OptionalLimit)) + i-- + dAtA[i] = 0x30 + } + if m.OptionalCursor != nil { + size, err := m.OptionalCursor.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a + } + if len(m.SubjectIds) > 0 { + for iNdEx := len(m.SubjectIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.SubjectIds[iNdEx]) + copy(dAtA[i:], m.SubjectIds[iNdEx]) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SubjectIds[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if m.SubjectRelation != nil { + if vtmsg, ok := interface{}(m.SubjectRelation).(interface { + MarshalToSizedBufferVT([]byte) (int, error) + }); ok { + size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + } else { + encoded, err := proto.Marshal(m.SubjectRelation) + if err != nil { + return 0, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) + } + i-- + dAtA[i] = 0x1a + } + if m.ResourceRelation != nil { + if vtmsg, ok := interface{}(m.ResourceRelation).(interface { + MarshalToSizedBufferVT([]byte) (int, error) + }); ok { + size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + } else { + encoded, err := proto.Marshal(m.ResourceRelation) + if err != nil { + return 0, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) + } + i-- + dAtA[i] = 0x12 + } + if m.Metadata != nil { + size, err := m.Metadata.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ReachableResource) 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 *ReachableResource) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ReachableResource) 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.ForSubjectIds) > 0 { + for iNdEx := len(m.ForSubjectIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ForSubjectIds[iNdEx]) + copy(dAtA[i:], m.ForSubjectIds[iNdEx]) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ForSubjectIds[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if m.ResultStatus != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ResultStatus)) + i-- + dAtA[i] = 0x10 + } + if len(m.ResourceId) > 0 { + i -= len(m.ResourceId) + copy(dAtA[i:], m.ResourceId) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ResourceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DispatchReachableResourcesResponse) 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 *DispatchReachableResourcesResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DispatchReachableResourcesResponse) 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.AfterResponseCursor != nil { + size, err := m.AfterResponseCursor.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x1a + } + if m.Metadata != nil { + size, err := m.Metadata.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if m.Resource != nil { + size, err := m.Resource.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DispatchLookupResourcesRequest) 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 *DispatchLookupResourcesRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } @@ -2744,34 +3428,78 @@ func (m *DispatchCheckRequest) SizeVT() (n int) { if m.Debug != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Debug)) } + if len(m.CheckHints) > 0 { + for _, e := range m.CheckHints { + l = e.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } n += len(m.unknownFields) return n } -func (m *DispatchCheckResponse) SizeVT() (n int) { +func (m *CheckHint) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l - if m.Metadata != nil { - l = m.Metadata.SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if len(m.ResultsByResourceId) > 0 { - for k, v := range m.ResultsByResourceId { - _ = k - _ = v - l = 0 - if v != nil { - l = v.SizeVT() - } - l += 1 + protohelpers.SizeOfVarint(uint64(l)) - mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l - n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) + if m.Resource != nil { + if size, ok := interface{}(m.Resource).(interface { + SizeVT() int + }); ok { + l = size.SizeVT() + } else { + l = proto.Size(m.Resource) } + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } - n += len(m.unknownFields) + if m.Subject != nil { + if size, ok := interface{}(m.Subject).(interface { + SizeVT() int + }); ok { + l = size.SizeVT() + } else { + l = proto.Size(m.Subject) + } + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.TtuComputedUsersetRelation) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Result != nil { + l = m.Result.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *DispatchCheckResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Metadata != nil { + l = m.Metadata.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if len(m.ResultsByResourceId) > 0 { + for k, v := range m.ResultsByResourceId { + _ = k + _ = v + l = 0 + if v != nil { + l = v.SizeVT() + } + l += 1 + protohelpers.SizeOfVarint(uint64(l)) + mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l + n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) + } + } + n += len(m.unknownFields) return n } @@ -2874,6 +3602,115 @@ func (m *Cursor) SizeVT() (n int) { return n } +func (m *DispatchLookupResources2Request) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Metadata != nil { + l = m.Metadata.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.ResourceRelation != nil { + if size, ok := interface{}(m.ResourceRelation).(interface { + SizeVT() int + }); ok { + l = size.SizeVT() + } else { + l = proto.Size(m.ResourceRelation) + } + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.SubjectRelation != nil { + if size, ok := interface{}(m.SubjectRelation).(interface { + SizeVT() int + }); ok { + l = size.SizeVT() + } else { + l = proto.Size(m.SubjectRelation) + } + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if len(m.SubjectIds) > 0 { + for _, s := range m.SubjectIds { + l = len(s) + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } + if m.TerminalSubject != nil { + if size, ok := interface{}(m.TerminalSubject).(interface { + SizeVT() int + }); ok { + l = size.SizeVT() + } else { + l = proto.Size(m.TerminalSubject) + } + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Context != nil { + l = (*structpb1.Struct)(m.Context).SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.OptionalCursor != nil { + l = m.OptionalCursor.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.OptionalLimit != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.OptionalLimit)) + } + n += len(m.unknownFields) + return n +} + +func (m *PossibleResource) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ResourceId) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if len(m.ForSubjectIds) > 0 { + for _, s := range m.ForSubjectIds { + l = len(s) + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } + if len(m.MissingContextParams) > 0 { + for _, s := range m.MissingContextParams { + l = len(s) + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *DispatchLookupResources2Response) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Resource != nil { + l = m.Resource.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Metadata != nil { + l = m.Metadata.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.AfterResponseCursor != nil { + l = m.AfterResponseCursor.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + func (m *DispatchReachableResourcesRequest) SizeVT() (n int) { if m == nil { return 0 @@ -3497,6 +4334,40 @@ func (m *DispatchCheckRequest) UnmarshalVT(dAtA []byte) error { break } } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CheckHints", 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 + } + m.CheckHints = append(m.CheckHints, &CheckHint{}) + if err := m.CheckHints[len(m.CheckHints)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) @@ -3519,7 +4390,7 @@ func (m *DispatchCheckRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *DispatchCheckResponse) UnmarshalVT(dAtA []byte) error { +func (m *CheckHint) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3542,15 +4413,15 @@ func (m *DispatchCheckResponse) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: DispatchCheckResponse: wiretype end group for non-group") + return fmt.Errorf("proto: CheckHint: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: DispatchCheckResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CheckHint: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Resource", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -3577,16 +4448,24 @@ func (m *DispatchCheckResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Metadata == nil { - m.Metadata = &ResponseMeta{} + if m.Resource == nil { + m.Resource = &v1.ObjectAndRelation{} } - if err := m.Metadata.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { - return err + if unmarshal, ok := interface{}(m.Resource).(interface { + UnmarshalVT([]byte) error + }); ok { + if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + } else { + if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Resource); err != nil { + return err + } } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ResultsByResourceId", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Subject", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -3613,63 +4492,262 @@ func (m *DispatchCheckResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.ResultsByResourceId == nil { - m.ResultsByResourceId = make(map[string]*ResourceCheckResult) + if m.Subject == nil { + m.Subject = &v1.ObjectAndRelation{} } - var mapkey string - var mapvalue *ResourceCheckResult - for iNdEx < postIndex { - entryPreIndex := 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 - } + if unmarshal, ok := interface{}(m.Subject).(interface { + UnmarshalVT([]byte) error + }); ok { + if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return protohelpers.ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return protohelpers.ErrInvalidLength - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey < 0 { - return protohelpers.ErrInvalidLength - } - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var mapmsglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return protohelpers.ErrIntOverflow + } else { + if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Subject); err != nil { + return err + } + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TtuComputedUsersetRelation", 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.TtuComputedUsersetRelation = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", 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.Result == nil { + m.Result = &ResourceCheckResult{} + } + if err := m.Result.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 *DispatchCheckResponse) 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: DispatchCheckResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DispatchCheckResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", 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.Metadata == nil { + m.Metadata = &ResponseMeta{} + } + if err := m.Metadata.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResultsByResourceId", 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.ResultsByResourceId == nil { + m.ResultsByResourceId = make(map[string]*ResourceCheckResult) + } + var mapkey string + var mapvalue *ResourceCheckResult + for iNdEx < postIndex { + entryPreIndex := 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) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protohelpers.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protohelpers.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF @@ -4264,6 +5342,654 @@ func (m *Cursor) UnmarshalVT(dAtA []byte) error { } return nil } +func (m *DispatchLookupResources2Request) 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: DispatchLookupResources2Request: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DispatchLookupResources2Request: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", 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.Metadata == nil { + m.Metadata = &ResolverMeta{} + } + if err := m.Metadata.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceRelation", 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.ResourceRelation == nil { + m.ResourceRelation = &v1.RelationReference{} + } + if unmarshal, ok := interface{}(m.ResourceRelation).(interface { + UnmarshalVT([]byte) error + }); ok { + if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + } else { + if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ResourceRelation); err != nil { + return err + } + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubjectRelation", 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.SubjectRelation == nil { + m.SubjectRelation = &v1.RelationReference{} + } + if unmarshal, ok := interface{}(m.SubjectRelation).(interface { + UnmarshalVT([]byte) error + }); ok { + if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + } else { + if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.SubjectRelation); err != nil { + return err + } + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubjectIds", 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.SubjectIds = append(m.SubjectIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TerminalSubject", 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.TerminalSubject == nil { + m.TerminalSubject = &v1.ObjectAndRelation{} + } + if unmarshal, ok := interface{}(m.TerminalSubject).(interface { + UnmarshalVT([]byte) error + }); ok { + if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + } else { + if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.TerminalSubject); err != nil { + return err + } + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Context", 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.Context == nil { + m.Context = &structpb.Struct{} + } + if err := (*structpb1.Struct)(m.Context).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OptionalCursor", 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.OptionalCursor == nil { + m.OptionalCursor = &Cursor{} + } + if err := m.OptionalCursor.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OptionalLimit", wireType) + } + m.OptionalLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OptionalLimit |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *PossibleResource) 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: PossibleResource: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PossibleResource: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", 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.ResourceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ForSubjectIds", 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.ForSubjectIds = append(m.ForSubjectIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MissingContextParams", 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.MissingContextParams = append(m.MissingContextParams, 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 *DispatchLookupResources2Response) 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: DispatchLookupResources2Response: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DispatchLookupResources2Response: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Resource", 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.Resource == nil { + m.Resource = &PossibleResource{} + } + if err := m.Resource.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", 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.Metadata == nil { + m.Metadata = &ResponseMeta{} + } + if err := m.Metadata.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AfterResponseCursor", 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.AfterResponseCursor == nil { + m.AfterResponseCursor = &Cursor{} + } + if err := m.AfterResponseCursor.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 *DispatchReachableResourcesRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/pkg/typesystem/reachabilitygraph.go b/pkg/typesystem/reachabilitygraph.go index 828dee20c1..23c5d8da5b 100644 --- a/pkg/typesystem/reachabilitygraph.go +++ b/pkg/typesystem/reachabilitygraph.go @@ -67,6 +67,14 @@ func (re ReachabilityEntrypoint) EntrypointKind() core.ReachabilityEntrypoint_Re return re.re.Kind } +// ComputedUsersetRelation returns the tupleset relation of the computed userset, if any. +func (re ReachabilityEntrypoint) ComputedUsersetRelation() (string, error) { + if re.EntrypointKind() == core.ReachabilityEntrypoint_RELATION_ENTRYPOINT { + return "", fmt.Errorf("cannot call ComputedUsersetRelation for kind %v", re.EntrypointKind()) + } + return re.re.ComputedUsersetRelation, nil +} + // TuplesetRelation returns the tupleset relation of the TTU, if a TUPLESET_TO_USERSET_ENTRYPOINT. func (re ReachabilityEntrypoint) TuplesetRelation() (string, error) { if re.EntrypointKind() != core.ReachabilityEntrypoint_TUPLESET_TO_USERSET_ENTRYPOINT { @@ -85,6 +93,11 @@ func (re ReachabilityEntrypoint) DirectRelation() (*core.RelationReference, erro return re.re.TargetRelation, nil } +// TargetNamespace returns the namespace for the entrypoint's target relation. +func (re ReachabilityEntrypoint) TargetNamespace() string { + return re.re.TargetRelation.Namespace +} + // ContainingRelationOrPermission is the relation or permission containing this entrypoint. func (re ReachabilityEntrypoint) ContainingRelationOrPermission() *core.RelationReference { return re.parentRelation diff --git a/pkg/typesystem/reachabilitygraphbuilder.go b/pkg/typesystem/reachabilitygraphbuilder.go index 1abbf5297f..208c432585 100644 --- a/pkg/typesystem/reachabilitygraphbuilder.go +++ b/pkg/typesystem/reachabilitygraphbuilder.go @@ -82,9 +82,10 @@ func computeRewriteOpReachability(ctx context.Context, children []*core.SetOpera case *core.SetOperation_Child_ComputedUserset: // A computed userset adds an entrypoint indicating that the relation is rewritten. err := addSubjectEntrypoint(graph, ts.nsDef.Name, child.ComputedUserset.Relation, &core.ReachabilityEntrypoint{ - Kind: core.ReachabilityEntrypoint_COMPUTED_USERSET_ENTRYPOINT, - TargetRelation: rr, - ResultStatus: operationResultState, + Kind: core.ReachabilityEntrypoint_COMPUTED_USERSET_ENTRYPOINT, + TargetRelation: rr, + ComputedUsersetRelation: child.ComputedUserset.Relation, + ResultStatus: operationResultState, }) if err != nil { return err @@ -187,10 +188,11 @@ func computeTTUReachability( 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, + Kind: core.ReachabilityEntrypoint_TUPLESET_TO_USERSET_ENTRYPOINT, + TargetRelation: rr, + ResultStatus: operationResultState, + ComputedUsersetRelation: computedUsersetRelation, + TuplesetRelation: tuplesetRelation, }) if err != nil { return err diff --git a/proto/internal/core/v1/core.proto b/proto/internal/core/v1/core.proto index 5fa55ea94f..1b8a57ab59 100644 --- a/proto/internal/core/v1/core.proto +++ b/proto/internal/core/v1/core.proto @@ -348,6 +348,12 @@ message ReachabilityEntrypoint { * represents, if applicable. */ string tupleset_relation = 5; + + /** + * computed_userset_relation is the name of the computed userset relation on the ComputedUserset + * this entrypoint represents, if applicable. + */ + string computed_userset_relation = 6; } /** diff --git a/proto/internal/dispatch/v1/dispatch.proto b/proto/internal/dispatch/v1/dispatch.proto index 92c78a1a2e..fdcf3231d4 100644 --- a/proto/internal/dispatch/v1/dispatch.proto +++ b/proto/internal/dispatch/v1/dispatch.proto @@ -16,6 +16,9 @@ service DispatchService { rpc DispatchLookupResources(DispatchLookupResourcesRequest) returns (stream DispatchLookupResourcesResponse) {} rpc DispatchLookupSubjects(DispatchLookupSubjectsRequest) returns (stream DispatchLookupSubjectsResponse) {} + + + rpc DispatchLookupResources2(DispatchLookupResources2Request) returns (stream DispatchLookupResources2Response) {} } message DispatchCheckRequest { @@ -40,6 +43,22 @@ message DispatchCheckRequest { ResultsSetting results_setting = 5; DebugSetting debug = 6; + + /** + * check_hints are hints provided to the check call to help the resolver optimize the check + * by skipping calculations for the provided checks. The string key is the fully qualified + * "relationtuple"-string for the problem, e.g. `document:example#relation@user:someuser`. + * It is up to the caller to *ensure* that the hints provided are correct; if incorrect, + * the resolver may return incorrect results which will in turn be cached. + */ + repeated CheckHint check_hints = 7; +} + +message CheckHint { + core.v1.ObjectAndRelation resource = 1; + core.v1.ObjectAndRelation subject = 2; + string ttu_computed_userset_relation = 3; + ResourceCheckResult result = 4; } message DispatchCheckResponse { @@ -83,6 +102,32 @@ message Cursor { uint32 dispatch_version = 3; } +message DispatchLookupResources2Request { + ResolverMeta metadata = 1 [(validate.rules).message.required = true]; + + core.v1.RelationReference resource_relation = 2 [(validate.rules).message.required = true]; + core.v1.RelationReference subject_relation = 3 [(validate.rules).message.required = true]; + repeated string subject_ids = 4; + + core.v1.ObjectAndRelation terminal_subject = 5 [(validate.rules).message.required = true]; + google.protobuf.Struct context = 6; + + Cursor optional_cursor = 7; + uint32 optional_limit = 8; +} + +message PossibleResource { + string resource_id = 1; + repeated string for_subject_ids = 2; + repeated string missing_context_params = 3; +} + +message DispatchLookupResources2Response { + PossibleResource resource = 1; + ResponseMeta metadata = 2; + Cursor after_response_cursor = 3; +} + message DispatchReachableResourcesRequest { ResolverMeta metadata = 1 [(validate.rules).message.required = true];