diff --git a/docs/grpc/index.html b/docs/grpc/index.html index 4aa7e20fed..bc6321af5d 100644 --- a/docs/grpc/index.html +++ b/docs/grpc/index.html @@ -3292,6 +3292,13 @@

RegisteredResource

+ + namespace + Namespace + +

+ + metadata common.Metadata @@ -13772,11 +13779,25 @@

CreateRegist values string repeated -

Optional +

Optional Registered Resource Values (when provided) must be alphanumeric strings, allowing hyphens and underscores but not as the first or last character. The stored value will be normalized to lower case.

+ + namespace_id + string + +

+ + + + namespace_fqn + string + +

+ + metadata common.MetadataMutable @@ -14008,6 +14029,20 @@

GetRegisteredRe

+ + namespace_fqn + string + +

+ + + + namespace_id + string + +

+ + @@ -14245,6 +14280,20 @@

ListRegistere + + namespace_id + string + +

+ + + + namespace_fqn + string + +

+ + pagination policy.PageRequest diff --git a/docs/openapi/policy/objects.openapi.yaml b/docs/openapi/policy/objects.openapi.yaml index c1f082cc1a..361920e138 100644 --- a/docs/openapi/policy/objects.openapi.yaml +++ b/docs/openapi/policy/objects.openapi.yaml @@ -740,6 +740,9 @@ components: items: $ref: '#/components/schemas/policy.RegisteredResourceValue' title: values + namespace: + title: namespace + $ref: '#/components/schemas/policy.Namespace' metadata: title: metadata description: Common metadata diff --git a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml index 676ab81a2d..a11f21fb47 100644 --- a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml +++ b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml @@ -1049,6 +1049,9 @@ components: items: $ref: '#/components/schemas/policy.RegisteredResourceValue' title: values + namespace: + title: namespace + $ref: '#/components/schemas/policy.Namespace' metadata: title: metadata description: Common metadata @@ -1379,7 +1382,19 @@ components: uniqueItems: true title: values uniqueItems: true - description: "Optional \n Registered Resource Values (when provided) must be alphanumeric strings, allowing hyphens and underscores but not as the first or last character.\n The stored value will be normalized to lower case." + description: |- + Optional + Registered Resource Values (when provided) must be alphanumeric strings, allowing hyphens and underscores but not as the first or last character. + The stored value will be normalized to lower case. + namespaceId: + type: string + title: namespace_id + format: uuid + namespaceFqn: + type: string + title: namespace_fqn + minLength: 1 + format: uri metadata: title: metadata description: |- @@ -1505,6 +1520,16 @@ components: title: name required: - name + properties: + namespaceFqn: + type: string + title: namespace_fqn + minLength: 1 + format: uri + namespaceId: + type: string + title: namespace_id + format: uuid title: GetRegisteredResourceRequest additionalProperties: false policy.registeredresources.GetRegisteredResourceResponse: @@ -1619,6 +1644,15 @@ components: policy.registeredresources.ListRegisteredResourcesRequest: type: object properties: + namespaceId: + type: string + title: namespace_id + format: uuid + namespaceFqn: + type: string + title: namespace_fqn + minLength: 1 + format: uri pagination: title: pagination description: Optional diff --git a/lib/identifier/registered_resource_value.go b/lib/identifier/registered_resource_value.go index f286853afd..5866903ce1 100644 --- a/lib/identifier/registered_resource_value.go +++ b/lib/identifier/registered_resource_value.go @@ -7,51 +7,80 @@ import ( ) type FullyQualifiedRegisteredResourceValue struct { - Name string - Value string + Namespace string + Name string + Value string } -// protovalidate already validates the FQN format in the service request -// for parsing purposes, we can just look for any non-whitespace characters -// e.g. should be in format of "https://reg_res//value/" +// New FQN format: https:///reg_res//value/ var registeredResourceValueFqnRegex = regexp.MustCompile( - `^https:\/\/reg_res\/(?[^\/]+)\/value\/(?[^\/]+)$`, + `^https:\/\/(?P[^\/]+)\/reg_res\/(?P[^\/]+)\/value\/(?P[^\/]+)$`, ) -// parseRegisteredResourceValueFqn parses a registered resource value FQN string into a FullyQualifiedRegisteredResourceValue struct. -// The FQN must be in the format: https://reg_res//value/ -func parseRegisteredResourceValueFqn(fqn string) (*FullyQualifiedRegisteredResourceValue, error) { - matches := registeredResourceValueFqnRegex.FindStringSubmatch(fqn) +// Legacy FQN format: https://reg_res//value/ +var legacyRegisteredResourceValueFqnRegex = regexp.MustCompile( + `^https:\/\/reg_res\/(?P[^\/]+)\/value\/(?P[^\/]+)$`, +) - // Check if we have matches first +// matchFqnParts attempts to match fqn against re, extracts named groups, lowercases them, +// and validates name/value with validObjectNameRegex. Returns nil if the regex doesn't match. +func matchFqnParts(re *regexp.Regexp, fqn string, groups []string) (map[string]string, error) { + matches := re.FindStringSubmatch(fqn) if len(matches) == 0 { - return nil, fmt.Errorf("%w: FQN must be in format https://reg_res//value/", ErrInvalidFQNFormat) + return nil, nil //nolint:nilnil // nil means no match, not an error } + result := make(map[string]string, len(groups)) + for _, g := range groups { + idx := re.SubexpIndex(g) + if idx == -1 || idx >= len(matches) { + return nil, fmt.Errorf("%w: missing group %s", ErrInvalidFQNFormat, g) + } + result[g] = strings.ToLower(matches[idx]) + } + if namespace, ok := result["namespace"]; ok { + if !validNamespaceRegex.MatchString(namespace) { + return nil, fmt.Errorf("%w: invalid namespace format %s", ErrInvalidFQNFormat, namespace) + } + } + name, value := result["name"], result["value"] + if !validObjectNameRegex.MatchString(name) || !validObjectNameRegex.MatchString(value) { + return nil, fmt.Errorf("%w: found name %s with value %s", ErrInvalidFQNFormat, name, value) + } + return result, nil +} - nameIdx := registeredResourceValueFqnRegex.SubexpIndex("name") - valueIdx := registeredResourceValueFqnRegex.SubexpIndex("value") - - if nameIdx == -1 || valueIdx == -1 || len(matches) <= nameIdx || len(matches) <= valueIdx { - return nil, fmt.Errorf("%w: valid FQN format of https://reg_res//value/ must be provided", ErrInvalidFQNFormat) +// parseRegisteredResourceValueFqn parses a registered resource value FQN string into a FullyQualifiedRegisteredResourceValue struct. +// Supports both the new format: https:///reg_res//value/ +// and the legacy format: https://reg_res//value/ +func parseRegisteredResourceValueFqn(fqn string) (*FullyQualifiedRegisteredResourceValue, error) { + // Try new format: https:///reg_res//value/ + if parts, err := matchFqnParts(registeredResourceValueFqnRegex, fqn, []string{"namespace", "name", "value"}); err != nil { + return nil, err + } else if parts != nil { + return &FullyQualifiedRegisteredResourceValue{Namespace: parts["namespace"], Name: parts["name"], Value: parts["value"]}, nil } - name := strings.ToLower(matches[nameIdx]) - value := strings.ToLower(matches[valueIdx]) - isValid := validObjectNameRegex.MatchString(name) && validObjectNameRegex.MatchString(value) - if !isValid { - return nil, fmt.Errorf("%w: found name %s with value %s", ErrInvalidFQNFormat, name, value) + // Try legacy format: https://reg_res//value/ + if parts, err := matchFqnParts(legacyRegisteredResourceValueFqnRegex, fqn, []string{"name", "value"}); err != nil { + return nil, err + } else if parts != nil { + return &FullyQualifiedRegisteredResourceValue{Name: parts["name"], Value: parts["value"]}, nil } - return &FullyQualifiedRegisteredResourceValue{ - Name: name, - Value: value, - }, nil + return nil, fmt.Errorf("%w: FQN must be in format https:///reg_res//value/", ErrInvalidFQNFormat) } // Implementing FullyQualified interface for FullyQualifiedRegisteredResourceValue func (rrv *FullyQualifiedRegisteredResourceValue) FQN() string { builder := strings.Builder{} - builder.WriteString("https://reg_res/") + if rrv.Namespace != "" { + builder.WriteString("https://") + builder.WriteString(rrv.Namespace) + builder.WriteString("/reg_res/") + } else { + // Legacy format for backward compatibility + builder.WriteString("https://reg_res/") + } builder.WriteString(rrv.Name) builder.WriteString("/value/") builder.WriteString(rrv.Value) @@ -59,6 +88,9 @@ func (rrv *FullyQualifiedRegisteredResourceValue) FQN() string { } func (rrv *FullyQualifiedRegisteredResourceValue) Validate() error { + if rrv.Namespace != "" && !validNamespaceRegex.MatchString(rrv.Namespace) { + return fmt.Errorf("%w: invalid namespace format %s", ErrInvalidFQNFormat, rrv.Namespace) + } if !validObjectNameRegex.MatchString(rrv.Name) { return fmt.Errorf("%w: invalid resource name format %s", ErrInvalidFQNFormat, rrv.Name) } diff --git a/lib/identifier/registered_resource_value_test.go b/lib/identifier/registered_resource_value_test.go index 8b3873b6de..d7c83f8a5a 100644 --- a/lib/identifier/registered_resource_value_test.go +++ b/lib/identifier/registered_resource_value_test.go @@ -8,48 +8,67 @@ import ( func TestRegisteredResourceValueFQN(t *testing.T) { tests := []struct { - name string - resName string - value string - want string + name string + namespace string + resName string + value string + want string }{ { - name: "basic example", - resName: "resource", - value: "value", - want: "https://reg_res/resource/value/value", + name: "namespaced basic example", + namespace: "example.com", + resName: "resource", + value: "value", + want: "https://example.com/reg_res/resource/value/value", }, { - name: "with hyphens", - resName: "test-resource", - value: "test-value", - want: "https://reg_res/test-resource/value/test-value", + name: "namespaced with hyphens", + namespace: "example.com", + resName: "test-resource", + value: "test-value", + want: "https://example.com/reg_res/test-resource/value/test-value", }, { - name: "with underscores", - resName: "test_resource", - value: "test_value", - want: "https://reg_res/test_resource/value/test_value", + name: "namespaced with underscores", + namespace: "example.com", + resName: "test_resource", + value: "test_value", + want: "https://example.com/reg_res/test_resource/value/test_value", }, { - name: "with numbers", - resName: "resource123", - value: "value456", - want: "https://reg_res/resource123/value/value456", + name: "namespaced with numbers", + namespace: "example.com", + resName: "resource123", + value: "value456", + want: "https://example.com/reg_res/resource123/value/value456", + }, + { + name: "namespaced lower case", + namespace: "EXAMPLE.COM", + resName: "RESOURCE", + value: "VALUE", + want: "https://example.com/reg_res/resource/value/value", }, { - name: "lower case", - resName: "RESOURCE", - value: "VALUE", + name: "legacy no namespace", + resName: "resource", + value: "value", want: "https://reg_res/resource/value/value", }, + { + name: "legacy with hyphens", + resName: "test-resource", + value: "test-value", + want: "https://reg_res/test-resource/value/test-value", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { rrv := &FullyQualifiedRegisteredResourceValue{ - Name: tt.resName, - Value: tt.value, + Namespace: tt.namespace, + Name: tt.resName, + Value: tt.value, } got := rrv.FQN() require.Equal(t, tt.want, got) @@ -148,47 +167,91 @@ func TestRegisteredResourceValueValidate(t *testing.T) { func TestParseRegisteredResourceValueFqn(t *testing.T) { tests := []struct { - name string - fqn string - wantName string - wantValue string - wantErr bool + name string + fqn string + wantNamespace string + wantName string + wantValue string + wantErr bool }{ + // New format tests + { + name: "valid namespaced basic", + fqn: "https://example.com/reg_res/valid/value/test", + wantNamespace: "example.com", + wantName: "valid", + wantValue: "test", + wantErr: false, + }, + { + name: "valid namespaced with hyphens", + fqn: "https://example.com/reg_res/test-resource/value/test-value", + wantNamespace: "example.com", + wantName: "test-resource", + wantValue: "test-value", + wantErr: false, + }, + { + name: "valid namespaced with underscores", + fqn: "https://example.com/reg_res/test_resource/value/test_value", + wantNamespace: "example.com", + wantName: "test_resource", + wantValue: "test_value", + wantErr: false, + }, { - name: "valid basic", + name: "valid namespaced with numbers", + fqn: "https://example.com/reg_res/resource123/value/value456", + wantNamespace: "example.com", + wantName: "resource123", + wantValue: "value456", + wantErr: false, + }, + { + name: "valid namespaced lower case", + fqn: "https://EXAMPLE.COM/reg_res/RESOURce/value/valUE", + wantNamespace: "example.com", + wantName: "resource", + wantValue: "value", + wantErr: false, + }, + // Legacy format tests + { + name: "valid legacy basic", fqn: "https://reg_res/valid/value/test", wantName: "valid", wantValue: "test", wantErr: false, }, { - name: "valid with hyphens", + name: "valid legacy with hyphens", fqn: "https://reg_res/test-resource/value/test-value", wantName: "test-resource", wantValue: "test-value", wantErr: false, }, { - name: "valid with underscores", + name: "valid legacy with underscores", fqn: "https://reg_res/test_resource/value/test_value", wantName: "test_resource", wantValue: "test_value", wantErr: false, }, { - name: "valid with numbers", + name: "valid legacy with numbers", fqn: "https://reg_res/resource123/value/value456", wantName: "resource123", wantValue: "value456", wantErr: false, }, { - name: "valid lower case", + name: "valid legacy lower case", fqn: "https://reg_res/RESOURce/value/valUE", wantName: "resource", wantValue: "value", wantErr: false, }, + // Invalid format tests { name: "empty string", fqn: "", @@ -199,29 +262,29 @@ func TestParseRegisteredResourceValueFqn(t *testing.T) { fqn: "invalid", wantErr: true, }, - { - name: "wrong prefix", - fqn: "https://registered/valid/value/test", - wantErr: true, - }, { name: "missing parts", - fqn: "https://reg_res/valid", + fqn: "https://example.com/reg_res/valid", wantErr: true, }, { name: "missing value segment", - fqn: "https://reg_res/valid/value", + fqn: "https://example.com/reg_res/valid/value", wantErr: true, }, { name: "wrong protocol", - fqn: "http://reg_res/test/value/something", + fqn: "http://example.com/reg_res/test/value/something", wantErr: true, }, { name: "extra prefix", - fqn: "somethinghttps://reg_res/test/value/something", + fqn: "somethinghttps://example.com/reg_res/test/value/something", + wantErr: true, + }, + { + name: "invalid namespace format", + fqn: "https://not_a_valid_namespace/reg_res/test/value/something", wantErr: true, }, } @@ -236,6 +299,7 @@ func TestParseRegisteredResourceValueFqn(t *testing.T) { if tt.wantErr { return } + require.Equal(t, tt.wantNamespace, got.Namespace) require.Equal(t, tt.wantName, got.Name) require.Equal(t, tt.wantValue, got.Value) }) @@ -245,29 +309,44 @@ func TestParseRegisteredResourceValueFqn(t *testing.T) { func TestRegisteredResourceValueRoundTrip(t *testing.T) { // Test round trip from struct to FQN to parse and back tests := []struct { - name string - resName string - value string + name string + namespace string + resName string + value string }{ { - name: "basic example", - resName: "resource", - value: "value", + name: "namespaced basic example", + namespace: "example.com", + resName: "resource", + value: "value", }, { - name: "with hyphens", - resName: "test-resource", - value: "test-value", + name: "namespaced with hyphens", + namespace: "example.com", + resName: "test-resource", + value: "test-value", }, { - name: "with underscores", - resName: "test_resource", - value: "test_value", + name: "namespaced with underscores", + namespace: "my.namespace.org", + resName: "test_resource", + value: "test_value", }, { - name: "with numbers", - resName: "resource123", - value: "value456", + name: "namespaced with numbers", + namespace: "example.com", + resName: "resource123", + value: "value456", + }, + { + name: "legacy basic example", + resName: "resource", + value: "value", + }, + { + name: "legacy with hyphens", + resName: "test-resource", + value: "test-value", }, } @@ -275,8 +354,9 @@ func TestRegisteredResourceValueRoundTrip(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Create original registered resource value original := &FullyQualifiedRegisteredResourceValue{ - Name: tt.resName, - Value: tt.value, + Namespace: tt.namespace, + Name: tt.resName, + Value: tt.value, } // Get FQN @@ -287,6 +367,7 @@ func TestRegisteredResourceValueRoundTrip(t *testing.T) { require.NoError(t, err) // Check the parsed values match original + require.Equal(t, original.Namespace, parsed.Namespace) require.Equal(t, original.Name, parsed.Name) require.Equal(t, original.Value, parsed.Value) diff --git a/protocol/go/policy/registeredresources/registered_resources.pb.go b/protocol/go/policy/registeredresources/registered_resources.pb.go index 93b33efca5..9f31448e6a 100644 --- a/protocol/go/policy/registeredresources/registered_resources.pb.go +++ b/protocol/go/policy/registeredresources/registered_resources.pb.go @@ -164,9 +164,9 @@ type GetRegisteredResourceRequest struct { // // *GetRegisteredResourceRequest_Id // *GetRegisteredResourceRequest_Name - Identifier isGetRegisteredResourceRequest_Identifier `protobuf_oneof:"identifier"` - // Optional - namespace context for name-based lookups (since names are not globally unique for namespaced RRs) - NamespaceFqn string `protobuf:"bytes,3,opt,name=namespace_fqn,json=namespaceFqn,proto3" json:"namespace_fqn,omitempty"` + Identifier isGetRegisteredResourceRequest_Identifier `protobuf_oneof:"identifier"` + NamespaceFqn string `protobuf:"bytes,3,opt,name=namespace_fqn,json=namespaceFqn,proto3" json:"namespace_fqn,omitempty"` + NamespaceId string `protobuf:"bytes,4,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` } func (x *GetRegisteredResourceRequest) Reset() { @@ -229,6 +229,13 @@ func (x *GetRegisteredResourceRequest) GetNamespaceFqn() string { return "" } +func (x *GetRegisteredResourceRequest) GetNamespaceId() string { + if x != nil { + return x.NamespaceId + } + return "" +} + type isGetRegisteredResourceRequest_Identifier interface { isGetRegisteredResourceRequest_Identifier() } @@ -1500,7 +1507,7 @@ var file_policy_registeredresources_registered_resources_proto_rawDesc = []byte{ 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, - 0xaa, 0x04, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, + 0x98, 0x04, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0xc2, 0x02, 0x0a, @@ -1524,136 +1531,238 @@ var file_policy_registeredresources_registered_resources_proto_rawDesc = []byte{ 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x20, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0xc8, 0x01, 0x00, 0x72, 0x03, 0x18, 0xfd, 0x01, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x93, 0x01, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, - 0x66, 0x71, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x6e, 0xba, 0x48, 0x6b, 0xba, 0x01, - 0x68, 0x0a, 0x16, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x12, 0x2d, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x20, 0x46, 0x51, 0x4e, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, - 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x55, 0x52, 0x49, 0x20, 0x69, 0x66, 0x20, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x1a, 0x1f, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, - 0x68, 0x69, 0x73, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, - 0x73, 0x2e, 0x69, 0x73, 0x55, 0x72, 0x69, 0x28, 0x29, 0x52, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x46, 0x71, 0x6e, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x57, 0x0a, 0x1d, - 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, - 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xd9, 0x01, 0x0a, 0x1e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x65, 0x12, 0x2f, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, + 0x71, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, + 0x01, 0x88, 0x01, 0x01, 0x52, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x46, + 0x71, 0x6e, 0x12, 0x2b, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, + 0x01, 0x01, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x3a, + 0x24, 0xba, 0x48, 0x21, 0x22, 0x1f, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x69, 0x64, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, + 0x66, 0x71, 0x6e, 0x10, 0x00, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x57, 0x0a, 0x1d, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, + 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x22, 0xd9, 0x01, 0x0a, 0x1e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, + 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x5f, 0x66, 0x71, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, + 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x52, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x46, 0x71, 0x6e, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, + 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x24, 0xba, 0x48, 0x21, 0x22, 0x1f, + 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x0a, 0x0d, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x10, 0x00, 0x22, + 0x91, 0x01, 0x0a, 0x1f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x34, 0x0a, + 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x89, 0x04, 0x0a, 0x1f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, - 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, - 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x52, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x46, 0x71, 0x6e, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, - 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x24, 0xba, 0x48, 0x21, - 0x22, 0x1f, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x10, - 0x00, 0x22, 0x91, 0x01, 0x0a, 0x1f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, + 0x64, 0x12, 0xc0, 0x02, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x42, 0xab, 0x02, 0xba, 0x48, 0xa7, 0x02, 0xba, 0x01, 0x9b, 0x02, 0x0a, 0x0e, 0x72, 0x72, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0xb3, 0x01, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, + 0x6e, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x73, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, + 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x75, 0x6e, 0x64, 0x65, + 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, + 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x20, + 0x6c, 0x61, 0x73, 0x74, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x2e, 0x20, + 0x54, 0x68, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, + 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x2e, 0x1a, 0x53, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3e, 0x20, + 0x30, 0x20, 0x3f, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, + 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, + 0x3a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, 0x5b, + 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x20, + 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0xc8, 0x01, 0x00, 0x72, 0x03, 0x18, 0xfd, 0x01, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, 0x0a, 0x18, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, + 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x16, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x22, + 0x5a, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x3b, 0x0a, 0x1f, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, + 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x5a, 0x0a, 0x20, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x08, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x22, 0xad, 0x04, 0x0a, 0x14, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x27, 0x0a, + 0x09, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x08, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0xb2, 0x02, 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8e, 0x02, 0xba, + 0x48, 0x8a, 0x02, 0xba, 0x01, 0x81, 0x02, 0x0a, 0x12, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0xad, 0x01, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, + 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, + 0x63, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x69, + 0x6e, 0x67, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x75, + 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6e, + 0x6f, 0x74, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, + 0x6f, 0x72, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, + 0x72, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, + 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, + 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, 0x1a, 0x3b, 0x74, 0x68, 0x69, + 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, + 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, + 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, + 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x72, 0x03, 0x18, 0xfd, 0x01, 0x48, 0x00, 0x52, + 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x12, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, + 0x01, 0x48, 0x01, 0x52, 0x10, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x01, + 0x52, 0x11, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x46, 0x71, 0x6e, 0x42, 0x1a, 0x0a, 0x11, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x42, + 0x23, 0x0a, 0x1a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, + 0x48, 0x02, 0x08, 0x01, 0x22, 0xa0, 0x04, 0x0a, 0x24, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, + 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0a, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0xad, 0x02, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x96, 0x02, 0xba, 0x48, 0x92, 0x02, 0xba, + 0x01, 0x86, 0x02, 0x0a, 0x0f, 0x72, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x12, 0xb5, 0x01, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, + 0x64, 0x20, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, + 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, + 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, + 0x73, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x63, + 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, + 0x62, 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x74, 0x6f, + 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, 0x1a, 0x3b, 0x74, 0x68, + 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, + 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, + 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, + 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0xc8, 0x01, 0x01, 0x72, 0x03, 0x18, 0xfd, + 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x68, 0x0a, 0x17, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x15, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5e, 0x0a, 0x25, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x74, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, + 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x03, 0x66, 0x71, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, + 0x01, 0x48, 0x00, 0x52, 0x03, 0x66, 0x71, 0x6e, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x5b, 0x0a, + 0x22, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x53, 0x0a, 0x28, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x51, 0x4e, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x66, 0x71, 0x6e, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x42, 0x13, 0xba, 0x48, 0x10, 0x92, 0x01, 0x0d, 0x08, 0x01, 0x18, 0x01, + 0x22, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x52, 0x04, 0x66, 0x71, 0x6e, 0x73, 0x22, + 0x88, 0x02, 0x0a, 0x29, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, + 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, + 0x79, 0x46, 0x51, 0x4e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7a, 0x0a, + 0x0d, 0x66, 0x71, 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x56, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, + 0x51, 0x4e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x71, 0x6e, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x66, 0x71, + 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x61, 0x70, 0x1a, 0x5f, 0x0a, 0x10, 0x46, 0x71, 0x6e, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, + 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb2, 0x02, 0x0a, 0x23, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0xd5, 0x01, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0xb3, 0x01, 0xba, 0x48, 0xaf, 0x01, 0xba, + 0x01, 0xab, 0x01, 0x0a, 0x14, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x75, 0x75, + 0x69, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x23, 0x4f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, + 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x55, 0x55, 0x49, 0x44, 0x1a, 0x6e, + 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x20, + 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, + 0x27, 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x38, 0x7d, 0x2d, + 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x34, 0x7d, 0x2d, 0x5b, + 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x34, 0x7d, 0x2d, 0x5b, 0x30, + 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x34, 0x7d, 0x2d, 0x5b, 0x30, 0x2d, + 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x31, 0x32, 0x7d, 0x27, 0x29, 0x52, 0x0a, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61, + 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x95, 0x01, 0x0a, 0x24, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, - 0x34, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x89, 0x04, 0x0a, 0x1f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, - 0x02, 0x69, 0x64, 0x12, 0xc0, 0x02, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x42, 0xab, 0x02, 0xba, 0x48, 0xa7, 0x02, 0xba, 0x01, 0x9b, 0x02, 0x0a, 0x0e, 0x72, - 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0xb3, 0x01, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, - 0x20, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, - 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, - 0x67, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x75, 0x6e, - 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, - 0x74, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, - 0x72, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, - 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x6e, 0x61, 0x6d, - 0x65, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, - 0x69, 0x7a, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, - 0x73, 0x65, 0x2e, 0x1a, 0x53, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, - 0x3e, 0x20, 0x30, 0x20, 0x3f, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, - 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, - 0x28, 0x3f, 0x3a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, - 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, - 0x29, 0x20, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0xc8, 0x01, 0x00, 0x72, 0x03, 0x18, 0xfd, 0x01, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, 0x0a, 0x18, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, - 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x16, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, - 0x72, 0x22, 0x5a, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x3b, 0x0a, - 0x1f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, - 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x12, 0x34, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xfd, 0x04, 0x0a, 0x24, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, - 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x5a, 0x0a, 0x20, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, - 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xad, 0x04, 0x0a, 0x14, 0x41, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, - 0x27, 0x0a, 0x09, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x08, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0xb2, 0x02, 0x0a, 0x0b, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8e, - 0x02, 0xba, 0x48, 0x8a, 0x02, 0xba, 0x01, 0x81, 0x02, 0x0a, 0x12, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0xad, 0x01, - 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, - 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, - 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x62, 0x75, 0x74, - 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, - 0x74, 0x20, 0x6f, 0x72, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, - 0x74, 0x65, 0x72, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x77, 0x69, 0x6c, 0x6c, - 0x20, 0x62, 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x74, - 0x6f, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, 0x1a, 0x3b, 0x74, - 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, - 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, 0x61, 0x2d, 0x7a, - 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, - 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x72, 0x03, 0x18, 0xfd, 0x01, 0x48, - 0x00, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, - 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, - 0xb0, 0x01, 0x01, 0x48, 0x01, 0x52, 0x10, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, - 0x48, 0x01, 0x52, 0x11, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x46, 0x71, 0x6e, 0x42, 0x1a, 0x0a, 0x11, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, - 0x01, 0x42, 0x23, 0x0a, 0x1a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, - 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0xa0, 0x04, 0x0a, 0x24, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x29, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0a, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0xad, 0x02, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x96, 0x02, 0xba, 0x48, 0x92, - 0x02, 0xba, 0x01, 0x86, 0x02, 0x0a, 0x0f, 0x72, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, + 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0xc5, 0x02, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0xae, 0x02, 0xba, 0x48, 0xaa, + 0x02, 0xba, 0x01, 0x9e, 0x02, 0x0a, 0x0f, 0x72, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0xb5, 0x01, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x6c, @@ -1665,278 +1774,175 @@ var file_policy_registeredresources_registered_resources_proto_rawDesc = []byte{ 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, - 0x74, 0x6f, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, 0x1a, 0x3b, - 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, - 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, 0x61, 0x2d, - 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, - 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0xc8, 0x01, 0x01, 0x72, 0x03, - 0x18, 0xfd, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x68, 0x0a, 0x17, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x15, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5e, 0x0a, 0x25, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, + 0x74, 0x6f, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, 0x1a, 0x53, + 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x20, 0x3f, + 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, + 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, 0x61, + 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, + 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x20, 0x3a, 0x20, 0x74, + 0x72, 0x75, 0x65, 0xc8, 0x01, 0x00, 0x72, 0x03, 0x18, 0xfd, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x68, 0x0a, 0x17, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x15, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x54, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x65, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x52, + 0x16, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, + 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x22, 0x5e, 0x0a, 0x25, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x40, 0x0a, 0x24, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, + 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x5e, 0x0a, 0x25, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x74, 0x0a, 0x21, 0x47, 0x65, 0x74, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, - 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x03, 0x66, 0x71, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, - 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x03, 0x66, 0x71, 0x6e, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, - 0x5b, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, + 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0x88, 0x0e, 0x0a, 0x1a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x53, 0x0a, 0x28, - 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x51, 0x4e, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x66, 0x71, 0x6e, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x13, 0xba, 0x48, 0x10, 0x92, 0x01, 0x0d, 0x08, 0x01, - 0x18, 0x01, 0x22, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x52, 0x04, 0x66, 0x71, 0x6e, - 0x73, 0x22, 0x88, 0x02, 0x0a, 0x29, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x73, 0x42, 0x79, 0x46, 0x51, 0x4e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x7a, 0x0a, 0x0d, 0x66, 0x71, 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x61, 0x70, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x56, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x97, 0x01, 0x0a, 0x18, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x3b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x8e, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x38, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, - 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, - 0x79, 0x46, 0x51, 0x4e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x71, - 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, - 0x66, 0x71, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x61, 0x70, 0x1a, 0x5f, 0x0a, 0x10, 0x46, - 0x71, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb2, 0x02, 0x0a, - 0x23, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0xd5, 0x01, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0xb3, 0x01, 0xba, 0x48, 0xaf, - 0x01, 0xba, 0x01, 0xab, 0x01, 0x0a, 0x14, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, - 0x75, 0x75, 0x69, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x23, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x6d, 0x75, 0x73, 0x74, - 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x55, 0x55, 0x49, 0x44, - 0x1a, 0x6e, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3d, 0x3d, 0x20, - 0x30, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, - 0x73, 0x28, 0x27, 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x38, - 0x7d, 0x2d, 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x34, 0x7d, - 0x2d, 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x34, 0x7d, 0x2d, - 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x34, 0x7d, 0x2d, 0x5b, - 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x5d, 0x7b, 0x31, 0x32, 0x7d, 0x27, 0x29, - 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x0a, - 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x22, 0x95, 0x01, 0x0a, 0x24, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, - 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xfd, 0x04, 0x0a, 0x24, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, - 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0xc5, 0x02, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0xae, 0x02, 0xba, - 0x48, 0xaa, 0x02, 0xba, 0x01, 0x9e, 0x02, 0x0a, 0x0f, 0x72, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0xb5, 0x01, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x79, - 0x70, 0x68, 0x65, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, - 0x63, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x73, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x6c, 0x61, - 0x73, 0x74, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x2e, 0x20, 0x54, 0x68, - 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x77, - 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, - 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, - 0x1a, 0x53, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, - 0x20, 0x3f, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, - 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, - 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, - 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x20, 0x3a, - 0x20, 0x74, 0x72, 0x75, 0x65, 0xc8, 0x01, 0x00, 0x72, 0x03, 0x18, 0xfd, 0x01, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x12, 0x68, 0x0a, 0x17, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x15, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x33, - 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x54, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, - 0x65, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x75, - 0x6d, 0x52, 0x16, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x22, 0x5e, 0x0a, 0x25, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x40, 0x0a, 0x24, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, - 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x5e, 0x0a, 0x25, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0x88, 0x0e, 0x0a, 0x1a, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x97, 0x01, 0x0a, 0x18, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, + 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x94, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, + 0x3a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x97, 0x01, 0x0a, 0x18, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x3b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x8e, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x38, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x97, 0x01, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x12, 0x3b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0xa6, + 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x40, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x41, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x9d, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x94, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, + 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x51, 0x4e, 0x73, 0x12, 0x44, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, + 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x12, 0x3a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x51, 0x4e, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x45, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x51, + 0x4e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0xa3, 0x01, 0x0a, + 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x3f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x97, 0x01, 0x0a, - 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, - 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x3b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x97, 0x01, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x12, 0x3b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, - 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x3c, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0xa6, 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x12, 0x40, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, + 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x40, + 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0xa6, 0x01, 0x0a, 0x1d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x40, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x9d, 0x01, 0x0a, 0x1a, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x51, 0x4e, 0x73, 0x12, - 0x44, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x51, 0x4e, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x45, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, - 0x46, 0x51, 0x4e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0xa3, - 0x01, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, - 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, - 0x3f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x40, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0xa6, 0x01, 0x0a, 0x1d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x40, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0xa6, 0x01, - 0x0a, 0x1d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, - 0x40, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0xa6, 0x01, 0x0a, 0x1d, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x40, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, + 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x41, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x41, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x87, 0x02, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x18, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x42, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, - 0x72, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f, 0x2f, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0xa2, 0x02, 0x03, 0x50, 0x52, 0x58, 0xaa, - 0x02, 0x1a, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0xca, 0x02, 0x1a, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0xe2, 0x02, 0x26, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x5c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0xea, 0x02, 0x1b, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x3a, 0x3a, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x42, 0x87, 0x02, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x18, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x50, 0x01, 0x5a, 0x42, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0xa2, 0x02, 0x03, 0x50, 0x52, 0x58, 0xaa, 0x02, 0x1a, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, + 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0xca, 0x02, 0x1a, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x5c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0xe2, 0x02, 0x26, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x5c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x1b, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x3a, 0x3a, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x65, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/service/integration/registered_resources_test.go b/service/integration/registered_resources_test.go index 789118fe98..2f910b9d54 100644 --- a/service/integration/registered_resources_test.go +++ b/service/integration/registered_resources_test.go @@ -61,17 +61,21 @@ const invalidID = "00000000-0000-0000-0000-000000000000" func (s *RegisteredResourcesSuite) Test_CreateRegisteredResource_Succeeds() { req := ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_create_res", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_create_res", } created, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, req) s.Require().NoError(err) s.NotNil(created) + s.NotNil(created.GetNamespace()) + s.Equal(s.getNamespaceID("example.com"), created.GetNamespace().GetId()) } func (s *RegisteredResourcesSuite) Test_CreateRegisteredResource_NormalizedName_Succeeds() { req := ®isteredresources.CreateRegisteredResourceRequest{ - Name: "TeST_CrEaTe_RES_NorMa-LiZeD", + NamespaceId: s.getNamespaceID("example.com"), + Name: "TeST_CrEaTe_RES_NorMa-LiZeD", } created, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, req) @@ -86,8 +90,9 @@ func (s *RegisteredResourcesSuite) Test_CreateRegisteredResource_WithValues_Succ "test_create_res_values__value2", } req := ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_create_res_values", - Values: values, + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_create_res_values", + Values: values, } created, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, req) @@ -101,7 +106,8 @@ func (s *RegisteredResourcesSuite) Test_CreateRegisteredResource_WithValues_Succ func (s *RegisteredResourcesSuite) Test_CreateRegisteredResource_WithMetadata_Succeeds() { req := ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_create_res_metadata", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_create_res_metadata", Metadata: &common.MetadataMutable{ Labels: map[string]string{ "key1": "value1", @@ -116,16 +122,27 @@ func (s *RegisteredResourcesSuite) Test_CreateRegisteredResource_WithMetadata_Su s.Require().Len(created.GetMetadata().GetLabels(), 2) } -func (s *RegisteredResourcesSuite) Test_CreateRegisteredResource_WithNonUniqueName_Fails() { - existing := s.f.GetRegisteredResourceKey("res_with_values") +func (s *RegisteredResourcesSuite) Test_CreateRegisteredResource_WithNonUniqueName_SameNamespace_Fails() { + // Create a resource in a namespace first + nsID := s.getNamespaceID("example.com") + name := "test_unique_ns_res" req := ®isteredresources.CreateRegisteredResourceRequest{ - Name: existing.Name, + NamespaceId: nsID, + Name: name, } - created, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, req) + s.Require().NoError(err) + s.NotNil(created) + + // Try to create another with the same name in the same namespace + req2 := ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceId: nsID, + Name: name, + } + dup, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, req2) s.Require().Error(err) s.Require().ErrorIs(err, db.ErrUniqueConstraintViolation) - s.Nil(created) + s.Nil(dup) } // Get @@ -252,7 +269,8 @@ func (s *RegisteredResourcesSuite) Test_ListRegisteredResources_OrdersByCreatedA create := func(i int) string { name := fmt.Sprintf("order-test-resource-%d-%d", i, suffix) created, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: name, + NamespaceId: s.getNamespaceID("example.com"), + Name: name, }) s.Require().NoError(err) s.Require().NotNil(created) @@ -275,7 +293,8 @@ func (s *RegisteredResourcesSuite) Test_ListRegisteredResources_OrdersByCreatedA func (s *RegisteredResourcesSuite) Test_ListRegisteredResources_RegResValuesContainActionAttributeValues() { // Create a registered resource with values that have action attribute values newRegRes, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_list_reg_res_with_action_attr_values", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_list_reg_res_with_action_attr_values", }) s.Require().NoError(err) s.NotNil(newRegRes) @@ -450,7 +469,8 @@ func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResource_Succeeds() { } created, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_update_res", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_update_res", Metadata: &common.MetadataMutable{ Labels: labels, }, @@ -508,7 +528,8 @@ func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResource_Succeeds() { func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResource_NormalizedName_Succeeds() { created, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_update_res_normalized", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_update_res_normalized", }) s.Require().NoError(err) s.NotNil(created) @@ -540,17 +561,26 @@ func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResource_InvalidID_Fails s.Nil(updated) } -func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResource_NonUniqueName_Fails() { - created, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_update_res_non_unique", +func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResource_NonUniqueName_SameNamespace_Fails() { + nsID := s.getNamespaceID("example.com") + created1, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceId: nsID, + Name: "test_update_res_non_unique_a", }) s.Require().NoError(err) - s.NotNil(created) + s.NotNil(created1) - existingRes := s.f.GetRegisteredResourceKey("res_only") + created2, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceId: nsID, + Name: "test_update_res_non_unique_b", + }) + s.Require().NoError(err) + s.NotNil(created2) + + // Try to rename created2 to created1's name in the same namespace updated, err := s.db.PolicyClient.UpdateRegisteredResource(s.ctx, ®isteredresources.UpdateRegisteredResourceRequest{ - Id: created.GetId(), - Name: existingRes.Name, + Id: created2.GetId(), + Name: created1.GetName(), }) s.Require().Error(err) s.Require().ErrorIs(err, db.ErrUniqueConstraintViolation) @@ -561,7 +591,8 @@ func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResource_NonUniqueName_F func (s *RegisteredResourcesSuite) Test_DeleteRegisteredResource_Succeeds() { created, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_delete_res", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_delete_res", Values: []string{ "test_delete_value1", "test_delete_value2", @@ -620,7 +651,8 @@ func (s *RegisteredResourcesSuite) Test_DeleteRegisteredResource_WithInvalidID_F func (s *RegisteredResourcesSuite) Test_CreateRegisteredResourceValue_Succeeds() { res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_create_res_value", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_create_res_value", }) s.Require().NoError(err) s.NotNil(res) @@ -637,7 +669,8 @@ func (s *RegisteredResourcesSuite) Test_CreateRegisteredResourceValue_Succeeds() func (s *RegisteredResourcesSuite) Test_CreateRegisteredResourceValue_NormalizedName_Succeeds() { res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_create_res_value_normalized", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_create_res_value_normalized", }) s.Require().NoError(err) s.NotNil(res) @@ -655,7 +688,8 @@ func (s *RegisteredResourcesSuite) Test_CreateRegisteredResourceValue_Normalized func (s *RegisteredResourcesSuite) Test_CreateRegisteredResourceValue_WithMetadata_Succeeds() { res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_create_res_value_metadata", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_create_res_value_metadata", }) s.Require().NoError(err) s.NotNil(res) @@ -679,7 +713,8 @@ func (s *RegisteredResourcesSuite) Test_CreateRegisteredResourceValue_WithMetada func (s *RegisteredResourcesSuite) Test_CreateRegisteredResourceValue_With_ActionAttributeValues_Succeeds() { res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_create_res_value_action_attr_values", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_create_res_value_action_attr_values", }) s.Require().NoError(err) s.NotNil(res) @@ -762,7 +797,8 @@ func (s *RegisteredResourcesSuite) Test_CreateRegisteredResourceValue_WithNonUni func (s *RegisteredResourcesSuite) Test_CreateRegisteredResourceValue_WithInvalidActionAttributeValues_Fails() { res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_create_res_value_invalid_action_attr_values", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_create_res_value_invalid_action_attr_values", }) s.Require().NoError(err) s.NotNil(res) @@ -1046,7 +1082,8 @@ func (s *RegisteredResourcesSuite) Test_ListRegisteredResourceValues_NoPaginatio func (s *RegisteredResourcesSuite) Test_ListRegisteredResourceValues_OrdersByCreatedAt_Succeeds() { suffix := time.Now().UnixNano() resource, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: fmt.Sprintf("order-test-res-%d", suffix), + NamespaceId: s.getNamespaceID("example.com"), + Name: fmt.Sprintf("order-test-res-%d", suffix), }) s.Require().NoError(err) s.Require().NotNil(resource) @@ -1193,7 +1230,8 @@ func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResourceValue_Succeeds() } res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_update_res_value", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_update_res_value", }) s.Require().NoError(err) s.NotNil(res) @@ -1297,7 +1335,8 @@ func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResourceValue_Succeeds() func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResourceValue_NormalizedName_Succeeds() { res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_update_res_value_normalized", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_update_res_value_normalized", }) s.Require().NoError(err) s.NotNil(res) @@ -1338,7 +1377,8 @@ func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResourceValue_InvalidID_ func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResourceValue_NonUniqueResourceAndValue_Fails() { res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_update_res_value_non_unique", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_update_res_value_non_unique", }) s.Require().NoError(err) s.NotNil(res) @@ -1371,7 +1411,8 @@ func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResourceValue_NonUniqueR func (s *RegisteredResourcesSuite) Test_DeleteRegisteredResourceValue_Succeeds() { res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_delete_res_value", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_delete_res_value", }) s.Require().NoError(err) s.NotNil(res) @@ -1442,7 +1483,8 @@ func (s *RegisteredResourcesSuite) Test_DeleteAction_CascadeDeleteActionAttribut s.Require().NoError(err) res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_delete_action_res", + NamespaceId: s.getNamespaceID("example.com"), + Name: "test_delete_action_res", }) s.Require().NoError(err) s.NotNil(res) @@ -1507,7 +1549,8 @@ func (s *RegisteredResourcesSuite) Test_DeleteAttributeValue_CascadeDeleteAction s.Require().NotNil(attrVal) res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - Name: "test_delete_attr_value_res", + NamespaceId: ns.GetId(), + Name: "test_delete_attr_value_res", }) s.Require().NoError(err) s.NotNil(res) @@ -1549,3 +1592,358 @@ func (s *RegisteredResourcesSuite) Test_DeleteAttributeValue_CascadeDeleteAction s.NotNil(resVal) s.Empty(resVal.GetActionAttributeValues()) } + +/// +/// Namespace-scoped Registered Resources +/// + +func (s *RegisteredResourcesSuite) Test_CreateRegisteredResource_WithNamespaceFQN_Succeeds() { + nsFQN := s.getNamespaceFQN("example.com") + req := ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceFqn: nsFQN, + Name: "test_create_res_ns_fqn", + } + + created, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, req) + s.Require().NoError(err) + s.NotNil(created) + s.NotNil(created.GetNamespace()) + s.Equal(s.getNamespaceID("example.com"), created.GetNamespace().GetId()) + s.Equal("example.com", created.GetNamespace().GetName()) + s.Equal(nsFQN, created.GetNamespace().GetFqn()) +} + +func (s *RegisteredResourcesSuite) Test_CreateRegisteredResource_SameNameDifferentNamespaces_Succeeds() { + name := "test_same_name_diff_ns" + nsID1 := s.getNamespaceID("example.com") + nsID2 := s.getNamespaceID("example.net") + + created1, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceId: nsID1, + Name: name, + }) + s.Require().NoError(err) + s.NotNil(created1) + s.Equal(nsID1, created1.GetNamespace().GetId()) + + created2, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceId: nsID2, + Name: name, + }) + s.Require().NoError(err) + s.NotNil(created2) + s.Equal(nsID2, created2.GetNamespace().GetId()) + + // Both should exist with different IDs + s.NotEqual(created1.GetId(), created2.GetId()) +} + +func (s *RegisteredResourcesSuite) Test_GetRegisteredResource_ByNameWithNamespaceFQN_Succeeds() { + nsID := s.getNamespaceID("example.com") + nsFQN := s.getNamespaceFQN("example.com") + name := "test_get_by_name_ns" + + created, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceId: nsID, + Name: name, + }) + s.Require().NoError(err) + s.NotNil(created) + + got, err := s.db.PolicyClient.GetRegisteredResource(s.ctx, ®isteredresources.GetRegisteredResourceRequest{ + Identifier: ®isteredresources.GetRegisteredResourceRequest_Name{ + Name: name, + }, + NamespaceFqn: nsFQN, + }) + s.Require().NoError(err) + s.NotNil(got) + s.Equal(created.GetId(), got.GetId()) + s.Equal(name, got.GetName()) + s.NotNil(got.GetNamespace()) + s.Equal(nsID, got.GetNamespace().GetId()) +} + +func (s *RegisteredResourcesSuite) Test_ListRegisteredResources_FilterByNamespaceID_Succeeds() { + nsID := s.getNamespaceID("example.net") + name := "test_list_ns_filter" + + created, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceId: nsID, + Name: name, + }) + s.Require().NoError(err) + s.NotNil(created) + + list, err := s.db.PolicyClient.ListRegisteredResources(s.ctx, ®isteredresources.ListRegisteredResourcesRequest{ + NamespaceId: nsID, + }) + s.Require().NoError(err) + s.NotNil(list) + + // Should find at least the one we just created + found := false + for _, r := range list.GetResources() { + s.Equal(nsID, r.GetNamespace().GetId(), "all listed resources should belong to the filtered namespace") + if r.GetId() == created.GetId() { + found = true + } + } + s.True(found, "created resource should be in the filtered list") +} + +func (s *RegisteredResourcesSuite) Test_ListRegisteredResources_FilterByNamespaceFQN_Succeeds() { + nsID := s.getNamespaceID("example.net") + nsFQN := s.getNamespaceFQN("example.net") + name := "test_list_ns_fqn_filter" + + created, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceId: nsID, + Name: name, + }) + s.Require().NoError(err) + s.NotNil(created) + + list, err := s.db.PolicyClient.ListRegisteredResources(s.ctx, ®isteredresources.ListRegisteredResourcesRequest{ + NamespaceFqn: nsFQN, + }) + s.Require().NoError(err) + s.NotNil(list) + + found := false + for _, r := range list.GetResources() { + s.Equal(nsID, r.GetNamespace().GetId(), "all listed resources should belong to the filtered namespace") + if r.GetId() == created.GetId() { + found = true + } + } + s.True(found, "created resource should be in the filtered list") +} + +func (s *RegisteredResourcesSuite) Test_GetRegisteredResourceValue_NamespacedFQN_Succeeds() { + nsID := s.getNamespaceID("example.com") + name := "test_get_rrv_ns_fqn" + valueName := "test-value" + + res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceId: nsID, + Name: name, + Values: []string{valueName}, + }) + s.Require().NoError(err) + s.NotNil(res) + + // Get by namespaced FQN + fqn := fmt.Sprintf("https://example.com/reg_res/%s/value/%s", name, valueName) + got, err := s.db.PolicyClient.GetRegisteredResourceValue(s.ctx, ®isteredresources.GetRegisteredResourceValueRequest{ + Identifier: ®isteredresources.GetRegisteredResourceValueRequest_Fqn{ + Fqn: fqn, + }, + }) + s.Require().NoError(err) + s.NotNil(got) + s.Equal(valueName, got.GetValue()) + s.NotNil(got.GetResource()) + s.NotNil(got.GetResource().GetNamespace()) + s.Equal(nsID, got.GetResource().GetNamespace().GetId()) +} + +func (s *RegisteredResourcesSuite) Test_GetRegisteredResourceValuesByFQNs_NamespacedFormat_Succeeds() { + nsID := s.getNamespaceID("example.com") + name := "test_get_rrvs_ns_fqns" + val1 := "value1" + val2 := "value2" + + res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceId: nsID, + Name: name, + Values: []string{val1, val2}, + }) + s.Require().NoError(err) + s.NotNil(res) + + fqn1 := fmt.Sprintf("https://example.com/reg_res/%s/value/%s", name, val1) + fqn2 := fmt.Sprintf("https://example.com/reg_res/%s/value/%s", name, val2) + + fqnMap, err := s.db.PolicyClient.GetRegisteredResourceValuesByFQNs(s.ctx, ®isteredresources.GetRegisteredResourceValuesByFQNsRequest{ + Fqns: []string{fqn1, fqn2}, + }) + s.Require().NoError(err) + s.Require().Len(fqnMap, 2) + s.NotNil(fqnMap[fqn1]) + s.NotNil(fqnMap[fqn2]) + s.Equal(val1, fqnMap[fqn1].GetValue()) + s.Equal(val2, fqnMap[fqn2].GetValue()) +} + +func (s *RegisteredResourcesSuite) Test_RegisteredResource_NamespaceInResponses_Succeeds() { + nsID := s.getNamespaceID("example.com") + nsFQN := s.getNamespaceFQN("example.com") + name := "test_ns_in_responses" + + res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceId: nsID, + Name: name, + Values: []string{"resp-val"}, + }) + s.Require().NoError(err) + s.NotNil(res) + + // Verify namespace in Create response + s.NotNil(res.GetNamespace()) + s.Equal(nsID, res.GetNamespace().GetId()) + s.Equal("example.com", res.GetNamespace().GetName()) + s.Equal(nsFQN, res.GetNamespace().GetFqn()) + + // Verify namespace in Get response + got, err := s.db.PolicyClient.GetRegisteredResource(s.ctx, ®isteredresources.GetRegisteredResourceRequest{ + Identifier: ®isteredresources.GetRegisteredResourceRequest_Id{ + Id: res.GetId(), + }, + }) + s.Require().NoError(err) + s.NotNil(got.GetNamespace()) + s.Equal(nsID, got.GetNamespace().GetId()) + + // Verify namespace in List response + list, err := s.db.PolicyClient.ListRegisteredResources(s.ctx, ®isteredresources.ListRegisteredResourcesRequest{ + NamespaceId: nsID, + }) + s.Require().NoError(err) + found := false + for _, r := range list.GetResources() { + if r.GetId() == res.GetId() { + found = true + s.NotNil(r.GetNamespace()) + s.Equal(nsID, r.GetNamespace().GetId()) + } + } + s.True(found) + + // Verify namespace in Value response + valResp, err := s.db.PolicyClient.GetRegisteredResourceValue(s.ctx, ®isteredresources.GetRegisteredResourceValueRequest{ + Identifier: ®isteredresources.GetRegisteredResourceValueRequest_Id{ + Id: res.GetValues()[0].GetId(), + }, + }) + s.Require().NoError(err) + s.NotNil(valResp.GetResource()) + s.NotNil(valResp.GetResource().GetNamespace()) + s.Equal(nsID, valResp.GetResource().GetNamespace().GetId()) +} + +func (s *RegisteredResourcesSuite) Test_LegacyRegisteredResources_NoNamespace_StillAccessible() { + // Fixture resources are legacy (no namespace) - verify they're still accessible + existingRes := s.f.GetRegisteredResourceKey("res_only") + + got, err := s.db.PolicyClient.GetRegisteredResource(s.ctx, ®isteredresources.GetRegisteredResourceRequest{ + Identifier: ®isteredresources.GetRegisteredResourceRequest_Id{ + Id: existingRes.ID, + }, + }) + s.Require().NoError(err) + s.NotNil(got) + s.Equal(existingRes.Name, got.GetName()) + // Legacy resources have nil namespace + s.Nil(got.GetNamespace()) +} + +func (s *RegisteredResourcesSuite) Test_SameNamespaceEnforcement_DifferentNamespace_Fails() { + // Create a resource in example.com namespace + nsID := s.getNamespaceID("example.com") + res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceId: nsID, + Name: "test_same_ns_enforcement", + Values: []string{"enforce-val"}, + }) + s.Require().NoError(err) + s.NotNil(res) + + // Create an attribute in example.net namespace + otherNsID := s.getNamespaceID("example.net") + attrName := fmt.Sprintf("test_enforce_attr_%d", time.Now().UnixNano()) + attr, err := s.db.PolicyClient.CreateAttribute(s.ctx, &attributes.CreateAttributeRequest{ + NamespaceId: otherNsID, + Name: attrName, + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF, + Values: []string{"val1"}, + }) + s.Require().NoError(err) + s.NotNil(attr) + s.Require().NotEmpty(attr.GetValues()) + + crossNsAttrValID := attr.GetValues()[0].GetId() + + // Try to create a value with action-attribute-value from different namespace -> should fail + _, err = s.db.PolicyClient.CreateRegisteredResourceValue(s.ctx, ®isteredresources.CreateRegisteredResourceValueRequest{ + ResourceId: res.GetId(), + Value: "enforce-val2", + ActionAttributeValues: []*registeredresources.ActionAttributeValue{ + { + ActionIdentifier: ®isteredresources.ActionAttributeValue_ActionName{ + ActionName: actions.ActionNameRead, + }, + AttributeValueIdentifier: ®isteredresources.ActionAttributeValue_AttributeValueId{ + AttributeValueId: crossNsAttrValID, + }, + }, + }, + }) + s.Require().Error(err) + s.Require().ErrorIs(err, db.ErrForeignKeyViolation) +} + +func (s *RegisteredResourcesSuite) Test_SameNamespaceEnforcement_SameNamespace_Succeeds() { + // Create a resource in example.com namespace + nsID := s.getNamespaceID("example.com") + res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ + NamespaceId: nsID, + Name: "test_same_ns_enforcement_ok", + Values: []string{"enforce-ok-val"}, + }) + s.Require().NoError(err) + s.NotNil(res) + + // Create an attribute in the SAME namespace (example.com) + attrName := fmt.Sprintf("test_enforce_same_attr_%d", time.Now().UnixNano()) + attr, err := s.db.PolicyClient.CreateAttribute(s.ctx, &attributes.CreateAttributeRequest{ + NamespaceId: nsID, + Name: attrName, + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF, + Values: []string{"val1"}, + }) + s.Require().NoError(err) + s.NotNil(attr) + s.Require().NotEmpty(attr.GetValues()) + + sameNsAttrValID := attr.GetValues()[0].GetId() + + // Create a value with action-attribute-value from same namespace -> should succeed + resVal, err := s.db.PolicyClient.CreateRegisteredResourceValue(s.ctx, ®isteredresources.CreateRegisteredResourceValueRequest{ + ResourceId: res.GetId(), + Value: "enforce-ok-val2", + ActionAttributeValues: []*registeredresources.ActionAttributeValue{ + { + ActionIdentifier: ®isteredresources.ActionAttributeValue_ActionName{ + ActionName: actions.ActionNameRead, + }, + AttributeValueIdentifier: ®isteredresources.ActionAttributeValue_AttributeValueId{ + AttributeValueId: sameNsAttrValID, + }, + }, + }, + }) + s.Require().NoError(err) + s.NotNil(resVal) + s.Require().Len(resVal.GetActionAttributeValues(), 1) +} + +func (s *RegisteredResourcesSuite) getNamespaceID(key string) string { + ns := s.f.GetNamespaceKey(key) + return ns.ID +} + +func (s *RegisteredResourcesSuite) getNamespaceFQN(key string) string { + ns := s.f.GetNamespaceKey(key) + return "https://" + ns.Name +} diff --git a/service/policy/db/attribute_values.sql.go b/service/policy/db/attribute_values.sql.go index 97313ca62c..7582fed807 100644 --- a/service/policy/db/attribute_values.sql.go +++ b/service/policy/db/attribute_values.sql.go @@ -351,6 +351,44 @@ func (q *Queries) getAttributeValue(ctx context.Context, arg getAttributeValuePa return i, err } +const getAttributeValueNamespaceIDs = `-- name: getAttributeValueNamespaceIDs :many +SELECT av.id AS attribute_value_id, ad.namespace_id +FROM attribute_values av +JOIN attribute_definitions ad ON av.attribute_definition_id = ad.id +WHERE av.id = ANY($1::uuid[]) +` + +type getAttributeValueNamespaceIDsRow struct { + AttributeValueID string `json:"attribute_value_id"` + NamespaceID string `json:"namespace_id"` +} + +// getAttributeValueNamespaceIDs +// +// SELECT av.id AS attribute_value_id, ad.namespace_id +// FROM attribute_values av +// JOIN attribute_definitions ad ON av.attribute_definition_id = ad.id +// WHERE av.id = ANY($1::uuid[]) +func (q *Queries) getAttributeValueNamespaceIDs(ctx context.Context, ids []string) ([]getAttributeValueNamespaceIDsRow, error) { + rows, err := q.db.Query(ctx, getAttributeValueNamespaceIDs, ids) + if err != nil { + return nil, err + } + defer rows.Close() + var items []getAttributeValueNamespaceIDsRow + for rows.Next() { + var i getAttributeValueNamespaceIDsRow + if err := rows.Scan(&i.AttributeValueID, &i.NamespaceID); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const removeKeyAccessServerFromAttributeValue = `-- name: removeKeyAccessServerFromAttributeValue :execrows DELETE FROM attribute_value_key_access_grants WHERE attribute_value_id = $1 AND key_access_server_id = $2 diff --git a/service/policy/db/copyfrom.go b/service/policy/db/copyfrom.go index 2f450ac823..07ec0ceaf7 100644 --- a/service/policy/db/copyfrom.go +++ b/service/policy/db/copyfrom.go @@ -39,9 +39,7 @@ func (r iteratorForcreateRegisteredResourceActionAttributeValues) Err() error { return nil } -// -------------------------------------------------------------- -// Registered Resource Action Attribute Values -// -------------------------------------------------------------- +// createRegisteredResourceActionAttributeValues // // INSERT INTO registered_resource_action_attribute_values (registered_resource_value_id, action_id, attribute_value_id) // VALUES ($1, $2, $3) diff --git a/service/policy/db/migrations/20260302000000_add_namespace_to_registered_resources.sql b/service/policy/db/migrations/20260302000000_add_namespace_to_registered_resources.sql new file mode 100644 index 0000000000..adbcf44ee1 --- /dev/null +++ b/service/policy/db/migrations/20260302000000_add_namespace_to_registered_resources.sql @@ -0,0 +1,36 @@ +-- +goose Up +-- +goose StatementBegin + +-- Add nullable namespace_id column to registered_resources +ALTER TABLE registered_resources + ADD COLUMN namespace_id UUID REFERENCES attribute_namespaces(id) ON DELETE CASCADE; + +-- Drop existing global uniqueness constraint +ALTER TABLE registered_resources DROP CONSTRAINT registered_resources_name_key; + +-- Namespaced RRs: unique name within namespace +CREATE UNIQUE INDEX registered_resources_namespace_name_unique + ON registered_resources(namespace_id, name) WHERE namespace_id IS NOT NULL; + +-- Legacy RRs (no namespace): unique name globally +CREATE UNIQUE INDEX registered_resources_name_unique + ON registered_resources(name) WHERE namespace_id IS NULL; + +-- Index for namespace-scoped queries +CREATE INDEX idx_registered_resources_namespace + ON registered_resources(namespace_id); + +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin + +DROP INDEX IF EXISTS idx_registered_resources_namespace; +DROP INDEX IF EXISTS registered_resources_name_unique; +DROP INDEX IF EXISTS registered_resources_namespace_name_unique; + +ALTER TABLE registered_resources ADD CONSTRAINT registered_resources_name_key UNIQUE (name); + +ALTER TABLE registered_resources DROP COLUMN IF EXISTS namespace_id; + +-- +goose StatementEnd diff --git a/service/policy/db/models.go b/service/policy/db/models.go index d3656a87a6..8e25e5b83e 100644 --- a/service/policy/db/models.go +++ b/service/policy/db/models.go @@ -346,7 +346,8 @@ type RegisteredResource struct { // Timestamp when the record was created CreatedAt pgtype.Timestamptz `json:"created_at"` // Timestamp when the record was last updated - UpdatedAt pgtype.Timestamptz `json:"updated_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` + NamespaceID pgtype.UUID `json:"namespace_id"` } // Table to store the linkage of registered resource values to actions and attribute values diff --git a/service/policy/db/queries/attribute_values.sql b/service/policy/db/queries/attribute_values.sql index 89941e0d05..ee11da1bf3 100644 --- a/service/policy/db/queries/attribute_values.sql +++ b/service/policy/db/queries/attribute_values.sql @@ -157,3 +157,9 @@ UPDATE attribute_value_public_key_map SET key_access_server_key_id = sqlc.arg('new_key_id')::uuid WHERE (key_access_server_key_id = sqlc.arg('old_key_id')::uuid) RETURNING value_id; + +-- name: getAttributeValueNamespaceIDs :many +SELECT av.id AS attribute_value_id, ad.namespace_id +FROM attribute_values av +JOIN attribute_definitions ad ON av.attribute_definition_id = ad.id +WHERE av.id = ANY(sqlc.arg('ids')::uuid[]); diff --git a/service/policy/db/queries/registered_resources.sql b/service/policy/db/queries/registered_resources.sql index 3a626ad71a..17c9cacd2f 100644 --- a/service/policy/db/queries/registered_resources.sql +++ b/service/policy/db/queries/registered_resources.sql @@ -3,15 +3,47 @@ ---------------------------------------------------------------- -- name: createRegisteredResource :one -INSERT INTO registered_resources (name, metadata) -VALUES ($1, $2) -RETURNING id; +WITH inserted AS ( + INSERT INTO registered_resources (namespace_id, name, metadata) + SELECT + COALESCE(sqlc.narg('namespace_id')::uuid, fqns.namespace_id), + @name, + @metadata + FROM ( + SELECT + sqlc.narg('namespace_id')::uuid as direct_namespace_id + ) direct + LEFT JOIN attribute_fqns fqns ON fqns.fqn = sqlc.narg('namespace_fqn')::text AND sqlc.narg('namespace_id')::text IS NULL + WHERE + (sqlc.narg('namespace_id')::text IS NOT NULL AND direct.direct_namespace_id IS NOT NULL) OR + (sqlc.narg('namespace_fqn')::text IS NOT NULL AND fqns.namespace_id IS NOT NULL) + RETURNING id, namespace_id, name, metadata +) +SELECT + i.id, + i.name, + i.metadata, + JSON_BUILD_OBJECT( + 'id', n.id, + 'name', n.name, + 'fqn', fqns.fqn + ) as namespace +FROM inserted i +JOIN attribute_namespaces n ON i.namespace_id = n.id +LEFT JOIN attribute_fqns fqns ON fqns.namespace_id = n.id AND fqns.attribute_id IS NULL AND fqns.value_id IS NULL; -- name: getRegisteredResource :one SELECT r.id, r.name, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', r.metadata -> 'labels', 'created_at', r.created_at, 'updated_at', r.updated_at)) as metadata, + CASE WHEN n.id IS NOT NULL THEN + JSON_BUILD_OBJECT( + 'id', n.id, + 'name', n.name, + 'fqn', ns_fqns.fqn + ) + ELSE NULL END as namespace, JSON_AGG( JSON_BUILD_OBJECT( 'id', v.id, @@ -19,21 +51,37 @@ SELECT ) ) FILTER (WHERE v.id IS NOT NULL) as values FROM registered_resources r +LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id +LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL LEFT JOIN registered_resource_values v ON v.registered_resource_id = r.id WHERE (sqlc.narg('id')::uuid IS NULL OR r.id = sqlc.narg('id')::uuid) AND - (sqlc.narg('name')::text IS NULL OR r.name = sqlc.narg('name')::text) -GROUP BY r.id; + (sqlc.narg('name')::text IS NULL OR r.name = sqlc.narg('name')::text) AND + (sqlc.narg('namespace_id')::uuid IS NULL OR r.namespace_id = sqlc.narg('namespace_id')::uuid) AND + (sqlc.narg('namespace_fqn')::text IS NULL OR ns_fqns.fqn = sqlc.narg('namespace_fqn')::text) +GROUP BY r.id, n.id, ns_fqns.fqn; -- name: listRegisteredResources :many WITH counted AS ( - SELECT COUNT(id) AS total - FROM registered_resources + SELECT COUNT(r.id) AS total + FROM registered_resources r + LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id + LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL + WHERE + (sqlc.narg('namespace_id')::uuid IS NULL OR r.namespace_id = sqlc.narg('namespace_id')::uuid) AND + (sqlc.narg('namespace_fqn')::text IS NULL OR ns_fqns.fqn = sqlc.narg('namespace_fqn')::text) ) SELECT r.id, r.name, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', r.metadata -> 'labels', 'created_at', r.created_at, 'updated_at', r.updated_at)) as metadata, + CASE WHEN n.id IS NOT NULL THEN + JSON_BUILD_OBJECT( + 'id', n.id, + 'name', n.name, + 'fqn', ns_fqns.fqn + ) + ELSE NULL END as namespace, -- Aggregate all values for this resource into a JSON array, filtering NULL entries JSON_AGG( JSON_BUILD_OBJECT( @@ -44,6 +92,8 @@ SELECT ) FILTER (WHERE v.id IS NOT NULL) as values, counted.total FROM registered_resources r +LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id +LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL CROSS JOIN counted LEFT JOIN registered_resource_values v ON v.registered_resource_id = r.id -- Build a JSON array of action/attribute pairs for each resource value @@ -69,9 +119,12 @@ LEFT JOIN LATERAL ( -- Correlate to the outer query's resource value WHERE rav.registered_resource_value_id = v.id ) action_attrs ON true -- required syntax for LATERAL joins -GROUP BY r.id, counted.total +WHERE + (sqlc.narg('namespace_id')::uuid IS NULL OR r.namespace_id = sqlc.narg('namespace_id')::uuid) AND + (sqlc.narg('namespace_fqn')::text IS NULL OR ns_fqns.fqn = sqlc.narg('namespace_fqn')::text) +GROUP BY r.id, n.id, ns_fqns.fqn, counted.total ORDER BY r.created_at DESC -LIMIT @limit_ +LIMIT @limit_ OFFSET @offset_; -- name: updateRegisteredResource :execrows @@ -100,6 +153,14 @@ SELECT v.registered_resource_id, v.value, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', v.metadata -> 'labels', 'created_at', v.created_at, 'updated_at', v.updated_at)) as metadata, + CASE WHEN n.id IS NOT NULL THEN + JSON_BUILD_OBJECT( + 'id', n.id, + 'name', n.name, + 'fqn', ns_fqns.fqn + ) + ELSE NULL END as namespace, + r.name as resource_name, JSON_AGG( JSON_BUILD_OBJECT( 'action', JSON_BUILD_OBJECT( @@ -115,6 +176,8 @@ SELECT ) FILTER (WHERE rav.id IS NOT NULL) as action_attribute_values FROM registered_resource_values v JOIN registered_resources r ON v.registered_resource_id = r.id +LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id +LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL LEFT JOIN registered_resource_action_attribute_values rav ON v.id = rav.registered_resource_value_id LEFT JOIN actions a on rav.action_id = a.id LEFT JOIN attribute_values av on rav.attribute_value_id = av.id @@ -122,8 +185,9 @@ LEFT JOIN attribute_fqns fqns on av.id = fqns.value_id WHERE (sqlc.narg('id')::uuid IS NULL OR v.id = sqlc.narg('id')::uuid) AND (sqlc.narg('name')::text IS NULL OR r.name = sqlc.narg('name')::text) AND - (sqlc.narg('value')::text IS NULL OR v.value = sqlc.narg('value')::text) -GROUP BY v.id; + (sqlc.narg('value')::text IS NULL OR v.value = sqlc.narg('value')::text) AND + (sqlc.narg('namespace_fqn')::text IS NULL OR ns_fqns.fqn = sqlc.narg('namespace_fqn')::text) +GROUP BY v.id, r.name, n.id, ns_fqns.fqn; -- name: listRegisteredResourceValues :many WITH counted AS ( @@ -136,6 +200,14 @@ SELECT v.registered_resource_id, v.value, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', v.metadata -> 'labels', 'created_at', v.created_at, 'updated_at', v.updated_at)) as metadata, + CASE WHEN n.id IS NOT NULL THEN + JSON_BUILD_OBJECT( + 'id', n.id, + 'name', n.name, + 'fqn', ns_fqns.fqn + ) + ELSE NULL END as namespace, + r.name as resource_name, JSON_AGG( JSON_BUILD_OBJECT( 'action', JSON_BUILD_OBJECT( @@ -152,14 +224,16 @@ SELECT counted.total FROM registered_resource_values v JOIN registered_resources r ON v.registered_resource_id = r.id +LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id +LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL LEFT JOIN registered_resource_action_attribute_values rav ON v.id = rav.registered_resource_value_id LEFT JOIN actions a on rav.action_id = a.id LEFT JOIN attribute_values av on rav.attribute_value_id = av.id -LEFT JOIN attribute_fqns fqns on av.id = fqns.value_id +LEFT JOIN attribute_fqns fqns on av.id = fqns.value_id CROSS JOIN counted WHERE sqlc.narg('registered_resource_id')::uuid IS NULL OR v.registered_resource_id = sqlc.narg('registered_resource_id')::uuid -GROUP BY v.id, counted.total +GROUP BY v.id, r.name, n.id, ns_fqns.fqn, counted.total ORDER BY v.created_at DESC LIMIT @limit_ OFFSET @offset_; @@ -174,10 +248,16 @@ WHERE id = $1; -- name: deleteRegisteredResourceValue :execrows DELETE FROM registered_resource_values WHERE id = $1; ----------------------------------------------------------------- +---------------------------------------------------------------- -- Registered Resource Action Attribute Values ---------------------------------------------------------------- +-- name: getRegisteredResourceNamespaceIDByValueID :one +SELECT rr.namespace_id +FROM registered_resources rr +JOIN registered_resource_values rrv ON rrv.registered_resource_id = rr.id +WHERE rrv.id = $1; + -- name: createRegisteredResourceActionAttributeValues :copyfrom INSERT INTO registered_resource_action_attribute_values (registered_resource_value_id, action_id, attribute_value_id) VALUES ($1, $2, $3); diff --git a/service/policy/db/registered_resources.go b/service/policy/db/registered_resources.go index 73bfe1e579..61d3109d54 100644 --- a/service/policy/db/registered_resources.go +++ b/service/policy/db/registered_resources.go @@ -57,28 +57,72 @@ func unmarshalRegisteredResourceActionAttributeValuesProto(actionAttrValuesJSON return nil } +// hydrateNamespaceFromInterface converts a nullable namespace interface{} (from CASE WHEN SQL) +// to a *policy.Namespace. Returns an empty Namespace if the namespace is NULL (legacy RRs without namespace). +func hydrateNamespaceFromInterface(nsRaw interface{}) (*policy.Namespace, error) { + if nsRaw == nil { + return nil, nil //nolint:nilnil // nil namespace is valid for legacy RRs without namespace + } + + var nsBytes []byte + switch v := nsRaw.(type) { + case []byte: + nsBytes = v + case map[string]interface{}: + var err error + nsBytes, err = json.Marshal(v) + if err != nil { + return nil, fmt.Errorf("failed to marshal namespace map: %w", err) + } + default: + return nil, fmt.Errorf("unexpected namespace type: %T", nsRaw) + } + + ns := &policy.Namespace{} + if err := unmarshalNamespace(nsBytes, ns); err != nil { + return nil, fmt.Errorf("failed to unmarshal registered resource namespace: %w", err) + } + return ns, nil +} + /// /// Registered Resources /// func (c PolicyDBClient) CreateRegisteredResource(ctx context.Context, r *registeredresources.CreateRegisteredResourceRequest) (*policy.RegisteredResource, error) { name := strings.ToLower(r.GetName()) + namespaceID := r.GetNamespaceId() + namespaceFqn := r.GetNamespaceFqn() + + useID := len(namespaceID) > 0 + parsedID := pgtypeUUID(namespaceID) + if useID && !parsedID.Valid { + return nil, db.ErrUUIDInvalid + } + metadataJSON, _, err := db.MarshalCreateMetadata(r.GetMetadata()) if err != nil { return nil, err } - createdID, err := c.queries.createRegisteredResource(ctx, createRegisteredResourceParams{ - Name: name, - Metadata: metadataJSON, + row, err := c.queries.createRegisteredResource(ctx, createRegisteredResourceParams{ + NamespaceID: pgtypeUUID(namespaceID), + NamespaceFqn: pgtypeText(namespaceFqn), + Name: name, + Metadata: metadataJSON, }) if err != nil { return nil, db.WrapIfKnownInvalidQueryErr(err) } + namespace := &policy.Namespace{} + if err := unmarshalNamespace(row.Namespace, namespace); err != nil { + return nil, fmt.Errorf("failed to unmarshal registered resource namespace: %w", err) + } + for _, v := range r.GetValues() { req := ®isteredresources.CreateRegisteredResourceValueRequest{ - ResourceId: createdID, + ResourceId: row.ID, Value: v, } _, err := c.CreateRegisteredResourceValue(ctx, req) @@ -89,7 +133,7 @@ func (c PolicyDBClient) CreateRegisteredResource(ctx context.Context, r *registe return c.GetRegisteredResource(ctx, ®isteredresources.GetRegisteredResourceRequest{ Identifier: ®isteredresources.GetRegisteredResourceRequest_Id{ - Id: createdID, + Id: row.ID, }, }) } @@ -102,6 +146,16 @@ func (c PolicyDBClient) GetRegisteredResource(ctx context.Context, r *registered params.ID = pgtypeUUID(r.GetId()) case r.GetName() != "": params.Name = pgtypeText(strings.ToLower(r.GetName())) + namespaceID := r.GetNamespaceId() + if len(namespaceID) > 0 { + parsedID := pgtypeUUID(namespaceID) + if !parsedID.Valid { + return nil, db.ErrUUIDInvalid + } + params.NamespaceID = parsedID + } else if r.GetNamespaceFqn() != "" { + params.NamespaceFqn = pgtypeText(r.GetNamespaceFqn()) + } default: return nil, db.ErrSelectIdentifierInvalid } @@ -116,20 +170,33 @@ func (c PolicyDBClient) GetRegisteredResource(ctx context.Context, r *registered return nil, err } + namespace, err := hydrateNamespaceFromInterface(rr.Namespace) + if err != nil { + return nil, err + } + values := []*policy.RegisteredResourceValue{} if err = unmarshalRegisteredResourceValuesProto(rr.Values, &values); err != nil { return nil, err } return &policy.RegisteredResource{ - Id: rr.ID, - Name: rr.Name, - Metadata: metadata, - Values: values, + Id: rr.ID, + Name: rr.Name, + Metadata: metadata, + Namespace: namespace, + Values: values, }, nil } func (c PolicyDBClient) ListRegisteredResources(ctx context.Context, r *registeredresources.ListRegisteredResourcesRequest) (*registeredresources.ListRegisteredResourcesResponse, error) { + namespaceID := r.GetNamespaceId() + useID := len(namespaceID) > 0 + parsedID := pgtypeUUID(namespaceID) + if useID && !parsedID.Valid { + return nil, db.ErrUUIDInvalid + } + limit, offset := c.getRequestedLimitOffset(r.GetPagination()) maxLimit := c.listCfg.limitMax @@ -138,8 +205,10 @@ func (c PolicyDBClient) ListRegisteredResources(ctx context.Context, r *register } list, err := c.queries.listRegisteredResources(ctx, listRegisteredResourcesParams{ - Limit: limit, - Offset: offset, + NamespaceID: parsedID, + NamespaceFqn: pgtypeText(r.GetNamespaceFqn()), + Limit: limit, + Offset: offset, }) if err != nil { return nil, db.WrapIfKnownInvalidQueryErr(err) @@ -153,16 +222,22 @@ func (c PolicyDBClient) ListRegisteredResources(ctx context.Context, r *register return nil, err } + namespace, err := hydrateNamespaceFromInterface(r.Namespace) + if err != nil { + return nil, err + } + values := []*policy.RegisteredResourceValue{} if err = unmarshalRegisteredResourceValuesProto(r.Values, &values); err != nil { return nil, err } rrList[i] = &policy.RegisteredResource{ - Id: r.ID, - Name: r.Name, - Metadata: metadata, - Values: values, + Id: r.ID, + Name: r.Name, + Metadata: metadata, + Namespace: namespace, + Values: values, } } @@ -281,6 +356,9 @@ func (c PolicyDBClient) GetRegisteredResourceValue(ctx context.Context, r *regis } params.Name = pgtypeText(parsed.Name) params.Value = pgtypeText(parsed.Value) + if parsed.Namespace != "" { + params.NamespaceFqn = pgtypeText("https://" + parsed.Namespace) + } default: // unexpected type return nil, db.ErrSelectIdentifierInvalid @@ -296,6 +374,11 @@ func (c PolicyDBClient) GetRegisteredResourceValue(ctx context.Context, r *regis return nil, err } + namespace, err := hydrateNamespaceFromInterface(rv.Namespace) + if err != nil { + return nil, err + } + actionAttrValues := []*policy.RegisteredResourceValue_ActionAttributeValue{} if err = unmarshalRegisteredResourceActionAttributeValuesProto(rv.ActionAttributeValues, &actionAttrValues); err != nil { return nil, err @@ -306,7 +389,9 @@ func (c PolicyDBClient) GetRegisteredResourceValue(ctx context.Context, r *regis Value: rv.Value, Metadata: metadata, Resource: &policy.RegisteredResource{ - Id: rv.RegisteredResourceID, + Id: rv.RegisteredResourceID, + Name: rv.ResourceName, + Namespace: namespace, }, ActionAttributeValues: actionAttrValues, }, nil @@ -371,6 +456,11 @@ func (c PolicyDBClient) ListRegisteredResourceValues(ctx context.Context, r *reg return nil, err } + namespace, err := hydrateNamespaceFromInterface(r.Namespace) + if err != nil { + return nil, err + } + actionAttrValues := []*policy.RegisteredResourceValue_ActionAttributeValue{} if err = unmarshalRegisteredResourceActionAttributeValuesProto(r.ActionAttributeValues, &actionAttrValues); err != nil { return nil, err @@ -381,7 +471,9 @@ func (c PolicyDBClient) ListRegisteredResourceValues(ctx context.Context, r *reg Value: r.Value, Metadata: metadata, Resource: &policy.RegisteredResource{ - Id: r.RegisteredResourceID, + Id: r.RegisteredResourceID, + Name: r.ResourceName, + Namespace: namespace, }, ActionAttributeValues: actionAttrValues, } @@ -478,15 +570,22 @@ func (c PolicyDBClient) createRegisteredResourceActionAttributeValues(ctx contex return nil } + // Look up the namespace_id of the registered resource for same-namespace enforcement + nsUUID, err := c.queries.getRegisteredResourceNamespaceIDByValueID(ctx, registeredResourceValueID) + if err != nil { + return db.WrapIfKnownInvalidQueryErr(err) + } + resourceNamespaceID := UUIDToString(nsUUID) + createActionAttributeValueParams := make([]createRegisteredResourceActionAttributeValuesParams, len(actionAttrValues)) var actionID, attributeValueID string for i, aav := range actionAttrValues { - switch identifier := aav.GetActionIdentifier().(type) { + switch ident := aav.GetActionIdentifier().(type) { case *registeredresources.ActionAttributeValue_ActionId: - actionID = identifier.ActionId + actionID = ident.ActionId case *registeredresources.ActionAttributeValue_ActionName: a, err := c.queries.getAction(ctx, getActionParams{ - Name: pgtypeText(strings.ToLower(identifier.ActionName)), + Name: pgtypeText(strings.ToLower(ident.ActionName)), }) if err != nil { return db.WrapIfKnownInvalidQueryErr(err) @@ -496,12 +595,12 @@ func (c PolicyDBClient) createRegisteredResourceActionAttributeValues(ctx contex return db.ErrSelectIdentifierInvalid } - switch identifier := aav.GetAttributeValueIdentifier().(type) { + switch ident := aav.GetAttributeValueIdentifier().(type) { case *registeredresources.ActionAttributeValue_AttributeValueId: - attributeValueID = identifier.AttributeValueId + attributeValueID = ident.AttributeValueId case *registeredresources.ActionAttributeValue_AttributeValueFqn: av, err := c.queries.getAttributeValue(ctx, getAttributeValueParams{ - Fqn: pgtypeText(strings.ToLower(identifier.AttributeValueFqn)), + Fqn: pgtypeText(strings.ToLower(ident.AttributeValueFqn)), }) if err != nil { return db.WrapIfKnownInvalidQueryErr(err) @@ -518,6 +617,24 @@ func (c PolicyDBClient) createRegisteredResourceActionAttributeValues(ctx contex } } + // Same-namespace enforcement (batch): all attribute values must belong to the same namespace as the registered resource + if resourceNamespaceID != "" { + avIDs := make([]string, len(createActionAttributeValueParams)) + for i, p := range createActionAttributeValueParams { + avIDs[i] = p.AttributeValueID + } + rows, err := c.queries.getAttributeValueNamespaceIDs(ctx, avIDs) + if err != nil { + return db.WrapIfKnownInvalidQueryErr(err) + } + for _, row := range rows { + if row.NamespaceID != resourceNamespaceID { + return fmt.Errorf("attribute value %s belongs to namespace %s, but registered resource belongs to namespace %s: %w", + row.AttributeValueID, row.NamespaceID, resourceNamespaceID, db.ErrForeignKeyViolation) + } + } + } + count, err := c.queries.createRegisteredResourceActionAttributeValues(ctx, createActionAttributeValueParams) if err != nil { return db.WrapIfKnownInvalidQueryErr(err) diff --git a/service/policy/db/registered_resources.sql.go b/service/policy/db/registered_resources.sql.go index d3fee8aa61..000a36ae02 100644 --- a/service/policy/db/registered_resources.sql.go +++ b/service/policy/db/registered_resources.sql.go @@ -13,28 +13,97 @@ import ( const createRegisteredResource = `-- name: createRegisteredResource :one -INSERT INTO registered_resources (name, metadata) -VALUES ($1, $2) -RETURNING id +WITH inserted AS ( + INSERT INTO registered_resources (namespace_id, name, metadata) + SELECT + COALESCE($1::uuid, fqns.namespace_id), + $2, + $3 + FROM ( + SELECT + $1::uuid as direct_namespace_id + ) direct + LEFT JOIN attribute_fqns fqns ON fqns.fqn = $4::text AND $1::text IS NULL + WHERE + ($1::text IS NOT NULL AND direct.direct_namespace_id IS NOT NULL) OR + ($4::text IS NOT NULL AND fqns.namespace_id IS NOT NULL) + RETURNING id, namespace_id, name, metadata +) +SELECT + i.id, + i.name, + i.metadata, + JSON_BUILD_OBJECT( + 'id', n.id, + 'name', n.name, + 'fqn', fqns.fqn + ) as namespace +FROM inserted i +JOIN attribute_namespaces n ON i.namespace_id = n.id +LEFT JOIN attribute_fqns fqns ON fqns.namespace_id = n.id AND fqns.attribute_id IS NULL AND fqns.value_id IS NULL ` type createRegisteredResourceParams struct { - Name string `json:"name"` - Metadata []byte `json:"metadata"` + NamespaceID pgtype.UUID `json:"namespace_id"` + Name string `json:"name"` + Metadata []byte `json:"metadata"` + NamespaceFqn pgtype.Text `json:"namespace_fqn"` +} + +type createRegisteredResourceRow struct { + ID string `json:"id"` + Name string `json:"name"` + Metadata []byte `json:"metadata"` + Namespace []byte `json:"namespace"` } // -------------------------------------------------------------- // REGISTERED RESOURCES // -------------------------------------------------------------- // -// INSERT INTO registered_resources (name, metadata) -// VALUES ($1, $2) -// RETURNING id -func (q *Queries) createRegisteredResource(ctx context.Context, arg createRegisteredResourceParams) (string, error) { - row := q.db.QueryRow(ctx, createRegisteredResource, arg.Name, arg.Metadata) - var id string - err := row.Scan(&id) - return id, err +// WITH inserted AS ( +// INSERT INTO registered_resources (namespace_id, name, metadata) +// SELECT +// COALESCE($1::uuid, fqns.namespace_id), +// $2, +// $3 +// FROM ( +// SELECT +// $1::uuid as direct_namespace_id +// ) direct +// LEFT JOIN attribute_fqns fqns ON fqns.fqn = $4::text AND $1::text IS NULL +// WHERE +// ($1::text IS NOT NULL AND direct.direct_namespace_id IS NOT NULL) OR +// ($4::text IS NOT NULL AND fqns.namespace_id IS NOT NULL) +// RETURNING id, namespace_id, name, metadata +// ) +// SELECT +// i.id, +// i.name, +// i.metadata, +// JSON_BUILD_OBJECT( +// 'id', n.id, +// 'name', n.name, +// 'fqn', fqns.fqn +// ) as namespace +// FROM inserted i +// JOIN attribute_namespaces n ON i.namespace_id = n.id +// LEFT JOIN attribute_fqns fqns ON fqns.namespace_id = n.id AND fqns.attribute_id IS NULL AND fqns.value_id IS NULL +func (q *Queries) createRegisteredResource(ctx context.Context, arg createRegisteredResourceParams) (createRegisteredResourceRow, error) { + row := q.db.QueryRow(ctx, createRegisteredResource, + arg.NamespaceID, + arg.Name, + arg.Metadata, + arg.NamespaceFqn, + ) + var i createRegisteredResourceRow + err := row.Scan( + &i.ID, + &i.Name, + &i.Metadata, + &i.Namespace, + ) + return i, err } type createRegisteredResourceActionAttributeValuesParams struct { @@ -122,6 +191,13 @@ SELECT r.id, r.name, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', r.metadata -> 'labels', 'created_at', r.created_at, 'updated_at', r.updated_at)) as metadata, + CASE WHEN n.id IS NOT NULL THEN + JSON_BUILD_OBJECT( + 'id', n.id, + 'name', n.name, + 'fqn', ns_fqns.fqn + ) + ELSE NULL END as namespace, JSON_AGG( JSON_BUILD_OBJECT( 'id', v.id, @@ -129,23 +205,30 @@ SELECT ) ) FILTER (WHERE v.id IS NOT NULL) as values FROM registered_resources r +LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id +LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL LEFT JOIN registered_resource_values v ON v.registered_resource_id = r.id WHERE ($1::uuid IS NULL OR r.id = $1::uuid) AND - ($2::text IS NULL OR r.name = $2::text) -GROUP BY r.id + ($2::text IS NULL OR r.name = $2::text) AND + ($3::uuid IS NULL OR r.namespace_id = $3::uuid) AND + ($4::text IS NULL OR ns_fqns.fqn = $4::text) +GROUP BY r.id, n.id, ns_fqns.fqn ` type getRegisteredResourceParams struct { - ID pgtype.UUID `json:"id"` - Name pgtype.Text `json:"name"` + ID pgtype.UUID `json:"id"` + Name pgtype.Text `json:"name"` + NamespaceID pgtype.UUID `json:"namespace_id"` + NamespaceFqn pgtype.Text `json:"namespace_fqn"` } type getRegisteredResourceRow struct { - ID string `json:"id"` - Name string `json:"name"` - Metadata []byte `json:"metadata"` - Values []byte `json:"values"` + ID string `json:"id"` + Name string `json:"name"` + Metadata []byte `json:"metadata"` + Namespace interface{} `json:"namespace"` + Values []byte `json:"values"` } // getRegisteredResource @@ -154,6 +237,13 @@ type getRegisteredResourceRow struct { // r.id, // r.name, // JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', r.metadata -> 'labels', 'created_at', r.created_at, 'updated_at', r.updated_at)) as metadata, +// CASE WHEN n.id IS NOT NULL THEN +// JSON_BUILD_OBJECT( +// 'id', n.id, +// 'name', n.name, +// 'fqn', ns_fqns.fqn +// ) +// ELSE NULL END as namespace, // JSON_AGG( // JSON_BUILD_OBJECT( // 'id', v.id, @@ -161,29 +251,70 @@ type getRegisteredResourceRow struct { // ) // ) FILTER (WHERE v.id IS NOT NULL) as values // FROM registered_resources r +// LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id +// LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL // LEFT JOIN registered_resource_values v ON v.registered_resource_id = r.id // WHERE // ($1::uuid IS NULL OR r.id = $1::uuid) AND -// ($2::text IS NULL OR r.name = $2::text) -// GROUP BY r.id +// ($2::text IS NULL OR r.name = $2::text) AND +// ($3::uuid IS NULL OR r.namespace_id = $3::uuid) AND +// ($4::text IS NULL OR ns_fqns.fqn = $4::text) +// GROUP BY r.id, n.id, ns_fqns.fqn func (q *Queries) getRegisteredResource(ctx context.Context, arg getRegisteredResourceParams) (getRegisteredResourceRow, error) { - row := q.db.QueryRow(ctx, getRegisteredResource, arg.ID, arg.Name) + row := q.db.QueryRow(ctx, getRegisteredResource, + arg.ID, + arg.Name, + arg.NamespaceID, + arg.NamespaceFqn, + ) var i getRegisteredResourceRow err := row.Scan( &i.ID, &i.Name, &i.Metadata, + &i.Namespace, &i.Values, ) return i, err } +const getRegisteredResourceNamespaceIDByValueID = `-- name: getRegisteredResourceNamespaceIDByValueID :one + +SELECT rr.namespace_id +FROM registered_resources rr +JOIN registered_resource_values rrv ON rrv.registered_resource_id = rr.id +WHERE rrv.id = $1 +` + +// -------------------------------------------------------------- +// Registered Resource Action Attribute Values +// -------------------------------------------------------------- +// +// SELECT rr.namespace_id +// FROM registered_resources rr +// JOIN registered_resource_values rrv ON rrv.registered_resource_id = rr.id +// WHERE rrv.id = $1 +func (q *Queries) getRegisteredResourceNamespaceIDByValueID(ctx context.Context, id string) (pgtype.UUID, error) { + row := q.db.QueryRow(ctx, getRegisteredResourceNamespaceIDByValueID, id) + var namespace_id pgtype.UUID + err := row.Scan(&namespace_id) + return namespace_id, err +} + const getRegisteredResourceValue = `-- name: getRegisteredResourceValue :one SELECT v.id, v.registered_resource_id, v.value, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', v.metadata -> 'labels', 'created_at', v.created_at, 'updated_at', v.updated_at)) as metadata, + CASE WHEN n.id IS NOT NULL THEN + JSON_BUILD_OBJECT( + 'id', n.id, + 'name', n.name, + 'fqn', ns_fqns.fqn + ) + ELSE NULL END as namespace, + r.name as resource_name, JSON_AGG( JSON_BUILD_OBJECT( 'action', JSON_BUILD_OBJECT( @@ -199,6 +330,8 @@ SELECT ) FILTER (WHERE rav.id IS NOT NULL) as action_attribute_values FROM registered_resource_values v JOIN registered_resources r ON v.registered_resource_id = r.id +LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id +LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL LEFT JOIN registered_resource_action_attribute_values rav ON v.id = rav.registered_resource_value_id LEFT JOIN actions a on rav.action_id = a.id LEFT JOIN attribute_values av on rav.attribute_value_id = av.id @@ -206,22 +339,26 @@ LEFT JOIN attribute_fqns fqns on av.id = fqns.value_id WHERE ($1::uuid IS NULL OR v.id = $1::uuid) AND ($2::text IS NULL OR r.name = $2::text) AND - ($3::text IS NULL OR v.value = $3::text) -GROUP BY v.id + ($3::text IS NULL OR v.value = $3::text) AND + ($4::text IS NULL OR ns_fqns.fqn = $4::text) +GROUP BY v.id, r.name, n.id, ns_fqns.fqn ` type getRegisteredResourceValueParams struct { - ID pgtype.UUID `json:"id"` - Name pgtype.Text `json:"name"` - Value pgtype.Text `json:"value"` + ID pgtype.UUID `json:"id"` + Name pgtype.Text `json:"name"` + Value pgtype.Text `json:"value"` + NamespaceFqn pgtype.Text `json:"namespace_fqn"` } type getRegisteredResourceValueRow struct { - ID string `json:"id"` - RegisteredResourceID string `json:"registered_resource_id"` - Value string `json:"value"` - Metadata []byte `json:"metadata"` - ActionAttributeValues []byte `json:"action_attribute_values"` + ID string `json:"id"` + RegisteredResourceID string `json:"registered_resource_id"` + Value string `json:"value"` + Metadata []byte `json:"metadata"` + Namespace interface{} `json:"namespace"` + ResourceName string `json:"resource_name"` + ActionAttributeValues []byte `json:"action_attribute_values"` } // getRegisteredResourceValue @@ -231,6 +368,14 @@ type getRegisteredResourceValueRow struct { // v.registered_resource_id, // v.value, // JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', v.metadata -> 'labels', 'created_at', v.created_at, 'updated_at', v.updated_at)) as metadata, +// CASE WHEN n.id IS NOT NULL THEN +// JSON_BUILD_OBJECT( +// 'id', n.id, +// 'name', n.name, +// 'fqn', ns_fqns.fqn +// ) +// ELSE NULL END as namespace, +// r.name as resource_name, // JSON_AGG( // JSON_BUILD_OBJECT( // 'action', JSON_BUILD_OBJECT( @@ -246,6 +391,8 @@ type getRegisteredResourceValueRow struct { // ) FILTER (WHERE rav.id IS NOT NULL) as action_attribute_values // FROM registered_resource_values v // JOIN registered_resources r ON v.registered_resource_id = r.id +// LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id +// LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL // LEFT JOIN registered_resource_action_attribute_values rav ON v.id = rav.registered_resource_value_id // LEFT JOIN actions a on rav.action_id = a.id // LEFT JOIN attribute_values av on rav.attribute_value_id = av.id @@ -253,16 +400,24 @@ type getRegisteredResourceValueRow struct { // WHERE // ($1::uuid IS NULL OR v.id = $1::uuid) AND // ($2::text IS NULL OR r.name = $2::text) AND -// ($3::text IS NULL OR v.value = $3::text) -// GROUP BY v.id +// ($3::text IS NULL OR v.value = $3::text) AND +// ($4::text IS NULL OR ns_fqns.fqn = $4::text) +// GROUP BY v.id, r.name, n.id, ns_fqns.fqn func (q *Queries) getRegisteredResourceValue(ctx context.Context, arg getRegisteredResourceValueParams) (getRegisteredResourceValueRow, error) { - row := q.db.QueryRow(ctx, getRegisteredResourceValue, arg.ID, arg.Name, arg.Value) + row := q.db.QueryRow(ctx, getRegisteredResourceValue, + arg.ID, + arg.Name, + arg.Value, + arg.NamespaceFqn, + ) var i getRegisteredResourceValueRow err := row.Scan( &i.ID, &i.RegisteredResourceID, &i.Value, &i.Metadata, + &i.Namespace, + &i.ResourceName, &i.ActionAttributeValues, ) return i, err @@ -279,6 +434,14 @@ SELECT v.registered_resource_id, v.value, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', v.metadata -> 'labels', 'created_at', v.created_at, 'updated_at', v.updated_at)) as metadata, + CASE WHEN n.id IS NOT NULL THEN + JSON_BUILD_OBJECT( + 'id', n.id, + 'name', n.name, + 'fqn', ns_fqns.fqn + ) + ELSE NULL END as namespace, + r.name as resource_name, JSON_AGG( JSON_BUILD_OBJECT( 'action', JSON_BUILD_OBJECT( @@ -295,14 +458,16 @@ SELECT counted.total FROM registered_resource_values v JOIN registered_resources r ON v.registered_resource_id = r.id +LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id +LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL LEFT JOIN registered_resource_action_attribute_values rav ON v.id = rav.registered_resource_value_id LEFT JOIN actions a on rav.action_id = a.id LEFT JOIN attribute_values av on rav.attribute_value_id = av.id -LEFT JOIN attribute_fqns fqns on av.id = fqns.value_id +LEFT JOIN attribute_fqns fqns on av.id = fqns.value_id CROSS JOIN counted WHERE $1::uuid IS NULL OR v.registered_resource_id = $1::uuid -GROUP BY v.id, counted.total +GROUP BY v.id, r.name, n.id, ns_fqns.fqn, counted.total ORDER BY v.created_at DESC LIMIT $3 OFFSET $2 @@ -315,12 +480,14 @@ type listRegisteredResourceValuesParams struct { } type listRegisteredResourceValuesRow struct { - ID string `json:"id"` - RegisteredResourceID string `json:"registered_resource_id"` - Value string `json:"value"` - Metadata []byte `json:"metadata"` - ActionAttributeValues []byte `json:"action_attribute_values"` - Total int64 `json:"total"` + ID string `json:"id"` + RegisteredResourceID string `json:"registered_resource_id"` + Value string `json:"value"` + Metadata []byte `json:"metadata"` + Namespace interface{} `json:"namespace"` + ResourceName string `json:"resource_name"` + ActionAttributeValues []byte `json:"action_attribute_values"` + Total int64 `json:"total"` } // listRegisteredResourceValues @@ -335,6 +502,14 @@ type listRegisteredResourceValuesRow struct { // v.registered_resource_id, // v.value, // JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', v.metadata -> 'labels', 'created_at', v.created_at, 'updated_at', v.updated_at)) as metadata, +// CASE WHEN n.id IS NOT NULL THEN +// JSON_BUILD_OBJECT( +// 'id', n.id, +// 'name', n.name, +// 'fqn', ns_fqns.fqn +// ) +// ELSE NULL END as namespace, +// r.name as resource_name, // JSON_AGG( // JSON_BUILD_OBJECT( // 'action', JSON_BUILD_OBJECT( @@ -351,6 +526,8 @@ type listRegisteredResourceValuesRow struct { // counted.total // FROM registered_resource_values v // JOIN registered_resources r ON v.registered_resource_id = r.id +// LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id +// LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL // LEFT JOIN registered_resource_action_attribute_values rav ON v.id = rav.registered_resource_value_id // LEFT JOIN actions a on rav.action_id = a.id // LEFT JOIN attribute_values av on rav.attribute_value_id = av.id @@ -358,7 +535,7 @@ type listRegisteredResourceValuesRow struct { // CROSS JOIN counted // WHERE // $1::uuid IS NULL OR v.registered_resource_id = $1::uuid -// GROUP BY v.id, counted.total +// GROUP BY v.id, r.name, n.id, ns_fqns.fqn, counted.total // ORDER BY v.created_at DESC // LIMIT $3 // OFFSET $2 @@ -376,6 +553,8 @@ func (q *Queries) listRegisteredResourceValues(ctx context.Context, arg listRegi &i.RegisteredResourceID, &i.Value, &i.Metadata, + &i.Namespace, + &i.ResourceName, &i.ActionAttributeValues, &i.Total, ); err != nil { @@ -391,13 +570,25 @@ func (q *Queries) listRegisteredResourceValues(ctx context.Context, arg listRegi const listRegisteredResources = `-- name: listRegisteredResources :many WITH counted AS ( - SELECT COUNT(id) AS total - FROM registered_resources + SELECT COUNT(r.id) AS total + FROM registered_resources r + LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id + LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL + WHERE + ($1::uuid IS NULL OR r.namespace_id = $1::uuid) AND + ($2::text IS NULL OR ns_fqns.fqn = $2::text) ) SELECT r.id, r.name, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', r.metadata -> 'labels', 'created_at', r.created_at, 'updated_at', r.updated_at)) as metadata, + CASE WHEN n.id IS NOT NULL THEN + JSON_BUILD_OBJECT( + 'id', n.id, + 'name', n.name, + 'fqn', ns_fqns.fqn + ) + ELSE NULL END as namespace, -- Aggregate all values for this resource into a JSON array, filtering NULL entries JSON_AGG( JSON_BUILD_OBJECT( @@ -408,6 +599,8 @@ SELECT ) FILTER (WHERE v.id IS NOT NULL) as values, counted.total FROM registered_resources r +LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id +LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL CROSS JOIN counted LEFT JOIN registered_resource_values v ON v.registered_resource_id = r.id LEFT JOIN LATERAL ( @@ -432,35 +625,53 @@ LEFT JOIN LATERAL ( -- Correlate to the outer query's resource value WHERE rav.registered_resource_value_id = v.id ) action_attrs ON true -- required syntax for LATERAL joins -GROUP BY r.id, counted.total +WHERE + ($1::uuid IS NULL OR r.namespace_id = $1::uuid) AND + ($2::text IS NULL OR ns_fqns.fqn = $2::text) +GROUP BY r.id, n.id, ns_fqns.fqn, counted.total ORDER BY r.created_at DESC -LIMIT $2 -OFFSET $1 +LIMIT $4 +OFFSET $3 ` type listRegisteredResourcesParams struct { - Offset int32 `json:"offset_"` - Limit int32 `json:"limit_"` + NamespaceID pgtype.UUID `json:"namespace_id"` + NamespaceFqn pgtype.Text `json:"namespace_fqn"` + Offset int32 `json:"offset_"` + Limit int32 `json:"limit_"` } type listRegisteredResourcesRow struct { - ID string `json:"id"` - Name string `json:"name"` - Metadata []byte `json:"metadata"` - Values []byte `json:"values"` - Total int64 `json:"total"` + ID string `json:"id"` + Name string `json:"name"` + Metadata []byte `json:"metadata"` + Namespace interface{} `json:"namespace"` + Values []byte `json:"values"` + Total int64 `json:"total"` } // Build a JSON array of action/attribute pairs for each resource value // // WITH counted AS ( -// SELECT COUNT(id) AS total -// FROM registered_resources +// SELECT COUNT(r.id) AS total +// FROM registered_resources r +// LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id +// LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL +// WHERE +// ($1::uuid IS NULL OR r.namespace_id = $1::uuid) AND +// ($2::text IS NULL OR ns_fqns.fqn = $2::text) // ) // SELECT // r.id, // r.name, // JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', r.metadata -> 'labels', 'created_at', r.created_at, 'updated_at', r.updated_at)) as metadata, +// CASE WHEN n.id IS NOT NULL THEN +// JSON_BUILD_OBJECT( +// 'id', n.id, +// 'name', n.name, +// 'fqn', ns_fqns.fqn +// ) +// ELSE NULL END as namespace, // -- Aggregate all values for this resource into a JSON array, filtering NULL entries // JSON_AGG( // JSON_BUILD_OBJECT( @@ -471,6 +682,8 @@ type listRegisteredResourcesRow struct { // ) FILTER (WHERE v.id IS NOT NULL) as values, // counted.total // FROM registered_resources r +// LEFT JOIN attribute_namespaces n ON r.namespace_id = n.id +// LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL // CROSS JOIN counted // LEFT JOIN registered_resource_values v ON v.registered_resource_id = r.id // LEFT JOIN LATERAL ( @@ -495,12 +708,20 @@ type listRegisteredResourcesRow struct { // -- Correlate to the outer query's resource value // WHERE rav.registered_resource_value_id = v.id // ) action_attrs ON true -- required syntax for LATERAL joins -// GROUP BY r.id, counted.total +// WHERE +// ($1::uuid IS NULL OR r.namespace_id = $1::uuid) AND +// ($2::text IS NULL OR ns_fqns.fqn = $2::text) +// GROUP BY r.id, n.id, ns_fqns.fqn, counted.total // ORDER BY r.created_at DESC -// LIMIT $2 -// OFFSET $1 +// LIMIT $4 +// OFFSET $3 func (q *Queries) listRegisteredResources(ctx context.Context, arg listRegisteredResourcesParams) ([]listRegisteredResourcesRow, error) { - rows, err := q.db.Query(ctx, listRegisteredResources, arg.Offset, arg.Limit) + rows, err := q.db.Query(ctx, listRegisteredResources, + arg.NamespaceID, + arg.NamespaceFqn, + arg.Offset, + arg.Limit, + ) if err != nil { return nil, err } @@ -512,6 +733,7 @@ func (q *Queries) listRegisteredResources(ctx context.Context, arg listRegistere &i.ID, &i.Name, &i.Metadata, + &i.Namespace, &i.Values, &i.Total, ); err != nil { diff --git a/service/policy/registeredresources/registered_resources.proto b/service/policy/registeredresources/registered_resources.proto index 6f46aecc7d..85fd52d673 100644 --- a/service/policy/registeredresources/registered_resources.proto +++ b/service/policy/registeredresources/registered_resources.proto @@ -79,13 +79,14 @@ message GetRegisteredResourceRequest { } // Optional - namespace context for name-based lookups (since names are not globally unique for namespaced RRs) + option (buf.validate.message).oneof = { fields: ["namespace_id", "namespace_fqn"], required: false }; string namespace_fqn = 3 [ - (buf.validate.field).cel = { - id: "optional_namespace_fqn", - message: "Namespace FQN must be a valid URI if provided", - expression: "size(this) == 0 || this.isUri()" + (buf.validate.field).string = { + min_len : 1 + uri : true } ]; + string namespace_id = 4 [(buf.validate.field).string.uuid = true]; } message GetRegisteredResourceResponse { policy.RegisteredResource resource = 1; diff --git a/service/policy/registeredresources/registered_resources_test.go b/service/policy/registeredresources/registered_resources_test.go index e527a347f5..06dcfaf84e 100644 --- a/service/policy/registeredresources/registered_resources_test.go +++ b/service/policy/registeredresources/registered_resources_test.go @@ -241,6 +241,24 @@ func (s *RegisteredResourcesSuite) TestGetRegisteredResource_Valid_Succeeds() { }, }, }, + { + name: "Name with Namespace ID", + req: ®isteredresources.GetRegisteredResourceRequest{ + Identifier: ®isteredresources.GetRegisteredResourceRequest_Name{ + Name: validName, + }, + NamespaceId: validUUID, + }, + }, + { + name: "Name with Namespace FQN", + req: ®isteredresources.GetRegisteredResourceRequest{ + Identifier: ®isteredresources.GetRegisteredResourceRequest_Name{ + Name: validName, + }, + NamespaceFqn: validURI, + }, + }, } for _, tc := range testCases { @@ -281,6 +299,37 @@ func (s *RegisteredResourcesSuite) TestGetRegisteredResource_Invalid_Fails() { }, errMsg: errMsgNameFormat, }, + { + name: "Invalid Namespace ID (non-UUID)", + req: ®isteredresources.GetRegisteredResourceRequest{ + Identifier: ®isteredresources.GetRegisteredResourceRequest_Name{ + Name: validName, + }, + NamespaceId: invalidUUID, + }, + errMsg: errMsgUUID, + }, + { + name: "Invalid Namespace FQN (non-URI)", + req: ®isteredresources.GetRegisteredResourceRequest{ + Identifier: ®isteredresources.GetRegisteredResourceRequest_Name{ + Name: validName, + }, + NamespaceFqn: invalidURI, + }, + errMsg: errMsgURI, + }, + { + name: "Both Namespace ID and FQN provided (oneof violation)", + req: ®isteredresources.GetRegisteredResourceRequest{ + Identifier: ®isteredresources.GetRegisteredResourceRequest_Name{ + Name: validName, + }, + NamespaceId: validUUID, + NamespaceFqn: validURI, + }, + errMsg: "oneof", + }, } for _, tc := range testCases {