From b52ff2d72f647e6aef799cecbd09bee206fd567b Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Thu, 29 May 2025 12:26:59 -0600 Subject: [PATCH 01/55] initial reg res support in pdp structs --- service/internal/access/v2/helpers.go | 1 + .../internal/access/v2/just_in_time_pdp.go | 35 ++++++++++++++++++- service/internal/access/v2/pdp.go | 23 ++++++++++-- service/internal/access/v2/validators.go | 16 +++++++++ 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/service/internal/access/v2/helpers.go b/service/internal/access/v2/helpers.go index 6c14462f02..416a887109 100644 --- a/service/internal/access/v2/helpers.go +++ b/service/internal/access/v2/helpers.go @@ -16,6 +16,7 @@ import ( var ( ErrInvalidSubjectMapping = errors.New("access: invalid subject mapping") ErrInvalidAttributeDefinition = errors.New("access: invalid attribute definition") + ErrInvalidRegisteredResource = errors.New("access: invalid registered resource") ) // getDefinition parses the value FQN and uses it to retrieve the definition from the provided definitions canmap diff --git a/service/internal/access/v2/just_in_time_pdp.go b/service/internal/access/v2/just_in_time_pdp.go index b50dde0e84..dd2dd29917 100644 --- a/service/internal/access/v2/just_in_time_pdp.go +++ b/service/internal/access/v2/just_in_time_pdp.go @@ -13,6 +13,7 @@ import ( entityresolutionV2 "github.com/opentdf/platform/protocol/go/entityresolution/v2" "github.com/opentdf/platform/protocol/go/policy" attrs "github.com/opentdf/platform/protocol/go/policy/attributes" + "github.com/opentdf/platform/protocol/go/policy/registeredresources" "github.com/opentdf/platform/protocol/go/policy/subjectmapping" otdfSDK "github.com/opentdf/platform/sdk" @@ -63,7 +64,11 @@ func NewJustInTimePDP( if err != nil { return nil, fmt.Errorf("failed to fetch all subject mappings: %w", err) } - pdp, err := NewPolicyDecisionPoint(ctx, l, allAttributes, allSubjectMappings) + allRegisteredResources, err := p.fetchAllRegisteredResources(ctx) + if err != nil { + return nil, fmt.Errorf("failed to fetch all registered resources: %w", err) + } + pdp, err := NewPolicyDecisionPoint(ctx, l, allAttributes, allSubjectMappings, allRegisteredResources) if err != nil { return nil, fmt.Errorf("failed to create new policy decision point: %w", err) } @@ -269,6 +274,34 @@ func (p *JustInTimePDP) fetchAllSubjectMappings(ctx context.Context) ([]*policy. return smList, nil } +// fetchAllRegisteredResources retrieves all registered resources within policy +func (p *JustInTimePDP) fetchAllRegisteredResources(ctx context.Context) ([]*policy.RegisteredResource, error) { + // If quantity of registered resources exceeds maximum list pagination, all are needed to determine entitlements + var nextOffset int32 + rrList := make([]*policy.RegisteredResource, 0) + + for { + listed, err := p.sdk.RegisteredResources.ListRegisteredResources(ctx, ®isteredresources.ListRegisteredResourcesRequest{ + // defer to service default for limit pagination + Pagination: &policy.PageRequest{ + Offset: nextOffset, + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to list registered resources: %w", err) + } + + nextOffset = listed.GetPagination().GetNextOffset() + rrList = append(rrList, listed.GetResources()...) + + if nextOffset <= 0 { + break + } + } + + return rrList, nil +} + // resolveEntitiesFromEntityChain roundtrips to ERS to resolve the provided entity chain // and optionally skips environment entities (which is expected behavior in decision flow) func (p *JustInTimePDP) resolveEntitiesFromEntityChain( diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 7649ad9ed9..3339b76dd1 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -8,6 +8,7 @@ import ( "strconv" "strings" + "github.com/opentdf/platform/lib/identifier" authz "github.com/opentdf/platform/protocol/go/authorization/v2" entityresolutionV2 "github.com/opentdf/platform/protocol/go/entityresolution/v2" "github.com/opentdf/platform/protocol/go/policy" @@ -47,7 +48,7 @@ type EntitlementFailure struct { type PolicyDecisionPoint struct { logger *logger.Logger allEntitleableAttributesByValueFQN map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue - // allRegisteredResourcesByValueFQN map[string]*policy.RegisteredResourceValue + allRegisteredResourcesByValueFQN map[string]*policy.RegisteredResourceValue } var ( @@ -67,8 +68,7 @@ func NewPolicyDecisionPoint( l *logger.Logger, allAttributeDefinitions []*policy.Attribute, allSubjectMappings []*policy.SubjectMapping, - // TODO: take in all registered resources and store them in memory by value FQN - // allRegisteredResources []*policy.RegisteredResource, + allRegisteredResources []*policy.RegisteredResource, ) (*PolicyDecisionPoint, error) { var err error @@ -126,9 +126,26 @@ func NewPolicyDecisionPoint( allEntitleableAttributesByValueFQN[mappedValueFQN] = mapped } + allRegisteredResourcesByValueFQN := make(map[string]*policy.RegisteredResourceValue) + for _, rr := range allRegisteredResources { + if err := validateRegisteredResource(rr); err != nil { + return nil, fmt.Errorf("invalid registered resource: %w", err) + } + rrName := rr.GetName() + + for _, v := range rr.GetValues() { + fullyQualifiedValue := identifier.FullyQualifiedRegisteredResourceValue{ + Name: rrName, + Value: v.GetValue(), + } + allRegisteredResourcesByValueFQN[fullyQualifiedValue.FQN()] = v + } + } + pdp := &PolicyDecisionPoint{ l, allEntitleableAttributesByValueFQN, + allRegisteredResourcesByValueFQN, } return pdp, nil } diff --git a/service/internal/access/v2/validators.go b/service/internal/access/v2/validators.go index 7e21b1895e..f74b1bd5da 100644 --- a/service/internal/access/v2/validators.go +++ b/service/internal/access/v2/validators.go @@ -97,6 +97,22 @@ func validateAttribute(attribute *policy.Attribute) error { return nil } +// validateRegisteredResource validates the registered resource is valid for an entitlement decision +// +// registered resource: +// +// - must not be nil +// - must have non-empty values +func validateRegisteredResource(registeredResource *policy.RegisteredResource) error { + if registeredResource == nil { + return fmt.Errorf("registered resource is nil: %w", ErrInvalidRegisteredResource) + } + if len(registeredResource.GetValues()) == 0 { + return fmt.Errorf("registered resource values are empty: %w", ErrInvalidRegisteredResource) + } + return nil +} + // validateEntityRepresentations validates the entity representations are valid for an entitlement decision // // - entityRepresentations: must have at least one non-nil entity representation From cf9f89112e52c019d350776f0db60cff53cd914f Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Mon, 2 Jun 2025 10:49:56 -0600 Subject: [PATCH 02/55] rename to reflect actual items in list --- service/internal/access/v2/pdp.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 3339b76dd1..788031950a 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -48,7 +48,7 @@ type EntitlementFailure struct { type PolicyDecisionPoint struct { logger *logger.Logger allEntitleableAttributesByValueFQN map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue - allRegisteredResourcesByValueFQN map[string]*policy.RegisteredResourceValue + allRegisteredResourceValuesByFQN map[string]*policy.RegisteredResourceValue } var ( @@ -126,7 +126,7 @@ func NewPolicyDecisionPoint( allEntitleableAttributesByValueFQN[mappedValueFQN] = mapped } - allRegisteredResourcesByValueFQN := make(map[string]*policy.RegisteredResourceValue) + allRegisteredResourceValuesByFQN := make(map[string]*policy.RegisteredResourceValue) for _, rr := range allRegisteredResources { if err := validateRegisteredResource(rr); err != nil { return nil, fmt.Errorf("invalid registered resource: %w", err) @@ -138,14 +138,14 @@ func NewPolicyDecisionPoint( Name: rrName, Value: v.GetValue(), } - allRegisteredResourcesByValueFQN[fullyQualifiedValue.FQN()] = v + allRegisteredResourceValuesByFQN[fullyQualifiedValue.FQN()] = v } } pdp := &PolicyDecisionPoint{ l, allEntitleableAttributesByValueFQN, - allRegisteredResourcesByValueFQN, + allRegisteredResourceValuesByFQN, } return pdp, nil } From d8020608a3c6d76df056d7b002b23c8faeee0e33 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Mon, 2 Jun 2025 11:28:54 -0600 Subject: [PATCH 03/55] initial setup for reg res GetEntitlements --- service/internal/access/v2/helpers.go | 7 +++--- .../internal/access/v2/just_in_time_pdp.go | 7 ++++-- service/internal/access/v2/pdp.go | 25 +++++++++++++++++++ service/internal/access/v2/validators.go | 16 ++++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/service/internal/access/v2/helpers.go b/service/internal/access/v2/helpers.go index 416a887109..4fde2d01b8 100644 --- a/service/internal/access/v2/helpers.go +++ b/service/internal/access/v2/helpers.go @@ -14,9 +14,10 @@ import ( ) var ( - ErrInvalidSubjectMapping = errors.New("access: invalid subject mapping") - ErrInvalidAttributeDefinition = errors.New("access: invalid attribute definition") - ErrInvalidRegisteredResource = errors.New("access: invalid registered resource") + ErrInvalidSubjectMapping = errors.New("access: invalid subject mapping") + ErrInvalidAttributeDefinition = errors.New("access: invalid attribute definition") + ErrInvalidRegisteredResource = errors.New("access: invalid registered resource") + ErrInvalidRegisteredResourceValue = errors.New("access: invalid registered resource value") ) // getDefinition parses the value FQN and uses it to retrieve the definition from the provided definitions canmap diff --git a/service/internal/access/v2/just_in_time_pdp.go b/service/internal/access/v2/just_in_time_pdp.go index dd2dd29917..1bc4a0f335 100644 --- a/service/internal/access/v2/just_in_time_pdp.go +++ b/service/internal/access/v2/just_in_time_pdp.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "log/slog" + "strings" "github.com/opentdf/platform/lib/flattening" authzV2 "github.com/opentdf/platform/protocol/go/authorization/v2" @@ -155,8 +156,10 @@ func (p *JustInTimePDP) GetEntitlements( case *authzV2.EntityIdentifier_RegisteredResourceValueFqn: p.logger.DebugContext(ctx, "getting decision - resolving registered resource value FQN") - return nil, errors.New("registered resources not yet implemented") - // TODO: implement this case + valueFQN := strings.ToLower(entityIdentifier.GetRegisteredResourceValueFqn()) + // registered resources do not have entity representations, so we can skip the remaining logic + return p.pdp.GetEntitlementsRegisteredResource(ctx, valueFQN, withComprehensiveHierarchy) + default: return nil, fmt.Errorf("entity type %T: %w", entityIdentifier.GetIdentifier(), ErrInvalidEntityType) } diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 788031950a..2c4052f519 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -244,6 +244,31 @@ func (p *PolicyDecisionPoint) GetDecision( return decision, nil } +func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( + ctx context.Context, + registeredResourceValueFQN string, + withComprehensiveHierarchy bool, +) ([]*authz.EntityEntitlements, error) { + value := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] + + err := validateRegisteredResourceValue(value) + if err != nil { + return nil, fmt.Errorf("invalid registered resource value: %w", err) + } + + l := p.logger.With("withComprehensiveHierarchy", strconv.FormatBool(withComprehensiveHierarchy)) + l.DebugContext(ctx, "getting entitlements for registered resource value", slog.String("fqn", registeredResourceValueFQN)) + + res := &authz.EntityEntitlements{ + EphemeralId: registeredResourceValueFQN, + ActionsPerAttributeValueFqn: make(map[string]*authz.EntityEntitlements_ActionsList), + } + + // todo: get entitlements for registered resource value + + return []*authz.EntityEntitlements{res}, nil +} + func (p *PolicyDecisionPoint) GetEntitlements( ctx context.Context, entityRepresentations []*entityresolutionV2.EntityRepresentation, diff --git a/service/internal/access/v2/validators.go b/service/internal/access/v2/validators.go index f74b1bd5da..a7ab6d6dd6 100644 --- a/service/internal/access/v2/validators.go +++ b/service/internal/access/v2/validators.go @@ -113,6 +113,22 @@ func validateRegisteredResource(registeredResource *policy.RegisteredResource) e return nil } +// validateRegisteredResourceValue validates the registered resource value is valid for an entitlement decision +// +// registered resource: +// +// - must not be nil +// - must have non-empty values +func validateRegisteredResourceValue(registeredResourceValue *policy.RegisteredResourceValue) error { + if registeredResourceValue == nil { + return fmt.Errorf("registered resource value is nil: %w", ErrInvalidRegisteredResourceValue) + } + if registeredResourceValue.GetValue() == "" { + return fmt.Errorf("registered resource value is empty: %w", ErrInvalidRegisteredResourceValue) + } + return nil +} + // validateEntityRepresentations validates the entity representations are valid for an entitlement decision // // - entityRepresentations: must have at least one non-nil entity representation From 299ecbaa18b126c2b7c04fa1b91957b60b569ca7 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 3 Jun 2025 10:06:04 -0600 Subject: [PATCH 04/55] completed GetEntitlements impl --- service/internal/access/v2/pdp.go | 86 ++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 25 deletions(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 2c4052f519..622ede9b6a 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "log/slog" + "slices" "strconv" "strings" @@ -244,31 +245,6 @@ func (p *PolicyDecisionPoint) GetDecision( return decision, nil } -func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( - ctx context.Context, - registeredResourceValueFQN string, - withComprehensiveHierarchy bool, -) ([]*authz.EntityEntitlements, error) { - value := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] - - err := validateRegisteredResourceValue(value) - if err != nil { - return nil, fmt.Errorf("invalid registered resource value: %w", err) - } - - l := p.logger.With("withComprehensiveHierarchy", strconv.FormatBool(withComprehensiveHierarchy)) - l.DebugContext(ctx, "getting entitlements for registered resource value", slog.String("fqn", registeredResourceValueFQN)) - - res := &authz.EntityEntitlements{ - EphemeralId: registeredResourceValueFQN, - ActionsPerAttributeValueFqn: make(map[string]*authz.EntityEntitlements_ActionsList), - } - - // todo: get entitlements for registered resource value - - return []*authz.EntityEntitlements{res}, nil -} - func (p *PolicyDecisionPoint) GetEntitlements( ctx context.Context, entityRepresentations []*entityresolutionV2.EntityRepresentation, @@ -341,3 +317,63 @@ func (p *PolicyDecisionPoint) GetEntitlements( ) return result, nil } + +func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( + ctx context.Context, + registeredResourceValueFQN string, + withComprehensiveHierarchy bool, +) ([]*authz.EntityEntitlements, error) { + registeredResourceValue := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] + + err := validateRegisteredResourceValue(registeredResourceValue) + if err != nil { + return nil, fmt.Errorf("invalid registered resource value: %w", err) + } + + l := p.logger.With("withComprehensiveHierarchy", strconv.FormatBool(withComprehensiveHierarchy)) + l.DebugContext(ctx, "getting entitlements for registered resource value", slog.String("fqn", registeredResourceValueFQN)) + + actionsPerAttributeValueFqn := make(map[string]*authz.EntityEntitlements_ActionsList) + + for _, aav := range registeredResourceValue.GetActionAttributeValues() { + action := aav.GetAction() + attrVal := aav.GetAttributeValue() + attrValFQN := attrVal.GetFqn() + + actionsList, ok := actionsPerAttributeValueFqn[attrValFQN] + if !ok { + actionsList = &authz.EntityEntitlements_ActionsList{ + Actions: make([]*policy.Action, 0), + } + } + + if !slices.ContainsFunc(actionsList.GetActions(), func(a *policy.Action) bool { + return a.GetName() == action.GetName() + }) { + actionsList.Actions = append(actionsList.Actions, action) + } + + actionsPerAttributeValueFqn[attrValFQN] = actionsList + + if withComprehensiveHierarchy { + err = populateLowerValuesIfHierarchy(attrValFQN, p.allEntitleableAttributesByValueFQN, actionsList, actionsPerAttributeValueFqn) + if err != nil { + return nil, fmt.Errorf("error populating comprehensive lower hierarchy values of valueFQN [%s] for registered resource value: %w", attrValFQN, err) + } + } + } + + result := []*authz.EntityEntitlements{ + { + EphemeralId: registeredResourceValueFQN, + ActionsPerAttributeValueFqn: actionsPerAttributeValueFqn, + }, + } + l.DebugContext( + ctx, + "entitlement results for registered resource value", + slog.Any("entitlements", result), + ) + + return result, nil +} From 5c5cdcc5ed6ac47589b69dc9f351856ab075a31b Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 3 Jun 2025 10:30:48 -0600 Subject: [PATCH 05/55] clean up validators and add tests --- service/internal/access/v2/pdp.go | 9 +-- service/internal/access/v2/validators.go | 25 +++--- service/internal/access/v2/validators_test.go | 76 +++++++++++++++++++ 3 files changed, 89 insertions(+), 21 deletions(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 622ede9b6a..6b27fa53d9 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -323,12 +323,9 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( registeredResourceValueFQN string, withComprehensiveHierarchy bool, ) ([]*authz.EntityEntitlements, error) { - registeredResourceValue := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] + // todo: validate the registered resource value FQN? - err := validateRegisteredResourceValue(registeredResourceValue) - if err != nil { - return nil, fmt.Errorf("invalid registered resource value: %w", err) - } + registeredResourceValue := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] l := p.logger.With("withComprehensiveHierarchy", strconv.FormatBool(withComprehensiveHierarchy)) l.DebugContext(ctx, "getting entitlements for registered resource value", slog.String("fqn", registeredResourceValueFQN)) @@ -356,7 +353,7 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( actionsPerAttributeValueFqn[attrValFQN] = actionsList if withComprehensiveHierarchy { - err = populateLowerValuesIfHierarchy(attrValFQN, p.allEntitleableAttributesByValueFQN, actionsList, actionsPerAttributeValueFqn) + err := populateLowerValuesIfHierarchy(attrValFQN, p.allEntitleableAttributesByValueFQN, actionsList, actionsPerAttributeValueFqn) if err != nil { return nil, fmt.Errorf("error populating comprehensive lower hierarchy values of valueFQN [%s] for registered resource value: %w", attrValFQN, err) } diff --git a/service/internal/access/v2/validators.go b/service/internal/access/v2/validators.go index a7ab6d6dd6..2a6ffb2726 100644 --- a/service/internal/access/v2/validators.go +++ b/service/internal/access/v2/validators.go @@ -107,24 +107,19 @@ func validateRegisteredResource(registeredResource *policy.RegisteredResource) e if registeredResource == nil { return fmt.Errorf("registered resource is nil: %w", ErrInvalidRegisteredResource) } + if registeredResource.GetName() == "" { + return fmt.Errorf("registered resource name is empty: %w", ErrInvalidRegisteredResource) + } if len(registeredResource.GetValues()) == 0 { return fmt.Errorf("registered resource values are empty: %w", ErrInvalidRegisteredResource) } - return nil -} - -// validateRegisteredResourceValue validates the registered resource value is valid for an entitlement decision -// -// registered resource: -// -// - must not be nil -// - must have non-empty values -func validateRegisteredResourceValue(registeredResourceValue *policy.RegisteredResourceValue) error { - if registeredResourceValue == nil { - return fmt.Errorf("registered resource value is nil: %w", ErrInvalidRegisteredResourceValue) - } - if registeredResourceValue.GetValue() == "" { - return fmt.Errorf("registered resource value is empty: %w", ErrInvalidRegisteredResourceValue) + for _, value := range registeredResource.GetValues() { + if value == nil { + return fmt.Errorf("registered resource value is nil: %w", ErrInvalidRegisteredResource) + } + if value.GetValue() == "" { + return fmt.Errorf("registered resource value is empty: %w", ErrInvalidRegisteredResource) + } } return nil } diff --git a/service/internal/access/v2/validators_test.go b/service/internal/access/v2/validators_test.go index 16408bcb95..446d45d65b 100644 --- a/service/internal/access/v2/validators_test.go +++ b/service/internal/access/v2/validators_test.go @@ -283,6 +283,82 @@ func TestValidateAttribute(t *testing.T) { } } +func TestValidateRegisteredResource(t *testing.T) { + tests := []struct { + name string + registeredResource *policy.RegisteredResource + wantErr error + }{ + { + name: "Valid registered resource", + registeredResource: &policy.RegisteredResource{ + Name: "valid-resource", + Values: []*policy.RegisteredResourceValue{ + { + Value: "valid-value", + }, + }, + }, + wantErr: nil, + }, + { + name: "Nil registered resource", + registeredResource: nil, + wantErr: ErrInvalidRegisteredResource, + }, + { + name: "Empty registered resource name", + registeredResource: &policy.RegisteredResource{ + Name: "", + }, + wantErr: ErrInvalidRegisteredResource, + }, + { + name: "Empty registered resource values", + registeredResource: &policy.RegisteredResource{ + Name: "empty-values", + Values: []*policy.RegisteredResourceValue{}, + }, + wantErr: ErrInvalidRegisteredResource, + }, + { + name: "Nil registered resource values", + registeredResource: &policy.RegisteredResource{ + Name: "nil-values", + Values: nil, + }, + wantErr: ErrInvalidRegisteredResource, + }, + { + name: "Nil value in registered resource values", + registeredResource: &policy.RegisteredResource{ + Name: "nil-value-in-values", + Values: []*policy.RegisteredResourceValue{nil}, + }, + wantErr: ErrInvalidRegisteredResource, + }, + { + name: "Empty value in registered resource values", + registeredResource: &policy.RegisteredResource{ + Name: "empty-value-in-values", + Values: []*policy.RegisteredResourceValue{{Value: ""}}, + }, + wantErr: ErrInvalidRegisteredResource, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateRegisteredResource(tt.registeredResource) + if tt.wantErr != nil { + require.ErrorIs(t, err, tt.wantErr) + } else { + require.NoError(t, err) + } + }) + } +} + func TestValidateEntityRepresentations(t *testing.T) { tests := []struct { name string From 4e55edad7416d9dcb0f9f43f0a9de7c35140cc69 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 3 Jun 2025 10:38:04 -0600 Subject: [PATCH 06/55] more validator cleanup --- service/internal/access/v2/helpers.go | 7 +++---- service/internal/access/v2/validators.go | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/service/internal/access/v2/helpers.go b/service/internal/access/v2/helpers.go index 4fde2d01b8..416a887109 100644 --- a/service/internal/access/v2/helpers.go +++ b/service/internal/access/v2/helpers.go @@ -14,10 +14,9 @@ import ( ) var ( - ErrInvalidSubjectMapping = errors.New("access: invalid subject mapping") - ErrInvalidAttributeDefinition = errors.New("access: invalid attribute definition") - ErrInvalidRegisteredResource = errors.New("access: invalid registered resource") - ErrInvalidRegisteredResourceValue = errors.New("access: invalid registered resource value") + ErrInvalidSubjectMapping = errors.New("access: invalid subject mapping") + ErrInvalidAttributeDefinition = errors.New("access: invalid attribute definition") + ErrInvalidRegisteredResource = errors.New("access: invalid registered resource") ) // getDefinition parses the value FQN and uses it to retrieve the definition from the provided definitions canmap diff --git a/service/internal/access/v2/validators.go b/service/internal/access/v2/validators.go index 2a6ffb2726..be1385e0a9 100644 --- a/service/internal/access/v2/validators.go +++ b/service/internal/access/v2/validators.go @@ -102,6 +102,7 @@ func validateAttribute(attribute *policy.Attribute) error { // registered resource: // // - must not be nil +// - must have a non-empty name // - must have non-empty values func validateRegisteredResource(registeredResource *policy.RegisteredResource) error { if registeredResource == nil { From 53fe2dc329db54be5431e6810a5461df839d72af Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 3 Jun 2025 10:45:54 -0600 Subject: [PATCH 07/55] add reg res value fqn validation in GetEntitlements func --- service/internal/access/v2/pdp.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 6b27fa53d9..206daa4f32 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -323,7 +323,9 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( registeredResourceValueFQN string, withComprehensiveHierarchy bool, ) ([]*authz.EntityEntitlements, error) { - // todo: validate the registered resource value FQN? + if _, err := identifier.Parse[*identifier.FullyQualifiedRegisteredResourceValue](registeredResourceValueFQN); err != nil { + return nil, fmt.Errorf("invalid registered resource value FQN: %s", registeredResourceValueFQN) + } registeredResourceValue := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] @@ -355,7 +357,7 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( if withComprehensiveHierarchy { err := populateLowerValuesIfHierarchy(attrValFQN, p.allEntitleableAttributesByValueFQN, actionsList, actionsPerAttributeValueFqn) if err != nil { - return nil, fmt.Errorf("error populating comprehensive lower hierarchy values of valueFQN [%s] for registered resource value: %w", attrValFQN, err) + return nil, fmt.Errorf("error populating comprehensive lower hierarchy values for registered resource value FQN [%s]: %w", attrValFQN, err) } } } From c444216c15bff31b560d4be1dc0c872f573ab4f6 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 3 Jun 2025 10:49:28 -0600 Subject: [PATCH 08/55] simplify error returned --- service/internal/access/v2/pdp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 206daa4f32..e59d0828d5 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -324,7 +324,7 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( withComprehensiveHierarchy bool, ) ([]*authz.EntityEntitlements, error) { if _, err := identifier.Parse[*identifier.FullyQualifiedRegisteredResourceValue](registeredResourceValueFQN); err != nil { - return nil, fmt.Errorf("invalid registered resource value FQN: %s", registeredResourceValueFQN) + return nil, err } registeredResourceValue := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] From 98801ea948db9c96d4fb9aa8962f4c911f4f58a7 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 3 Jun 2025 11:07:00 -0600 Subject: [PATCH 09/55] update tests to include registered resources --- service/internal/access/v2/pdp_test.go | 47 +++++++++++++++++++++----- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/service/internal/access/v2/pdp_test.go b/service/internal/access/v2/pdp_test.go index 6d0a6065e0..e181afe62a 100644 --- a/service/internal/access/v2/pdp_test.go +++ b/service/internal/access/v2/pdp_test.go @@ -116,6 +116,9 @@ type PDPTestSuite struct { adminEntity *entityresolutionV2.EntityRepresentation developerEntity *entityresolutionV2.EntityRepresentation analystEntity *entityresolutionV2.EntityRepresentation + + // Test registered resources + simpleRegisteredResource *policy.RegisteredResource } } @@ -324,6 +327,16 @@ func (s *PDPTestSuite) SetupTest() { "department": "finance", "country": []any{"uk"}, }) + + // Initialize test registered resources + s.fixtures.simpleRegisteredResource = &policy.RegisteredResource{ + Name: "simple-resource", + Values: []*policy.RegisteredResourceValue{ + { + Value: "test-value", + }, + }, + } } // TestPDPSuite runs the test suite @@ -336,16 +349,18 @@ func (s *PDPTestSuite) TestNewPolicyDecisionPoint() { f := s.fixtures tests := []struct { - name string - attributes []*policy.Attribute - subjectMappings []*policy.SubjectMapping - expectError bool + name string + attributes []*policy.Attribute + subjectMappings []*policy.SubjectMapping + registeredResources []*policy.RegisteredResource + expectError bool }{ { - name: "valid initialization", - attributes: []*policy.Attribute{f.classificationAttr, f.departmentAttr, f.countryAttr}, - subjectMappings: []*policy.SubjectMapping{f.secretMapping, f.confidentialMapping, f.rndMapping}, - expectError: false, + name: "valid initialization", + attributes: []*policy.Attribute{f.classificationAttr, f.departmentAttr, f.countryAttr}, + subjectMappings: []*policy.SubjectMapping{f.secretMapping, f.confidentialMapping, f.rndMapping}, + registeredResources: []*policy.RegisteredResource{f.simpleRegisteredResource}, + expectError: false, }, { name: "nil attributes and nil subject mappings", @@ -365,11 +380,18 @@ func (s *PDPTestSuite) TestNewPolicyDecisionPoint() { subjectMappings: nil, expectError: true, }, + { + name: "non-nil attributes and subject mappings but nil registered resources", + attributes: []*policy.Attribute{f.classificationAttr}, + subjectMappings: []*policy.SubjectMapping{f.secretMapping}, + registeredResources: nil, + expectError: false, + }, } for _, tc := range tests { s.Run(tc.name, func() { - pdp, err := NewPolicyDecisionPoint(s.T().Context(), s.logger, tc.attributes, tc.subjectMappings) + pdp, err := NewPolicyDecisionPoint(s.T().Context(), s.logger, tc.attributes, tc.subjectMappings, tc.registeredResources) if tc.expectError { s.Require().Error(err) @@ -392,6 +414,7 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { s.logger, []*policy.Attribute{f.classificationAttr, f.departmentAttr}, []*policy.SubjectMapping{f.secretMapping, f.topSecretMapping, f.confidentialMapping, f.engineeringMapping, f.financeMapping}, + []*policy.RegisteredResource{}, ) s.Require().NoError(err) s.Require().NotNil(pdp) @@ -587,6 +610,7 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { f.secretMapping, f.topSecretMapping, printConfidentialMapping, allActionsPublicMapping, f.engineeringMapping, f.financeMapping, viewProjectAlphaMapping, }, + []*policy.RegisteredResource{}, ) s.Require().NoError(err) s.Require().NotNil(pdp) @@ -698,6 +722,7 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { s.logger, []*policy.Attribute{f.classificationAttr}, []*policy.SubjectMapping{allActionsPublicMapping, restrictedMapping}, + []*policy.RegisteredResource{}, ) s.Require().NoError(err) s.Require().NotNil(classificationPDP) @@ -744,6 +769,7 @@ func (s *PDPTestSuite) Test_GetDecision_CombinedAttributeRules_SingleResource() f.engineeringMapping, f.financeMapping, f.rndMapping, f.usaMapping, f.ukMapping, f.projectAlphaMapping, f.platformCloudMapping, }, + []*policy.RegisteredResource{}, ) s.Require().NoError(err) s.Require().NotNil(pdp) @@ -1103,6 +1129,7 @@ func (s *PDPTestSuite) Test_GetDecision_AcrossNamespaces() { f.platformCloudMapping, onPremMapping, hybridMapping, f.usaMapping, }, + []*policy.RegisteredResource{}, ) s.Require().NoError(err) s.Require().NotNil(pdp) @@ -1397,6 +1424,7 @@ func (s *PDPTestSuite) Test_GetEntitlements() { f.engineeringMapping, f.financeMapping, f.rndMapping, f.usaMapping, }, + []*policy.RegisteredResource{}, ) s.Require().NoError(err) s.Require().NotNil(pdp) @@ -1695,6 +1723,7 @@ func (s *PDPTestSuite) Test_GetEntitlements_AdvancedHierarchy() { lowerMiddleMapping, bottomMapping, }, + []*policy.RegisteredResource{}, ) s.Require().NoError(err) s.Require().NotNil(pdp) From 17b4538cc87b76fd3b03214d6fa524f2833117f1 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 3 Jun 2025 16:07:41 -0600 Subject: [PATCH 10/55] initial test cases for reg res GetEntitlements func --- service/internal/access/v2/pdp.go | 5 +- service/internal/access/v2/pdp_test.go | 180 +++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 1 deletion(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index e59d0828d5..45b870c5b2 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -327,7 +327,10 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( return nil, err } - registeredResourceValue := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] + registeredResourceValue, ok := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] + if !ok { + return nil, fmt.Errorf("registered resource value not found for FQN [%s]", registeredResourceValueFQN) + } l := p.logger.With("withComprehensiveHierarchy", strconv.FormatBool(withComprehensiveHierarchy)) l.DebugContext(ctx, "getting entitlements for registered resource value", slog.String("fqn", registeredResourceValueFQN)) diff --git a/service/internal/access/v2/pdp_test.go b/service/internal/access/v2/pdp_test.go index e181afe62a..5e1cb81fca 100644 --- a/service/internal/access/v2/pdp_test.go +++ b/service/internal/access/v2/pdp_test.go @@ -41,6 +41,15 @@ func createAttrValueFQN(namespace, name, value string) string { return attr.FQN() } +// Helper function to create registered resource value FQNs +func createRegisteredResourceValueFQN(name, value string) string { + resourceValue := &identifier.FullyQualifiedRegisteredResourceValue{ + Name: name, + Value: value, + } + return resourceValue.FQN() +} + // Attribute FQNs using identifier package var ( // Base attribute FQNs @@ -1793,6 +1802,177 @@ func (s *PDPTestSuite) Test_GetEntitlements_AdvancedHierarchy() { s.Contains(bottomActionNames, customActionGather, "Bottom level should have gather action") } +func (s *PDPTestSuite) Test_GetEntitlementsRegisteredResource() { + resourceValueNoEntitlements := &policy.RegisteredResourceValue{ + Value: "no-entitlements", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{}, + } + resourceValueSingleEntitlement := &policy.RegisteredResourceValue{ + Value: "single", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testClassSecretFQN, + Value: "secret", + }, + }, + }, + } + resourceValueMultiEntitlements := &policy.RegisteredResourceValue{ + Value: "multi", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testPlatformCloudFQN, + Value: "cloud", + }, + }, + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testPlatformCloudFQN, + Value: "cloud", + }, + }, + }, + } + resourceValueDuplicateEntitlements := &policy.RegisteredResourceValue{ + Value: "duplicate", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testClassSecretFQN, + Value: "secret", + }, + }, + { + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testClassSecretFQN, + Value: "secret", + }, + }, + }, + } + + resource := &policy.RegisteredResource{ + Name: "test-res", + Values: []*policy.RegisteredResourceValue{ + resourceValueNoEntitlements, + resourceValueSingleEntitlement, + resourceValueMultiEntitlements, + resourceValueDuplicateEntitlements, + }, + } + + resourceValueNoEntitlementsFQN := createRegisteredResourceValueFQN(resource.GetName(), resourceValueNoEntitlements.GetValue()) + resourceValueSingleEntitlementFQN := createRegisteredResourceValueFQN(resource.GetName(), resourceValueSingleEntitlement.GetValue()) + resourceValueMultiEntitlementsFQN := createRegisteredResourceValueFQN(resource.GetName(), resourceValueMultiEntitlements.GetValue()) + resourceValueDuplicateEntitlementsFQN := createRegisteredResourceValueFQN(resource.GetName(), resourceValueDuplicateEntitlements.GetValue()) + + pdp, err := NewPolicyDecisionPoint( + s.T().Context(), + s.logger, + []*policy.Attribute{}, + []*policy.SubjectMapping{}, + []*policy.RegisteredResource{resource}, + ) + s.Require().NoError(err) + s.Require().NotNil(pdp) + + s.Run("Invalid registered resource value FQN format", func() { + entitlements, err := pdp.GetEntitlementsRegisteredResource( + s.T().Context(), + "invalid_fqn_format", + false, + ) + + s.Require().Error(err) + s.Require().ErrorIs(err, identifier.ErrInvalidFQNFormat) + s.Require().Nil(entitlements) + }) + + s.Run("Valid but non-existent registered resource value FQN", func() { + validButNonexistentFQN := createRegisteredResourceValueFQN("test-res-not-exist", "test-value-not-exist") + entitlements, err := pdp.GetEntitlementsRegisteredResource( + s.T().Context(), + validButNonexistentFQN, + false, + ) + + s.Require().EqualError(err, fmt.Sprintf("registered resource value not found for FQN [%s]", validButNonexistentFQN)) + s.Require().Nil(entitlements) + }) + + s.Run("no entitlements", func() { + entitlements, err := pdp.GetEntitlementsRegisteredResource( + s.T().Context(), + resourceValueNoEntitlementsFQN, + false, + ) + s.Require().NoError(err) + s.Require().NotNil(entitlements) + s.Require().Len(entitlements, 1) + entityEntitlement := entitlements[0] + s.Equal(resourceValueNoEntitlementsFQN, entityEntitlement.GetEphemeralId()) + s.Require().Len(entityEntitlement.GetActionsPerAttributeValueFqn(), 0) + }) + + s.Run("single entitlement", func() { + entitlements, err := pdp.GetEntitlementsRegisteredResource( + s.T().Context(), + resourceValueSingleEntitlementFQN, + false, + ) + s.Require().NoError(err) + s.Require().NotNil(entitlements) + + s.Require().Len(entitlements, 1) + entityEntitlement := entitlements[0] + s.Equal(resourceValueSingleEntitlementFQN, entityEntitlement.GetEphemeralId()) + actionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testClassSecretFQN] + s.ElementsMatch(actionNames(actionsList.GetActions()), []string{actions.ActionNameCreate}) + }) + + s.Run("multiple entitlements", func() { + entitlements, err := pdp.GetEntitlementsRegisteredResource( + s.T().Context(), + resourceValueMultiEntitlementsFQN, + false, + ) + s.Require().NoError(err) + s.Require().NotNil(entitlements) + + s.Require().Len(entitlements, 1) + entityEntitlement := entitlements[0] + s.Equal(resourceValueMultiEntitlementsFQN, entityEntitlement.GetEphemeralId()) + actionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testPlatformCloudFQN] + s.ElementsMatch(actionNames(actionsList.GetActions()), []string{actions.ActionNameCreate, actions.ActionNameRead}) + }) + + // todo: multiple entitlements for multiple attribute values + + s.Run("duplicate entitlements", func() { + entitlements, err := pdp.GetEntitlementsRegisteredResource( + s.T().Context(), + resourceValueDuplicateEntitlementsFQN, + false, + ) + s.Require().NoError(err) + s.Require().NotNil(entitlements) + s.Require().Len(entitlements, 1) + entityEntitlement := entitlements[0] + s.Equal(resourceValueDuplicateEntitlementsFQN, entityEntitlement.GetEphemeralId()) + actionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testClassSecretFQN] + s.ElementsMatch(actionNames(actionsList.GetActions()), []string{actions.ActionNameCreate}) + }) + + // todo: comprehensive hierarchy +} + // Helper functions for all tests // assertDecisionResult is a helper function to assert that a decision result for a given FQN matches the expected pass/fail state From 787e114d427635d393ae460d2e668989c6cf91f8 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 3 Jun 2025 16:11:29 -0600 Subject: [PATCH 11/55] fix lint finding --- service/internal/access/v2/pdp_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/internal/access/v2/pdp_test.go b/service/internal/access/v2/pdp_test.go index 5e1cb81fca..256f334fbd 100644 --- a/service/internal/access/v2/pdp_test.go +++ b/service/internal/access/v2/pdp_test.go @@ -1918,7 +1918,7 @@ func (s *PDPTestSuite) Test_GetEntitlementsRegisteredResource() { s.Require().Len(entitlements, 1) entityEntitlement := entitlements[0] s.Equal(resourceValueNoEntitlementsFQN, entityEntitlement.GetEphemeralId()) - s.Require().Len(entityEntitlement.GetActionsPerAttributeValueFqn(), 0) + s.Require().Empty(entityEntitlement.GetActionsPerAttributeValueFqn()) }) s.Run("single entitlement", func() { From 488f3d7b77aab8a715e1efad91da6ee455989699 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Wed, 4 Jun 2025 08:44:58 -0600 Subject: [PATCH 12/55] fix lint --- service/internal/access/v2/pdp.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 45b870c5b2..f21b6dd6e7 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -327,8 +327,8 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( return nil, err } - registeredResourceValue, ok := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] - if !ok { + registeredResourceValue, found := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] + if !found { return nil, fmt.Errorf("registered resource value not found for FQN [%s]", registeredResourceValueFQN) } From 53f29e1ecec9612b8437f70d408926d25edd086d Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Wed, 4 Jun 2025 10:52:18 -0600 Subject: [PATCH 13/55] test refactor --- service/internal/access/v2/pdp_test.go | 295 +++++++++++++++++-------- 1 file changed, 200 insertions(+), 95 deletions(-) diff --git a/service/internal/access/v2/pdp_test.go b/service/internal/access/v2/pdp_test.go index 256f334fbd..88190af0c1 100644 --- a/service/internal/access/v2/pdp_test.go +++ b/service/internal/access/v2/pdp_test.go @@ -88,6 +88,16 @@ var ( testPlatformHybridFQN = createAttrValueFQN(testSecondaryNamespace, "platform", "hybrid") ) +// Registered resource value FQNs using identifier package +var ( + regResValNoActionAttrValFQN string + regResValSingleActionAttrValFQN string + regResValDuplicateActionAttrValFQN string + regResValMultiActionSingleAttrValFQN string + regResValMultiActionMultiAttrValFQN string + regResValComprehensiveHierarchyActionAttrValFQN string +) + // Standard action definitions used across tests var ( testActionRead = &policy.Action{Name: actions.ActionNameRead} @@ -127,7 +137,13 @@ type PDPTestSuite struct { analystEntity *entityresolutionV2.EntityRepresentation // Test registered resources - simpleRegisteredResource *policy.RegisteredResource + regRes *policy.RegisteredResource + regResValNoActionAttrVal *policy.RegisteredResourceValue + regResValSingleActionAttrVal *policy.RegisteredResourceValue + regResValDuplicateActionAttrVal *policy.RegisteredResourceValue + regResValMultiActionSingleAttrVal *policy.RegisteredResourceValue + regResValMultiActionMultiAttrVal *policy.RegisteredResourceValue + regResValComprehensiveHierarchyActionAttrVal *policy.RegisteredResourceValue } } @@ -338,14 +354,125 @@ func (s *PDPTestSuite) SetupTest() { }) // Initialize test registered resources - s.fixtures.simpleRegisteredResource = &policy.RegisteredResource{ - Name: "simple-resource", - Values: []*policy.RegisteredResourceValue{ + regResValNoActionAttrVal := &policy.RegisteredResourceValue{ + Value: "no-action-attr-val", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{}, + } + regResValSingleActionAttrVal := &policy.RegisteredResourceValue{ + Value: "single-action-attr-val", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ { - Value: "test-value", + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testClassSecretFQN, + Value: "secret", + }, }, }, } + regResValDuplicateActionAttrVal := &policy.RegisteredResourceValue{ + Value: "duplicate-action-attr-val", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testClassSecretFQN, + Value: "secret", + }, + }, + { + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testClassSecretFQN, + Value: "secret", + }, + }, + }, + } + regResValMultiActionSingleAttrVal := &policy.RegisteredResourceValue{ + Value: "multi-action-single-attr-val", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testPlatformCloudFQN, + Value: "cloud", + }, + }, + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testPlatformCloudFQN, + Value: "cloud", + }, + }, + }, + } + regResValMultiActionMultiAttrVal := &policy.RegisteredResourceValue{ + Value: "multi-action-multi-attr-val", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testClassSecretFQN, + Value: "secret", + }, + }, + { + Action: testActionUpdate, + AttributeValue: &policy.Value{ + Fqn: testPlatformCloudFQN, + Value: "cloud", + }, + }, + { + Action: testActionDelete, + AttributeValue: &policy.Value{ + Fqn: testPlatformCloudFQN, + Value: "cloud", + }, + }, + }, + } + regResValComprehensiveHierarchyActionAttrVal := &policy.RegisteredResourceValue{ + Value: "comprehensive-hierarchy-action-attr-val", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassSecretFQN, + Value: "secret", + }, + }, + }, + } + + regRes := &policy.RegisteredResource{ + Name: "test-res", + Values: []*policy.RegisteredResourceValue{ + regResValNoActionAttrVal, + regResValSingleActionAttrVal, + regResValDuplicateActionAttrVal, + regResValMultiActionSingleAttrVal, + regResValMultiActionMultiAttrVal, + regResValComprehensiveHierarchyActionAttrVal, + }, + } + + s.fixtures.regRes = regRes + s.fixtures.regResValNoActionAttrVal = regResValNoActionAttrVal + s.fixtures.regResValSingleActionAttrVal = regResValSingleActionAttrVal + s.fixtures.regResValDuplicateActionAttrVal = regResValDuplicateActionAttrVal + s.fixtures.regResValMultiActionSingleAttrVal = regResValMultiActionSingleAttrVal + s.fixtures.regResValMultiActionMultiAttrVal = regResValMultiActionMultiAttrVal + s.fixtures.regResValComprehensiveHierarchyActionAttrVal = regResValComprehensiveHierarchyActionAttrVal + + regResValNoActionAttrValFQN = createRegisteredResourceValueFQN(regRes.GetName(), regResValNoActionAttrVal.GetValue()) + regResValSingleActionAttrValFQN = createRegisteredResourceValueFQN(regRes.GetName(), regResValSingleActionAttrVal.GetValue()) + regResValDuplicateActionAttrValFQN = createRegisteredResourceValueFQN(regRes.GetName(), regResValDuplicateActionAttrVal.GetValue()) + regResValMultiActionSingleAttrValFQN = createRegisteredResourceValueFQN(regRes.GetName(), regResValMultiActionSingleAttrVal.GetValue()) + regResValMultiActionMultiAttrValFQN = createRegisteredResourceValueFQN(regRes.GetName(), regResValMultiActionMultiAttrVal.GetValue()) + regResValComprehensiveHierarchyActionAttrValFQN = createRegisteredResourceValueFQN(regRes.GetName(), regResValComprehensiveHierarchyActionAttrVal.GetValue()) } // TestPDPSuite runs the test suite @@ -368,7 +495,7 @@ func (s *PDPTestSuite) TestNewPolicyDecisionPoint() { name: "valid initialization", attributes: []*policy.Attribute{f.classificationAttr, f.departmentAttr, f.countryAttr}, subjectMappings: []*policy.SubjectMapping{f.secretMapping, f.confidentialMapping, f.rndMapping}, - registeredResources: []*policy.RegisteredResource{f.simpleRegisteredResource}, + registeredResources: []*policy.RegisteredResource{f.regRes}, expectError: false, }, { @@ -1803,82 +1930,14 @@ func (s *PDPTestSuite) Test_GetEntitlements_AdvancedHierarchy() { } func (s *PDPTestSuite) Test_GetEntitlementsRegisteredResource() { - resourceValueNoEntitlements := &policy.RegisteredResourceValue{ - Value: "no-entitlements", - ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{}, - } - resourceValueSingleEntitlement := &policy.RegisteredResourceValue{ - Value: "single", - ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ - { - Action: testActionCreate, - AttributeValue: &policy.Value{ - Fqn: testClassSecretFQN, - Value: "secret", - }, - }, - }, - } - resourceValueMultiEntitlements := &policy.RegisteredResourceValue{ - Value: "multi", - ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ - { - Action: testActionCreate, - AttributeValue: &policy.Value{ - Fqn: testPlatformCloudFQN, - Value: "cloud", - }, - }, - { - Action: testActionRead, - AttributeValue: &policy.Value{ - Fqn: testPlatformCloudFQN, - Value: "cloud", - }, - }, - }, - } - resourceValueDuplicateEntitlements := &policy.RegisteredResourceValue{ - Value: "duplicate", - ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ - { - Action: testActionCreate, - AttributeValue: &policy.Value{ - Fqn: testClassSecretFQN, - Value: "secret", - }, - }, - { - Action: testActionCreate, - AttributeValue: &policy.Value{ - Fqn: testClassSecretFQN, - Value: "secret", - }, - }, - }, - } - - resource := &policy.RegisteredResource{ - Name: "test-res", - Values: []*policy.RegisteredResourceValue{ - resourceValueNoEntitlements, - resourceValueSingleEntitlement, - resourceValueMultiEntitlements, - resourceValueDuplicateEntitlements, - }, - } - - resourceValueNoEntitlementsFQN := createRegisteredResourceValueFQN(resource.GetName(), resourceValueNoEntitlements.GetValue()) - resourceValueSingleEntitlementFQN := createRegisteredResourceValueFQN(resource.GetName(), resourceValueSingleEntitlement.GetValue()) - resourceValueMultiEntitlementsFQN := createRegisteredResourceValueFQN(resource.GetName(), resourceValueMultiEntitlements.GetValue()) - resourceValueDuplicateEntitlementsFQN := createRegisteredResourceValueFQN(resource.GetName(), resourceValueDuplicateEntitlements.GetValue()) + f := s.fixtures pdp, err := NewPolicyDecisionPoint( s.T().Context(), s.logger, - []*policy.Attribute{}, + []*policy.Attribute{f.classificationAttr}, []*policy.SubjectMapping{}, - []*policy.RegisteredResource{resource}, + []*policy.RegisteredResource{f.regRes}, ) s.Require().NoError(err) s.Require().NotNil(pdp) @@ -1907,70 +1966,116 @@ func (s *PDPTestSuite) Test_GetEntitlementsRegisteredResource() { s.Require().Nil(entitlements) }) - s.Run("no entitlements", func() { + s.Run("no action attribute values", func() { entitlements, err := pdp.GetEntitlementsRegisteredResource( s.T().Context(), - resourceValueNoEntitlementsFQN, + regResValNoActionAttrValFQN, false, ) + s.Require().NoError(err) s.Require().NotNil(entitlements) s.Require().Len(entitlements, 1) entityEntitlement := entitlements[0] - s.Equal(resourceValueNoEntitlementsFQN, entityEntitlement.GetEphemeralId()) + s.Equal(regResValNoActionAttrValFQN, entityEntitlement.GetEphemeralId()) s.Require().Empty(entityEntitlement.GetActionsPerAttributeValueFqn()) }) - s.Run("single entitlement", func() { + s.Run("single action attribute value", func() { entitlements, err := pdp.GetEntitlementsRegisteredResource( s.T().Context(), - resourceValueSingleEntitlementFQN, + regResValSingleActionAttrValFQN, false, ) + s.Require().NoError(err) s.Require().NotNil(entitlements) - s.Require().Len(entitlements, 1) entityEntitlement := entitlements[0] - s.Equal(resourceValueSingleEntitlementFQN, entityEntitlement.GetEphemeralId()) + s.Equal(regResValSingleActionAttrValFQN, entityEntitlement.GetEphemeralId()) actionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testClassSecretFQN] s.ElementsMatch(actionNames(actionsList.GetActions()), []string{actions.ActionNameCreate}) }) - s.Run("multiple entitlements", func() { + s.Run("duplicate action attribute values", func() { entitlements, err := pdp.GetEntitlementsRegisteredResource( s.T().Context(), - resourceValueMultiEntitlementsFQN, + regResValDuplicateActionAttrValFQN, false, ) s.Require().NoError(err) s.Require().NotNil(entitlements) + s.Require().Len(entitlements, 1) + entityEntitlement := entitlements[0] + s.Equal(regResValDuplicateActionAttrValFQN, entityEntitlement.GetEphemeralId()) + actionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testClassSecretFQN] + s.ElementsMatch(actionNames(actionsList.GetActions()), []string{actions.ActionNameCreate}) + }) + + s.Run("multiple actions single attribute value", func() { + entitlements, err := pdp.GetEntitlementsRegisteredResource( + s.T().Context(), + regResValMultiActionSingleAttrValFQN, + false, + ) + s.Require().NoError(err) + s.Require().NotNil(entitlements) s.Require().Len(entitlements, 1) entityEntitlement := entitlements[0] - s.Equal(resourceValueMultiEntitlementsFQN, entityEntitlement.GetEphemeralId()) + s.Equal(regResValMultiActionSingleAttrValFQN, entityEntitlement.GetEphemeralId()) actionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testPlatformCloudFQN] s.ElementsMatch(actionNames(actionsList.GetActions()), []string{actions.ActionNameCreate, actions.ActionNameRead}) }) - // todo: multiple entitlements for multiple attribute values - - s.Run("duplicate entitlements", func() { + s.Run("multiple actions multiple attribute values", func() { entitlements, err := pdp.GetEntitlementsRegisteredResource( s.T().Context(), - resourceValueDuplicateEntitlementsFQN, + regResValMultiActionMultiAttrValFQN, false, ) + s.Require().NoError(err) s.Require().NotNil(entitlements) s.Require().Len(entitlements, 1) entityEntitlement := entitlements[0] - s.Equal(resourceValueDuplicateEntitlementsFQN, entityEntitlement.GetEphemeralId()) - actionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testClassSecretFQN] - s.ElementsMatch(actionNames(actionsList.GetActions()), []string{actions.ActionNameCreate}) + s.Equal(regResValMultiActionMultiAttrValFQN, entityEntitlement.GetEphemeralId()) + secretActionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testClassSecretFQN] + s.ElementsMatch(actionNames(secretActionsList.GetActions()), []string{actions.ActionNameCreate}) + cloudActionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testPlatformCloudFQN] + s.ElementsMatch(actionNames(cloudActionsList.GetActions()), []string{actions.ActionNameUpdate, actions.ActionNameDelete}) }) - // todo: comprehensive hierarchy + s.Run(" comprehensive hierarchy action attribute value", func() { + entitlements, err := pdp.GetEntitlementsRegisteredResource( + s.T().Context(), + regResValComprehensiveHierarchyActionAttrValFQN, + true, // With comprehensive hierarchy + ) + + s.Require().NoError(err) + s.Require().NotNil(entitlements) + s.Require().Len(entitlements, 1) + entityEntitlement := entitlements[0] + s.Equal(regResValComprehensiveHierarchyActionAttrValFQN, entityEntitlement.GetEphemeralId()) + + actionsPerAttributeValueFQN := entityEntitlement.GetActionsPerAttributeValueFqn() + + // secret should give access to all lower classifications (secret > confidential > public)) + s.Contains(actionsPerAttributeValueFQN, testClassSecretFQN) + s.Contains(actionsPerAttributeValueFQN, testClassConfidentialFQN) + s.Contains(actionsPerAttributeValueFQN, testClassPublicFQN) + + // but not higher classifications + s.NotContains(actionsPerAttributeValueFQN, testClassTopSecretFQN) + + // all actions for secret, confidential, and public should be the same + secretActions := actionsPerAttributeValueFQN[testClassSecretFQN] + confidentialActions := actionsPerAttributeValueFQN[testClassConfidentialFQN] + publicActions := actionsPerAttributeValueFQN[testClassPublicFQN] + s.ElementsMatch(actionNames(secretActions.GetActions()), actionNames(confidentialActions.GetActions())) + s.ElementsMatch(actionNames(secretActions.GetActions()), actionNames(publicActions.GetActions())) + }) } // Helper functions for all tests From 42a8422ff809776cba2f5e2e50f93444178f90a9 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Thu, 5 Jun 2025 09:13:35 -0600 Subject: [PATCH 14/55] pr suggestions --- service/internal/access/v2/pdp_test.go | 29 ++++++++++++++++++-------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/service/internal/access/v2/pdp_test.go b/service/internal/access/v2/pdp_test.go index 88190af0c1..979deb7e82 100644 --- a/service/internal/access/v2/pdp_test.go +++ b/service/internal/access/v2/pdp_test.go @@ -1993,7 +1993,9 @@ func (s *PDPTestSuite) Test_GetEntitlementsRegisteredResource() { s.Require().Len(entitlements, 1) entityEntitlement := entitlements[0] s.Equal(regResValSingleActionAttrValFQN, entityEntitlement.GetEphemeralId()) - actionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testClassSecretFQN] + actionsPerAttrValueFQN := entityEntitlement.GetActionsPerAttributeValueFqn() + s.Require().Len(actionsPerAttrValueFQN, 1) + actionsList := actionsPerAttrValueFQN[testClassSecretFQN] s.ElementsMatch(actionNames(actionsList.GetActions()), []string{actions.ActionNameCreate}) }) @@ -2008,7 +2010,9 @@ func (s *PDPTestSuite) Test_GetEntitlementsRegisteredResource() { s.Require().Len(entitlements, 1) entityEntitlement := entitlements[0] s.Equal(regResValDuplicateActionAttrValFQN, entityEntitlement.GetEphemeralId()) - actionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testClassSecretFQN] + actionsPerAttrValueFQN := entityEntitlement.GetActionsPerAttributeValueFqn() + s.Require().Len(actionsPerAttrValueFQN, 1) + actionsList := actionsPerAttrValueFQN[testClassSecretFQN] s.ElementsMatch(actionNames(actionsList.GetActions()), []string{actions.ActionNameCreate}) }) @@ -2024,7 +2028,9 @@ func (s *PDPTestSuite) Test_GetEntitlementsRegisteredResource() { s.Require().Len(entitlements, 1) entityEntitlement := entitlements[0] s.Equal(regResValMultiActionSingleAttrValFQN, entityEntitlement.GetEphemeralId()) - actionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testPlatformCloudFQN] + actionsPerAttrValueFQN := entityEntitlement.GetActionsPerAttributeValueFqn() + s.Require().Len(actionsPerAttrValueFQN, 1) + actionsList := actionsPerAttrValueFQN[testPlatformCloudFQN] s.ElementsMatch(actionNames(actionsList.GetActions()), []string{actions.ActionNameCreate, actions.ActionNameRead}) }) @@ -2040,13 +2046,15 @@ func (s *PDPTestSuite) Test_GetEntitlementsRegisteredResource() { s.Require().Len(entitlements, 1) entityEntitlement := entitlements[0] s.Equal(regResValMultiActionMultiAttrValFQN, entityEntitlement.GetEphemeralId()) - secretActionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testClassSecretFQN] + actionsPerAttrValueFQN := entityEntitlement.GetActionsPerAttributeValueFqn() + s.Require().Len(actionsPerAttrValueFQN, 2) + secretActionsList := actionsPerAttrValueFQN[testClassSecretFQN] s.ElementsMatch(actionNames(secretActionsList.GetActions()), []string{actions.ActionNameCreate}) - cloudActionsList := entityEntitlement.GetActionsPerAttributeValueFqn()[testPlatformCloudFQN] + cloudActionsList := actionsPerAttrValueFQN[testPlatformCloudFQN] s.ElementsMatch(actionNames(cloudActionsList.GetActions()), []string{actions.ActionNameUpdate, actions.ActionNameDelete}) }) - s.Run(" comprehensive hierarchy action attribute value", func() { + s.Run("comprehensive hierarchy action attribute value", func() { entitlements, err := pdp.GetEntitlementsRegisteredResource( s.T().Context(), regResValComprehensiveHierarchyActionAttrValFQN, @@ -2061,7 +2069,8 @@ func (s *PDPTestSuite) Test_GetEntitlementsRegisteredResource() { actionsPerAttributeValueFQN := entityEntitlement.GetActionsPerAttributeValueFqn() - // secret should give access to all lower classifications (secret > confidential > public)) + // secret should give access to all lower classifications (secret > confidential > public) + s.Require().Len(actionsPerAttributeValueFQN, 3) s.Contains(actionsPerAttributeValueFQN, testClassSecretFQN) s.Contains(actionsPerAttributeValueFQN, testClassConfidentialFQN) s.Contains(actionsPerAttributeValueFQN, testClassPublicFQN) @@ -2073,8 +2082,10 @@ func (s *PDPTestSuite) Test_GetEntitlementsRegisteredResource() { secretActions := actionsPerAttributeValueFQN[testClassSecretFQN] confidentialActions := actionsPerAttributeValueFQN[testClassConfidentialFQN] publicActions := actionsPerAttributeValueFQN[testClassPublicFQN] - s.ElementsMatch(actionNames(secretActions.GetActions()), actionNames(confidentialActions.GetActions())) - s.ElementsMatch(actionNames(secretActions.GetActions()), actionNames(publicActions.GetActions())) + expectedActionNames := []string{actions.ActionNameRead} + s.ElementsMatch(actionNames(secretActions.GetActions()), expectedActionNames) + s.ElementsMatch(actionNames(confidentialActions.GetActions()), expectedActionNames) + s.ElementsMatch(actionNames(publicActions.GetActions()), expectedActionNames) }) } From f18a23d2fc91e799a32c7d795702803a8999199d Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Thu, 5 Jun 2025 10:36:43 -0600 Subject: [PATCH 15/55] split up resource and resource value validation to support resources without values and update tests --- service/internal/access/v2/helpers.go | 7 ++- service/internal/access/v2/pdp.go | 12 ++-- service/internal/access/v2/pdp_test.go | 3 +- service/internal/access/v2/validators.go | 24 ++++--- service/internal/access/v2/validators_test.go | 62 ++++++++++--------- 5 files changed, 58 insertions(+), 50 deletions(-) diff --git a/service/internal/access/v2/helpers.go b/service/internal/access/v2/helpers.go index 416a887109..4fde2d01b8 100644 --- a/service/internal/access/v2/helpers.go +++ b/service/internal/access/v2/helpers.go @@ -14,9 +14,10 @@ import ( ) var ( - ErrInvalidSubjectMapping = errors.New("access: invalid subject mapping") - ErrInvalidAttributeDefinition = errors.New("access: invalid attribute definition") - ErrInvalidRegisteredResource = errors.New("access: invalid registered resource") + ErrInvalidSubjectMapping = errors.New("access: invalid subject mapping") + ErrInvalidAttributeDefinition = errors.New("access: invalid attribute definition") + ErrInvalidRegisteredResource = errors.New("access: invalid registered resource") + ErrInvalidRegisteredResourceValue = errors.New("access: invalid registered resource value") ) // getDefinition parses the value FQN and uses it to retrieve the definition from the provided definitions canmap diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index f21b6dd6e7..581fe34475 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -323,18 +323,18 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( registeredResourceValueFQN string, withComprehensiveHierarchy bool, ) ([]*authz.EntityEntitlements, error) { + l := p.logger.With("withComprehensiveHierarchy", strconv.FormatBool(withComprehensiveHierarchy)) + l.DebugContext(ctx, "getting entitlements for registered resource value", slog.String("fqn", registeredResourceValueFQN)) + if _, err := identifier.Parse[*identifier.FullyQualifiedRegisteredResourceValue](registeredResourceValueFQN); err != nil { return nil, err } - registeredResourceValue, found := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] - if !found { - return nil, fmt.Errorf("registered resource value not found for FQN [%s]", registeredResourceValueFQN) + registeredResourceValue := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] + if err := validateRegisteredResourceValue(registeredResourceValue); err != nil { + return nil, err } - l := p.logger.With("withComprehensiveHierarchy", strconv.FormatBool(withComprehensiveHierarchy)) - l.DebugContext(ctx, "getting entitlements for registered resource value", slog.String("fqn", registeredResourceValueFQN)) - actionsPerAttributeValueFqn := make(map[string]*authz.EntityEntitlements_ActionsList) for _, aav := range registeredResourceValue.GetActionAttributeValues() { diff --git a/service/internal/access/v2/pdp_test.go b/service/internal/access/v2/pdp_test.go index 979deb7e82..e256cda832 100644 --- a/service/internal/access/v2/pdp_test.go +++ b/service/internal/access/v2/pdp_test.go @@ -1962,7 +1962,8 @@ func (s *PDPTestSuite) Test_GetEntitlementsRegisteredResource() { false, ) - s.Require().EqualError(err, fmt.Sprintf("registered resource value not found for FQN [%s]", validButNonexistentFQN)) + s.Require().Error(err) + s.Require().ErrorIs(err, ErrInvalidRegisteredResourceValue) s.Require().Nil(entitlements) }) diff --git a/service/internal/access/v2/validators.go b/service/internal/access/v2/validators.go index be1385e0a9..444acf1338 100644 --- a/service/internal/access/v2/validators.go +++ b/service/internal/access/v2/validators.go @@ -103,7 +103,6 @@ func validateAttribute(attribute *policy.Attribute) error { // // - must not be nil // - must have a non-empty name -// - must have non-empty values func validateRegisteredResource(registeredResource *policy.RegisteredResource) error { if registeredResource == nil { return fmt.Errorf("registered resource is nil: %w", ErrInvalidRegisteredResource) @@ -111,16 +110,21 @@ func validateRegisteredResource(registeredResource *policy.RegisteredResource) e if registeredResource.GetName() == "" { return fmt.Errorf("registered resource name is empty: %w", ErrInvalidRegisteredResource) } - if len(registeredResource.GetValues()) == 0 { - return fmt.Errorf("registered resource values are empty: %w", ErrInvalidRegisteredResource) + return nil +} + +// validateRegisteredResourceValue validates the registered resource value is valid for an entitlement decision +// +// registered resource value: +// +// - must not be nil +// - must have a non-empty name +func validateRegisteredResourceValue(registeredResourceValue *policy.RegisteredResourceValue) error { + if registeredResourceValue == nil { + return fmt.Errorf("registered resource value is nil: %w", ErrInvalidRegisteredResourceValue) } - for _, value := range registeredResource.GetValues() { - if value == nil { - return fmt.Errorf("registered resource value is nil: %w", ErrInvalidRegisteredResource) - } - if value.GetValue() == "" { - return fmt.Errorf("registered resource value is empty: %w", ErrInvalidRegisteredResource) - } + if registeredResourceValue.GetValue() == "" { + return fmt.Errorf("registered resource value is empty: %w", ErrInvalidRegisteredResourceValue) } return nil } diff --git a/service/internal/access/v2/validators_test.go b/service/internal/access/v2/validators_test.go index 446d45d65b..0d9f57d226 100644 --- a/service/internal/access/v2/validators_test.go +++ b/service/internal/access/v2/validators_test.go @@ -293,11 +293,6 @@ func TestValidateRegisteredResource(t *testing.T) { name: "Valid registered resource", registeredResource: &policy.RegisteredResource{ Name: "valid-resource", - Values: []*policy.RegisteredResourceValue{ - { - Value: "valid-value", - }, - }, }, wantErr: nil, }, @@ -313,43 +308,50 @@ func TestValidateRegisteredResource(t *testing.T) { }, wantErr: ErrInvalidRegisteredResource, }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateRegisteredResource(tt.registeredResource) + if tt.wantErr != nil { + require.ErrorIs(t, err, tt.wantErr) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestValidateRegisteredResourceValue(t *testing.T) { + tests := []struct { + name string + registeredResourceValue *policy.RegisteredResourceValue + wantErr error + }{ { - name: "Empty registered resource values", - registeredResource: &policy.RegisteredResource{ - Name: "empty-values", - Values: []*policy.RegisteredResourceValue{}, - }, - wantErr: ErrInvalidRegisteredResource, - }, - { - name: "Nil registered resource values", - registeredResource: &policy.RegisteredResource{ - Name: "nil-values", - Values: nil, + name: "Valid registered resource value", + registeredResourceValue: &policy.RegisteredResourceValue{ + Value: "valid-value", }, - wantErr: ErrInvalidRegisteredResource, + wantErr: nil, }, { - name: "Nil value in registered resource values", - registeredResource: &policy.RegisteredResource{ - Name: "nil-value-in-values", - Values: []*policy.RegisteredResourceValue{nil}, - }, - wantErr: ErrInvalidRegisteredResource, + name: "Nil registered resource value", + registeredResourceValue: nil, + wantErr: ErrInvalidRegisteredResourceValue, }, { - name: "Empty value in registered resource values", - registeredResource: &policy.RegisteredResource{ - Name: "empty-value-in-values", - Values: []*policy.RegisteredResourceValue{{Value: ""}}, + name: "Empty registered resource value", + registeredResourceValue: &policy.RegisteredResourceValue{ + Value: "", }, - wantErr: ErrInvalidRegisteredResource, + wantErr: ErrInvalidRegisteredResourceValue, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := validateRegisteredResource(tt.registeredResource) + err := validateRegisteredResourceValue(tt.registeredResourceValue) if tt.wantErr != nil { require.ErrorIs(t, err, tt.wantErr) } else { From 4fb99bdbb59eb0202527be8c822c96673eff478a Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Thu, 5 Jun 2025 12:18:37 -0600 Subject: [PATCH 16/55] initial logic stub --- service/internal/access/v2/just_in_time_pdp.go | 13 ++++++++++--- service/internal/access/v2/pdp.go | 9 +++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/service/internal/access/v2/just_in_time_pdp.go b/service/internal/access/v2/just_in_time_pdp.go index 1bc4a0f335..9e336729b4 100644 --- a/service/internal/access/v2/just_in_time_pdp.go +++ b/service/internal/access/v2/just_in_time_pdp.go @@ -100,10 +100,17 @@ func (p *JustInTimePDP) GetDecision( case *authzV2.EntityIdentifier_Token: entityRepresentations, err = p.resolveEntitiesFromToken(ctx, entityIdentifier.GetToken(), skipEnvironmentEntities) - // TODO: implement this case case *authzV2.EntityIdentifier_RegisteredResourceValueFqn: - p.logger.DebugContext(ctx, "getting decision - resolving registered resource value FQN") - return nil, false, errors.New("registered resources not yet implemented") + valueFQN := strings.ToLower(entityIdentifier.GetRegisteredResourceValueFqn()) + // registered resources do not have entity representations, so only one decision to make and we can skip the remaining logic + decision, err := p.pdp.GetDecisionRegisteredResource(ctx, valueFQN, action, resources) + if err != nil { + return nil, false, fmt.Errorf("failed to get decision for registered resource value FQN [%s]: %w", valueFQN, err) + } + if decision == nil { + return nil, false, fmt.Errorf("decision is nil for registered resource value FQN [%s]", valueFQN) + } + return []*Decision{decision}, decision.Access, nil default: return nil, false, ErrInvalidEntityType diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 581fe34475..22c668804a 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -245,6 +245,15 @@ func (p *PolicyDecisionPoint) GetDecision( return decision, nil } +func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( + ctx context.Context, + registeredResourceValueFQN string, + action *policy.Action, + resources []*authz.Resource, +) (*Decision, error) { + return nil, fmt.Errorf("GetDecisionRegisteredResource not yet implemented") +} + func (p *PolicyDecisionPoint) GetEntitlements( ctx context.Context, entityRepresentations []*entityresolutionV2.EntityRepresentation, From cd2aa18835795d67cbf827860c32822cee16e42d Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Thu, 5 Jun 2025 12:50:23 -0600 Subject: [PATCH 17/55] initial full implementation --- service/internal/access/v2/pdp.go | 98 +++++++++++++++++++++++- service/internal/access/v2/validators.go | 23 ++++++ 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 22c668804a..0fbd89080a 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -251,7 +251,103 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( action *policy.Action, resources []*authz.Resource, ) (*Decision, error) { - return nil, fmt.Errorf("GetDecisionRegisteredResource not yet implemented") + l := p.logger.With("registeredResourceValueFQN", registeredResourceValueFQN) + l = l.With("action", action.GetName()) + l.DebugContext(ctx, "getting decision", slog.Int("resourcesCount", len(resources))) + + if err := validateGetDecisionRegisteredResource(registeredResourceValueFQN, action, resources); err != nil { + return nil, err + } + + registeredResourceValue := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] + if err := validateRegisteredResourceValue(registeredResourceValue); err != nil { + return nil, err + } + + // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources + decisionableAttributes := make(map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue) + + for idx, resource := range resources { + // Assign indexed ephemeral ID for resource if not already set + if resource.GetEphemeralId() == "" { + resource.EphemeralId = "resource-" + strconv.Itoa(idx) + } + + for idx, valueFQN := range resource.GetAttributeValues().GetFqns() { + // lowercase each resource attribute value FQN for case consistent map key lookups + valueFQN = strings.ToLower(valueFQN) + resource.GetAttributeValues().Fqns[idx] = valueFQN + + // If same value FQN more than once, skip + if _, ok := decisionableAttributes[valueFQN]; ok { + continue + } + + attributeAndValue, ok := p.allEntitleableAttributesByValueFQN[valueFQN] + if !ok { + return nil, fmt.Errorf("resource value FQN not found in memory [%s]: %w", valueFQN, ErrInvalidResource) + } + + decisionableAttributes[valueFQN] = attributeAndValue + err := populateHigherValuesIfHierarchy(ctx, p.logger, valueFQN, attributeAndValue.GetAttribute(), p.allEntitleableAttributesByValueFQN, decisionableAttributes) + if err != nil { + return nil, fmt.Errorf("error populating higher hierarchy attribute values: %w", err) + } + } + } + l.DebugContext(ctx, "filtered to only entitlements relevant to decisioning", slog.Int("decisionableAttributeValuesCount", len(decisionableAttributes))) + + entitledFQNsToActions := make(map[string][]*policy.Action) + for _, aav := range registeredResourceValue.GetActionAttributeValues() { + aavAction := aav.GetAction() + if action.GetName() != aavAction.GetName() { + l.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("actionName", aavAction.GetName())) + continue + } + + attrVal := aav.GetAttributeValue() + attrValFQN := attrVal.GetFqn() + actionsList, ok := entitledFQNsToActions[attrValFQN] + if !ok { + actionsList = make([]*policy.Action, 0) + } + + if !slices.ContainsFunc(actionsList, func(a *policy.Action) bool { + return a.GetName() == aavAction.GetName() + }) { + actionsList = append(actionsList, aavAction) + } + + entitledFQNsToActions[attrValFQN] = actionsList + + // todo: does hierarchy (low or high) need to be populated here? + } + + decision := &Decision{ + Access: true, + Results: make([]ResourceDecision, len(resources)), + } + + for idx, resource := range resources { + resourceDecision, err := getResourceDecision(ctx, p.logger, decisionableAttributes, entitledFQNsToActions, action, resource) + if err != nil || resourceDecision == nil { + return nil, fmt.Errorf("error evaluating a decision on resource [%v]: %w", resource, err) + } + if !resourceDecision.Passed { + decision.Access = false + } + + l.DebugContext( + ctx, + "resourceDecision result", + slog.Bool("passed", resourceDecision.Passed), + slog.String("resourceID", resourceDecision.ResourceID), + slog.Int("dataRuleResultsCount", len(resourceDecision.DataRuleResults)), + ) + decision.Results[idx] = *resourceDecision + } + + return decision, nil } func (p *PolicyDecisionPoint) GetEntitlements( diff --git a/service/internal/access/v2/validators.go b/service/internal/access/v2/validators.go index 444acf1338..e49c9d6509 100644 --- a/service/internal/access/v2/validators.go +++ b/service/internal/access/v2/validators.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/opentdf/platform/lib/identifier" authzV2 "github.com/opentdf/platform/protocol/go/authorization/v2" entityresolutionV2 "github.com/opentdf/platform/protocol/go/entityresolution/v2" "github.com/opentdf/platform/protocol/go/policy" @@ -41,6 +42,28 @@ func validateGetDecision(entityRepresentation *entityresolutionV2.EntityRepresen return nil } +// validateGetDecisionRegisteredResource validates the input parameters for GetDecisionRegisteredResource: +// - registeredResourceValueFQN: must be a valid registered resource value FQN +// - action: must not be nil +// - resources: must not be nil and must contain at least one resource +func validateGetDecisionRegisteredResource(registeredResourceValueFQN string, action *policy.Action, resources []*authzV2.Resource) error { + if _, err := identifier.Parse[*identifier.FullyQualifiedRegisteredResourceValue](registeredResourceValueFQN); err != nil { + return err + } + if action.GetName() == "" { + return fmt.Errorf("action required with name: %w", ErrInvalidAction) + } + if len(resources) == 0 { + return fmt.Errorf("resources are empty: %w", ErrInvalidResource) + } + for _, resource := range resources { + if resource == nil { + return fmt.Errorf("resource is nil: %w", ErrInvalidResource) + } + } + return nil +} + // validateSubjectMapping validates the subject mapping is valid for an entitlement decision // // subjectMapping: From 1f38e7e449b00715bc33a288e7ef98baa8fd0ef8 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Fri, 6 Jun 2025 08:51:21 -0600 Subject: [PATCH 18/55] rename param to infer reg res value as an entity --- service/internal/access/v2/pdp.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 0fbd89080a..a85d18db48 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -247,19 +247,19 @@ func (p *PolicyDecisionPoint) GetDecision( func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( ctx context.Context, - registeredResourceValueFQN string, + entityRegisteredResourceValueFQN string, action *policy.Action, resources []*authz.Resource, ) (*Decision, error) { - l := p.logger.With("registeredResourceValueFQN", registeredResourceValueFQN) + l := p.logger.With("entityRegisteredResourceValueFQN", entityRegisteredResourceValueFQN) l = l.With("action", action.GetName()) l.DebugContext(ctx, "getting decision", slog.Int("resourcesCount", len(resources))) - if err := validateGetDecisionRegisteredResource(registeredResourceValueFQN, action, resources); err != nil { + if err := validateGetDecisionRegisteredResource(entityRegisteredResourceValueFQN, action, resources); err != nil { return nil, err } - registeredResourceValue := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] + registeredResourceValue := p.allRegisteredResourceValuesByFQN[entityRegisteredResourceValueFQN] if err := validateRegisteredResourceValue(registeredResourceValue); err != nil { return nil, err } From d3f60ef2e7a4db3ab9c95da1ded9615e5454c294 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Fri, 6 Jun 2025 09:24:27 -0600 Subject: [PATCH 19/55] rename valueFQN to regResValueFQN for clarity --- service/internal/access/v2/just_in_time_pdp.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/service/internal/access/v2/just_in_time_pdp.go b/service/internal/access/v2/just_in_time_pdp.go index 9e336729b4..7a499016b6 100644 --- a/service/internal/access/v2/just_in_time_pdp.go +++ b/service/internal/access/v2/just_in_time_pdp.go @@ -101,14 +101,14 @@ func (p *JustInTimePDP) GetDecision( entityRepresentations, err = p.resolveEntitiesFromToken(ctx, entityIdentifier.GetToken(), skipEnvironmentEntities) case *authzV2.EntityIdentifier_RegisteredResourceValueFqn: - valueFQN := strings.ToLower(entityIdentifier.GetRegisteredResourceValueFqn()) + regResValueFQN := strings.ToLower(entityIdentifier.GetRegisteredResourceValueFqn()) // registered resources do not have entity representations, so only one decision to make and we can skip the remaining logic - decision, err := p.pdp.GetDecisionRegisteredResource(ctx, valueFQN, action, resources) + decision, err := p.pdp.GetDecisionRegisteredResource(ctx, regResValueFQN, action, resources) if err != nil { - return nil, false, fmt.Errorf("failed to get decision for registered resource value FQN [%s]: %w", valueFQN, err) + return nil, false, fmt.Errorf("failed to get decision for registered resource value FQN [%s]: %w", regResValueFQN, err) } if decision == nil { - return nil, false, fmt.Errorf("decision is nil for registered resource value FQN [%s]", valueFQN) + return nil, false, fmt.Errorf("decision is nil for registered resource value FQN [%s]", regResValueFQN) } return []*Decision{decision}, decision.Access, nil @@ -163,9 +163,9 @@ func (p *JustInTimePDP) GetEntitlements( case *authzV2.EntityIdentifier_RegisteredResourceValueFqn: p.logger.DebugContext(ctx, "getting decision - resolving registered resource value FQN") - valueFQN := strings.ToLower(entityIdentifier.GetRegisteredResourceValueFqn()) + regResValueFQN := strings.ToLower(entityIdentifier.GetRegisteredResourceValueFqn()) // registered resources do not have entity representations, so we can skip the remaining logic - return p.pdp.GetEntitlementsRegisteredResource(ctx, valueFQN, withComprehensiveHierarchy) + return p.pdp.GetEntitlementsRegisteredResource(ctx, regResValueFQN, withComprehensiveHierarchy) default: return nil, fmt.Errorf("entity type %T: %w", entityIdentifier.GetIdentifier(), ErrInvalidEntityType) From 90b6471a96961d88dd3a5399f6dc88d4291b49d8 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Fri, 6 Jun 2025 15:41:24 -0600 Subject: [PATCH 20/55] refactor resource decisionable attributes logic --- service/internal/access/v2/pdp.go | 150 ++++++++++++++++-------------- 1 file changed, 79 insertions(+), 71 deletions(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index a85d18db48..898203e7f1 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -167,47 +167,11 @@ func (p *PolicyDecisionPoint) GetDecision( } // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources - decisionableAttributes := make(map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue) - - for idx, resource := range resources { - // Assign indexed ephemeral ID for resource if not already set - if resource.GetEphemeralId() == "" { - resource.EphemeralId = "resource-" + strconv.Itoa(idx) - } - - switch resource.GetResource().(type) { - // TODO: handle gathering decisionable attributes of registered resources - case *authz.Resource_RegisteredResourceValueFqn: - return nil, fmt.Errorf("registered resource value FQN not supported: %w", ErrInvalidResource) - - case *authz.Resource_AttributeValues_: - for idx, valueFQN := range resource.GetAttributeValues().GetFqns() { - // lowercase each resource attribute value FQN for case consistent map key lookups - valueFQN = strings.ToLower(valueFQN) - resource.GetAttributeValues().Fqns[idx] = valueFQN - - // If same value FQN more than once, skip - if _, ok := decisionableAttributes[valueFQN]; ok { - continue - } - - attributeAndValue, ok := p.allEntitleableAttributesByValueFQN[valueFQN] - if !ok { - return nil, fmt.Errorf("resource value FQN not found in memory [%s]: %w", valueFQN, ErrInvalidResource) - } - - decisionableAttributes[valueFQN] = attributeAndValue - err := populateHigherValuesIfHierarchy(ctx, p.logger, valueFQN, attributeAndValue.GetAttribute(), p.allEntitleableAttributesByValueFQN, decisionableAttributes) - if err != nil { - return nil, fmt.Errorf("error populating higher hierarchy attribute values: %w", err) - } - } - - default: - // default should never happen as we validate above - return nil, fmt.Errorf("invalid resource type [%T]: %w", resource.GetResource(), ErrInvalidResource) - } + decisionableAttributes, err := p.getResourceDecisionableAttributes(ctx, action, resources) + if err != nil { + return nil, fmt.Errorf("error determining decisionable attributes for resources: %w", err) } + l.DebugContext(ctx, "filtered to only entitlements relevant to decisioning", slog.Int("decisionableAttributeValuesCount", len(decisionableAttributes))) // Resolve them to their entitled FQNs and the actions available on each @@ -265,35 +229,9 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( } // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources - decisionableAttributes := make(map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue) - - for idx, resource := range resources { - // Assign indexed ephemeral ID for resource if not already set - if resource.GetEphemeralId() == "" { - resource.EphemeralId = "resource-" + strconv.Itoa(idx) - } - - for idx, valueFQN := range resource.GetAttributeValues().GetFqns() { - // lowercase each resource attribute value FQN for case consistent map key lookups - valueFQN = strings.ToLower(valueFQN) - resource.GetAttributeValues().Fqns[idx] = valueFQN - - // If same value FQN more than once, skip - if _, ok := decisionableAttributes[valueFQN]; ok { - continue - } - - attributeAndValue, ok := p.allEntitleableAttributesByValueFQN[valueFQN] - if !ok { - return nil, fmt.Errorf("resource value FQN not found in memory [%s]: %w", valueFQN, ErrInvalidResource) - } - - decisionableAttributes[valueFQN] = attributeAndValue - err := populateHigherValuesIfHierarchy(ctx, p.logger, valueFQN, attributeAndValue.GetAttribute(), p.allEntitleableAttributesByValueFQN, decisionableAttributes) - if err != nil { - return nil, fmt.Errorf("error populating higher hierarchy attribute values: %w", err) - } - } + decisionableAttributes, err := p.getResourceDecisionableAttributes(ctx, action, resources) + if err != nil { + return nil, fmt.Errorf("error determining decisionable attributes for resources: %w", err) } l.DebugContext(ctx, "filtered to only entitlements relevant to decisioning", slog.Int("decisionableAttributeValuesCount", len(decisionableAttributes))) @@ -319,8 +257,6 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( } entitledFQNsToActions[attrValFQN] = actionsList - - // todo: does hierarchy (low or high) need to be populated here? } decision := &Decision{ @@ -484,3 +420,75 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( return result, nil } + +func (p *PolicyDecisionPoint) getResourceDecisionableAttributes(ctx context.Context, action *policy.Action, resources []*authz.Resource) ( + map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue, + error, +) { + var ( + // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources + decisionableAttributes = make(map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue) + attrValFQNs = make([]string, 0) + ) + + for idx, resource := range resources { + // Assign indexed ephemeral ID for resource if not already set + if resource.GetEphemeralId() == "" { + resource.EphemeralId = "resource-" + strconv.Itoa(idx) + } + + switch resource.GetResource().(type) { + case *authz.Resource_RegisteredResourceValueFqn: + regResValueFQN := strings.ToLower(resource.GetRegisteredResourceValueFqn()) + + regResValue := p.allRegisteredResourceValuesByFQN[regResValueFQN] + if err := validateRegisteredResourceValue(regResValue); err != nil { + return nil, fmt.Errorf("invalid registered resource value FQN [%s]: %w", regResValueFQN, err) + } + + for _, aav := range regResValue.GetActionAttributeValues() { + aavAction := aav.GetAction() + if aavAction.GetName() != action.GetName() { + p.logger.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("actionName", aavAction.GetName())) + continue + } + + attrVal := aav.GetAttributeValue() + attrValFQN := strings.ToLower(attrVal.GetFqn()) + attrValFQNs = append(attrValFQNs, attrValFQN) + } + case *authz.Resource_AttributeValues_: + for idx, attrValueFQN := range resource.GetAttributeValues().GetFqns() { + // lowercase each resource attribute value FQN for case consistent map key lookups + attrValueFQN = strings.ToLower(attrValueFQN) + resource.GetAttributeValues().Fqns[idx] = attrValueFQN + attrValFQNs = append(attrValFQNs, attrValueFQN) + } + + default: + // default should never happen as we validate above + return nil, fmt.Errorf("invalid resource type [%T]: %w", resource.GetResource(), ErrInvalidResource) + } + } + + for _, valueFQN := range attrValFQNs { + // If same value FQN more than once, skip + if _, ok := decisionableAttributes[valueFQN]; ok { + continue + } + + attributeAndValue, ok := p.allEntitleableAttributesByValueFQN[valueFQN] + if !ok { + return nil, fmt.Errorf("resource value FQN not found in memory [%s]: %w", valueFQN, ErrInvalidResource) + } + + decisionableAttributes[valueFQN] = attributeAndValue + + err := populateHigherValuesIfHierarchy(ctx, p.logger, valueFQN, attributeAndValue.GetAttribute(), p.allEntitleableAttributesByValueFQN, decisionableAttributes) + if err != nil { + return nil, fmt.Errorf("error populating higher hierarchy attribute values: %w", err) + } + } + + return decisionableAttributes, nil +} From 04df6dedcbfa9a272eafebc6695853e4f8a18f88 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 10 Jun 2025 12:31:39 -0600 Subject: [PATCH 21/55] Revert "refactor resource decisionable attributes logic" This reverts commit 90b6471a96961d88dd3a5399f6dc88d4291b49d8. --- service/internal/access/v2/pdp.go | 150 ++++++++++++++---------------- 1 file changed, 71 insertions(+), 79 deletions(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 898203e7f1..a85d18db48 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -167,11 +167,47 @@ func (p *PolicyDecisionPoint) GetDecision( } // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources - decisionableAttributes, err := p.getResourceDecisionableAttributes(ctx, action, resources) - if err != nil { - return nil, fmt.Errorf("error determining decisionable attributes for resources: %w", err) - } + decisionableAttributes := make(map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue) + + for idx, resource := range resources { + // Assign indexed ephemeral ID for resource if not already set + if resource.GetEphemeralId() == "" { + resource.EphemeralId = "resource-" + strconv.Itoa(idx) + } + + switch resource.GetResource().(type) { + // TODO: handle gathering decisionable attributes of registered resources + case *authz.Resource_RegisteredResourceValueFqn: + return nil, fmt.Errorf("registered resource value FQN not supported: %w", ErrInvalidResource) + case *authz.Resource_AttributeValues_: + for idx, valueFQN := range resource.GetAttributeValues().GetFqns() { + // lowercase each resource attribute value FQN for case consistent map key lookups + valueFQN = strings.ToLower(valueFQN) + resource.GetAttributeValues().Fqns[idx] = valueFQN + + // If same value FQN more than once, skip + if _, ok := decisionableAttributes[valueFQN]; ok { + continue + } + + attributeAndValue, ok := p.allEntitleableAttributesByValueFQN[valueFQN] + if !ok { + return nil, fmt.Errorf("resource value FQN not found in memory [%s]: %w", valueFQN, ErrInvalidResource) + } + + decisionableAttributes[valueFQN] = attributeAndValue + err := populateHigherValuesIfHierarchy(ctx, p.logger, valueFQN, attributeAndValue.GetAttribute(), p.allEntitleableAttributesByValueFQN, decisionableAttributes) + if err != nil { + return nil, fmt.Errorf("error populating higher hierarchy attribute values: %w", err) + } + } + + default: + // default should never happen as we validate above + return nil, fmt.Errorf("invalid resource type [%T]: %w", resource.GetResource(), ErrInvalidResource) + } + } l.DebugContext(ctx, "filtered to only entitlements relevant to decisioning", slog.Int("decisionableAttributeValuesCount", len(decisionableAttributes))) // Resolve them to their entitled FQNs and the actions available on each @@ -229,9 +265,35 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( } // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources - decisionableAttributes, err := p.getResourceDecisionableAttributes(ctx, action, resources) - if err != nil { - return nil, fmt.Errorf("error determining decisionable attributes for resources: %w", err) + decisionableAttributes := make(map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue) + + for idx, resource := range resources { + // Assign indexed ephemeral ID for resource if not already set + if resource.GetEphemeralId() == "" { + resource.EphemeralId = "resource-" + strconv.Itoa(idx) + } + + for idx, valueFQN := range resource.GetAttributeValues().GetFqns() { + // lowercase each resource attribute value FQN for case consistent map key lookups + valueFQN = strings.ToLower(valueFQN) + resource.GetAttributeValues().Fqns[idx] = valueFQN + + // If same value FQN more than once, skip + if _, ok := decisionableAttributes[valueFQN]; ok { + continue + } + + attributeAndValue, ok := p.allEntitleableAttributesByValueFQN[valueFQN] + if !ok { + return nil, fmt.Errorf("resource value FQN not found in memory [%s]: %w", valueFQN, ErrInvalidResource) + } + + decisionableAttributes[valueFQN] = attributeAndValue + err := populateHigherValuesIfHierarchy(ctx, p.logger, valueFQN, attributeAndValue.GetAttribute(), p.allEntitleableAttributesByValueFQN, decisionableAttributes) + if err != nil { + return nil, fmt.Errorf("error populating higher hierarchy attribute values: %w", err) + } + } } l.DebugContext(ctx, "filtered to only entitlements relevant to decisioning", slog.Int("decisionableAttributeValuesCount", len(decisionableAttributes))) @@ -257,6 +319,8 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( } entitledFQNsToActions[attrValFQN] = actionsList + + // todo: does hierarchy (low or high) need to be populated here? } decision := &Decision{ @@ -420,75 +484,3 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( return result, nil } - -func (p *PolicyDecisionPoint) getResourceDecisionableAttributes(ctx context.Context, action *policy.Action, resources []*authz.Resource) ( - map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue, - error, -) { - var ( - // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources - decisionableAttributes = make(map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue) - attrValFQNs = make([]string, 0) - ) - - for idx, resource := range resources { - // Assign indexed ephemeral ID for resource if not already set - if resource.GetEphemeralId() == "" { - resource.EphemeralId = "resource-" + strconv.Itoa(idx) - } - - switch resource.GetResource().(type) { - case *authz.Resource_RegisteredResourceValueFqn: - regResValueFQN := strings.ToLower(resource.GetRegisteredResourceValueFqn()) - - regResValue := p.allRegisteredResourceValuesByFQN[regResValueFQN] - if err := validateRegisteredResourceValue(regResValue); err != nil { - return nil, fmt.Errorf("invalid registered resource value FQN [%s]: %w", regResValueFQN, err) - } - - for _, aav := range regResValue.GetActionAttributeValues() { - aavAction := aav.GetAction() - if aavAction.GetName() != action.GetName() { - p.logger.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("actionName", aavAction.GetName())) - continue - } - - attrVal := aav.GetAttributeValue() - attrValFQN := strings.ToLower(attrVal.GetFqn()) - attrValFQNs = append(attrValFQNs, attrValFQN) - } - case *authz.Resource_AttributeValues_: - for idx, attrValueFQN := range resource.GetAttributeValues().GetFqns() { - // lowercase each resource attribute value FQN for case consistent map key lookups - attrValueFQN = strings.ToLower(attrValueFQN) - resource.GetAttributeValues().Fqns[idx] = attrValueFQN - attrValFQNs = append(attrValFQNs, attrValueFQN) - } - - default: - // default should never happen as we validate above - return nil, fmt.Errorf("invalid resource type [%T]: %w", resource.GetResource(), ErrInvalidResource) - } - } - - for _, valueFQN := range attrValFQNs { - // If same value FQN more than once, skip - if _, ok := decisionableAttributes[valueFQN]; ok { - continue - } - - attributeAndValue, ok := p.allEntitleableAttributesByValueFQN[valueFQN] - if !ok { - return nil, fmt.Errorf("resource value FQN not found in memory [%s]: %w", valueFQN, ErrInvalidResource) - } - - decisionableAttributes[valueFQN] = attributeAndValue - - err := populateHigherValuesIfHierarchy(ctx, p.logger, valueFQN, attributeAndValue.GetAttribute(), p.allEntitleableAttributesByValueFQN, decisionableAttributes) - if err != nil { - return nil, fmt.Errorf("error populating higher hierarchy attribute values: %w", err) - } - } - - return decisionableAttributes, nil -} From 52ec7edd4245bf591a55ec8380195f54e35ef6bc Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 10 Jun 2025 12:58:42 -0600 Subject: [PATCH 22/55] simplify reg res value validation --- service/internal/access/v2/pdp.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index a85d18db48..e183fa924a 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -135,6 +135,10 @@ func NewPolicyDecisionPoint( rrName := rr.GetName() for _, v := range rr.GetValues() { + if err := validateRegisteredResourceValue(v); err != nil { + return nil, fmt.Errorf("invalid registered resource value: %w", err) + } + fullyQualifiedValue := identifier.FullyQualifiedRegisteredResourceValue{ Name: rrName, Value: v.GetValue(), @@ -259,9 +263,9 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( return nil, err } - registeredResourceValue := p.allRegisteredResourceValuesByFQN[entityRegisteredResourceValueFQN] - if err := validateRegisteredResourceValue(registeredResourceValue); err != nil { - return nil, err + entityRegisteredResourceValue, ok := p.allRegisteredResourceValuesByFQN[entityRegisteredResourceValueFQN] + if !ok { + return nil, fmt.Errorf("registered resource value FQN not found in memory [%s]: %w", entityRegisteredResourceValueFQN, ErrInvalidResource) } // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources @@ -298,7 +302,7 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( l.DebugContext(ctx, "filtered to only entitlements relevant to decisioning", slog.Int("decisionableAttributeValuesCount", len(decisionableAttributes))) entitledFQNsToActions := make(map[string][]*policy.Action) - for _, aav := range registeredResourceValue.GetActionAttributeValues() { + for _, aav := range entityRegisteredResourceValue.GetActionAttributeValues() { aavAction := aav.GetAction() if action.GetName() != aavAction.GetName() { l.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("actionName", aavAction.GetName())) @@ -435,9 +439,9 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( return nil, err } - registeredResourceValue := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] - if err := validateRegisteredResourceValue(registeredResourceValue); err != nil { - return nil, err + registeredResourceValue, ok := p.allRegisteredResourceValuesByFQN[registeredResourceValueFQN] + if !ok { + return nil, fmt.Errorf("registered resource value FQN not found in memory [%s]: %w", registeredResourceValueFQN, ErrInvalidResource) } actionsPerAttributeValueFqn := make(map[string]*authz.EntityEntitlements_ActionsList) From a2b368597e5d23e35342ac630298cd8fff17d063 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 10 Jun 2025 14:47:09 -0600 Subject: [PATCH 23/55] add evaluation implementation --- service/internal/access/v2/evaluate.go | 42 ++++++++++++++++++++++++++ service/internal/access/v2/pdp.go | 32 ++++++++++++-------- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/service/internal/access/v2/evaluate.go b/service/internal/access/v2/evaluate.go index 86b33556bc..4f89dc05d1 100644 --- a/service/internal/access/v2/evaluate.go +++ b/service/internal/access/v2/evaluate.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "log/slog" + "slices" "strings" authz "github.com/opentdf/platform/protocol/go/authorization/v2" @@ -55,6 +56,47 @@ func getResourceDecision( } } +func getResourceDecisionRegisteredResource( + ctx context.Context, + logger *logger.Logger, + accessibleAttributeValues map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue, + accessibleRegisteredResourceValues map[string]*policy.RegisteredResourceValue, + entitlements subjectmappingbuiltin.AttributeValueFQNsToActions, + action *policy.Action, + resource *authz.Resource, +) (*ResourceDecision, error) { + // todo: add validation + + logger.DebugContext( + ctx, + "getting decision on one registered resource", + slog.Any("resource", resource.GetResource()), + ) + + regResValueFQN := strings.ToLower(resource.GetRegisteredResourceValueFqn()) + + regResValue, ok := accessibleRegisteredResourceValues[regResValueFQN] + if !ok { + return nil, fmt.Errorf("%w: %s", ErrFQNNotFound, regResValueFQN) + } + + resourceAttributeValues := &authz.Resource_AttributeValues{ + Fqns: make([]string, 0), + } + for _, aav := range regResValue.GetActionAttributeValues() { + if aav.GetAction().GetName() != action.GetName() { + continue + } + + aavAttrValueFQN := aav.GetAttributeValue().GetFqn() + if !slices.Contains(resourceAttributeValues.Fqns, aavAttrValueFQN) { + resourceAttributeValues.Fqns = append(resourceAttributeValues.Fqns, aavAttrValueFQN) + } + } + + return evaluateResourceAttributeValues(ctx, logger, resourceAttributeValues, resource.GetEphemeralId(), action, entitlements, accessibleAttributeValues) +} + // evaluateResourceAttributeValues evaluates a list of attribute values against the action and entitlements // and lowercases the FQNs to ensure case-insensitive matching func evaluateResourceAttributeValues( diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index e183fa924a..0d69b20625 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -277,23 +277,33 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( resource.EphemeralId = "resource-" + strconv.Itoa(idx) } - for idx, valueFQN := range resource.GetAttributeValues().GetFqns() { - // lowercase each resource attribute value FQN for case consistent map key lookups - valueFQN = strings.ToLower(valueFQN) - resource.GetAttributeValues().Fqns[idx] = valueFQN + resourceRegResValueFQN := strings.ToLower(resource.GetRegisteredResourceValueFqn()) + resourceRegResValue, found := p.allRegisteredResourceValuesByFQN[resourceRegResValueFQN] + if !found { + return nil, fmt.Errorf("resource registered resource value FQN not found in memory [%s]: %w", resourceRegResValueFQN, ErrInvalidResource) + } + + for _, aav := range resourceRegResValue.GetActionAttributeValues() { + aavAction := aav.GetAction() + if aavAction.GetName() != action.GetName() { + l.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("action", aavAction.GetName())) + continue + } + + aavAttrValFQN := aav.GetAttributeValue().GetFqn() // If same value FQN more than once, skip - if _, ok := decisionableAttributes[valueFQN]; ok { + if _, ok := decisionableAttributes[aavAttrValFQN]; ok { continue } - attributeAndValue, ok := p.allEntitleableAttributesByValueFQN[valueFQN] + attributeAndValue, ok := p.allEntitleableAttributesByValueFQN[aavAttrValFQN] if !ok { - return nil, fmt.Errorf("resource value FQN not found in memory [%s]: %w", valueFQN, ErrInvalidResource) + return nil, fmt.Errorf("resource value FQN not found in memory [%s]: %w", aavAttrValFQN, ErrInvalidResource) } - decisionableAttributes[valueFQN] = attributeAndValue - err := populateHigherValuesIfHierarchy(ctx, p.logger, valueFQN, attributeAndValue.GetAttribute(), p.allEntitleableAttributesByValueFQN, decisionableAttributes) + decisionableAttributes[aavAttrValFQN] = attributeAndValue + err := populateHigherValuesIfHierarchy(ctx, p.logger, aavAttrValFQN, attributeAndValue.GetAttribute(), p.allEntitleableAttributesByValueFQN, decisionableAttributes) if err != nil { return nil, fmt.Errorf("error populating higher hierarchy attribute values: %w", err) } @@ -323,8 +333,6 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( } entitledFQNsToActions[attrValFQN] = actionsList - - // todo: does hierarchy (low or high) need to be populated here? } decision := &Decision{ @@ -333,7 +341,7 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( } for idx, resource := range resources { - resourceDecision, err := getResourceDecision(ctx, p.logger, decisionableAttributes, entitledFQNsToActions, action, resource) + resourceDecision, err := getResourceDecisionRegisteredResource(ctx, p.logger, decisionableAttributes, p.allRegisteredResourceValuesByFQN, entitledFQNsToActions, action, resource) if err != nil || resourceDecision == nil { return nil, fmt.Errorf("error evaluating a decision on resource [%v]: %w", resource, err) } From a420a466c8ee1f5ce19944923dc8c9bb481845a7 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 10 Jun 2025 15:14:43 -0600 Subject: [PATCH 24/55] add todo validation --- service/internal/access/v2/evaluate.go | 4 ++- service/internal/access/v2/validators.go | 32 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/service/internal/access/v2/evaluate.go b/service/internal/access/v2/evaluate.go index 4f89dc05d1..61bdedbff8 100644 --- a/service/internal/access/v2/evaluate.go +++ b/service/internal/access/v2/evaluate.go @@ -65,7 +65,9 @@ func getResourceDecisionRegisteredResource( action *policy.Action, resource *authz.Resource, ) (*ResourceDecision, error) { - // todo: add validation + if err := validateGetResourceDecisionRegisteredResource(accessibleAttributeValues, accessibleRegisteredResourceValues, entitlements, action, resource); err != nil { + return nil, err + } logger.DebugContext( ctx, diff --git a/service/internal/access/v2/validators.go b/service/internal/access/v2/validators.go index e49c9d6509..65d4c02fba 100644 --- a/service/internal/access/v2/validators.go +++ b/service/internal/access/v2/validators.go @@ -194,3 +194,35 @@ func validateGetResourceDecision( } return nil } + +// validateOneResourceDecision validates the parameters for an access decision on a resource +// +// - accessibleAttributeValues: must not be nil +// - accessibleRegisteredResourceValues: must not be nil +// - entitlements: must not be nil +// - action: must not be nil +// - resource: must not be nil +func validateGetResourceDecisionRegisteredResource( + accessibleAttributeValues map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue, + accessibleRegisteredResourceValues map[string]*policy.RegisteredResourceValue, + entitlements subjectmappingbuiltin.AttributeValueFQNsToActions, + action *policy.Action, + resource *authzV2.Resource, +) error { + if entitlements == nil { + return fmt.Errorf("entitled FQNs to actions are nil: %w", ErrInvalidEntitledFQNsToActions) + } + if action.GetName() == "" { + return fmt.Errorf("action name required: %w", ErrInvalidAction) + } + if resource.GetResource() == nil { + return fmt.Errorf("resource is nil: %w", ErrInvalidResource) + } + if len(accessibleAttributeValues) == 0 { + return fmt.Errorf("accessible attribute values are empty: %w", ErrMissingRequiredPolicy) + } + if len(accessibleRegisteredResourceValues) == 0 { + return fmt.Errorf("accessible registered resource values are empty: %w", ErrMissingRequiredPolicy) + } + return nil +} From 6d41da31e47b8d781f630d9593682ec1d2aec714 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Wed, 11 Jun 2025 10:01:25 -0600 Subject: [PATCH 25/55] fix comment typo --- service/internal/access/v2/validators.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/internal/access/v2/validators.go b/service/internal/access/v2/validators.go index 65d4c02fba..9b61db8e2a 100644 --- a/service/internal/access/v2/validators.go +++ b/service/internal/access/v2/validators.go @@ -195,7 +195,7 @@ func validateGetResourceDecision( return nil } -// validateOneResourceDecision validates the parameters for an access decision on a resource +// validateGetResourceDecisionRegisteredResource validates the parameters for an access decision on a registered resource // // - accessibleAttributeValues: must not be nil // - accessibleRegisteredResourceValues: must not be nil From e4d83df1e899e5aecb9d49d2139d9ce0b1c402c6 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Wed, 11 Jun 2025 10:33:03 -0600 Subject: [PATCH 26/55] fix failing test and add decisionable attr refactor back in --- service/internal/access/v2/pdp.go | 153 ++++++++++++------------- service/internal/access/v2/pdp_test.go | 2 +- 2 files changed, 75 insertions(+), 80 deletions(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 0d69b20625..5cad9ed23b 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -171,46 +171,9 @@ func (p *PolicyDecisionPoint) GetDecision( } // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources - decisionableAttributes := make(map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue) - - for idx, resource := range resources { - // Assign indexed ephemeral ID for resource if not already set - if resource.GetEphemeralId() == "" { - resource.EphemeralId = "resource-" + strconv.Itoa(idx) - } - - switch resource.GetResource().(type) { - // TODO: handle gathering decisionable attributes of registered resources - case *authz.Resource_RegisteredResourceValueFqn: - return nil, fmt.Errorf("registered resource value FQN not supported: %w", ErrInvalidResource) - - case *authz.Resource_AttributeValues_: - for idx, valueFQN := range resource.GetAttributeValues().GetFqns() { - // lowercase each resource attribute value FQN for case consistent map key lookups - valueFQN = strings.ToLower(valueFQN) - resource.GetAttributeValues().Fqns[idx] = valueFQN - - // If same value FQN more than once, skip - if _, ok := decisionableAttributes[valueFQN]; ok { - continue - } - - attributeAndValue, ok := p.allEntitleableAttributesByValueFQN[valueFQN] - if !ok { - return nil, fmt.Errorf("resource value FQN not found in memory [%s]: %w", valueFQN, ErrInvalidResource) - } - - decisionableAttributes[valueFQN] = attributeAndValue - err := populateHigherValuesIfHierarchy(ctx, p.logger, valueFQN, attributeAndValue.GetAttribute(), p.allEntitleableAttributesByValueFQN, decisionableAttributes) - if err != nil { - return nil, fmt.Errorf("error populating higher hierarchy attribute values: %w", err) - } - } - - default: - // default should never happen as we validate above - return nil, fmt.Errorf("invalid resource type [%T]: %w", resource.GetResource(), ErrInvalidResource) - } + decisionableAttributes, err := p.getResourceDecisionableAttributes(ctx, l, action, resources) + if err != nil { + return nil, fmt.Errorf("error getting decisionable attributes: %w", err) } l.DebugContext(ctx, "filtered to only entitlements relevant to decisioning", slog.Int("decisionableAttributeValuesCount", len(decisionableAttributes))) @@ -269,45 +232,9 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( } // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources - decisionableAttributes := make(map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue) - - for idx, resource := range resources { - // Assign indexed ephemeral ID for resource if not already set - if resource.GetEphemeralId() == "" { - resource.EphemeralId = "resource-" + strconv.Itoa(idx) - } - - resourceRegResValueFQN := strings.ToLower(resource.GetRegisteredResourceValueFqn()) - resourceRegResValue, found := p.allRegisteredResourceValuesByFQN[resourceRegResValueFQN] - if !found { - return nil, fmt.Errorf("resource registered resource value FQN not found in memory [%s]: %w", resourceRegResValueFQN, ErrInvalidResource) - } - - for _, aav := range resourceRegResValue.GetActionAttributeValues() { - aavAction := aav.GetAction() - if aavAction.GetName() != action.GetName() { - l.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("action", aavAction.GetName())) - continue - } - - aavAttrValFQN := aav.GetAttributeValue().GetFqn() - - // If same value FQN more than once, skip - if _, ok := decisionableAttributes[aavAttrValFQN]; ok { - continue - } - - attributeAndValue, ok := p.allEntitleableAttributesByValueFQN[aavAttrValFQN] - if !ok { - return nil, fmt.Errorf("resource value FQN not found in memory [%s]: %w", aavAttrValFQN, ErrInvalidResource) - } - - decisionableAttributes[aavAttrValFQN] = attributeAndValue - err := populateHigherValuesIfHierarchy(ctx, p.logger, aavAttrValFQN, attributeAndValue.GetAttribute(), p.allEntitleableAttributesByValueFQN, decisionableAttributes) - if err != nil { - return nil, fmt.Errorf("error populating higher hierarchy attribute values: %w", err) - } - } + decisionableAttributes, err := p.getResourceDecisionableAttributes(ctx, l, action, resources) + if err != nil { + return nil, fmt.Errorf("error getting decisionable attributes: %w", err) } l.DebugContext(ctx, "filtered to only entitlements relevant to decisioning", slog.Int("decisionableAttributeValuesCount", len(decisionableAttributes))) @@ -496,3 +423,71 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( return result, nil } + +func (p *PolicyDecisionPoint) getResourceDecisionableAttributes(ctx context.Context, logger *logger.Logger, action *policy.Action, resources []*authz.Resource) (map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue, error) { + var ( + decisionableAttributes = make(map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue) + attrValueFQNs = make([]string, 0) + ) + + // Parse attribute value FQNs from various resource types + for idx, resource := range resources { + // Assign indexed ephemeral ID for resource if not already set + if resource.GetEphemeralId() == "" { + resource.EphemeralId = "resource-" + strconv.Itoa(idx) + } + + switch resource.GetResource().(type) { + case *authz.Resource_RegisteredResourceValueFqn: + regResValueFQN := strings.ToLower(resource.GetRegisteredResourceValueFqn()) + regResValue, found := p.allRegisteredResourceValuesByFQN[regResValueFQN] + if !found { + return nil, fmt.Errorf("resource registered resource value FQN not found in memory [%s]: %w", regResValueFQN, ErrInvalidResource) + } + + for _, aav := range regResValue.GetActionAttributeValues() { + aavAction := aav.GetAction() + if aavAction.GetName() != action.GetName() { + logger.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("action", aavAction.GetName())) + continue + } + + attrValueFQNs = append(attrValueFQNs, aav.GetAttributeValue().GetFqn()) + } + + case *authz.Resource_AttributeValues_: + for idx, attrValueFQN := range resource.GetAttributeValues().GetFqns() { + // lowercase each resource attribute value FQN for case consistent map key lookups + attrValueFQN = strings.ToLower(attrValueFQN) + resource.GetAttributeValues().Fqns[idx] = attrValueFQN + + attrValueFQNs = append(attrValueFQNs, attrValueFQN) + } + + default: + // default should never happen as we validate above + return nil, fmt.Errorf("invalid resource type [%T]: %w", resource.GetResource(), ErrInvalidResource) + } + } + + // determine decisionable attributes based on the attribute value FQNs + for _, attrValueFQN := range attrValueFQNs { + // If same value FQN more than once, skip (dedupe) + if _, ok := decisionableAttributes[attrValueFQN]; ok { + continue + } + + attributeAndValue, ok := p.allEntitleableAttributesByValueFQN[attrValueFQN] + if !ok { + return nil, fmt.Errorf("resource attribute value FQN not found in memory [%s]: %w", attrValueFQN, ErrInvalidResource) + } + + decisionableAttributes[attrValueFQN] = attributeAndValue + err := populateHigherValuesIfHierarchy(ctx, logger, attrValueFQN, attributeAndValue.GetAttribute(), p.allEntitleableAttributesByValueFQN, decisionableAttributes) + if err != nil { + return nil, fmt.Errorf("error populating higher hierarchy attribute values: %w", err) + } + } + + return decisionableAttributes, nil +} diff --git a/service/internal/access/v2/pdp_test.go b/service/internal/access/v2/pdp_test.go index e256cda832..9ded45b944 100644 --- a/service/internal/access/v2/pdp_test.go +++ b/service/internal/access/v2/pdp_test.go @@ -1963,7 +1963,7 @@ func (s *PDPTestSuite) Test_GetEntitlementsRegisteredResource() { ) s.Require().Error(err) - s.Require().ErrorIs(err, ErrInvalidRegisteredResourceValue) + s.Require().ErrorIs(err, ErrInvalidResource) s.Require().Nil(entitlements) }) From 98452598ff94026bb91fd18a64d02a5d14c46729 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Thu, 12 Jun 2025 09:46:54 -0600 Subject: [PATCH 27/55] refactor getResourceDecision to include reg res values and remove dead code --- service/internal/access/v2/evaluate.go | 73 ++++++++------------- service/internal/access/v2/evaluate_test.go | 14 ++-- service/internal/access/v2/pdp.go | 4 +- service/internal/access/v2/validators.go | 32 --------- 4 files changed, 40 insertions(+), 83 deletions(-) diff --git a/service/internal/access/v2/evaluate.go b/service/internal/access/v2/evaluate.go index 61bdedbff8..75330322f1 100644 --- a/service/internal/access/v2/evaluate.go +++ b/service/internal/access/v2/evaluate.go @@ -30,6 +30,7 @@ func getResourceDecision( ctx context.Context, logger *logger.Logger, accessibleAttributeValues map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue, + accessibleRegisteredResourceValues map[string]*policy.RegisteredResourceValue, entitlements subjectmappingbuiltin.AttributeValueFQNsToActions, action *policy.Action, resource *authz.Resource, @@ -44,59 +45,41 @@ func getResourceDecision( slog.Any("resource", resource.GetResource()), ) - switch resource.GetResource().(type) { - // TODO: handle registered resources - case *authz.Resource_RegisteredResourceValueFqn: - return nil, fmt.Errorf("registered resources not supported yet: %w", ErrInvalidResource) - case *authz.Resource_AttributeValues_: - return evaluateResourceAttributeValues(ctx, logger, resource.GetAttributeValues(), resource.GetEphemeralId(), action, entitlements, accessibleAttributeValues) - - default: - return nil, fmt.Errorf("unsupported resource type: %w", ErrInvalidResource) - } -} - -func getResourceDecisionRegisteredResource( - ctx context.Context, - logger *logger.Logger, - accessibleAttributeValues map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue, - accessibleRegisteredResourceValues map[string]*policy.RegisteredResourceValue, - entitlements subjectmappingbuiltin.AttributeValueFQNsToActions, - action *policy.Action, - resource *authz.Resource, -) (*ResourceDecision, error) { - if err := validateGetResourceDecisionRegisteredResource(accessibleAttributeValues, accessibleRegisteredResourceValues, entitlements, action, resource); err != nil { - return nil, err - } - - logger.DebugContext( - ctx, - "getting decision on one registered resource", - slog.Any("resource", resource.GetResource()), + var ( + resourceID = resource.GetEphemeralId() + resourceAttributeValues *authz.Resource_AttributeValues ) - regResValueFQN := strings.ToLower(resource.GetRegisteredResourceValueFqn()) - - regResValue, ok := accessibleRegisteredResourceValues[regResValueFQN] - if !ok { - return nil, fmt.Errorf("%w: %s", ErrFQNNotFound, regResValueFQN) - } + switch resource.GetResource().(type) { + case *authz.Resource_RegisteredResourceValueFqn: + regResValueFQN := strings.ToLower(resource.GetRegisteredResourceValueFqn()) + regResValue, found := accessibleRegisteredResourceValues[regResValueFQN] + if !found { + return nil, fmt.Errorf("%w: %s", ErrFQNNotFound, regResValueFQN) + } - resourceAttributeValues := &authz.Resource_AttributeValues{ - Fqns: make([]string, 0), - } - for _, aav := range regResValue.GetActionAttributeValues() { - if aav.GetAction().GetName() != action.GetName() { - continue + resourceAttributeValues = &authz.Resource_AttributeValues{ + Fqns: make([]string, 0), } + for _, aav := range regResValue.GetActionAttributeValues() { + if aav.GetAction().GetName() != action.GetName() { + continue + } - aavAttrValueFQN := aav.GetAttributeValue().GetFqn() - if !slices.Contains(resourceAttributeValues.Fqns, aavAttrValueFQN) { - resourceAttributeValues.Fqns = append(resourceAttributeValues.Fqns, aavAttrValueFQN) + aavAttrValueFQN := aav.GetAttributeValue().GetFqn() + if !slices.Contains(resourceAttributeValues.Fqns, aavAttrValueFQN) { + resourceAttributeValues.Fqns = append(resourceAttributeValues.Fqns, aavAttrValueFQN) + } } + + case *authz.Resource_AttributeValues_: + resourceAttributeValues = resource.GetAttributeValues() + + default: + return nil, fmt.Errorf("unsupported resource type: %w", ErrInvalidResource) } - return evaluateResourceAttributeValues(ctx, logger, resourceAttributeValues, resource.GetEphemeralId(), action, entitlements, accessibleAttributeValues) + return evaluateResourceAttributeValues(ctx, logger, resourceAttributeValues, resourceID, action, entitlements, accessibleAttributeValues) } // evaluateResourceAttributeValues evaluates a list of attribute values against the action and entitlements diff --git a/service/internal/access/v2/evaluate_test.go b/service/internal/access/v2/evaluate_test.go index 5e2dee2aab..ed8d08a42c 100644 --- a/service/internal/access/v2/evaluate_test.go +++ b/service/internal/access/v2/evaluate_test.go @@ -54,10 +54,11 @@ type EvaluateTestSuite struct { action *policy.Action // Common test data - hierarchicalClassAttr *policy.Attribute - allOfProjectAttr *policy.Attribute - anyOfDepartmentAttr *policy.Attribute - accessibleAttrValues map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue + hierarchicalClassAttr *policy.Attribute + allOfProjectAttr *policy.Attribute + anyOfDepartmentAttr *policy.Attribute + accessibleAttrValues map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue + accessibleRegisteredResourceValues map[string]*policy.RegisteredResourceValue } func (s *EvaluateTestSuite) SetupTest() { @@ -152,6 +153,10 @@ func (s *EvaluateTestSuite) SetupTest() { Value: &policy.Value{Fqn: projectFantasicFourFQN}, }, } + + // Setup accessible registered resource values map + // TODO: revisit + s.accessibleRegisteredResourceValues = map[string]*policy.RegisteredResourceValue{} } func TestEvaluateSuite(t *testing.T) { @@ -780,6 +785,7 @@ func (s *EvaluateTestSuite) TestGetResourceDecision() { s.T().Context(), s.logger, s.accessibleAttrValues, + s.accessibleRegisteredResourceValues, tc.entitlements, s.action, tc.resource, diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 5cad9ed23b..2c92f3802e 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -190,7 +190,7 @@ func (p *PolicyDecisionPoint) GetDecision( } for idx, resource := range resources { - resourceDecision, err := getResourceDecision(ctx, p.logger, decisionableAttributes, entitledFQNsToActions, action, resource) + resourceDecision, err := getResourceDecision(ctx, p.logger, decisionableAttributes, p.allRegisteredResourceValuesByFQN, entitledFQNsToActions, action, resource) if err != nil || resourceDecision == nil { return nil, fmt.Errorf("error evaluating a decision on resource [%v]: %w", resource, err) } @@ -268,7 +268,7 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( } for idx, resource := range resources { - resourceDecision, err := getResourceDecisionRegisteredResource(ctx, p.logger, decisionableAttributes, p.allRegisteredResourceValuesByFQN, entitledFQNsToActions, action, resource) + resourceDecision, err := getResourceDecision(ctx, p.logger, decisionableAttributes, p.allRegisteredResourceValuesByFQN, entitledFQNsToActions, action, resource) if err != nil || resourceDecision == nil { return nil, fmt.Errorf("error evaluating a decision on resource [%v]: %w", resource, err) } diff --git a/service/internal/access/v2/validators.go b/service/internal/access/v2/validators.go index 9b61db8e2a..e49c9d6509 100644 --- a/service/internal/access/v2/validators.go +++ b/service/internal/access/v2/validators.go @@ -194,35 +194,3 @@ func validateGetResourceDecision( } return nil } - -// validateGetResourceDecisionRegisteredResource validates the parameters for an access decision on a registered resource -// -// - accessibleAttributeValues: must not be nil -// - accessibleRegisteredResourceValues: must not be nil -// - entitlements: must not be nil -// - action: must not be nil -// - resource: must not be nil -func validateGetResourceDecisionRegisteredResource( - accessibleAttributeValues map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue, - accessibleRegisteredResourceValues map[string]*policy.RegisteredResourceValue, - entitlements subjectmappingbuiltin.AttributeValueFQNsToActions, - action *policy.Action, - resource *authzV2.Resource, -) error { - if entitlements == nil { - return fmt.Errorf("entitled FQNs to actions are nil: %w", ErrInvalidEntitledFQNsToActions) - } - if action.GetName() == "" { - return fmt.Errorf("action name required: %w", ErrInvalidAction) - } - if resource.GetResource() == nil { - return fmt.Errorf("resource is nil: %w", ErrInvalidResource) - } - if len(accessibleAttributeValues) == 0 { - return fmt.Errorf("accessible attribute values are empty: %w", ErrMissingRequiredPolicy) - } - if len(accessibleRegisteredResourceValues) == 0 { - return fmt.Errorf("accessible registered resource values are empty: %w", ErrMissingRequiredPolicy) - } - return nil -} From c33ba54a47022bca5a604c7c449f6c56ab74742c Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Thu, 12 Jun 2025 10:25:09 -0600 Subject: [PATCH 28/55] move getResourceDecisionableAttributes func to helpers file for easier testing --- service/internal/access/v2/helpers.go | 77 +++++++++++++++++++++++++++ service/internal/access/v2/pdp.go | 73 +------------------------ 2 files changed, 79 insertions(+), 71 deletions(-) diff --git a/service/internal/access/v2/helpers.go b/service/internal/access/v2/helpers.go index 4fde2d01b8..d96101912d 100644 --- a/service/internal/access/v2/helpers.go +++ b/service/internal/access/v2/helpers.go @@ -5,6 +5,8 @@ import ( "errors" "fmt" "log/slog" + "strconv" + "strings" "github.com/opentdf/platform/lib/identifier" authz "github.com/opentdf/platform/protocol/go/authorization/v2" @@ -183,3 +185,78 @@ func mergeDeduplicatedActions(actionsSet map[string]*policy.Action, actionsToMer return merged } + +func getResourceDecisionableAttributes( + ctx context.Context, + logger *logger.Logger, + accessibleRegisteredResourceValues map[string]*policy.RegisteredResourceValue, + entitleableAttributesByValueFQN map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue, + action *policy.Action, + resources []*authz.Resource, +) (map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue, error) { + var ( + decisionableAttributes = make(map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue) + attrValueFQNs = make([]string, 0) + ) + + // Parse attribute value FQNs from various resource types + for idx, resource := range resources { + // Assign indexed ephemeral ID for resource if not already set + if resource.GetEphemeralId() == "" { + resource.EphemeralId = "resource-" + strconv.Itoa(idx) + } + + switch resource.GetResource().(type) { + case *authz.Resource_RegisteredResourceValueFqn: + regResValueFQN := strings.ToLower(resource.GetRegisteredResourceValueFqn()) + regResValue, found := accessibleRegisteredResourceValues[regResValueFQN] + if !found { + return nil, fmt.Errorf("resource registered resource value FQN not found in memory [%s]: %w", regResValueFQN, ErrInvalidResource) + } + + for _, aav := range regResValue.GetActionAttributeValues() { + aavAction := aav.GetAction() + if aavAction.GetName() != action.GetName() { + logger.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("action", aavAction.GetName())) + continue + } + + attrValueFQNs = append(attrValueFQNs, aav.GetAttributeValue().GetFqn()) + } + + case *authz.Resource_AttributeValues_: + for idx, attrValueFQN := range resource.GetAttributeValues().GetFqns() { + // lowercase each resource attribute value FQN for case consistent map key lookups + attrValueFQN = strings.ToLower(attrValueFQN) + resource.GetAttributeValues().Fqns[idx] = attrValueFQN + + attrValueFQNs = append(attrValueFQNs, attrValueFQN) + } + + default: + // default should never happen as we validate above + return nil, fmt.Errorf("invalid resource type [%T]: %w", resource.GetResource(), ErrInvalidResource) + } + } + + // determine decisionable attributes based on the attribute value FQNs + for _, attrValueFQN := range attrValueFQNs { + // If same value FQN more than once, skip (dedupe) + if _, ok := decisionableAttributes[attrValueFQN]; ok { + continue + } + + attributeAndValue, ok := entitleableAttributesByValueFQN[attrValueFQN] + if !ok { + return nil, fmt.Errorf("resource attribute value FQN not found in memory [%s]: %w", attrValueFQN, ErrInvalidResource) + } + + decisionableAttributes[attrValueFQN] = attributeAndValue + err := populateHigherValuesIfHierarchy(ctx, logger, attrValueFQN, attributeAndValue.GetAttribute(), entitleableAttributesByValueFQN, decisionableAttributes) + if err != nil { + return nil, fmt.Errorf("error populating higher hierarchy attribute values: %w", err) + } + } + + return decisionableAttributes, nil +} diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 2c92f3802e..434968f548 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -7,7 +7,6 @@ import ( "log/slog" "slices" "strconv" - "strings" "github.com/opentdf/platform/lib/identifier" authz "github.com/opentdf/platform/protocol/go/authorization/v2" @@ -171,7 +170,7 @@ func (p *PolicyDecisionPoint) GetDecision( } // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources - decisionableAttributes, err := p.getResourceDecisionableAttributes(ctx, l, action, resources) + decisionableAttributes, err := getResourceDecisionableAttributes(ctx, l, p.allRegisteredResourceValuesByFQN, p.allEntitleableAttributesByValueFQN, action, resources) if err != nil { return nil, fmt.Errorf("error getting decisionable attributes: %w", err) } @@ -232,7 +231,7 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( } // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources - decisionableAttributes, err := p.getResourceDecisionableAttributes(ctx, l, action, resources) + decisionableAttributes, err := getResourceDecisionableAttributes(ctx, l, p.allRegisteredResourceValuesByFQN, p.allEntitleableAttributesByValueFQN, action, resources) if err != nil { return nil, fmt.Errorf("error getting decisionable attributes: %w", err) } @@ -423,71 +422,3 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( return result, nil } - -func (p *PolicyDecisionPoint) getResourceDecisionableAttributes(ctx context.Context, logger *logger.Logger, action *policy.Action, resources []*authz.Resource) (map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue, error) { - var ( - decisionableAttributes = make(map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue) - attrValueFQNs = make([]string, 0) - ) - - // Parse attribute value FQNs from various resource types - for idx, resource := range resources { - // Assign indexed ephemeral ID for resource if not already set - if resource.GetEphemeralId() == "" { - resource.EphemeralId = "resource-" + strconv.Itoa(idx) - } - - switch resource.GetResource().(type) { - case *authz.Resource_RegisteredResourceValueFqn: - regResValueFQN := strings.ToLower(resource.GetRegisteredResourceValueFqn()) - regResValue, found := p.allRegisteredResourceValuesByFQN[regResValueFQN] - if !found { - return nil, fmt.Errorf("resource registered resource value FQN not found in memory [%s]: %w", regResValueFQN, ErrInvalidResource) - } - - for _, aav := range regResValue.GetActionAttributeValues() { - aavAction := aav.GetAction() - if aavAction.GetName() != action.GetName() { - logger.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("action", aavAction.GetName())) - continue - } - - attrValueFQNs = append(attrValueFQNs, aav.GetAttributeValue().GetFqn()) - } - - case *authz.Resource_AttributeValues_: - for idx, attrValueFQN := range resource.GetAttributeValues().GetFqns() { - // lowercase each resource attribute value FQN for case consistent map key lookups - attrValueFQN = strings.ToLower(attrValueFQN) - resource.GetAttributeValues().Fqns[idx] = attrValueFQN - - attrValueFQNs = append(attrValueFQNs, attrValueFQN) - } - - default: - // default should never happen as we validate above - return nil, fmt.Errorf("invalid resource type [%T]: %w", resource.GetResource(), ErrInvalidResource) - } - } - - // determine decisionable attributes based on the attribute value FQNs - for _, attrValueFQN := range attrValueFQNs { - // If same value FQN more than once, skip (dedupe) - if _, ok := decisionableAttributes[attrValueFQN]; ok { - continue - } - - attributeAndValue, ok := p.allEntitleableAttributesByValueFQN[attrValueFQN] - if !ok { - return nil, fmt.Errorf("resource attribute value FQN not found in memory [%s]: %w", attrValueFQN, ErrInvalidResource) - } - - decisionableAttributes[attrValueFQN] = attributeAndValue - err := populateHigherValuesIfHierarchy(ctx, logger, attrValueFQN, attributeAndValue.GetAttribute(), p.allEntitleableAttributesByValueFQN, decisionableAttributes) - if err != nil { - return nil, fmt.Errorf("error populating higher hierarchy attribute values: %w", err) - } - } - - return decisionableAttributes, nil -} From f609ab2a2350db0acafd50f0f6ac231615628b61 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Mon, 16 Jun 2025 10:18:54 -0400 Subject: [PATCH 29/55] add reg res to existing tests for multi resource GetDecision --- service/internal/access/v2/evaluate.go | 9 +- service/internal/access/v2/helpers.go | 13 ++- service/internal/access/v2/pdp_test.go | 139 +++++++++++++++++++------ 3 files changed, 121 insertions(+), 40 deletions(-) diff --git a/service/internal/access/v2/evaluate.go b/service/internal/access/v2/evaluate.go index 75330322f1..777fcd235e 100644 --- a/service/internal/access/v2/evaluate.go +++ b/service/internal/access/v2/evaluate.go @@ -62,9 +62,12 @@ func getResourceDecision( Fqns: make([]string, 0), } for _, aav := range regResValue.GetActionAttributeValues() { - if aav.GetAction().GetName() != action.GetName() { - continue - } + // todo: revisit this logic -- reg res' are different from attr values since they can be both entity and resource + // and are tied to actions and attribute values + // + // if aav.GetAction().GetName() != action.GetName() { + // continue + // } aavAttrValueFQN := aav.GetAttributeValue().GetFqn() if !slices.Contains(resourceAttributeValues.Fqns, aavAttrValueFQN) { diff --git a/service/internal/access/v2/helpers.go b/service/internal/access/v2/helpers.go index d96101912d..323249b5de 100644 --- a/service/internal/access/v2/helpers.go +++ b/service/internal/access/v2/helpers.go @@ -215,11 +215,14 @@ func getResourceDecisionableAttributes( } for _, aav := range regResValue.GetActionAttributeValues() { - aavAction := aav.GetAction() - if aavAction.GetName() != action.GetName() { - logger.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("action", aavAction.GetName())) - continue - } + slog.Info("processing action attribute value", slog.Any("aav", aav)) + + // todo: revist this logic, bc it is causing failures for attributes with missing actions + // aavAction := aav.GetAction() + // if aavAction.GetName() != action.GetName() { + // logger.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("action", aavAction.GetName())) + // continue + // } attrValueFQNs = append(attrValueFQNs, aav.GetAttributeValue().GetFqn()) } diff --git a/service/internal/access/v2/pdp_test.go b/service/internal/access/v2/pdp_test.go index 9ded45b944..034eacb133 100644 --- a/service/internal/access/v2/pdp_test.go +++ b/service/internal/access/v2/pdp_test.go @@ -86,6 +86,10 @@ var ( testPlatformCloudFQN = createAttrValueFQN(testSecondaryNamespace, "platform", "cloud") testPlatformOnPremFQN = createAttrValueFQN(testSecondaryNamespace, "platform", "onprem") testPlatformHybridFQN = createAttrValueFQN(testSecondaryNamespace, "platform", "hybrid") + + // Registered resource value FQNs + testNetworkPrivateFQN = createRegisteredResourceValueFQN("network", "private") + testNetworkPublicFQN = createRegisteredResourceValueFQN("network", "public") ) // Registered resource value FQNs using identifier package @@ -137,6 +141,9 @@ type PDPTestSuite struct { analystEntity *entityresolutionV2.EntityRepresentation // Test registered resources + networkRegRes *policy.RegisteredResource + + // Test registered resources (todo: replace with above real use cases) regRes *policy.RegisteredResource regResValNoActionAttrVal *policy.RegisteredResourceValue regResValSingleActionAttrVal *policy.RegisteredResourceValue @@ -354,6 +361,44 @@ func (s *PDPTestSuite) SetupTest() { }) // Initialize test registered resources + s.fixtures.networkRegRes = &policy.RegisteredResource{ + Name: "network", + Values: []*policy.RegisteredResourceValue{ + { + Value: "private", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassSecretFQN, + Value: "secret", + }, + }, + { + Action: testActionUpdate, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + }, + }, + { + Value: "public", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + }, + }, + }, + } + + // Initialize test registered resources (todo: replace with above real use cases) regResValNoActionAttrVal := &policy.RegisteredResourceValue{ Value: "no-action-attr-val", ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{}, @@ -549,8 +594,8 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { s.T().Context(), s.logger, []*policy.Attribute{f.classificationAttr, f.departmentAttr}, - []*policy.SubjectMapping{f.secretMapping, f.topSecretMapping, f.confidentialMapping, f.engineeringMapping, f.financeMapping}, - []*policy.RegisteredResource{}, + []*policy.SubjectMapping{f.secretMapping, f.topSecretMapping, f.confidentialMapping, f.publicMapping, f.engineeringMapping, f.financeMapping}, + []*policy.RegisteredResource{f.networkRegRes}, ) s.Require().NoError(err) s.Require().NotNil(pdp) @@ -561,18 +606,20 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { "department": "engineering", }) - resources := createResourcePerFqn(testClassSecretFQN, testDeptEngineeringFQN) + resources := createResourcePerFqn(testClassSecretFQN, testDeptEngineeringFQN, testNetworkPrivateFQN, testNetworkPublicFQN) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionRead, resources) s.Require().NoError(err) s.Require().NotNil(decision) s.True(decision.Access) - s.Len(decision.Results, 2) + s.Len(decision.Results, 4) expectedResults := map[string]bool{ testClassSecretFQN: true, testDeptEngineeringFQN: true, + testNetworkPrivateFQN: true, + testNetworkPublicFQN: true, } s.assertAllDecisionResults(decision, expectedResults) for _, result := range decision.Results { @@ -588,19 +635,22 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { "department": "engineering", }) secretFQN := strings.ToUpper(testClassSecretFQN) + networkPrivateFQN := strings.ToUpper(testNetworkPrivateFQN) - resources := createResourcePerFqn(secretFQN, testDeptEngineeringFQN) + resources := createResourcePerFqn(secretFQN, testDeptEngineeringFQN, networkPrivateFQN, testNetworkPublicFQN) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionRead, resources) s.Require().NoError(err) s.Require().NotNil(decision) s.True(decision.Access) - s.Len(decision.Results, 2) + s.Len(decision.Results, 4) expectedResults := map[string]bool{ secretFQN: true, testDeptEngineeringFQN: true, + networkPrivateFQN: true, + testNetworkPublicFQN: true, } s.assertAllDecisionResults(decision, expectedResults) for _, result := range decision.Results { @@ -616,18 +666,19 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { "department": "finance", // Not engineering }) - resources := createResourcePerFqn(testClassSecretFQN, testDeptEngineeringFQN) + resources := createResourcePerFqn(testClassSecretFQN, testDeptEngineeringFQN, testNetworkPrivateFQN) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionUpdate, resources) s.Require().NoError(err) s.Require().NotNil(decision) s.False(decision.Access) - s.Len(decision.Results, 2) + s.Len(decision.Results, 3) expectedResults := map[string]bool{ testClassSecretFQN: false, testDeptEngineeringFQN: false, + testNetworkPrivateFQN: false, } s.assertAllDecisionResults(decision, expectedResults) @@ -644,7 +695,7 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { "department": "engineering", }) - resources := createResourcePerFqn(testDeptEngineeringFQN, testClassSecretFQN) + resources := createResourcePerFqn(testDeptEngineeringFQN, testClassSecretFQN, testNetworkPrivateFQN, testNetworkPublicFQN) // Get decision for delete action (not allowed by either attribute's subject mappings) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionDelete, resources) @@ -652,11 +703,13 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { s.Require().NoError(err) s.Require().NotNil(decision) s.False(decision.Access) - s.Len(decision.Results, 2) + s.Len(decision.Results, 4) expectedResults := map[string]bool{ testDeptEngineeringFQN: false, testClassSecretFQN: false, + testNetworkPrivateFQN: false, + testNetworkPublicFQN: false, } s.assertAllDecisionResults(decision, expectedResults) }) @@ -667,18 +720,20 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { "department": "engineering", // not finance }) - resources := createResourcePerFqn(testClassSecretFQN, testDeptFinanceFQN) + resources := createResourcePerFqn(testClassSecretFQN, testDeptFinanceFQN, testNetworkPrivateFQN, testNetworkPublicFQN) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionRead, resources) s.Require().NoError(err) s.Require().NotNil(decision) s.False(decision.Access) // False because one resource is denied - s.Len(decision.Results, 2) + s.Len(decision.Results, 4) expectedResults := map[string]bool{ - testClassSecretFQN: true, - testDeptFinanceFQN: false, + testClassSecretFQN: true, + testDeptFinanceFQN: false, + testNetworkPrivateFQN: true, + testNetworkPublicFQN: true, } s.assertAllDecisionResults(decision, expectedResults) @@ -746,7 +801,7 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { f.secretMapping, f.topSecretMapping, printConfidentialMapping, allActionsPublicMapping, f.engineeringMapping, f.financeMapping, viewProjectAlphaMapping, }, - []*policy.RegisteredResource{}, + []*policy.RegisteredResource{f.networkRegRes}, ) s.Require().NoError(err) s.Require().NotNil(pdp) @@ -758,7 +813,7 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { }) // Resource to evaluate - resources := createResourcePerFqn(testClassSecretFQN) + resources := createResourcePerFqn(testClassSecretFQN, testNetworkPrivateFQN) decision, err := pdp.GetDecision(s.T().Context(), entity, actionRead, resources) @@ -766,14 +821,14 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { s.Require().NoError(err) s.Require().NotNil(decision) s.True(decision.Access) // Should be true because read is allowed - s.Len(decision.Results, 1) + s.Len(decision.Results, 2) // Create should fail decision, err = pdp.GetDecision(s.T().Context(), entity, actionCreate, resources) s.Require().NoError(err) s.Require().NotNil(decision) s.False(decision.Access) // Should be false because create is not allowed - s.Len(decision.Results, 1) + s.Len(decision.Results, 2) }) s.Run("Scenario 2: User has overlapping action sets", func() { @@ -783,7 +838,7 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { "department": "finance", }) - combinedResource := createResource("combined-attr-resource", testClassConfidentialFQN, testDeptFinanceFQN) + combinedResource := createAttributeValueResource("combined-attr-resource", testClassConfidentialFQN, testDeptFinanceFQN) // Test read access - should be allowed by both attributes decision, err := pdp.GetDecision(s.T().Context(), entity, actionRead, []*authz.Resource{combinedResource}) @@ -918,7 +973,7 @@ func (s *PDPTestSuite) Test_GetDecision_CombinedAttributeRules_SingleResource() }) // Single resource with both HIERARCHY (classification) and ANY_OF (department) attributes - combinedResource := createResource("secret-engineering-resource", testClassSecretFQN, testDeptEngineeringFQN) + combinedResource := createAttributeValueResource("secret-engineering-resource", testClassSecretFQN, testDeptEngineeringFQN) // Test read access (both allow) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionRead, []*authz.Resource{combinedResource}) @@ -947,7 +1002,7 @@ func (s *PDPTestSuite) Test_GetDecision_CombinedAttributeRules_SingleResource() }) // Single resource with both HIERARCHY and ALL_OF attributes - combinedResource := createResource("secret-usa-resource", testClassSecretFQN, testCountryUSAFQN) + combinedResource := createAttributeValueResource("secret-usa-resource", testClassSecretFQN, testCountryUSAFQN) // Test read access (both allow) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionRead, []*authz.Resource{combinedResource}) @@ -970,7 +1025,7 @@ func (s *PDPTestSuite) Test_GetDecision_CombinedAttributeRules_SingleResource() }) // Single resource with both ANY_OF and ALL_OF attributes - combinedResource := createResource("engineering-usa-uk-resource", testDeptEngineeringFQN, testCountryUSAFQN, testCountryUKFQN) + combinedResource := createAttributeValueResource("engineering-usa-uk-resource", testDeptEngineeringFQN, testCountryUSAFQN, testCountryUKFQN) // Test read access (both allow) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionRead, []*authz.Resource{combinedResource}) @@ -994,7 +1049,7 @@ func (s *PDPTestSuite) Test_GetDecision_CombinedAttributeRules_SingleResource() }) // Single resource with all three attribute rule types, but missing one ALL_OF value FQN - combinedResource := createResource("secret-engineering-usa-uk-resource", testClassSecretFQN, testDeptEngineeringFQN, testCountryUKFQN, testCountryUSAFQN) + combinedResource := createAttributeValueResource("secret-engineering-usa-uk-resource", testClassSecretFQN, testDeptEngineeringFQN, testCountryUKFQN, testCountryUSAFQN) // Test read access (all three allow) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionRead, []*authz.Resource{combinedResource}) @@ -1029,7 +1084,7 @@ func (s *PDPTestSuite) Test_GetDecision_CombinedAttributeRules_SingleResource() }) // Single resource with all three attribute rule types - combinedResource := createResource("secret-engineering-usa-resource", testClassSecretFQN, testDeptEngineeringFQN, testCountryUSAFQN) + combinedResource := createAttributeValueResource("secret-engineering-usa-resource", testClassSecretFQN, testDeptEngineeringFQN, testCountryUSAFQN) // Test read access (all three allow) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionRead, []*authz.Resource{combinedResource}) @@ -1055,7 +1110,7 @@ func (s *PDPTestSuite) Test_GetDecision_CombinedAttributeRules_SingleResource() }) // Resource with all three attribute types - combinedResource := createResource("secret-engineering-usa-resource", testClassSecretFQN, testDeptEngineeringFQN, testCountryUSAFQN) + combinedResource := createAttributeValueResource("secret-engineering-usa-resource", testClassSecretFQN, testDeptEngineeringFQN, testCountryUSAFQN) // Test read access - should fail because department doesn't match decision, err := pdp.GetDecision(s.T().Context(), entity, testActionRead, []*authz.Resource{combinedResource}) @@ -1094,7 +1149,7 @@ func (s *PDPTestSuite) Test_GetDecision_CombinedAttributeRules_SingleResource() }) // Resource with attributes from different namespaces and with different rules - complexResource := createResource("complex-multi-ns-resource", + complexResource := createAttributeValueResource("complex-multi-ns-resource", testClassSecretFQN, // HIERARCHY rule testCountryUSAFQN, // ALL_OF rule testProjectAlphaFQN, // ANY_OF rule @@ -1180,7 +1235,7 @@ func (s *PDPTestSuite) Test_GetDecision_CombinedAttributeRules_SingleResource() s.Run("Multiple HIERARCHY of different levels", func() { // Create a resource with multiple classifications (hierarchy rule) - cascadingResource := createResource("classification-cascade-resource", + cascadingResource := createAttributeValueResource("classification-cascade-resource", testClassSecretFQN, // Secret classification testClassConfidentialFQN, // Confidential classification (lower than Secret) ) @@ -1303,7 +1358,7 @@ func (s *PDPTestSuite) Test_GetDecision_AcrossNamespaces() { }) // Resource with attribute values from two different namespaces - resource := createResource("secret-alpha-cloud-fqn", testClassSecretFQN, testProjectAlphaFQN, testPlatformCloudFQN) + resource := createAttributeValueResource("secret-alpha-cloud-fqn", testClassSecretFQN, testProjectAlphaFQN, testPlatformCloudFQN) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionRead, []*authz.Resource{resource}) @@ -1489,7 +1544,7 @@ func (s *PDPTestSuite) Test_GetDecision_AcrossNamespaces() { }) // A single resource with attribute values from different namespaces - combinedResource := createResource("combined-multi-ns-resource", + combinedResource := createAttributeValueResource("combined-multi-ns-resource", testClassConfidentialFQN, // base namespace testCountryUSAFQN, // base namespace testProjectBetaFQN, // secondary namespace @@ -2134,8 +2189,8 @@ func (s *PDPTestSuite) createEntityWithProps(entityID string, props map[string]i } } -// createResource creates a resource with attribute values -func createResource(ephemeralID string, attributeValueFQNs ...string) *authz.Resource { +// createAttributeValueResource creates a resource with attribute values +func createAttributeValueResource(ephemeralID string, attributeValueFQNs ...string) *authz.Resource { return &authz.Resource{ EphemeralId: ephemeralID, Resource: &authz.Resource_AttributeValues_{ @@ -2146,12 +2201,32 @@ func createResource(ephemeralID string, attributeValueFQNs ...string) *authz.Res } } +// createRegisteredResource creates a resource with registered resource value FQN +func createRegisteredResource(ephemeralID string, registeredResourceValueFQN string) *authz.Resource { + return &authz.Resource{ + EphemeralId: ephemeralID, + Resource: &authz.Resource_RegisteredResourceValueFqn{ + RegisteredResourceValueFqn: registeredResourceValueFQN, + }, + } +} + // createResourcePerFqn creates multiple resources, one for each attribute value FQN func createResourcePerFqn(attributeValueFQNs ...string) []*authz.Resource { resources := make([]*authz.Resource, len(attributeValueFQNs)) for i, fqn := range attributeValueFQNs { // Use the FQN itself as the resource ID instead of a generic "ephemeral-id-X" - resources[i] = createResource(fqn, fqn) + resourceID := fqn + + // todo: identifier lib does not do case-insensitive parsing, so we need to ensure FQNs are lowercased + // should maybe be fixed in the identifier library? + if _, err := identifier.Parse[*identifier.FullyQualifiedRegisteredResourceValue](strings.ToLower(fqn)); err == nil { + // FQN is a registered resource value + resources[i] = createRegisteredResource(resourceID, fqn) + } else { + // FQN is an attribute value + resources[i] = createAttributeValueResource(resourceID, fqn) + } } return resources } From 85bfb86aee1e8f605e2b33738607d154b4d64533 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 17 Jun 2025 18:27:24 -0400 Subject: [PATCH 30/55] update multi-resource tests to use reg res that match subject mapppings and checkpoint before moving to refactor others --- service/internal/access/v2/pdp_test.go | 702 +++++++++++++++++++++++-- 1 file changed, 657 insertions(+), 45 deletions(-) diff --git a/service/internal/access/v2/pdp_test.go b/service/internal/access/v2/pdp_test.go index 034eacb133..45637a7f3d 100644 --- a/service/internal/access/v2/pdp_test.go +++ b/service/internal/access/v2/pdp_test.go @@ -87,9 +87,40 @@ var ( testPlatformOnPremFQN = createAttrValueFQN(testSecondaryNamespace, "platform", "onprem") testPlatformHybridFQN = createAttrValueFQN(testSecondaryNamespace, "platform", "hybrid") - // Registered resource value FQNs - testNetworkPrivateFQN = createRegisteredResourceValueFQN("network", "private") - testNetworkPublicFQN = createRegisteredResourceValueFQN("network", "public") + // Registered resource value FQNs (todo: remove) + testNetworkPrivateFQN = createRegisteredResourceValueFQN("network", "private") + testNetworkPublicFQN = createRegisteredResourceValueFQN("network", "public") + testNetworkConfidentialFQN = createRegisteredResourceValueFQN("network", "confidential") + testNetworkAlphaFQN = createRegisteredResourceValueFQN("network", "alpha") +) + +// registered resource value FQNs using identifier package +var ( + // Classification values + testClassTopSecretRegResFQN = createRegisteredResourceValueFQN("classification", "topsecret") + testClassSecretRegResFQN = createRegisteredResourceValueFQN("classification", "secret") + testClassConfidentialRegResFQN = createRegisteredResourceValueFQN("classification", "confidential") + testClassPublicRegResFQN = createRegisteredResourceValueFQN("classification", "public") + + // Department values + testDeptRnDRegResFQN = createRegisteredResourceValueFQN("department", "rnd") + testDeptEngineeringRegResFQN = createRegisteredResourceValueFQN("department", "engineering") + testDeptSalesRegResFQN = createRegisteredResourceValueFQN("department", "sales") + testDeptFinanceRegResFQN = createRegisteredResourceValueFQN("department", "finance") + + // Country values + testCountryUSARegResFQN = createRegisteredResourceValueFQN("country", "usa") + testCountryUKRegResFQN = createRegisteredResourceValueFQN("country", "uk") + + // Project values in secondary namespace + testProjectAlphaRegResFQN = createRegisteredResourceValueFQN("project", "alpha") + testProjectBetaRegResFQN = createRegisteredResourceValueFQN("project", "beta") + testProjectGammaRegResFQN = createRegisteredResourceValueFQN("project", "gamma") + + // Platform values in secondary namespace + testPlatformCloudRegResFQN = createRegisteredResourceValueFQN("platform", "cloud") + testPlatformOnPremRegResFQN = createRegisteredResourceValueFQN("platform", "onprem") + testPlatformHybridRegResFQN = createRegisteredResourceValueFQN("platform", "hybrid") ) // Registered resource value FQNs using identifier package @@ -141,7 +172,12 @@ type PDPTestSuite struct { analystEntity *entityresolutionV2.EntityRepresentation // Test registered resources - networkRegRes *policy.RegisteredResource + classificationRegRes *policy.RegisteredResource + deptRegRes *policy.RegisteredResource + networkRegRes *policy.RegisteredResource + countryRegRes *policy.RegisteredResource + projectRegRes *policy.RegisteredResource + platformRegRes *policy.RegisteredResource // Test registered resources (todo: replace with above real use cases) regRes *policy.RegisteredResource @@ -343,6 +379,231 @@ func (s *PDPTestSuite) SetupTest() { []string{"cloud"}, ) + // Initialize registered resources + s.fixtures.classificationRegRes = &policy.RegisteredResource{ + Name: "classification", + Values: []*policy.RegisteredResourceValue{ + { + Value: "topsecret", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassTopSecretFQN, + Value: "topsecret", + }, + }, + }, + }, + { + Value: "secret", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassSecretFQN, + Value: "secret", + }, + }, + { + Action: testActionUpdate, + AttributeValue: &policy.Value{ + Fqn: testClassSecretFQN, + Value: "secret", + }, + }, + }, + }, + { + Value: "confidential", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassConfidentialFQN, + Value: "confidential", + }, + }, + }, + }, + { + Value: "public", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + }, + }, + }, + } + + s.fixtures.deptRegRes = &policy.RegisteredResource{ + Name: "department", + Values: []*policy.RegisteredResourceValue{ + { + Value: "rnd", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testDeptRnDFQN, + Value: "rnd", + }, + }, + { + Action: testActionUpdate, + AttributeValue: &policy.Value{ + Fqn: testDeptRnDFQN, + Value: "rnd", + }, + }, + }, + }, + { + Value: "engineering", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testDeptEngineeringFQN, + Value: "engineering", + }, + }, + { + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testDeptEngineeringFQN, + Value: "engineering", + }, + }, + }, + }, + { + Value: "sales", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{}, + }, + { + Value: "finance", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testDeptFinanceFQN, + Value: "finance", + }, + }, + { + Action: testActionUpdate, + AttributeValue: &policy.Value{ + Fqn: testDeptFinanceFQN, + Value: "finance", + }, + }, + }, + }, + }, + } + + s.fixtures.countryRegRes = &policy.RegisteredResource{ + Name: "country", + Values: []*policy.RegisteredResourceValue{ + { + Value: "usa", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testCountryUSAFQN, + Value: "usa", + }, + }, + }, + }, + { + Value: "uk", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testCountryUKFQN, + Value: "uk", + }, + }, + }, + }, + }, + } + + s.fixtures.projectRegRes = &policy.RegisteredResource{ + Name: "project", + Values: []*policy.RegisteredResourceValue{ + { + Value: "alpha", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testProjectAlphaFQN, + Value: "alpha", + }, + }, + { + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testProjectAlphaFQN, + Value: "alpha", + }, + }, + }, + }, + { + Value: "beta", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{}, + }, + { + Value: "gamma", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{}, + }, + }, + } + + s.fixtures.platformRegRes = &policy.RegisteredResource{ + Name: "platform", + Values: []*policy.RegisteredResourceValue{ + { + Value: "cloud", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testPlatformCloudFQN, + Value: "cloud", + }, + }, + { + Action: testActionDelete, + AttributeValue: &policy.Value{ + Fqn: testPlatformCloudFQN, + Value: "cloud", + }, + }, + }, + }, + { + Value: "onprem", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{}, + }, + { + Value: "hybrid", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{}, + }, + }, + } + // Initialize standard test entities s.fixtures.adminEntity = s.createEntityWithProps("admin-entity", map[string]interface{}{ "clearance": "secret", @@ -377,8 +638,8 @@ func (s *PDPTestSuite) SetupTest() { { Action: testActionUpdate, AttributeValue: &policy.Value{ - Fqn: testClassPublicFQN, - Value: "public", + Fqn: testClassSecretFQN, + Value: "secret", }, }, }, @@ -395,6 +656,44 @@ func (s *PDPTestSuite) SetupTest() { }, }, }, + { + Value: "confidential", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassConfidentialFQN, + Value: "confidential", + }, + }, + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testDeptFinanceFQN, + Value: "finance", + }, + }, + }, + }, + { + Value: "alpha", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testProjectAlphaFQN, + Value: "alpha", + }, + }, + { + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testProjectAlphaFQN, + Value: "alpha", + }, + }, + }, + }, }, } @@ -595,7 +894,7 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { s.logger, []*policy.Attribute{f.classificationAttr, f.departmentAttr}, []*policy.SubjectMapping{f.secretMapping, f.topSecretMapping, f.confidentialMapping, f.publicMapping, f.engineeringMapping, f.financeMapping}, - []*policy.RegisteredResource{f.networkRegRes}, + []*policy.RegisteredResource{f.classificationRegRes, f.deptRegRes}, ) s.Require().NoError(err) s.Require().NotNil(pdp) @@ -606,7 +905,10 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { "department": "engineering", }) - resources := createResourcePerFqn(testClassSecretFQN, testDeptEngineeringFQN, testNetworkPrivateFQN, testNetworkPublicFQN) + resources := createResourcePerFqn( + testClassSecretFQN, testDeptEngineeringFQN, + testClassSecretRegResFQN, testDeptEngineeringRegResFQN, + ) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionRead, resources) @@ -616,10 +918,10 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { s.Len(decision.Results, 4) expectedResults := map[string]bool{ - testClassSecretFQN: true, - testDeptEngineeringFQN: true, - testNetworkPrivateFQN: true, - testNetworkPublicFQN: true, + testClassSecretFQN: true, + testDeptEngineeringFQN: true, + testClassSecretRegResFQN: true, + testDeptEngineeringRegResFQN: true, } s.assertAllDecisionResults(decision, expectedResults) for _, result := range decision.Results { @@ -635,9 +937,9 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { "department": "engineering", }) secretFQN := strings.ToUpper(testClassSecretFQN) - networkPrivateFQN := strings.ToUpper(testNetworkPrivateFQN) + secretRegResFQN := strings.ToUpper(testClassSecretRegResFQN) - resources := createResourcePerFqn(secretFQN, testDeptEngineeringFQN, networkPrivateFQN, testNetworkPublicFQN) + resources := createResourcePerFqn(secretFQN, testDeptEngineeringFQN, secretRegResFQN, testDeptEngineeringRegResFQN) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionRead, resources) @@ -647,10 +949,10 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { s.Len(decision.Results, 4) expectedResults := map[string]bool{ - secretFQN: true, - testDeptEngineeringFQN: true, - networkPrivateFQN: true, - testNetworkPublicFQN: true, + secretFQN: true, + testDeptEngineeringFQN: true, + secretRegResFQN: true, + testDeptEngineeringRegResFQN: true, } s.assertAllDecisionResults(decision, expectedResults) for _, result := range decision.Results { @@ -666,19 +968,23 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { "department": "finance", // Not engineering }) - resources := createResourcePerFqn(testClassSecretFQN, testDeptEngineeringFQN, testNetworkPrivateFQN) + resources := createResourcePerFqn( + testClassSecretFQN, testDeptEngineeringFQN, + testClassSecretRegResFQN, testDeptEngineeringRegResFQN, + ) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionUpdate, resources) s.Require().NoError(err) s.Require().NotNil(decision) s.False(decision.Access) - s.Len(decision.Results, 3) + s.Len(decision.Results, 4) expectedResults := map[string]bool{ - testClassSecretFQN: false, - testDeptEngineeringFQN: false, - testNetworkPrivateFQN: false, + testClassSecretFQN: false, + testDeptEngineeringFQN: false, + testClassSecretRegResFQN: false, + testDeptEngineeringRegResFQN: false, } s.assertAllDecisionResults(decision, expectedResults) @@ -695,7 +1001,9 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { "department": "engineering", }) - resources := createResourcePerFqn(testDeptEngineeringFQN, testClassSecretFQN, testNetworkPrivateFQN, testNetworkPublicFQN) + resources := createResourcePerFqn( + testDeptEngineeringFQN, testClassSecretFQN, + testDeptEngineeringRegResFQN, testClassSecretRegResFQN) // Get decision for delete action (not allowed by either attribute's subject mappings) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionDelete, resources) @@ -706,10 +1014,10 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { s.Len(decision.Results, 4) expectedResults := map[string]bool{ - testDeptEngineeringFQN: false, - testClassSecretFQN: false, - testNetworkPrivateFQN: false, - testNetworkPublicFQN: false, + testDeptEngineeringFQN: false, + testClassSecretFQN: false, + testDeptEngineeringRegResFQN: false, + testClassSecretRegResFQN: false, } s.assertAllDecisionResults(decision, expectedResults) }) @@ -720,7 +1028,10 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { "department": "engineering", // not finance }) - resources := createResourcePerFqn(testClassSecretFQN, testDeptFinanceFQN, testNetworkPrivateFQN, testNetworkPublicFQN) + resources := createResourcePerFqn( + testClassSecretFQN, testDeptFinanceFQN, + testClassSecretRegResFQN, testDeptFinanceRegResFQN, + ) decision, err := pdp.GetDecision(s.T().Context(), entity, testActionRead, resources) @@ -730,10 +1041,10 @@ func (s *PDPTestSuite) Test_GetDecision_MultipleResources() { s.Len(decision.Results, 4) expectedResults := map[string]bool{ - testClassSecretFQN: true, - testDeptFinanceFQN: false, - testNetworkPrivateFQN: true, - testNetworkPublicFQN: true, + testClassSecretFQN: true, + testDeptFinanceFQN: false, + testClassSecretRegResFQN: true, + testDeptFinanceRegResFQN: false, } s.assertAllDecisionResults(decision, expectedResults) @@ -801,7 +1112,7 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { f.secretMapping, f.topSecretMapping, printConfidentialMapping, allActionsPublicMapping, f.engineeringMapping, f.financeMapping, viewProjectAlphaMapping, }, - []*policy.RegisteredResource{f.networkRegRes}, + []*policy.RegisteredResource{f.classificationRegRes, f.deptRegRes, f.projectRegRes}, ) s.Require().NoError(err) s.Require().NotNil(pdp) @@ -817,7 +1128,7 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { decision, err := pdp.GetDecision(s.T().Context(), entity, actionRead, resources) - // Read shuld pass + // Read should pass s.Require().NoError(err) s.Require().NotNil(decision) s.True(decision.Access) // Should be true because read is allowed @@ -839,34 +1150,35 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { }) combinedResource := createAttributeValueResource("combined-attr-resource", testClassConfidentialFQN, testDeptFinanceFQN) + networkConfidentialResource := createRegisteredResource("confidential-network-regres-resource", testNetworkConfidentialFQN) - // Test read access - should be allowed by both attributes - decision, err := pdp.GetDecision(s.T().Context(), entity, actionRead, []*authz.Resource{combinedResource}) + // Test read access - should be allowed by all attributes + decision, err := pdp.GetDecision(s.T().Context(), entity, actionRead, []*authz.Resource{combinedResource, networkConfidentialResource}) s.Require().NoError(err) s.Require().NotNil(decision) s.True(decision.Access) - s.Len(decision.Results, 1) + s.Len(decision.Results, 2) // Test create access - should be denied (confidential doesn't allow it) - decision, err = pdp.GetDecision(s.T().Context(), entity, actionCreate, []*authz.Resource{combinedResource}) + decision, err = pdp.GetDecision(s.T().Context(), entity, actionCreate, []*authz.Resource{combinedResource, networkConfidentialResource}) s.Require().NoError(err) s.Require().NotNil(decision) s.False(decision.Access) // Overall access is denied // Test print access - allowed by confidential but not by finance - decision, err = pdp.GetDecision(s.T().Context(), entity, testActionPrint, []*authz.Resource{combinedResource}) + decision, err = pdp.GetDecision(s.T().Context(), entity, testActionPrint, []*authz.Resource{combinedResource, networkConfidentialResource}) s.Require().NoError(err) s.Require().NotNil(decision) s.False(decision.Access) // Overall access is denied because one rule fails // Test update access - allowed by finance but not by confidential - decision, err = pdp.GetDecision(s.T().Context(), entity, testActionUpdate, []*authz.Resource{combinedResource}) + decision, err = pdp.GetDecision(s.T().Context(), entity, testActionUpdate, []*authz.Resource{combinedResource, networkConfidentialResource}) s.Require().NoError(err) s.Require().NotNil(decision) s.False(decision.Access) // Overall access is denied because one rule fails // Test delete access - denied by both - decision, err = pdp.GetDecision(s.T().Context(), entity, testActionDelete, []*authz.Resource{combinedResource}) + decision, err = pdp.GetDecision(s.T().Context(), entity, testActionDelete, []*authz.Resource{combinedResource, networkConfidentialResource}) s.Require().NoError(err) s.Require().NotNil(decision) s.False(decision.Access) @@ -877,7 +1189,7 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { "project": "alpha", }) - resources := createResourcePerFqn(testProjectAlphaFQN) + resources := createResourcePerFqn(testProjectAlphaFQN, testNetworkAlphaFQN) // Test view access - should be allowed decision, err := pdp.GetDecision(s.T().Context(), entity, testActionView, resources) @@ -908,12 +1220,96 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { []string{"restricted"}, ) + allActionPublicRegRes := &policy.RegisteredResource{ + Name: "all-actions", + Values: []*policy.RegisteredResourceValue{ + { + Value: "public", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionUpdate, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionDelete, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionPrint, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionView, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionList, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionSearch, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + }, + }, + }, + } + restrictedRegRes := &policy.RegisteredResource{ + Name: "restricted", + Values: []*policy.RegisteredResourceValue{ + { + Value: "restricted", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassConfidentialFQN, + Value: "confidential", + }, + }, + }, + }, + }, + } + classificationPDP, err := NewPolicyDecisionPoint( s.T().Context(), s.logger, []*policy.Attribute{f.classificationAttr}, []*policy.SubjectMapping{allActionsPublicMapping, restrictedMapping}, - []*policy.RegisteredResource{}, + []*policy.RegisteredResource{allActionPublicRegRes, restrictedRegRes}, ) s.Require().NoError(err) s.Require().NotNil(classificationPDP) @@ -923,8 +1319,10 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { "clearance": "restricted", }) + testRestrictedRegResFQN := createRegisteredResourceValueFQN("restricted", "restricted") + // Resource with restricted classification - restrictedResources := createResourcePerFqn(testClassConfidentialFQN) + restrictedResources := createResourcePerFqn(testClassConfidentialFQN, testRestrictedRegResFQN) // Test read access - should be allowed for restricted decision, err := classificationPDP.GetDecision(s.T().Context(), entity, actionRead, restrictedResources) @@ -1601,6 +1999,220 @@ func (s *PDPTestSuite) Test_GetDecision_AcrossNamespaces() { }) } +func (s *PDPTestSuite) Test_GetDecisionRegisteredResource_MultipleResources() { + f := s.fixtures + + regResS3BucketEntity := &policy.RegisteredResource{ + Name: "s3-bucket", + Values: []*policy.RegisteredResourceValue{ + { + Value: "ts-engineering", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassTopSecretFQN, + Value: "topsecret", + }, + }, + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testDeptEngineeringFQN, + Value: "engineering", + }, + }, + }, + }, + { + Value: "confidential-finance", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassConfidentialFQN, + Value: "confidential", + }, + }, + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testDeptFinanceFQN, + Value: "finance", + }, + }, + }, + }, + { + Value: "secret-engineering", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassSecretFQN, + Value: "secret", + }, + }, + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testDeptEngineeringFQN, + Value: "engineering", + }, + }, + }, + }, + }, + } + + // Create a PDP with relevant attributes and mappings + pdp, err := NewPolicyDecisionPoint( + s.T().Context(), + s.logger, + []*policy.Attribute{f.classificationAttr, f.departmentAttr}, + []*policy.SubjectMapping{f.secretMapping, f.topSecretMapping, f.confidentialMapping, f.publicMapping, f.engineeringMapping, f.financeMapping}, + []*policy.RegisteredResource{f.networkRegRes, regResS3BucketEntity}, + ) + s.Require().NoError(err) + s.Require().NotNil(pdp) + + s.Run("Multiple resources and entitled actions/attributes - full access", func() { + entityRegResFQN := createRegisteredResourceValueFQN(regResS3BucketEntity.GetName(), "ts-engineering") + resources := createResourcePerFqn(testClassSecretFQN, testDeptEngineeringFQN, testNetworkPrivateFQN, testNetworkPublicFQN) + + decision, err := pdp.GetDecisionRegisteredResource(s.T().Context(), entityRegResFQN, testActionRead, resources) + + s.Require().NoError(err) + s.Require().NotNil(decision) + s.True(decision.Access) + s.Len(decision.Results, 4) + + expectedResults := map[string]bool{ + testClassSecretFQN: true, + testDeptEngineeringFQN: true, + testNetworkPrivateFQN: true, + testNetworkPublicFQN: true, + } + s.assertAllDecisionResults(decision, expectedResults) + for _, result := range decision.Results { + s.True(result.Passed, "All data rules should pass") + s.Len(result.DataRuleResults, 1) + s.Empty(result.DataRuleResults[0].EntitlementFailures) + } + }) + + s.Run("Multiple resources and entitled actions/attributes of varied casing - full access", func() { + entityRegResFQN := createRegisteredResourceValueFQN(regResS3BucketEntity.GetName(), "ts-engineering") + secretFQN := strings.ToUpper(testClassSecretFQN) + networkPrivateFQN := strings.ToUpper(testNetworkPrivateFQN) + + resources := createResourcePerFqn(secretFQN, testDeptEngineeringFQN, networkPrivateFQN, testNetworkPublicFQN) + + decision, err := pdp.GetDecisionRegisteredResource(s.T().Context(), entityRegResFQN, testActionRead, resources) + + s.Require().NoError(err) + s.Require().NotNil(decision) + s.True(decision.Access) + s.Len(decision.Results, 4) + + expectedResults := map[string]bool{ + secretFQN: true, + testDeptEngineeringFQN: true, + networkPrivateFQN: true, + testNetworkPublicFQN: true, + } + s.assertAllDecisionResults(decision, expectedResults) + for _, result := range decision.Results { + s.True(result.Passed, "All data rules should pass") + s.Len(result.DataRuleResults, 1) + s.Empty(result.DataRuleResults[0].EntitlementFailures) + } + }) + + s.Run("Multiple resources and unentitled attributes - full denial", func() { + entityRegResFQN := createRegisteredResourceValueFQN(regResS3BucketEntity.GetName(), "confidential-finance") + + resources := createResourcePerFqn(testClassSecretFQN, testDeptEngineeringFQN, testNetworkPrivateFQN, testNetworkPublicFQN) + + decision, err := pdp.GetDecisionRegisteredResource(s.T().Context(), entityRegResFQN, testActionUpdate, resources) + + s.Require().NoError(err) + s.Require().NotNil(decision) + s.False(decision.Access) + s.Len(decision.Results, 4) + + expectedResults := map[string]bool{ + testClassSecretFQN: false, + testDeptEngineeringFQN: false, + testNetworkPrivateFQN: false, + testNetworkPublicFQN: false, + } + + s.assertAllDecisionResults(decision, expectedResults) + for _, result := range decision.Results { + s.False(result.Passed, "Data rules should not pass") + s.Len(result.DataRuleResults, 1) + s.NotEmpty(result.DataRuleResults[0].EntitlementFailures) + } + }) + + s.Run("Multiple resources and unentitled actions - full denial", func() { + entityRegResFQN := createRegisteredResourceValueFQN(regResS3BucketEntity.GetName(), "ts-engineering") + + resources := createResourcePerFqn(testDeptEngineeringFQN, testClassSecretFQN, testNetworkPrivateFQN, testNetworkPublicFQN) + + // Get decision for delete action (not allowed by either attribute's subject mappings) + decision, err := pdp.GetDecisionRegisteredResource(s.T().Context(), entityRegResFQN, testActionDelete, resources) + + s.Require().NoError(err) + s.Require().NotNil(decision) + s.False(decision.Access) + s.Len(decision.Results, 4) + + expectedResults := map[string]bool{ + testDeptEngineeringFQN: false, + testClassSecretFQN: false, + testNetworkPrivateFQN: false, + testNetworkPublicFQN: false, + } + s.assertAllDecisionResults(decision, expectedResults) + }) + + s.Run("Multiple resources - partial access", func() { + entityRegResFQN := createRegisteredResourceValueFQN(regResS3BucketEntity.GetName(), "secret-engineering") + + resources := createResourcePerFqn(testClassSecretFQN, testDeptFinanceFQN, testNetworkPrivateFQN, testNetworkPublicFQN) + + decision, err := pdp.GetDecisionRegisteredResource(s.T().Context(), entityRegResFQN, testActionRead, resources) + + s.Require().NoError(err) + s.Require().NotNil(decision) + s.False(decision.Access) // False because one resource is denied + s.Len(decision.Results, 4) + + expectedResults := map[string]bool{ + testClassSecretFQN: true, + testDeptFinanceFQN: false, + testNetworkPrivateFQN: true, + testNetworkPublicFQN: true, + } + s.assertAllDecisionResults(decision, expectedResults) + + // Validate proper data rule results + for _, result := range decision.Results { + s.Len(result.DataRuleResults, 1) + + if result.ResourceID == testClassSecretFQN { + s.True(result.Passed, "Secret should pass") + s.Empty(result.DataRuleResults[0].EntitlementFailures) + } else if result.ResourceID == testDeptFinanceFQN { + s.False(result.Passed, "Finance should not pass") + s.NotEmpty(result.DataRuleResults[0].EntitlementFailures) + } + } + }) +} + // TestGetEntitlements tests the functionality of retrieving entitlements for entities func (s *PDPTestSuite) Test_GetEntitlements() { f := s.fixtures From 97eb4a7b2eb3416681a45e4bc17479a20edc093e Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Wed, 18 Jun 2025 12:23:10 -0400 Subject: [PATCH 31/55] update getDecision partial tests to use reg res to mapping matches --- service/internal/access/v2/pdp_test.go | 211 +++++++++++++++---------- 1 file changed, 128 insertions(+), 83 deletions(-) diff --git a/service/internal/access/v2/pdp_test.go b/service/internal/access/v2/pdp_test.go index be4bb5c706..dc0fcbfd5b 100644 --- a/service/internal/access/v2/pdp_test.go +++ b/service/internal/access/v2/pdp_test.go @@ -1082,6 +1082,31 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { []string{"confidential"}, ) + printConfidentialRegRes := &policy.RegisteredResource{ + Name: "classification-print", + Values: []*policy.RegisteredResourceValue{ + { + Value: "confidential-print", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassConfidentialFQN, + Value: "confidential", + }, + }, + { + Action: testActionPrint, + AttributeValue: &policy.Value{ + Fqn: testClassConfidentialFQN, + Value: "confidential", + }, + }, + }, + }, + }, + } + // Create a mapping with a comprehensive set of actions instead of using a wildcard allActionsPublicMapping := createSimpleSubjectMapping( testClassPublicFQN, @@ -1094,6 +1119,73 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { []string{"public"}, ) + allActionsPublicRegRes := &policy.RegisteredResource{ + Name: "classification-all-actions", + Values: []*policy.RegisteredResourceValue{ + { + Value: "public-all-actions", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionRead, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionCreate, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionUpdate, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionDelete, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionPrint, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionView, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionList, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + { + Action: testActionSearch, + AttributeValue: &policy.Value{ + Fqn: testClassPublicFQN, + Value: "public", + }, + }, + }, + }, + }, + } + // Create a view mapping for Project Alpha with view being a parent action of read and list viewProjectAlphaMapping := createSimpleSubjectMapping( testProjectAlphaFQN, @@ -1103,6 +1195,24 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { []string{"alpha"}, ) + viewProjectAlphaRegRes := &policy.RegisteredResource{ + Name: "project-view", + Values: []*policy.RegisteredResourceValue{ + { + Value: "alpha-view", + ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ + { + Action: testActionView, + AttributeValue: &policy.Value{ + Fqn: testProjectAlphaFQN, + Value: "alpha", + }, + }, + }, + }, + }, + } + // Create a PDP with relevant attributes and mappings pdp, err := NewPolicyDecisionPoint( s.T().Context(), @@ -1112,7 +1222,10 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { f.secretMapping, f.topSecretMapping, printConfidentialMapping, allActionsPublicMapping, f.engineeringMapping, f.financeMapping, viewProjectAlphaMapping, }, - []*policy.RegisteredResource{f.classificationRegRes, f.deptRegRes, f.projectRegRes}, + []*policy.RegisteredResource{ + f.classificationRegRes, f.deptRegRes, f.projectRegRes, + printConfidentialRegRes, allActionsPublicRegRes, viewProjectAlphaRegRes, + }, ) s.Require().NoError(err) s.Require().NotNil(pdp) @@ -1124,7 +1237,7 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { }) // Resource to evaluate - resources := createResourcePerFqn(testClassSecretFQN, testNetworkPrivateFQN) + resources := createResourcePerFqn(testClassSecretFQN, testClassSecretRegResFQN) decision, err := pdp.GetDecision(s.T().Context(), entity, actionRead, resources) @@ -1150,35 +1263,36 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { }) combinedResource := createAttributeValueResource("combined-attr-resource", testClassConfidentialFQN, testDeptFinanceFQN) - networkConfidentialResource := createRegisteredResource("confidential-network-regres-resource", testNetworkConfidentialFQN) + testClassConfidentialRegResResource := createRegisteredResource(testClassConfidentialRegResFQN, testClassConfidentialRegResFQN) + testDeptFinanceRegResResource := createRegisteredResource(testDeptFinanceRegResFQN, testDeptFinanceRegResFQN) // Test read access - should be allowed by all attributes - decision, err := pdp.GetDecision(s.T().Context(), entity, actionRead, []*authz.Resource{combinedResource, networkConfidentialResource}) + decision, err := pdp.GetDecision(s.T().Context(), entity, actionRead, []*authz.Resource{combinedResource, testClassConfidentialRegResResource, testDeptFinanceRegResResource}) s.Require().NoError(err) s.Require().NotNil(decision) s.True(decision.Access) - s.Len(decision.Results, 2) + s.Len(decision.Results, 3) // Test create access - should be denied (confidential doesn't allow it) - decision, err = pdp.GetDecision(s.T().Context(), entity, actionCreate, []*authz.Resource{combinedResource, networkConfidentialResource}) + decision, err = pdp.GetDecision(s.T().Context(), entity, actionCreate, []*authz.Resource{combinedResource, testClassConfidentialRegResResource, testDeptFinanceRegResResource}) s.Require().NoError(err) s.Require().NotNil(decision) s.False(decision.Access) // Overall access is denied // Test print access - allowed by confidential but not by finance - decision, err = pdp.GetDecision(s.T().Context(), entity, testActionPrint, []*authz.Resource{combinedResource, networkConfidentialResource}) + decision, err = pdp.GetDecision(s.T().Context(), entity, testActionPrint, []*authz.Resource{combinedResource, testClassConfidentialRegResResource, testDeptFinanceRegResResource}) s.Require().NoError(err) s.Require().NotNil(decision) s.False(decision.Access) // Overall access is denied because one rule fails // Test update access - allowed by finance but not by confidential - decision, err = pdp.GetDecision(s.T().Context(), entity, testActionUpdate, []*authz.Resource{combinedResource, networkConfidentialResource}) + decision, err = pdp.GetDecision(s.T().Context(), entity, testActionUpdate, []*authz.Resource{combinedResource, testClassConfidentialRegResResource, testDeptFinanceRegResResource}) s.Require().NoError(err) s.Require().NotNil(decision) s.False(decision.Access) // Overall access is denied because one rule fails // Test delete access - denied by both - decision, err = pdp.GetDecision(s.T().Context(), entity, testActionDelete, []*authz.Resource{combinedResource, networkConfidentialResource}) + decision, err = pdp.GetDecision(s.T().Context(), entity, testActionDelete, []*authz.Resource{combinedResource, testClassConfidentialRegResResource, testDeptFinanceRegResResource}) s.Require().NoError(err) s.Require().NotNil(decision) s.False(decision.Access) @@ -1189,7 +1303,7 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { "project": "alpha", }) - resources := createResourcePerFqn(testProjectAlphaFQN, testNetworkAlphaFQN) + resources := createResourcePerFqn(testProjectAlphaFQN, testProjectAlphaRegResFQN) // Test view access - should be allowed decision, err := pdp.GetDecision(s.T().Context(), entity, testActionView, resources) @@ -1219,78 +1333,11 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { ".properties.clearance", []string{"restricted"}, ) - - allActionPublicRegRes := &policy.RegisteredResource{ - Name: "all-actions", - Values: []*policy.RegisteredResourceValue{ - { - Value: "public", - ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ - { - Action: testActionRead, - AttributeValue: &policy.Value{ - Fqn: testClassPublicFQN, - Value: "public", - }, - }, - { - Action: testActionCreate, - AttributeValue: &policy.Value{ - Fqn: testClassPublicFQN, - Value: "public", - }, - }, - { - Action: testActionUpdate, - AttributeValue: &policy.Value{ - Fqn: testClassPublicFQN, - Value: "public", - }, - }, - { - Action: testActionDelete, - AttributeValue: &policy.Value{ - Fqn: testClassPublicFQN, - Value: "public", - }, - }, - { - Action: testActionPrint, - AttributeValue: &policy.Value{ - Fqn: testClassPublicFQN, - Value: "public", - }, - }, - { - Action: testActionView, - AttributeValue: &policy.Value{ - Fqn: testClassPublicFQN, - Value: "public", - }, - }, - { - Action: testActionList, - AttributeValue: &policy.Value{ - Fqn: testClassPublicFQN, - Value: "public", - }, - }, - { - Action: testActionSearch, - AttributeValue: &policy.Value{ - Fqn: testClassPublicFQN, - Value: "public", - }, - }, - }, - }, - }, - } restrictedRegRes := &policy.RegisteredResource{ - Name: "restricted", + Name: "confidential-restricted", Values: []*policy.RegisteredResourceValue{ { - Value: "restricted", + Value: "restricted-read", ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{ { Action: testActionRead, @@ -1309,7 +1356,7 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { s.logger, []*policy.Attribute{f.classificationAttr}, []*policy.SubjectMapping{allActionsPublicMapping, restrictedMapping}, - []*policy.RegisteredResource{allActionPublicRegRes, restrictedRegRes}, + []*policy.RegisteredResource{f.classificationRegRes,allActionsPublicRegRes, restrictedRegRes}, ) s.Require().NoError(err) s.Require().NotNil(classificationPDP) @@ -1319,10 +1366,8 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { "clearance": "restricted", }) - testRestrictedRegResFQN := createRegisteredResourceValueFQN("restricted", "restricted") - // Resource with restricted classification - restrictedResources := createResourcePerFqn(testClassConfidentialFQN, testRestrictedRegResFQN) + restrictedResources := createResourcePerFqn(testClassConfidentialFQN, testClassConfidentialRegResFQN) // Test read access - should be allowed for restricted decision, err := classificationPDP.GetDecision(s.T().Context(), entity, actionRead, restrictedResources) From 010e73d1dec7f9d61607a4677d96ca4cde761fb2 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Wed, 18 Jun 2025 12:31:16 -0400 Subject: [PATCH 32/55] update comments --- service/internal/access/v2/pdp_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/service/internal/access/v2/pdp_test.go b/service/internal/access/v2/pdp_test.go index dc0fcbfd5b..037da321f4 100644 --- a/service/internal/access/v2/pdp_test.go +++ b/service/internal/access/v2/pdp_test.go @@ -124,6 +124,7 @@ var ( ) // Registered resource value FQNs using identifier package +// TODO: remove these and use the other ones above var ( regResValNoActionAttrValFQN string regResValSingleActionAttrValFQN string @@ -174,12 +175,12 @@ type PDPTestSuite struct { // Test registered resources classificationRegRes *policy.RegisteredResource deptRegRes *policy.RegisteredResource - networkRegRes *policy.RegisteredResource + networkRegRes *policy.RegisteredResource // todo: remove this and use the others that match test attributes countryRegRes *policy.RegisteredResource projectRegRes *policy.RegisteredResource platformRegRes *policy.RegisteredResource - // Test registered resources (todo: replace with above real use cases) + // Test registered resources (todo: remove these and use the ones above) regRes *policy.RegisteredResource regResValNoActionAttrVal *policy.RegisteredResourceValue regResValSingleActionAttrVal *policy.RegisteredResourceValue @@ -1356,7 +1357,7 @@ func (s *PDPTestSuite) Test_GetDecision_PartialActionEntitlement() { s.logger, []*policy.Attribute{f.classificationAttr}, []*policy.SubjectMapping{allActionsPublicMapping, restrictedMapping}, - []*policy.RegisteredResource{f.classificationRegRes,allActionsPublicRegRes, restrictedRegRes}, + []*policy.RegisteredResource{f.classificationRegRes, allActionsPublicRegRes, restrictedRegRes}, ) s.Require().NoError(err) s.Require().NotNil(classificationPDP) From 2319da6ef634d31618f19d21482e7db415e57607 Mon Sep 17 00:00:00 2001 From: Krish Suchak Date: Thu, 19 Jun 2025 13:02:05 -0400 Subject: [PATCH 33/55] lint --- service/internal/access/v2/evaluate.go | 2 +- service/internal/access/v2/helpers.go | 2 +- service/internal/access/v2/pdp.go | 8 +++---- service/internal/access/v2/pdp_test.go | 32 +++++++++++++------------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/service/internal/access/v2/evaluate.go b/service/internal/access/v2/evaluate.go index 1446814b3f..d7009ee4a0 100644 --- a/service/internal/access/v2/evaluate.go +++ b/service/internal/access/v2/evaluate.go @@ -70,7 +70,7 @@ func getResourceDecision( // } aavAttrValueFQN := aav.GetAttributeValue().GetFqn() - if !slices.Contains(resourceAttributeValues.Fqns, aavAttrValueFQN) { + if !slices.Contains(resourceAttributeValues.GetFqns(), aavAttrValueFQN) { resourceAttributeValues.Fqns = append(resourceAttributeValues.Fqns, aavAttrValueFQN) } } diff --git a/service/internal/access/v2/helpers.go b/service/internal/access/v2/helpers.go index 323249b5de..22cb3edcf3 100644 --- a/service/internal/access/v2/helpers.go +++ b/service/internal/access/v2/helpers.go @@ -191,7 +191,7 @@ func getResourceDecisionableAttributes( logger *logger.Logger, accessibleRegisteredResourceValues map[string]*policy.RegisteredResourceValue, entitleableAttributesByValueFQN map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue, - action *policy.Action, + // action *policy.Action, resources []*authz.Resource, ) (map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue, error) { var ( diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 844aa0c7c1..d028e985bf 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -262,8 +262,8 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( attrVal := aav.GetAttributeValue() attrValFQN := attrVal.GetFqn() - actionsList, ok := entitledFQNsToActions[attrValFQN] - if !ok { + actionsList, actionsAreOK := entitledFQNsToActions[attrValFQN] + if !actionsAreOK { actionsList = make([]*policy.Action, 0) } @@ -400,8 +400,8 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( attrVal := aav.GetAttributeValue() attrValFQN := attrVal.GetFqn() - actionsList, ok := actionsPerAttributeValueFqn[attrValFQN] - if !ok { + actionsList, actionsAreOK := actionsPerAttributeValueFqn[attrValFQN] + if !actionsAreOK { actionsList = &authz.EntityEntitlements_ActionsList{ Actions: make([]*policy.Action, 0), } diff --git a/service/internal/access/v2/pdp_test.go b/service/internal/access/v2/pdp_test.go index 037da321f4..dd4e47ac6d 100644 --- a/service/internal/access/v2/pdp_test.go +++ b/service/internal/access/v2/pdp_test.go @@ -88,39 +88,39 @@ var ( testPlatformHybridFQN = createAttrValueFQN(testSecondaryNamespace, "platform", "hybrid") // Registered resource value FQNs (todo: remove) - testNetworkPrivateFQN = createRegisteredResourceValueFQN("network", "private") - testNetworkPublicFQN = createRegisteredResourceValueFQN("network", "public") - testNetworkConfidentialFQN = createRegisteredResourceValueFQN("network", "confidential") - testNetworkAlphaFQN = createRegisteredResourceValueFQN("network", "alpha") + testNetworkPrivateFQN = createRegisteredResourceValueFQN("network", "private") + testNetworkPublicFQN = createRegisteredResourceValueFQN("network", "public") + // testNetworkConfidentialFQN = createRegisteredResourceValueFQN("network", "confidential") + // testNetworkAlphaFQN = createRegisteredResourceValueFQN("network", "alpha") ) // registered resource value FQNs using identifier package var ( // Classification values - testClassTopSecretRegResFQN = createRegisteredResourceValueFQN("classification", "topsecret") + // testClassTopSecretRegResFQN = createRegisteredResourceValueFQN("classification", "topsecret") testClassSecretRegResFQN = createRegisteredResourceValueFQN("classification", "secret") testClassConfidentialRegResFQN = createRegisteredResourceValueFQN("classification", "confidential") - testClassPublicRegResFQN = createRegisteredResourceValueFQN("classification", "public") + // testClassPublicRegResFQN = createRegisteredResourceValueFQN("classification", "public") // Department values - testDeptRnDRegResFQN = createRegisteredResourceValueFQN("department", "rnd") + // testDeptRnDRegResFQN = createRegisteredResourceValueFQN("department", "rnd") testDeptEngineeringRegResFQN = createRegisteredResourceValueFQN("department", "engineering") - testDeptSalesRegResFQN = createRegisteredResourceValueFQN("department", "sales") - testDeptFinanceRegResFQN = createRegisteredResourceValueFQN("department", "finance") + // testDeptSalesRegResFQN = createRegisteredResourceValueFQN("department", "sales") + testDeptFinanceRegResFQN = createRegisteredResourceValueFQN("department", "finance") // Country values - testCountryUSARegResFQN = createRegisteredResourceValueFQN("country", "usa") - testCountryUKRegResFQN = createRegisteredResourceValueFQN("country", "uk") + // testCountryUSARegResFQN = createRegisteredResourceValueFQN("country", "usa") + // testCountryUKRegResFQN = createRegisteredResourceValueFQN("country", "uk") // Project values in secondary namespace testProjectAlphaRegResFQN = createRegisteredResourceValueFQN("project", "alpha") - testProjectBetaRegResFQN = createRegisteredResourceValueFQN("project", "beta") - testProjectGammaRegResFQN = createRegisteredResourceValueFQN("project", "gamma") + // testProjectBetaRegResFQN = createRegisteredResourceValueFQN("project", "beta") + // testProjectGammaRegResFQN = createRegisteredResourceValueFQN("project", "gamma") // Platform values in secondary namespace - testPlatformCloudRegResFQN = createRegisteredResourceValueFQN("platform", "cloud") - testPlatformOnPremRegResFQN = createRegisteredResourceValueFQN("platform", "onprem") - testPlatformHybridRegResFQN = createRegisteredResourceValueFQN("platform", "hybrid") + // testPlatformCloudRegResFQN = createRegisteredResourceValueFQN("platform", "cloud") + // testPlatformOnPremRegResFQN = createRegisteredResourceValueFQN("platform", "onprem") + // testPlatformHybridRegResFQN = createRegisteredResourceValueFQN("platform", "hybrid") ) // Registered resource value FQNs using identifier package From 8a8720e4c52a1fb0f2d6813191bc4b060b1e91c9 Mon Sep 17 00:00:00 2001 From: Krish Suchak Date: Thu, 19 Jun 2025 14:07:02 -0400 Subject: [PATCH 34/55] extra args --- service/internal/access/v2/pdp.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index d028e985bf..093fee511b 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -172,7 +172,7 @@ func (p *PolicyDecisionPoint) GetDecision( } // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources - decisionableAttributes, err := getResourceDecisionableAttributes(ctx, l, p.allRegisteredResourceValuesByFQN, p.allEntitleableAttributesByValueFQN, action, resources) + decisionableAttributes, err := getResourceDecisionableAttributes(ctx, l, p.allRegisteredResourceValuesByFQN, p.allEntitleableAttributesByValueFQN /* action, */, resources) if err != nil { return nil, fmt.Errorf("error getting decisionable attributes: %w", err) } @@ -246,7 +246,7 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( } // Filter all attributes down to only those that relevant to the entitlement decisioning of these specific resources - decisionableAttributes, err := getResourceDecisionableAttributes(ctx, l, p.allRegisteredResourceValuesByFQN, p.allEntitleableAttributesByValueFQN, action, resources) + decisionableAttributes, err := getResourceDecisionableAttributes(ctx, l, p.allRegisteredResourceValuesByFQN, p.allEntitleableAttributesByValueFQN /*action, */, resources) if err != nil { return nil, fmt.Errorf("error getting decisionable attributes: %w", err) } From d5ca7a0119d0ce8b424b0e87210504048f2a1b2f Mon Sep 17 00:00:00 2001 From: Krish Suchak Date: Fri, 20 Jun 2025 18:49:48 -0400 Subject: [PATCH 35/55] mark todos with issue number --- service/internal/access/v2/evaluate.go | 2 +- service/internal/access/v2/evaluate_test.go | 2 +- service/internal/access/v2/helpers.go | 2 +- service/internal/access/v2/pdp_test.go | 12 ++++++------ 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/service/internal/access/v2/evaluate.go b/service/internal/access/v2/evaluate.go index d7009ee4a0..4add265d3e 100644 --- a/service/internal/access/v2/evaluate.go +++ b/service/internal/access/v2/evaluate.go @@ -62,7 +62,7 @@ func getResourceDecision( Fqns: make([]string, 0), } for _, aav := range regResValue.GetActionAttributeValues() { - // todo: revisit this logic -- reg res' are different from attr values since they can be both entity and resource + // TODO: DSPX-1295 - revisit this logic -- reg res' are different from attr values since they can be both entity and resource // and are tied to actions and attribute values // // if aav.GetAction().GetName() != action.GetName() { diff --git a/service/internal/access/v2/evaluate_test.go b/service/internal/access/v2/evaluate_test.go index ed8d08a42c..e0174b0d1d 100644 --- a/service/internal/access/v2/evaluate_test.go +++ b/service/internal/access/v2/evaluate_test.go @@ -155,7 +155,7 @@ func (s *EvaluateTestSuite) SetupTest() { } // Setup accessible registered resource values map - // TODO: revisit + // TODO: DSPX-1295 s.accessibleRegisteredResourceValues = map[string]*policy.RegisteredResourceValue{} } diff --git a/service/internal/access/v2/helpers.go b/service/internal/access/v2/helpers.go index 22cb3edcf3..2b011d2652 100644 --- a/service/internal/access/v2/helpers.go +++ b/service/internal/access/v2/helpers.go @@ -217,7 +217,7 @@ func getResourceDecisionableAttributes( for _, aav := range regResValue.GetActionAttributeValues() { slog.Info("processing action attribute value", slog.Any("aav", aav)) - // todo: revist this logic, bc it is causing failures for attributes with missing actions + // TODO: DSPX-1295 - revisit this logic bc it is causing failures for attributes with missing actions // aavAction := aav.GetAction() // if aavAction.GetName() != action.GetName() { // logger.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("action", aavAction.GetName())) diff --git a/service/internal/access/v2/pdp_test.go b/service/internal/access/v2/pdp_test.go index dd4e47ac6d..0224e9b1ae 100644 --- a/service/internal/access/v2/pdp_test.go +++ b/service/internal/access/v2/pdp_test.go @@ -87,7 +87,7 @@ var ( testPlatformOnPremFQN = createAttrValueFQN(testSecondaryNamespace, "platform", "onprem") testPlatformHybridFQN = createAttrValueFQN(testSecondaryNamespace, "platform", "hybrid") - // Registered resource value FQNs (todo: remove) + // Registered resource value FQNs (TODO: DSPX-1295 - remove) testNetworkPrivateFQN = createRegisteredResourceValueFQN("network", "private") testNetworkPublicFQN = createRegisteredResourceValueFQN("network", "public") // testNetworkConfidentialFQN = createRegisteredResourceValueFQN("network", "confidential") @@ -124,7 +124,7 @@ var ( ) // Registered resource value FQNs using identifier package -// TODO: remove these and use the other ones above +// TODO: DSPX-1295 - remove these and use the other ones above var ( regResValNoActionAttrValFQN string regResValSingleActionAttrValFQN string @@ -175,12 +175,12 @@ type PDPTestSuite struct { // Test registered resources classificationRegRes *policy.RegisteredResource deptRegRes *policy.RegisteredResource - networkRegRes *policy.RegisteredResource // todo: remove this and use the others that match test attributes + networkRegRes *policy.RegisteredResource // TODO: DSPX-1295 - remove this and use the others that match test attributes countryRegRes *policy.RegisteredResource projectRegRes *policy.RegisteredResource platformRegRes *policy.RegisteredResource - // Test registered resources (todo: remove these and use the ones above) + // Test registered resources (TODO: DSPX-1295 - remove these and use the ones above) regRes *policy.RegisteredResource regResValNoActionAttrVal *policy.RegisteredResourceValue regResValSingleActionAttrVal *policy.RegisteredResourceValue @@ -698,7 +698,7 @@ func (s *PDPTestSuite) SetupTest() { }, } - // Initialize test registered resources (todo: replace with above real use cases) + // Initialize test registered resources (TODO: DSPX-1295: replace with above real use cases) regResValNoActionAttrVal := &policy.RegisteredResourceValue{ Value: "no-action-attr-val", ActionAttributeValues: []*policy.RegisteredResourceValue_ActionAttributeValue{}, @@ -2876,7 +2876,7 @@ func createResourcePerFqn(attributeValueFQNs ...string) []*authz.Resource { // Use the FQN itself as the resource ID instead of a generic "ephemeral-id-X" resourceID := fqn - // todo: identifier lib does not do case-insensitive parsing, so we need to ensure FQNs are lowercased + // TODO: DSPX-1295 - identifier lib does not do case-insensitive parsing, so we need to ensure FQNs are lowercased // should maybe be fixed in the identifier library? if _, err := identifier.Parse[*identifier.FullyQualifiedRegisteredResourceValue](strings.ToLower(fqn)); err == nil { // FQN is a registered resource value From 94966450488d832e72bd1e5259a3101360914d5b Mon Sep 17 00:00:00 2001 From: Krish Suchak Date: Mon, 23 Jun 2025 10:52:21 -0400 Subject: [PATCH 36/55] use correct logger --- service/internal/access/v2/pdp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index 093fee511b..eccf5c0244 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -282,7 +282,7 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( } for idx, resource := range resources { - resourceDecision, err := getResourceDecision(ctx, p.logger, decisionableAttributes, p.allRegisteredResourceValuesByFQN, entitledFQNsToActions, action, resource) + resourceDecision, err := getResourceDecision(ctx, l, decisionableAttributes, p.allRegisteredResourceValuesByFQN, entitledFQNsToActions, action, resource) if err != nil || resourceDecision == nil { return nil, fmt.Errorf("error evaluating a decision on resource [%v]: %w", resource, err) } From 9e85a025572a02884ae352a0e4625369387ce1ce Mon Sep 17 00:00:00 2001 From: Krish Suchak Date: Mon, 23 Jun 2025 11:00:09 -0400 Subject: [PATCH 37/55] move log to DSPX-1295 todo --- service/internal/access/v2/helpers.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/service/internal/access/v2/helpers.go b/service/internal/access/v2/helpers.go index 2b011d2652..2b039263c3 100644 --- a/service/internal/access/v2/helpers.go +++ b/service/internal/access/v2/helpers.go @@ -215,9 +215,8 @@ func getResourceDecisionableAttributes( } for _, aav := range regResValue.GetActionAttributeValues() { - slog.Info("processing action attribute value", slog.Any("aav", aav)) - // TODO: DSPX-1295 - revisit this logic bc it is causing failures for attributes with missing actions + // slog.Info("processing action attribute value", slog.Any("aav", aav)) // aavAction := aav.GetAction() // if aavAction.GetName() != action.GetName() { // logger.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("action", aavAction.GetName())) From d0bd63959cb1f0413bfd996b4c40de40b9c25249 Mon Sep 17 00:00:00 2001 From: Krish Suchak Date: Mon, 23 Jun 2025 11:24:48 -0400 Subject: [PATCH 38/55] add todo for unit tests --- service/internal/access/v2/validators.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/service/internal/access/v2/validators.go b/service/internal/access/v2/validators.go index e49c9d6509..bd4148daa6 100644 --- a/service/internal/access/v2/validators.go +++ b/service/internal/access/v2/validators.go @@ -46,6 +46,8 @@ func validateGetDecision(entityRepresentation *entityresolutionV2.EntityRepresen // - registeredResourceValueFQN: must be a valid registered resource value FQN // - action: must not be nil // - resources: must not be nil and must contain at least one resource +// +// TODO: DSPX-1295 - add unit tests to detect regressions func validateGetDecisionRegisteredResource(registeredResourceValueFQN string, action *policy.Action, resources []*authzV2.Resource) error { if _, err := identifier.Parse[*identifier.FullyQualifiedRegisteredResourceValue](registeredResourceValueFQN); err != nil { return err From 3d5a51d968de1c1e11a7cae713844396c1be4a6f Mon Sep 17 00:00:00 2001 From: Chris Reed <87077975+c-r33d@users.noreply.github.com> Date: Wed, 18 Jun 2025 11:45:38 -0500 Subject: [PATCH 39/55] feat(kas): expose provider config from key details. (#2459) ### Proposed Changes 1.) Expose provider configuration from key details and indexer. ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- service/internal/security/basic_manager_test.go | 8 ++++++++ service/internal/security/in_process_provider.go | 6 ++++++ service/kas/access/publicKey_test.go | 5 +++++ service/kas/access/rewrap_test.go | 4 ++++ service/kas/key_indexer.go | 4 ++++ service/kas/key_indexer_test.go | 6 ++++-- service/trust/delegating_key_service_test.go | 9 +++++++++ service/trust/key_index.go | 5 +++++ 8 files changed, 45 insertions(+), 2 deletions(-) diff --git a/service/internal/security/basic_manager_test.go b/service/internal/security/basic_manager_test.go index 7e722e5020..ee83b597da 100644 --- a/service/internal/security/basic_manager_test.go +++ b/service/internal/security/basic_manager_test.go @@ -81,6 +81,14 @@ func (m *MockKeyDetails) System() string { return args.String(0) } +func (m *MockKeyDetails) ProviderConfig() *policy.KeyProviderConfig { + args := m.Called() + if pk, ok := args.Get(0).(*policy.KeyProviderConfig); ok { + return pk + } + return nil +} + type MockEncapsulator struct { mock.Mock } diff --git a/service/internal/security/in_process_provider.go b/service/internal/security/in_process_provider.go index 99aaeb58e3..b26a8af22d 100644 --- a/service/internal/security/in_process_provider.go +++ b/service/internal/security/in_process_provider.go @@ -11,6 +11,7 @@ import ( "log/slog" "github.com/opentdf/platform/lib/ocrypto" + "github.com/opentdf/platform/protocol/go/policy" "github.com/opentdf/platform/service/trust" ) @@ -179,6 +180,11 @@ func (k *KeyDetailsAdapter) ExportCertificate(_ context.Context) (string, error) return "", errors.New("certificates only available for EC keys") } +func (k *KeyDetailsAdapter) ProviderConfig() *policy.KeyProviderConfig { + // Provider config is not supported for this adapter. + return nil +} + // NewSecurityProviderAdapter creates a new adapter that implements SecurityProvider using a CryptoProvider func NewSecurityProviderAdapter(cryptoProvider *StandardCrypto, defaultKeys, legacyKeys []string) trust.KeyService { legacyKeysMap := make(map[string]bool, len(legacyKeys)) diff --git a/service/kas/access/publicKey_test.go b/service/kas/access/publicKey_test.go index d6e943b78b..df46bcda7c 100644 --- a/service/kas/access/publicKey_test.go +++ b/service/kas/access/publicKey_test.go @@ -16,6 +16,7 @@ import ( "connectrpc.com/connect" kaspb "github.com/opentdf/platform/protocol/go/kas" + "github.com/opentdf/platform/protocol/go/policy" "github.com/opentdf/platform/service/internal/security" "github.com/opentdf/platform/service/logger" "github.com/opentdf/platform/service/trust" @@ -80,6 +81,10 @@ func (m *MockKeyDetails) ExportCertificate(_ context.Context) (string, error) { return m.certData, nil } +func (m *MockKeyDetails) ProviderConfig() *policy.KeyProviderConfig { + return nil +} + // MockSecurityProvider is a test implementation of SecurityProvider type MockSecurityProvider struct { keys map[trust.KeyIdentifier]*MockKeyDetails diff --git a/service/kas/access/rewrap_test.go b/service/kas/access/rewrap_test.go index 1f8df78e7c..f58115ce42 100644 --- a/service/kas/access/rewrap_test.go +++ b/service/kas/access/rewrap_test.go @@ -28,6 +28,7 @@ import ( "github.com/google/uuid" kaspb "github.com/opentdf/platform/protocol/go/kas" + "github.com/opentdf/platform/protocol/go/policy" "google.golang.org/grpc/metadata" ) @@ -49,6 +50,9 @@ func (f *fakeKeyDetails) ExportPublicKey(context.Context, trust.KeyType) (string } func (f *fakeKeyDetails) ExportCertificate(context.Context) (string, error) { return "", nil } func (f *fakeKeyDetails) System() string { return "" } +func (f *fakeKeyDetails) ProviderConfig() *policy.KeyProviderConfig { + return &policy.KeyProviderConfig{} +} type fakeKeyIndex struct { keys []trust.KeyDetails diff --git a/service/kas/key_indexer.go b/service/kas/key_indexer.go index 967b10abd1..3f9dfae9ff 100644 --- a/service/kas/key_indexer.go +++ b/service/kas/key_indexer.go @@ -163,6 +163,10 @@ func (p *KeyAdapter) System() string { return mode } +func (p *KeyAdapter) ProviderConfig() *policy.KeyProviderConfig { + return p.key.GetKey().GetProviderConfig() +} + func pemToPublicKey(publicPEM string) (*rsa.PublicKey, error) { // Decode the PEM data block, _ := pem.Decode([]byte(publicPEM)) diff --git a/service/kas/key_indexer_test.go b/service/kas/key_indexer_test.go index 5525566ffd..63a2fe9c1f 100644 --- a/service/kas/key_indexer_test.go +++ b/service/kas/key_indexer_test.go @@ -30,8 +30,9 @@ func (s *KeyIndexTestSuite) SetupTest() { Pem: "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF3SEw0TkVrOFpDa0JzNjZXQVpWagpIS3NseDRseWdmaXN3aW42RUx5OU9OczZLVDRYa1crRGxsdExtck14bHZkbzVRaDg1UmFZS01mWUdDTWtPM0dGCkFsK0JOeWFOM1kwa0N1QjNPU2ErTzdyMURhNVZteVVuaEJNbFBrYnVPY1Y0cjlLMUhOSGd3eDl2UFp3RjRpQW8KQStEY1VBcWFEeHlvYjV6enNGZ0hUNjJHLzdLdEtiZ2hYT1dCanRUYUl1ZHpsK2FaSjFPemY0U1RkOXhST2QrMQordVo2VG1ocmFEUm9zdDUrTTZUN0toL2lGWk40TTFUY2hwWXU1TDhKR2tVaG9YaEdZcHUrMGczSzlqYlh6RVh5CnpJU3VXN2d6SGRWYUxvcnBkQlNkRHpOWkNvTFVoL0U1T3d5TFZFQkNKaDZJVUtvdWJ5WHVucnIxQnJmK2tLbEsKeHdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", }, ProviderConfig: &policy.KeyProviderConfig{ - Id: "test-provider-id", - Name: "openbao", + Id: "test-provider-id", + Name: "openbao", + ConfigJson: []byte("config"), }, }, }, @@ -44,6 +45,7 @@ func (s *KeyIndexTestSuite) TestKeyDetails() { s.Equal("ALGORITHM_RSA_2048", s.rsaKey.Algorithm()) s.False(s.rsaKey.IsLegacy()) s.Equal("openbao", s.rsaKey.System()) + s.Equal("config", string(s.rsaKey.ProviderConfig().GetConfigJson())) } func (s *KeyIndexTestSuite) TestKeyExportPublicKey_JWKFormat() { diff --git a/service/trust/delegating_key_service_test.go b/service/trust/delegating_key_service_test.go index b9e156bd34..83fe6bee4c 100644 --- a/service/trust/delegating_key_service_test.go +++ b/service/trust/delegating_key_service_test.go @@ -5,6 +5,7 @@ import ( "crypto/elliptic" "testing" + "github.com/opentdf/platform/protocol/go/policy" "github.com/opentdf/platform/service/logger" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" @@ -125,6 +126,14 @@ func (m *MockKeyDetails) System() string { return args.String(0) } +func (m *MockKeyDetails) ProviderConfig() *policy.KeyProviderConfig { + args := m.Called() + if a0, ok := args.Get(0).(*policy.KeyProviderConfig); ok { + return a0 + } + return nil +} + type MockProtectedKey struct { mock.Mock } diff --git a/service/trust/key_index.go b/service/trust/key_index.go index 08fb1b9e31..0cd8a45e7e 100644 --- a/service/trust/key_index.go +++ b/service/trust/key_index.go @@ -2,6 +2,8 @@ package trust import ( "context" + + "github.com/opentdf/platform/protocol/go/policy" ) // KeyType represents the format in which a key can be exported @@ -47,6 +49,9 @@ type KeyDetails interface { // Gets the mode indicator for the key; this is used to lookup the appropriate KeyManager. System() string + + // Get the provider configutaiton for the key + ProviderConfig() *policy.KeyProviderConfig } // KeyIndex provides methods to locate keys by various criteria From d89567c68ea802dcc6f8ff58498c4b106218bf33 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy <35498075+elizabethhealy@users.noreply.github.com> Date: Wed, 18 Jun 2025 13:12:46 -0400 Subject: [PATCH 40/55] feat(core): ERS cache setup, fix cache initialization (#2458) ### Proposed Changes * fix the cache initialization, provide a func on service registration that services can call to instantiate their own caches if they need them * ERS initial cache setup -- no caching yet, just setup ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions --- service/entityresolution/entityresolution.go | 24 ++++++++++++++++-- .../keycloak/entity_resolution.go | 6 +++-- .../keycloak/v2/entity_resolution.go | 6 +++-- .../entityresolution/v2/entity_resolution.go | 25 +++++++++++++++++-- service/pkg/cache/cache.go | 13 ++++------ service/pkg/cache/cache_test.go | 1 - service/pkg/server/services.go | 13 +++++----- .../pkg/serviceregistry/serviceregistry.go | 9 ++----- 8 files changed, 67 insertions(+), 30 deletions(-) diff --git a/service/entityresolution/entityresolution.go b/service/entityresolution/entityresolution.go index 38622f8b39..defdfdaa93 100644 --- a/service/entityresolution/entityresolution.go +++ b/service/entityresolution/entityresolution.go @@ -1,17 +1,21 @@ package entityresolution import ( + "time" + "github.com/go-viper/mapstructure/v2" "github.com/opentdf/platform/protocol/go/entityresolution" "github.com/opentdf/platform/protocol/go/entityresolution/entityresolutionconnect" claims "github.com/opentdf/platform/service/entityresolution/claims" keycloak "github.com/opentdf/platform/service/entityresolution/keycloak" + "github.com/opentdf/platform/service/pkg/cache" "github.com/opentdf/platform/service/pkg/serviceregistry" "go.opentelemetry.io/otel/trace" ) type ERSConfig struct { - Mode string `mapstructure:"mode" json:"mode"` + Mode string `mapstructure:"mode" json:"mode"` + CacheExpiration string `mapstructure:"cache_expiration" json:"cache_expiration"` } const ( @@ -42,9 +46,25 @@ func NewRegistration() *serviceregistry.Service[entityresolutionconnect.EntityRe claimsSVC.Tracer = srp.Tracer return EntityResolution{EntityResolutionServiceHandler: claimsSVC}, claimsHandler } + var ersCache *cache.Cache + // default to no cache if no exipiration is set + if inputConfig.CacheExpiration != "" { + exp, err := time.ParseDuration(inputConfig.CacheExpiration) + if err != nil { + srp.Logger.Error("Failed to parse cache expiration duration", "error", err) + panic(err) + } + ersCache, err = srp.NewCacheClient(cache.Options{ + Expiration: exp, + }) + if err != nil { + srp.Logger.Error("Failed to create cache for Entity Resolution Service", "error", err) + panic(err) + } + } // Default to keycloak ERS - kcSVC, kcHandler := keycloak.RegisterKeycloakERS(srp.Config, srp.Logger) + kcSVC, kcHandler := keycloak.RegisterKeycloakERS(srp.Config, srp.Logger, ersCache) kcSVC.Tracer = srp.Tracer return EntityResolution{EntityResolutionServiceHandler: kcSVC, Tracer: srp.Tracer}, kcHandler diff --git a/service/entityresolution/keycloak/entity_resolution.go b/service/entityresolution/keycloak/entity_resolution.go index 4d32e20d88..5616d70326 100644 --- a/service/entityresolution/keycloak/entity_resolution.go +++ b/service/entityresolution/keycloak/entity_resolution.go @@ -21,6 +21,7 @@ import ( "github.com/opentdf/platform/protocol/go/entityresolution" "github.com/opentdf/platform/service/entity" "github.com/opentdf/platform/service/logger" + "github.com/opentdf/platform/service/pkg/cache" "github.com/opentdf/platform/service/pkg/config" "github.com/opentdf/platform/service/pkg/serviceregistry" "go.opentelemetry.io/otel/trace" @@ -49,6 +50,7 @@ type KeycloakEntityResolutionService struct { //nolint:revive // Too late! Alrea trace.Tracer connector *KeyCloakConnector connectorMu sync.Mutex + svcCache *cache.Cache } type KeycloakConfig struct { //nolint:revive // yeah but what if we want to embed multiple configs? @@ -62,7 +64,7 @@ type KeycloakConfig struct { //nolint:revive // yeah but what if we want to embe TokenBuffer time.Duration `mapstructure:"token_buffer_seconds" json:"token_buffer_seconds" default:"120s"` } -func RegisterKeycloakERS(config config.ServiceConfig, logger *logger.Logger) (*KeycloakEntityResolutionService, serviceregistry.HandlerServer) { +func RegisterKeycloakERS(config config.ServiceConfig, logger *logger.Logger, svcCache *cache.Cache) (*KeycloakEntityResolutionService, serviceregistry.HandlerServer) { var inputIdpConfig KeycloakConfig if err := defaults.Set(&inputIdpConfig); err != nil { @@ -73,7 +75,7 @@ func RegisterKeycloakERS(config config.ServiceConfig, logger *logger.Logger) (*K panic(err) } logger.Debug("entity_resolution configuration", "config", inputIdpConfig) - keycloakSVC := &KeycloakEntityResolutionService{idpConfig: inputIdpConfig, logger: logger} + keycloakSVC := &KeycloakEntityResolutionService{idpConfig: inputIdpConfig, logger: logger, svcCache: svcCache} return keycloakSVC, nil } diff --git a/service/entityresolution/keycloak/v2/entity_resolution.go b/service/entityresolution/keycloak/v2/entity_resolution.go index a69d4449fd..5351d78f11 100644 --- a/service/entityresolution/keycloak/v2/entity_resolution.go +++ b/service/entityresolution/keycloak/v2/entity_resolution.go @@ -20,6 +20,7 @@ import ( entityresolutionV2 "github.com/opentdf/platform/protocol/go/entityresolution/v2" ent "github.com/opentdf/platform/service/entity" "github.com/opentdf/platform/service/logger" + "github.com/opentdf/platform/service/pkg/cache" "github.com/opentdf/platform/service/pkg/config" "github.com/opentdf/platform/service/pkg/serviceregistry" "go.opentelemetry.io/otel/trace" @@ -48,6 +49,7 @@ type EntityResolutionServiceV2 struct { trace.Tracer connector *Connector connectorMu sync.Mutex + svcCache *cache.Cache } type Config struct { @@ -61,7 +63,7 @@ type Config struct { TokenBuffer time.Duration `mapstructure:"token_buffer_seconds" json:"token_buffer_seconds" default:"120s"` } -func RegisterKeycloakERS(config config.ServiceConfig, logger *logger.Logger) (*EntityResolutionServiceV2, serviceregistry.HandlerServer) { +func RegisterKeycloakERS(config config.ServiceConfig, logger *logger.Logger, svcCache *cache.Cache) (*EntityResolutionServiceV2, serviceregistry.HandlerServer) { var inputIdpConfig Config if err := defaults.Set(&inputIdpConfig); err != nil { @@ -72,7 +74,7 @@ func RegisterKeycloakERS(config config.ServiceConfig, logger *logger.Logger) (*E panic(err) } logger.Debug("entity_resolution configuration", "config", inputIdpConfig) - keycloakSVC := &EntityResolutionServiceV2{idpConfig: inputIdpConfig, logger: logger} + keycloakSVC := &EntityResolutionServiceV2{idpConfig: inputIdpConfig, logger: logger, svcCache: svcCache} return keycloakSVC, nil } diff --git a/service/entityresolution/v2/entity_resolution.go b/service/entityresolution/v2/entity_resolution.go index 9fd485b2c0..ac2cd15cef 100644 --- a/service/entityresolution/v2/entity_resolution.go +++ b/service/entityresolution/v2/entity_resolution.go @@ -1,17 +1,21 @@ package entityresolution import ( + "time" + "github.com/go-viper/mapstructure/v2" ersV2 "github.com/opentdf/platform/protocol/go/entityresolution/v2" "github.com/opentdf/platform/protocol/go/entityresolution/v2/entityresolutionv2connect" claims "github.com/opentdf/platform/service/entityresolution/claims/v2" keycloak "github.com/opentdf/platform/service/entityresolution/keycloak/v2" + "github.com/opentdf/platform/service/pkg/cache" "github.com/opentdf/platform/service/pkg/serviceregistry" "go.opentelemetry.io/otel/trace" ) type ERSConfig struct { - Mode string `mapstructure:"mode" json:"mode"` + Mode string `mapstructure:"mode" json:"mode"` + CacheExpiration string `mapstructure:"cache_expiration" json:"cache_expiration"` } const ( @@ -43,8 +47,25 @@ func NewRegistration() *serviceregistry.Service[entityresolutionv2connect.Entity return EntityResolution{EntityResolutionServiceHandler: claimsSVC}, claimsHandler } + var ersCache *cache.Cache + // default to no cache if no exipiration is set + if inputConfig.CacheExpiration != "" { + exp, err := time.ParseDuration(inputConfig.CacheExpiration) + if err != nil { + srp.Logger.Error("Failed to parse cache expiration duration", "error", err) + panic(err) + } + ersCache, err = srp.NewCacheClient(cache.Options{ + Expiration: exp, + }) + if err != nil { + srp.Logger.Error("Failed to create cache for Entity Resolution Service", "error", err) + panic(err) + } + } + // Default to keycloak ERS - kcSVC, kcHandler := keycloak.RegisterKeycloakERS(srp.Config, srp.Logger) + kcSVC, kcHandler := keycloak.RegisterKeycloakERS(srp.Config, srp.Logger, ersCache) kcSVC.Tracer = srp.Tracer return EntityResolution{EntityResolutionServiceHandler: kcSVC, Tracer: srp.Tracer}, kcHandler diff --git a/service/pkg/cache/cache.go b/service/pkg/cache/cache.go index b9927e182f..7cc70d9e76 100644 --- a/service/pkg/cache/cache.go +++ b/service/pkg/cache/cache.go @@ -3,7 +3,6 @@ package cache import ( "context" "errors" - "strconv" "time" "github.com/dgraph-io/ristretto" @@ -30,7 +29,7 @@ type Cache struct { type Options struct { Expiration time.Duration - Cost int64 + // Cost int64 // TODO } // NewCacheManager creates a new cache manager using Ristretto as the backend. @@ -70,8 +69,7 @@ func (c *Manager) NewCache(serviceName string, log *logger.Logger, options Optio cache.logger = log. With("subsystem", "cache"). With("serviceTag", cache.getServiceTag()). - With("expiration", options.Expiration.String()). - With("cost", strconv.FormatInt(options.Cost, 10)) + With("expiration", options.Expiration.String()) cache.logger.Info("created cache") return cache, nil } @@ -91,10 +89,9 @@ func (c *Cache) Get(ctx context.Context, key string) (any, error) { // Set stores a value of type T in the cache. func (c *Cache) Set(ctx context.Context, key string, object any, tags []string) error { tags = append(tags, c.getServiceTag()) - opts := []store.Option{ - store.WithTags(tags), - store.WithExpiration(c.cacheOptions.Expiration), - store.WithCost(c.cacheOptions.Cost), + opts := []store.Option{store.WithTags(tags)} + if c.cacheOptions.Expiration > 0 { + opts = append(opts, store.WithExpiration(c.cacheOptions.Expiration)) } err := c.manager.cache.Set(ctx, c.getKey(key), object, opts...) diff --git a/service/pkg/cache/cache_test.go b/service/pkg/cache/cache_test.go index 552c94602d..efdfb517fb 100644 --- a/service/pkg/cache/cache_test.go +++ b/service/pkg/cache/cache_test.go @@ -36,7 +36,6 @@ func TestNewCacheManager_NewCacheIntegration(t *testing.T) { options := Options{ Expiration: 1 * time.Minute, - Cost: 1, } cache, err := manager.NewCache("testService", log, options) require.NoError(t, err) diff --git a/service/pkg/server/services.go b/service/pkg/server/services.go index cfa6426450..2e67a5d573 100644 --- a/service/pkg/server/services.go +++ b/service/pkg/server/services.go @@ -194,13 +194,14 @@ func startServices(ctx context.Context, params startServicesParams) (func(), err svcLogger = svcLogger.With("version", svc.GetVersion()) } - // Check if the service supports and needs a cache - var cacheClient *cache.Cache - if cacheSvc, ok := svc.(serviceregistry.CacheSupportedService); ok { - cacheClient, err = cacheManager.NewCache(ns, svcLogger, *cacheSvc.CacheOptions()) + // Function to create a cache given cache options + var createCacheClient func(cache.Options) (*cache.Cache, error) = func(options cache.Options) (*cache.Cache, error) { + slog.Info("creating cache client for", slog.String("namespace", ns), slog.String("service", svc.GetServiceDesc().ServiceName)) + cacheClient, err := cacheManager.NewCache(ns, svcLogger, options) if err != nil { - return func() {}, fmt.Errorf("issue creating cache client for %s: %w", ns, err) + return nil, fmt.Errorf("issue creating cache client for %s: %w", ns, err) } + return cacheClient, nil } err = svc.Start(ctx, serviceregistry.RegistrationParams{ @@ -212,7 +213,7 @@ func startServices(ctx context.Context, params startServicesParams) (func(), err RegisterReadinessCheck: health.RegisterReadinessCheck, OTDF: otdf, // TODO: REMOVE THIS Tracer: tracer, - Cache: cacheClient, + NewCacheClient: createCacheClient, KeyManagers: keyManagers, }) if err != nil { diff --git a/service/pkg/serviceregistry/serviceregistry.go b/service/pkg/serviceregistry/serviceregistry.go index 2e24bcd63c..701ab0ba65 100644 --- a/service/pkg/serviceregistry/serviceregistry.go +++ b/service/pkg/serviceregistry/serviceregistry.go @@ -44,8 +44,8 @@ type RegistrationParams struct { Logger *logger.Logger trace.Tracer - // Cache is the cache that can be used to cache data. This cache is scoped to the service - Cache *cache.Cache + // NewCacheClient is a function that can be used to create a new cache instance for the service + NewCacheClient func(cache.Options) (*cache.Cache, error) KeyManagers []trust.KeyManager @@ -90,11 +90,6 @@ type IService interface { RegisterHTTPHandlers(context.Context, *runtime.ServeMux) error } -// CacheSupportedService is implemented by services that support caching. -type CacheSupportedService interface { - CacheOptions() *cache.Options -} - // Service is a struct that holds the registration information for a service as well as the state // of the service within the instance of the platform. type Service[S any] struct { From 76a46e23e8cf288b92fe098f4eccd015a89bd9d3 Mon Sep 17 00:00:00 2001 From: Sean Trantalis <18211470+strantalis@users.noreply.github.com> Date: Wed, 18 Jun 2025 14:26:33 -0400 Subject: [PATCH 41/55] chore(ci): generate public key instead of certificate (#2455) ### Proposed Changes * ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions --- .github/scripts/init-temp-keys.sh | 63 +++++++++++++++++-------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/.github/scripts/init-temp-keys.sh b/.github/scripts/init-temp-keys.sh index 2874489547..5dbbf7ac5c 100755 --- a/.github/scripts/init-temp-keys.sh +++ b/.github/scripts/init-temp-keys.sh @@ -19,18 +19,18 @@ while [ "$1" != "$EOL" ]; do opt="$1" shift case "$opt" in - -v | --verbose) opt_verbose='true' ;; - -h | --help) - printf "%s\n" "$USAGE" - exit 0 - ;; - -o | --output) - check "$1" "-o|--output" - opt_output="$1" - shift - ;; - # process special cases - -[A-Za-z0-9] | -*[!A-Za-z0-9]*) exit2 "invalid option" "$opt" ;; + -v | --verbose) opt_verbose='true' ;; + -h | --help) + printf "%s\n" "$USAGE" + exit 0 + ;; + -o | --output) + check "$1" "-o|--output" + opt_output="$1" + shift + ;; + # process special cases + -[A-Za-z0-9] | -*[!A-Za-z0-9]*) exit2 "invalid option" "$opt" ;; esac done shift @@ -41,30 +41,35 @@ fi mkdir -p "$opt_output" -openssl req -x509 -nodes -newkey RSA:2048 -subj "/CN=kas" -keyout "$opt_output/kas-private.pem" -out "$opt_output/kas-cert.pem" -days 365 +# Generate KAS RSA private key. +openssl genpkey -algorithm RSA -out "$opt_output/kas-private.pem" -pkeyopt rsa_keygen_bits:2048 +# Extract the KAS RSA public key from the private key and save it to "$opt_output/kas-cert.pem". +# This replaces the certificate that would have been generated by the original command. +openssl rsa -in "$opt_output/kas-private.pem" -pubout -out "$opt_output/kas-cert.pem" +# Generate ECC Key openssl ecparam -name prime256v1 >ecparams.tmp openssl req -x509 -nodes -newkey ec:ecparams.tmp -subj "/CN=kas" -keyout "$opt_output/kas-ec-private.pem" -out "$opt_output/kas-ec-cert.pem" -days 365 mkdir -p keys openssl req -x509 -nodes -newkey RSA:2048 -subj "/CN=ca" -keyout keys/keycloak-ca-private.pem -out keys/keycloak-ca.pem -days 365 -printf "subjectAltName=DNS:localhost,IP:127.0.0.1" > keys/sanX509.conf -printf "[req]\ndistinguished_name=req_distinguished_name\n[req_distinguished_name]\n[alt_names]\nDNS.1=localhost\nIP.1=127.0.0.1" > keys/req.conf +printf "subjectAltName=DNS:localhost,IP:127.0.0.1" >keys/sanX509.conf +printf "[req]\ndistinguished_name=req_distinguished_name\n[req_distinguished_name]\n[alt_names]\nDNS.1=localhost\nIP.1=127.0.0.1" >keys/req.conf openssl req -new -nodes -newkey rsa:2048 -keyout keys/localhost.key -out keys/localhost.req -batch -subj "/CN=localhost" -config keys/req.conf -openssl x509 -req -in keys/localhost.req -CA keys/keycloak-ca.pem -CAkey keys/keycloak-ca-private.pem -CAcreateserial -out keys/localhost.crt -days 3650 -sha256 -extfile keys/sanX509.conf +openssl x509 -req -in keys/localhost.req -CA keys/keycloak-ca.pem -CAkey keys/keycloak-ca-private.pem -CAcreateserial -out keys/localhost.crt -days 3650 -sha256 -extfile keys/sanX509.conf openssl req -new -nodes -newkey rsa:2048 -keyout keys/sampleuser.key -out keys/sampleuser.req -batch -subj "/CN=sampleuser" -openssl x509 -req -in keys/sampleuser.req -CA keys/keycloak-ca.pem -CAkey keys/keycloak-ca-private.pem -CAcreateserial -out keys/sampleuser.crt -days 3650 +openssl x509 -req -in keys/sampleuser.req -CA keys/keycloak-ca.pem -CAkey keys/keycloak-ca-private.pem -CAcreateserial -out keys/sampleuser.crt -days 3650 openssl pkcs12 -export -in keys/keycloak-ca.pem -inkey keys/keycloak-ca-private.pem -out keys/ca.p12 -nodes -passout pass:password docker run \ - -v $(pwd)/keys:/keys \ - --entrypoint keytool \ - --user $(id -u):$(id -g) \ - keycloak/keycloak:25.0 \ - -importkeystore \ - -srckeystore /keys/ca.p12 \ - -srcstoretype PKCS12 \ - -destkeystore /keys/ca.jks \ - -deststoretype JKS \ - -srcstorepass "password" \ - -deststorepass "password" \ - -noprompt + -v $(pwd)/keys:/keys \ + --entrypoint keytool \ + --user $(id -u):$(id -g) \ + keycloak/keycloak:25.0 \ + -importkeystore \ + -srckeystore /keys/ca.p12 \ + -srcstoretype PKCS12 \ + -destkeystore /keys/ca.jks \ + -deststoretype JKS \ + -srcstorepass "password" \ + -deststorepass "password" \ + -noprompt From 15aca16b0886a345fdfdec788cb434bc0bb55c07 Mon Sep 17 00:00:00 2001 From: b-long Date: Fri, 20 Jun 2025 12:17:51 -0400 Subject: [PATCH 42/55] chore(docs): describe `LegacyPublicKey` and run `make proto-generate` (#2415) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Motivation Improving the comments in `.proto` files has downstream effects, like improving generated documentation for OpenAPI. Before this change, there's a description property for `kas` set to `buf:lint:ignore RPC_RESPONSE_STANDARD_NAME`, which tends to be shown quite near the top of `kas` itself. In other words, the reader might wonder "_What is kas?_" and be met with the text "`buf:lint:ignore RPC_RESPONSE_STANDARD_NAME`". Making this change also helps me to understand how we might improve other `.proto` comments, to improve other OpenAPI & documentation. ### Proposed Changes * Describe `LegacyPublicKey`, so that `buf:lint` pragma isn't the primary comment ✨ _NOTE_ This small `.proto` file change, followed by running `make proto-generate` seems to have improved several generated files (e.g. `docs/grpc/index.html`), but it also seems to have removed some detail from other generated files, like: * `docs/openapi/authorization/authorization.openapi.yaml`, and * `docs/openapi/policy/actions/actions.openapi.yaml` Maybe the updates to these files (removing `examples` object) is desired? ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [x] I have added or updated documentation ### Testing Instructions --- docs/grpc/index.html | 27 +++++++++- .../authorization/authorization.openapi.yaml | 3 -- .../v2/authorization.openapi.yaml | 3 -- docs/openapi/common/common.openapi.yaml | 3 -- docs/openapi/kas/kas.openapi.yaml | 7 ++- .../policy/actions/actions.openapi.yaml | 3 -- .../policy/attributes/attributes.openapi.yaml | 3 -- .../key_access_server_registry.openapi.yaml | 3 -- .../keymanagement/key_management.openapi.yaml | 3 -- .../policy/namespaces/namespaces.openapi.yaml | 3 -- docs/openapi/policy/objects.openapi.yaml | 3 -- .../registered_resources.openapi.yaml | 3 -- .../resource_mapping.openapi.yaml | 3 -- .../subject_mapping.openapi.yaml | 3 -- .../openapi/policy/unsafe/unsafe.openapi.yaml | 3 -- protocol/go/kas/kas.pb.go | 50 +++++++++---------- protocol/go/kas/kas_grpc.pb.go | 11 ++++ protocol/go/kas/kasconnect/kas.connect.go | 14 ++++++ service/kas/kas.proto | 5 ++ 19 files changed, 87 insertions(+), 66 deletions(-) diff --git a/docs/grpc/index.html b/docs/grpc/index.html index e4329f9e4b..1d37f268d4 100644 --- a/docs/grpc/index.html +++ b/docs/grpc/index.html @@ -6283,7 +6283,11 @@

AccessService

LegacyPublicKey LegacyPublicKeyRequest .google.protobuf.StringValue -

buf:lint:ignore RPC_RESPONSE_STANDARD_NAME

+

Endpoint intended for gRPC Gateway's REST endpoint to provide v1 compatibility with older TDF clients + +This endpoint is not recommended for use in new applications, prefer the v2 endpoint ('PublicKey') instead. + +buf:lint:ignore RPC_RESPONSE_STANDARD_NAME

@@ -6299,6 +6303,27 @@

AccessService

+

Methods with deprecated option

+ + + + + + + + + + + + + + + +
Method NameOption
LegacyPublicKey

true

+ + + +

Methods with HTTP bindings

diff --git a/docs/openapi/authorization/authorization.openapi.yaml b/docs/openapi/authorization/authorization.openapi.yaml index 582d5392ab..1f263fc4d0 100644 --- a/docs/openapi/authorization/authorization.openapi.yaml +++ b/docs/openapi/authorization/authorization.openapi.yaml @@ -662,9 +662,6 @@ components: description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. google.protobuf.Timestamp: type: string - examples: - - 1s - - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/docs/openapi/authorization/v2/authorization.openapi.yaml b/docs/openapi/authorization/v2/authorization.openapi.yaml index b3c47c540c..7e7500e6b8 100644 --- a/docs/openapi/authorization/v2/authorization.openapi.yaml +++ b/docs/openapi/authorization/v2/authorization.openapi.yaml @@ -584,9 +584,6 @@ components: has no plan to be removed. google.protobuf.Timestamp: type: string - examples: - - 1s - - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/docs/openapi/common/common.openapi.yaml b/docs/openapi/common/common.openapi.yaml index bff7e6603e..b19945f5df 100644 --- a/docs/openapi/common/common.openapi.yaml +++ b/docs/openapi/common/common.openapi.yaml @@ -77,9 +77,6 @@ components: additionalProperties: false google.protobuf.Timestamp: type: string - examples: - - 1s - - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/docs/openapi/kas/kas.openapi.yaml b/docs/openapi/kas/kas.openapi.yaml index 7d68195fd6..681fa2301d 100644 --- a/docs/openapi/kas/kas.openapi.yaml +++ b/docs/openapi/kas/kas.openapi.yaml @@ -42,7 +42,12 @@ paths: tags: - kas.AccessService summary: LegacyPublicKey - description: buf:lint:ignore RPC_RESPONSE_STANDARD_NAME + description: |- + Endpoint intended for gRPC Gateway's REST endpoint to provide v1 compatibility with older TDF clients + + This endpoint is not recommended for use in new applications, prefer the v2 endpoint ('PublicKey') instead. + + buf:lint:ignore RPC_RESPONSE_STANDARD_NAME operationId: kas.AccessService.LegacyPublicKey parameters: - name: algorithm diff --git a/docs/openapi/policy/actions/actions.openapi.yaml b/docs/openapi/policy/actions/actions.openapi.yaml index 2c4d842913..e0171cc60a 100644 --- a/docs/openapi/policy/actions/actions.openapi.yaml +++ b/docs/openapi/policy/actions/actions.openapi.yaml @@ -314,9 +314,6 @@ components: has no plan to be removed. google.protobuf.Timestamp: type: string - examples: - - 1s - - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/docs/openapi/policy/attributes/attributes.openapi.yaml b/docs/openapi/policy/attributes/attributes.openapi.yaml index 919b97b0c1..f555d80f49 100644 --- a/docs/openapi/policy/attributes/attributes.openapi.yaml +++ b/docs/openapi/policy/attributes/attributes.openapi.yaml @@ -847,9 +847,6 @@ components: has no plan to be removed. google.protobuf.Timestamp: type: string - examples: - - 1s - - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml index 253dd56e91..a53094fec4 100644 --- a/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml +++ b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml @@ -603,9 +603,6 @@ components: has no plan to be removed. google.protobuf.Timestamp: type: string - examples: - - 1s - - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/docs/openapi/policy/keymanagement/key_management.openapi.yaml b/docs/openapi/policy/keymanagement/key_management.openapi.yaml index e481df3bf2..4d28d36dc0 100644 --- a/docs/openapi/policy/keymanagement/key_management.openapi.yaml +++ b/docs/openapi/policy/keymanagement/key_management.openapi.yaml @@ -246,9 +246,6 @@ components: additionalProperties: false google.protobuf.Timestamp: type: string - examples: - - 1s - - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/docs/openapi/policy/namespaces/namespaces.openapi.yaml b/docs/openapi/policy/namespaces/namespaces.openapi.yaml index 4530e5c11a..f41f301bd6 100644 --- a/docs/openapi/policy/namespaces/namespaces.openapi.yaml +++ b/docs/openapi/policy/namespaces/namespaces.openapi.yaml @@ -441,9 +441,6 @@ components: has no plan to be removed. google.protobuf.Timestamp: type: string - examples: - - 1s - - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/docs/openapi/policy/objects.openapi.yaml b/docs/openapi/policy/objects.openapi.yaml index 4de545e486..af46b90964 100644 --- a/docs/openapi/policy/objects.openapi.yaml +++ b/docs/openapi/policy/objects.openapi.yaml @@ -127,9 +127,6 @@ components: has no plan to be removed. google.protobuf.Timestamp: type: string - examples: - - 1s - - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml index 01c37ae346..51e2ec8cc2 100644 --- a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml +++ b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml @@ -524,9 +524,6 @@ components: has no plan to be removed. google.protobuf.Timestamp: type: string - examples: - - 1s - - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml b/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml index 8d37ac723e..95748b8998 100644 --- a/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml +++ b/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml @@ -524,9 +524,6 @@ components: has no plan to be removed. google.protobuf.Timestamp: type: string - examples: - - 1s - - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml b/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml index 796706ee6b..c6551c63d2 100644 --- a/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml +++ b/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml @@ -560,9 +560,6 @@ components: has no plan to be removed. google.protobuf.Timestamp: type: string - examples: - - 1s - - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/docs/openapi/policy/unsafe/unsafe.openapi.yaml b/docs/openapi/policy/unsafe/unsafe.openapi.yaml index ffa80ffd1e..f42d103313 100644 --- a/docs/openapi/policy/unsafe/unsafe.openapi.yaml +++ b/docs/openapi/policy/unsafe/unsafe.openapi.yaml @@ -475,9 +475,6 @@ components: has no plan to be removed. google.protobuf.Timestamp: type: string - examples: - - 1s - - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/protocol/go/kas/kas.pb.go b/protocol/go/kas/kas.pb.go index cd41a7c299..b9b08d616c 100644 --- a/protocol/go/kas/kas.pb.go +++ b/protocol/go/kas/kas.pb.go @@ -1165,7 +1165,7 @@ var file_kas_kas_proto_rawDesc = []byte{ 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xce, 0x02, 0x0a, 0x0d, 0x41, 0x63, 0x63, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xd1, 0x02, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x69, 0x0a, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, @@ -1173,35 +1173,35 @@ var file_kas_kas_proto_rawDesc = []byte{ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, 0x00, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x6b, 0x61, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, - 0x65, 0x79, 0x90, 0x02, 0x01, 0x12, 0x78, 0x0a, 0x0f, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x50, + 0x65, 0x79, 0x90, 0x02, 0x01, 0x12, 0x7b, 0x0a, 0x0f, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x22, 0x2a, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, + 0x6c, 0x75, 0x65, 0x22, 0x2d, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, 0x00, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x6b, 0x61, - 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x90, 0x02, 0x01, 0x12, - 0x58, 0x0a, 0x06, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x12, 0x12, 0x2e, 0x6b, 0x61, 0x73, 0x2e, - 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, - 0x6b, 0x61, 0x73, 0x2e, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x25, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, 0x00, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x6b, 0x61, 0x73, 0x2f, - 0x76, 0x32, 0x2f, 0x72, 0x65, 0x77, 0x72, 0x61, 0x70, 0x42, 0xe2, 0x01, 0x92, 0x41, 0x73, 0x12, - 0x71, 0x0a, 0x1a, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x44, 0x46, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x41, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2a, 0x4c, 0x0a, - 0x12, 0x42, 0x53, 0x44, 0x20, 0x33, 0x2d, 0x43, 0x6c, 0x61, 0x75, 0x73, 0x65, 0x20, 0x43, 0x6c, - 0x65, 0x61, 0x72, 0x12, 0x36, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, - 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, - 0x74, 0x65, 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x35, - 0x2e, 0x30, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x6b, 0x61, 0x73, 0x42, 0x08, 0x4b, 0x61, 0x73, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 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, 0x6b, 0x61, 0x73, 0xa2, 0x02, 0x03, 0x4b, 0x58, 0x58, 0xaa, 0x02, 0x03, 0x4b, 0x61, 0x73, - 0xca, 0x02, 0x03, 0x4b, 0x61, 0x73, 0xe2, 0x02, 0x0f, 0x4b, 0x61, 0x73, 0x5c, 0x47, 0x50, 0x42, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x03, 0x4b, 0x61, 0x73, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x88, 0x02, 0x01, 0x90, + 0x02, 0x01, 0x12, 0x58, 0x0a, 0x06, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x12, 0x12, 0x2e, 0x6b, + 0x61, 0x73, 0x2e, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x13, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, + 0x30, 0x12, 0x00, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x6b, + 0x61, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x72, 0x65, 0x77, 0x72, 0x61, 0x70, 0x42, 0xe2, 0x01, 0x92, + 0x41, 0x73, 0x12, 0x71, 0x0a, 0x1a, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x44, 0x46, 0x20, 0x4b, 0x65, + 0x79, 0x20, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x2a, 0x4c, 0x0a, 0x12, 0x42, 0x53, 0x44, 0x20, 0x33, 0x2d, 0x43, 0x6c, 0x61, 0x75, 0x73, 0x65, + 0x20, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x12, 0x36, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, + 0x64, 0x66, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, + 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, + 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x6b, 0x61, 0x73, 0x42, 0x08, + 0x4b, 0x61, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 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, 0x6b, 0x61, 0x73, 0xa2, 0x02, 0x03, 0x4b, 0x58, 0x58, 0xaa, 0x02, 0x03, + 0x4b, 0x61, 0x73, 0xca, 0x02, 0x03, 0x4b, 0x61, 0x73, 0xe2, 0x02, 0x0f, 0x4b, 0x61, 0x73, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x03, 0x4b, 0x61, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/protocol/go/kas/kas_grpc.pb.go b/protocol/go/kas/kas_grpc.pb.go index 018561050b..6fe37a4c7b 100644 --- a/protocol/go/kas/kas_grpc.pb.go +++ b/protocol/go/kas/kas_grpc.pb.go @@ -30,6 +30,11 @@ const ( // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type AccessServiceClient interface { PublicKey(ctx context.Context, in *PublicKeyRequest, opts ...grpc.CallOption) (*PublicKeyResponse, error) + // Deprecated: Do not use. + // Endpoint intended for gRPC Gateway's REST endpoint to provide v1 compatibility with older TDF clients + // + // This endpoint is not recommended for use in new applications, prefer the v2 endpoint ('PublicKey') instead. + // // buf:lint:ignore RPC_RESPONSE_STANDARD_NAME LegacyPublicKey(ctx context.Context, in *LegacyPublicKeyRequest, opts ...grpc.CallOption) (*wrapperspb.StringValue, error) Rewrap(ctx context.Context, in *RewrapRequest, opts ...grpc.CallOption) (*RewrapResponse, error) @@ -52,6 +57,7 @@ func (c *accessServiceClient) PublicKey(ctx context.Context, in *PublicKeyReques return out, nil } +// Deprecated: Do not use. func (c *accessServiceClient) LegacyPublicKey(ctx context.Context, in *LegacyPublicKeyRequest, opts ...grpc.CallOption) (*wrapperspb.StringValue, error) { out := new(wrapperspb.StringValue) err := c.cc.Invoke(ctx, AccessService_LegacyPublicKey_FullMethodName, in, out, opts...) @@ -75,6 +81,11 @@ func (c *accessServiceClient) Rewrap(ctx context.Context, in *RewrapRequest, opt // for forward compatibility type AccessServiceServer interface { PublicKey(context.Context, *PublicKeyRequest) (*PublicKeyResponse, error) + // Deprecated: Do not use. + // Endpoint intended for gRPC Gateway's REST endpoint to provide v1 compatibility with older TDF clients + // + // This endpoint is not recommended for use in new applications, prefer the v2 endpoint ('PublicKey') instead. + // // buf:lint:ignore RPC_RESPONSE_STANDARD_NAME LegacyPublicKey(context.Context, *LegacyPublicKeyRequest) (*wrapperspb.StringValue, error) Rewrap(context.Context, *RewrapRequest) (*RewrapResponse, error) diff --git a/protocol/go/kas/kasconnect/kas.connect.go b/protocol/go/kas/kasconnect/kas.connect.go index fb443bfd92..b56315d0f8 100644 --- a/protocol/go/kas/kasconnect/kas.connect.go +++ b/protocol/go/kas/kasconnect/kas.connect.go @@ -54,7 +54,13 @@ var ( // AccessServiceClient is a client for the kas.AccessService service. type AccessServiceClient interface { PublicKey(context.Context, *connect.Request[kas.PublicKeyRequest]) (*connect.Response[kas.PublicKeyResponse], error) + // Endpoint intended for gRPC Gateway's REST endpoint to provide v1 compatibility with older TDF clients + // + // This endpoint is not recommended for use in new applications, prefer the v2 endpoint ('PublicKey') instead. + // // buf:lint:ignore RPC_RESPONSE_STANDARD_NAME + // + // Deprecated: do not use. LegacyPublicKey(context.Context, *connect.Request[kas.LegacyPublicKeyRequest]) (*connect.Response[wrapperspb.StringValue], error) Rewrap(context.Context, *connect.Request[kas.RewrapRequest]) (*connect.Response[kas.RewrapResponse], error) } @@ -105,6 +111,8 @@ func (c *accessServiceClient) PublicKey(ctx context.Context, req *connect.Reques } // LegacyPublicKey calls kas.AccessService.LegacyPublicKey. +// +// Deprecated: do not use. func (c *accessServiceClient) LegacyPublicKey(ctx context.Context, req *connect.Request[kas.LegacyPublicKeyRequest]) (*connect.Response[wrapperspb.StringValue], error) { return c.legacyPublicKey.CallUnary(ctx, req) } @@ -117,7 +125,13 @@ func (c *accessServiceClient) Rewrap(ctx context.Context, req *connect.Request[k // AccessServiceHandler is an implementation of the kas.AccessService service. type AccessServiceHandler interface { PublicKey(context.Context, *connect.Request[kas.PublicKeyRequest]) (*connect.Response[kas.PublicKeyResponse], error) + // Endpoint intended for gRPC Gateway's REST endpoint to provide v1 compatibility with older TDF clients + // + // This endpoint is not recommended for use in new applications, prefer the v2 endpoint ('PublicKey') instead. + // // buf:lint:ignore RPC_RESPONSE_STANDARD_NAME + // + // Deprecated: do not use. LegacyPublicKey(context.Context, *connect.Request[kas.LegacyPublicKeyRequest]) (*connect.Response[wrapperspb.StringValue], error) Rewrap(context.Context, *connect.Request[kas.RewrapRequest]) (*connect.Response[kas.RewrapResponse], error) } diff --git a/service/kas/kas.proto b/service/kas/kas.proto index 72b8913e7f..a19bc84d8d 100644 --- a/service/kas/kas.proto +++ b/service/kas/kas.proto @@ -132,6 +132,10 @@ service AccessService { option idempotency_level = NO_SIDE_EFFECTS; } + // Endpoint intended for gRPC Gateway's REST endpoint to provide v1 compatibility with older TDF clients + // + // This endpoint is not recommended for use in new applications, prefer the v2 endpoint ('PublicKey') instead. + // // buf:lint:ignore RPC_RESPONSE_STANDARD_NAME rpc LegacyPublicKey(LegacyPublicKeyRequest) returns (google.protobuf.StringValue) { option (google.api.http) = {get: "/kas/kas_public_key"}; @@ -140,6 +144,7 @@ service AccessService { responses: {key: "200"} }; option idempotency_level = NO_SIDE_EFFECTS; + option deprecated = true; } rpc Rewrap(RewrapRequest) returns (RewrapResponse) { From 7f245e68e69b7440f7c9107ba3dda299dc1fbed8 Mon Sep 17 00:00:00 2001 From: Ryan Schumacher Date: Fri, 20 Jun 2025 13:50:00 -0500 Subject: [PATCH 43/55] chore: improve external contributor check (#2447) ### Proposed Changes * Get org teams and check if user is in the team ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions --- .github/workflows/label.yaml | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/.github/workflows/label.yaml b/.github/workflows/label.yaml index 3362e1d595..409a79b1e3 100644 --- a/.github/workflows/label.yaml +++ b/.github/workflows/label.yaml @@ -50,20 +50,29 @@ jobs: return; } - // Define associations considered "internal" - const internalAssociations = ["MEMBER"]; - // Label to add if the author is external const externalLabel = "external-contributor"; - const authorAssociation = pr.author_association; - const isExternal = !internalAssociations.includes(authorAssociation); + // Check if the user is a member of the org using the GitHub API + let isOrgMember = false; + try { + const { data } = await github.rest.orgs.getMembershipForUser({ + org: context.repo.owner, + username: pr.user.login + }); + if (data && data.state === "active") { + isOrgMember = true; + } + } catch (error) { + if (error.status !== 404) throw error; // only ignore 404s + } + const isExternal = !isOrgMember; const prLabels = pr.labels.map(label => label.name); const hasExternalLabel = prLabels.includes(externalLabel); core.info(`Event: ${context.eventName}, Action: ${context.payload.action}`); - core.info(`Author: ${pr.user.login}, Association: ${authorAssociation}, Is External: ${isExternal}`); + core.info(`Author: ${pr.user.login}, Is Org Member: ${isOrgMember}, Is External: ${isExternal}`); core.info(`Current PR Labels: ${prLabels.join(', ')}`); // Logic for 'unlabeled' event: only re-add if *our* label was removed and author is still external @@ -86,7 +95,7 @@ jobs: // Logic for 'opened' or 'reopened' events: add label if external and not already present else if (['opened', 'reopened'].includes(context.payload.action)) { if (isExternal && !hasExternalLabel) { - core.info(`Author association "${authorAssociation}" is external and label is missing. Adding label: ${externalLabel}`); + core.info(`Author is external and label is missing. Adding label: ${externalLabel}`); await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, @@ -98,9 +107,9 @@ jobs: core.info(`Author is external, but label "${externalLabel}" is already present.`); } else { if (hasExternalLabel) { - core.warning(`Author association "${authorAssociation}" is internal. However, external contributor label is present.`) + core.warning(`Author is internal. However, external contributor label is present.`) } else { - core.info(`Author association "${authorAssociation}" is internal. No label added.`) + core.info(`Author is internal. No label added.`) } } } From 079c109919c687f3209d5a9d988dea6558af8f2c Mon Sep 17 00:00:00 2001 From: Sean Trantalis <18211470+strantalis@users.noreply.github.com> Date: Fri, 20 Jun 2025 16:00:59 -0400 Subject: [PATCH 44/55] chore(ci): need github app token to call org members api (#2463) ### Proposed Changes * ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions --- .github/workflows/label.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/label.yaml b/.github/workflows/label.yaml index 409a79b1e3..875017317d 100644 --- a/.github/workflows/label.yaml +++ b/.github/workflows/label.yaml @@ -39,10 +39,16 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: + - name: "Generate a GitHub app token" + id: generate-token + uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.AUTOMATION_KEY }} - name: Check Author Association and Label PR uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: - github-token: ${{ secrets.GITHUB_TOKEN }} + github-token: ${{ steps.generate-token.outputs.token }} script: | const pr = context.payload.pull_request; if (!pr) { From fc7b9ee7d6bca9c8e2de40562d3c2f22ebddaa2a Mon Sep 17 00:00:00 2001 From: Sean Trantalis <18211470+strantalis@users.noreply.github.com> Date: Mon, 23 Jun 2025 10:26:38 -0400 Subject: [PATCH 45/55] feat(policy)!: disable kas grants in favor of key mappings (#2220) ### Proposed Changes ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions --- buf.yaml | 2 +- docs/grpc/index.html | 108 +- .../policy/actions/actions.openapi.yaml | 8 +- .../policy/attributes/attributes.openapi.yaml | 24 +- .../key_access_server_registry.openapi.yaml | 1 + .../policy/namespaces/namespaces.openapi.yaml | 5 +- docs/openapi/policy/objects.openapi.yaml | 8 +- .../registered_resources.openapi.yaml | 8 +- .../resource_mapping.openapi.yaml | 8 +- docs/openapi/policy/selectors.openapi.yaml | 6 + .../subject_mapping.openapi.yaml | 8 +- .../openapi/policy/unsafe/unsafe.openapi.yaml | 8 +- examples/cmd/attributes.go | 157 +-- .../go/policy/attributes/attributes.pb.go | 1087 +++++++++-------- .../policy/attributes/attributes_grpc.pb.go | 12 + .../attributesconnect/attributes.connect.go | 18 + .../key_access_server_registry.connect.go | 6 + .../key_access_server_registry.pb.go | 949 +++++++------- .../key_access_server_registry_grpc.pb.go | 3 + .../go/policy/namespaces/namespaces.pb.go | 523 ++++---- .../policy/namespaces/namespaces_grpc.pb.go | 6 + .../namespacesconnect/namespaces.connect.go | 10 + protocol/go/policy/objects.pb.go | 7 +- protocol/go/policy/selectors.pb.go | 6 + service/go.mod | 6 +- service/integration/attribute_fqns_test.go | 362 ++---- service/integration/attribute_values_test.go | 115 -- service/integration/attributes_test.go | 118 -- service/integration/kas_registry_test.go | 432 ------- service/integration/namespaces_test.go | 249 ---- .../internal/fixtures/policy_fixtures.yaml | 10 +- service/policy/attributes/attributes.go | 47 +- service/policy/attributes/attributes.proto | 203 ++- service/policy/attributes/attributes_test.go | 82 -- service/policy/db/attribute_values.go | 22 - service/policy/db/attributes.go | 35 +- .../policy/db/key_access_server_registry.go | 10 +- service/policy/db/models.go | 2 +- service/policy/db/namespaces.go | 22 - service/policy/db/query.sql | 94 -- service/policy/db/query.sql.go | 237 ---- .../key_access_server_registry.proto | 7 +- .../key_access_server_registry_test.go | 74 -- service/policy/namespaces/namespaces.go | 23 +- service/policy/namespaces/namespaces.proto | 75 +- service/policy/namespaces/namespaces_test.go | 41 - service/policy/objects.proto | 7 +- service/policy/selectors.proto | 65 +- test/policy-service.bats | 30 +- test/tdf-roundtrips.bats | 58 - 50 files changed, 1819 insertions(+), 3585 deletions(-) diff --git a/buf.yaml b/buf.yaml index 93bdfe1565..5031f81191 100644 --- a/buf.yaml +++ b/buf.yaml @@ -9,7 +9,7 @@ deps: - buf.build/grpc-ecosystem/grpc-gateway lint: use: - - DEFAULT + - STANDARD except: - FIELD_NOT_REQUIRED - PACKAGE_NO_IMPORT_CYCLE diff --git a/docs/grpc/index.html b/docs/grpc/index.html index 1d37f268d4..f1066982b7 100644 --- a/docs/grpc/index.html +++ b/docs/grpc/index.html @@ -2245,7 +2245,7 @@

Attribute

- + @@ -2678,7 +2678,7 @@

Namespace

- + @@ -3349,8 +3349,7 @@

Value

- + @@ -6418,7 +6417,7 @@

AttributeDefinitionSelector

- + @@ -6463,7 +6462,7 @@

AttributeDefinitionSel

- + @@ -6525,7 +6524,7 @@

AttributeNamespaceS

- + @@ -6556,7 +6555,7 @@

Attri

- + @@ -6594,7 +6593,7 @@

AttributeValueSelector

- + @@ -6639,7 +6638,7 @@

AttributeValueSelector.

- + @@ -7332,7 +7331,7 @@

AttributeKey

AttributeKeyAccessServer

-

+

Deprecated

grants KeyAccessServer repeated

Deprecated

Deprecated KAS grants for the attribute. Use kas_keys instead.

grants KeyAccessServer repeated

KAS grants for the namespace

Deprecated KAS grants for the namespace. Use kas_keys instead.

grants KeyAccessServer repeated

Deprecated -list of key access servers

Deprecated KAS grants for the value. Use kas_keys instead.

with_key_access_grants bool

Deprecated

with_key_access_grants bool

Deprecated

with_key_access_grants bool

Deprecated

with_key_access_grants bool

Deprecated

with_key_access_grants bool

Deprecated

with_key_access_grants bool

Deprecated

@@ -7397,7 +7396,7 @@

CreateAttributeRequest

- @@ -8563,6 +8562,42 @@

AttributesService

+

Methods with deprecated option

+
values string repeated

Optional +

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

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Method NameOption
AssignKeyAccessServerToAttribute

true

RemoveKeyAccessServerFromAttribute

true

AssignKeyAccessServerToValue

true

RemoveKeyAccessServerFromValue

true

+ + + +

Methods with HTTP bindings

@@ -10517,6 +10552,27 @@

KeyAccessServerRegist +

Methods with deprecated option

+
+ + + + + + + + + + + + + + +
Method NameOption
ListKeyAccessServerGrants

true

+ + + +

Methods with HTTP bindings

@@ -11303,7 +11359,7 @@

NamespaceKey

NamespaceKeyAccessServer

-

+

Deprecated

@@ -11578,6 +11634,32 @@

NamespaceService

+

Methods with deprecated option

+
+ + + + + + + + + + + + + + + + + + + +
Method NameOption
AssignKeyAccessServerToNamespace

true

RemoveKeyAccessServerFromNamespace

true

+ + + +

Methods with idempotency_level option

diff --git a/docs/openapi/policy/actions/actions.openapi.yaml b/docs/openapi/policy/actions/actions.openapi.yaml index e0171cc60a..46fa758656 100644 --- a/docs/openapi/policy/actions/actions.openapi.yaml +++ b/docs/openapi/policy/actions/actions.openapi.yaml @@ -466,7 +466,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: Deprecated + description: Deprecated KAS grants for the attribute. Use kas_keys instead. fqn: type: string title: fqn @@ -653,7 +653,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: KAS grants for the namespace + description: Deprecated KAS grants for the namespace. Use kas_keys instead. kasKeys: type: array items: @@ -900,9 +900,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: |- - Deprecated - list of key access servers + description: Deprecated KAS grants for the value. Use kas_keys instead. fqn: type: string title: fqn diff --git a/docs/openapi/policy/attributes/attributes.openapi.yaml b/docs/openapi/policy/attributes/attributes.openapi.yaml index f555d80f49..5c2c6e3971 100644 --- a/docs/openapi/policy/attributes/attributes.openapi.yaml +++ b/docs/openapi/policy/attributes/attributes.openapi.yaml @@ -137,9 +137,11 @@ paths: Fully Qualified Names of attribute values (i.e. https:///attr//value/), normalized to lower case. - name: withValue.withKeyAccessGrants in: query + description: Deprecated schema: type: boolean title: with_key_access_grants + description: Deprecated - name: withValue.withSubjectMaps in: query schema: @@ -152,9 +154,11 @@ paths: title: with_resource_maps - name: withValue.withAttribute.withKeyAccessGrants in: query + description: Deprecated schema: type: boolean title: with_key_access_grants + description: Deprecated responses: default: description: Error @@ -456,6 +460,7 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.attributes.AssignKeyAccessServerToAttributeResponse' + deprecated: true /policy.attributes.AttributesService/RemoveKeyAccessServerFromAttribute: post: tags: @@ -491,6 +496,7 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.attributes.RemoveKeyAccessServerFromAttributeResponse' + deprecated: true /policy.attributes.AttributesService/AssignKeyAccessServerToValue: post: tags: @@ -526,6 +532,7 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.attributes.AssignKeyAccessServerToValueResponse' + deprecated: true /policy.attributes.AttributesService/RemoveKeyAccessServerFromValue: post: tags: @@ -561,6 +568,7 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.attributes.RemoveKeyAccessServerFromValueResponse' + deprecated: true /policy.attributes.AttributesService/AssignPublicKeyToAttribute: post: tags: @@ -999,7 +1007,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: Deprecated + description: Deprecated KAS grants for the attribute. Use kas_keys instead. fqn: type: string title: fqn @@ -1027,6 +1035,7 @@ components: withKeyAccessGrants: type: boolean title: with_key_access_grants + description: Deprecated withSubjectMaps: type: boolean title: with_subject_maps @@ -1044,6 +1053,7 @@ components: withKeyAccessGrants: type: boolean title: with_key_access_grants + description: Deprecated withNamespace: title: with_namespace $ref: '#/components/schemas/policy.AttributeValueSelector.AttributeSelector.NamespaceSelector' @@ -1218,7 +1228,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: KAS grants for the namespace + description: Deprecated KAS grants for the namespace. Use kas_keys instead. kasKeys: type: array items: @@ -1465,9 +1475,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: |- - Deprecated - list of key access servers + description: Deprecated KAS grants for the value. Use kas_keys instead. fqn: type: string title: fqn @@ -1604,6 +1612,7 @@ components: description: Required title: AttributeKeyAccessServer additionalProperties: false + description: Deprecated policy.attributes.CreateAttributeRequest: type: object properties: @@ -1636,7 +1645,10 @@ components: uniqueItems: true title: values uniqueItems: true - description: "Optional \n Attribute values (when provided) must be alphanumeric strings, allowing hyphens and underscores but not as the first or last character.\n The stored attribute value will be normalized to lower case." + description: |- + Optional + Attribute values (when provided) must be alphanumeric strings, allowing hyphens and underscores but not as the first or last character. + The stored attribute value will be normalized to lower case. metadata: title: metadata description: Optional diff --git a/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml index a53094fec4..dd0813b260 100644 --- a/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml +++ b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml @@ -224,6 +224,7 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.kasregistry.ListKeyAccessServerGrantsResponse' + deprecated: true /policy.kasregistry.KeyAccessServerRegistryService/CreateKey: post: tags: diff --git a/docs/openapi/policy/namespaces/namespaces.openapi.yaml b/docs/openapi/policy/namespaces/namespaces.openapi.yaml index f41f301bd6..c4fb199ef4 100644 --- a/docs/openapi/policy/namespaces/namespaces.openapi.yaml +++ b/docs/openapi/policy/namespaces/namespaces.openapi.yaml @@ -216,6 +216,7 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.namespaces.AssignKeyAccessServerToNamespaceResponse' + deprecated: true /policy.namespaces.NamespaceService/RemoveKeyAccessServerFromNamespace: post: tags: @@ -251,6 +252,7 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.namespaces.RemoveKeyAccessServerFromNamespaceResponse' + deprecated: true /policy.namespaces.NamespaceService/AssignPublicKeyToNamespace: post: tags: @@ -646,7 +648,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: KAS grants for the namespace + description: Deprecated KAS grants for the namespace. Use kas_keys instead. kasKeys: type: array items: @@ -945,6 +947,7 @@ components: description: Required title: NamespaceKeyAccessServer additionalProperties: false + description: Deprecated policy.namespaces.RemoveKeyAccessServerFromNamespaceRequest: type: object properties: diff --git a/docs/openapi/policy/objects.openapi.yaml b/docs/openapi/policy/objects.openapi.yaml index af46b90964..e7803f55be 100644 --- a/docs/openapi/policy/objects.openapi.yaml +++ b/docs/openapi/policy/objects.openapi.yaml @@ -320,7 +320,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: Deprecated + description: Deprecated KAS grants for the attribute. Use kas_keys instead. fqn: type: string title: fqn @@ -565,7 +565,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: KAS grants for the namespace + description: Deprecated KAS grants for the namespace. Use kas_keys instead. kasKeys: type: array items: @@ -911,9 +911,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: |- - Deprecated - list of key access servers + description: Deprecated KAS grants for the value. Use kas_keys instead. fqn: type: string title: fqn diff --git a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml index 51e2ec8cc2..25b599a25a 100644 --- a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml +++ b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml @@ -676,7 +676,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: Deprecated + description: Deprecated KAS grants for the attribute. Use kas_keys instead. fqn: type: string title: fqn @@ -863,7 +863,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: KAS grants for the namespace + description: Deprecated KAS grants for the namespace. Use kas_keys instead. kasKeys: type: array items: @@ -1171,9 +1171,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: |- - Deprecated - list of key access servers + description: Deprecated KAS grants for the value. Use kas_keys instead. fqn: type: string title: fqn diff --git a/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml b/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml index 95748b8998..da6169647f 100644 --- a/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml +++ b/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml @@ -676,7 +676,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: Deprecated + description: Deprecated KAS grants for the attribute. Use kas_keys instead. fqn: type: string title: fqn @@ -863,7 +863,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: KAS grants for the namespace + description: Deprecated KAS grants for the namespace. Use kas_keys instead. kasKeys: type: array items: @@ -1110,9 +1110,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: |- - Deprecated - list of key access servers + description: Deprecated KAS grants for the value. Use kas_keys instead. fqn: type: string title: fqn diff --git a/docs/openapi/policy/selectors.openapi.yaml b/docs/openapi/policy/selectors.openapi.yaml index a35d717fd8..3a430300ed 100644 --- a/docs/openapi/policy/selectors.openapi.yaml +++ b/docs/openapi/policy/selectors.openapi.yaml @@ -10,6 +10,7 @@ components: withKeyAccessGrants: type: boolean title: with_key_access_grants + description: Deprecated withNamespace: title: with_namespace $ref: '#/components/schemas/policy.AttributeDefinitionSelector.NamespaceSelector' @@ -28,6 +29,7 @@ components: withKeyAccessGrants: type: boolean title: with_key_access_grants + description: Deprecated withSubjectMaps: type: boolean title: with_subject_maps @@ -50,6 +52,7 @@ components: withKeyAccessGrants: type: boolean title: with_key_access_grants + description: Deprecated withValues: title: with_values $ref: '#/components/schemas/policy.AttributeNamespaceSelector.AttributeSelector.ValueSelector' @@ -61,6 +64,7 @@ components: withKeyAccessGrants: type: boolean title: with_key_access_grants + description: Deprecated withSubjectMaps: type: boolean title: with_subject_maps @@ -75,6 +79,7 @@ components: withKeyAccessGrants: type: boolean title: with_key_access_grants + description: Deprecated withSubjectMaps: type: boolean title: with_subject_maps @@ -92,6 +97,7 @@ components: withKeyAccessGrants: type: boolean title: with_key_access_grants + description: Deprecated withNamespace: title: with_namespace $ref: '#/components/schemas/policy.AttributeValueSelector.AttributeSelector.NamespaceSelector' diff --git a/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml b/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml index c6551c63d2..8b3f52784e 100644 --- a/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml +++ b/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml @@ -712,7 +712,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: Deprecated + description: Deprecated KAS grants for the attribute. Use kas_keys instead. fqn: type: string title: fqn @@ -899,7 +899,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: KAS grants for the namespace + description: Deprecated KAS grants for the namespace. Use kas_keys instead. kasKeys: type: array items: @@ -1171,9 +1171,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: |- - Deprecated - list of key access servers + description: Deprecated KAS grants for the value. Use kas_keys instead. fqn: type: string title: fqn diff --git a/docs/openapi/policy/unsafe/unsafe.openapi.yaml b/docs/openapi/policy/unsafe/unsafe.openapi.yaml index f42d103313..88d8e107c9 100644 --- a/docs/openapi/policy/unsafe/unsafe.openapi.yaml +++ b/docs/openapi/policy/unsafe/unsafe.openapi.yaml @@ -627,7 +627,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: Deprecated + description: Deprecated KAS grants for the attribute. Use kas_keys instead. fqn: type: string title: fqn @@ -839,7 +839,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: KAS grants for the namespace + description: Deprecated KAS grants for the namespace. Use kas_keys instead. kasKeys: type: array items: @@ -1044,9 +1044,7 @@ components: items: $ref: '#/components/schemas/policy.KeyAccessServer' title: grants - description: |- - Deprecated - list of key access servers + description: Deprecated KAS grants for the value. Use kas_keys instead. fqn: type: string title: fqn diff --git a/examples/cmd/attributes.go b/examples/cmd/attributes.go index 3715e632a6..755c6de7c9 100644 --- a/examples/cmd/attributes.go +++ b/examples/cmd/attributes.go @@ -1,4 +1,4 @@ -//nolint:forbidigo,nestif // We use Println here extensively because we are printing markdown. +//nolint:forbidigo // We use Println here extensively because we are printing markdown. package cmd import ( @@ -22,7 +22,6 @@ import ( var ( ns string attr string - kases []string longformat bool rule string values []string @@ -47,18 +46,6 @@ func init() { add.Flags().StringSliceVarP(&values, "values", "v", []string{}, "list of attribute values") attributes.AddCommand(add) - assign := &cobra.Command{ - Use: "assign", - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, _ []string) error { - return assignAttribute(cmd, true) - }, - } - assign.Flags().StringVarP(&attr, "attr", "a", "", "attribute prefix, e.g. https://name.space/attr/name") - assign.Flags().StringSliceVarP(&kases, "kas", "k", []string{}, "which kas to assign") - assign.Flags().StringSliceVarP(&values, "values", "v", []string{}, "any attribute values to include; if empty, applies to all") - attributes.AddCommand(assign) - list := &cobra.Command{ Use: "list", Args: cobra.NoArgs, @@ -85,18 +72,6 @@ func init() { remove.Flags().BoolVarP(&unsafeBool, "unsafe", "f", false, "delete for real; otherwise deactivate (soft delete)") attributes.AddCommand(remove) - unassign := &cobra.Command{ - Use: "unassign", - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, _ []string) error { - return assignAttribute(cmd, false) - }, - } - unassign.Flags().StringVarP(&attr, "attr", "a", "", "attribute prefix, e.g. https://name.space/attr/name") - unassign.Flags().StringSliceVarP(&kases, "kas", "k", []string{}, "which kases to assign") - unassign.Flags().StringSliceVarP(&values, "values", "v", []string{}, "any attribute values to include; if empty, applies to all") - attributes.AddCommand(unassign) - ExamplesCmd.AddCommand(attributes) } @@ -329,136 +304,6 @@ func removeAttribute(cmd *cobra.Command) error { return nil } -func assignAttribute(cmd *cobra.Command, assign bool) error { - s, err := newSDK() - if err != nil { - slog.Error("could not connect", "err", err) - return err - } - defer s.Close() - - are := regexp.MustCompile(`^(https?://[\w./]+)/attr/([^/\s]*)$`) - m := are.FindStringSubmatch(attr) - if len(m) < 3 || len(m[0]) == 0 { - return fmt.Errorf("not a valid attribute fqn [%s]", attr) - } - auth := m[1] - nsu, err := nsuuid(cmd.Context(), s, auth) - if errors.Is(err, ErrNotFound) { - nsu, err = addNamespace(cmd.Context(), s, auth) - } - if err != nil { - return err - } - - auuid, err := attruuid(cmd.Context(), s, nsu, attr) - if err != nil { - return err - } - - kasByID := make(map[string]string) - - var kasids []string - switch { - case len(kases) != 0: - for _, kas := range kases { - kasid, err := upsertKasRegistration(cmd.Context(), s, kas, nil) - if err != nil { - return err - } - kasids = append(kasids, kasid) - kasByID[kasid] = kas - } - case assign: - return errors.New("assign must take a `--kas` parameter") - case len(values) == 0: - // look up all kasids associated with the attribute - ar, err := s.Attributes.GetAttribute(cmd.Context(), &attributes.GetAttributeRequest{Id: auuid}) - if err != nil { - return err - } - for _, b := range ar.GetAttribute().GetGrants() { - kasids = append(kasids, b.GetId()) - kasByID[b.GetId()] = b.GetUri() - } - case len(values) > 1: - return errors.New("TODO: unassign from multiple values at a time") - default: - // look up all kasids associated with the value - avu, err := avuuid(cmd.Context(), s, auuid, values[0]) - if err != nil { - return err - } - ar, err := s.Attributes.GetAttributeValue(cmd.Context(), &attributes.GetAttributeValueRequest{Id: avu}) - if err != nil { - return err - } - for _, b := range ar.GetValue().GetGrants() { - kasids = append(kasids, b.GetId()) - kasByID[b.GetId()] = b.GetUri() - } - } - - for _, kasid := range kasids { - if len(values) == 0 { - if assign { - r, err := s.Attributes.AssignKeyAccessServerToAttribute(cmd.Context(), &attributes.AssignKeyAccessServerToAttributeRequest{ - AttributeKeyAccessServer: &attributes.AttributeKeyAccessServer{ - AttributeId: auuid, - KeyAccessServerId: kasid, - }, - }) - if err != nil { - return err - } - cmd.Printf("successfully assigned all of [%s] to [%s] (binding [%v])\n", attr, kasByID[kasid], *r.GetAttributeKeyAccessServer()) - } else { - r, err := s.Attributes.RemoveKeyAccessServerFromAttribute(cmd.Context(), &attributes.RemoveKeyAccessServerFromAttributeRequest{ - AttributeKeyAccessServer: &attributes.AttributeKeyAccessServer{ - AttributeId: auuid, - KeyAccessServerId: kasid, - }, - }) - if err != nil { - return err - } - cmd.Printf("successfully unassigned [%s] from [%s] (binding %v)\n", attr, kasByID[kasid], *r.GetAttributeKeyAccessServer()) - } - } else { - for _, v := range values { - avu, err := avuuid(cmd.Context(), s, auuid, v) - if err != nil { - return err - } - if assign { - r, err := s.Attributes.AssignKeyAccessServerToValue(cmd.Context(), &attributes.AssignKeyAccessServerToValueRequest{ - ValueKeyAccessServer: &attributes.ValueKeyAccessServer{ - ValueId: avu, - KeyAccessServerId: kasid, - }, - }) - if err != nil { - return err - } - cmd.Printf("successfully assigned [%s] to [%s] (binding [%v])\n", attr, kasByID[kasid], *r.GetValueKeyAccessServer()) - } else { - r, err := s.Attributes.RemoveKeyAccessServerFromValue(cmd.Context(), &attributes.RemoveKeyAccessServerFromValueRequest{ - ValueKeyAccessServer: &attributes.ValueKeyAccessServer{ - ValueId: avu, - KeyAccessServerId: kasid, - }, - }) - if err != nil { - return err - } - cmd.Printf("successfully unassigned [%s] from [%s] (binding [%v])\n", attr, kasByID[kasid], *r.GetValueKeyAccessServer()) - } - } - } - } - return nil -} - func ruler() policy.AttributeRuleTypeEnum { switch strings.ToLower(rule) { case "allof": diff --git a/protocol/go/policy/attributes/attributes.pb.go b/protocol/go/policy/attributes/attributes.pb.go index 2d6f789cf9..3559202e26 100644 --- a/protocol/go/policy/attributes/attributes.pb.go +++ b/protocol/go/policy/attributes/attributes.pb.go @@ -24,6 +24,9 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// Deprecated +// +// Deprecated: Marked as deprecated in policy/attributes/attributes.proto. type AttributeKeyAccessServer struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -81,6 +84,7 @@ func (x *AttributeKeyAccessServer) GetKeyAccessServerId() string { return "" } +// Deprecated: Marked as deprecated in policy/attributes/attributes.proto. type ValueKeyAccessServer struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1551,6 +1555,7 @@ func (x *GetAttributeValuesByFqnsResponse) GetFqnAttributeValues() map[string]*G return nil } +// Deprecated: Marked as deprecated in policy/attributes/attributes.proto. type AssignKeyAccessServerToAttributeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1599,6 +1604,7 @@ func (x *AssignKeyAccessServerToAttributeRequest) GetAttributeKeyAccessServer() return nil } +// Deprecated: Marked as deprecated in policy/attributes/attributes.proto. type AssignKeyAccessServerToAttributeResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1646,6 +1652,7 @@ func (x *AssignKeyAccessServerToAttributeResponse) GetAttributeKeyAccessServer() return nil } +// Deprecated: Marked as deprecated in policy/attributes/attributes.proto. type RemoveKeyAccessServerFromAttributeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1694,6 +1701,7 @@ func (x *RemoveKeyAccessServerFromAttributeRequest) GetAttributeKeyAccessServer( return nil } +// Deprecated: Marked as deprecated in policy/attributes/attributes.proto. type RemoveKeyAccessServerFromAttributeResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1741,6 +1749,7 @@ func (x *RemoveKeyAccessServerFromAttributeResponse) GetAttributeKeyAccessServer return nil } +// Deprecated: Marked as deprecated in policy/attributes/attributes.proto. type AssignKeyAccessServerToValueRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1789,6 +1798,7 @@ func (x *AssignKeyAccessServerToValueRequest) GetValueKeyAccessServer() *ValueKe return nil } +// Deprecated: Marked as deprecated in policy/attributes/attributes.proto. type AssignKeyAccessServerToValueResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1836,6 +1846,7 @@ func (x *AssignKeyAccessServerToValueResponse) GetValueKeyAccessServer() *ValueK return nil } +// Deprecated: Marked as deprecated in policy/attributes/attributes.proto. type RemoveKeyAccessServerFromValueRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1884,6 +1895,7 @@ func (x *RemoveKeyAccessServerFromValueRequest) GetValueKeyAccessServer() *Value return nil } +// Deprecated: Marked as deprecated in policy/attributes/attributes.proto. type RemoveKeyAccessServerFromValueResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2384,7 +2396,7 @@ var file_policy_attributes_attributes_proto_rawDesc = []byte{ 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x16, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x82, 0x01, 0x0a, 0x18, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x86, 0x01, 0x0a, 0x18, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, @@ -2392,305 +2404,306 @@ var file_policy_attributes_attributes_proto_rawDesc = []byte{ 0x39, 0x0a, 0x14, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x11, 0x6b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x22, 0x76, 0x0a, 0x14, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x12, 0x23, 0x0a, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x07, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x14, 0x6b, 0x65, 0x79, 0x5f, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, - 0x11, 0x6b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x49, 0x64, 0x22, 0x62, 0x0a, 0x0c, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, - 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x7a, + 0x0a, 0x14, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, + 0x01, 0x01, 0x52, 0x07, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x14, 0x6b, + 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, + 0xb0, 0x01, 0x01, 0x52, 0x11, 0x6b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x62, 0x0a, 0x0c, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x0c, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0b, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x06, 0x6b, 0x65, + 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, + 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x56, + 0x0a, 0x08, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x08, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0xba, 0x48, + 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x07, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, - 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x56, 0x0a, 0x08, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4b, - 0x65, 0x79, 0x12, 0x26, 0x0a, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, - 0x01, 0x52, 0x07, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x06, 0x6b, 0x65, - 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, - 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x99, - 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x75, 0x6d, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 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, 0x81, 0x01, 0x0a, 0x16, 0x4c, - 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x0a, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 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, 0xbe, - 0x03, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x0d, 0xba, 0x48, 0x08, 0xd8, 0x01, 0x02, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x18, - 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2d, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, - 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x03, 0x66, 0x71, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, - 0x03, 0x66, 0x71, 0x6e, 0x3a, 0xaa, 0x02, 0xba, 0x48, 0xa6, 0x02, 0x1a, 0xa2, 0x01, 0x0a, 0x10, - 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, - 0x12, 0x50, 0x45, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x75, 0x73, 0x65, 0x20, 0x64, 0x65, 0x70, - 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x27, 0x69, 0x64, 0x27, 0x20, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x27, 0x20, 0x6f, 0x72, 0x20, 0x27, - 0x66, 0x71, 0x6e, 0x27, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x6f, - 0x74, 0x68, 0x1a, 0x3c, 0x21, 0x28, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x69, - 0x64, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x20, 0x7c, 0x7c, - 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x66, 0x71, 0x6e, 0x29, 0x29, 0x29, - 0x1a, 0x7f, 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x73, 0x12, 0x33, 0x45, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x69, 0x64, 0x20, 0x6f, - 0x72, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x5f, 0x69, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x66, 0x71, 0x6e, 0x20, 0x6d, 0x75, 0x73, - 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x1a, 0x37, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, - 0x69, 0x73, 0x2e, 0x69, 0x64, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, - 0x69, 0x73, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x29, - 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x66, 0x71, 0x6e, - 0x29, 0x42, 0x0c, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, - 0x47, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0xc4, 0x04, 0x0a, 0x16, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 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, 0xaf, 0x02, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x9a, 0x02, 0xba, 0x48, 0x96, 0x02, 0xba, 0x01, 0x8a, 0x02, 0x0a, 0x15, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x12, 0xb3, 0x01, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 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, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 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, 0xc8, 0x01, 0x01, 0x72, 0x03, 0x18, 0xfd, 0x01, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x1d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x42, - 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x72, 0x75, - 0x6c, 0x65, 0x12, 0x56, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x09, 0x42, 0x3e, 0xba, 0x48, 0x3b, 0x92, 0x01, 0x38, 0x08, 0x00, 0x18, 0x01, 0x22, 0x32, - 0x72, 0x30, 0x18, 0xfd, 0x01, 0x32, 0x2b, 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, 0x52, 0x06, 0x76, 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, - 0x4a, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0xbd, 0x01, 0x0a, 0x16, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 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, 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, 0x4a, 0x0a, 0x17, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0x36, 0x0a, 0x1a, 0x44, 0x65, 0x61, 0x63, 0x74, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 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, - 0x4e, 0x0a, 0x1b, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, + 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x99, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 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, 0x81, 0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, + 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 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, 0xbe, 0x03, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0xba, 0x48, 0x08, 0xd8, + 0x01, 0x02, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x18, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2d, 0x0a, + 0x0c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, + 0x0b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x03, + 0x66, 0x71, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, + 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x03, 0x66, 0x71, 0x6e, 0x3a, 0xaa, 0x02, 0xba, + 0x48, 0xa6, 0x02, 0x1a, 0xa2, 0x01, 0x0a, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, + 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x50, 0x45, 0x69, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x75, 0x73, 0x65, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, + 0x27, 0x69, 0x64, 0x27, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x6e, + 0x65, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x69, 0x64, 0x27, 0x20, 0x6f, 0x72, 0x20, 0x27, 0x66, 0x71, 0x6e, 0x27, 0x2c, 0x20, 0x62, 0x75, + 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x1a, 0x3c, 0x21, 0x28, 0x68, 0x61, + 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x69, 0x64, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x68, + 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x69, 0x64, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, + 0x73, 0x2e, 0x66, 0x71, 0x6e, 0x29, 0x29, 0x29, 0x1a, 0x7f, 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x33, 0x45, 0x69, 0x74, + 0x68, 0x65, 0x72, 0x20, 0x69, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, + 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x20, 0x6f, 0x72, + 0x20, 0x66, 0x71, 0x6e, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, + 0x1a, 0x37, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x69, 0x64, 0x29, 0x20, 0x7c, + 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, + 0x74, 0x68, 0x69, 0x73, 0x2e, 0x66, 0x71, 0x6e, 0x29, 0x42, 0x0c, 0x0a, 0x0a, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x47, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x22, 0xc4, 0x04, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 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, 0xaf, 0x02, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x9a, 0x02, 0xba, 0x48, 0x96, 0x02, 0xba, 0x01, + 0x8a, 0x02, 0x0a, 0x15, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0xb3, 0x01, 0x41, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 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, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 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, 0xc8, 0x01, 0x01, 0x72, + 0x03, 0x18, 0xfd, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x04, 0x72, 0x75, + 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, + 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x56, 0x0a, 0x06, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x42, 0x3e, 0xba, 0x48, 0x3b, 0x92, + 0x01, 0x38, 0x08, 0x00, 0x18, 0x01, 0x22, 0x32, 0x72, 0x30, 0x18, 0xfd, 0x01, 0x32, 0x2b, 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, 0x52, 0x06, 0x76, 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, 0x4a, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x22, 0xbd, 0x01, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 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, 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, 0x4a, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, - 0xab, 0x03, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0xba, 0x48, 0x08, 0xd8, 0x01, 0x02, - 0x72, 0x03, 0xb0, 0x01, 0x01, 0x18, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x08, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, - 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x07, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x03, 0x66, 0x71, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x03, 0x66, - 0x71, 0x6e, 0x3a, 0x9a, 0x02, 0xba, 0x48, 0x96, 0x02, 0x1a, 0x9a, 0x01, 0x0a, 0x10, 0x65, 0x78, - 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x4c, - 0x45, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x75, 0x73, 0x65, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, - 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x27, 0x69, 0x64, 0x27, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x5f, 0x69, 0x64, 0x27, 0x20, 0x6f, 0x72, 0x20, 0x27, 0x66, 0x71, 0x6e, 0x27, 0x2c, 0x20, - 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x1a, 0x38, 0x21, 0x28, - 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x69, 0x64, 0x29, 0x20, 0x26, 0x26, 0x20, - 0x28, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, - 0x69, 0x64, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, - 0x66, 0x71, 0x6e, 0x29, 0x29, 0x29, 0x1a, 0x77, 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x64, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x2f, 0x45, 0x69, 0x74, 0x68, 0x65, - 0x72, 0x20, 0x69, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x66, 0x71, 0x6e, 0x20, 0x6d, - 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x1a, 0x33, 0x68, 0x61, 0x73, 0x28, - 0x74, 0x68, 0x69, 0x73, 0x2e, 0x69, 0x64, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, - 0x74, 0x68, 0x69, 0x73, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x20, 0x7c, - 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x66, 0x71, 0x6e, 0x29, 0x42, - 0x0c, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x40, 0x0a, - 0x19, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0xad, 0x01, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, - 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0b, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, - 0x6e, 0x75, 0x6d, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 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, - 0x7a, 0x0a, 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, - 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 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, 0xc5, 0x03, 0x0a, 0x1b, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x0c, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0b, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x49, 0x64, 0x12, 0xb4, 0x02, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x9d, 0x02, 0xba, 0x48, 0x99, 0x02, 0xba, - 0x01, 0x8d, 0x02, 0x0a, 0x16, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0xb5, 0x01, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x76, 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, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 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, - 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, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x73, 0x22, 0x43, 0x0a, 0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xd1, 0x01, 0x0a, 0x1b, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 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, 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, 0x4a, 0x04, 0x08, - 0x04, 0x10, 0x05, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0x43, 0x0a, 0x1c, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x22, 0x3b, 0x0a, 0x1f, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, + 0x36, 0x0a, 0x1a, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 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, 0x4e, 0x0a, 0x1b, 0x44, 0x65, 0x61, 0x63, 0x74, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0xab, 0x03, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 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, 0x47, - 0x0a, 0x20, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x0d, 0xba, 0x48, 0x08, 0xd8, 0x01, 0x02, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x18, 0x01, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, + 0x00, 0x52, 0x07, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x03, 0x66, 0x71, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, + 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x03, 0x66, 0x71, 0x6e, 0x3a, 0x9a, 0x02, 0xba, 0x48, 0x96, + 0x02, 0x1a, 0x9a, 0x01, 0x0a, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x4c, 0x45, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x75, + 0x73, 0x65, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x27, 0x69, + 0x64, 0x27, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x65, 0x20, + 0x6f, 0x66, 0x20, 0x27, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x27, 0x20, 0x6f, 0x72, + 0x20, 0x27, 0x66, 0x71, 0x6e, 0x27, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, + 0x62, 0x6f, 0x74, 0x68, 0x1a, 0x38, 0x21, 0x28, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, + 0x2e, 0x69, 0x64, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, + 0x73, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x68, + 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x66, 0x71, 0x6e, 0x29, 0x29, 0x29, 0x1a, 0x77, + 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x12, 0x2f, 0x45, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x69, 0x64, 0x20, 0x6f, 0x72, 0x20, + 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x20, + 0x6f, 0x72, 0x20, 0x66, 0x71, 0x6e, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, + 0x65, 0x74, 0x1a, 0x33, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x69, 0x64, 0x29, + 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, + 0x69, 0x73, 0x2e, 0x66, 0x71, 0x6e, 0x29, 0x42, 0x0c, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x40, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x81, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x41, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, - 0x46, 0x71, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x04, 0x66, - 0x71, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x0b, 0xba, 0x48, 0x08, 0x92, 0x01, - 0x05, 0x08, 0x01, 0x10, 0xfa, 0x01, 0x52, 0x04, 0x66, 0x71, 0x6e, 0x73, 0x12, 0x3d, 0x0a, 0x0a, - 0x77, 0x69, 0x74, 0x68, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x52, 0x09, 0x77, 0x69, 0x74, 0x68, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x9b, 0x03, 0x0a, 0x20, - 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x42, 0x79, 0x46, 0x71, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x7d, 0x0a, 0x14, 0x66, 0x71, 0x6e, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x71, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x71, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x66, 0x71, 0x6e, - 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, - 0x69, 0x0a, 0x11, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x8c, 0x01, 0x0a, 0x17, 0x46, - 0x71, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x5b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, - 0x46, 0x71, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x95, 0x01, 0x0a, 0x27, 0x41, 0x73, - 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x54, 0x6f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6a, 0x0a, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x22, 0x96, 0x01, 0x0a, 0x28, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xad, 0x01, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, + 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, + 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 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, 0x7a, 0x0a, 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 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, 0xc5, 0x03, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, + 0xb0, 0x01, 0x01, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x49, 0x64, + 0x12, 0xb4, 0x02, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x9d, 0x02, 0xba, 0x48, 0x99, 0x02, 0xba, 0x01, 0x8d, 0x02, 0x0a, 0x16, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x12, 0xb5, 0x01, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, + 0x76, 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, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 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, 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, 0x4a, 0x04, 0x08, 0x03, + 0x10, 0x04, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0x43, 0x0a, 0x1c, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x22, 0xd1, 0x01, 0x0a, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 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, 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, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x52, 0x07, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x22, 0x43, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3b, 0x0a, 0x1f, 0x44, 0x65, 0x61, + 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 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, 0x47, 0x0a, 0x20, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, + 0x81, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x71, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x04, 0x66, 0x71, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x09, 0x42, 0x0b, 0xba, 0x48, 0x08, 0x92, 0x01, 0x05, 0x08, 0x01, 0x10, 0xfa, 0x01, 0x52, 0x04, + 0x66, 0x71, 0x6e, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x09, 0x77, 0x69, 0x74, 0x68, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0x9b, 0x03, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x71, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x14, 0x66, 0x71, 0x6e, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, + 0x71, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x71, 0x6e, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x12, 0x66, 0x71, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, 0x69, 0x0a, 0x11, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2f, 0x0a, 0x09, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x23, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x1a, 0x8c, 0x01, 0x0a, 0x17, 0x46, 0x71, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x5b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x45, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x71, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x41, 0x6e, + 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x99, 0x01, 0x0a, 0x27, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, - 0x0a, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x52, 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x97, 0x01, 0x0a, 0x29, 0x52, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6a, 0x0a, + 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, + 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x9a, 0x01, + 0x0a, 0x28, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x1b, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, + 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x18, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x9b, 0x01, 0x0a, 0x29, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6a, 0x0a, 0x1b, 0x61, 0x74, 0x74, 0x72, @@ -2700,34 +2713,35 @@ var file_policy_attributes_attributes_proto_rawDesc = []byte{ 0x73, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x22, 0x98, 0x01, 0x0a, 0x2a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, - 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, - 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, - 0x85, 0x01, 0x0a, 0x23, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5e, 0x0a, 0x17, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x52, 0x14, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x86, 0x01, 0x0a, 0x24, 0x41, 0x73, 0x73, 0x69, + 0x72, 0x76, 0x65, 0x72, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x9c, 0x01, 0x0a, 0x2a, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x89, 0x01, 0x0a, 0x23, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x5e, 0x0a, 0x17, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x14, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x22, 0x87, 0x01, 0x0a, 0x25, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, + 0x72, 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x5e, 0x0a, 0x17, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x27, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x14, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, + 0x02, 0x18, 0x01, 0x22, 0x8a, 0x01, 0x0a, 0x24, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, + 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x17, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x14, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, + 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x02, 0x18, 0x01, + 0x22, 0x8b, 0x01, 0x0a, 0x25, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5e, 0x0a, 0x17, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, @@ -2735,240 +2749,241 @@ var file_policy_attributes_attributes_proto_rawDesc = []byte{ 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x14, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x88, 0x01, 0x0a, 0x26, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x17, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6b, - 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, - 0x14, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x71, 0x0a, 0x21, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x6a, 0x0a, 0x22, 0x41, 0x73, 0x73, 0x69, - 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, - 0x0a, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x4b, 0x65, 0x79, 0x22, 0x73, 0x0a, 0x23, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x4b, 0x65, 0x79, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x6c, 0x0a, 0x24, 0x52, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, - 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x44, 0x0a, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x61, 0x0a, 0x1d, 0x41, 0x73, 0x73, 0x69, 0x67, - 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, - 0x52, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x5a, 0x0a, 0x1e, 0x41, 0x73, - 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x09, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x08, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x63, 0x0a, 0x1f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x09, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, - 0x01, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x5c, 0x0a, 0x20, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, - 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x38, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x52, - 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x32, 0xe6, 0x13, 0x0a, 0x11, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x6a, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x73, 0x12, 0x28, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x79, 0x0a, 0x13, 0x4c, + 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x8c, + 0x01, 0x0a, 0x26, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x17, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x52, 0x14, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x71, 0x0a, + 0x21, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x54, 0x6f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, + 0x01, 0x01, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, + 0x22, 0x6a, 0x0a, 0x22, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x73, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x0c, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x73, 0x0a, 0x23, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, + 0x72, 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x06, 0xba, 0x48, 0x03, + 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, + 0x79, 0x22, 0x6c, 0x0a, 0x24, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0d, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, + 0x79, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x22, + 0x61, 0x0a, 0x1d, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, + 0x65, 0x79, 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x40, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, + 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4b, + 0x65, 0x79, 0x22, 0x5a, 0x0a, 0x1e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x4b, 0x65, 0x79, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x63, + 0x0a, 0x1f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, + 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x40, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, + 0x79, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x4b, 0x65, 0x79, 0x22, 0x5c, 0x0a, 0x20, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4b, 0x65, + 0x79, 0x32, 0xf2, 0x13, 0x0a, 0x11, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6a, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x28, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, + 0x90, 0x02, 0x01, 0x12, 0x79, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x2d, 0x2e, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x12, 0x2d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x64, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x64, + 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0xa1, 0x01, 0x0a, - 0x18, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x71, 0x6e, 0x73, 0x12, 0x32, 0x2e, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x47, 0x65, - 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x42, 0x79, 0x46, 0x71, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x71, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x66, 0x71, 0x6e, 0x90, 0x02, 0x01, - 0x12, 0x6a, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6a, 0x0a, 0x0f, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, - 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x76, 0x0a, 0x13, 0x44, 0x65, 0x61, 0x63, - 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, - 0x2d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x73, 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x73, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x03, 0x90, 0x02, 0x01, 0x12, 0xa1, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x71, 0x6e, + 0x73, 0x12, 0x32, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x71, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x79, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2e, 0x2e, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x79, 0x46, 0x71, + 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x13, 0x12, 0x11, 0x2f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2f, + 0x2a, 0x2f, 0x66, 0x71, 0x6e, 0x90, 0x02, 0x01, 0x12, 0x6a, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x6a, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x76, 0x0a, 0x13, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x61, 0x63, + 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x79, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, + 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x47, + 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x79, 0x0a, + 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x79, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x85, 0x01, 0x0a, 0x18, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x32, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x85, 0x01, 0x0a, 0x18, - 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x32, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x61, - 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x70, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0xa0, 0x01, 0x0a, 0x20, + 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x12, 0x3a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x41, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x9d, 0x01, 0x0a, 0x20, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, - 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x41, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x3a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0xa6, + 0x01, 0x0a, 0x22, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x3c, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, + 0x72, 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, + 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, + 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x94, 0x01, 0x0a, 0x1c, 0x41, 0x73, 0x73, 0x69, + 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x36, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x54, 0x6f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, - 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, - 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0xa3, 0x01, 0x0a, 0x22, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, - 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, - 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x3c, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x91, 0x01, 0x0a, 0x1c, 0x41, 0x73, - 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x36, 0x2e, 0x70, 0x6f, 0x6c, + 0x65, 0x72, 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x37, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x9a, + 0x01, 0x0a, 0x1e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x38, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x8b, 0x01, 0x0a, 0x1a, + 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, + 0x6f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x34, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, - 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x97, 0x01, - 0x0a, 0x1e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x12, 0x38, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x70, 0x6f, 0x6c, + 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, + 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x35, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x91, 0x01, 0x0a, 0x1c, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, + 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x36, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8b, 0x01, 0x0a, 0x1a, 0x41, 0x73, 0x73, 0x69, - 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x34, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, - 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x41, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, - 0x54, 0x6f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x91, 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x36, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, - 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7f, 0x0a, 0x16, 0x41, 0x73, 0x73, - 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x12, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x85, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, - 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x32, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x70, 0x6f, + 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7f, 0x0a, + 0x16, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x54, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, + 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x41, 0x73, + 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x85, + 0x01, 0x0a, 0x18, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, + 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x32, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, - 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x42, 0xc8, 0x01, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x42, 0x0f, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, - 0x39, 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, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0xa2, 0x02, 0x03, 0x50, 0x41, 0x58, - 0xaa, 0x02, 0x11, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x73, 0xca, 0x02, 0x11, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0xe2, 0x02, 0x1d, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x5c, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5c, 0x47, 0x50, 0x42, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x3a, 0x3a, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x33, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xc8, 0x01, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x42, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x50, 0x01, 0x5a, 0x39, 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, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0xa2, 0x02, + 0x03, 0x50, 0x41, 0x58, 0xaa, 0x02, 0x11, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0xca, 0x02, 0x11, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x5c, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0xe2, 0x02, 0x1d, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x12, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x3a, 0x3a, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/protocol/go/policy/attributes/attributes_grpc.pb.go b/protocol/go/policy/attributes/attributes_grpc.pb.go index 4acc651d93..f840180945 100644 --- a/protocol/go/policy/attributes/attributes_grpc.pb.go +++ b/protocol/go/policy/attributes/attributes_grpc.pb.go @@ -61,12 +61,16 @@ type AttributesServiceClient interface { CreateAttributeValue(ctx context.Context, in *CreateAttributeValueRequest, opts ...grpc.CallOption) (*CreateAttributeValueResponse, error) UpdateAttributeValue(ctx context.Context, in *UpdateAttributeValueRequest, opts ...grpc.CallOption) (*UpdateAttributeValueResponse, error) DeactivateAttributeValue(ctx context.Context, in *DeactivateAttributeValueRequest, opts ...grpc.CallOption) (*DeactivateAttributeValueResponse, error) + // Deprecated: Do not use. // --------------------------------------* // Attribute <> Key Access Server RPCs // --------------------------------------- AssignKeyAccessServerToAttribute(ctx context.Context, in *AssignKeyAccessServerToAttributeRequest, opts ...grpc.CallOption) (*AssignKeyAccessServerToAttributeResponse, error) + // Deprecated: Do not use. RemoveKeyAccessServerFromAttribute(ctx context.Context, in *RemoveKeyAccessServerFromAttributeRequest, opts ...grpc.CallOption) (*RemoveKeyAccessServerFromAttributeResponse, error) + // Deprecated: Do not use. AssignKeyAccessServerToValue(ctx context.Context, in *AssignKeyAccessServerToValueRequest, opts ...grpc.CallOption) (*AssignKeyAccessServerToValueResponse, error) + // Deprecated: Do not use. RemoveKeyAccessServerFromValue(ctx context.Context, in *RemoveKeyAccessServerFromValueRequest, opts ...grpc.CallOption) (*RemoveKeyAccessServerFromValueResponse, error) AssignPublicKeyToAttribute(ctx context.Context, in *AssignPublicKeyToAttributeRequest, opts ...grpc.CallOption) (*AssignPublicKeyToAttributeResponse, error) RemovePublicKeyFromAttribute(ctx context.Context, in *RemovePublicKeyFromAttributeRequest, opts ...grpc.CallOption) (*RemovePublicKeyFromAttributeResponse, error) @@ -181,6 +185,7 @@ func (c *attributesServiceClient) DeactivateAttributeValue(ctx context.Context, return out, nil } +// Deprecated: Do not use. func (c *attributesServiceClient) AssignKeyAccessServerToAttribute(ctx context.Context, in *AssignKeyAccessServerToAttributeRequest, opts ...grpc.CallOption) (*AssignKeyAccessServerToAttributeResponse, error) { out := new(AssignKeyAccessServerToAttributeResponse) err := c.cc.Invoke(ctx, AttributesService_AssignKeyAccessServerToAttribute_FullMethodName, in, out, opts...) @@ -190,6 +195,7 @@ func (c *attributesServiceClient) AssignKeyAccessServerToAttribute(ctx context.C return out, nil } +// Deprecated: Do not use. func (c *attributesServiceClient) RemoveKeyAccessServerFromAttribute(ctx context.Context, in *RemoveKeyAccessServerFromAttributeRequest, opts ...grpc.CallOption) (*RemoveKeyAccessServerFromAttributeResponse, error) { out := new(RemoveKeyAccessServerFromAttributeResponse) err := c.cc.Invoke(ctx, AttributesService_RemoveKeyAccessServerFromAttribute_FullMethodName, in, out, opts...) @@ -199,6 +205,7 @@ func (c *attributesServiceClient) RemoveKeyAccessServerFromAttribute(ctx context return out, nil } +// Deprecated: Do not use. func (c *attributesServiceClient) AssignKeyAccessServerToValue(ctx context.Context, in *AssignKeyAccessServerToValueRequest, opts ...grpc.CallOption) (*AssignKeyAccessServerToValueResponse, error) { out := new(AssignKeyAccessServerToValueResponse) err := c.cc.Invoke(ctx, AttributesService_AssignKeyAccessServerToValue_FullMethodName, in, out, opts...) @@ -208,6 +215,7 @@ func (c *attributesServiceClient) AssignKeyAccessServerToValue(ctx context.Conte return out, nil } +// Deprecated: Do not use. func (c *attributesServiceClient) RemoveKeyAccessServerFromValue(ctx context.Context, in *RemoveKeyAccessServerFromValueRequest, opts ...grpc.CallOption) (*RemoveKeyAccessServerFromValueResponse, error) { out := new(RemoveKeyAccessServerFromValueResponse) err := c.cc.Invoke(ctx, AttributesService_RemoveKeyAccessServerFromValue_FullMethodName, in, out, opts...) @@ -274,12 +282,16 @@ type AttributesServiceServer interface { CreateAttributeValue(context.Context, *CreateAttributeValueRequest) (*CreateAttributeValueResponse, error) UpdateAttributeValue(context.Context, *UpdateAttributeValueRequest) (*UpdateAttributeValueResponse, error) DeactivateAttributeValue(context.Context, *DeactivateAttributeValueRequest) (*DeactivateAttributeValueResponse, error) + // Deprecated: Do not use. // --------------------------------------* // Attribute <> Key Access Server RPCs // --------------------------------------- AssignKeyAccessServerToAttribute(context.Context, *AssignKeyAccessServerToAttributeRequest) (*AssignKeyAccessServerToAttributeResponse, error) + // Deprecated: Do not use. RemoveKeyAccessServerFromAttribute(context.Context, *RemoveKeyAccessServerFromAttributeRequest) (*RemoveKeyAccessServerFromAttributeResponse, error) + // Deprecated: Do not use. AssignKeyAccessServerToValue(context.Context, *AssignKeyAccessServerToValueRequest) (*AssignKeyAccessServerToValueResponse, error) + // Deprecated: Do not use. RemoveKeyAccessServerFromValue(context.Context, *RemoveKeyAccessServerFromValueRequest) (*RemoveKeyAccessServerFromValueResponse, error) AssignPublicKeyToAttribute(context.Context, *AssignPublicKeyToAttributeRequest) (*AssignPublicKeyToAttributeResponse, error) RemovePublicKeyFromAttribute(context.Context, *RemovePublicKeyFromAttributeRequest) (*RemovePublicKeyFromAttributeResponse, error) diff --git a/protocol/go/policy/attributes/attributesconnect/attributes.connect.go b/protocol/go/policy/attributes/attributesconnect/attributes.connect.go index 2b6d378fa5..1b23fb8512 100644 --- a/protocol/go/policy/attributes/attributesconnect/attributes.connect.go +++ b/protocol/go/policy/attributes/attributesconnect/attributes.connect.go @@ -138,9 +138,14 @@ type AttributesServiceClient interface { // --------------------------------------* // Attribute <> Key Access Server RPCs // --------------------------------------- + // + // Deprecated: do not use. AssignKeyAccessServerToAttribute(context.Context, *connect.Request[attributes.AssignKeyAccessServerToAttributeRequest]) (*connect.Response[attributes.AssignKeyAccessServerToAttributeResponse], error) + // Deprecated: do not use. RemoveKeyAccessServerFromAttribute(context.Context, *connect.Request[attributes.RemoveKeyAccessServerFromAttributeRequest]) (*connect.Response[attributes.RemoveKeyAccessServerFromAttributeResponse], error) + // Deprecated: do not use. AssignKeyAccessServerToValue(context.Context, *connect.Request[attributes.AssignKeyAccessServerToValueRequest]) (*connect.Response[attributes.AssignKeyAccessServerToValueResponse], error) + // Deprecated: do not use. RemoveKeyAccessServerFromValue(context.Context, *connect.Request[attributes.RemoveKeyAccessServerFromValueRequest]) (*connect.Response[attributes.RemoveKeyAccessServerFromValueResponse], error) AssignPublicKeyToAttribute(context.Context, *connect.Request[attributes.AssignPublicKeyToAttributeRequest]) (*connect.Response[attributes.AssignPublicKeyToAttributeResponse], error) RemovePublicKeyFromAttribute(context.Context, *connect.Request[attributes.RemovePublicKeyFromAttributeRequest]) (*connect.Response[attributes.RemovePublicKeyFromAttributeResponse], error) @@ -360,24 +365,32 @@ func (c *attributesServiceClient) DeactivateAttributeValue(ctx context.Context, // AssignKeyAccessServerToAttribute calls // policy.attributes.AttributesService.AssignKeyAccessServerToAttribute. +// +// Deprecated: do not use. func (c *attributesServiceClient) AssignKeyAccessServerToAttribute(ctx context.Context, req *connect.Request[attributes.AssignKeyAccessServerToAttributeRequest]) (*connect.Response[attributes.AssignKeyAccessServerToAttributeResponse], error) { return c.assignKeyAccessServerToAttribute.CallUnary(ctx, req) } // RemoveKeyAccessServerFromAttribute calls // policy.attributes.AttributesService.RemoveKeyAccessServerFromAttribute. +// +// Deprecated: do not use. func (c *attributesServiceClient) RemoveKeyAccessServerFromAttribute(ctx context.Context, req *connect.Request[attributes.RemoveKeyAccessServerFromAttributeRequest]) (*connect.Response[attributes.RemoveKeyAccessServerFromAttributeResponse], error) { return c.removeKeyAccessServerFromAttribute.CallUnary(ctx, req) } // AssignKeyAccessServerToValue calls // policy.attributes.AttributesService.AssignKeyAccessServerToValue. +// +// Deprecated: do not use. func (c *attributesServiceClient) AssignKeyAccessServerToValue(ctx context.Context, req *connect.Request[attributes.AssignKeyAccessServerToValueRequest]) (*connect.Response[attributes.AssignKeyAccessServerToValueResponse], error) { return c.assignKeyAccessServerToValue.CallUnary(ctx, req) } // RemoveKeyAccessServerFromValue calls // policy.attributes.AttributesService.RemoveKeyAccessServerFromValue. +// +// Deprecated: do not use. func (c *attributesServiceClient) RemoveKeyAccessServerFromValue(ctx context.Context, req *connect.Request[attributes.RemoveKeyAccessServerFromValueRequest]) (*connect.Response[attributes.RemoveKeyAccessServerFromValueResponse], error) { return c.removeKeyAccessServerFromValue.CallUnary(ctx, req) } @@ -425,9 +438,14 @@ type AttributesServiceHandler interface { // --------------------------------------* // Attribute <> Key Access Server RPCs // --------------------------------------- + // + // Deprecated: do not use. AssignKeyAccessServerToAttribute(context.Context, *connect.Request[attributes.AssignKeyAccessServerToAttributeRequest]) (*connect.Response[attributes.AssignKeyAccessServerToAttributeResponse], error) + // Deprecated: do not use. RemoveKeyAccessServerFromAttribute(context.Context, *connect.Request[attributes.RemoveKeyAccessServerFromAttributeRequest]) (*connect.Response[attributes.RemoveKeyAccessServerFromAttributeResponse], error) + // Deprecated: do not use. AssignKeyAccessServerToValue(context.Context, *connect.Request[attributes.AssignKeyAccessServerToValueRequest]) (*connect.Response[attributes.AssignKeyAccessServerToValueResponse], error) + // Deprecated: do not use. RemoveKeyAccessServerFromValue(context.Context, *connect.Request[attributes.RemoveKeyAccessServerFromValueRequest]) (*connect.Response[attributes.RemoveKeyAccessServerFromValueResponse], error) AssignPublicKeyToAttribute(context.Context, *connect.Request[attributes.AssignPublicKeyToAttributeRequest]) (*connect.Response[attributes.AssignPublicKeyToAttributeResponse], error) RemovePublicKeyFromAttribute(context.Context, *connect.Request[attributes.RemovePublicKeyFromAttributeRequest]) (*connect.Response[attributes.RemovePublicKeyFromAttributeResponse], error) diff --git a/protocol/go/policy/kasregistry/kasregistryconnect/key_access_server_registry.connect.go b/protocol/go/policy/kasregistry/kasregistryconnect/key_access_server_registry.connect.go index d5917a2077..59ed07e294 100644 --- a/protocol/go/policy/kasregistry/kasregistryconnect/key_access_server_registry.connect.go +++ b/protocol/go/policy/kasregistry/kasregistryconnect/key_access_server_registry.connect.go @@ -102,6 +102,8 @@ type KeyAccessServerRegistryServiceClient interface { UpdateKeyAccessServer(context.Context, *connect.Request[kasregistry.UpdateKeyAccessServerRequest]) (*connect.Response[kasregistry.UpdateKeyAccessServerResponse], error) DeleteKeyAccessServer(context.Context, *connect.Request[kasregistry.DeleteKeyAccessServerRequest]) (*connect.Response[kasregistry.DeleteKeyAccessServerResponse], error) // Deprecated + // + // Deprecated: do not use. ListKeyAccessServerGrants(context.Context, *connect.Request[kasregistry.ListKeyAccessServerGrantsRequest]) (*connect.Response[kasregistry.ListKeyAccessServerGrantsResponse], error) // KAS Key Management // Request to create a new key in the Key Access Service. @@ -263,6 +265,8 @@ func (c *keyAccessServerRegistryServiceClient) DeleteKeyAccessServer(ctx context // ListKeyAccessServerGrants calls // policy.kasregistry.KeyAccessServerRegistryService.ListKeyAccessServerGrants. +// +// Deprecated: do not use. func (c *keyAccessServerRegistryServiceClient) ListKeyAccessServerGrants(ctx context.Context, req *connect.Request[kasregistry.ListKeyAccessServerGrantsRequest]) (*connect.Response[kasregistry.ListKeyAccessServerGrantsResponse], error) { return c.listKeyAccessServerGrants.CallUnary(ctx, req) } @@ -311,6 +315,8 @@ type KeyAccessServerRegistryServiceHandler interface { UpdateKeyAccessServer(context.Context, *connect.Request[kasregistry.UpdateKeyAccessServerRequest]) (*connect.Response[kasregistry.UpdateKeyAccessServerResponse], error) DeleteKeyAccessServer(context.Context, *connect.Request[kasregistry.DeleteKeyAccessServerRequest]) (*connect.Response[kasregistry.DeleteKeyAccessServerResponse], error) // Deprecated + // + // Deprecated: do not use. ListKeyAccessServerGrants(context.Context, *connect.Request[kasregistry.ListKeyAccessServerGrantsRequest]) (*connect.Response[kasregistry.ListKeyAccessServerGrantsResponse], error) // KAS Key Management // Request to create a new key in the Key Access Service. diff --git a/protocol/go/policy/kasregistry/key_access_server_registry.pb.go b/protocol/go/policy/kasregistry/key_access_server_registry.pb.go index 1f40fba672..629d658fa8 100644 --- a/protocol/go/policy/kasregistry/key_access_server_registry.pb.go +++ b/protocol/go/policy/kasregistry/key_access_server_registry.pb.go @@ -1655,6 +1655,8 @@ func (x *ActivatePublicKeyResponse) GetKey() *policy.Key { // does not employ selectors for grants to specific policy objects or build the // attribute tree relation. If grants to a known namespace, attribute, or value // are needed, use the respective GET request to the specific policy object. +// +// Deprecated: Marked as deprecated in policy/kasregistry/key_access_server_registry.proto. type ListKeyAccessServerGrantsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1740,6 +1742,8 @@ func (x *ListKeyAccessServerGrantsRequest) GetPagination() *policy.PageRequest { } // Deprecated +// +// Deprecated: Marked as deprecated in policy/kasregistry/key_access_server_registry.proto. type ListKeyAccessServerGrantsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3295,10 +3299,10 @@ var file_policy_kasregistry_key_access_server_registry_proto_rawDesc = []byte{ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x16, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe4, 0x03, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4b, @@ -3643,7 +3647,7 @@ var file_policy_kasregistry_key_access_server_registry_proto_rawDesc = []byte{ 0x3a, 0x0a, 0x19, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xa1, 0x07, 0x0a, 0x20, + 0x63, 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xa5, 0x07, 0x0a, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xcb, 0x01, 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, @@ -3701,484 +3705,485 @@ var file_policy_kasregistry_key_access_server_registry_proto_rawDesc = []byte{ 0x01, 0x52, 0x07, 0x6b, 0x61, 0x73, 0x4e, 0x61, 0x6d, 0x65, 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, - 0xa0, 0x01, 0x0a, 0x21, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, - 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, - 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 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, 0xb1, 0x0c, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, - 0x01, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0xa1, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, - 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, - 0x74, 0x68, 0x6d, 0x42, 0x69, 0xba, 0x48, 0x66, 0xba, 0x01, 0x63, 0x0a, 0x15, 0x6b, 0x65, 0x79, - 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, - 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, - 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, - 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x14, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, - 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x5d, 0x52, 0x0c, - 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x93, 0x01, 0x0a, - 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x0f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, - 0x42, 0x67, 0xba, 0x48, 0x64, 0xba, 0x01, 0x61, 0x0a, 0x10, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, - 0x64, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x35, 0x54, 0x68, 0x65, 0x20, - 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, - 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, - 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x28, 0x31, 0x2d, 0x34, 0x29, - 0x2e, 0x1a, 0x16, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3e, 0x3d, 0x20, 0x31, 0x20, 0x26, 0x26, 0x20, - 0x74, 0x68, 0x69, 0x73, 0x20, 0x3c, 0x3d, 0x20, 0x34, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4d, 0x6f, - 0x64, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, - 0x5f, 0x63, 0x74, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, - 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x3d, 0x0a, 0x0f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x49, 0x64, 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, 0x3a, 0xbb, 0x07, 0xba, 0x48, 0xb7, 0x07, 0x1a, - 0x97, 0x03, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, - 0x63, 0x74, 0x78, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xbc, 0x01, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, - 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, - 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, - 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, - 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, - 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x2e, 0x20, 0x54, 0x68, 0x65, - 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x6d, 0x75, 0x73, - 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, - 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, - 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, - 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, - 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xb0, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, - 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, - 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, - 0x3d, 0x20, 0x32, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x69, - 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, - 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, - 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, - 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, - 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, - 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, - 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, - 0x65, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xf4, 0x02, 0x0a, 0x26, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, - 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, - 0x69, 0x72, 0x65, 0x64, 0x12, 0xa8, 0x01, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x69, 0x64, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, - 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, - 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, - 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, - 0x54, 0x45, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, - 0x6d, 0x70, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, - 0x59, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, - 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, - 0x9e, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, - 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, - 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, - 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, - 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, - 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, - 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x29, 0x20, 0x26, - 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, - 0x1a, 0xa3, 0x01, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, - 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, - 0x6b, 0x65, 0x79, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x48, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, - 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, - 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, - 0x59, 0x2e, 0x1a, 0x32, 0x21, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, - 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x20, 0x26, 0x26, 0x20, 0x68, 0x61, 0x73, 0x28, + 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, + 0x02, 0x18, 0x01, 0x22, 0xa4, 0x01, 0x0a, 0x21, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x06, 0x67, 0x72, 0x61, + 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, + 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, + 0x61, 0x6e, 0x74, 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 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, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xb1, 0x0c, 0x0a, 0x10, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1f, 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, + 0x12, 0x1e, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, + 0x12, 0xa1, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, + 0x68, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x42, 0x69, 0xba, 0x48, 0x66, + 0xba, 0x01, 0x63, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, + 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, + 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, + 0x1a, 0x14, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, 0x32, 0x2c, + 0x20, 0x33, 0x2c, 0x20, 0x34, 0x5d, 0x52, 0x0c, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, + 0x69, 0x74, 0x68, 0x6d, 0x12, 0x93, 0x01, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x67, 0xba, 0x48, 0x64, 0xba, 0x01, 0x61, + 0x0a, 0x10, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x64, 0x12, 0x35, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, + 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x20, 0x28, 0x31, 0x2d, 0x34, 0x29, 0x2e, 0x1a, 0x16, 0x74, 0x68, 0x69, 0x73, 0x20, + 0x3e, 0x3d, 0x20, 0x31, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3c, 0x3d, 0x20, + 0x34, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, + 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x3d, + 0x0a, 0x0f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, + 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x52, 0x0d, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x2c, 0x0a, + 0x12, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x64, 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, + 0x3a, 0xbb, 0x07, 0xba, 0x48, 0xb7, 0x07, 0x1a, 0x97, 0x03, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, + 0xbc, 0x01, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, + 0x79, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, + 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, + 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, + 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, + 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, + 0x4b, 0x45, 0x59, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, + 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, + 0x74, 0x79, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, + 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, + 0x45, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, + 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xb0, + 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, + 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, + 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, - 0x5f, 0x63, 0x74, 0x78, 0x29, 0x29, 0x22, 0x3c, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, - 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, - 0x73, 0x4b, 0x65, 0x79, 0x22, 0x7a, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x42, 0x13, 0x0a, 0x0a, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, - 0x22, 0x39, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, - 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0xeb, 0x02, 0x0a, 0x0f, - 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0xa4, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, - 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x42, 0x6c, 0xba, 0x48, 0x69, 0xba, - 0x01, 0x66, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, - 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, - 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, - 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, - 0x17, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, - 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x5d, 0x52, 0x0c, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, - 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x21, 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, - 0x48, 0x00, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x08, 0x6b, 0x61, 0x73, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, - 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x07, 0x6b, 0x61, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x25, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x06, - 0x6b, 0x61, 0x73, 0x55, 0x72, 0x69, 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, 0x42, 0x0c, 0x0a, 0x0a, 0x6b, - 0x61, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x73, 0x0a, 0x10, 0x4c, 0x69, 0x73, - 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, - 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, - 0x07, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 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, 0x86, - 0x03, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 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, 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, 0x3a, 0xcc, 0x01, 0xba, 0x48, 0xc8, 0x01, 0x1a, - 0xc5, 0x01, 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, 0x12, 0x52, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x62, 0x65, - 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, - 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x20, 0x6f, 0x72, 0x20, - 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x2c, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x1a, 0x55, 0x28, 0x28, 0x21, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x29, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x68, 0x61, 0x73, - 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, - 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, - 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x29, 0x22, 0x3c, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, - 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, - 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0xa4, 0x01, 0x0a, 0x10, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, - 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x06, 0x6b, 0x61, - 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, - 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, 0x1d, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, - 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x03, - 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, - 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x19, 0x0a, 0x03, - 0x6b, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, - 0x10, 0x01, 0x52, 0x03, 0x6b, 0x69, 0x64, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0xe2, 0x0e, 0x0a, - 0x10, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 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, 0x38, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, - 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x07, 0x6e, 0x65, 0x77, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x52, 0x6f, - 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4e, - 0x65, 0x77, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x1a, 0xcc, 0x04, - 0x0a, 0x06, 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x9a, 0x01, 0x0a, 0x09, 0x61, 0x6c, 0x67, - 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x42, - 0x69, 0xba, 0x48, 0x66, 0xba, 0x01, 0x63, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, - 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x34, - 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, - 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x14, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x31, - 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x5d, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f, - 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x9e, 0x01, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, - 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x72, 0xba, 0x48, 0x6f, 0xba, 0x01, - 0x67, 0x0a, 0x14, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x39, 0x54, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, - 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, + 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, + 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, + 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x20, + 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, + 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, + 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, + 0x29, 0x1a, 0xf4, 0x02, 0x0a, 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xa8, 0x01, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x69, + 0x64, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, + 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, + 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, + 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, + 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x6d, + 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, + 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4b, 0x45, + 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, + 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0x9e, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, + 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, + 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, + 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, + 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, + 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x20, + 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, + 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, + 0x64, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xa3, 0x01, 0x0a, 0x23, 0x70, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x66, 0x6f, 0x72, + 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, + 0x12, 0x48, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, + 0x78, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, + 0x74, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, + 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, + 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0x32, 0x21, 0x28, 0x74, 0x68, + 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, + 0x20, 0x26, 0x26, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x29, 0x29, 0x22, 0x3c, + 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, + 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0x7a, 0x0a, 0x0d, + 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, + 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, + 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x39, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4b, + 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, + 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, + 0x4b, 0x65, 0x79, 0x22, 0xeb, 0x02, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xa4, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x5f, + 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, + 0x68, 0x6d, 0x42, 0x6c, 0xba, 0x48, 0x69, 0xba, 0x01, 0x66, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, + 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, + 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, + 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x17, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, + 0x20, 0x5b, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x5d, + 0x52, 0x0c, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x21, + 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, + 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, + 0x64, 0x12, 0x24, 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x07, + 0x6b, 0x61, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x75, + 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, + 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x55, 0x72, 0x69, 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, 0x42, 0x0c, 0x0a, 0x0a, 0x6b, 0x61, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, + 0x72, 0x22, 0x73, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x07, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 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, 0x86, 0x03, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x4b, 0x65, 0x79, 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, 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, + 0x3a, 0xcc, 0x01, 0xba, 0x48, 0xc8, 0x01, 0x1a, 0xc5, 0x01, 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, 0x12, 0x52, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x20, 0x6d, + 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x41, 0x50, + 0x50, 0x45, 0x4e, 0x44, 0x20, 0x6f, 0x72, 0x20, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x2c, + 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x1a, 0x55, 0x28, 0x28, 0x21, 0x68, 0x61, 0x73, + 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x29, 0x29, + 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, + 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x29, 0x22, + 0x3c, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, + 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0xa4, 0x01, + 0x0a, 0x10, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x12, 0x21, 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, + 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, + 0x03, 0x75, 0x72, 0x69, 0x12, 0x19, 0x0a, 0x03, 0x6b, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x6b, 0x69, 0x64, 0x42, + 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, + 0x48, 0x02, 0x08, 0x01, 0x22, 0xe2, 0x0e, 0x0a, 0x10, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, + 0x65, 0x79, 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, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x44, 0x0a, 0x07, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6e, + 0x65, 0x77, 0x4b, 0x65, 0x79, 0x1a, 0xcc, 0x04, 0x0a, 0x06, 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79, + 0x12, 0x1e, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, + 0x12, 0x9a, 0x01, 0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, + 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x42, 0x69, 0xba, 0x48, 0x66, 0xba, 0x01, 0x63, 0x0a, + 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, + 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x28, 0x31, 0x2d, 0x34, - 0x29, 0x2e, 0x1a, 0x14, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, - 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x5d, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x6b, - 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, - 0x79, 0x43, 0x74, 0x78, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x3d, 0x0a, 0x0f, 0x70, 0x72, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x72, 0x69, - 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x64, 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, 0x3a, 0xcd, 0x08, 0xba, - 0x48, 0xc9, 0x08, 0x1a, 0xd8, 0x03, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, - 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, - 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xcd, 0x01, 0x46, 0x6f, - 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x69, - 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, - 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, - 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, - 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, - 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, - 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, - 0x79, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, - 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, - 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x20, 0x6f, - 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, - 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xe0, 0x01, 0x28, 0x28, - 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, - 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, - 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, - 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, - 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, - 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20, - 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, - 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x20, 0x7c, 0x7c, 0x20, - 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, - 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, - 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, - 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xb5, - 0x03, 0x0a, 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, - 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xb9, 0x01, 0x46, 0x6f, 0x72, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x69, 0x64, 0x20, - 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, - 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, - 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, - 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x6d, 0x75, 0x73, - 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4b, - 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, - 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4b, 0x45, 0x59, 0x5f, - 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, - 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xce, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, + 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x14, 0x74, 0x68, + 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, + 0x34, 0x5d, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x9e, 0x01, + 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x0f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, + 0x65, 0x42, 0x72, 0xba, 0x48, 0x6f, 0xba, 0x01, 0x67, 0x0a, 0x14, 0x6e, 0x65, 0x77, 0x5f, 0x6b, + 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, + 0x39, 0x54, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, + 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x20, 0x28, 0x31, 0x2d, 0x34, 0x29, 0x2e, 0x1a, 0x14, 0x74, 0x68, 0x69, 0x73, + 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x5d, + 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x42, + 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x42, 0x06, 0xba, 0x48, + 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, + 0x74, 0x78, 0x12, 0x3d, 0x0a, 0x0f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, + 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, + 0x74, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, + 0x78, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x64, 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, 0x3a, 0xcd, 0x08, 0xba, 0x48, 0xc9, 0x08, 0x1a, 0xd8, 0x03, 0x0a, 0x23, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x12, 0xcd, 0x01, 0x46, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, + 0x77, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, + 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, + 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, + 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, + 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, + 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, + 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, + 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, + 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, + 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, + 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, + 0x4c, 0x59, 0x2e, 0x1a, 0xe0, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, + 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, + 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, + 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, + 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, + 0x79, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, + 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x21, 0x3d, + 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, - 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, + 0x3d, 0x3d, 0x20, 0x33, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, - 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c, - 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, - 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x20, 0x7c, 0x7c, - 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, - 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x29, 0x20, 0x26, 0x26, 0x20, - 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, - 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xb3, 0x01, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x48, - 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x20, - 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, - 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, - 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, - 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0x42, 0x21, 0x28, 0x74, 0x68, 0x69, 0x73, - 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, - 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x20, 0x26, 0x26, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, - 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x29, 0x29, 0x42, 0x13, 0x0a, 0x0a, - 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, - 0x01, 0x22, 0x32, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, - 0x6e, 0x67, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x71, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x66, 0x71, 0x6e, 0x22, 0xe3, 0x02, 0x0a, 0x10, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, - 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x0f, 0x72, 0x6f, - 0x74, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, - 0x4b, 0x65, 0x79, 0x52, 0x0d, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x4f, 0x75, 0x74, 0x4b, - 0x65, 0x79, 0x12, 0x66, 0x0a, 0x1d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, - 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x1b, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5c, 0x0a, 0x18, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x61, - 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x79, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, - 0x52, 0x16, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x51, 0x0a, 0x12, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, + 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, + 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, + 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xb5, 0x03, 0x0a, 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x12, 0xb9, 0x01, 0x46, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, + 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x20, 0x69, 0x64, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, + 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, + 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, + 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, + 0x2e, 0x20, 0x49, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, + 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, + 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, + 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xce, 0x01, + 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, + 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, + 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, + 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x3d, + 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, + 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, + 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, + 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, + 0x3d, 0x20, 0x33, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, + 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xb3, + 0x01, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, + 0x74, 0x78, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, + 0x79, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x48, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, + 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, + 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, + 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, + 0x1a, 0x42, 0x21, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, + 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x20, 0x26, + 0x26, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, + 0x65, 0x79, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, + 0x74, 0x78, 0x29, 0x29, 0x42, 0x13, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6b, + 0x65, 0x79, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x32, 0x0a, 0x0e, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x66, + 0x71, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x66, 0x71, 0x6e, 0x22, 0xe3, 0x02, + 0x0a, 0x10, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x12, 0x36, 0x0a, 0x0f, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x75, + 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x0d, 0x72, 0x6f, 0x74, + 0x61, 0x74, 0x65, 0x64, 0x4f, 0x75, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x66, 0x0a, 0x1d, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x70, + 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, + 0x67, 0x73, 0x12, 0x5c, 0x0a, 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x11, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x11, - 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, - 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x51, 0x0a, 0x11, 0x72, 0x6f, - 0x74, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, - 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, - 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x10, 0x72, 0x6f, 0x74, - 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0x7e, 0x0a, - 0x11, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 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, 0x38, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, - 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x42, 0x13, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, - 0x76, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x13, 0x0a, - 0x11, 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x45, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, - 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x8e, 0x01, 0x0a, 0x12, 0x53, 0x65, - 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x36, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x6e, 0x65, - 0x77, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x40, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76, - 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x69, 0x6d, - 0x70, 0x6c, 0x65, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, - 0x6f, 0x75, 0x73, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x32, 0xc4, 0x0b, 0x0a, 0x1e, 0x4b, - 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x99, 0x01, - 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x2f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x15, 0x12, 0x13, 0x2f, 0x6b, 0x65, 0x79, 0x2d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x2d, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x90, 0x02, 0x01, 0x12, 0x78, 0x0a, 0x12, 0x47, 0x65, 0x74, - 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, - 0x2d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, - 0x90, 0x02, 0x01, 0x12, 0x7e, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x30, 0x2e, 0x70, + 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x16, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, + 0x12, 0x51, 0x0a, 0x12, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6d, 0x61, + 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x30, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x30, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, + 0x79, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, + 0x52, 0x11, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, + 0x6e, 0x67, 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x11, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, 0x73, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, + 0x65, 0x79, 0x12, 0x51, 0x0a, 0x11, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x52, 0x10, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0x7e, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, + 0x4b, 0x65, 0x79, 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, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, + 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x42, 0x13, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x05, + 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x13, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x45, 0x0a, 0x12, 0x47, 0x65, + 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x2f, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x69, 0x6d, 0x70, + 0x6c, 0x65, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x4b, 0x65, + 0x79, 0x22, 0x8e, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, + 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4b, 0x61, + 0x73, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, + 0x12, 0x40, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x61, 0x73, + 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4b, 0x61, 0x73, 0x4b, 0x65, + 0x79, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x61, 0x73, 0x65, 0x4b, + 0x65, 0x79, 0x32, 0xc7, 0x0b, 0x0a, 0x1e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x99, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, + 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x2f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x8d, 0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e, 0x74, - 0x73, 0x12, 0x34, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, - 0x90, 0x02, 0x01, 0x12, 0x5a, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, - 0x12, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x6b, 0x65, 0x79, 0x2d, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x90, 0x02, + 0x01, 0x12, 0x78, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x2d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, + 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, + 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x7e, 0x0a, 0x15, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x12, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, + 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x51, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, - 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x57, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x23, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, - 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x09, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x09, 0x52, 0x6f, 0x74, 0x61, 0x74, - 0x65, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, - 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, - 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x0a, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, - 0x79, 0x12, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x53, 0x65, - 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, - 0x12, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x15, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x12, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, + 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x15, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x12, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, + 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x90, 0x01, 0x0a, 0x19, + 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x34, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x35, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0x88, 0x02, 0x01, 0x90, 0x02, 0x01, 0x12, 0x5a, + 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x06, 0x47, 0x65, + 0x74, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, + 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, - 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x42, 0xdb, 0x01, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x42, 0x1c, 0x4b, 0x65, - 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 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, 0x6b, 0x61, 0x73, - 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0xa2, 0x02, 0x03, 0x50, 0x4b, 0x58, 0xaa, 0x02, - 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0xca, 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x4b, 0x61, 0x73, - 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0xe2, 0x02, 0x1e, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x5c, 0x4b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x5c, 0x47, 0x50, - 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x13, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x3a, 0x3a, 0x4b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, + 0x08, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x23, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x4b, 0x65, 0x79, 0x12, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, + 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, + 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x09, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, + 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, + 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, + 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, + 0x0a, 0x0a, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x79, 0x2e, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, + 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, + 0x0a, 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, + 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, + 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xdb, 0x01, 0x0a, + 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x42, 0x1c, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 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, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x79, 0xa2, 0x02, 0x03, 0x50, 0x4b, 0x58, 0xaa, 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0xca, 0x02, + 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x4b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x79, 0xe2, 0x02, 0x1e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x4b, 0x61, 0x73, + 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x13, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x3a, 0x3a, 0x4b, + 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/protocol/go/policy/kasregistry/key_access_server_registry_grpc.pb.go b/protocol/go/policy/kasregistry/key_access_server_registry_grpc.pb.go index c931a42d77..843e3dca3a 100644 --- a/protocol/go/policy/kasregistry/key_access_server_registry_grpc.pb.go +++ b/protocol/go/policy/kasregistry/key_access_server_registry_grpc.pb.go @@ -43,6 +43,7 @@ type KeyAccessServerRegistryServiceClient interface { CreateKeyAccessServer(ctx context.Context, in *CreateKeyAccessServerRequest, opts ...grpc.CallOption) (*CreateKeyAccessServerResponse, error) UpdateKeyAccessServer(ctx context.Context, in *UpdateKeyAccessServerRequest, opts ...grpc.CallOption) (*UpdateKeyAccessServerResponse, error) DeleteKeyAccessServer(ctx context.Context, in *DeleteKeyAccessServerRequest, opts ...grpc.CallOption) (*DeleteKeyAccessServerResponse, error) + // Deprecated: Do not use. // Deprecated ListKeyAccessServerGrants(ctx context.Context, in *ListKeyAccessServerGrantsRequest, opts ...grpc.CallOption) (*ListKeyAccessServerGrantsResponse, error) // KAS Key Management @@ -115,6 +116,7 @@ func (c *keyAccessServerRegistryServiceClient) DeleteKeyAccessServer(ctx context return out, nil } +// Deprecated: Do not use. func (c *keyAccessServerRegistryServiceClient) ListKeyAccessServerGrants(ctx context.Context, in *ListKeyAccessServerGrantsRequest, opts ...grpc.CallOption) (*ListKeyAccessServerGrantsResponse, error) { out := new(ListKeyAccessServerGrantsResponse) err := c.cc.Invoke(ctx, KeyAccessServerRegistryService_ListKeyAccessServerGrants_FullMethodName, in, out, opts...) @@ -196,6 +198,7 @@ type KeyAccessServerRegistryServiceServer interface { CreateKeyAccessServer(context.Context, *CreateKeyAccessServerRequest) (*CreateKeyAccessServerResponse, error) UpdateKeyAccessServer(context.Context, *UpdateKeyAccessServerRequest) (*UpdateKeyAccessServerResponse, error) DeleteKeyAccessServer(context.Context, *DeleteKeyAccessServerRequest) (*DeleteKeyAccessServerResponse, error) + // Deprecated: Do not use. // Deprecated ListKeyAccessServerGrants(context.Context, *ListKeyAccessServerGrantsRequest) (*ListKeyAccessServerGrantsResponse, error) // KAS Key Management diff --git a/protocol/go/policy/namespaces/namespaces.pb.go b/protocol/go/policy/namespaces/namespaces.pb.go index e454a56dc1..0360665178 100644 --- a/protocol/go/policy/namespaces/namespaces.pb.go +++ b/protocol/go/policy/namespaces/namespaces.pb.go @@ -23,6 +23,9 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// Deprecated +// +// Deprecated: Marked as deprecated in policy/namespaces/namespaces.proto. type NamespaceKeyAccessServer struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1082,7 +1085,7 @@ var file_policy_namespaces_namespaces_proto_rawDesc = []byte{ 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x16, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x82, 0x01, 0x0a, 0x18, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x86, 0x01, 0x0a, 0x18, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 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, @@ -1090,283 +1093,283 @@ var file_policy_namespaces_namespaces_proto_rawDesc = []byte{ 0x64, 0x12, 0x39, 0x0a, 0x14, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x11, 0x6b, 0x65, 0x79, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x22, 0x62, 0x0a, 0x0c, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x0c, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, - 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x06, - 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0xba, 0x48, - 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, - 0x22, 0xbe, 0x03, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0xba, 0x48, 0x08, 0xd8, 0x01, 0x02, 0x72, 0x03, 0xb0, 0x01, - 0x01, 0x18, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2d, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, - 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x03, 0x66, 0x71, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, - 0x00, 0x52, 0x03, 0x66, 0x71, 0x6e, 0x3a, 0xaa, 0x02, 0xba, 0x48, 0xa6, 0x02, 0x1a, 0xa2, 0x01, - 0x0a, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x73, 0x12, 0x50, 0x45, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x75, 0x73, 0x65, 0x20, 0x64, - 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x27, 0x69, 0x64, 0x27, 0x20, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x27, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x27, 0x20, 0x6f, 0x72, - 0x20, 0x27, 0x66, 0x71, 0x6e, 0x27, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, - 0x62, 0x6f, 0x74, 0x68, 0x1a, 0x3c, 0x21, 0x28, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, - 0x2e, 0x69, 0x64, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, - 0x73, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x20, - 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x66, 0x71, 0x6e, 0x29, - 0x29, 0x29, 0x1a, 0x7f, 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x33, 0x45, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x69, 0x64, - 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x66, 0x71, 0x6e, 0x20, 0x6d, - 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x1a, 0x37, 0x68, 0x61, 0x73, 0x28, - 0x74, 0x68, 0x69, 0x73, 0x2e, 0x69, 0x64, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, - 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, - 0x64, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x66, - 0x71, 0x6e, 0x29, 0x42, 0x0c, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x22, 0x47, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, - 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x7b, 0x0a, 0x15, 0x4c, 0x69, - 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, - 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 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, 0x81, 0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x31, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 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, 0xfe, 0x04, 0x0a, 0x16, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xae, 0x04, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x99, 0x04, 0xba, 0x48, 0x95, 0x04, 0xba, 0x01, 0x89, 0x04, - 0x0a, 0x10, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x12, 0xa1, 0x03, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x6d, - 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x68, - 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x73, 0x68, 0x6f, 0x75, - 0x6c, 0x64, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x61, 0x74, 0x20, 0x6c, 0x65, - 0x61, 0x73, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x64, 0x6f, 0x74, 0x2c, 0x20, 0x77, 0x69, 0x74, - 0x68, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x28, - 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x29, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x20, - 0x61, 0x6e, 0x64, 0x20, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, - 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, - 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x2e, 0x20, 0x45, 0x61, 0x63, 0x68, 0x20, - 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x31, 0x20, - 0x74, 0x6f, 0x20, 0x36, 0x33, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, - 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, - 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 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, 0x74, 0x6f, 0x70, 0x2d, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x64, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x28, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x73, - 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x64, 0x6f, 0x74, 0x29, 0x20, 0x6d, 0x75, 0x73, 0x74, - 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x74, 0x20, 0x6c, - 0x65, 0x61, 0x73, 0x74, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x62, 0x65, - 0x74, 0x69, 0x63, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x20, - 0x54, 0x68, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 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, 0x51, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, - 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, - 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5c, 0x5c, - 0x2d, 0x5d, 0x7b, 0x30, 0x2c, 0x36, 0x31, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, - 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x5c, 0x5c, 0x2e, 0x29, 0x2b, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, - 0x5a, 0x5d, 0x7b, 0x32, 0x2c, 0x7d, 0x24, 0x27, 0x29, 0xc8, 0x01, 0x01, 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, 0x22, 0x4a, 0x0a, 0x17, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, + 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x3a, 0x02, 0x18, 0x01, + 0x22, 0x62, 0x0a, 0x0c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, + 0x12, 0x2e, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, + 0xb0, 0x01, 0x01, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, + 0x12, 0x22, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x05, 0x6b, + 0x65, 0x79, 0x49, 0x64, 0x22, 0xbe, 0x03, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0xba, 0x48, 0x08, 0xd8, 0x01, 0x02, + 0x72, 0x03, 0xb0, 0x01, 0x01, 0x18, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2d, 0x0a, 0x0c, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x0b, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x03, 0x66, 0x71, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, + 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x03, 0x66, 0x71, 0x6e, 0x3a, 0xaa, 0x02, 0xba, 0x48, 0xa6, + 0x02, 0x1a, 0xa2, 0x01, 0x0a, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x50, 0x45, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x75, + 0x73, 0x65, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x27, 0x69, + 0x64, 0x27, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x65, 0x20, + 0x6f, 0x66, 0x20, 0x27, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x27, 0x20, 0x6f, 0x72, 0x20, 0x27, 0x66, 0x71, 0x6e, 0x27, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, + 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x1a, 0x3c, 0x21, 0x28, 0x68, 0x61, 0x73, 0x28, + 0x74, 0x68, 0x69, 0x73, 0x2e, 0x69, 0x64, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x68, 0x61, 0x73, + 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, + 0x66, 0x71, 0x6e, 0x29, 0x29, 0x29, 0x1a, 0x7f, 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x64, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x33, 0x45, 0x69, 0x74, 0x68, 0x65, + 0x72, 0x20, 0x69, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x66, + 0x71, 0x6e, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x1a, 0x37, + 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x69, 0x64, 0x29, 0x20, 0x7c, 0x7c, 0x20, + 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, + 0x69, 0x73, 0x2e, 0x66, 0x71, 0x6e, 0x29, 0x42, 0x0c, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x47, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, + 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x7b, + 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 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, 0x81, 0x01, 0x0a, 0x16, + 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x0a, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 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, + 0xfe, 0x04, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xae, 0x04, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x99, 0x04, 0xba, 0x48, 0x95, 0x04, + 0xba, 0x01, 0x89, 0x04, 0x0a, 0x10, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0xa1, 0x03, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x20, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x20, 0x49, 0x74, 0x20, + 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x61, + 0x74, 0x20, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x64, 0x6f, 0x74, 0x2c, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x73, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x20, 0x28, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x29, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x77, + 0x69, 0x74, 0x68, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, + 0x72, 0x69, 0x63, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x2e, 0x20, 0x45, + 0x61, 0x63, 0x68, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, + 0x65, 0x20, 0x31, 0x20, 0x74, 0x6f, 0x20, 0x36, 0x33, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, + 0x74, 0x65, 0x72, 0x73, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x69, 0x6e, 0x67, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 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, 0x74, 0x6f, 0x70, 0x2d, 0x6c, 0x65, 0x76, 0x65, + 0x6c, 0x20, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x28, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, + 0x73, 0x74, 0x20, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x64, 0x6f, 0x74, 0x29, 0x20, + 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, + 0x61, 0x74, 0x20, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x62, 0x65, 0x74, 0x69, 0x63, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, + 0x72, 0x73, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 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, 0x51, 0x74, 0x68, 0x69, 0x73, + 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, + 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, + 0x2d, 0x39, 0x5c, 0x5c, 0x2d, 0x5d, 0x7b, 0x30, 0x2c, 0x36, 0x31, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, + 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x5c, 0x5c, 0x2e, 0x29, 0x2b, 0x5b, 0x61, + 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x5d, 0x7b, 0x32, 0x2c, 0x7d, 0x24, 0x27, 0x29, 0xc8, 0x01, 0x01, + 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, + 0x22, 0x4a, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xbd, 0x01, 0x0a, + 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 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, 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, 0x4a, 0x0a, 0x17, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xbd, 0x01, 0x0a, 0x16, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 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, 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, 0x4a, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x22, 0x36, 0x0a, 0x1a, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 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, 0x1d, 0x0a, 0x1b, - 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x95, 0x01, 0x0a, 0x27, - 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6a, 0x0a, 0x1b, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x18, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x22, 0x96, 0x01, 0x0a, 0x28, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, - 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x6a, 0x0a, 0x1b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6b, 0x65, - 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x52, 0x18, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x97, 0x01, 0x0a, - 0x29, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6a, 0x0a, 0x1b, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x18, 0x6e, 0x61, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x36, 0x0a, 0x1a, 0x44, 0x65, 0x61, 0x63, + 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 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, 0x1d, 0x0a, 0x1b, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x95, 0x01, 0x0a, 0x27, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6a, 0x0a, 0x1b, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, + 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x18, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x96, 0x01, 0x0a, 0x28, 0x41, 0x73, 0x73, 0x69, + 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x1b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x98, 0x01, 0x0a, 0x2a, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x18, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x1b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x18, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x22, 0x71, 0x0a, 0x21, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x06, - 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x4b, 0x65, 0x79, 0x22, 0x6a, 0x0a, 0x22, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0d, 0x6e, 0x61, + 0x22, 0x97, 0x01, 0x0a, 0x29, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6a, + 0x0a, 0x1b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x52, 0x18, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x98, 0x01, 0x0a, 0x2a, 0x52, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x1b, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, + 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x41, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x18, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x71, 0x0a, 0x21, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, - 0x65, 0x79, 0x52, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, - 0x22, 0x73, 0x0a, 0x23, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, + 0x65, 0x79, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x6a, 0x0a, 0x22, 0x41, 0x73, 0x73, 0x69, + 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, + 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x4b, 0x65, 0x79, 0x22, 0x73, 0x0a, 0x23, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x4b, 0x65, 0x79, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x6c, 0x0a, 0x24, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x44, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x32, 0xa2, 0x09, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x64, 0x0a, 0x0c, + 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x26, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, + 0x02, 0x01, 0x12, 0x6a, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0x12, 0x28, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x42, - 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x6c, 0x0a, 0x24, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, - 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x4b, 0x65, 0x79, 0x32, 0x9c, 0x09, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x64, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x27, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x6a, - 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x12, 0x28, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x6a, 0x0a, 0x0f, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x2e, + 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x6a, + 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x12, 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6a, 0x0a, 0x0f, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, + 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6a, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x76, 0x0a, 0x13, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, - 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x61, - 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x9d, 0x01, 0x0a, 0x20, 0x41, - 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x3a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x76, 0x0a, 0x13, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2d, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x73, 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0xa0, + 0x01, 0x0a, 0x20, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x12, 0x3a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, + 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x3b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, - 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0xa3, 0x01, 0x0a, 0x22, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x3c, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, + 0x01, 0x12, 0xa6, 0x01, 0x0a, 0x22, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x3c, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x8b, 0x01, 0x0a, 0x1a, 0x41, + 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x34, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x41, 0x73, + 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x3d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x8b, 0x01, 0x0a, 0x1a, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x34, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x35, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x91, - 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, - 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x36, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x42, 0xc8, 0x01, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x0f, 0x4e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, - 0x39, 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, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0xa2, 0x02, 0x03, 0x50, 0x4e, 0x58, - 0xaa, 0x02, 0x11, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x73, 0xca, 0x02, 0x11, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x4e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0xe2, 0x02, 0x1d, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x5c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x5c, 0x47, 0x50, 0x42, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x3a, 0x3a, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x91, 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x36, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, + 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x37, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x4b, 0x65, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xc8, 0x01, 0x0a, + 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x0f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x39, 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, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0xa2, 0x02, 0x03, 0x50, 0x4e, 0x58, 0xaa, 0x02, 0x11, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0xca, 0x02, + 0x11, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x73, 0xe2, 0x02, 0x1d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0xea, 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x3a, 0x3a, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/protocol/go/policy/namespaces/namespaces_grpc.pb.go b/protocol/go/policy/namespaces/namespaces_grpc.pb.go index 0e74bf7d3b..26d3c847dc 100644 --- a/protocol/go/policy/namespaces/namespaces_grpc.pb.go +++ b/protocol/go/policy/namespaces/namespaces_grpc.pb.go @@ -39,10 +39,12 @@ type NamespaceServiceClient interface { CreateNamespace(ctx context.Context, in *CreateNamespaceRequest, opts ...grpc.CallOption) (*CreateNamespaceResponse, error) UpdateNamespace(ctx context.Context, in *UpdateNamespaceRequest, opts ...grpc.CallOption) (*UpdateNamespaceResponse, error) DeactivateNamespace(ctx context.Context, in *DeactivateNamespaceRequest, opts ...grpc.CallOption) (*DeactivateNamespaceResponse, error) + // Deprecated: Do not use. // --------------------------------------* // Namespace <> Key Access Server RPCs // --------------------------------------- AssignKeyAccessServerToNamespace(ctx context.Context, in *AssignKeyAccessServerToNamespaceRequest, opts ...grpc.CallOption) (*AssignKeyAccessServerToNamespaceResponse, error) + // Deprecated: Do not use. RemoveKeyAccessServerFromNamespace(ctx context.Context, in *RemoveKeyAccessServerFromNamespaceRequest, opts ...grpc.CallOption) (*RemoveKeyAccessServerFromNamespaceResponse, error) // --------------------------------------* // Namespace <> Key RPCs @@ -104,6 +106,7 @@ func (c *namespaceServiceClient) DeactivateNamespace(ctx context.Context, in *De return out, nil } +// Deprecated: Do not use. func (c *namespaceServiceClient) AssignKeyAccessServerToNamespace(ctx context.Context, in *AssignKeyAccessServerToNamespaceRequest, opts ...grpc.CallOption) (*AssignKeyAccessServerToNamespaceResponse, error) { out := new(AssignKeyAccessServerToNamespaceResponse) err := c.cc.Invoke(ctx, NamespaceService_AssignKeyAccessServerToNamespace_FullMethodName, in, out, opts...) @@ -113,6 +116,7 @@ func (c *namespaceServiceClient) AssignKeyAccessServerToNamespace(ctx context.Co return out, nil } +// Deprecated: Do not use. func (c *namespaceServiceClient) RemoveKeyAccessServerFromNamespace(ctx context.Context, in *RemoveKeyAccessServerFromNamespaceRequest, opts ...grpc.CallOption) (*RemoveKeyAccessServerFromNamespaceResponse, error) { out := new(RemoveKeyAccessServerFromNamespaceResponse) err := c.cc.Invoke(ctx, NamespaceService_RemoveKeyAccessServerFromNamespace_FullMethodName, in, out, opts...) @@ -149,10 +153,12 @@ type NamespaceServiceServer interface { CreateNamespace(context.Context, *CreateNamespaceRequest) (*CreateNamespaceResponse, error) UpdateNamespace(context.Context, *UpdateNamespaceRequest) (*UpdateNamespaceResponse, error) DeactivateNamespace(context.Context, *DeactivateNamespaceRequest) (*DeactivateNamespaceResponse, error) + // Deprecated: Do not use. // --------------------------------------* // Namespace <> Key Access Server RPCs // --------------------------------------- AssignKeyAccessServerToNamespace(context.Context, *AssignKeyAccessServerToNamespaceRequest) (*AssignKeyAccessServerToNamespaceResponse, error) + // Deprecated: Do not use. RemoveKeyAccessServerFromNamespace(context.Context, *RemoveKeyAccessServerFromNamespaceRequest) (*RemoveKeyAccessServerFromNamespaceResponse, error) // --------------------------------------* // Namespace <> Key RPCs diff --git a/protocol/go/policy/namespaces/namespacesconnect/namespaces.connect.go b/protocol/go/policy/namespaces/namespacesconnect/namespaces.connect.go index 1eac8bbc30..e924667632 100644 --- a/protocol/go/policy/namespaces/namespacesconnect/namespaces.connect.go +++ b/protocol/go/policy/namespaces/namespacesconnect/namespaces.connect.go @@ -86,7 +86,10 @@ type NamespaceServiceClient interface { // --------------------------------------* // Namespace <> Key Access Server RPCs // --------------------------------------- + // + // Deprecated: do not use. AssignKeyAccessServerToNamespace(context.Context, *connect.Request[namespaces.AssignKeyAccessServerToNamespaceRequest]) (*connect.Response[namespaces.AssignKeyAccessServerToNamespaceResponse], error) + // Deprecated: do not use. RemoveKeyAccessServerFromNamespace(context.Context, *connect.Request[namespaces.RemoveKeyAccessServerFromNamespaceRequest]) (*connect.Response[namespaces.RemoveKeyAccessServerFromNamespaceResponse], error) // --------------------------------------* // Namespace <> Key RPCs @@ -204,12 +207,16 @@ func (c *namespaceServiceClient) DeactivateNamespace(ctx context.Context, req *c // AssignKeyAccessServerToNamespace calls // policy.namespaces.NamespaceService.AssignKeyAccessServerToNamespace. +// +// Deprecated: do not use. func (c *namespaceServiceClient) AssignKeyAccessServerToNamespace(ctx context.Context, req *connect.Request[namespaces.AssignKeyAccessServerToNamespaceRequest]) (*connect.Response[namespaces.AssignKeyAccessServerToNamespaceResponse], error) { return c.assignKeyAccessServerToNamespace.CallUnary(ctx, req) } // RemoveKeyAccessServerFromNamespace calls // policy.namespaces.NamespaceService.RemoveKeyAccessServerFromNamespace. +// +// Deprecated: do not use. func (c *namespaceServiceClient) RemoveKeyAccessServerFromNamespace(ctx context.Context, req *connect.Request[namespaces.RemoveKeyAccessServerFromNamespaceRequest]) (*connect.Response[namespaces.RemoveKeyAccessServerFromNamespaceResponse], error) { return c.removeKeyAccessServerFromNamespace.CallUnary(ctx, req) } @@ -235,7 +242,10 @@ type NamespaceServiceHandler interface { // --------------------------------------* // Namespace <> Key Access Server RPCs // --------------------------------------- + // + // Deprecated: do not use. AssignKeyAccessServerToNamespace(context.Context, *connect.Request[namespaces.AssignKeyAccessServerToNamespaceRequest]) (*connect.Response[namespaces.AssignKeyAccessServerToNamespaceResponse], error) + // Deprecated: do not use. RemoveKeyAccessServerFromNamespace(context.Context, *connect.Request[namespaces.RemoveKeyAccessServerFromNamespaceRequest]) (*connect.Response[namespaces.RemoveKeyAccessServerFromNamespaceResponse], error) // --------------------------------------* // Namespace <> Key RPCs diff --git a/protocol/go/policy/objects.pb.go b/protocol/go/policy/objects.pb.go index 70a5002bd3..f7c9e00b29 100644 --- a/protocol/go/policy/objects.pb.go +++ b/protocol/go/policy/objects.pb.go @@ -735,7 +735,7 @@ type Namespace struct { // active by default until explicitly deactivated Active *wrapperspb.BoolValue `protobuf:"bytes,4,opt,name=active,proto3" json:"active,omitempty"` Metadata *common.Metadata `protobuf:"bytes,5,opt,name=metadata,proto3" json:"metadata,omitempty"` - // KAS grants for the namespace + // Deprecated KAS grants for the namespace. Use kas_keys instead. Grants []*KeyAccessServer `protobuf:"bytes,6,rep,name=grants,proto3" json:"grants,omitempty"` // Keys for the namespace KasKeys []*SimpleKasKey `protobuf:"bytes,7,rep,name=kas_keys,json=kasKeys,proto3" json:"kas_keys,omitempty"` @@ -835,7 +835,7 @@ type Attribute struct { // attribute rule enum Rule AttributeRuleTypeEnum `protobuf:"varint,4,opt,name=rule,proto3,enum=policy.AttributeRuleTypeEnum" json:"rule,omitempty"` Values []*Value `protobuf:"bytes,5,rep,name=values,proto3" json:"values,omitempty"` - // Deprecated + // Deprecated KAS grants for the attribute. Use kas_keys instead. Grants []*KeyAccessServer `protobuf:"bytes,6,rep,name=grants,proto3" json:"grants,omitempty"` Fqn string `protobuf:"bytes,7,opt,name=fqn,proto3" json:"fqn,omitempty"` // active by default until explicitly deactivated @@ -957,8 +957,7 @@ type Value struct { Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Attribute *Attribute `protobuf:"bytes,2,opt,name=attribute,proto3" json:"attribute,omitempty"` Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` - // Deprecated - // list of key access servers + // Deprecated KAS grants for the value. Use kas_keys instead. Grants []*KeyAccessServer `protobuf:"bytes,5,rep,name=grants,proto3" json:"grants,omitempty"` Fqn string `protobuf:"bytes,6,opt,name=fqn,proto3" json:"fqn,omitempty"` // active by default until explicitly deactivated diff --git a/protocol/go/policy/selectors.pb.go b/protocol/go/policy/selectors.pb.go index 9c2e061217..20ec94de56 100644 --- a/protocol/go/policy/selectors.pb.go +++ b/protocol/go/policy/selectors.pb.go @@ -72,6 +72,7 @@ type AttributeDefinitionSelector struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Deprecated WithKeyAccessGrants bool `protobuf:"varint,1,opt,name=with_key_access_grants,json=withKeyAccessGrants,proto3" json:"with_key_access_grants,omitempty"` WithNamespace *AttributeDefinitionSelector_NamespaceSelector `protobuf:"bytes,10,opt,name=with_namespace,json=withNamespace,proto3" json:"with_namespace,omitempty"` WithValues *AttributeDefinitionSelector_ValueSelector `protobuf:"bytes,11,opt,name=with_values,json=withValues,proto3" json:"with_values,omitempty"` @@ -135,6 +136,7 @@ type AttributeValueSelector struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Deprecated WithKeyAccessGrants bool `protobuf:"varint,1,opt,name=with_key_access_grants,json=withKeyAccessGrants,proto3" json:"with_key_access_grants,omitempty"` WithSubjectMaps bool `protobuf:"varint,2,opt,name=with_subject_maps,json=withSubjectMaps,proto3" json:"with_subject_maps,omitempty"` WithResourceMaps bool `protobuf:"varint,3,opt,name=with_resource_maps,json=withResourceMaps,proto3" json:"with_resource_maps,omitempty"` @@ -333,6 +335,7 @@ type AttributeNamespaceSelector_AttributeSelector struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Deprecated WithKeyAccessGrants bool `protobuf:"varint,1,opt,name=with_key_access_grants,json=withKeyAccessGrants,proto3" json:"with_key_access_grants,omitempty"` WithValues *AttributeNamespaceSelector_AttributeSelector_ValueSelector `protobuf:"bytes,10,opt,name=with_values,json=withValues,proto3" json:"with_values,omitempty"` } @@ -388,6 +391,7 @@ type AttributeNamespaceSelector_AttributeSelector_ValueSelector struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Deprecated WithKeyAccessGrants bool `protobuf:"varint,1,opt,name=with_key_access_grants,json=withKeyAccessGrants,proto3" json:"with_key_access_grants,omitempty"` WithSubjectMaps bool `protobuf:"varint,2,opt,name=with_subject_maps,json=withSubjectMaps,proto3" json:"with_subject_maps,omitempty"` WithResourceMaps bool `protobuf:"varint,3,opt,name=with_resource_maps,json=withResourceMaps,proto3" json:"with_resource_maps,omitempty"` @@ -489,6 +493,7 @@ type AttributeDefinitionSelector_ValueSelector struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Deprecated WithKeyAccessGrants bool `protobuf:"varint,1,opt,name=with_key_access_grants,json=withKeyAccessGrants,proto3" json:"with_key_access_grants,omitempty"` WithSubjectMaps bool `protobuf:"varint,2,opt,name=with_subject_maps,json=withSubjectMaps,proto3" json:"with_subject_maps,omitempty"` WithResourceMaps bool `protobuf:"varint,3,opt,name=with_resource_maps,json=withResourceMaps,proto3" json:"with_resource_maps,omitempty"` @@ -552,6 +557,7 @@ type AttributeValueSelector_AttributeSelector struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Deprecated WithKeyAccessGrants bool `protobuf:"varint,1,opt,name=with_key_access_grants,json=withKeyAccessGrants,proto3" json:"with_key_access_grants,omitempty"` WithNamespace *AttributeValueSelector_AttributeSelector_NamespaceSelector `protobuf:"bytes,10,opt,name=with_namespace,json=withNamespace,proto3" json:"with_namespace,omitempty"` } diff --git a/service/go.mod b/service/go.mod index df265f30fa..3dd8788a03 100644 --- a/service/go.mod +++ b/service/go.mod @@ -56,6 +56,8 @@ require ( ) require ( + cel.dev/expr v0.23.1 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // indirect github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect @@ -65,15 +67,14 @@ require ( github.com/moby/go-archive v0.1.0 // indirect github.com/moby/sys/atomicwriter v0.1.0 // indirect github.com/shirou/gopsutil/v4 v4.25.1 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/vektah/gqlparser/v2 v2.5.26 // indirect ) require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250613105001-9f2d3c737feb.1 // indirect - cel.dev/expr v0.23.1 // indirect dario.cat/mergo v1.0.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect - github.com/Masterminds/semver/v3 v3.3.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect; indi\ github.com/agnivade/levenshtein v1.2.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect @@ -154,7 +155,6 @@ require ( github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect - github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tchap/go-patricia/v2 v2.3.2 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect diff --git a/service/integration/attribute_fqns_test.go b/service/integration/attribute_fqns_test.go index 8ac32ae088..bea349dfc9 100644 --- a/service/integration/attribute_fqns_test.go +++ b/service/integration/attribute_fqns_test.go @@ -142,22 +142,11 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithCasingNormalized() { fqnFixtureKey := "example.com/attr/attr1/value/value1" fullFqn := strings.ToUpper("https://" + fqnFixtureKey) valueFixture := s.f.GetAttributeValueKey(fqnFixtureKey) + key := s.f.GetKasRegistryServerKeys("kas_key_1") - // assign a KAS grant to the value - kas, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://testing_granted_values.com/kas", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Remote{ - Remote: "https://testing_granted_values.com/kas", - }, - }, - }) - s.Require().NoError(err) - s.NotNil(kas) - - grant, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, &attributes.ValueKeyAccessServer{ - KeyAccessServerId: kas.GetId(), - ValueId: valueFixture.ID, + grant, err := s.db.PolicyClient.AssignPublicKeyToValue(s.ctx, &attributes.ValueKey{ + KeyId: key.ID, + ValueId: valueFixture.ID, }) s.Require().NoError(err) s.NotNil(grant) @@ -182,7 +171,7 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithCasingNormalized() { s.GreaterOrEqual(len(v.GetGrants()), 1) found := false for _, g := range v.GetGrants() { - if g.GetId() == kas.GetId() { + if g.GetId() == key.KeyAccessServerID { found = true break } @@ -446,6 +435,8 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeysAssociatedAttributes_M } func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_Definitions() { + key := s.f.GetKasRegistryServerKeys("kas_key_1") + key2 := s.f.GetKasRegistryServerKeys("kas_key_2") // create attribute under fixture namespace id n := s.f.GetNamespaceKey("example.org") a, err := s.db.PolicyClient.CreateAttribute(s.ctx, &attributes.CreateAttributeRequest{ @@ -457,54 +448,20 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_Definition s.Require().NoError(err) s.NotNil(a) - // create a new kas registration - remoteKAS, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://example.org/kas", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Remote{ - Remote: "https://example.org/kas", - }, - }, - }) - s.Require().NoError(err) - s.NotNil(remoteKAS) - // make a first grant association to the attribute definition - grant, err := s.db.PolicyClient.AssignKeyAccessServerToAttribute(s.ctx, &attributes.AttributeKeyAccessServer{ - KeyAccessServerId: remoteKAS.GetId(), - AttributeId: a.GetId(), - }) - s.Require().NoError(err) - s.NotNil(grant) - - // create a second kas registration and grant it to the attribute definition - cachedKeyPem := "cached_key" - cachedKASName := "test_kas_name" - cachedKas, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://example.org/kas2", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Cached{ - Cached: &policy.KasPublicKeySet{ - Keys: []*policy.KasPublicKey{ - { - Pem: cachedKeyPem, - }, - }, - }, - }, - }, - Name: cachedKASName, + keyMapping, err := s.db.PolicyClient.AssignPublicKeyToAttribute(s.ctx, &attributes.AttributeKey{ + KeyId: key.ID, + AttributeId: a.GetId(), }) s.Require().NoError(err) - s.NotNil(cachedKas) + s.NotNil(keyMapping) - grant2, err := s.db.PolicyClient.AssignKeyAccessServerToAttribute(s.ctx, &attributes.AttributeKeyAccessServer{ - KeyAccessServerId: cachedKas.GetId(), - AttributeId: a.GetId(), + keyMapping2, err := s.db.PolicyClient.AssignPublicKeyToAttribute(s.ctx, &attributes.AttributeKey{ + KeyId: key2.ID, + AttributeId: a.GetId(), }) - cachedKasID := grant2.GetKeyAccessServerId() s.Require().NoError(err) - s.NotNil(grant2) + s.NotNil(keyMapping2) // get the attribute by the fqn of the attribute definition got, err := s.db.PolicyClient.GetAttributeByFqn(s.ctx, "https://example.org/attr/attr_with_grants") @@ -512,17 +469,25 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_Definition s.NotNil(got) // ensure the attribute has the grants - s.Len(got.GetGrants(), 2) - grantIDs := []string{remoteKAS.GetId(), cachedKas.GetId()} + s.Len(got.GetGrants(), 1) + // Ensure we get 2 public keys because it's the same KAS + s.Len(got.GetGrants()[0].GetPublicKey().GetCached().GetKeys(), 2) + keyIDs := []string{key.KeyID, key2.KeyID} + s.Contains(keyIDs, got.GetGrants()[0].GetPublicKey().GetCached().GetKeys()[0].GetKid()) + s.Contains(keyIDs, got.GetGrants()[0].GetPublicKey().GetCached().GetKeys()[1].GetKid()) + grantIDs := []string{key.KeyAccessServerID, key2.KeyAccessServerID} s.Contains(grantIDs, got.GetGrants()[0].GetId()) - s.Contains(grantIDs, got.GetGrants()[1].GetId()) - s.NotEqual(got.GetGrants()[0].GetId(), got.GetGrants()[1].GetId()) - // ensure grant has cached key pem pemIsPresent := false + for _, g := range got.GetGrants() { - if g.GetId() == cachedKasID { - s.Equal(g.GetPublicKey().GetCached().GetKeys()[0].GetPem(), cachedKeyPem) - s.Equal(g.GetName(), cachedKASName) + if g.GetId() == key2.KeyAccessServerID { + decodedPubKey, err := base64.StdEncoding.DecodeString(key2.PublicKeyCtx) + s.Require().NoError(err) + s.JSONEq( + strings.TrimRight(string(decodedPubKey), "\n"), + fmt.Sprintf("{\"pem\":\"%s\"}", base64.StdEncoding.EncodeToString([]byte(g.GetPublicKey().GetCached().GetKeys()[0].GetPem()))), + ) + s.Equal(g.GetId(), key2.KeyAccessServerID) pemIsPresent = true } } @@ -532,12 +497,12 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_Definition got, err = s.db.PolicyClient.GetAttributeByFqn(s.ctx, "https://example.org/attr/attr_with_grants/value/value1") s.Require().NoError(err) s.NotNil(got) - s.Len(got.GetGrants(), 2) + s.Len(got.GetGrants(), 1) // assign a KAS to the value and make sure it is not granted to the definition - grant3, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, &attributes.ValueKeyAccessServer{ - KeyAccessServerId: s.f.GetKasRegistryKey("key_access_server_1").ID, - ValueId: got.GetValues()[0].GetId(), + grant3, err := s.db.PolicyClient.AssignPublicKeyToValue(s.ctx, &attributes.ValueKey{ + KeyId: key.ID, + ValueId: got.GetValues()[0].GetId(), }) s.NotNil(grant3) s.Require().NoError(err) @@ -545,10 +510,12 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_Definition got, err = s.db.PolicyClient.GetAttributeByFqn(s.ctx, "https://example.org/attr/attr_with_grants/value/value1") s.Require().NoError(err) s.NotNil(got) - s.Len(got.GetGrants(), 2) + s.Len(got.GetGrants(), 1) } func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_Values() { + key := s.f.GetKasRegistryServerKeys("kas_key_1") + key2 := s.f.GetKasRegistryServerKeys("kas_key_2") attrName := "attr_with_values_grants" attrFqn := "https://example.org/attr/" + attrName // create attribute under fixture namespace id @@ -589,51 +556,17 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_Values() { s.Empty(got.GetGrants()) s.Empty(got.GetValues()[0].GetGrants()) - // create a new kas registration - remoteKASName := "testing-io-remote" - remoteKAS, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://testing.io/kas", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Remote{ - Remote: "https://testing.org/kas", - }, - }, - Name: remoteKASName, - }) - s.Require().NoError(err) - s.NotNil(remoteKAS) - // make a grant association to the first value - grant, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, &attributes.ValueKeyAccessServer{ - KeyAccessServerId: remoteKAS.GetId(), - ValueId: valueFirst.GetId(), + grant, err := s.db.PolicyClient.AssignPublicKeyToValue(s.ctx, &attributes.ValueKey{ + KeyId: key.ID, + ValueId: valueFirst.GetId(), }) s.Require().NoError(err) s.NotNil(grant) - // create a second kas registration and grant it to the second value - cachedKASName := "testion-io-local" - cachedKAS, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://testing.io/kas2", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Cached{ - Cached: &policy.KasPublicKeySet{ - Keys: []*policy.KasPublicKey{ - { - Pem: "local_key", - }, - }, - }, - }, - }, - Name: cachedKASName, - }) - s.Require().NoError(err) - s.NotNil(cachedKAS) - - grant2, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, &attributes.ValueKeyAccessServer{ - KeyAccessServerId: cachedKAS.GetId(), - ValueId: valueSecond.GetId(), + grant2, err := s.db.PolicyClient.AssignPublicKeyToValue(s.ctx, &attributes.ValueKey{ + KeyId: key2.ID, + ValueId: valueSecond.GetId(), }) s.Require().NoError(err) s.NotNil(grant2) @@ -659,11 +592,9 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_Values() { firstGrant := grants[0] switch v.GetId() { case valueFirst.GetId(): - s.Equal(remoteKAS.GetId(), firstGrant.GetId()) - s.Equal(remoteKASName, firstGrant.GetName()) + s.Equal(key.KeyAccessServerID, firstGrant.GetId()) case valueSecond.GetId(): - s.Equal(cachedKAS.GetId(), firstGrant.GetId()) - s.Equal(cachedKASName, firstGrant.GetName()) + s.Equal(key2.KeyAccessServerID, firstGrant.GetId()) default: s.Fail("unexpected value", v) } @@ -671,6 +602,9 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_Values() { } func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_DefAndValuesGrantsBoth() { + key := s.f.GetKasRegistryServerKeys("kas_key_1") + key2 := s.f.GetKasRegistryServerKeys("kas_key_2") + key3 := s.f.GetKasRegistryServerKeys("kas_key_3") attrName := "def_and_vals_grants" attrFqn := "https://example.org/attr/" + attrName // create attribute under fixture namespace id @@ -711,72 +645,24 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_DefAndValu s.Empty(got.GetGrants()) s.Empty(got.GetValues()[0].GetGrants()) - // create a new kas registration - valKAS1, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://testing.org/kas", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Remote{ - Remote: "https://testing.org/kas", - }, - }, - }) - s.Require().NoError(err) - s.NotNil(valKAS1) - // make a grant association to the first value - grant, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, &attributes.ValueKeyAccessServer{ - KeyAccessServerId: valKAS1.GetId(), - ValueId: valueFirst.GetId(), + grant, err := s.db.PolicyClient.AssignPublicKeyToValue(s.ctx, &attributes.ValueKey{ + KeyId: key.ID, + ValueId: valueFirst.GetId(), }) s.Require().NoError(err) s.NotNil(grant) - // create a second kas registration and grant it to the second value - valKAS2, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://testing.org/kas2", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Cached{ - Cached: &policy.KasPublicKeySet{ - Keys: []*policy.KasPublicKey{ - { - Pem: "local_key", - }, - }, - }, - }, - }, - }) - s.Require().NoError(err) - s.NotNil(valKAS2) - - grant2, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, &attributes.ValueKeyAccessServer{ - KeyAccessServerId: valKAS2.GetId(), - ValueId: valueSecond.GetId(), + grant2, err := s.db.PolicyClient.AssignPublicKeyToValue(s.ctx, &attributes.ValueKey{ + KeyId: key2.ID, + ValueId: valueSecond.GetId(), }) s.Require().NoError(err) s.NotNil(grant2) - // create a third kas registration and grant it to the attribute definition - defKAS, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://testing.org/kas3", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Cached{ - Cached: &policy.KasPublicKeySet{ - Keys: []*policy.KasPublicKey{ - { - Pem: "local_key", - }, - }, - }, - }, - }, - }) - s.Require().NoError(err) - s.NotNil(defKAS) - - defGrant, err := s.db.PolicyClient.AssignKeyAccessServerToAttribute(s.ctx, &attributes.AttributeKeyAccessServer{ - KeyAccessServerId: defKAS.GetId(), - AttributeId: a.GetId(), + defGrant, err := s.db.PolicyClient.AssignPublicKeyToAttribute(s.ctx, &attributes.AttributeKey{ + KeyId: key3.ID, + AttributeId: a.GetId(), }) s.Require().NoError(err) s.NotNil(defGrant) @@ -788,7 +674,7 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_DefAndValu // ensure the attribute has exactly one definition grant s.Len(got.GetGrants(), 1) - s.Equal(defKAS.GetId(), got.GetGrants()[0].GetId()) + s.Equal(key3.KeyAccessServerID, got.GetGrants()[0].GetId()) // get the attribute by the fqn of one of its values and ensure the grants are present got, err = s.db.PolicyClient.GetAttributeByFqn(s.ctx, val1Fqn) @@ -796,16 +682,16 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_DefAndValu s.NotNil(got) s.Len(got.GetValues(), 2) s.Len(got.GetGrants(), 1) - s.Equal(defKAS.GetId(), got.GetGrants()[0].GetId()) + s.Equal(key3.KeyAccessServerID, got.GetGrants()[0].GetId()) for _, v := range got.GetValues() { switch v.GetId() { case valueFirst.GetId(): s.Require().Len(v.GetGrants(), 1) - s.Equal(valKAS1.GetId(), v.GetGrants()[0].GetId()) + s.Equal(key.KeyAccessServerID, v.GetGrants()[0].GetId()) case valueSecond.GetId(): s.Require().Len(v.GetGrants(), 1) - s.Equal(valKAS2.GetId(), v.GetGrants()[0].GetId()) + s.Equal(key2.KeyAccessServerID, v.GetGrants()[0].GetId()) default: s.Fail("unexpected value", v) } @@ -813,6 +699,7 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_DefAndValu } func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_NamespaceGrants() { + key := s.f.GetKasRegistryServerKeys("kas_key_1") // create a new namespace ns, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{ Name: "test_fqn_namespace.net", @@ -830,24 +717,10 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_NamespaceG s.Require().NoError(err) s.NotNil(attr) - // create a new kas registration - nsKASName := "namespace-kas1" - kas, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://testing_granted_namespace.com/kas", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Remote{ - Remote: "https://testing_granted_namespace.com/kas", - }, - }, - Name: nsKASName, - }) - s.Require().NoError(err) - s.NotNil(kas) - // make a grant association to the namespace - grant, err := s.db.PolicyClient.AssignKeyAccessServerToNamespace(s.ctx, &namespaces.NamespaceKeyAccessServer{ - KeyAccessServerId: kas.GetId(), - NamespaceId: ns.GetId(), + grant, err := s.db.PolicyClient.AssignPublicKeyToNamespace(s.ctx, &namespaces.NamespaceKey{ + KeyId: key.ID, + NamespaceId: ns.GetId(), }) s.Require().NoError(err) s.NotNil(grant) @@ -861,24 +734,29 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_NamespaceG gotNs := got.GetNamespace() grants := gotNs.GetGrants() s.Len(grants, 1) - s.Equal(kas.GetId(), grants[0].GetId()) - s.Equal(nsKASName, grants[0].GetName()) + s.Equal(key.KeyAccessServerID, grants[0].GetId()) } // for all the big tests set up: // attribute name is "test_attr", values are "value1" and "value2" // kas uris granted to each are "https://testing_granted_.com//kas", +type KasAssociations struct { + kasID string + uri string + keyID string + keyUUID string +} type bigSetup struct { - attrFqn string - nsID string - attrID string - val1ID string - val2ID string - kasAssociations map[string]string - rms map[string]struct { + attrFqn string + nsID string + attrID string + val1ID string + val2ID string + rms map[string]struct { Terms []string GroupID string } + kasAssociations map[string]*KasAssociations } func (s *AttributeFqnSuite) TestGetAttributeByFqn_SameResultsWhetherAttrOrValueFqnUsed() { @@ -936,51 +814,51 @@ func (s *AttributeFqnSuite) TestGetAttributeByFqn_WithKeyAccessGrants_ProperOnAl // ensure the namespace has the grants s.Len(got.GetNamespace().GetGrants(), 1) nsGrant := got.GetNamespace().GetGrants()[0] - s.Equal(setup.kasAssociations[got.GetNamespace().GetId()], nsGrant.GetId()) + s.Equal(setup.kasAssociations[got.GetNamespace().GetId()].kasID, nsGrant.GetId()) s.Equal(fmt.Sprintf("https://testing_granted_ns.com/%s/kas", ns), nsGrant.GetUri()) // ensure the attribute has the grants s.Len(got.GetGrants(), 1) attrGrant := got.GetGrants()[0] - s.Equal(setup.kasAssociations[got.GetId()], attrGrant.GetId()) + s.Equal(setup.kasAssociations[got.GetId()].kasID, attrGrant.GetId()) s.Equal(fmt.Sprintf("https://testing_granted_attr.com/%s/kas", ns), attrGrant.GetUri()) // ensure the first value has the grants val1 := got.GetValues()[0] s.Len(val1.GetGrants(), 1) val1Grant := val1.GetGrants()[0] - s.Equal(setup.kasAssociations[val1.GetId()], val1Grant.GetId()) + s.Equal(setup.kasAssociations[val1.GetId()].kasID, val1Grant.GetId()) s.Equal(fmt.Sprintf("https://testing_granted_val.com/%s/kas", ns), val1Grant.GetUri()) // ensure the second value has the grants val2 := got.GetValues()[1] s.Len(val2.GetGrants(), 1) val2Grant := val2.GetGrants()[0] - s.Equal(setup.kasAssociations[val2.GetId()], val2Grant.GetId()) + s.Equal(setup.kasAssociations[val2.GetId()].kasID, val2Grant.GetId()) s.Equal(fmt.Sprintf("https://testing_granted_val2.com/%s/kas", ns), val2Grant.GetUri()) // remove grants from all objects - _, err = s.db.PolicyClient.RemoveKeyAccessServerFromNamespace(s.ctx, &namespaces.NamespaceKeyAccessServer{ - KeyAccessServerId: nsGrant.GetId(), - NamespaceId: got.GetNamespace().GetId(), + _, err = s.db.PolicyClient.RemovePublicKeyFromNamespace(s.ctx, &namespaces.NamespaceKey{ + KeyId: setup.kasAssociations[got.GetNamespace().GetId()].keyUUID, + NamespaceId: got.GetNamespace().GetId(), }) s.Require().NoError(err) - _, err = s.db.PolicyClient.RemoveKeyAccessServerFromAttribute(s.ctx, &attributes.AttributeKeyAccessServer{ - KeyAccessServerId: attrGrant.GetId(), - AttributeId: got.GetId(), + _, err = s.db.PolicyClient.RemovePublicKeyFromAttribute(s.ctx, &attributes.AttributeKey{ + KeyId: setup.kasAssociations[got.GetId()].keyUUID, + AttributeId: got.GetId(), }) s.Require().NoError(err) - _, err = s.db.PolicyClient.RemoveKeyAccessServerFromValue(s.ctx, &attributes.ValueKeyAccessServer{ - KeyAccessServerId: val1Grant.GetId(), - ValueId: val1.GetId(), + _, err = s.db.PolicyClient.RemovePublicKeyFromValue(s.ctx, &attributes.ValueKey{ + KeyId: setup.kasAssociations[val1.GetId()].keyUUID, + ValueId: val1.GetId(), }) s.Require().NoError(err) - _, err = s.db.PolicyClient.RemoveKeyAccessServerFromValue(s.ctx, &attributes.ValueKeyAccessServer{ - KeyAccessServerId: val2Grant.GetId(), - ValueId: val2.GetId(), + _, err = s.db.PolicyClient.RemovePublicKeyFromValue(s.ctx, &attributes.ValueKey{ + KeyId: setup.kasAssociations[val2.GetId()].keyUUID, + ValueId: val2.GetId(), }) s.Require().NoError(err) @@ -1812,7 +1690,7 @@ func (s *AttributeFqnSuite) bigTestSetup(namespaceName string) bigSetup { val1KasURI := fmt.Sprintf("https://testing_granted_val.com/%s/kas", namespaceName) val2KasURI := fmt.Sprintf("https://testing_granted_val2.com/%s/kas", namespaceName) - kasAssociations := map[string]string{} + kasAssociations := map[string]*KasAssociations{} // create new KASes for _, toAssociate := range []struct { id string @@ -1833,37 +1711,57 @@ func (s *AttributeFqnSuite) bigTestSetup(namespaceName string) bigSetup { }) s.Require().NoError(err) s.NotNil(kas) - kasAssociations[toAssociate.id] = kas.GetId() + + req := kasregistry.CreateKeyRequest{ + KasId: kas.GetId(), + KeyId: "big_test_key", + KeyAlgorithm: policy.Algorithm_ALGORITHM_EC_P256, + KeyMode: policy.KeyMode_KEY_MODE_CONFIG_ROOT_KEY, + PublicKeyCtx: &policy.PublicKeyCtx{Pem: keyCtx}, + PrivateKeyCtx: &policy.PrivateKeyCtx{ + WrappedKey: keyCtx, + }, + } + resp, err := s.db.PolicyClient.CreateKey(s.ctx, &req) + s.Require().NoError(err) + s.NotNil(resp) + + kasAssociations[toAssociate.id] = &KasAssociations{ + kasID: kas.GetId(), + uri: toAssociate.uri, + keyID: resp.GetKasKey().GetKey().GetKeyId(), + keyUUID: resp.GetKasKey().GetKey().GetId(), + } } // make a grant association to the namespace - nsGrant, err := s.db.PolicyClient.AssignKeyAccessServerToNamespace(s.ctx, &namespaces.NamespaceKeyAccessServer{ - KeyAccessServerId: kasAssociations[ns.GetId()], - NamespaceId: ns.GetId(), + nsGrant, err := s.db.PolicyClient.AssignPublicKeyToNamespace(s.ctx, &namespaces.NamespaceKey{ + KeyId: kasAssociations[ns.GetId()].keyUUID, + NamespaceId: ns.GetId(), }) s.Require().NoError(err) s.NotNil(nsGrant) // make a grant association to the attribute definition - attrGrant, err := s.db.PolicyClient.AssignKeyAccessServerToAttribute(s.ctx, &attributes.AttributeKeyAccessServer{ - KeyAccessServerId: kasAssociations[attr.GetId()], - AttributeId: attr.GetId(), + attrGrant, err := s.db.PolicyClient.AssignPublicKeyToAttribute(s.ctx, &attributes.AttributeKey{ + KeyId: kasAssociations[attr.GetId()].keyUUID, + AttributeId: attr.GetId(), }) s.Require().NoError(err) s.NotNil(attrGrant) // make a grant association to the first value - val1Grant, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, &attributes.ValueKeyAccessServer{ - KeyAccessServerId: kasAssociations[val1.GetId()], - ValueId: val1.GetId(), + val1Grant, err := s.db.PolicyClient.AssignPublicKeyToValue(s.ctx, &attributes.ValueKey{ + KeyId: kasAssociations[val1.GetId()].keyUUID, + ValueId: val1.GetId(), }) s.Require().NoError(err) s.NotNil(val1Grant) // make a grant association to the second value - val2Grant, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, &attributes.ValueKeyAccessServer{ - KeyAccessServerId: kasAssociations[val2.GetId()], - ValueId: val2.GetId(), + val2Grant, err := s.db.PolicyClient.AssignPublicKeyToValue(s.ctx, &attributes.ValueKey{ + KeyId: kasAssociations[val2.GetId()].keyUUID, + ValueId: val2.GetId(), }) s.Require().NoError(err) s.NotNil(val2Grant) diff --git a/service/integration/attribute_values_test.go b/service/integration/attribute_values_test.go index d9a7e55894..32f2226c13 100644 --- a/service/integration/attribute_values_test.go +++ b/service/integration/attribute_values_test.go @@ -265,43 +265,6 @@ func (s *AttributeValuesSuite) Test_GetAttributeValue_NotFound() { } } -func (s *AttributeValuesSuite) Test_GetAttributeValue_ContainsKASGrants() { - // create a value with KAS grants - attrDef := s.f.GetAttributeKey("example.net/attr/attr1") - value := &attributes.CreateAttributeValueRequest{ - Value: "kas_grants_test", - } - createdValue, err := s.db.PolicyClient.CreateAttributeValue(s.ctx, attrDef.ID, value) - s.Require().NoError(err) - s.NotNil(createdValue) - - // ensure it has no grants - got, err := s.db.PolicyClient.GetAttributeValue(s.ctx, createdValue.GetId()) - s.Require().NoError(err) - s.NotNil(got) - s.Empty(got.GetGrants()) - - fixtureKeyAccessServer := s.f.GetKasRegistryKey("key_access_server_1") - fixtureKeyAccessServerID := fixtureKeyAccessServer.ID - assignment := &attributes.ValueKeyAccessServer{ - ValueId: createdValue.GetId(), - KeyAccessServerId: fixtureKeyAccessServerID, - } - grant, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, assignment) - s.Require().NoError(err) - s.NotNil(grant) - - // get the value and ensure it contains the grants - got, err = s.db.PolicyClient.GetAttributeValue(s.ctx, createdValue.GetId()) - s.Require().NoError(err) - s.NotNil(got) - s.Equal(createdValue.GetId(), got.GetId()) - gotGrants := got.GetGrants() - s.Len(gotGrants, 1) - s.Equal(fixtureKeyAccessServerID, gotGrants[0].GetId()) - s.Equal(fixtureKeyAccessServer.Name, gotGrants[0].GetName()) -} - func (s *AttributeValuesSuite) Test_CreateAttributeValue_SetsActiveStateTrueByDefault() { attrDef := s.f.GetAttributeKey("example.net/attr/attr1") @@ -891,84 +854,6 @@ func (s *AttributeValuesSuite) Test_UnsafeReactivateAttributeValue_DoesNotReacti s.False(gotVal.GetActive().GetValue()) } -func (s *AttributeValuesSuite) Test_AssignKeyAccessServerToValue_Returns_Error_When_Value_Not_Found() { - v := &attributes.ValueKeyAccessServer{ - ValueId: absentAttributeValueUUID, - KeyAccessServerId: fixtureKeyAccessServerID, - } - - resp, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, v) - - s.Require().Error(err) - s.Nil(resp) - s.Require().ErrorIs(err, db.ErrForeignKeyViolation) -} - -func (s *AttributeValuesSuite) Test_AssignKeyAccessServerToValue_Returns_Error_When_KeyAccessServer_Not_Found() { - v := &attributes.ValueKeyAccessServer{ - ValueId: s.f.GetAttributeValueKey("example.net/attr/attr1/value/value1").ID, - KeyAccessServerId: nonExistentKasRegistryID, - } - - resp, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, v) - - s.Require().Error(err) - s.Nil(resp) - s.Require().ErrorIs(err, db.ErrForeignKeyViolation) -} - -func (s *AttributeValuesSuite) Test_AssignKeyAccessServerToValue_Returns_Success_When_Value_And_KeyAccessServer_Exist() { - v := &attributes.ValueKeyAccessServer{ - ValueId: s.f.GetAttributeValueKey("example.net/attr/attr1/value/value1").ID, - KeyAccessServerId: fixtureKeyAccessServerID, - } - - resp, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, v) - - s.Require().NoError(err) - s.NotNil(resp) - s.Equal(v, resp) -} - -func (s *AttributeValuesSuite) Test_RemoveKeyAccessServerFromValue_Returns_Error_When_Value_Not_Found() { - v := &attributes.ValueKeyAccessServer{ - ValueId: absentAttributeValueUUID, - KeyAccessServerId: fixtureKeyAccessServerID, - } - - resp, err := s.db.PolicyClient.RemoveKeyAccessServerFromValue(s.ctx, v) - - s.Require().Error(err) - s.Nil(resp) - s.Require().ErrorIs(err, db.ErrNotFound) -} - -func (s *AttributeValuesSuite) Test_RemoveKeyAccessServerFromValue_Returns_Error_When_KeyAccessServer_Not_Found() { - v := &attributes.ValueKeyAccessServer{ - ValueId: s.f.GetAttributeValueKey("example.net/attr/attr1/value/value1").ID, - KeyAccessServerId: nonExistentAttrID, - } - - resp, err := s.db.PolicyClient.RemoveKeyAccessServerFromValue(s.ctx, v) - - s.Require().Error(err) - s.Nil(resp) - s.Require().ErrorIs(err, db.ErrNotFound) -} - -func (s *AttributeValuesSuite) Test_RemoveKeyAccessServerFromValue_Returns_Success_When_Value_And_KeyAccessServer_Exist() { - v := &attributes.ValueKeyAccessServer{ - ValueId: s.f.GetAttributeValueKey("example.com/attr/attr1/value/value1").ID, - KeyAccessServerId: s.f.GetKasRegistryKey("key_access_server_1").ID, - } - - resp, err := s.db.PolicyClient.RemoveKeyAccessServerFromValue(s.ctx, v) - - s.Require().NoError(err) - s.NotNil(resp) - s.Equal(v, resp) -} - // Add tests for assinging key to value / removing key from value func (s *AttributeValuesSuite) Test_AssignPublicKeyToAttributeValue_Returns_Error_When_Attribute_Not_Found() { diff --git a/service/integration/attributes_test.go b/service/integration/attributes_test.go index b3618a40d3..daadaf5822 100644 --- a/service/integration/attributes_test.go +++ b/service/integration/attributes_test.go @@ -370,52 +370,6 @@ func (s *AttributesSuite) Test_GetAttribute_Deactivated_Succeeds() { s.False(gotAttr.GetActive().GetValue()) } -func (s *AttributesSuite) Test_GetAttribute_ContainsKASGrants() { - // create an attribute - attr := &attributes.CreateAttributeRequest{ - Name: "test__get_attribute_contains_kas_grants", - NamespaceId: fixtureNamespaceID, - Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, - } - createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, attr) - s.Require().NoError(err) - s.NotNil(createdAttr) - - // create a KAS - kas := &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://example.com/kas", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Remote{ - Remote: "https://example.com/kas/key/1", - }, - }, - Name: "def_kas-name", - } - createdKAS, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, kas) - s.Require().NoError(err) - s.NotNil(createdKAS) - - // create a grant for the KAS - assignment := &attributes.AttributeKeyAccessServer{ - AttributeId: createdAttr.GetId(), - KeyAccessServerId: createdKAS.GetId(), - } - - createdGrant, err := s.db.PolicyClient.AssignKeyAccessServerToAttribute(s.ctx, assignment) - s.Require().NoError(err) - s.NotNil(createdGrant) - - // get the attribute & ensure it contains the grant - gotAttr, err := s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId()) - s.Require().NoError(err) - s.NotNil(gotAttr) - - gotGrants := gotAttr.GetGrants() - s.Len(gotGrants, 1) - s.Equal(createdKAS.GetId(), gotGrants[0].GetId()) - s.Equal(kas.GetName(), gotGrants[0].GetName()) -} - func (s *AttributesSuite) Test_ListAttributes_NoPagination_Succeeds() { fixtures := s.getAttributeFixtures() @@ -1243,78 +1197,6 @@ func (s *AttributesSuite) Test_UnsafeReactivateAttribute_WithInvalidIdFails() { s.Require().ErrorIs(err, db.ErrNotFound) } -func (s *AttributesSuite) Test_AssignKeyAccessServerToAttribute_Returns_Error_When_Attribute_Not_Found() { - aKas := &attributes.AttributeKeyAccessServer{ - AttributeId: nonExistentAttrID, - KeyAccessServerId: fixtureKeyAccessServerID, - } - resp, err := s.db.PolicyClient.AssignKeyAccessServerToAttribute(s.ctx, aKas) - - s.Require().Error(err) - s.Nil(resp) - s.Require().ErrorIs(err, db.ErrForeignKeyViolation) -} - -func (s *AttributesSuite) Test_AssignKeyAccessServerToAttribute_Returns_Error_When_KeyAccessServer_Not_Found() { - aKas := &attributes.AttributeKeyAccessServer{ - AttributeId: s.f.GetAttributeKey("example.com/attr/attr1").ID, - KeyAccessServerId: nonExistentAttrID, - } - resp, err := s.db.PolicyClient.AssignKeyAccessServerToAttribute(s.ctx, aKas) - - s.Require().Error(err) - s.Nil(resp) - s.Require().ErrorIs(err, db.ErrForeignKeyViolation) -} - -func (s *AttributesSuite) Test_AssignKeyAccessServerToAttribute_Returns_Success_When_Attribute_And_KeyAccessServer_Exist() { - aKas := &attributes.AttributeKeyAccessServer{ - AttributeId: s.f.GetAttributeKey("example.com/attr/attr2").ID, - KeyAccessServerId: fixtureKeyAccessServerID, - } - resp, err := s.db.PolicyClient.AssignKeyAccessServerToAttribute(s.ctx, aKas) - - s.Require().NoError(err) - s.NotNil(resp) - s.Equal(aKas, resp) -} - -func (s *AttributesSuite) Test_RemoveKeyAccessServerFromAttribute_Returns_Error_When_Attribute_Not_Found() { - aKas := &attributes.AttributeKeyAccessServer{ - AttributeId: nonExistentAttrID, - KeyAccessServerId: fixtureKeyAccessServerID, - } - resp, err := s.db.PolicyClient.RemoveKeyAccessServerFromAttribute(s.ctx, aKas) - - s.Require().Error(err) - s.Nil(resp) - s.Require().ErrorIs(err, db.ErrNotFound) -} - -func (s *AttributesSuite) Test_RemoveKeyAccessServerFromAttribute_Returns_Error_When_KeyAccessServer_Not_Found() { - aKas := &attributes.AttributeKeyAccessServer{ - AttributeId: s.f.GetAttributeKey("example.com/attr/attr1").ID, - KeyAccessServerId: nonExistentAttrID, - } - resp, err := s.db.PolicyClient.RemoveKeyAccessServerFromAttribute(s.ctx, aKas) - - s.Require().Error(err) - s.Nil(resp) - s.Require().ErrorIs(err, db.ErrNotFound) -} - -func (s *AttributesSuite) Test_RemoveKeyAccessServerFromAttribute_Returns_Success_When_Attribute_And_KeyAccessServer_Exist() { - aKas := &attributes.AttributeKeyAccessServer{ - AttributeId: s.f.GetAttributeKey("example.com/attr/attr1").ID, - KeyAccessServerId: s.f.GetKasRegistryKey("key_access_server_1").ID, - } - resp, err := s.db.PolicyClient.RemoveKeyAccessServerFromAttribute(s.ctx, aKas) - - s.Require().NoError(err) - s.NotNil(resp) - s.Equal(aKas, resp) -} - func (s *AttributesSuite) Test_AssociatePublicKeyToAttribute_Returns_Error_When_Attribute_Not_Found() { kasKeys := s.f.GetKasRegistryServerKeys("kas_key_1") resp, err := s.db.PolicyClient.AssignPublicKeyToAttribute(s.ctx, &attributes.AttributeKey{ diff --git a/service/integration/kas_registry_test.go b/service/integration/kas_registry_test.go index a321c71c2d..d67c71e660 100644 --- a/service/integration/kas_registry_test.go +++ b/service/integration/kas_registry_test.go @@ -2,17 +2,14 @@ package integration import ( "context" - "fmt" "log/slog" "strings" "testing" "github.com/opentdf/platform/protocol/go/common" "github.com/opentdf/platform/protocol/go/policy" - "github.com/opentdf/platform/protocol/go/policy/attributes" "github.com/opentdf/platform/protocol/go/policy/kasregistry" - "github.com/opentdf/platform/protocol/go/policy/namespaces" "github.com/opentdf/platform/service/internal/fixtures" "github.com/opentdf/platform/service/pkg/db" "google.golang.org/protobuf/proto" @@ -814,435 +811,6 @@ func (s *KasRegistrySuite) Test_DeleteKeyAccessServer_WithInvalidId_Fails() { s.Require().ErrorIs(err, db.ErrUUIDInvalid) } -func (s *KasRegistrySuite) Test_ListKeyAccessServerGrants_KasId() { - // create an attribute - attr := &attributes.CreateAttributeRequest{ - Name: "test__list_key_access_server_grants_by_kas_id", - NamespaceId: fixtureNamespaceID, - Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, - } - createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, attr) - s.Require().NoError(err) - s.NotNil(createdAttr) - - // create a value - val := &attributes.CreateAttributeValueRequest{ - AttributeId: createdAttr.GetId(), - Value: "value2", - } - createdVal, err := s.db.PolicyClient.CreateAttributeValue(s.ctx, createdAttr.GetId(), val) - s.Require().NoError(err) - s.NotNil(createdVal) - - firstKAS, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://firstkas.com/kas/uri", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Cached{ - Cached: &policy.KasPublicKeySet{ - Keys: []*policy.KasPublicKey{ - { - Pem: "public", - }, - }, - }, - }, - }, - Name: "first_kas", - }) - s.Require().NoError(err) - s.NotNil(firstKAS) - firstKAS, _ = s.db.PolicyClient.GetKeyAccessServer(s.ctx, firstKAS.GetId()) - - otherKAS, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://otherkas.com/kas/uri", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Cached{ - Cached: &policy.KasPublicKeySet{ - Keys: []*policy.KasPublicKey{ - { - Pem: "public", - }, - }, - }, - }, - }, - // Leave off 'name' to test optionality - }) - s.Require().NoError(err) - otherKAS, _ = s.db.PolicyClient.GetKeyAccessServer(s.ctx, otherKAS.GetId()) - - // assign a KAS to the attribute - aKas := &attributes.AttributeKeyAccessServer{ - AttributeId: createdAttr.GetId(), - KeyAccessServerId: firstKAS.GetId(), - } - createdGrant, err := s.db.PolicyClient.AssignKeyAccessServerToAttribute(s.ctx, aKas) - s.Require().NoError(err) - s.NotNil(createdGrant) - - // assign a KAS to the value - bKas := &attributes.ValueKeyAccessServer{ - ValueId: createdVal.GetId(), - KeyAccessServerId: otherKAS.GetId(), - } - valGrant, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, bKas) - s.Require().NoError(err) - s.NotNil(valGrant) - - // list grants by KAS ID - listRsp, err := s.db.PolicyClient.ListKeyAccessServerGrants(s.ctx, &kasregistry.ListKeyAccessServerGrantsRequest{ - KasId: firstKAS.GetId(), - }) - s.Require().NoError(err) - s.NotNil(listRsp) - - listedGrants := listRsp.GetGrants() //nolint:staticcheck // still needed for testing - s.Len(listedGrants, 1) - g := listedGrants[0] - s.Equal(firstKAS.GetId(), g.GetKeyAccessServer().GetId()) - s.Equal(firstKAS.GetUri(), g.GetKeyAccessServer().GetUri()) - s.Equal(firstKAS.GetName(), g.GetKeyAccessServer().GetName()) - s.Len(g.GetAttributeGrants(), 1) - s.Empty(g.GetValueGrants()) - s.Empty(g.GetNamespaceGrants()) - - // list grants by the other KAS ID - listRsp, err = s.db.PolicyClient.ListKeyAccessServerGrants(s.ctx, &kasregistry.ListKeyAccessServerGrantsRequest{ - KasId: otherKAS.GetId(), - }) - s.Require().NoError(err) - s.NotNil(listRsp) - - listedGrants = listRsp.GetGrants() //nolint:staticcheck // still needed for testing - s.Len(listedGrants, 1) - g = listedGrants[0] - s.Equal(otherKAS.GetId(), g.GetKeyAccessServer().GetId()) - s.Equal(otherKAS.GetUri(), g.GetKeyAccessServer().GetUri()) - s.Empty(g.GetKeyAccessServer().GetName()) - s.Empty(g.GetAttributeGrants()) - s.Len(g.GetValueGrants(), 1) - s.Empty(g.GetNamespaceGrants()) -} - -func (s *KasRegistrySuite) Test_ListKeyAccessServerGrants_KasId_NoResultsIfNotFound() { - // list grants by KAS ID - listRsp, err := s.db.PolicyClient.ListKeyAccessServerGrants(s.ctx, &kasregistry.ListKeyAccessServerGrantsRequest{ - KasId: nonExistentKasRegistryID, - }) - s.Require().NoError(err) - s.Empty(listRsp.GetGrants()) //nolint:staticcheck // still needed for testing -} - -func (s *KasRegistrySuite) Test_ListKeyAccessServerGrants_KasUri() { - fixtureKAS := s.f.GetKasRegistryKey("key_access_server_1") - - // create an attribute - attr := &attributes.CreateAttributeRequest{ - Name: "test__list_key_access_server_grants_by_kas_uri", - NamespaceId: fixtureNamespaceID, - Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, - } - createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, attr) - s.Require().NoError(err) - s.NotNil(createdAttr) - - // add a KAS to the attribute - aKas := &attributes.AttributeKeyAccessServer{ - AttributeId: createdAttr.GetId(), - KeyAccessServerId: fixtureKAS.ID, - } - createdGrant, err := s.db.PolicyClient.AssignKeyAccessServerToAttribute(s.ctx, aKas) - s.Require().NoError(err) - s.NotNil(createdGrant) - - // list grants by KAS URI - listGrantsRsp, err := s.db.PolicyClient.ListKeyAccessServerGrants(s.ctx, &kasregistry.ListKeyAccessServerGrantsRequest{ - KasUri: fixtureKAS.URI, - }) - s.Require().NoError(err) - s.NotNil(listGrantsRsp) - listedGrants := listGrantsRsp.GetGrants() //nolint:staticcheck // still needed for testing - s.GreaterOrEqual(len(listedGrants), 1) - for _, g := range listedGrants { - s.Equal(fixtureKAS.ID, g.GetKeyAccessServer().GetId()) - s.Equal(fixtureKAS.URI, g.GetKeyAccessServer().GetUri()) - s.Equal(fixtureKAS.Name, g.GetKeyAccessServer().GetName()) - } -} - -func (s *KasRegistrySuite) Test_ListKeyAccessServerGrants_KasUri_NoResultsIfNotFound() { - // list grants by KAS ID - listGrantsRsp, err := s.db.PolicyClient.ListKeyAccessServerGrants(s.ctx, &kasregistry.ListKeyAccessServerGrantsRequest{ - KasUri: "https://notfound.com/kas/uri", - }) - s.Require().NoError(err) - s.Empty(listGrantsRsp.GetGrants()) //nolint:staticcheck // still needed for testing -} - -func (s *KasRegistrySuite) Test_ListKeyAccessServerGrants_KasName() { - fixtureKAS := s.f.GetKasRegistryKey("key_access_server_acme") - - // create an attribute - attr := &attributes.CreateAttributeRequest{ - Name: "test__list_key_access_server_grants_by_kas_name", - NamespaceId: fixtureNamespaceID, - Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF, - } - createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, attr) - s.Require().NoError(err) - s.NotNil(createdAttr) - - // add a KAS to the attribute - aKas := &attributes.AttributeKeyAccessServer{ - AttributeId: createdAttr.GetId(), - KeyAccessServerId: fixtureKAS.ID, - } - createdGrant, err := s.db.PolicyClient.AssignKeyAccessServerToAttribute(s.ctx, aKas) - s.Require().NoError(err) - s.NotNil(createdGrant) - - // list grants by KAS URI - listedGrantsRsp, err := s.db.PolicyClient.ListKeyAccessServerGrants(s.ctx, - &kasregistry.ListKeyAccessServerGrantsRequest{ - KasName: fixtureKAS.Name, - }) - - s.Require().NoError(err) - s.NotNil(listedGrantsRsp) - listedGrants := listedGrantsRsp.GetGrants() //nolint:staticcheck // still needed for testing - s.GreaterOrEqual(len(listedGrants), 1) - found := false - for _, g := range listedGrants { - if g.GetKeyAccessServer().GetId() == fixtureKAS.ID { - s.Equal(fixtureKAS.URI, g.GetKeyAccessServer().GetUri()) - s.Equal(fixtureKAS.Name, g.GetKeyAccessServer().GetName()) - for _, attrGrant := range g.GetAttributeGrants() { - if attrGrant.GetId() == createdAttr.GetId() { - found = true - s.Equal(attrGrant.GetFqn(), createdAttr.GetFqn()) - } - } - } - } - s.True(found) -} - -func (s *KasRegistrySuite) Test_ListKeyAccessServerGrants_KasName_NoResultsIfNotFound() { - // list grants by KAS ID - listGrantsRsp, err := s.db.PolicyClient.ListKeyAccessServerGrants(s.ctx, &kasregistry.ListKeyAccessServerGrantsRequest{ - KasName: "unknown-name", - }) - s.Require().NoError(err) - s.Empty(listGrantsRsp.GetGrants()) //nolint:staticcheck // still needed for testing -} - -func (s *KasRegistrySuite) Test_ListAllKeyAccessServerGrants() { - // create a KAS - kas := &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://listingkasgrants.com/kas/uri", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Cached{ - Cached: &policy.KasPublicKeySet{ - Keys: []*policy.KasPublicKey{ - { - Pem: "public", - }, - }, - }, - }, - }, - Name: "listingkasgrants", - } - firstKAS, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, kas) - s.Require().NoError(err) - s.NotNil(firstKAS) - - // create a second KAS - second := &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://listingkasgrants.com/another/kas/uri", - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Cached{ - Cached: &policy.KasPublicKeySet{ - Keys: []*policy.KasPublicKey{ - { - Pem: "public", - }, - }, - }, - }, - }, - Name: "listingkasgrants_second", - } - secondKAS, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, second) - s.Require().NoError(err) - s.NotNil(secondKAS) - - // create a new namespace - ns := &namespaces.CreateNamespaceRequest{ - Name: "test__list_all_kas_grants", - } - createdNs, err := s.db.PolicyClient.CreateNamespace(s.ctx, ns) - s.Require().NoError(err) - s.NotNil(createdNs) - nsFQN := "https://" + ns.GetName() - - // create an attribute - attr := &attributes.CreateAttributeRequest{ - Name: "test_attr_list_all_kas_grants", - NamespaceId: createdNs.GetId(), - Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, - Values: []string{"value1"}, - } - createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, attr) - s.Require().NoError(err) - s.NotNil(createdAttr) - attrFQN := fmt.Sprintf("%s/attr/%s", nsFQN, attr.GetName()) - - got, err := s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId()) - s.Require().NoError(err) - s.NotNil(got) - value := got.GetValues()[0] - valueFQN := fmt.Sprintf("%s/value/%s", attrFQN, value.GetValue()) - - // add first KAS to the attribute - aKas := &attributes.AttributeKeyAccessServer{ - AttributeId: createdAttr.GetId(), - KeyAccessServerId: firstKAS.GetId(), - } - createdGrant, err := s.db.PolicyClient.AssignKeyAccessServerToAttribute(s.ctx, aKas) - s.Require().NoError(err) - s.NotNil(createdGrant) - - // assign a grant of the second KAS to the value - bKas := &attributes.ValueKeyAccessServer{ - ValueId: value.GetId(), - KeyAccessServerId: secondKAS.GetId(), - } - valGrant, err := s.db.PolicyClient.AssignKeyAccessServerToValue(s.ctx, bKas) - s.Require().NoError(err) - s.NotNil(valGrant) - - // grant each KAS to the namespace - nsKas := &namespaces.NamespaceKeyAccessServer{ - NamespaceId: createdNs.GetId(), - KeyAccessServerId: firstKAS.GetId(), - } - nsGrant, err := s.db.PolicyClient.AssignKeyAccessServerToNamespace(s.ctx, nsKas) - s.Require().NoError(err) - s.NotNil(nsGrant) - - nsAnotherKas := &namespaces.NamespaceKeyAccessServer{ - NamespaceId: createdNs.GetId(), - KeyAccessServerId: secondKAS.GetId(), - } - nsAnotherGrant, err := s.db.PolicyClient.AssignKeyAccessServerToNamespace(s.ctx, nsAnotherKas) - s.Require().NoError(err) - s.NotNil(nsAnotherGrant) - - // list all grants - listGrantsRsp, err := s.db.PolicyClient.ListKeyAccessServerGrants(s.ctx, &kasregistry.ListKeyAccessServerGrantsRequest{}) - s.Require().NoError(err) - s.NotNil(listGrantsRsp) - listedGrants := listGrantsRsp.GetGrants() //nolint:staticcheck // still needed for testing - s.GreaterOrEqual(len(listedGrants), 1) - - s.GreaterOrEqual(len(listedGrants), 2) - foundCount := 0 - for _, g := range listedGrants { - switch g.GetKeyAccessServer().GetId() { - case firstKAS.GetId(): - // should have expected sole attribute grant - s.Len(g.GetAttributeGrants(), 1) - s.Equal(createdAttr.GetId(), g.GetAttributeGrants()[0].GetId()) - s.Equal(attrFQN, g.GetAttributeGrants()[0].GetFqn()) - // should have expected sole namespace grant - s.Len(g.GetNamespaceGrants(), 1) - s.Equal(createdNs.GetId(), g.GetNamespaceGrants()[0].GetId()) - s.Equal(nsFQN, g.GetNamespaceGrants()[0].GetFqn()) - - foundCount++ - - case secondKAS.GetId(): - // should have expected value grant - s.Len(g.GetValueGrants(), 1) - s.Equal(value.GetId(), g.GetValueGrants()[0].GetId()) - s.Equal(valueFQN, g.GetValueGrants()[0].GetFqn()) - // should have expected namespace grant - s.Len(g.GetNamespaceGrants(), 1) - s.Equal(createdNs.GetId(), g.GetNamespaceGrants()[0].GetId()) - s.Equal(nsFQN, g.GetNamespaceGrants()[0].GetFqn()) - - foundCount++ - } - } - s.Equal(2, foundCount) -} - -func (s *KasRegistrySuite) Test_ListKeyAccessServerGrants_Limit_Succeeds() { - var limit int32 = 2 - listRsp, err := s.db.PolicyClient.ListKeyAccessServerGrants(s.ctx, &kasregistry.ListKeyAccessServerGrantsRequest{ - Pagination: &policy.PageRequest{ - Limit: limit, - }, - }) - s.Require().NoError(err) - s.NotNil(listRsp) - - listed := listRsp.GetGrants() //nolint:staticcheck // still needed for testing - s.Equal(len(listed), int(limit)) - - for _, grant := range listed { - s.NotNil(grant.GetKeyAccessServer()) - } - - // request with one below maximum - listRsp, err = s.db.PolicyClient.ListKeyAccessServerGrants(s.ctx, &kasregistry.ListKeyAccessServerGrantsRequest{ - Pagination: &policy.PageRequest{ - Limit: s.db.LimitMax - 1, - }, - }) - s.Require().NoError(err) - s.NotNil(listRsp) -} - -func (s *NamespacesSuite) Test_ListKeyAccessServerGrants_Limit_TooLarge_Fails() { - listRsp, err := s.db.PolicyClient.ListKeyAccessServerGrants(s.ctx, &kasregistry.ListKeyAccessServerGrantsRequest{ - Pagination: &policy.PageRequest{ - Limit: s.db.LimitMax + 1, - }, - }) - s.Require().Error(err) - s.Require().ErrorIs(err, db.ErrListLimitTooLarge) - s.Nil(listRsp) -} - -func (s *KasRegistrySuite) Test_ListKeyAccessServerGrants_Offset_Succeeds() { - req := &kasregistry.ListKeyAccessServerGrantsRequest{} - // make initial list request to compare against - listRsp, err := s.db.PolicyClient.ListKeyAccessServerGrants(s.ctx, req) - s.Require().NoError(err) - s.NotNil(listRsp) - listed := listRsp.GetGrants() //nolint:staticcheck // still needed for testing - - // set the offset pagination - offset := 1 - req.Pagination = &policy.PageRequest{ - Offset: int32(offset), - } - offsetListRsp, err := s.db.PolicyClient.ListKeyAccessServerGrants(s.ctx, req) - s.Require().NoError(err) - s.NotNil(offsetListRsp) - offsetListed := offsetListRsp.GetGrants() //nolint:staticcheck // still needed for testing - - // length is reduced by the offset amount - s.Equal(len(offsetListed), len(listed)-offset) - - // objects are equal between offset and original list beginning at offset index - for i, val := range offsetListed { - s.True(proto.Equal(val, listed[i+offset])) - } -} - func (s *KasRegistrySuite) getKasRegistryFixtures() []fixtures.FixtureDataKasRegistry { return []fixtures.FixtureDataKasRegistry{ s.f.GetKasRegistryKey("key_access_server_1"), diff --git a/service/integration/namespaces_test.go b/service/integration/namespaces_test.go index 3b31d5b5a1..7c368f6d38 100644 --- a/service/integration/namespaces_test.go +++ b/service/integration/namespaces_test.go @@ -900,255 +900,6 @@ func (s *NamespacesSuite) Test_UnsafeUpdateNamespace_NormalizeCasing() { s.Equal("https://helloworld.com", got.GetFqn()) } -/* - Key Access Server Grant Assignments (KAS Grants) -*/ - -func (s *NamespacesSuite) Test_AssignKASGrant() { - n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "kas-namespace.com"}) - s.Require().NoError(err) - s.NotNil(n) - - // test no grants on the namespace - got, err := s.db.PolicyClient.GetNamespace(s.ctx, n.GetId()) - s.Require().NoError(err) - s.NotNil(got) - s.Empty(got.GetGrants()) - - pubKey := &policy.PublicKey{ - PublicKey: &policy.PublicKey_Remote{ - Remote: "https://remote.com/key", - }, - } - kasRegistry := &kasregistry.CreateKeyAccessServerRequest{ - Uri: "kas.uri/ns", - PublicKey: pubKey, - Name: "kas-name-ns", - } - kas, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, kasRegistry) - s.Require().NoError(err) - s.NotNil(kas) - - // assign a grant - grant, err := s.db.PolicyClient.AssignKeyAccessServerToNamespace(s.ctx, &namespaces.NamespaceKeyAccessServer{ - NamespaceId: n.GetId(), - KeyAccessServerId: kas.GetId(), - }) - s.Require().NoError(err) - s.NotNil(grant) - - // get the namespace and verify the grant is present - got, err = s.db.PolicyClient.GetNamespace(s.ctx, n.GetId()) - s.Require().NoError(err) - s.NotNil(got) - s.Len(got.GetGrants(), 1) - s.Equal(kas.GetId(), got.GetGrants()[0].GetId()) - s.Equal(kasRegistry.GetName(), got.GetGrants()[0].GetName()) -} - -func (s *NamespacesSuite) Test_AssignKASGrant_FailsInvalidIds() { - n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "kas-failure-namespace.com"}) - s.Require().NoError(err) - s.NotNil(n) - - pubKey := &policy.PublicKey{ - PublicKey: &policy.PublicKey_Remote{ - Remote: "https://testingassignmentfailure.com/key", - }, - } - kasRegistry := &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://testingassignmentfailure.com", - PublicKey: pubKey, - } - kas, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, kasRegistry) - s.Require().NoError(err) - s.NotNil(kas) - - // ns doesn't exist - grant, err := s.db.PolicyClient.AssignKeyAccessServerToNamespace(s.ctx, &namespaces.NamespaceKeyAccessServer{ - NamespaceId: nonExistentNamespaceID, - KeyAccessServerId: kas.GetId(), - }) - s.Nil(grant) - s.Require().Error(err) - s.Require().ErrorIs(err, db.ErrForeignKeyViolation) - - // kas doesn't exist - grant, err = s.db.PolicyClient.AssignKeyAccessServerToNamespace(s.ctx, &namespaces.NamespaceKeyAccessServer{ - NamespaceId: n.GetId(), - KeyAccessServerId: nonExistentKasRegistryID, - }) - s.Nil(grant) - s.Require().Error(err) - s.Require().ErrorIs(err, db.ErrForeignKeyViolation) -} - -func (s *NamespacesSuite) Test_AssignKASGrant_FailsAlreadyAssigned() { - n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "kas-conflict.com"}) - s.Require().NoError(err) - s.NotNil(n) - - pubKey := &policy.PublicKey{ - PublicKey: &policy.PublicKey_Remote{ - Remote: "https://remote.com/key", - }, - } - kasRegistry := &kasregistry.CreateKeyAccessServerRequest{ - Uri: "kas.conflict/ns", - PublicKey: pubKey, - } - kas, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, kasRegistry) - s.Require().NoError(err) - s.NotNil(kas) - - // assign a grant - grant, err := s.db.PolicyClient.AssignKeyAccessServerToNamespace(s.ctx, &namespaces.NamespaceKeyAccessServer{ - NamespaceId: n.GetId(), - KeyAccessServerId: kas.GetId(), - }) - s.Require().NoError(err) - s.NotNil(grant) - - // assign again - grant, err = s.db.PolicyClient.AssignKeyAccessServerToNamespace(s.ctx, &namespaces.NamespaceKeyAccessServer{ - NamespaceId: n.GetId(), - KeyAccessServerId: kas.GetId(), - }) - s.Nil(grant) - s.Require().Error(err) - s.Require().ErrorIs(err, db.ErrUniqueConstraintViolation) -} - -func (s *NamespacesSuite) Test_RemoveKASGrant() { - n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "kas-removal-namespace.com"}) - s.Require().NoError(err) - s.NotNil(n) - - pubKey := &policy.PublicKey{ - PublicKey: &policy.PublicKey_Remote{ - Remote: "https://remote.com/key", - }, - } - kasRegistry := &kasregistry.CreateKeyAccessServerRequest{ - Uri: "kas.uri/removal", - PublicKey: pubKey, - } - kas, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, kasRegistry) - s.Require().NoError(err) - s.NotNil(kas) - - // verify namespace has no grants - got, err := s.db.PolicyClient.GetNamespace(s.ctx, n.GetId()) - s.Require().NoError(err) - s.NotNil(got) - s.Empty(got.GetGrants()) - - granted := &namespaces.NamespaceKeyAccessServer{ - NamespaceId: n.GetId(), - KeyAccessServerId: kas.GetId(), - } - // grant kas to namespace - grant, err := s.db.PolicyClient.AssignKeyAccessServerToNamespace(s.ctx, granted) - s.Require().NoError(err) - s.NotNil(grant) - - // verify namespace has grant - got, err = s.db.PolicyClient.GetNamespace(s.ctx, n.GetId()) - s.Require().NoError(err) - s.NotNil(got) - s.Len(got.GetGrants(), 1) - s.Equal(kas.GetId(), got.GetGrants()[0].GetId()) - - // remove grant - removed, err := s.db.PolicyClient.RemoveKeyAccessServerFromNamespace(s.ctx, granted) - s.Require().NoError(err) - s.NotNil(removed) - s.Equal(granted.GetKeyAccessServerId(), removed.GetKeyAccessServerId()) - s.Equal(granted.GetNamespaceId(), removed.GetNamespaceId()) - - // verify namespace has no grants - got, err = s.db.PolicyClient.GetNamespace(s.ctx, n.GetId()) - s.Require().NoError(err) - s.NotNil(got) - s.Empty(got.GetGrants()) -} - -func (s *NamespacesSuite) Test_RemoveKASGrant_FailsInvalidIds() { - n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "kas-failure-namespace-remove.com"}) - s.Require().NoError(err) - s.NotNil(n) - - pubKey := &policy.PublicKey{ - PublicKey: &policy.PublicKey_Remote{ - Remote: "https://testingremovalfailure.com/key", - }, - } - kasRegistry := &kasregistry.CreateKeyAccessServerRequest{ - Uri: "https://testingremovalfailure.com", - PublicKey: pubKey, - } - kas, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, kasRegistry) - s.Require().NoError(err) - s.NotNil(kas) - - // ns doesn't exist - grant, err := s.db.PolicyClient.RemoveKeyAccessServerFromNamespace(s.ctx, &namespaces.NamespaceKeyAccessServer{ - NamespaceId: nonExistentNamespaceID, - KeyAccessServerId: kas.GetId(), - }) - s.Nil(grant) - s.Require().Error(err) - s.Require().ErrorIs(err, db.ErrNotFound) - - // kas doesn't exist - grant, err = s.db.PolicyClient.RemoveKeyAccessServerFromNamespace(s.ctx, &namespaces.NamespaceKeyAccessServer{ - NamespaceId: n.GetId(), - KeyAccessServerId: nonExistentKasRegistryID, - }) - s.Nil(grant) - s.Require().Error(err) - s.Require().ErrorIs(err, db.ErrNotFound) -} - -func (s *NamespacesSuite) Test_RemoveKASGrant_FailsAlreadyRemoved() { - n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "kas-conflict-removal.com"}) - s.Require().NoError(err) - s.NotNil(n) - - pubKey := &policy.PublicKey{ - PublicKey: &policy.PublicKey_Remote{ - Remote: "https://remote.com/key", - }, - } - kasRegistry := &kasregistry.CreateKeyAccessServerRequest{ - Uri: "kas.conflictremoval/ns", - PublicKey: pubKey, - } - kas, err := s.db.PolicyClient.CreateKeyAccessServer(s.ctx, kasRegistry) - s.Require().NoError(err) - s.NotNil(kas) - - granted := &namespaces.NamespaceKeyAccessServer{ - NamespaceId: n.GetId(), - KeyAccessServerId: kas.GetId(), - } - // assign a grant - grant, err := s.db.PolicyClient.AssignKeyAccessServerToNamespace(s.ctx, granted) - s.Require().NoError(err) - s.NotNil(grant) - - // remove once - grant, err = s.db.PolicyClient.RemoveKeyAccessServerFromNamespace(s.ctx, granted) - s.NotNil(grant) - s.Require().NoError(err) - - // remove again - grant, err = s.db.PolicyClient.RemoveKeyAccessServerFromNamespace(s.ctx, granted) - s.Nil(grant) - s.Require().Error(err) - s.Require().ErrorIs(err, db.ErrNotFound) -} - func (s *NamespacesSuite) Test_AssociatePublicKeyToNamespace_Returns_Error_When_Namespace_Not_Found() { kasKeys := s.f.GetKasRegistryServerKeys("kas_key_1") resp, err := s.db.PolicyClient.AssignPublicKeyToNamespace(s.ctx, &namespaces.NamespaceKey{ diff --git a/service/internal/fixtures/policy_fixtures.yaml b/service/internal/fixtures/policy_fixtures.yaml index 4b668c2d66..9746b6593e 100644 --- a/service/internal/fixtures/policy_fixtures.yaml +++ b/service/internal/fixtures/policy_fixtures.yaml @@ -558,7 +558,15 @@ kas_registry_keys: key_status: 1 # KEY_STATUS_ACTIVE private_key_ctx: eyAid3JhcHBlZF9rZXkiOiAiMXZ2V2pJZ2E2emM0bkhKVHEwL3RmM2hKZXU2VlpDY2o5WTVvbXZNekN2MEw5OVNxcGNpUHY5dUw1ZTQzeHVabTBYMDR3ekJ4VVdhUnJBaWJQRGN2TzFOQWJwS1ZObVNPODRyaXE4aklVeTRJRTRWeHBiTWYwL0duR1U2RjJIODhWb251K2YxMFRQeXdvVWgrU0dXTUZRZWU0eHFkaDJvQ2JlY3FiWklkK1JRR3dRUUh4dUlmOVljUW53MXJXcVMyRHhEekc5cGtleVZCTFBnaTQvZmo5SWhHVmtQQ0RaRFpZYys5MksrZ2UzT0htSlVSSWlURUI4b3BXbVNTc2Zhblc5YkJSYURWdWwxV3EyaHVrK1piVWhqeEZ6bEJWVHRuUVQ2c2R3Yzl0YlpSd0Rqa2ViVGExNVJHVkRKZFhZK1drTHZ2a2J0TVdWZHJQdlNNT2hwRndtcGNvN2g5VjRMdnRLalhvcTA9IiwgImtleV9pZCI6ICJjb25maWciIH0= public_key_ctx: eyAicGVtIjogIkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVUpqUkVORFFWSmxaMEYzU1VKQlowbFZXRGRMUW14blVUaENWa3BEYldkWlp6SldTM0pyUWt3eVl6UkpkME5uV1VsTGIxcEplbW93UlVGM1NYY0tSR3BGVFUxQmIwZEJNVlZGUVhkM1JHRXlSbnBOUWpSWVJGUkpNRTFFVlhkT2FrVjVUVlJCZUUxc2IxaEVWRWt4VFVSVmQwNXFSWGxOVkVGNFRXeHZkd3BFYWtWTlRVRnZSMEV4VlVWQmQzZEVZVEpHZWsxR2EzZEZkMWxJUzI5YVNYcHFNRU5CVVZsSlMyOWFTWHBxTUVSQlVXTkVVV2RCUldRNFJGSnlhRTFFQ2xCUlUyTkpVR3hTZFZaaVdtSlZZakoxV1RoVlRFSldaVzFSZW5oblZEZHZURGRaTjFWR1NHSmtTWFZuVjBGRFVsVm5UMEl6VjJ0a1MzTTVjVXBFYUcwS1EwUkZZazh2WXl0YWMzSnpUa3RPVkUxR1JYZElVVmxFVmxJd1QwSkNXVVZHUlM5MlluVjJWVms1U2tVdk4xb3hSR0pTY25kcE1tWTNUV1JGVFVJNFJ3cEJNVlZrU1hkUldVMUNZVUZHUlM5MlluVjJWVms1U2tVdk4xb3hSR0pTY25kcE1tWTNUV1JGVFVFNFIwRXhWV1JGZDBWQ0wzZFJSazFCVFVKQlpqaDNDa05uV1VsTGIxcEplbW93UlVGM1NVUlNkMEYzVWtGSloxSnliVEJvU1VwSWFYaHlVVEpGT0VneWVtZE1SMVZMUkdsUlJYcE5aRWMwYzNGRWIySTBjVm9LZVN0clEwbElTMjVIZEUxU05GcHlia3BxSzJaM2FrSTJhelZzZVhSM1dYRnhibGxJWVU1NWNFVmlTVlUwYWpWcUNpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyIgfQ== - + kas_key_3: + id: 8f3c5e4a-6b5f-4d3c-9d1f-0f7d8e0e2a2b + key_access_server_id: e36640a6-61c5-4d4c-a45b-0e0a26d1c45f # key_access_server_2 + key_algorithm: 2 # ALGORITHM_RSA_4096 + key_id: kas_key_3 + key_mode: 1 # KEY_MODE_LOCAL + key_status: 1 # KEY_STATUS_INACTIVE + private_key_ctx: eyAid3JhcHBlZEtleSI6ICIwUXNLV1p6Q3I5NXdNdkxjYnh6cUpIcTRrNlJqSXZ0MU1UUGZ6aE9HZHhXYmZnYW5wVUhpWWw2ZUMxOGdKUWhkTm5TTENla1MwdTY5V0JTZUdGV0VtcENlb29TbmhTWElWcTJaQnJ1bVBTOWxWQndXRXl6biszbVlSK3FkQW80ZHY1YlFIcDBidExVUWNoRTNJWHU1NC9FeUFUbm8xRmlMSjZLa00xUHA4MmE3L2FPRDVNckpOZ0x5dXYySVZKbzhEckFXVmQyeXFra29MdXo5NzZERldJUndBd1JUN1dyOUo4MHpYdkFBVS9ibHdGSWFBbmRRZFJLMXppS21nUUJvWExNaCttb3IyVnM2KzFOSEYycWtRalVEMEQyVXlOUThycHVjSXM4RGhrWFFZYXVtWm1iZVdWQUo0a1lPRDRnUUQ3NVdSYUNNWXp3Nm9tTHBHS0RYUzc4bGZTR05VUGMvSnF1M2QxUU8vNVIyTVJST0NLVXVLL0VPSTB0QmFOQUFESXVpT3pVaEoydHZMQ0preTFGMndHeGgxM1hqUmVmS0gzVVdtYVd6SVl4TnM2TW9hdU91enJFNndNV1lWUnA5UWlpOXkvMS9oZ1hicGIxYWZZbWlzT1EvOGcwWmhvMWRmTWdLdlJWWHEyRUFiSWpYb0ZoNE9JUnd0VWhsU29pcVBIVTRPTW1rOVE5Q2RaMVk3anpXdHNra1NnOEY3MjdSdy80S1hTS1hGYWdRbTRNUWN1K2graVpFWGxTOGhmNVRTQXh6M2psejg3M281ZTlXNzh3MkJPL2dBZFloVGxjUnVKeEwxK09CQmF6ZDBWSjkxVitJNmM3bTR3NHVxc1BnRmFNSW1DaTdGYlJSaW16UWh0ZWdKYXpzZVJBNm01VjM3TlhmK3VUbS9PaFJ2ekZBMnRNN0NHem9LdTNubkZHUVdJVUlSSGUxSC82UVJEc2VoSzZ6SFN4VDVaOHk5enpJblh4bUw1bmVGOFNHUms0OWx3MWE1YVZZUlRsVHRmaEQvKzVoVUxuZFlMUzBhTnVSdnNGODU3QjhzN0YveWV2eFBiUnBSdWY3dFhheGwvUHptSFVHRzlKTjYvNy9RWjNXNWVNdU44ZXdqUmlwOGM1SEpydzBBazQvcFVzcnM0dnBpQ1hPYjBNejlvbWNTSU16UjlFUkUxNnp5b2ptTXFXZ3BGYjhja0R0ZXpEWU5tN3RVWDhSY2NZSnRwOEtCU052dTREZXVJL2pYYnF5b3Z6Q09SUnhCUmxUNDByb1hFQURGU2dSc0pXTkg0SGxBSjVDWW9oNE43RUVGS2lCVnkxeWNWajg0aEk0ODNkdy95dC9ZU3E5ak5LTjlVT1daVG0zU20xZk9XODdpR0lwWUZsejk4K3ZRYjZvOU5tSjZycEkzUTJ0c3JrS0dMQzhFTThycmFzMGFYRm9HOUhIaTRTR1VseXFSYXJVK0RjUEtneHphTkVDa2U0QmJRWHFBRlBXRU5vNXJMUEJZTHMyNG9XRDJjZEpVYzIyWWdDV1FBNlI1SFE4dXpTMDZxUmF5SUdnTmtjYThneXUrWEJXcjl5UTUzWllVdWNQdURwQjdVaWFGcVVNbnpZUmwwdzA0NGc5S09ycnpWdk0rZmNCVENaTDJCVUUxY3FUUHNHQlkrRDJoVVV0V1drd1Jha1NkVlpNQjF5bE1PNVNzYVpFaVl0Z3ZhSkZ3SGZKR2t3NE9EWlI3ejhHRW45Mlg1MC8vWktZZW4vWnNFMllodE5DVGJadm1TZk84RmZjZ01lektmR0t6MXNySE5IMm1DSThmSkVyekxBQmZ0WC9udUFDTEdCS08zaTU1VFl3UEhqbHRWU1J4c3krSGlGeGdQR0JNNnNucGhtcmxyS0x1M1lsZlYxRmhQT3o1ZXdqV205RGdDUDJkYktEQUxxMVFGTzlpSnIvckRGTDlLanZITmdIWjE4REsvU1Vld1pFcjAvRmpWaWxZUm1BRUlEZllhWTJmZ1pVVGJlQjJYaUpLTDJncDFFTWZCbjhNZzdDK1c4RWY5dWJSR1JSOCsvUHVFTjFmemYzNzNkNmZlSVA5UmYyZFJ4UnFUSFpodG9NSGoxS0FRcDZpWXlDbTZOM2VRQVVpMHgxa1h1ZDRQQm4wL2pwbEZNYTRib0dzSzFRdUFtTDI1eTRHMjJ1NzUzMmdiemUwNzlNNmUya0NVSFhsVmJWT1lnTktKTmJGNzQ0dlFOYU5UbnNZNVdWcE1vU200TU82Z0p3UWpac2UwMkJqbk92cWJBMjV1OVV6WTlwcElJN3E5TUpSNG1mQnJWcjFMdi9SZnpIVllUTjlEdzQvVy9ld01lbFZTNk1ldCtIWHJuZFV1QjErbXIvVWE3UUJHR0hobCswTWdYcG00aSt4U3EzKy95bk0zZG50Z0xOSEIrTEpjbUdEaG5EWXZ1THRVWWpPbmhmR05QYzNsRE9yN29jb2V1cXBWYVpIOUFEMFhmVHdpVVJYVmdWb1pVN1dWd1l4YTc4WGpDL0U2NWFjU1Q3aTl2Vk5lVUNYTlA2SFVnV1NiNStzZzRpV2dNTVBtNzR3Z0ZwMHVBb25reGZxb1h6ZXROaElyR2RvamhDbFdLb3E5WXAxaHJYb2RkM1Q4L0UrL1l4aFo2NkdsQ2NpY1UwTlZLQ1lJREI1SnY5WnY3SmFBRk1qVnhyd09hak5FeHZLTDFobFVEYVhKNG9OM2NNaW5QR2hJeTlRRmh5aGxxWDFyL0kzVmcydHN0NUFtZnZKQzcyeXJWUkFzZU5BUlJQWllBem96Y1FBb3pZQWpGY2lveGhXMHZ1RTFFOE85RUliMXlSTDFvU3F2Q05YcVJXYis0Y0FrcmdjMmp5eEJvSDdtdXBLa25CTVZ2UWd4WHQrZzZVSUlRR2laTkxxME9FdWhkRG1FRitUamU1ME05Vm9hbnNoejVoc09lZ2dZellTWFFIN1NRVm5OZitjK1RQa1UwWHBlRzdFVEQ4WUdueGhXTW5mRk5lK0FON0o5b0FjRTFSN0dZLzdLTVRQZ09DaVc2aTd5eVI5YllQcmsyYkU1eEtINnF1dVU5amJvaG90ZWJrS2ZEU05LTDNiWGh2UDd1bmEzUFFxbXJRRm1DZWpPcGRrb0lEL1E4eDI1KzlQYTNDWXd0aEFEaUdnTHBSM1lNVy9NR0IvNGs5ZFpNVUNaS3lBcTlNTnkvUWh5bm5NV1BBZmVGM0E3ZHBxY2pUb01KT3diakFuY0VPUjcrQU51VWtnbTBOWlRzdUUyTGtFeTJJdnRmZ0ZRY3FkZGt4U0xLNTFpbDBSU2VJaXlNN3Vxa3lOaTVKQ2JWckRJL1pBSmUyUHJFQmhlQWJ2SUhlSjZtamFnVndXNEY2Q3RUT0VJa0VoMENyTlNnb0VLL25VUlJGMzNkMjMxYm4wRVhTbnErZHlQVjZNSFR5d255cWxCWDJ2ZjBGUHhpNDVnTldrQTY2cUJHUStvVlVtQlY5YnZ1NkNlN0xWRndIVjNpRkN2dVI0ajd4V3ZqSXNtdnJ5b3JWTkkvN0s0YW1NMjRYTitZdVBvTW1iSmI4SGFGM2hvNXVFMlhTeVgzK2EwL2l6clRidjlVWEJwNUJ6bTVpYm5PL0g5NFJUTlhsSDQxZkVnNitacHJMcjlmckE1V2pFYXJ5QlMzRlFUN002c3ppL085eXpUMXE5cVB4d1ZRRDA4M0NObjNDaE5hbEFCK2t0SHFIUE4wbGRVL3UzSE01UFRXejhjcFBWVWtqUStOejAxaDl2VDBlNFpud01WVFBEZTVLZExNZDZueENsK2UxeGp4R2FMbkFNSmVqVVlsWURQeEc1dmhqYWcyVFkvTkl5ajZoemlZUXB1Y2F5aTg5VzV2NVJUWmFDeUpNdzh0T2Y0YjZScE9oY3JVa0hGV3hIUDduaXQySE1yblBmNmhKeWZ0cDcxSHQzNUt3aUxMUXY0VnVERzhxT0Y3bzhsL3RDcDBYOEdvNzVvYjBxVDY2SnB3aFpKdndLbWdKZ3k3VTNnbzhEVXZpY21RRDVmMk83eEFjZit0N3dyZlZPcEYrNnFJdnFJNE5kcHhDYlM5Nm00dm1FR2hwL2RiYjdwQjFrbEZFY0x4MGpQbW5MejRmTk1XMFMvSGdDckJjUkdGelVvWWRCeGFodHVNd09VeFdTc203dnQ4RnM1U0Z6NlJ4cWI2YUxxVEFXeFVYWDBCandLZUlUTyswUTU1eUhLdkRPbVJxOExKZXpEMFc2bStqVVVuSlB0TllxVUUxdVRrM00rUFlTdzVsUWJsaWNEcDlNaHNDUm84dGlGS0t0S3BZRmJMclZ6eGJKKytFajBYQ080bGorcUppYVlEN2lGUVp5b1NPelhQeUdGQzRaU2c3V1VkWlBWRW00RjlwN0U2ODgyQWV3ekFjcFZML1lGenVYWkNqaTltK3hHVHN3WENNdnBHNzFqZjBqUm9sSnZjd2pJVXFjdXZlWExWa2FmTVZybkllbXM2VzdPNjBtTTFqY0xtUWlqOGkxNUJrM214MTVmcnhsamJJaldHSEsvRFBwNkh3SzY4UmIyaGZrTXV2b0FSNExGMDFRMEk4MkZWWWY1SCt0OU5sSmw0dUNDSDNuUlNLZTBDRzM0NmIwOENCZ1JwamRuZHFwNVNKT251SjdpMENVMGRYZ0NxVi9ZRVJmT01GZ1BKcnBuNXVyTWNiZmtZVlJZOE1qRmxvaG9FczVUQmRKaWE4NE9mWVZkUHpZZSttSDAvUURGa0VvZmN1RHJVUUJPbjlGL1AvbUVidUZFR3NtSHNhaUx2NnJyaTJWM3Q2eDRsUmJleDNZTW84dGIrbFRwdkVhdGp2eGRTdmRXZlFlR2J6cDRDZDNHQ2ZEMWpuekh1ZEc2UFNLMVhubXphVUEzek9DT1JJYURaczRBQ04wRS82M1VSMit1aFppVW83ZjVmS2Q5anhtT0k4NStDajN2ZlhRU1ZTMCt6dXE4K3lYbk55NGhoUnhZWEF4ZE9uNWNjOFE0dTc1bjc0RGo0ekM3UVFrVGl4VjkyZjlsZGFLdUFPbVB1ak5tclhJSnhYN1o1SjI1TXdqcHVCMkV4a3pzTDZNN0RWQXZZMitUUlhMNjRYRlpSY2hCbzNkN0orUmZFcFU4T0lpNEpKV3YxTzcxMzVWeXhsRm1Lb3Q3Q1lFSGsvWjZTM29ZZHY0RWh2NkxLRzRBQ1BvWjRtb1VGVjRqajkvY3Q5dVZObFdlZXpEWXpNQUdFMVdPa29Cc0xaWDIrQjVWcWpIdGV0aFpPK1REMS9YeDVRaWp4TWRWWEZaRm42NVdVT3cvaGUvVUZIMllUTVlJOTg1UFNUek8rYmpvY0VQaWxIclkycmN0U0JabkFjYkF1bTNBNmN6VVlRMmtDT09JZ3Y2WjVBcEpCdmxSV2RQYk9TQjhnZkJJZElXeitOUW1DMFp0OUpsOWtMR2xIeS9qUnZtVkViNi9kaGFuOCtaLzkzREN1UmFmZkpZWHBqK3djR2YwSGp6Rms3Z2JsZCswMFV6WmZQaW13dkU2ZDNVVEFZOHRRdDRTKzZtcHVnMy9QK3ZvRFB0cFhaQjJNR3pLdlU5dGdPR0w1Z1gvOFBKbEkrUFU1bkVxL2Z5WG9vQy9Ud0EwTnJRWkhPSWg0c3hzbklvdnpJSzhDcVp2aWxqc0M5ZUhwVkpYT0lyUHhKNkdnM3d1cG5lUzdFMFcvRzRtanNUc0F2WHJOck5jZUw4K0JiRU5vcUR5NndKblJRcU5WZFlmVE1paWFFQThxNFNsTTRwLzI3d0RhSHNaS09HeVdIaGpxWnBNT2xiN3R3QmJZa3hyQlU5K05JY1ZOTXVOV1oyT00rRGtUUyIsICJrZXlfaWQiOiAiY29uZmlnIiB9 + public_key_ctx: eyAicGVtIjogIkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVZ0UkVORFFXOUJRME5SUkVRd2J6WkRhREl2UzJKNlFVNUNaMnR4YUd0cFJ6bDNNRUpCVVhOR1FVUkJUMDFSZDNkRFoxbEVWbEZSUkVSQlRuSUtXVmhOZDBob1kwNU5hbFYzVG1wRmVrMVVUWGhOUkUwd1YyaGpUazFxV1hkT2FrVjZUVlJOZUUxRVRUQlhha0ZQVFZGM2QwTm5XVVJXVVZGRVJFRk9jZ3BaV0UxM1oyZEphVTFCTUVkRFUzRkhVMGxpTTBSUlJVSkJVVlZCUVRSSlEwUjNRWGRuWjBsTFFXOUpRMEZSUkRZNFEydFJkVU5rWW1kdFVFdFJNa2N5Q25JM05sZHNPVzR5Tm1aa1FYaDRkWGRZT1VOeWExaHBWbGhHV0U1M2ExSnRkSE40Yms5cmIxUkdVM2xWYWtseVRIa3JabVZIVUdKcGVsVnhjMVZFVEZnS1RVVkhlREI2ZUdkalptZHFNakptVERObGJVNXhLMVV5UldSeGNtdFdSblpWWVhvek1reEVZVnAzY21OYWEzRXJlbTl6UzJkQ09VWXlhQzlyVlVScWJRcDVZelJJVG5WeVprMXZWR1Z1UVZOeWJESTRaekZaV21kV2VGbDFVbFZtY1V0aVVURm5TSGxaUkdreVNFTjVMM2gxVXpGTGVFbEtNbEV2T1dJMVNuaG1Da015VDI1R2RVazJUVVpRZFVwb1Fra3pkMUZ3T1RVdlJEWlVVVFpxUlV0clZIUlhjVmg0Uldsb01rdEdNVFJQYkUxUGMwRjZLMGxpTW1aNE1XcDVRUzhLTWpCYUwyaFZkRko1VjBwbVJESlJiamRuYVdKa1RXdDNPV1ZITHpSV1oycDRibnB0WWxOTmJ5OHJNSFpvZWpkSVFsRTNVVEJqYzA5V1lVaEVaR1IxUlFwbGEzTjRjVGt3TWt4TE9XZzBNbHBwVld0WlMwMVdlVEk1VjBaM1VUWlNXbnBHWWtwNFEyNUdRakZVU2s1eVFrMWtLemhaYkUxVWFWTjFlWEZ4VlNzeENsZDBTbTUxTTFKSFVGY3JXSEU0YjBKSE1XaElLeTlQVEM5Vk5GcDJlRE4yTlhkTE5WVlBSak54YVdSWVdXUkNZeXRyYm5wVGJqTnpUa3BGU1VoU01Xa0tiV3RvYjFOdWFFVnhkMFZHTldod2JrazJhamQ1UlUwcllVNHlhR3A1ZGtKVlVtUllVR3hvYldwYVZDdEtRVU1yTjNSS1dYTjZXSFp0VW5FMk5sRTNSZ3A0VW1WNlZYZDZPRTh3UW5GNGMwZHhSemQxZDBsQ2QwZzFZVFF4UTFaNWJHMVpURTF3TVhaTmJGVnJZa2R1YUdwaEwydzVhSFpOT1RSMWFuSk9XVGxaQ25wR1dtbFdlWEJoUXl0VVNqWlpkV3BTV0d0eU9EaEljMWx2WWpOQ01UZzBjVXM1Y1dzNE1qZGllRlZ1YTB0Wk4xZDBaVkY0ZUd0blR6SnFLMWhNUTNRS1YwaHdWMWc0TlN0dlYycDFRbVUyTDFaQ1RXZEpVMlpIUVZGSlJFRlJRVUpOUVRCSFExTnhSMU5KWWpORVVVVkNRM2RWUVVFMFNVTkJVVU5vYVRkVFFRcHdSbUkwVFhWMVJFbExWbFJJWjBoUlpGTnVPRXBwVkdkUlJteFpTbGx5WlVwa2JFTllSSEZZZFZkQ1ZqRmpVMkpVVkRWbVNsUXdlWEF4Y1dWeVRUWm5DbGhwVUROVmRqUnRXakJLVW5neVFqWXlTVWx6UldSa1J5OWhRM1kwY2tsUWJHRkhhRUZIWTFkWmR6QTJMMUF4TlhoQ09UaGxhRk4zTlhWMFNsVmhkbVFLZDBjMk5VcGlhbVpoYzFGamRFNWpSU3RLTm10aFlVZG9RMlpsTkVVd2RVSXlhazlSWVRneldFNTRTa2MxZDFweU5qaFFNVzVsUlZoSmJEZGFXakppVXdvNWJTdHphWFp0WVVOSE9GcHlkR1ZHV21weE5uSllhM2MxYW10WGRYTjJVamtyYXpSMVJERXJUbGRaVjJzNVdYSlFjRWRCZVdjM1UzaFFUVXhCU0dGWUNqQkZiREJoTkN0dmIxcDZNa1J2TkU5MVltZEVVRkYwVVZoblJVdHFiMDlCUWxFNE5EWmlSMVZWYzFwMlVUa3dUa28zSzNwcWMyOU5OSFZyY1cxTk0yd0tNRzVKTW5nNFJHSXJLMEUzV1dKVVJVMDNjMHN6TXpKVFZ6TkVVWEZzTm5CYU1GQnpiRWRJTHk5Rk4xWTROMVJPVlZoeWJIcEpRalo2ZDNsaFEwSkxkZ3AwWWpoS01VTmhVbmgzVjNWVlkwdEhTVWgzWkhaSVZYazVObTF3WXpGSlZGaFRja1ZtVTJ0U2IwcDFZazVuVlZaaFMyeHZjREU0U2xNclEwMVRUbXhGQ210NFVYSlViVlZPUnpoNFZFZFhaamxwTDI0Mk5rUlJRbmxqT1VOSVZtMTFRVGRxUVhvcmRYZE5hbVpuYkdselRYQmxjRms1VTAxMmVFOTZUVmxsUmxZS2F6ZE1kbWxHV2tNdmFTdEpjRXRsYjBKdWJHWkxObXhoVlRBNFpGQlpVR1JVUmtWSk9HSlRSVTlvTmxRdlUwbFNiMHhQUm1kR0wzSnBNMVJrUm1ST1dRcGpNR3RRYUhsSmEyWlpkMnBwTkV4MVZFMHhWUzh3WTJaWmJWbElNRGhXYzNwa1VVdE1NVEpwVDFvd00wdHFXRVJNUjB0VmFtSjRRamRGT0UxMlZrWkJDaTg1U0N0TFZrTkZaRmxCWlVwS1MyUlFiVVExT0RNck4zSlNZbWwyVEdKQ1FUVnpZMUJSUFQwS0xTMHRMUzFGVGtRZ1EwVlNWRWxHU1VOQlZFVXRMUzB0TFFvPSIgfQ== ############## # Provider Config ############## diff --git a/service/policy/attributes/attributes.go b/service/policy/attributes/attributes.go index 36bbe4e17e..e36e0cb9c3 100644 --- a/service/policy/attributes/attributes.go +++ b/service/policy/attributes/attributes.go @@ -2,6 +2,7 @@ package attributes import ( "context" + "errors" "fmt" "log/slog" @@ -369,27 +370,8 @@ func (s *AttributesService) DeactivateAttributeValue(ctx context.Context, req *c return connect.NewResponse(rsp), nil } -func (s *AttributesService) AssignKeyAccessServerToAttribute(ctx context.Context, req *connect.Request[attributes.AssignKeyAccessServerToAttributeRequest]) (*connect.Response[attributes.AssignKeyAccessServerToAttributeResponse], error) { - rsp := &attributes.AssignKeyAccessServerToAttributeResponse{} - - auditParams := audit.PolicyEventParams{ - ActionType: audit.ActionTypeCreate, - ObjectType: audit.ObjectTypeKasAttributeDefinitionAssignment, - } - - attributeKas, err := s.dbClient.AssignKeyAccessServerToAttribute(ctx, req.Msg.GetAttributeKeyAccessServer()) - if err != nil { - s.logger.Audit.PolicyCRUDFailure(ctx, auditParams) - return nil, db.StatusifyError(ctx, s.logger, err, db.ErrTextCreationFailed, slog.String("attributeKas", req.Msg.GetAttributeKeyAccessServer().String())) - } - - auditParams.ObjectID = attributeKas.GetAttributeId() - auditParams.Original = attributeKas - s.logger.Audit.PolicyCRUDSuccess(ctx, auditParams) - - rsp.AttributeKeyAccessServer = attributeKas - - return connect.NewResponse(rsp), nil +func (s *AttributesService) AssignKeyAccessServerToAttribute(_ context.Context, _ *connect.Request[attributes.AssignKeyAccessServerToAttributeRequest]) (*connect.Response[attributes.AssignKeyAccessServerToAttributeResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("this compatibility stub will be removed entirely in the following release")) } func (s *AttributesService) RemoveKeyAccessServerFromAttribute(ctx context.Context, req *connect.Request[attributes.RemoveKeyAccessServerFromAttributeRequest]) (*connect.Response[attributes.RemoveKeyAccessServerFromAttributeResponse], error) { @@ -416,27 +398,8 @@ func (s *AttributesService) RemoveKeyAccessServerFromAttribute(ctx context.Conte return connect.NewResponse(rsp), nil } -func (s *AttributesService) AssignKeyAccessServerToValue(ctx context.Context, req *connect.Request[attributes.AssignKeyAccessServerToValueRequest]) (*connect.Response[attributes.AssignKeyAccessServerToValueResponse], error) { - rsp := &attributes.AssignKeyAccessServerToValueResponse{} - - auditParams := audit.PolicyEventParams{ - ActionType: audit.ActionTypeCreate, - ObjectType: audit.ObjectTypeKasAttributeValueAssignment, - } - - valueKas, err := s.dbClient.AssignKeyAccessServerToValue(ctx, req.Msg.GetValueKeyAccessServer()) - if err != nil { - s.logger.Audit.PolicyCRUDFailure(ctx, auditParams) - return nil, db.StatusifyError(ctx, s.logger, err, db.ErrTextCreationFailed, slog.String("attributeValueKas", req.Msg.GetValueKeyAccessServer().String())) - } - - auditParams.ObjectID = valueKas.GetValueId() - auditParams.Original = valueKas - s.logger.Audit.PolicyCRUDSuccess(ctx, auditParams) - - rsp.ValueKeyAccessServer = valueKas - - return connect.NewResponse(rsp), nil +func (s *AttributesService) AssignKeyAccessServerToValue(_ context.Context, _ *connect.Request[attributes.AssignKeyAccessServerToValueRequest]) (*connect.Response[attributes.AssignKeyAccessServerToValueResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("this compatibility stub will be removed entirely in the following release")) } func (s *AttributesService) RemoveKeyAccessServerFromValue(ctx context.Context, req *connect.Request[attributes.RemoveKeyAccessServerFromValueRequest]) (*connect.Response[attributes.RemoveKeyAccessServerFromValueResponse], error) { diff --git a/service/policy/attributes/attributes.proto b/service/policy/attributes/attributes.proto index 7a5c7fa4fd..47c8d44888 100644 --- a/service/policy/attributes/attributes.proto +++ b/service/policy/attributes/attributes.proto @@ -11,31 +11,25 @@ import "policy/selectors.proto"; /* Key Access Server Grants */ - +// Deprecated message AttributeKeyAccessServer { + option deprecated = true; // Required - string attribute_id = 1 [ - (buf.validate.field).string.uuid = true - ]; + string attribute_id = 1 [(buf.validate.field).string.uuid = true]; // Required - string key_access_server_id = 2 [ - (buf.validate.field).string.uuid = true - ]; + string key_access_server_id = 2 [(buf.validate.field).string.uuid = true]; } /* - Key Maps + Key Maps */ message ValueKeyAccessServer { + option deprecated = true; // Required - string value_id = 1 [ - (buf.validate.field).string.uuid = true - ]; + string value_id = 1 [(buf.validate.field).string.uuid = true]; // Required - string key_access_server_id = 2 [ - (buf.validate.field).string.uuid = true - ]; + string key_access_server_id = 2 [(buf.validate.field).string.uuid = true]; } message AttributeKey { @@ -88,14 +82,14 @@ message ListAttributesResponse { message GetAttributeRequest { // Temporary message level validation until we remove the deprecated id field option (buf.validate.message).cel = { - id: "exclusive_fields", - expression: "!(has(this.id) && (has(this.attribute_id) || has(this.fqn)))", + id: "exclusive_fields" + expression: "!(has(this.id) && (has(this.attribute_id) || has(this.fqn)))" message: "Either use deprecated 'id' field or one of 'attribute_id' or 'fqn', but not both" }; option (buf.validate.message).cel = { - id: "required_fields", - expression: "has(this.id) || has(this.attribute_id) || has(this.fqn)", + id: "required_fields" + expression: "has(this.id) || has(this.attribute_id) || has(this.fqn)" message: "Either id or one of attribute_id or fqn must be set" }; @@ -103,20 +97,16 @@ message GetAttributeRequest { string id = 1 [ deprecated = true, (buf.validate.field).ignore = IGNORE_IF_DEFAULT_VALUE, - (buf.validate.field).string.uuid= true - ];; + (buf.validate.field).string.uuid = true + ]; oneof identifier { //option (buf.validate.oneof).required = true; // TODO: enable this when we remove the deprecated field - string attribute_id = 2 [ - (buf.validate.field).string.uuid = true - ]; - string fqn = 3 [ - (buf.validate.field).string = { - min_len : 1 - uri : true - } - ]; + string attribute_id = 2 [(buf.validate.field).string.uuid = true]; + string fqn = 3 [(buf.validate.field).string = { + min_len: 1 + uri: true + }]; } } message GetAttributeResponse { @@ -125,16 +115,14 @@ message GetAttributeResponse { message CreateAttributeRequest { // Required - string namespace_id = 1 [ - (buf.validate.field).string.uuid = true - ]; + string namespace_id = 1 [(buf.validate.field).string.uuid = true]; // Required string name = 2 [ (buf.validate.field).required = true, (buf.validate.field).string.max_len = 253, (buf.validate.field).cel = { - id: "attribute_name_format", - message: "Attribute name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored attribute name will be normalized to lower case.", + id: "attribute_name_format" + message: "Attribute name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored attribute name will be normalized to lower case." expression: "this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$')" } ]; @@ -143,22 +131,19 @@ message CreateAttributeRequest { (buf.validate.field).enum.defined_only = true, (buf.validate.field).required = true ]; - // Optional + // Optional // Attribute values (when provided) must be alphanumeric strings, allowing hyphens and underscores but not as the first or last character. // The stored attribute value will be normalized to lower case. - repeated string values = 4 [ - (buf.validate.field).repeated = { - min_items: 0, - unique: true, - items: { - string: - { - max_len: 253, - pattern: "^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$" - } - }, + repeated string values = 4 [(buf.validate.field).repeated = { + min_items: 0 + unique: true + items: { + string: { + max_len: 253 + pattern: "^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$" + } } - ]; + }]; // Optional common.MetadataMutable metadata = 100; @@ -169,9 +154,7 @@ message CreateAttributeResponse { message UpdateAttributeRequest { // Required - string id = 1 [ - (buf.validate.field).string.uuid = true - ]; + string id = 1 [(buf.validate.field).string.uuid = true]; // Optional common.MetadataMutable metadata = 100; @@ -183,9 +166,7 @@ message UpdateAttributeResponse { message DeactivateAttributeRequest { // Required - string id = 1 [ - (buf.validate.field).string.uuid = true - ]; + string id = 1 [(buf.validate.field).string.uuid = true]; } message DeactivateAttributeResponse { policy.Attribute attribute = 1; @@ -197,14 +178,14 @@ message DeactivateAttributeResponse { message GetAttributeValueRequest { // Temporary message level validation until we remove the deprecated id field option (buf.validate.message).cel = { - id: "exclusive_fields", - expression: "!(has(this.id) && (has(this.value_id) || has(this.fqn)))", + id: "exclusive_fields" + expression: "!(has(this.id) && (has(this.value_id) || has(this.fqn)))" message: "Either use deprecated 'id' field or one of 'value_id' or 'fqn', but not both" }; option (buf.validate.message).cel = { - id: "required_fields", - expression: "has(this.id) || has(this.value_id) || has(this.fqn)", + id: "required_fields" + expression: "has(this.id) || has(this.value_id) || has(this.fqn)" message: "Either id or one of value_id or fqn must be set" }; @@ -212,22 +193,17 @@ message GetAttributeValueRequest { string id = 1 [ deprecated = true, (buf.validate.field).ignore = IGNORE_IF_DEFAULT_VALUE, - (buf.validate.field).string.uuid= true - ];; + (buf.validate.field).string.uuid = true + ]; oneof identifier { //option (buf.validate.oneof).required = true; // TODO: enable this when we remove the deprecated field - string value_id = 2 [ - (buf.validate.field).string.uuid = true - ]; - string fqn = 3 [ - (buf.validate.field).string = { - min_len : 1 - uri : true - } - ]; + string value_id = 2 [(buf.validate.field).string.uuid = true]; + string fqn = 3 [(buf.validate.field).string = { + min_len: 1 + uri: true + }]; } - } message GetAttributeValueResponse { policy.Value value = 1; @@ -235,9 +211,7 @@ message GetAttributeValueResponse { message ListAttributeValuesRequest { // Required - string attribute_id = 1 [ - (buf.validate.field).string.uuid = true - ]; + string attribute_id = 1 [(buf.validate.field).string.uuid = true]; // Optional // ACTIVE by default when not specified common.ActiveStateEnum state = 2; @@ -253,16 +227,14 @@ message ListAttributeValuesResponse { message CreateAttributeValueRequest { // Required - string attribute_id = 1 [ - (buf.validate.field).string.uuid = true - ]; + string attribute_id = 1 [(buf.validate.field).string.uuid = true]; // Required - string value = 2 [ + string value = 2 [ (buf.validate.field).required = true, (buf.validate.field).string.max_len = 253, (buf.validate.field).cel = { - id: "attribute_value_format", - message: "Attribute value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored attribute value will be normalized to lower case.", + id: "attribute_value_format" + message: "Attribute value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored attribute value will be normalized to lower case." expression: "this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$')" } ]; @@ -281,9 +253,7 @@ message CreateAttributeValueResponse { message UpdateAttributeValueRequest { // Required - string id = 1 [ - (buf.validate.field).string.uuid = true - ]; + string id = 1 [(buf.validate.field).string.uuid = true]; // Deprecated reserved "members"; @@ -300,9 +270,7 @@ message UpdateAttributeValueResponse { message DeactivateAttributeValueRequest { // Required - string id = 1 [ - (buf.validate.field).string.uuid = true - ]; + string id = 1 [(buf.validate.field).string.uuid = true]; } message DeactivateAttributeValueResponse { policy.Value value = 1; @@ -311,12 +279,10 @@ message DeactivateAttributeValueResponse { message GetAttributeValuesByFqnsRequest { // Required // Fully Qualified Names of attribute values (i.e. https:///attr//value/), normalized to lower case. - repeated string fqns = 1 [ - (buf.validate.field).repeated = { - min_items: 1, - max_items: 250 - } - ]; + repeated string fqns = 1 [(buf.validate.field).repeated = { + min_items: 1 + max_items: 250 + }]; // Optional // This attribute value selector is not used currently, but left here for future use. @@ -336,38 +302,47 @@ message GetAttributeValuesByFqnsResponse { */ message AssignKeyAccessServerToAttributeRequest { + option deprecated = true; + // Required AttributeKeyAccessServer attribute_key_access_server = 1; } message AssignKeyAccessServerToAttributeResponse { + option deprecated = true; AttributeKeyAccessServer attribute_key_access_server = 1; } message RemoveKeyAccessServerFromAttributeRequest { + option deprecated = true; // Required AttributeKeyAccessServer attribute_key_access_server = 1; } message RemoveKeyAccessServerFromAttributeResponse { + option deprecated = true; AttributeKeyAccessServer attribute_key_access_server = 1; } message AssignKeyAccessServerToValueRequest { + option deprecated = true; // Required ValueKeyAccessServer value_key_access_server = 1; } message AssignKeyAccessServerToValueResponse { + option deprecated = true; ValueKeyAccessServer value_key_access_server = 1; } message RemoveKeyAccessServerFromValueRequest { + option deprecated = true; // Required ValueKeyAccessServer value_key_access_server = 1; } message RemoveKeyAccessServerFromValueResponse { + option deprecated = true; ValueKeyAccessServer value_key_access_server = 1; } @@ -377,9 +352,7 @@ message RemoveKeyAccessServerFromValueResponse { message AssignPublicKeyToAttributeRequest { // Required - AttributeKey attribute_key = 1 [ - (buf.validate.field).required = true - ]; + AttributeKey attribute_key = 1 [(buf.validate.field).required = true]; } message AssignPublicKeyToAttributeResponse { @@ -389,9 +362,7 @@ message AssignPublicKeyToAttributeResponse { message RemovePublicKeyFromAttributeRequest { // Required - AttributeKey attribute_key = 1 [ - (buf.validate.field).required = true - ]; + AttributeKey attribute_key = 1 [(buf.validate.field).required = true]; } message RemovePublicKeyFromAttributeResponse { @@ -401,9 +372,7 @@ message RemovePublicKeyFromAttributeResponse { message AssignPublicKeyToValueRequest { // Required - ValueKey value_key = 1 [ - (buf.validate.field).required = true - ]; + ValueKey value_key = 1 [(buf.validate.field).required = true]; } message AssignPublicKeyToValueResponse { @@ -413,9 +382,7 @@ message AssignPublicKeyToValueResponse { message RemovePublicKeyFromValueRequest { // Required - ValueKey value_key = 1 [ - (buf.validate.field).required = true - ]; + ValueKey value_key = 1 [(buf.validate.field).required = true]; } message RemovePublicKeyFromValueResponse { @@ -423,8 +390,6 @@ message RemovePublicKeyFromValueResponse { ValueKey value_key = 1; } - - /// /// Attribute Service /// @@ -469,25 +434,31 @@ service AttributesService { /*--------------------------------------* * Attribute <> Key Access Server RPCs *---------------------------------------*/ - rpc AssignKeyAccessServerToAttribute(AssignKeyAccessServerToAttributeRequest) returns (AssignKeyAccessServerToAttributeResponse) {} + rpc AssignKeyAccessServerToAttribute(AssignKeyAccessServerToAttributeRequest) returns (AssignKeyAccessServerToAttributeResponse) { + option deprecated = true; + } - rpc RemoveKeyAccessServerFromAttribute(RemoveKeyAccessServerFromAttributeRequest) returns (RemoveKeyAccessServerFromAttributeResponse) {} + rpc RemoveKeyAccessServerFromAttribute(RemoveKeyAccessServerFromAttributeRequest) returns (RemoveKeyAccessServerFromAttributeResponse) { + option deprecated = true; + } - rpc AssignKeyAccessServerToValue(AssignKeyAccessServerToValueRequest) returns (AssignKeyAccessServerToValueResponse) {} + rpc AssignKeyAccessServerToValue(AssignKeyAccessServerToValueRequest) returns (AssignKeyAccessServerToValueResponse) { + option deprecated = true; + } - rpc RemoveKeyAccessServerFromValue(RemoveKeyAccessServerFromValueRequest) returns (RemoveKeyAccessServerFromValueResponse) {} + rpc RemoveKeyAccessServerFromValue(RemoveKeyAccessServerFromValueRequest) returns (RemoveKeyAccessServerFromValueResponse) { + option deprecated = true; + } /*--------------------------------------* - * Attribute <> Key RPCs - *---------------------------------------*/ + * Attribute <> Key RPCs + *---------------------------------------*/ - rpc AssignPublicKeyToAttribute(AssignPublicKeyToAttributeRequest) returns (AssignPublicKeyToAttributeResponse){} + rpc AssignPublicKeyToAttribute(AssignPublicKeyToAttributeRequest) returns (AssignPublicKeyToAttributeResponse) {} - rpc RemovePublicKeyFromAttribute(RemovePublicKeyFromAttributeRequest) returns (RemovePublicKeyFromAttributeResponse){} + rpc RemovePublicKeyFromAttribute(RemovePublicKeyFromAttributeRequest) returns (RemovePublicKeyFromAttributeResponse) {} - rpc AssignPublicKeyToValue(AssignPublicKeyToValueRequest) returns (AssignPublicKeyToValueResponse){} + rpc AssignPublicKeyToValue(AssignPublicKeyToValueRequest) returns (AssignPublicKeyToValueResponse) {} - rpc RemovePublicKeyFromValue(RemovePublicKeyFromValueRequest) returns (RemovePublicKeyFromValueResponse){} + rpc RemovePublicKeyFromValue(RemovePublicKeyFromValueRequest) returns (RemovePublicKeyFromValueResponse) {} } - - diff --git a/service/policy/attributes/attributes_test.go b/service/policy/attributes/attributes_test.go index 7dc7e3e7c9..da6dd51dbb 100644 --- a/service/policy/attributes/attributes_test.go +++ b/service/policy/attributes/attributes_test.go @@ -201,47 +201,6 @@ func TestCreateAttribute_ValueInvalid_Fails(t *testing.T) { require.Contains(t, err.Error(), "[string.pattern]") } -func TestAttributeKeyAccessServer_Succeeds(t *testing.T) { - validAttrKAS := &attributes.AttributeKeyAccessServer{ - AttributeId: validUUID, - KeyAccessServerId: validUUID, - } - - err := getValidator().Validate(validAttrKAS) - require.NoError(t, err) -} - -func TestAttributeKeyAccessServer_Fails(t *testing.T) { - bad := []struct { - attrID string - kasID string - }{ - { - "", - validUUID, - }, - { - validUUID, - "", - }, - { - "", - "", - }, - {}, - } - - for _, test := range bad { - invalidAttrKAS := &attributes.AttributeKeyAccessServer{ - AttributeId: test.attrID, - KeyAccessServerId: test.kasID, - } - err := getValidator().Validate(invalidAttrKAS) - require.Error(t, err) - require.Contains(t, err.Error(), errMessageUUID) - } -} - func Test_GetAttributeRequest(t *testing.T) { testCases := []struct { name string @@ -470,47 +429,6 @@ func TestCreateAttributeValue_ValueMissing_Fails(t *testing.T) { require.Contains(t, err.Error(), errMessageRequired) } -func TestValueKeyAccessServer_Succeeds(t *testing.T) { - validValueKAS := &attributes.ValueKeyAccessServer{ - ValueId: validUUID, - KeyAccessServerId: validUUID, - } - - err := getValidator().Validate(validValueKAS) - require.NoError(t, err) -} - -func TestValueKeyAccessServer_Fails(t *testing.T) { - bad := []struct { - valID string - kasID string - }{ - { - "", - validUUID, - }, - { - validUUID, - "", - }, - { - "", - "", - }, - {}, - } - - for _, test := range bad { - invalidValKAS := &attributes.ValueKeyAccessServer{ - ValueId: test.valID, - KeyAccessServerId: test.kasID, - } - err := getValidator().Validate(invalidValKAS) - require.Error(t, err) - require.Contains(t, err.Error(), errMessageUUID) - } -} - func Test_GetAttributeValueRequest(t *testing.T) { testCases := []struct { name string diff --git a/service/policy/db/attribute_values.go b/service/policy/db/attribute_values.go index 931377a9b8..73ed45589e 100644 --- a/service/policy/db/attribute_values.go +++ b/service/policy/db/attribute_values.go @@ -83,15 +83,6 @@ func (c PolicyDBClient) GetAttributeValue(ctx context.Context, identifier any) ( return nil, err } - var grants []*policy.KeyAccessServer - if av.Grants != nil { - grants, err = db.KeyAccessServerProtoJSON(av.Grants) - if err != nil { - c.logger.ErrorContext(ctx, "could not unmarshal key access grants", slog.String("error", err.Error())) - return nil, err - } - } - var keys []*policy.SimpleKasKey if av.Keys != nil { keys, err = db.SimpleKasKeysProtoJSON(av.Keys) @@ -105,7 +96,6 @@ func (c PolicyDBClient) GetAttributeValue(ctx context.Context, identifier any) ( Id: av.ID, Value: av.Value, Active: &wrapperspb.BoolValue{Value: av.Active}, - Grants: grants, Metadata: metadata, Attribute: &policy.Attribute{ Id: av.AttributeDefinitionID, @@ -317,18 +307,6 @@ func (c PolicyDBClient) UnsafeDeleteAttributeValue(ctx context.Context, toDelete }, nil } -func (c PolicyDBClient) AssignKeyAccessServerToValue(ctx context.Context, k *attributes.ValueKeyAccessServer) (*attributes.ValueKeyAccessServer, error) { - _, err := c.Queries.AssignKeyAccessServerToAttributeValue(ctx, AssignKeyAccessServerToAttributeValueParams{ - AttributeValueID: k.GetValueId(), - KeyAccessServerID: k.GetKeyAccessServerId(), - }) - if err != nil { - return nil, db.WrapIfKnownInvalidQueryErr(err) - } - - return k, nil -} - func (c PolicyDBClient) RemoveKeyAccessServerFromValue(ctx context.Context, k *attributes.ValueKeyAccessServer) (*attributes.ValueKeyAccessServer, error) { count, err := c.Queries.RemoveKeyAccessServerFromAttributeValue(ctx, RemoveKeyAccessServerFromAttributeValueParams{ AttributeValueID: k.GetValueId(), diff --git a/service/policy/db/attributes.go b/service/policy/db/attributes.go index 82aff380e3..d9fa200800 100644 --- a/service/policy/db/attributes.go +++ b/service/policy/db/attributes.go @@ -62,7 +62,6 @@ type attributeQueryRow struct { active bool namespaceName string valuesJSON []byte - grantsJSON []byte fqn sql.NullString } @@ -81,15 +80,6 @@ func hydrateAttribute(row *attributeQueryRow) (*policy.Attribute, error) { values = v } - var grants []*policy.KeyAccessServer - if row.grantsJSON != nil { - k, err := db.KeyAccessServerProtoJSON(row.grantsJSON) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal grantsJSON [%s]: %w", string(row.grantsJSON), err) - } - grants = k - } - ns := &policy.Namespace{ Id: row.namespaceID, Name: row.namespaceName, @@ -104,7 +94,6 @@ func hydrateAttribute(row *attributeQueryRow) (*policy.Attribute, error) { Active: &wrapperspb.BoolValue{Value: row.active}, Metadata: metadata, Namespace: ns, - Grants: grants, Fqn: row.fqn.String, } @@ -264,7 +253,6 @@ func (c PolicyDBClient) GetAttribute(ctx context.Context, identifier any) (*poli namespaceID: attr.NamespaceID, namespaceName: attr.NamespaceName.String, valuesJSON: attr.Values, - grantsJSON: attr.Grants, fqn: sql.NullString(attr.Fqn), }) if err != nil { @@ -302,15 +290,8 @@ func (c PolicyDBClient) ListAttributesByFqns(ctx context.Context, fqns []string) return nil, fmt.Errorf("failed to unmarshal values [%s]: %w", string(attr.Values), err) } - var grants []*policy.KeyAccessServer - if attr.Grants != nil { - grants, err = db.KeyAccessServerProtoJSON(attr.Grants) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal grants [%s]: %w", string(attr.Grants), err) - } - } - var keys []*policy.SimpleKasKey + var grants []*policy.KeyAccessServer if len(attr.Keys) > 0 { keys, err = db.SimpleKasKeysProtoJSON(attr.Keys) if err != nil { @@ -347,8 +328,8 @@ func (c PolicyDBClient) ListAttributesByFqns(ctx context.Context, fqns []string) Rule: attributesRuleTypeEnumTransformOut(string(attr.Rule)), Fqn: attr.Fqn, Active: &wrapperspb.BoolValue{Value: attr.Active}, - Grants: grants, Namespace: ns, + Grants: grants, Values: values, KasKeys: keys, } @@ -597,18 +578,6 @@ func (c PolicyDBClient) UnsafeDeleteAttribute(ctx context.Context, existing *pol /// Key Access Server assignments /// -func (c PolicyDBClient) AssignKeyAccessServerToAttribute(ctx context.Context, k *attributes.AttributeKeyAccessServer) (*attributes.AttributeKeyAccessServer, error) { - _, err := c.Queries.AssignKeyAccessServerToAttribute(ctx, AssignKeyAccessServerToAttributeParams{ - AttributeDefinitionID: k.GetAttributeId(), - KeyAccessServerID: k.GetKeyAccessServerId(), - }) - if err != nil { - return nil, db.WrapIfKnownInvalidQueryErr(err) - } - - return k, nil -} - func (c PolicyDBClient) RemoveKeyAccessServerFromAttribute(ctx context.Context, k *attributes.AttributeKeyAccessServer) (*attributes.AttributeKeyAccessServer, error) { count, err := c.Queries.RemoveKeyAccessServerFromAttribute(ctx, RemoveKeyAccessServerFromAttributeParams{ AttributeDefinitionID: k.GetAttributeId(), diff --git a/service/policy/db/key_access_server_registry.go b/service/policy/db/key_access_server_registry.go index d8b4a89dd5..0f7dfc68a7 100644 --- a/service/policy/db/key_access_server_registry.go +++ b/service/policy/db/key_access_server_registry.go @@ -350,11 +350,6 @@ func (c PolicyDBClient) ListKeyAccessServerGrants(ctx context.Context, r *kasreg }, nil } -func isValidBase64(s string) bool { - _, err := base64.StdEncoding.DecodeString(s) - return err == nil -} - /* * Key Access Server Keys */ @@ -932,3 +927,8 @@ func (c PolicyDBClient) verifyKeyIsActive(ctx context.Context, id string) error return nil } + +func isValidBase64(s string) bool { + _, err := base64.StdEncoding.DecodeString(s) + return err == nil +} diff --git a/service/policy/db/models.go b/service/policy/db/models.go index ba9fe7d908..d3342262cd 100644 --- a/service/policy/db/models.go +++ b/service/policy/db/models.go @@ -279,7 +279,7 @@ type KeyAccessServerKey struct { type ProviderConfig struct { // Unique identifier for the provider configuration ID string `json:"id"` - // Name of the key provider + // Unique name for the key provider. ProviderName string `json:"provider_name"` // Configuration details for the key provider Config []byte `json:"config"` diff --git a/service/policy/db/namespaces.go b/service/policy/db/namespaces.go index 6888570080..d7d4e19271 100644 --- a/service/policy/db/namespaces.go +++ b/service/policy/db/namespaces.go @@ -52,15 +52,6 @@ func (c PolicyDBClient) GetNamespace(ctx context.Context, identifier any) (*poli return nil, err } - var grants []*policy.KeyAccessServer - if ns.Grants != nil { - grants, err = db.KeyAccessServerProtoJSON(ns.Grants) - if err != nil { - c.logger.ErrorContext(ctx, "could not unmarshal grants", slog.String("error", err.Error())) - return nil, err - } - } - var keys []*policy.SimpleKasKey if len(ns.Keys) > 0 { keys, err = db.SimpleKasKeysProtoJSON(ns.Keys) @@ -74,7 +65,6 @@ func (c PolicyDBClient) GetNamespace(ctx context.Context, identifier any) (*poli Id: ns.ID, Name: ns.Name, Active: &wrapperspb.BoolValue{Value: ns.Active}, - Grants: grants, Metadata: metadata, Fqn: ns.Fqn.String, KasKeys: keys, @@ -337,18 +327,6 @@ func (c PolicyDBClient) UnsafeDeleteNamespace(ctx context.Context, existing *pol }, nil } -func (c PolicyDBClient) AssignKeyAccessServerToNamespace(ctx context.Context, k *namespaces.NamespaceKeyAccessServer) (*namespaces.NamespaceKeyAccessServer, error) { - _, err := c.Queries.AssignKeyAccessServerToNamespace(ctx, AssignKeyAccessServerToNamespaceParams{ - NamespaceID: k.GetNamespaceId(), - KeyAccessServerID: k.GetKeyAccessServerId(), - }) - if err != nil { - return nil, db.WrapIfKnownInvalidQueryErr(err) - } - - return k, nil -} - func (c PolicyDBClient) RemoveKeyAccessServerFromNamespace(ctx context.Context, k *namespaces.NamespaceKeyAccessServer) (*namespaces.NamespaceKeyAccessServer, error) { count, err := c.Queries.RemoveKeyAccessServerFromNamespace(ctx, RemoveKeyAccessServerFromNamespaceParams{ NamespaceID: k.GetNamespaceId(), diff --git a/service/policy/db/query.sql b/service/policy/db/query.sql index 107b001a94..9a5c12b61e 100644 --- a/service/policy/db/query.sql +++ b/service/policy/db/query.sql @@ -1,7 +1,6 @@ ---------------------------------------------------------------- -- KEY ACCESS SERVERS ---------------------------------------------------------------- - -- name: ListKeyAccessServerGrants :many WITH listed AS ( SELECT @@ -414,18 +413,8 @@ LEFT JOIN ( av.id, av.value, av.active, - JSON_AGG( - DISTINCT JSONB_BUILD_OBJECT( - 'id', vkas.id, - 'uri', vkas.uri, - 'name', vkas.name, - 'public_key', vkas.public_key - ) - ) FILTER (WHERE vkas.id IS NOT NULL AND vkas.uri IS NOT NULL AND vkas.public_key IS NOT NULL) AS val_grants_arr, av.attribute_definition_id FROM attribute_values av - LEFT JOIN attribute_value_key_access_grants avg ON av.id = avg.attribute_value_id - LEFT JOIN key_access_servers vkas ON avg.key_access_server_id = vkas.id GROUP BY av.id ) avt ON avt.attribute_definition_id = ad.id LEFT JOIN attribute_fqns fqns ON fqns.attribute_id = ad.id AND fqns.value_id IS NULL @@ -468,19 +457,9 @@ WITH target_definition AS ( ad.rule, ad.active, ad.values_order, - JSONB_AGG( - DISTINCT JSONB_BUILD_OBJECT( - 'id', kas.id, - 'uri', kas.uri, - 'name', kas.name, - 'public_key', kas.public_key - ) - ) FILTER (WHERE kas.id IS NOT NULL) AS grants, defk.keys AS keys FROM attribute_fqns fqns INNER JOIN attribute_definitions ad ON fqns.attribute_id = ad.id - LEFT JOIN attribute_definition_key_access_grants adkag ON ad.id = adkag.attribute_definition_id - LEFT JOIN key_access_servers kas ON adkag.key_access_server_id = kas.id LEFT JOIN ( SELECT k.definition_id, @@ -512,21 +491,11 @@ namespaces AS ( 'name', n.name, 'active', n.active, 'fqn', fqns.fqn, - 'grants', JSONB_AGG( - DISTINCT JSONB_BUILD_OBJECT( - 'id', kas.id, - 'uri', kas.uri, - 'name', kas.name, - 'public_key', kas.public_key - ) - ) FILTER (WHERE kas.id IS NOT NULL), 'kas_keys', nmp_keys.keys ) AS namespace FROM target_definition td INNER JOIN attribute_namespaces n ON td.namespace_id = n.id INNER JOIN attribute_fqns fqns ON n.id = fqns.namespace_id - LEFT JOIN attribute_namespace_key_access_grants ankag ON n.id = ankag.namespace_id - LEFT JOIN key_access_servers kas ON ankag.key_access_server_id = kas.id LEFT JOIN ( SELECT k.namespace_id, @@ -550,23 +519,6 @@ namespaces AS ( AND (fqns.attribute_id IS NULL AND fqns.value_id IS NULL) GROUP BY n.id, fqns.fqn, nmp_keys.keys ), -value_grants AS ( - SELECT - av.id, - JSON_AGG( - DISTINCT JSONB_BUILD_OBJECT( - 'id', kas.id, - 'uri', kas.uri, - 'name', kas.name, - 'public_key', kas.public_key - ) - ) FILTER (WHERE kas.id IS NOT NULL) AS grants - FROM target_definition td - LEFT JOIN attribute_values av on td.id = av.attribute_definition_id - LEFT JOIN attribute_value_key_access_grants avkag ON av.id = avkag.attribute_value_id - LEFT JOIN key_access_servers kas ON avkag.key_access_server_id = kas.id - GROUP BY av.id -), value_subject_mappings AS ( SELECT av.id, @@ -631,7 +583,6 @@ values AS ( 'value', av.value, 'active', av.active, 'fqn', fqns.fqn, - 'grants', avg.grants, 'subject_mappings', avsm.sub_maps, 'resource_mappings', avrm.res_maps, 'kas_keys', value_keys.keys @@ -641,7 +592,6 @@ values AS ( FROM target_definition td LEFT JOIN attribute_values av ON td.id = av.attribute_definition_id LEFT JOIN attribute_fqns fqns ON av.id = fqns.value_id - LEFT JOIN value_grants avg ON av.id = avg.id LEFT JOIN value_subject_mappings avsm ON av.id = avsm.id LEFT JOIN value_resource_mappings avrm ON av.id = avrm.id LEFT JOIN ( @@ -674,7 +624,6 @@ SELECT n.namespace, fqns.fqn, values.values, - td.grants, td.keys FROM target_definition td INNER JOIN attribute_fqns fqns ON td.id = fqns.attribute_id @@ -699,14 +648,6 @@ SELECT 'fqn', CONCAT(fqns.fqn, '/value/', avt.value) ) ORDER BY ARRAY_POSITION(ad.values_order, avt.id) ) AS values, - JSONB_AGG( - DISTINCT JSONB_BUILD_OBJECT( - 'id', kas.id, - 'uri', kas.uri, - 'name', kas.name, - 'public_key', kas.public_key - ) - ) FILTER (WHERE adkag.attribute_definition_id IS NOT NULL) AS grants, fqns.fqn, defk.keys as keys FROM attribute_definitions ad @@ -716,15 +657,10 @@ LEFT JOIN ( av.id, av.value, av.active, - JSON_AGG(DISTINCT JSONB_BUILD_OBJECT('id', vkas.id,'uri', vkas.uri,'name', vkas.name,'public_key', vkas.public_key )) FILTER (WHERE vkas.id IS NOT NULL AND vkas.uri IS NOT NULL AND vkas.public_key IS NOT NULL) AS val_grants_arr, av.attribute_definition_id FROM attribute_values av - LEFT JOIN attribute_value_key_access_grants avg ON av.id = avg.attribute_value_id - LEFT JOIN key_access_servers vkas ON avg.key_access_server_id = vkas.id GROUP BY av.id ) avt ON avt.attribute_definition_id = ad.id -LEFT JOIN attribute_definition_key_access_grants adkag ON adkag.attribute_definition_id = ad.id -LEFT JOIN key_access_servers kas ON kas.id = adkag.key_access_server_id LEFT JOIN attribute_fqns fqns ON fqns.attribute_id = ad.id AND fqns.value_id IS NULL LEFT JOIN ( SELECT @@ -768,10 +704,6 @@ WHERE id = $1; -- name: DeleteAttribute :execrows DELETE FROM attribute_definitions WHERE id = $1; --- name: AssignKeyAccessServerToAttribute :execrows -INSERT INTO attribute_definition_key_access_grants (attribute_definition_id, key_access_server_id) -VALUES ($1, $2); - -- name: RemoveKeyAccessServerFromAttribute :execrows DELETE FROM attribute_definition_key_access_grants WHERE attribute_definition_id = $1 AND key_access_server_id = $2; @@ -826,19 +758,9 @@ SELECT JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', av.metadata -> 'labels', 'created_at', av.created_at, 'updated_at', av.updated_at)) as metadata, av.attribute_definition_id, fqns.fqn, - JSONB_AGG( - DISTINCT JSONB_BUILD_OBJECT( - 'id', kas.id, - 'uri', kas.uri, - 'name', kas.name, - 'public_key', kas.public_key - ) - ) FILTER (WHERE avkag.attribute_value_id IS NOT NULL) AS grants, value_keys.keys as keys FROM attribute_values av LEFT JOIN attribute_fqns fqns ON av.id = fqns.value_id -LEFT JOIN attribute_value_key_access_grants avkag ON av.id = avkag.attribute_value_id -LEFT JOIN key_access_servers kas ON avkag.key_access_server_id = kas.id LEFT JOIN ( SELECT k.value_id, @@ -879,10 +801,6 @@ WHERE id = $1; -- name: DeleteAttributeValue :execrows DELETE FROM attribute_values WHERE id = $1; --- name: AssignKeyAccessServerToAttributeValue :execrows -INSERT INTO attribute_value_key_access_grants (attribute_value_id, key_access_server_id) -VALUES ($1, $2); - -- name: RemoveKeyAccessServerFromAttributeValue :execrows DELETE FROM attribute_value_key_access_grants WHERE attribute_value_id = $1 AND key_access_server_id = $2; @@ -1065,16 +983,8 @@ SELECT ns.active, fqns.fqn, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', ns.metadata -> 'labels', 'created_at', ns.created_at, 'updated_at', ns.updated_at)) as metadata, - JSONB_AGG(DISTINCT JSONB_BUILD_OBJECT( - 'id', kas.id, - 'uri', kas.uri, - 'name', kas.name, - 'public_key', kas.public_key - )) FILTER (WHERE kas_ns_grants.namespace_id IS NOT NULL) as grants, nmp_keys.keys as keys FROM attribute_namespaces ns -LEFT JOIN attribute_namespace_key_access_grants kas_ns_grants ON kas_ns_grants.namespace_id = ns.id -LEFT JOIN key_access_servers kas ON kas.id = kas_ns_grants.key_access_server_id LEFT JOIN attribute_fqns fqns ON fqns.namespace_id = ns.id LEFT JOIN ( SELECT @@ -1117,10 +1027,6 @@ WHERE id = $1; -- name: DeleteNamespace :execrows DELETE FROM attribute_namespaces WHERE id = $1; --- name: AssignKeyAccessServerToNamespace :execrows -INSERT INTO attribute_namespace_key_access_grants (namespace_id, key_access_server_id) -VALUES ($1, $2); - -- name: RemoveKeyAccessServerFromNamespace :execrows DELETE FROM attribute_namespace_key_access_grants WHERE namespace_id = $1 AND key_access_server_id = $2; diff --git a/service/policy/db/query.sql.go b/service/policy/db/query.sql.go index 14c4605e42..7ff01345e0 100644 --- a/service/policy/db/query.sql.go +++ b/service/policy/db/query.sql.go @@ -11,72 +11,6 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) -const assignKeyAccessServerToAttribute = `-- name: AssignKeyAccessServerToAttribute :execrows -INSERT INTO attribute_definition_key_access_grants (attribute_definition_id, key_access_server_id) -VALUES ($1, $2) -` - -type AssignKeyAccessServerToAttributeParams struct { - AttributeDefinitionID string `json:"attribute_definition_id"` - KeyAccessServerID string `json:"key_access_server_id"` -} - -// AssignKeyAccessServerToAttribute -// -// INSERT INTO attribute_definition_key_access_grants (attribute_definition_id, key_access_server_id) -// VALUES ($1, $2) -func (q *Queries) AssignKeyAccessServerToAttribute(ctx context.Context, arg AssignKeyAccessServerToAttributeParams) (int64, error) { - result, err := q.db.Exec(ctx, assignKeyAccessServerToAttribute, arg.AttributeDefinitionID, arg.KeyAccessServerID) - if err != nil { - return 0, err - } - return result.RowsAffected(), nil -} - -const assignKeyAccessServerToAttributeValue = `-- name: AssignKeyAccessServerToAttributeValue :execrows -INSERT INTO attribute_value_key_access_grants (attribute_value_id, key_access_server_id) -VALUES ($1, $2) -` - -type AssignKeyAccessServerToAttributeValueParams struct { - AttributeValueID string `json:"attribute_value_id"` - KeyAccessServerID string `json:"key_access_server_id"` -} - -// AssignKeyAccessServerToAttributeValue -// -// INSERT INTO attribute_value_key_access_grants (attribute_value_id, key_access_server_id) -// VALUES ($1, $2) -func (q *Queries) AssignKeyAccessServerToAttributeValue(ctx context.Context, arg AssignKeyAccessServerToAttributeValueParams) (int64, error) { - result, err := q.db.Exec(ctx, assignKeyAccessServerToAttributeValue, arg.AttributeValueID, arg.KeyAccessServerID) - if err != nil { - return 0, err - } - return result.RowsAffected(), nil -} - -const assignKeyAccessServerToNamespace = `-- name: AssignKeyAccessServerToNamespace :execrows -INSERT INTO attribute_namespace_key_access_grants (namespace_id, key_access_server_id) -VALUES ($1, $2) -` - -type AssignKeyAccessServerToNamespaceParams struct { - NamespaceID string `json:"namespace_id"` - KeyAccessServerID string `json:"key_access_server_id"` -} - -// AssignKeyAccessServerToNamespace -// -// INSERT INTO attribute_namespace_key_access_grants (namespace_id, key_access_server_id) -// VALUES ($1, $2) -func (q *Queries) AssignKeyAccessServerToNamespace(ctx context.Context, arg AssignKeyAccessServerToNamespaceParams) (int64, error) { - result, err := q.db.Exec(ctx, assignKeyAccessServerToNamespace, arg.NamespaceID, arg.KeyAccessServerID) - if err != nil { - return 0, err - } - return result.RowsAffected(), nil -} - const createAttribute = `-- name: CreateAttribute :one INSERT INTO attribute_definitions (namespace_id, name, rule, metadata) VALUES ($1, $2, $3, $4) @@ -416,14 +350,6 @@ SELECT 'fqn', CONCAT(fqns.fqn, '/value/', avt.value) ) ORDER BY ARRAY_POSITION(ad.values_order, avt.id) ) AS values, - JSONB_AGG( - DISTINCT JSONB_BUILD_OBJECT( - 'id', kas.id, - 'uri', kas.uri, - 'name', kas.name, - 'public_key', kas.public_key - ) - ) FILTER (WHERE adkag.attribute_definition_id IS NOT NULL) AS grants, fqns.fqn, defk.keys as keys FROM attribute_definitions ad @@ -433,15 +359,10 @@ LEFT JOIN ( av.id, av.value, av.active, - JSON_AGG(DISTINCT JSONB_BUILD_OBJECT('id', vkas.id,'uri', vkas.uri,'name', vkas.name,'public_key', vkas.public_key )) FILTER (WHERE vkas.id IS NOT NULL AND vkas.uri IS NOT NULL AND vkas.public_key IS NOT NULL) AS val_grants_arr, av.attribute_definition_id FROM attribute_values av - LEFT JOIN attribute_value_key_access_grants avg ON av.id = avg.attribute_value_id - LEFT JOIN key_access_servers vkas ON avg.key_access_server_id = vkas.id GROUP BY av.id ) avt ON avt.attribute_definition_id = ad.id -LEFT JOIN attribute_definition_key_access_grants adkag ON adkag.attribute_definition_id = ad.id -LEFT JOIN key_access_servers kas ON kas.id = adkag.key_access_server_id LEFT JOIN attribute_fqns fqns ON fqns.attribute_id = ad.id AND fqns.value_id IS NULL LEFT JOIN ( SELECT @@ -481,7 +402,6 @@ type GetAttributeRow struct { Active bool `json:"active"` NamespaceName pgtype.Text `json:"namespace_name"` Values []byte `json:"values"` - Grants []byte `json:"grants"` Fqn pgtype.Text `json:"fqn"` Keys []byte `json:"keys"` } @@ -504,14 +424,6 @@ type GetAttributeRow struct { // 'fqn', CONCAT(fqns.fqn, '/value/', avt.value) // ) ORDER BY ARRAY_POSITION(ad.values_order, avt.id) // ) AS values, -// JSONB_AGG( -// DISTINCT JSONB_BUILD_OBJECT( -// 'id', kas.id, -// 'uri', kas.uri, -// 'name', kas.name, -// 'public_key', kas.public_key -// ) -// ) FILTER (WHERE adkag.attribute_definition_id IS NOT NULL) AS grants, // fqns.fqn, // defk.keys as keys // FROM attribute_definitions ad @@ -521,15 +433,10 @@ type GetAttributeRow struct { // av.id, // av.value, // av.active, -// JSON_AGG(DISTINCT JSONB_BUILD_OBJECT('id', vkas.id,'uri', vkas.uri,'name', vkas.name,'public_key', vkas.public_key )) FILTER (WHERE vkas.id IS NOT NULL AND vkas.uri IS NOT NULL AND vkas.public_key IS NOT NULL) AS val_grants_arr, // av.attribute_definition_id // FROM attribute_values av -// LEFT JOIN attribute_value_key_access_grants avg ON av.id = avg.attribute_value_id -// LEFT JOIN key_access_servers vkas ON avg.key_access_server_id = vkas.id // GROUP BY av.id // ) avt ON avt.attribute_definition_id = ad.id -// LEFT JOIN attribute_definition_key_access_grants adkag ON adkag.attribute_definition_id = ad.id -// LEFT JOIN key_access_servers kas ON kas.id = adkag.key_access_server_id // LEFT JOIN attribute_fqns fqns ON fqns.attribute_id = ad.id AND fqns.value_id IS NULL // LEFT JOIN ( // SELECT @@ -565,7 +472,6 @@ func (q *Queries) GetAttribute(ctx context.Context, arg GetAttributeParams) (Get &i.Active, &i.NamespaceName, &i.Values, - &i.Grants, &i.Fqn, &i.Keys, ) @@ -580,19 +486,9 @@ SELECT JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', av.metadata -> 'labels', 'created_at', av.created_at, 'updated_at', av.updated_at)) as metadata, av.attribute_definition_id, fqns.fqn, - JSONB_AGG( - DISTINCT JSONB_BUILD_OBJECT( - 'id', kas.id, - 'uri', kas.uri, - 'name', kas.name, - 'public_key', kas.public_key - ) - ) FILTER (WHERE avkag.attribute_value_id IS NOT NULL) AS grants, value_keys.keys as keys FROM attribute_values av LEFT JOIN attribute_fqns fqns ON av.id = fqns.value_id -LEFT JOIN attribute_value_key_access_grants avkag ON av.id = avkag.attribute_value_id -LEFT JOIN key_access_servers kas ON avkag.key_access_server_id = kas.id LEFT JOIN ( SELECT k.value_id, @@ -629,7 +525,6 @@ type GetAttributeValueRow struct { Metadata []byte `json:"metadata"` AttributeDefinitionID string `json:"attribute_definition_id"` Fqn pgtype.Text `json:"fqn"` - Grants []byte `json:"grants"` Keys []byte `json:"keys"` } @@ -642,19 +537,9 @@ type GetAttributeValueRow struct { // JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', av.metadata -> 'labels', 'created_at', av.created_at, 'updated_at', av.updated_at)) as metadata, // av.attribute_definition_id, // fqns.fqn, -// JSONB_AGG( -// DISTINCT JSONB_BUILD_OBJECT( -// 'id', kas.id, -// 'uri', kas.uri, -// 'name', kas.name, -// 'public_key', kas.public_key -// ) -// ) FILTER (WHERE avkag.attribute_value_id IS NOT NULL) AS grants, // value_keys.keys as keys // FROM attribute_values av // LEFT JOIN attribute_fqns fqns ON av.id = fqns.value_id -// LEFT JOIN attribute_value_key_access_grants avkag ON av.id = avkag.attribute_value_id -// LEFT JOIN key_access_servers kas ON avkag.key_access_server_id = kas.id // LEFT JOIN ( // SELECT // k.value_id, @@ -687,7 +572,6 @@ func (q *Queries) GetAttributeValue(ctx context.Context, arg GetAttributeValuePa &i.Metadata, &i.AttributeDefinitionID, &i.Fqn, - &i.Grants, &i.Keys, ) return i, err @@ -808,16 +692,8 @@ SELECT ns.active, fqns.fqn, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', ns.metadata -> 'labels', 'created_at', ns.created_at, 'updated_at', ns.updated_at)) as metadata, - JSONB_AGG(DISTINCT JSONB_BUILD_OBJECT( - 'id', kas.id, - 'uri', kas.uri, - 'name', kas.name, - 'public_key', kas.public_key - )) FILTER (WHERE kas_ns_grants.namespace_id IS NOT NULL) as grants, nmp_keys.keys as keys FROM attribute_namespaces ns -LEFT JOIN attribute_namespace_key_access_grants kas_ns_grants ON kas_ns_grants.namespace_id = ns.id -LEFT JOIN key_access_servers kas ON kas.id = kas_ns_grants.key_access_server_id LEFT JOIN attribute_fqns fqns ON fqns.namespace_id = ns.id LEFT JOIN ( SELECT @@ -855,7 +731,6 @@ type GetNamespaceRow struct { Active bool `json:"active"` Fqn pgtype.Text `json:"fqn"` Metadata []byte `json:"metadata"` - Grants []byte `json:"grants"` Keys []byte `json:"keys"` } @@ -867,16 +742,8 @@ type GetNamespaceRow struct { // ns.active, // fqns.fqn, // JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', ns.metadata -> 'labels', 'created_at', ns.created_at, 'updated_at', ns.updated_at)) as metadata, -// JSONB_AGG(DISTINCT JSONB_BUILD_OBJECT( -// 'id', kas.id, -// 'uri', kas.uri, -// 'name', kas.name, -// 'public_key', kas.public_key -// )) FILTER (WHERE kas_ns_grants.namespace_id IS NOT NULL) as grants, // nmp_keys.keys as keys // FROM attribute_namespaces ns -// LEFT JOIN attribute_namespace_key_access_grants kas_ns_grants ON kas_ns_grants.namespace_id = ns.id -// LEFT JOIN key_access_servers kas ON kas.id = kas_ns_grants.key_access_server_id // LEFT JOIN attribute_fqns fqns ON fqns.namespace_id = ns.id // LEFT JOIN ( // SELECT @@ -910,7 +777,6 @@ func (q *Queries) GetNamespace(ctx context.Context, arg GetNamespaceParams) (Get &i.Active, &i.Fqn, &i.Metadata, - &i.Grants, &i.Keys, ) return i, err @@ -1158,18 +1024,8 @@ LEFT JOIN ( av.id, av.value, av.active, - JSON_AGG( - DISTINCT JSONB_BUILD_OBJECT( - 'id', vkas.id, - 'uri', vkas.uri, - 'name', vkas.name, - 'public_key', vkas.public_key - ) - ) FILTER (WHERE vkas.id IS NOT NULL AND vkas.uri IS NOT NULL AND vkas.public_key IS NOT NULL) AS val_grants_arr, av.attribute_definition_id FROM attribute_values av - LEFT JOIN attribute_value_key_access_grants avg ON av.id = avg.attribute_value_id - LEFT JOIN key_access_servers vkas ON avg.key_access_server_id = vkas.id GROUP BY av.id ) avt ON avt.attribute_definition_id = ad.id LEFT JOIN attribute_fqns fqns ON fqns.attribute_id = ad.id AND fqns.value_id IS NULL @@ -1237,18 +1093,8 @@ type ListAttributesDetailRow struct { // av.id, // av.value, // av.active, -// JSON_AGG( -// DISTINCT JSONB_BUILD_OBJECT( -// 'id', vkas.id, -// 'uri', vkas.uri, -// 'name', vkas.name, -// 'public_key', vkas.public_key -// ) -// ) FILTER (WHERE vkas.id IS NOT NULL AND vkas.uri IS NOT NULL AND vkas.public_key IS NOT NULL) AS val_grants_arr, // av.attribute_definition_id // FROM attribute_values av -// LEFT JOIN attribute_value_key_access_grants avg ON av.id = avg.attribute_value_id -// LEFT JOIN key_access_servers vkas ON avg.key_access_server_id = vkas.id // GROUP BY av.id // ) avt ON avt.attribute_definition_id = ad.id // LEFT JOIN attribute_fqns fqns ON fqns.attribute_id = ad.id AND fqns.value_id IS NULL @@ -1386,7 +1232,6 @@ func (q *Queries) ListAttributesSummary(ctx context.Context, arg ListAttributesS } const listKeyAccessServerGrants = `-- name: ListKeyAccessServerGrants :many - WITH listed AS ( SELECT COUNT(*) OVER () AS total, @@ -3928,19 +3773,9 @@ WITH target_definition AS ( ad.rule, ad.active, ad.values_order, - JSONB_AGG( - DISTINCT JSONB_BUILD_OBJECT( - 'id', kas.id, - 'uri', kas.uri, - 'name', kas.name, - 'public_key', kas.public_key - ) - ) FILTER (WHERE kas.id IS NOT NULL) AS grants, defk.keys AS keys FROM attribute_fqns fqns INNER JOIN attribute_definitions ad ON fqns.attribute_id = ad.id - LEFT JOIN attribute_definition_key_access_grants adkag ON ad.id = adkag.attribute_definition_id - LEFT JOIN key_access_servers kas ON adkag.key_access_server_id = kas.id LEFT JOIN ( SELECT k.definition_id, @@ -3972,21 +3807,11 @@ namespaces AS ( 'name', n.name, 'active', n.active, 'fqn', fqns.fqn, - 'grants', JSONB_AGG( - DISTINCT JSONB_BUILD_OBJECT( - 'id', kas.id, - 'uri', kas.uri, - 'name', kas.name, - 'public_key', kas.public_key - ) - ) FILTER (WHERE kas.id IS NOT NULL), 'kas_keys', nmp_keys.keys ) AS namespace FROM target_definition td INNER JOIN attribute_namespaces n ON td.namespace_id = n.id INNER JOIN attribute_fqns fqns ON n.id = fqns.namespace_id - LEFT JOIN attribute_namespace_key_access_grants ankag ON n.id = ankag.namespace_id - LEFT JOIN key_access_servers kas ON ankag.key_access_server_id = kas.id LEFT JOIN ( SELECT k.namespace_id, @@ -4010,23 +3835,6 @@ namespaces AS ( AND (fqns.attribute_id IS NULL AND fqns.value_id IS NULL) GROUP BY n.id, fqns.fqn, nmp_keys.keys ), -value_grants AS ( - SELECT - av.id, - JSON_AGG( - DISTINCT JSONB_BUILD_OBJECT( - 'id', kas.id, - 'uri', kas.uri, - 'name', kas.name, - 'public_key', kas.public_key - ) - ) FILTER (WHERE kas.id IS NOT NULL) AS grants - FROM target_definition td - LEFT JOIN attribute_values av on td.id = av.attribute_definition_id - LEFT JOIN attribute_value_key_access_grants avkag ON av.id = avkag.attribute_value_id - LEFT JOIN key_access_servers kas ON avkag.key_access_server_id = kas.id - GROUP BY av.id -), value_subject_mappings AS ( SELECT av.id, @@ -4091,7 +3899,6 @@ values AS ( 'value', av.value, 'active', av.active, 'fqn', fqns.fqn, - 'grants', avg.grants, 'subject_mappings', avsm.sub_maps, 'resource_mappings', avrm.res_maps, 'kas_keys', value_keys.keys @@ -4101,7 +3908,6 @@ values AS ( FROM target_definition td LEFT JOIN attribute_values av ON td.id = av.attribute_definition_id LEFT JOIN attribute_fqns fqns ON av.id = fqns.value_id - LEFT JOIN value_grants avg ON av.id = avg.id LEFT JOIN value_subject_mappings avsm ON av.id = avsm.id LEFT JOIN value_resource_mappings avrm ON av.id = avrm.id LEFT JOIN ( @@ -4134,7 +3940,6 @@ SELECT n.namespace, fqns.fqn, values.values, - td.grants, td.keys FROM target_definition td INNER JOIN attribute_fqns fqns ON td.id = fqns.attribute_id @@ -4151,7 +3956,6 @@ type listAttributesByDefOrValueFqnsRow struct { Namespace []byte `json:"namespace"` Fqn string `json:"fqn"` Values []byte `json:"values"` - Grants []byte `json:"grants"` Keys []byte `json:"keys"` } @@ -4165,19 +3969,9 @@ type listAttributesByDefOrValueFqnsRow struct { // ad.rule, // ad.active, // ad.values_order, -// JSONB_AGG( -// DISTINCT JSONB_BUILD_OBJECT( -// 'id', kas.id, -// 'uri', kas.uri, -// 'name', kas.name, -// 'public_key', kas.public_key -// ) -// ) FILTER (WHERE kas.id IS NOT NULL) AS grants, // defk.keys AS keys // FROM attribute_fqns fqns // INNER JOIN attribute_definitions ad ON fqns.attribute_id = ad.id -// LEFT JOIN attribute_definition_key_access_grants adkag ON ad.id = adkag.attribute_definition_id -// LEFT JOIN key_access_servers kas ON adkag.key_access_server_id = kas.id // LEFT JOIN ( // SELECT // k.definition_id, @@ -4209,21 +4003,11 @@ type listAttributesByDefOrValueFqnsRow struct { // 'name', n.name, // 'active', n.active, // 'fqn', fqns.fqn, -// 'grants', JSONB_AGG( -// DISTINCT JSONB_BUILD_OBJECT( -// 'id', kas.id, -// 'uri', kas.uri, -// 'name', kas.name, -// 'public_key', kas.public_key -// ) -// ) FILTER (WHERE kas.id IS NOT NULL), // 'kas_keys', nmp_keys.keys // ) AS namespace // FROM target_definition td // INNER JOIN attribute_namespaces n ON td.namespace_id = n.id // INNER JOIN attribute_fqns fqns ON n.id = fqns.namespace_id -// LEFT JOIN attribute_namespace_key_access_grants ankag ON n.id = ankag.namespace_id -// LEFT JOIN key_access_servers kas ON ankag.key_access_server_id = kas.id // LEFT JOIN ( // SELECT // k.namespace_id, @@ -4247,23 +4031,6 @@ type listAttributesByDefOrValueFqnsRow struct { // AND (fqns.attribute_id IS NULL AND fqns.value_id IS NULL) // GROUP BY n.id, fqns.fqn, nmp_keys.keys // ), -// value_grants AS ( -// SELECT -// av.id, -// JSON_AGG( -// DISTINCT JSONB_BUILD_OBJECT( -// 'id', kas.id, -// 'uri', kas.uri, -// 'name', kas.name, -// 'public_key', kas.public_key -// ) -// ) FILTER (WHERE kas.id IS NOT NULL) AS grants -// FROM target_definition td -// LEFT JOIN attribute_values av on td.id = av.attribute_definition_id -// LEFT JOIN attribute_value_key_access_grants avkag ON av.id = avkag.attribute_value_id -// LEFT JOIN key_access_servers kas ON avkag.key_access_server_id = kas.id -// GROUP BY av.id -// ), // value_subject_mappings AS ( // SELECT // av.id, @@ -4328,7 +4095,6 @@ type listAttributesByDefOrValueFqnsRow struct { // 'value', av.value, // 'active', av.active, // 'fqn', fqns.fqn, -// 'grants', avg.grants, // 'subject_mappings', avsm.sub_maps, // 'resource_mappings', avrm.res_maps, // 'kas_keys', value_keys.keys @@ -4338,7 +4104,6 @@ type listAttributesByDefOrValueFqnsRow struct { // FROM target_definition td // LEFT JOIN attribute_values av ON td.id = av.attribute_definition_id // LEFT JOIN attribute_fqns fqns ON av.id = fqns.value_id -// LEFT JOIN value_grants avg ON av.id = avg.id // LEFT JOIN value_subject_mappings avsm ON av.id = avsm.id // LEFT JOIN value_resource_mappings avrm ON av.id = avrm.id // LEFT JOIN ( @@ -4371,7 +4136,6 @@ type listAttributesByDefOrValueFqnsRow struct { // n.namespace, // fqns.fqn, // values.values, -// td.grants, // td.keys // FROM target_definition td // INNER JOIN attribute_fqns fqns ON td.id = fqns.attribute_id @@ -4395,7 +4159,6 @@ func (q *Queries) listAttributesByDefOrValueFqns(ctx context.Context, fqns []str &i.Namespace, &i.Fqn, &i.Values, - &i.Grants, &i.Keys, ); err != nil { return nil, err diff --git a/service/policy/kasregistry/key_access_server_registry.proto b/service/policy/kasregistry/key_access_server_registry.proto index 5b17d06b35..4af8420fb5 100644 --- a/service/policy/kasregistry/key_access_server_registry.proto +++ b/service/policy/kasregistry/key_access_server_registry.proto @@ -3,9 +3,8 @@ syntax = "proto3"; package policy.kasregistry; import "buf/validate/validate.proto"; -import "google/api/annotations.proto"; - import "common/common.proto"; +import "google/api/annotations.proto"; import "policy/objects.proto"; import "policy/selectors.proto"; @@ -314,6 +313,7 @@ message ActivatePublicKeyResponse { // attribute tree relation. If grants to a known namespace, attribute, or value // are needed, use the respective GET request to the specific policy object. message ListKeyAccessServerGrantsRequest { + option deprecated = true; // Optional // Filter LIST by ID of a registered Key Access Server. // If neither is provided, grants from all registered KASs to policy attribute @@ -365,6 +365,8 @@ message ListKeyAccessServerGrantsRequest { // Deprecated message ListKeyAccessServerGrantsResponse { + option deprecated = true; + repeated KeyAccessServerGrants grants = 1 [deprecated = true]; policy.PageResponse pagination = 10; @@ -633,6 +635,7 @@ service KeyAccessServerRegistryService { // Deprecated rpc ListKeyAccessServerGrants(ListKeyAccessServerGrantsRequest) returns (ListKeyAccessServerGrantsResponse) { + option deprecated = true; option idempotency_level = NO_SIDE_EFFECTS; } diff --git a/service/policy/kasregistry/key_access_server_registry_test.go b/service/policy/kasregistry/key_access_server_registry_test.go index 2e6a04d670..5df15134d4 100644 --- a/service/policy/kasregistry/key_access_server_registry_test.go +++ b/service/policy/kasregistry/key_access_server_registry_test.go @@ -217,80 +217,6 @@ func Test_DeleteKeyAccessServerRequest_Succeeds(t *testing.T) { require.NoError(t, err) } -func Test_ListKeyAccessServerGrantsRequest_Fails(t *testing.T) { - v := getValidator() - bad := []struct { - id string - uri string - scenario string - }{ - { - "", - "missing.scheme", - "bad URI format", - }, - { - "bad-id-format", - validSecureURI, - "invalid UUID", - }, - } - - for _, test := range bad { - req := &kasregistry.ListKeyAccessServerGrantsRequest{ - KasId: test.id, - KasUri: test.uri, - } - err := v.Validate(req) - require.Error(t, err, test.scenario) - } -} - -func Test_ListKeyAccessServerGrantsRequest_Succeeds(t *testing.T) { - v := getValidator() - - good := []struct { - id string - uri string - scenario string - }{ - { - validUUID, - validSecureURI, - "both https URI and ID", - }, - { - validUUID, - validInsecureURI, - "both http URI and ID", - }, - { - validUUID, - "", - "no optional URI", - }, - { - "", - validSecureURI, - "no optional KAS ID", - }, - { - "", - "", - "neither optional ID nor URI", - }, - } - - for _, test := range good { - req := &kasregistry.ListKeyAccessServerGrantsRequest{ - KasId: test.id, - KasUri: test.uri, - } - err := v.Validate(req) - require.NoError(t, err, test.scenario) - } -} - func Test_CreateKeyAccessServer_Succeeds(t *testing.T) { good := []struct { uri string diff --git a/service/policy/namespaces/namespaces.go b/service/policy/namespaces/namespaces.go index 653ae4b534..a50e43593b 100644 --- a/service/policy/namespaces/namespaces.go +++ b/service/policy/namespaces/namespaces.go @@ -2,6 +2,7 @@ package namespaces import ( "context" + "errors" "fmt" "log/slog" @@ -221,26 +222,8 @@ func (ns NamespacesService) DeactivateNamespace(ctx context.Context, req *connec return connect.NewResponse(rsp), nil } -func (ns NamespacesService) AssignKeyAccessServerToNamespace(ctx context.Context, req *connect.Request[namespaces.AssignKeyAccessServerToNamespaceRequest]) (*connect.Response[namespaces.AssignKeyAccessServerToNamespaceResponse], error) { - rsp := &namespaces.AssignKeyAccessServerToNamespaceResponse{} - - grant := req.Msg.GetNamespaceKeyAccessServer() - auditParams := audit.PolicyEventParams{ - ActionType: audit.ActionTypeCreate, - ObjectType: audit.ObjectTypeKasAttributeNamespaceAssignment, - ObjectID: fmt.Sprintf("%s-%s", grant.GetNamespaceId(), grant.GetKeyAccessServerId()), - } - - namespaceKas, err := ns.dbClient.AssignKeyAccessServerToNamespace(ctx, grant) - if err != nil { - ns.logger.Audit.PolicyCRUDFailure(ctx, auditParams) - return nil, db.StatusifyError(ctx, ns.logger, err, db.ErrTextCreationFailed, slog.String("namespaceKas", grant.String())) - } - ns.logger.Audit.PolicyCRUDSuccess(ctx, auditParams) - - rsp.NamespaceKeyAccessServer = namespaceKas - - return connect.NewResponse(rsp), nil +func (ns NamespacesService) AssignKeyAccessServerToNamespace(_ context.Context, _ *connect.Request[namespaces.AssignKeyAccessServerToNamespaceRequest]) (*connect.Response[namespaces.AssignKeyAccessServerToNamespaceResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("this compatibility stub will be removed entirely in the following release")) } func (ns NamespacesService) RemoveKeyAccessServerFromNamespace(ctx context.Context, req *connect.Request[namespaces.RemoveKeyAccessServerFromNamespaceRequest]) (*connect.Response[namespaces.RemoveKeyAccessServerFromNamespaceResponse], error) { diff --git a/service/policy/namespaces/namespaces.proto b/service/policy/namespaces/namespaces.proto index 7bb541246d..1d643bafdd 100644 --- a/service/policy/namespaces/namespaces.proto +++ b/service/policy/namespaces/namespaces.proto @@ -3,28 +3,25 @@ syntax = "proto3"; package policy.namespaces; import "buf/validate/validate.proto"; - import "common/common.proto"; import "policy/objects.proto"; import "policy/selectors.proto"; /* - Key Access Server Grants + Key Access Server Grants */ +// Deprecated message NamespaceKeyAccessServer { + option deprecated = true; // Required - string namespace_id = 1 [ - (buf.validate.field).string.uuid = true - ]; + string namespace_id = 1 [(buf.validate.field).string.uuid = true]; // Required - string key_access_server_id = 2 [ - (buf.validate.field).string.uuid = true - ]; + string key_access_server_id = 2 [(buf.validate.field).string.uuid = true]; } /* - Key Maps + Key Maps */ message NamespaceKey { @@ -49,14 +46,14 @@ message NamespaceKey { message GetNamespaceRequest { // Temporary message level validation until we remove the deprecated id field option (buf.validate.message).cel = { - id: "exclusive_fields", - expression: "!(has(this.id) && (has(this.namespace_id) || has(this.fqn)))", + id: "exclusive_fields" + expression: "!(has(this.id) && (has(this.namespace_id) || has(this.fqn)))" message: "Either use deprecated 'id' field or one of 'namespace_id' or 'fqn', but not both" }; option (buf.validate.message).cel = { - id: "required_fields", - expression: "has(this.id) || has(this.namespace_id) || has(this.fqn)", + id: "required_fields" + expression: "has(this.id) || has(this.namespace_id) || has(this.fqn)" message: "Either id or one of namespace_id or fqn must be set" }; @@ -64,20 +61,16 @@ message GetNamespaceRequest { string id = 1 [ deprecated = true, (buf.validate.field).ignore = IGNORE_IF_DEFAULT_VALUE, - (buf.validate.field).string.uuid= true + (buf.validate.field).string.uuid = true ]; oneof identifier { //option (buf.validate.oneof).required = true; // TODO: enable this when we remove the deprecated field - string namespace_id = 2 [ - (buf.validate.field).string.uuid = true - ]; - string fqn = 3 [ - (buf.validate.field).string = { - min_len : 1 - uri : true - } - ]; + string namespace_id = 2 [(buf.validate.field).string.uuid = true]; + string fqn = 3 [(buf.validate.field).string = { + min_len: 1 + uri: true + }]; } } @@ -105,8 +98,8 @@ message CreateNamespaceRequest { (buf.validate.field).required = true, (buf.validate.field).string.max_len = 253, (buf.validate.field).cel = { - id: "namespace_format", - message: "Namespace must be a valid hostname. It should include at least one dot, with each segment (label) starting and ending with an alphanumeric character. Each label must be 1 to 63 characters long, allowing hyphens but not as the first or last character. The top-level domain (the last segment after the final dot) must consist of at least two alphabetic characters. The stored namespace will be normalized to lower case.", + id: "namespace_format" + message: "Namespace must be a valid hostname. It should include at least one dot, with each segment (label) starting and ending with an alphanumeric character. Each label must be 1 to 63 characters long, allowing hyphens but not as the first or last character. The top-level domain (the last segment after the final dot) must consist of at least two alphabetic characters. The stored namespace will be normalized to lower case." expression: "this.matches('^([a-zA-Z0-9]([a-zA-Z0-9\\\\-]{0,61}[a-zA-Z0-9])?\\\\.)+[a-zA-Z]{2,}$')" } ]; @@ -120,9 +113,7 @@ message CreateNamespaceResponse { message UpdateNamespaceRequest { // Required - string id = 1 [ - (buf.validate.field).string.uuid = true - ]; + string id = 1 [(buf.validate.field).string.uuid = true]; // Optional common.MetadataMutable metadata = 100; @@ -134,17 +125,14 @@ message UpdateNamespaceResponse { message DeactivateNamespaceRequest { // Required - string id = 1 [ - (buf.validate.field).string.uuid = true - ]; + string id = 1 [(buf.validate.field).string.uuid = true]; } message DeactivateNamespaceResponse {} /* - Assign Key Access Server to Namespace + Assign Key Access Server to Namespace */ - message AssignKeyAccessServerToNamespaceRequest { NamespaceKeyAccessServer namespace_key_access_server = 1; } @@ -162,12 +150,10 @@ message RemoveKeyAccessServerFromNamespaceResponse { } /* - Assign Key to Namespace + Assign Key to Namespace */ message AssignPublicKeyToNamespaceRequest { - NamespaceKey namespace_key = 1 [ - (buf.validate.field).required = true - ]; + NamespaceKey namespace_key = 1 [(buf.validate.field).required = true]; } message AssignPublicKeyToNamespaceResponse { @@ -175,9 +161,7 @@ message AssignPublicKeyToNamespaceResponse { } message RemovePublicKeyFromNamespaceRequest { - NamespaceKey namespace_key = 1 [ - (buf.validate.field).required = true - ]; + NamespaceKey namespace_key = 1 [(buf.validate.field).required = true]; } message RemovePublicKeyFromNamespaceResponse { @@ -197,12 +181,16 @@ service NamespaceService { rpc UpdateNamespace(UpdateNamespaceRequest) returns (UpdateNamespaceResponse) {} rpc DeactivateNamespace(DeactivateNamespaceRequest) returns (DeactivateNamespaceResponse) {} - /*--------------------------------------* + /*--------------------------------------* * Namespace <> Key Access Server RPCs *---------------------------------------*/ - rpc AssignKeyAccessServerToNamespace(AssignKeyAccessServerToNamespaceRequest) returns (AssignKeyAccessServerToNamespaceResponse) {} + rpc AssignKeyAccessServerToNamespace(AssignKeyAccessServerToNamespaceRequest) returns (AssignKeyAccessServerToNamespaceResponse) { + option deprecated = true; + } - rpc RemoveKeyAccessServerFromNamespace(RemoveKeyAccessServerFromNamespaceRequest) returns (RemoveKeyAccessServerFromNamespaceResponse) {} + rpc RemoveKeyAccessServerFromNamespace(RemoveKeyAccessServerFromNamespaceRequest) returns (RemoveKeyAccessServerFromNamespaceResponse) { + option deprecated = true; + } /*--------------------------------------* * Namespace <> Key RPCs @@ -210,4 +198,3 @@ service NamespaceService { rpc AssignPublicKeyToNamespace(AssignPublicKeyToNamespaceRequest) returns (AssignPublicKeyToNamespaceResponse) {} rpc RemovePublicKeyFromNamespace(RemovePublicKeyFromNamespaceRequest) returns (RemovePublicKeyFromNamespaceResponse) {} } - diff --git a/service/policy/namespaces/namespaces_test.go b/service/policy/namespaces/namespaces_test.go index 1e00187e92..8b7a1bf1fd 100644 --- a/service/policy/namespaces/namespaces_test.go +++ b/service/policy/namespaces/namespaces_test.go @@ -279,47 +279,6 @@ func Test_DeactivateNamespaceRequest_Succeeds(t *testing.T) { require.NoError(t, err) } -func Test_NamespaceKeyAccessServer_Succeeds(t *testing.T) { - validNamespaceKas := &namespaces.NamespaceKeyAccessServer{ - NamespaceId: validUUID, - KeyAccessServerId: validUUID, - } - - err := getValidator().Validate(validNamespaceKas) - require.NoError(t, err) -} - -func Test_NamespaceKeyAccessServer_Fails(t *testing.T) { - bad := []struct { - nsID string - kasID string - }{ - { - "", - validUUID, - }, - { - validUUID, - "", - }, - { - "", - "", - }, - {}, - } - - for _, test := range bad { - invalidNamespaceKAS := &namespaces.NamespaceKeyAccessServer{ - NamespaceId: test.nsID, - KeyAccessServerId: test.kasID, - } - err := getValidator().Validate(invalidNamespaceKAS) - require.Error(t, err) - require.Contains(t, err.Error(), errMessageUUID) - } -} - func Test_AssignPublicKeyToNamespace(t *testing.T) { testCases := []struct { name string diff --git a/service/policy/objects.proto b/service/policy/objects.proto index 6d046b8e51..cb7ba7a113 100644 --- a/service/policy/objects.proto +++ b/service/policy/objects.proto @@ -41,7 +41,7 @@ message Namespace { common.Metadata metadata = 5; - // KAS grants for the namespace + // Deprecated KAS grants for the namespace. Use kas_keys instead. repeated KeyAccessServer grants = 6; // Keys for the namespace @@ -65,7 +65,7 @@ message Attribute { repeated Value values = 5; - // Deprecated + // Deprecated KAS grants for the attribute. Use kas_keys instead. repeated KeyAccessServer grants = 6; string fqn = 7; @@ -99,8 +99,7 @@ message Value { reserved "members"; reserved 4; - // Deprecated - // list of key access servers + // Deprecated KAS grants for the value. Use kas_keys instead. repeated KeyAccessServer grants = 5; string fqn = 6; diff --git a/service/policy/selectors.proto b/service/policy/selectors.proto index 417f057527..5e717c6e24 100644 --- a/service/policy/selectors.proto +++ b/service/policy/selectors.proto @@ -3,45 +3,50 @@ syntax = "proto3"; package policy; message AttributeNamespaceSelector { - message AttributeSelector { - bool with_key_access_grants = 1; - - message ValueSelector { - bool with_key_access_grants = 1; - bool with_subject_maps = 2; - bool with_resource_maps = 3; - } - ValueSelector with_values = 10; - } - AttributeSelector with_attributes = 10; + message AttributeSelector { + // Deprecated + bool with_key_access_grants = 1; + message ValueSelector { + // Deprecated + bool with_key_access_grants = 1; + bool with_subject_maps = 2; + bool with_resource_maps = 3; + } + ValueSelector with_values = 10; + } + AttributeSelector with_attributes = 10; } message AttributeDefinitionSelector { - bool with_key_access_grants = 1; + // Deprecated + bool with_key_access_grants = 1; - message NamespaceSelector {} - NamespaceSelector with_namespace = 10; + message NamespaceSelector {} + NamespaceSelector with_namespace = 10; - message ValueSelector { - bool with_key_access_grants = 1; - bool with_subject_maps = 2; - bool with_resource_maps = 3; - } - ValueSelector with_values = 11; + message ValueSelector { + // Deprecated + bool with_key_access_grants = 1; + bool with_subject_maps = 2; + bool with_resource_maps = 3; + } + ValueSelector with_values = 11; } message AttributeValueSelector { - bool with_key_access_grants = 1; - bool with_subject_maps = 2; - bool with_resource_maps = 3; + // Deprecated + bool with_key_access_grants = 1; + bool with_subject_maps = 2; + bool with_resource_maps = 3; - message AttributeSelector { - bool with_key_access_grants = 1; + message AttributeSelector { + // Deprecated + bool with_key_access_grants = 1; - message NamespaceSelector {} - NamespaceSelector with_namespace = 10; - } - AttributeSelector with_attribute = 10; + message NamespaceSelector {} + NamespaceSelector with_namespace = 10; + } + AttributeSelector with_attribute = 10; } message PageRequest { @@ -62,4 +67,4 @@ message PageResponse { int32 next_offset = 2; // Total count of entire list int32 total = 3; -} \ No newline at end of file +} diff --git a/test/policy-service.bats b/test/policy-service.bats index 1b31ad5d17..374b054238 100755 --- a/test/policy-service.bats +++ b/test/policy-service.bats @@ -16,7 +16,7 @@ [[ $output = *"listing namespaces"* ]] [ $status = 0 ] - run go run ./examples --creds opentdf:secret attributes add -a https://example.io/attr/IntellectualProperty -v "TradeSecret,Proprietary,BusinessSensitive,Open" --rule hierarchy + run go run ./examples --creds opentdf:secret attributes add -a https://example.io/attr/IntellectualProperty -v "TradeSecret,Proprietary,BusinessSensitive,Open" --rule hierarchy echo "$output" [[ $output = *"created attribute"* ]] [ $status = 0 ] @@ -58,31 +58,3 @@ [[ $output = *"deleted kas registration"* ]] [ $status = 0 ] } - -@test "gRPC: kas grants assignment" { - go run ./examples --creds opentdf:secret kas add --kas https://a.example.io --algorithm "rsa:2048" --kid r1 --public-key "$(<${BATS_TEST_DIRNAME}/../kas-cert.pem)" - go run ./examples --creds opentdf:secret kas add --kas https://b.example.io --algorithm "rsa:2048" --kid r1 --public-key "$(<${BATS_TEST_DIRNAME}/../kas-cert.pem)" - go run ./examples --creds opentdf:secret kas add --kas https://c.example.io --algorithm "rsa:2048" --kid r1 --public-key "$(<${BATS_TEST_DIRNAME}/../kas-cert.pem)" - - run go run ./examples --creds opentdf:secret kas ls -l - echo "$output" - [[ $output = *"https://a.example.io"* ]] - [[ $output = *"https://b.example.io"* ]] - [[ $output = *"https://c.example.io"* ]] - [ $status = 0 ] - - go run ./examples --creds opentdf:secret attributes add -a https://grant.example.io/attr/test -v "a,b,c" - - go run ./examples --creds opentdf:secret attributes assign -a https://grant.example.io/attr/test -v a -k https://a.example.io - go run ./examples --creds opentdf:secret attributes assign -a https://grant.example.io/attr/test -v b -k https://b.example.io - go run ./examples --creds opentdf:secret attributes assign -a https://grant.example.io/attr/test -v c -k https://c.example.io - - go run ./examples --creds opentdf:secret kas rm -k https://a.example.io - go run ./examples --creds opentdf:secret kas rm -k https://b.example.io - go run ./examples --creds opentdf:secret kas rm -k https://c.example.io - - run go run ./examples --creds opentdf:secret attributes rm -f -a https://grant.example.io/attr/test - echo "$output" - [[ $output = *"deleted attribute"* ]] - [ $status = 0 ] -} diff --git a/test/tdf-roundtrips.bats b/test/tdf-roundtrips.bats index 0b9a08fe97..91405b0a6e 100755 --- a/test/tdf-roundtrips.bats +++ b/test/tdf-roundtrips.bats @@ -3,35 +3,6 @@ # Tests for creating and reading TDF files with various settings # Notably, tests both 'ztdf' and 'nano' formats. -@test "examples: roundtrip Z-TDF" { - # TODO: add subject mapping here to remove reliance on `provision fixtures` - echo "[INFO] configure attribute with grant for local kas" - go run ./examples --creds opentdf:secret kas add --kas https://localhost:8080 --algorithm "rsa:2048" --kid r1 --public-key "$(<${BATS_TEST_DIRNAME}/../kas-cert.pem)" - go run ./examples --creds opentdf:secret attributes unassign -a https://example.com/attr/attr1 -v value1 - go run ./examples --creds opentdf:secret attributes unassign -a https://example.com/attr/attr1 - go run ./examples --creds opentdf:secret attributes assign -a https://example.com/attr/attr1 -v value1 -k https://localhost:8080 - - echo "[INFO] create a tdf3 format file" - run go run ./examples encrypt "Hello Zero Trust" - echo "[INFO] echoing output; if successful, this is just the manifest" - echo "$output" - - echo "[INFO] Validate the manifest lists the expected kid in its KAO" - kid=$(jq -r '.encryptionInformation.keyAccess[0].kid' <<<"${output}") - echo "$kid" - [ "$kid" = r1 ] - - echo "[INFO] decrypting..." - run go run ./examples decrypt sensitive.txt.tdf - echo "$output" - printf '%s\n' "$output" | grep "Hello Zero Trust" - - echo "[INFO] decrypting with EC..." - run go run ./examples decrypt -A 'ec:secp256r1' sensitive.txt.tdf - echo "$output" - printf '%s\n' "$output" | grep "Hello Zero Trust" -} - @test "examples: roundtrip Z-TDF with EC wrapped KAO" { # TODO: add subject mapping here to remove reliance on `provision fixtures` echo "[INFO] create a tdf3 format file" @@ -55,35 +26,6 @@ printf '%s\n' "$output" | grep "Hello EC wrappers!" } -@test "examples: roundtrip Z-TDF with extra unnecessary, invalid kas" { - # TODO: add subject mapping here to remove reliance on `provision fixtures` - echo "[INFO] configure attribute with grant for local kas" - go run ./examples --creds opentdf:secret kas add --kas https://localhost:8080 --algorithm "rsa:2048" --kid r1 --public-key "$(<${BATS_TEST_DIRNAME}/../kas-cert.pem)" - go run ./examples --creds opentdf:secret kas add --kas http://localhost:9090 --algorithm "rsa:2048" --kid r2 --public-key "$(<${BATS_TEST_DIRNAME}/../kas-cert.pem)" - go run ./examples --creds opentdf:secret attributes unassign -a https://example.com/attr/attr1 -v value1 - go run ./examples --creds opentdf:secret attributes unassign -a https://example.com/attr/attr1 - go run ./examples --creds opentdf:secret attributes assign -a https://example.com/attr/attr1 -v value1 -k "https://localhost:8080,http://localhost:9090" - - echo "[INFO] create a tdf3 format file" - run go run ./examples encrypt "Hello multikao split" - echo "[INFO] echoing output; if successful, this is just the manifest" - echo "$output" - - echo "[INFO] Validate the manifest lists the expected kid in its KAO" - u1=$(jq -r '.encryptionInformation.keyAccess[0].url' <<<"${output}") - u2=$(jq -r '.encryptionInformation.keyAccess[1].url' <<<"${output}") - sid1=$(jq -r '.encryptionInformation.keyAccess[0].sid' <<<"${output}") - sid2=$(jq -r '.encryptionInformation.keyAccess[1].sid' <<<"${output}") - echo "${u1},${sid1} ?= ${u2},${sid2}" - [ "$u1" != "$u2" ] - [ "$sid1" = "$sid2" ] - - echo "[INFO] decrypting..." - run go run ./examples decrypt sensitive.txt.tdf - echo "$output" - printf '%s\n' "$output" | grep "Hello multikao split" -} - @test "examples: roundtrip nanoTDF (encrypted policy)" { echo "[INFO] creating nanotdf file" go run ./examples encrypt -o sensitive.txt.ntdf --nano --no-kid-in-nano "Hello NanoTDF" From e77008b4ddb74a99be2f09405da9c56b18cd6fb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 15:19:55 +0000 Subject: [PATCH 46/55] chore(ci): bump github/codeql-action from 3.28.18 to 3.28.19 (#2350) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.18 to 3.28.19.
Release notes

Sourced from github/codeql-action's releases.

v3.28.19

CodeQL Action Changelog

See the releases page for the relevant changes to the CodeQL CLI and language packs.

3.28.19 - 03 Jun 2025

  • The CodeQL Action no longer includes its own copy of the extractor for the actions language, which is currently in public preview. The actions extractor has been included in the CodeQL CLI since v2.20.6. If your workflow has enabled the actions language and you have pinned your tools: property to a specific version of the CodeQL CLI earlier than v2.20.6, you will need to update to at least CodeQL v2.20.6 or disable actions analysis.
  • Update default CodeQL bundle version to 2.21.4. #2910

See the full CHANGELOG.md for more information.

Changelog

Sourced from github/codeql-action's changelog.

CodeQL Action Changelog

See the releases page for the relevant changes to the CodeQL CLI and language packs.

[UNRELEASED]

No user facing changes.

3.28.19 - 03 Jun 2025

  • The CodeQL Action no longer includes its own copy of the extractor for the actions language, which is currently in public preview. The actions extractor has been included in the CodeQL CLI since v2.20.6. If your workflow has enabled the actions language and you have pinned your tools: property to a specific version of the CodeQL CLI earlier than v2.20.6, you will need to update to at least CodeQL v2.20.6 or disable actions analysis.
  • Update default CodeQL bundle version to 2.21.4. #2910

3.28.18 - 16 May 2025

  • Update default CodeQL bundle version to 2.21.3. #2893
  • Skip validating SARIF produced by CodeQL for improved performance. #2894
  • The number of threads and amount of RAM used by CodeQL can now be set via the CODEQL_THREADS and CODEQL_RAM runner environment variables. If set, these environment variables override the threads and ram inputs respectively. #2891

3.28.17 - 02 May 2025

  • Update default CodeQL bundle version to 2.21.2. #2872

3.28.16 - 23 Apr 2025

  • Update default CodeQL bundle version to 2.21.1. #2863

3.28.15 - 07 Apr 2025

  • Fix bug where the action would fail if it tried to produce a debug artifact with more than 65535 files. #2842

3.28.14 - 07 Apr 2025

  • Update default CodeQL bundle version to 2.21.0. #2838

3.28.13 - 24 Mar 2025

No user facing changes.

3.28.12 - 19 Mar 2025

  • Dependency caching should now cache more dependencies for Java build-mode: none extractions. This should speed up workflows and avoid inconsistent alerts in some cases.
  • Update default CodeQL bundle version to 2.20.7. #2810

3.28.11 - 07 Mar 2025

  • Update default CodeQL bundle version to 2.20.6. #2793

... (truncated)

Commits
  • fca7ace Merge pull request #2918 from github/update-v3.28.19-4a00331d4
  • 1dcd2be Update changelog for v3.28.19
  • 4a00331 Merge pull request #2910 from github/update-bundle/codeql-bundle-v2.21.4
  • c0a821d Add changelog note
  • d621686 Update default bundle to codeql-bundle-v2.21.4
  • dc138d4 Merge pull request #2913 from github/henrymercer/win-2019-deprecated
  • 3201e46 Stop running CI on windows-2019
  • 7fd6215 Merge pull request #2911 from github/update-supported-enterprise-server-versions
  • 31eae5e Update supported GitHub Enterprise Server versions
  • bc02a25 Merge pull request #2908 from github/henrymercer/dependabot
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=3.28.18&new-version=3.28.19)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/action-lint.yaml | 2 +- .github/workflows/codeql.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/action-lint.yaml b/.github/workflows/action-lint.yaml index d7183d5583..b260415fd4 100644 --- a/.github/workflows/action-lint.yaml +++ b/.github/workflows/action-lint.yaml @@ -49,7 +49,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # 3.28.18 + uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # 3.28.19 with: sarif_file: results.sarif category: zizmor diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index 3375a84a6a..347c4cdd1e 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -36,17 +36,17 @@ jobs: if: ${{ matrix.language == 'go' }} - name: Initialize the CodeQL tools for scanning - uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 with: languages: ${{ matrix.language }} timeout-minutes: 5 - name: Autobuild - uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + uses: github/codeql-action/autobuild@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 timeout-minutes: 10 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 with: category: "/language:${{matrix.language}}" timeout-minutes: 10 From d49a566b00e4d39d567abd95ca237986c8cf9fb7 Mon Sep 17 00:00:00 2001 From: Chris Reed <87077975+c-r33d@users.noreply.github.com> Date: Mon, 23 Jun 2025 10:31:03 -0500 Subject: [PATCH 47/55] feat(sdk): Allow key splits with same algo (#2454) ### Proposed Changes 1.) Fix key cache to store KID with algo and kasURI. Fixes the issue where we would create a conjunction of the same key given the same algo and uri. 2.) Deprecate notice for **WithDataAttributeValues** 3.) Update some small **BaseKey** error handling 4.) Update **Get** method on the kas_client key cache to look for a key matching the algorithm and kas url if the kid is not defined. ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions --- sdk/basekey.go | 14 ++-- sdk/basekey_test.go | 10 +-- sdk/basekeyerrors.go | 10 +-- sdk/granter.go | 28 +++++--- sdk/granter_test.go | 23 ++++--- sdk/kas_client.go | 21 ++++-- sdk/kas_client_test.go | 145 ++++++++++++++++++++++++++++++++++++++--- sdk/nanotdf.go | 2 +- sdk/tdf.go | 4 +- sdk/tdf_config.go | 1 + sdk/tdf_test.go | 105 +++++++++++++++++++---------- 11 files changed, 278 insertions(+), 85 deletions(-) diff --git a/sdk/basekey.go b/sdk/basekey.go index a0e54229ee..38df2dc076 100644 --- a/sdk/basekey.go +++ b/sdk/basekey.go @@ -77,12 +77,12 @@ func getBaseKey(ctx context.Context, s SDK) (*policy.SimpleKasKey, error) { baseKeyStructure, ok := configMap[baseKeyWellKnown] if !ok { - return nil, errBaseKeyNotFound + return nil, ErrBaseKeyNotFound } baseKeyMap, ok := baseKeyStructure.(map[string]interface{}) if !ok { - return nil, errBaseKeyInvalidFormat + return nil, ErrBaseKeyInvalidFormat } simpleKasKey, err := parseSimpleKasKey(baseKeyMap) @@ -97,28 +97,28 @@ func parseSimpleKasKey(baseKeyMap map[string]interface{}) (*policy.SimpleKasKey, simpleKasKey := &policy.SimpleKasKey{} if len(baseKeyMap) == 0 { - return nil, errBaseKeyEmpty + return nil, ErrBaseKeyEmpty } publicKey, ok := baseKeyMap[baseKeyPublicKey].(map[string]interface{}) if !ok { - return nil, errBaseKeyInvalidFormat + return nil, ErrBaseKeyInvalidFormat } alg, ok := publicKey[baseKeyAlg].(string) if !ok { - return nil, errBaseKeyInvalidFormat + return nil, ErrBaseKeyInvalidFormat } publicKey[baseKeyAlg] = getKasKeyAlg(alg) baseKeyMap[baseKeyPublicKey] = publicKey configJSON, err := json.Marshal(baseKeyMap) if err != nil { - return nil, errors.Join(errMarshalBaseKeyFailed, err) + return nil, errors.Join(ErrMarshalBaseKeyFailed, err) } err = protojson.Unmarshal(configJSON, simpleKasKey) if err != nil { - return nil, errors.Join(errUnmarshalBaseKeyFailed, err) + return nil, errors.Join(ErrUnmarshalBaseKeyFailed, err) } return simpleKasKey, nil } diff --git a/sdk/basekey_test.go b/sdk/basekey_test.go index 9e97fc51d7..faa0daf23e 100644 --- a/sdk/basekey_test.go +++ b/sdk/basekey_test.go @@ -249,7 +249,7 @@ func (s *BaseKeyTestSuite) TestGetBaseKeyMissingBaseKey() { s.Require().True(mockService.called) s.Require().Error(err) s.Require().Nil(baseKey) - s.Require().Contains(err.Error(), errBaseKeyNotFound.Error()) + s.Require().ErrorIs(err, ErrBaseKeyNotFound) } func (s *BaseKeyTestSuite) TestGetBaseKeyInvalidBaseKeyFormat() { @@ -267,7 +267,7 @@ func (s *BaseKeyTestSuite) TestGetBaseKeyInvalidBaseKeyFormat() { s.Require().True(mockService.called) s.Require().Error(err) s.Require().Nil(baseKey) - s.Require().ErrorContains(err, errBaseKeyInvalidFormat.Error()) + s.Require().ErrorIs(err, ErrBaseKeyInvalidFormat) } func (s *BaseKeyTestSuite) TestGetBaseKeyEmptyBaseKey() { @@ -286,7 +286,7 @@ func (s *BaseKeyTestSuite) TestGetBaseKeyEmptyBaseKey() { s.Require().True(mockService.called) s.Require().Error(err) s.Require().Nil(baseKey) - s.Require().ErrorContains(err, errBaseKeyEmpty.Error()) + s.Require().ErrorIs(err, ErrBaseKeyEmpty) } func (s *BaseKeyTestSuite) TestGetBaseKeyMissingPublicKey() { @@ -308,7 +308,7 @@ func (s *BaseKeyTestSuite) TestGetBaseKeyMissingPublicKey() { s.Require().True(mockService.called) s.Require().Error(err) s.Require().Nil(baseKey) - s.Require().ErrorContains(err, errBaseKeyInvalidFormat.Error()) + s.Require().ErrorIs(err, ErrBaseKeyInvalidFormat) } func (s *BaseKeyTestSuite) TestGetBaseKeyInvalidPublicKey() { @@ -330,5 +330,5 @@ func (s *BaseKeyTestSuite) TestGetBaseKeyInvalidPublicKey() { s.Require().True(mockService.called) s.Require().Error(err) s.Require().Nil(baseKey) - s.Require().ErrorContains(err, errBaseKeyInvalidFormat.Error()) + s.Require().ErrorIs(err, ErrBaseKeyInvalidFormat) } diff --git a/sdk/basekeyerrors.go b/sdk/basekeyerrors.go index dc41194c87..7a27733437 100644 --- a/sdk/basekeyerrors.go +++ b/sdk/basekeyerrors.go @@ -3,9 +3,9 @@ package sdk import "errors" var ( - errBaseKeyNotFound = errors.New("base key not found in well-known configuration") - errBaseKeyInvalidFormat = errors.New("base key has invalid format") - errBaseKeyEmpty = errors.New("base key is empty or not provided") - errMarshalBaseKeyFailed = errors.New("failed to marshal base key configuration") - errUnmarshalBaseKeyFailed = errors.New("failed to unmarshal base key configuration") + ErrBaseKeyNotFound = errors.New("base key not found in well-known configuration") + ErrBaseKeyInvalidFormat = errors.New("base key has invalid format") + ErrBaseKeyEmpty = errors.New("base key is empty or not provided") + ErrMarshalBaseKeyFailed = errors.New("failed to marshal base key configuration") + ErrUnmarshalBaseKeyFailed = errors.New("failed to unmarshal base key configuration") ) diff --git a/sdk/granter.go b/sdk/granter.go index 1773345794..18075c5145 100644 --- a/sdk/granter.go +++ b/sdk/granter.go @@ -388,23 +388,23 @@ func newGranterFromService(ctx context.Context, keyCache *kasKeyCache, as sdkcon def := pair.GetAttribute() if def != nil { - storeKeysToCache(def.GetGrants(), keyCache) + storeKeysToCache(def.GetGrants(), def.GetKasKeys(), keyCache) } v := pair.GetValue() gType := noKeysFound if v != nil { gType = grants.addAllGrants(fqn, v, def) - storeKeysToCache(v.GetGrants(), keyCache) + storeKeysToCache(v.GetGrants(), v.GetKasKeys(), keyCache) } // If no more specific grant was found, then add the value grants if gType == noKeysFound && def != nil { gType = grants.addAllGrants(fqn, def, def) - storeKeysToCache(def.GetGrants(), keyCache) + storeKeysToCache(def.GetGrants(), def.GetKasKeys(), keyCache) } if gType == noKeysFound && def.GetNamespace() != nil { grants.addAllGrants(fqn, def.GetNamespace(), def) - storeKeysToCache(def.GetNamespace().GetGrants(), keyCache) + storeKeysToCache(def.GetNamespace().GetGrants(), def.GetNamespace().GetKasKeys(), keyCache) } } @@ -429,7 +429,7 @@ func algProto2String(e policy.KasPublicKeyAlgEnum) string { return "" } -func storeKeysToCache(kases []*policy.KeyAccessServer, c *kasKeyCache) { +func storeKeysToCache(kases []*policy.KeyAccessServer, keys []*policy.SimpleKasKey, c *kasKeyCache) { for _, kas := range kases { keys := kas.GetPublicKey().GetCached().GetKeys() if len(keys) == 0 { @@ -445,6 +445,18 @@ func storeKeysToCache(kases []*policy.KeyAccessServer, c *kasKeyCache) { }) } } + for _, key := range keys { + alg, err := formatAlg(key.GetPublicKey().GetAlgorithm()) + if err != nil { + continue + } + c.store(KASInfo{ + URL: key.GetKasUri(), + KID: key.GetPublicKey().GetKid(), + Algorithm: alg, + PublicKey: key.GetPublicKey().GetPem(), + }) + } } // Given a policy (list of data attributes or tags), @@ -472,16 +484,16 @@ func newGranterFromAttributes(keyCache *kasKeyCache, attrs ...*policy.Value) (gr } if grants.addAllGrants(fqn, v, def) != noKeysFound { - storeKeysToCache(v.GetGrants(), keyCache) + storeKeysToCache(v.GetGrants(), v.GetKasKeys(), keyCache) continue } // If no more specific grant was found, then add the attr grants if grants.addAllGrants(fqn, def, def) != noKeysFound { - storeKeysToCache(def.GetGrants(), keyCache) + storeKeysToCache(def.GetGrants(), def.GetKasKeys(), keyCache) continue } grants.addAllGrants(fqn, namespace, def) - storeKeysToCache(namespace.GetGrants(), keyCache) + storeKeysToCache(namespace.GetGrants(), namespace.GetKasKeys(), keyCache) } return grants, nil diff --git a/sdk/granter_test.go b/sdk/granter_test.go index 4af6f43e5e..87cedfac76 100644 --- a/sdk/granter_test.go +++ b/sdk/granter_test.go @@ -30,7 +30,7 @@ const ( specifiedKas = "https://attr.kas.com/" evenMoreSpecificKas = "https://value.kas.com/" lessSpecificKas = "https://namespace.kas.com/" - fakePem = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQ...\n-----END PUBLIC KEY-----\n" + fakePem = mockRSAPublicKey1 ) var ( @@ -74,6 +74,8 @@ var ( MP, _ = NewAttributeNameFQN("https://virtru.com/attr/mapped") mpa, _ = NewAttributeValueFQN("https://virtru.com/attr/mapped/value/a") mpb, _ = NewAttributeValueFQN("https://virtru.com/attr/mapped/value/b") + mpc, _ = NewAttributeValueFQN("https://virtru.com/attr/mapped/value/c") + mpd, _ = NewAttributeValueFQN("https://virtru.com/attr/mapped/value/d") mpu, _ = NewAttributeValueFQN("https://virtru.com/attr/mapped/value/unspecified") ) @@ -248,7 +250,7 @@ func mockSimpleKasKey(kas, kid string) *policy.SimpleKasKey { } var alg policy.Algorithm switch kid { - case "r1": + case "r0", "r1", "r3": alg = policy.Algorithm_ALGORITHM_RSA_2048 case "r2": alg = policy.Algorithm_ALGORITHM_RSA_4096 @@ -262,7 +264,7 @@ func mockSimpleKasKey(kas, kid string) *policy.SimpleKasKey { PublicKey: &policy.SimpleKasPublicKey{ Algorithm: alg, Kid: kid, - Pem: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQ...\n-----END PUBLIC KEY-----\n", + Pem: fakePem, }, } } @@ -339,17 +341,20 @@ func mockValueFor(fqn AttributeValueFQN) *policy.Value { switch strings.ToLower(fqn.Value()) { case "a": p.KasKeys = make([]*policy.SimpleKasKey, 1) - p.Grants = make([]*policy.KeyAccessServer, 1) p.KasKeys[0] = mockSimpleKasKey(evenMoreSpecificKas, "r2") - p.Grants[0] = mockGrant(evenMoreSpecificKas, "r2") - p.Grants[0].PublicKey = createPublicKey("r2", fakePem, policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048) case "b": p.KasKeys = make([]*policy.SimpleKasKey, 1) - p.Grants = make([]*policy.KeyAccessServer, 1) p.KasKeys[0] = mockSimpleKasKey(evenMoreSpecificKas, "e1") - p.Grants[0] = mockGrant(evenMoreSpecificKas, "e1") - p.Grants[0].PublicKey = createPublicKey("e1", fakePem, policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048) + + case "c": + p.KasKeys = make([]*policy.SimpleKasKey, 1) + p.KasKeys[0] = mockSimpleKasKey(evenMoreSpecificKas, "r0") + + case "d": + p.KasKeys = make([]*policy.SimpleKasKey, 1) + p.KasKeys[0] = mockSimpleKasKey(evenMoreSpecificKas, "r3") + case "unspecified": // defaults only default: diff --git a/sdk/kas_client.go b/sdk/kas_client.go index 93bfdf1a27..51ebf321d1 100644 --- a/sdk/kas_client.go +++ b/sdk/kas_client.go @@ -378,7 +378,7 @@ func (k *KASClient) getRewrapRequest(reqs []*kas.UnsignedRewrapRequest_WithPolic } type kasKeyRequest struct { - url, algorithm string + url, algorithm, kid string } type timeStampedKASInfo struct { @@ -399,10 +399,19 @@ func (c *kasKeyCache) clear() { c.c = make(map[kasKeyRequest]timeStampedKASInfo) } -func (c *kasKeyCache) get(url, algorithm string) *KASInfo { - cacheKey := kasKeyRequest{url, algorithm} +func (c *kasKeyCache) get(url, algorithm, kid string) *KASInfo { + cacheKey := kasKeyRequest{url, algorithm, kid} now := time.Now() cv, ok := c.c[cacheKey] + if !ok && kid == "" { + for k, v := range c.c { + if k.url == url && k.algorithm == algorithm { + cv = v + ok = true + break + } + } + } if !ok { return nil } @@ -415,13 +424,13 @@ func (c *kasKeyCache) get(url, algorithm string) *KASInfo { } func (c *kasKeyCache) store(ki KASInfo) { - cacheKey := kasKeyRequest{ki.URL, ki.Algorithm} + cacheKey := kasKeyRequest{ki.URL, ki.Algorithm, ki.KID} c.c[cacheKey] = timeStampedKASInfo{ki, time.Now()} } -func (s SDK) getPublicKey(ctx context.Context, kasurl, algorithm string) (*KASInfo, error) { +func (s SDK) getPublicKey(ctx context.Context, kasurl, algorithm, kidToFind string) (*KASInfo, error) { if s.kasKeyCache != nil { - if cachedValue := s.kasKeyCache.get(kasurl, algorithm); nil != cachedValue { + if cachedValue := s.kasKeyCache.get(kasurl, algorithm, kidToFind); nil != cachedValue { return cachedValue, nil } } diff --git a/sdk/kas_client_test.go b/sdk/kas_client_test.go index f4f4f46da7..001c99dc2e 100644 --- a/sdk/kas_client_test.go +++ b/sdk/kas_client_test.go @@ -4,6 +4,7 @@ import ( "context" "net/http" "testing" + "time" "connectrpc.com/connect" "github.com/lestrrat-go/jwx/v2/jwa" @@ -136,8 +137,8 @@ func Test_StoreKASKeys(t *testing.T) { ) require.NoError(t, err) - assert.Nil(t, s.kasKeyCache.get("https://localhost:8080", "ec:secp256r1")) - assert.Nil(t, s.kasKeyCache.get("https://localhost:8080", "rsa:2048")) + assert.Nil(t, s.kasKeyCache.get("https://localhost:8080", "ec:secp256r1", "e1")) + assert.Nil(t, s.kasKeyCache.get("https://localhost:8080", "rsa:2048", "r1")) require.NoError(t, s.StoreKASKeys("https://localhost:8080", &policy.KasPublicKeySet{ Keys: []*policy.KasPublicKey{ @@ -145,12 +146,16 @@ func Test_StoreKASKeys(t *testing.T) { {Pem: "sample", Kid: "r1", Alg: policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048}, }, })) - assert.Nil(t, s.kasKeyCache.get("https://nowhere", "alg:unknown")) - assert.Nil(t, s.kasKeyCache.get("https://localhost:8080", "alg:unknown")) - assert.Equal(t, "e1", s.kasKeyCache.get("https://localhost:8080", "ec:secp256r1").KID) - assert.Equal(t, "r1", s.kasKeyCache.get("https://localhost:8080", "rsa:2048").KID) - - k1, err := s.getPublicKey(t.Context(), "https://localhost:8080", "ec:secp256r1") + assert.Nil(t, s.kasKeyCache.get("https://nowhere", "alg:unknown", "")) + assert.Nil(t, s.kasKeyCache.get("https://localhost:8080", "alg:unknown", "")) + ecKey := s.kasKeyCache.get("https://localhost:8080", "ec:secp256r1", "e1") + rsaKey := s.kasKeyCache.get("https://localhost:8080", "rsa:2048", "r1") + require.NotNil(t, ecKey) + require.Equal(t, "e1", ecKey.KID) + require.NotNil(t, rsaKey) + require.Equal(t, "r1", rsaKey.KID) + + k1, err := s.getPublicKey(t.Context(), "https://localhost:8080", "ec:secp256r1", "e1") require.NoError(t, err) assert.Equal(t, &KASInfo{ URL: "https://localhost:8080", @@ -161,7 +166,7 @@ func Test_StoreKASKeys(t *testing.T) { }, k1) s.kasKeyCache = nil - k2, err := s.getPublicKey(t.Context(), "https://localhost:54321", "ec:secp256r1") + k2, err := s.getPublicKey(t.Context(), "https://localhost:54321", "ec:secp256r1", "") assert.Nil(t, k2) require.ErrorContains(t, err, "error making request") } @@ -259,3 +264,125 @@ func TestParseBaseUrl(t *testing.T) { }) } } + +func TestKasKeyCache_NoKID(t *testing.T) { + // Create a new KAS key cache + cache := newKasKeyCache() + require.NotNil(t, cache, "Failed to create KAS key cache") + + // Define test data + const ( + testURL = "https://kas.example.org" + testAlgorithm = "ec:secp256r1" + testPubKey = "test-public-key" + ) + + // Test 1: Store a key with a specific KID + keyInfoWithKID := KASInfo{ + URL: testURL, + Algorithm: testAlgorithm, + KID: "specific-kid", + PublicKey: testPubKey, + } + cache.store(keyInfoWithKID) + + // Check if we can retrieve the key when querying with the specific KID + retrievedKey := cache.get(testURL, testAlgorithm, "specific-kid") + require.NotNil(t, retrievedKey, "Failed to retrieve key with specific KID") + assert.Equal(t, testPubKey, retrievedKey.PublicKey, "Retrieved key has incorrect public key") + assert.Equal(t, "specific-kid", retrievedKey.KID, "Retrieved key has incorrect KID") + + // Check that with empty KID we can find a match through iteration + retrievedWithEmptyKID := cache.get(testURL, testAlgorithm, "") + require.NotNil(t, retrievedWithEmptyKID, "Failed to retrieve key with empty KID (should find through iteration)") + assert.Equal(t, testPubKey, retrievedWithEmptyKID.PublicKey, "Retrieved key has incorrect public key") + assert.Equal(t, keyInfoWithKID.KID, retrievedWithEmptyKID.KID, "Retrieved key should have the original KID") + + // Test 2: Store a key with a different KID but same URL and algorithm + keyInfoWithDifferentKID := KASInfo{ + URL: testURL, + Algorithm: testAlgorithm, + KID: "different-kid", + PublicKey: "another-public-key", + } + // First try to get the key with same URL and algo as pre-existing key to ensure it doesn't iterate over the map. + specificKIDKey := cache.get(testURL, testAlgorithm, keyInfoWithDifferentKID.KID) + require.Nil(t, specificKIDKey, "Should not retrieve key with different KID using specific KID lookup") + + cache.store(keyInfoWithDifferentKID) + + // Both keys should be retrievable with their specific KIDs + specificKIDKey = cache.get(testURL, testAlgorithm, "specific-kid") + differentKIDKey := cache.get(testURL, testAlgorithm, "different-kid") + + require.NotNil(t, specificKIDKey, "Failed to retrieve original key with specific KID") + require.NotNil(t, differentKIDKey, "Failed to retrieve key with different KID") + + assert.Equal(t, testPubKey, specificKIDKey.PublicKey, "Retrieved key with specific KID has incorrect public key") + assert.Equal(t, keyInfoWithDifferentKID.PublicKey, differentKIDKey.PublicKey, "Retrieved key with different KID has incorrect public key") + + // Empty KID lookup should find a key through iteration + // Note: The implementation may return any key that matches URL and algorithm + emptyKIDLookup := cache.get(testURL, testAlgorithm, "") + require.NotNil(t, emptyKIDLookup, "Failed to retrieve key with empty KID") + // We don't assert which key is returned as that depends on map iteration order + + // Test 3: Store a key with empty KID + keyInfoWithEmptyKID := KASInfo{ + URL: testURL, + Algorithm: testAlgorithm, + KID: "", // Empty KID + PublicKey: "empty-kid-public-key", + } + cache.store(keyInfoWithEmptyKID) + + // Empty KID lookup should return this key + emptyKIDKey := cache.get(testURL, testAlgorithm, "") + require.NotNil(t, emptyKIDKey, "Failed to retrieve key with empty KID") + assert.Equal(t, "empty-kid-public-key", emptyKIDKey.PublicKey, "Retrieved key has incorrect public key") + assert.Empty(t, emptyKIDKey.KID, "Retrieved key should have empty KID") +} + +func TestKasKeyCache_Expiration(t *testing.T) { + // Create a new KAS key cache + cache := newKasKeyCache() + require.NotNil(t, cache, "Failed to create KAS key cache") + + // Store a key with a specific KID + keyInfo := KASInfo{ + URL: "https://kas.example.org", + Algorithm: "ec:secp256r1", + KID: "test-kid", + PublicKey: "test-public-key", + } + cache.store(keyInfo) + + // Verify the entry is in cache + retrievedKeyWithKID := cache.get(keyInfo.URL, keyInfo.Algorithm, keyInfo.KID) + require.NotNil(t, retrievedKeyWithKID, "Key with specific KID should be in cache") + + // Verify we can retrieve the key with empty KID through iteration + retrievedKeyNoKID := cache.get(keyInfo.URL, keyInfo.Algorithm, "") + require.NotNil(t, retrievedKeyNoKID, "Key with empty KID lookup should be found through iteration") + assert.Equal(t, keyInfo.KID, retrievedKeyNoKID.KID, "Empty KID lookup should return the key with the specific KID") + + // Manually modify the time to simulate expiration (beyond 5 minutes) + cacheKey := kasKeyRequest{keyInfo.URL, keyInfo.Algorithm, keyInfo.KID} + + // Update entry to be expired + cachedValue := cache.c[cacheKey] + cachedValue.Time = time.Now().Add(-6 * time.Minute) + cache.c[cacheKey] = cachedValue + + // Try to retrieve the expired key with specific KID + retrievedKeyWithKID = cache.get(keyInfo.URL, keyInfo.Algorithm, keyInfo.KID) + assert.Nil(t, retrievedKeyWithKID, "Expired key with specific KID should not be returned") + + // Try to retrieve with empty KID (should also find nothing since the key is expired) + retrievedKeyNoKID = cache.get(keyInfo.URL, keyInfo.Algorithm, "") + assert.Nil(t, retrievedKeyNoKID, "Expired key should not be found with empty KID lookup") + + // Verify the entry was actually removed from the cache + _, exists := cache.c[cacheKey] + assert.False(t, exists, "Expired key should be removed from cache") +} diff --git a/sdk/nanotdf.go b/sdk/nanotdf.go index ef73743fcc..9889e73c14 100644 --- a/sdk/nanotdf.go +++ b/sdk/nanotdf.go @@ -1113,7 +1113,7 @@ func getKasInfoForNanoTDF(s *SDK, config *NanoTDFConfig) (*KASInfo, error) { if kasURL == "https://" || kasURL == "http://" { return nil, errors.New("config.kasUrl is empty") } - ki, err = s.getPublicKey(context.Background(), kasURL, config.bindCfg.eccMode.String()) + ki, err = s.getPublicKey(context.Background(), kasURL, config.bindCfg.eccMode.String(), "") if err != nil { return nil, fmt.Errorf("getECPublicKey failed:%w", err) } diff --git a/sdk/tdf.go b/sdk/tdf.go index f6ed82d7ab..35d9c9286a 100644 --- a/sdk/tdf.go +++ b/sdk/tdf.go @@ -492,8 +492,8 @@ func (s SDK) prepareManifest(ctx context.Context, t *TDFObject, tdfConfig TDFCon // Public key was passed in with kasInfoList // TODO first look up in attribute information / add to split plan? ki, ok := latestKASInfo[tpl.KAS] - if !ok || ki.PublicKey == "" { - k, err := s.getPublicKey(ctx, tpl.KAS, keyAlgorithm) + if !ok || ki.PublicKey == "" || ki.KID != tpl.kid { + k, err := s.getPublicKey(ctx, tpl.KAS, keyAlgorithm, tpl.kid) if err != nil { return fmt.Errorf("unable to retrieve public key from KAS at [%s]: %w", tpl.KAS, err) } diff --git a/sdk/tdf_config.go b/sdk/tdf_config.go index d8d1c86d7a..e2031c60a1 100644 --- a/sdk/tdf_config.go +++ b/sdk/tdf_config.go @@ -149,6 +149,7 @@ func WithDataAttributes(attributes ...string) TDFOption { // during autoconfigure. That is, to use autoconfigure in an 'offline' context, // you must first store the relevant attribute information locally and load // it to the `CreateTDF` method with this option. +// DEPRECATION: This option is deprecated and will be removed in a future release. func WithDataAttributeValues(attributes ...*policy.Value) TDFOption { return func(c *TDFConfig) error { c.attributes = make([]AttributeValueFQN, len(attributes)) diff --git a/sdk/tdf_test.go b/sdk/tdf_test.go index 997d44476d..83a9ab27ac 100644 --- a/sdk/tdf_test.go +++ b/sdk/tdf_test.go @@ -57,9 +57,13 @@ const ( hundredMB = 100 * oneMB oneGB = 10 * hundredMB // tenGB = 10 * oneGB - baseKeyKID = "base-key-kid" - baseKeyURL = "http://base-key.com/" - defaultKID = "r1" + baseKeyKID = "base-key-kid" + baseKeyURL = "http://base-key.com/" + multipleKeysKID1 = "multiple-keys-kid1" + multipleKeysKID2 = "multiple-keys-kid2" + multipleKeysKasURL = "http://multiple-keys-kas.com/" + defaultKID = "r1" + r3KID = "r3" ) const ( @@ -1684,35 +1688,6 @@ func (s *TDFSuite) Test_MixedBaseKeyTest() { baseKey := createTestBaseKeyMap(&s.Suite, policy.Algorithm_ALGORITHM_RSA_2048, baseKeyKID, mockRSAPublicKey1, s.kasTestURLLookup[baseKeyURL]) s.fakeWellKnown = createWellKnown(baseKey) attrVal := mockValueFor(rel2aus) - cachedPublicKeySet := &policy.KasPublicKeySet{ - Keys: []*policy.KasPublicKey{ - { - Kid: defaultKID, - Pem: mockRSAPublicKey1, - Alg: policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048, - }, - }, - } - attrVal.Grants = []*policy.KeyAccessServer{ - { - Uri: s.kasTestURLLookup[kasAu], - PublicKey: &policy.PublicKey{ - PublicKey: &policy.PublicKey_Cached{ - Cached: cachedPublicKeySet, - }, - }, - }, - } - attrVal.KasKeys = []*policy.SimpleKasKey{ - { - KasUri: s.kasTestURLLookup[kasAu], - PublicKey: &policy.SimpleKasPublicKey{ - Algorithm: policy.Algorithm_ALGORITHM_RSA_2048, - Kid: defaultKID, - Pem: mockRSAPublicKey1, - }, - }, - } for index, test := range []baseKeyTest{ { tdfTest: tdfTest{ @@ -1732,7 +1707,7 @@ func (s *TDFSuite) Test_MixedBaseKeyTest() { tdfFileSize: 104866427, checksum: "cee41e98d0a6ad65cc0ec77a2ba50bf26d64dc9007f7f1c7d7df68b8b71291a6", }, - encryptOpts: []TDFOption{WithDataAttributeValues(attrVal)}, + encryptOpts: []TDFOption{WithDataAttributes(attrVal.GetFqn())}, expectedKID: defaultKID, expectedURL: s.kasTestURLLookup[kasAu], }, @@ -1758,6 +1733,66 @@ func (s *TDFSuite) Test_MixedBaseKeyTest() { } } +/* +The context for this test is that we want to be able to encrypt a TDF with multiple keys +of the same KAS with the same algorithm, but of different KIDs. + +The test creates mocks of attribute values which are then pulled from +granter_test.go, which return the simple kas key mappings. (r0, r2) +Both are of type RSA 2048, and the KAS is the same for both. +*/ +func (s *TDFSuite) Test_KeySplit_SameKas_SameAlgorithm() { + attrVal1 := mockValueFor(mpc) + attrVal2 := mockValueFor(mpd) + + // Add a key to the kas for proper decryption. + // * Note: This is a hack to get around having to handle multiple + // * keys within our testing structure. Ultimately we should + // * modify the test infra to handle multiple active keys + for _, fakeKas := range s.kases { + if fakeKas.KASInfo.URL == s.kasTestURLLookup[evenMoreSpecificKas] { + old := &fakeKas + fakeKas.privateKey = mockRSAPrivateKey1 + fakeKas.KASInfo.KID = "r0" + fakeKas.KASInfo.PublicKey = mockRSAPublicKey1 + fakeKas.legakeys[old.KID] = keyInfo{old.KID, old.privateKey, old.KASInfo.PublicKey} + } + } + + for index, test := range []tdfTest{ + { + n: "multiple-keys-same-kas-same-algorithm", + fileSize: 5, + tdfFileSize: 2581, + checksum: "ed968e840d10d2d313a870bc131a4e2c311d7ad09bdf32b3418147221f51a6e2", + }, + } { + s.Run(test.n, func() { + // create .txt file + plainTextFileName := test.n + "-" + strconv.Itoa(index) + ".txt" + tdfFileName := plainTextFileName + ".tdf" + decryptedTdfFileName := tdfFileName + ".txt" + + defer func() { + // Remove the test files + _ = os.Remove(plainTextFileName) + _ = os.Remove(tdfFileName) + }() + + // test encrypt + tdo := s.testEncrypt(s.sdk, []TDFOption{WithDataAttributes(attrVal1.GetFqn(), attrVal2.GetFqn())}, plainTextFileName, tdfFileName, test) + s.Len(tdo.manifest.EncryptionInformation.KeyAccessObjs, 2, "Should have two key access objects") + s.Equal("r0", tdo.manifest.EncryptionInformation.KeyAccessObjs[0].KID) + s.Equal("r3", tdo.manifest.EncryptionInformation.KeyAccessObjs[1].KID) + s.Equal(s.kasTestURLLookup[evenMoreSpecificKas], tdo.manifest.EncryptionInformation.KeyAccessObjs[0].KasURL) + s.Equal(s.kasTestURLLookup[evenMoreSpecificKas], tdo.manifest.EncryptionInformation.KeyAccessObjs[1].KasURL) + + // test decrypt with reader + s.testDecryptWithReader(s.sdk, tdfFileName, decryptedTdfFileName, test) + }) + } +} + func (s *TDFSuite) Test_KeyRotation() { for index, test := range []tdfTest{ { @@ -2115,6 +2150,7 @@ func (s *TDFSuite) startBackend() { {kasNz, mockRSAPrivateKey3, mockRSAPublicKey3, defaultKID}, {kasUs, mockRSAPrivateKey1, mockRSAPublicKey1, defaultKID}, {baseKeyURL, mockRSAPrivateKey1, mockRSAPublicKey1, baseKeyKID}, + {evenMoreSpecificKas, mockRSAPrivateKey1, mockRSAPublicKey1, r3KID}, } fkar := &FakeKASRegistry{kases: kasesToMake, s: s} @@ -2198,6 +2234,9 @@ func (f *FakeAttributes) GetAttributeValuesByFqns(_ context.Context, in *connect for i := range v.GetGrants() { v.Grants[i].Uri = f.s.kasTestURLLookup[v.GetGrants()[i].GetUri()] } + for i := range v.GetKasKeys() { + v.KasKeys[i].KasUri = f.s.kasTestURLLookup[v.GetKasKeys()[i].GetKasUri()] + } r[fqn] = &attributespb.GetAttributeValuesByFqnsResponse_AttributeAndValue{ Attribute: v.GetAttribute(), Value: v, From 59e861278075302c2c5c3a10d48a7b8574a7c86d Mon Sep 17 00:00:00 2001 From: Sean Trantalis <18211470+strantalis@users.noreply.github.com> Date: Mon, 23 Jun 2025 12:07:28 -0400 Subject: [PATCH 48/55] feat: inject logger and cache manager to key managers (#2461) This pull request refactors the caching mechanism and key management system in the security and KAS modules, introduces new test utilities for cache handling, and updates the key delegation service to support factory-based key manager initialization. The changes enhance modularity, improve testability, and streamline configuration handling. ### Refactoring of caching and key management: * [`service/internal/security/basic_manager.go`](diffhunk://#diff-61fd7c4622a365905e4d9b011faa96571bb4079aaa66a8b01cf9465226e5dfb2R10-R23): Replaced the `ristretto` cache implementation with a generic `cache.Cache` interface, allowing more flexible cache configurations. Updated the `BasicManager` constructor to accept a cache instance and modified cache-related logic to handle cases where caching is disabled. [[1]](diffhunk://#diff-61fd7c4622a365905e4d9b011faa96571bb4079aaa66a8b01cf9465226e5dfb2R10-R23) [[2]](diffhunk://#diff-61fd7c4622a365905e4d9b011faa96571bb4079aaa66a8b01cf9465226e5dfb2L36-R50) [[3]](diffhunk://#diff-61fd7c4622a365905e4d9b011faa96571bb4079aaa66a8b01cf9465226e5dfb2L164-R164) [[4]](diffhunk://#diff-61fd7c4622a365905e4d9b011faa96571bb4079aaa66a8b01cf9465226e5dfb2L190-R186) ### Updates to test utilities: * [`service/internal/security/basic_manager_test.go`](diffhunk://#diff-fb381e28a4874564397691b95895ee62ce11891490dd6808b39639b4ff83ad28R21-L26): Added a helper function `newTestCache` to create test cache instances and updated tests to use the new cache-based `BasicManager` constructor. Removed references to the old `ristretto` cache implementation. [[1]](diffhunk://#diff-fb381e28a4874564397691b95895ee62ce11891490dd6808b39639b4ff83ad28R21-L26) [[2]](diffhunk://#diff-fb381e28a4874564397691b95895ee62ce11891490dd6808b39639b4ff83ad28R139-R190) [[3]](diffhunk://#diff-fb381e28a4874564397691b95895ee62ce11891490dd6808b39639b4ff83ad28L184-R199) [[4]](diffhunk://#diff-fb381e28a4874564397691b95895ee62ce11891490dd6808b39639b4ff83ad28L205-R220) [[5]](diffhunk://#diff-fb381e28a4874564397691b95895ee62ce11891490dd6808b39639b4ff83ad28R252) [[6]](diffhunk://#diff-fb381e28a4874564397691b95895ee62ce11891490dd6808b39639b4ff83ad28L260-R276) [[7]](diffhunk://#diff-fb381e28a4874564397691b95895ee62ce11891490dd6808b39639b4ff83ad28R391) [[8]](diffhunk://#diff-fb381e28a4874564397691b95895ee62ce11891490dd6808b39639b4ff83ad28L386-R403) [[9]](diffhunk://#diff-fb381e28a4874564397691b95895ee62ce11891490dd6808b39639b4ff83ad28R466-R468) ### Enhancements to the key delegation service: * [`service/kas/kas.go`](diffhunk://#diff-7c373e04d1c0d44b7856a4a007ba28a24eef5a8edf50171b8635f31c3a03598cR17): Updated the `KeyDelegator` to support factory-based key manager registration, enabling dynamic creation of key managers. Added cache support for key delegation with configurable expiration. [[1]](diffhunk://#diff-7c373e04d1c0d44b7856a4a007ba28a24eef5a8edf50171b8635f31c3a03598cR17) [[2]](diffhunk://#diff-7c373e04d1c0d44b7856a4a007ba28a24eef5a8edf50171b8635f31c3a03598cR93-R132) ### Configuration improvements: * [`service/kas/access/provider.go`](diffhunk://#diff-cf99c51d2586a139a71682bb593326b2e9497d57c199b91ee694deb318ee18e7R6): Introduced a new `KeyCacheExpiration` field in `KASConfig` to allow configuration of cache expiration times. [[1]](diffhunk://#diff-cf99c51d2586a139a71682bb593326b2e9497d57c199b91ee694deb318ee18e7R6) [[2]](diffhunk://#diff-cf99c51d2586a139a71682bb593326b2e9497d57c199b91ee694deb318ee18e7R46-R47) ### Refactoring of server options and services: * [`service/pkg/server/options.go`](diffhunk://#diff-e7c833f4a4ae3659c3081ff5cef7d8c52c6730a0003529b268523b077eb5abbbL24-R24): Replaced direct key manager injection with factory-based initialization using `NamedKeyManagerFactory`. [[1]](diffhunk://#diff-e7c833f4a4ae3659c3081ff5cef7d8c52c6730a0003529b268523b077eb5abbbL24-R24) [[2]](diffhunk://#diff-e7c833f4a4ae3659c3081ff5cef7d8c52c6730a0003529b268523b077eb5abbbL130-R133) * [`service/pkg/server/services.go`](diffhunk://#diff-6fb82367e0e4c4e917e2e0fad446315e7125e98a9e4bffea82c039c51214225fL128-R128): Updated service initialization to use key manager factories instead of pre-configured key managers. [[1]](diffhunk://#diff-6fb82367e0e4c4e917e2e0fad446315e7125e98a9e4bffea82c039c51214225fL128-R128) [[2]](diffhunk://#diff-6fb82367e0e4c4e917e2e0fad446315e7125e98a9e4bffea82c039c51214225fL144-R144) ### Checklist - [x] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions --- service/internal/security/basic_manager.go | 56 +++++++++---------- .../internal/security/basic_manager_test.go | 44 ++++++++++----- service/kas/access/provider.go | 3 + service/kas/access/publicKey_test.go | 10 ++-- service/kas/access/rewrap_test.go | 6 +- service/kas/kas.go | 36 +++++++----- service/pkg/server/options.go | 8 +-- service/pkg/server/services.go | 18 +++--- service/pkg/server/services_test.go | 10 ++-- service/pkg/server/start.go | 14 ++--- service/pkg/server/start_test.go | 12 ++-- .../pkg/serviceregistry/serviceregistry.go | 2 +- service/trust/delegating_key_service.go | 34 +++++++---- service/trust/delegating_key_service_test.go | 8 +-- service/trust/key_manager.go | 6 ++ 15 files changed, 157 insertions(+), 110 deletions(-) diff --git a/service/internal/security/basic_manager.go b/service/internal/security/basic_manager.go index 6ae8bce9e2..68e5acfe76 100644 --- a/service/internal/security/basic_manager.go +++ b/service/internal/security/basic_manager.go @@ -7,23 +7,20 @@ import ( "encoding/base64" "encoding/hex" "encoding/pem" + "errors" "fmt" "log/slog" - "time" - "github.com/dgraph-io/ristretto" "github.com/opentdf/platform/lib/ocrypto" "github.com/opentdf/platform/protocol/go/policy" "github.com/opentdf/platform/service/logger" + "github.com/opentdf/platform/service/pkg/cache" "github.com/opentdf/platform/service/trust" - - "github.com/eko/gocache/lib/v4/cache" - "github.com/eko/gocache/lib/v4/store" - ristretto_store "github.com/eko/gocache/store/ristretto/v4" ) const ( - basicManagerName = "opentdf.io/basic" + // BasicManagerName is the unique identifier for the BasicManager. + BasicManagerName = "opentdf.io/basic" ristrettoBufferItems = 64 ristrettoMaxCost = 3400000 ristrettoNumCounters = ristrettoMaxCost * 10 @@ -33,37 +30,24 @@ const ( type BasicManager struct { l *logger.Logger rootKey []byte - cache *cache.Cache[[]byte] + cache *cache.Cache } -func NewBasicManager(logger *logger.Logger, rootKey string) (*BasicManager, error) { +func NewBasicManager(logger *logger.Logger, c *cache.Cache, rootKey string) (*BasicManager, error) { rk, err := hex.DecodeString(rootKey) if err != nil { return nil, fmt.Errorf("failed to hex decode root key: %w", err) } - ristrettoCache, err := ristretto.NewCache(&ristretto.Config{ - NumCounters: ristrettoNumCounters, - MaxCost: ristrettoMaxCost, - BufferItems: ristrettoBufferItems, - }) - if err != nil { - return nil, fmt.Errorf("failed to create ristretto cache: %w", err) - } - - ristrettoStore := ristretto_store.NewRistretto(ristrettoCache) - - cacheManager := cache.New[[]byte](ristrettoStore) - return &BasicManager{ l: logger, rootKey: rk, - cache: cacheManager, + cache: c, }, nil } func (b *BasicManager) Name() string { - return basicManagerName + return BasicManagerName } func (b *BasicManager) Decrypt(ctx context.Context, keyDetails trust.KeyDetails, ciphertext []byte, ephemeralPublicKey []byte) (trust.ProtectedKey, error) { @@ -161,15 +145,23 @@ func (b *BasicManager) Close() { b.rootKey[i] = 0 } b.rootKey = nil - return } func (b *BasicManager) unwrap(ctx context.Context, kid string, wrappedKey string) ([]byte, error) { - if privKey, err := b.cache.Get(ctx, kid); err == nil { - b.l.DebugContext(ctx, "found private key in cache", slog.String("kid", kid)) - return privKey, nil + cacheEnabled := b.cache != nil + if cacheEnabled { + if privKey, err := b.cache.Get(ctx, kid); err == nil { + b.l.DebugContext(ctx, "found private key in cache", slog.String("kid", kid)) + if privKeyBytes, ok := privKey.([]byte); ok { + return privKeyBytes, nil + } + b.l.ErrorContext(ctx, "private key in cache is not of type []byte", slog.String("kid", kid), slog.Any("type", fmt.Sprintf("%T", privKey))) + return nil, errors.New("private key in cache is not of type []byte") + } + b.l.DebugContext(ctx, "private key not found in cache", slog.String("kid", kid)) + } else { + b.l.DebugContext(ctx, "cache not configured, skipping cache lookup", slog.String("kid", kid)) } - b.l.DebugContext(ctx, "private key not found in cache", slog.String("kid", kid)) // base64 decode wk, err := base64.StdEncoding.DecodeString(wrappedKey) @@ -187,8 +179,10 @@ func (b *BasicManager) unwrap(ctx context.Context, kid string, wrappedKey string return nil, fmt.Errorf("failed to decrypt wrapped key: %w", err) } - if err := b.cache.Set(ctx, kid, privKey, store.WithExpiration(time.Second*ristrettoCacheTTL)); err != nil { - b.l.ErrorContext(ctx, "failed to cache private key", slog.String("kid", kid), slog.String("error", err.Error())) + if cacheEnabled { + if err := b.cache.Set(ctx, kid, privKey, nil); err != nil { + b.l.ErrorContext(ctx, "failed to cache private key", slog.String("kid", kid), slog.String("error", err.Error())) + } } return privKey, nil diff --git a/service/internal/security/basic_manager_test.go b/service/internal/security/basic_manager_test.go index ee83b597da..9c3f2e50ad 100644 --- a/service/internal/security/basic_manager_test.go +++ b/service/internal/security/basic_manager_test.go @@ -18,12 +18,11 @@ import ( "github.com/opentdf/platform/lib/ocrypto" "github.com/opentdf/platform/protocol/go/policy" "github.com/opentdf/platform/service/logger" + "github.com/opentdf/platform/service/pkg/cache" "github.com/opentdf/platform/service/trust" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - - "github.com/eko/gocache/lib/v4/store" ) // MockKeyDetails for testing @@ -137,19 +136,32 @@ func generateECKeyAndPEM(curve ocrypto.ECCMode) (ocrypto.ECKeyPair, error) { return ocrypto.NewECKeyPair(curve) } +// Helper to create a test cache +func newTestCache(t *testing.T, log *logger.Logger) *cache.Cache { + t.Helper() + cm, err := cache.NewCacheManager(ristrettoMaxCost) + require.NoError(t, err) + c, err := cm.NewCache("testBasicManagerCache", log, cache.Options{ + Expiration: time.Duration(ristrettoCacheTTL) * time.Second, + }) + require.NoError(t, err) + return c +} + func TestNewBasicManager(t *testing.T) { log := logger.CreateTestLogger() + testCache := newTestCache(t, log) validRootKeyHex := "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" // 32 bytes t.Run("successful creation", func(t *testing.T) { - bm, err := NewBasicManager(log, validRootKeyHex) + bm, err := NewBasicManager(log, testCache, validRootKeyHex) require.NoError(t, err) require.NotNil(t, bm) - assert.Equal(t, basicManagerName, bm.Name()) + assert.Equal(t, BasicManagerName, bm.Name()) }) t.Run("invalid root key hex", func(t *testing.T) { - _, err := NewBasicManager(log, "invalid-hex") + _, err := NewBasicManager(log, testCache, "invalid-hex") require.Error(t, err) assert.Contains(t, err.Error(), "failed to hex decode root key") }) @@ -157,15 +169,17 @@ func TestNewBasicManager(t *testing.T) { func TestBasicManager_Name(t *testing.T) { log := logger.CreateTestLogger() + testCache := newTestCache(t, log) validRootKeyHex := "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - bm, _ := NewBasicManager(log, validRootKeyHex) - assert.Equal(t, basicManagerName, bm.Name()) + bm, _ := NewBasicManager(log, testCache, validRootKeyHex) + assert.Equal(t, BasicManagerName, bm.Name()) } func TestBasicManager_Close(t *testing.T) { log := logger.CreateTestLogger() + testCache := newTestCache(t, log) validRootKeyHex := "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - bm, _ := NewBasicManager(log, validRootKeyHex) + bm, _ := NewBasicManager(log, testCache, validRootKeyHex) require.NotNil(t, bm.rootKey) bm.Close() assert.Nil(t, bm.rootKey, "rootKey should be nilled out after Close") @@ -173,6 +187,7 @@ func TestBasicManager_Close(t *testing.T) { func TestBasicManager_unwrap(t *testing.T) { log := logger.CreateTestLogger() + testCache := newTestCache(t, log) rootKeyHex := "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" rootKey, _ := hex.DecodeString(rootKeyHex) samplePrivateKey := []byte("this is a secret key") @@ -181,7 +196,7 @@ func TestBasicManager_unwrap(t *testing.T) { wrappedKeyStr, err := wrapKeyWithAESGCM(samplePrivateKey, rootKey) require.NoError(t, err) - bm, err := NewBasicManager(log, rootKeyHex) + bm, err := NewBasicManager(log, testCache, rootKeyHex) require.NoError(t, err) t.Run("cache miss, successful unwrap and cache", func(t *testing.T) { @@ -202,7 +217,7 @@ func TestBasicManager_unwrap(t *testing.T) { t.Run("cache hit", func(t *testing.T) { // Ensure key is in cache (from previous test or set it) - err := bm.cache.Set(t.Context(), kid, samplePrivateKey, store.WithExpiration(time.Second*30)) + err := bm.cache.Set(t.Context(), kid, samplePrivateKey, nil) require.NoError(t, err) unwrapped, err := bm.unwrap(t.Context(), kid, "this-should-not-be-used") // Provide dummy wrapped key @@ -234,6 +249,7 @@ func TestBasicManager_unwrap(t *testing.T) { func TestBasicManager_Decrypt(t *testing.T) { log := logger.CreateTestLogger() + testCache := newTestCache(t, log) rootKeyHex := "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" rootKey, _ := hex.DecodeString(rootKeyHex) @@ -257,7 +273,7 @@ func TestBasicManager_Decrypt(t *testing.T) { wrappedECPrivKeyStr, err := wrapKeyWithAESGCM([]byte(ecPrivKey), rootKey) require.NoError(t, err) - bm, err := NewBasicManager(log, rootKeyHex) + bm, err := NewBasicManager(log, testCache, rootKeyHex) require.NoError(t, err) samplePayload := []byte("secret payload") @@ -372,6 +388,7 @@ func TestBasicManager_Decrypt(t *testing.T) { func TestBasicManager_DeriveKey(t *testing.T) { log := logger.CreateTestLogger() + testCache := newTestCache(t, log) rootKeyHex := "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" rootKey, _ := hex.DecodeString(rootKeyHex) @@ -383,7 +400,7 @@ func TestBasicManager_DeriveKey(t *testing.T) { wrappedECPrivKeyStr, err := wrapKeyWithAESGCM([]byte(ecPrivKey), rootKey) require.NoError(t, err) - bm, err := NewBasicManager(log, rootKeyHex) + bm, err := NewBasicManager(log, testCache, rootKeyHex) require.NoError(t, err) clientEphemeralECDHKey, err := ecdh.P256().GenerateKey(rand.Reader) @@ -446,8 +463,9 @@ func TestBasicManager_DeriveKey(t *testing.T) { func TestBasicManager_GenerateECSessionKey(t *testing.T) { log := logger.CreateTestLogger() + testCache := newTestCache(t, log) rootKeyHex := "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - bm, err := NewBasicManager(log, rootKeyHex) + bm, err := NewBasicManager(log, testCache, rootKeyHex) require.NoError(t, err) clientPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) diff --git a/service/kas/access/provider.go b/service/kas/access/provider.go index 20932134e7..28dd31a622 100644 --- a/service/kas/access/provider.go +++ b/service/kas/access/provider.go @@ -3,6 +3,7 @@ package access import ( "context" "net/url" + "time" kaspb "github.com/opentdf/platform/protocol/go/kas" otdf "github.com/opentdf/platform/sdk" @@ -42,6 +43,8 @@ type KASConfig struct { RootKey string `mapstructure:"root_key" json:"root_key"` + KeyCacheExpiration time.Duration `mapstructure:"key_cache_expiration" json:"key_cache_expiration"` + // Deprecated // Enables experimental EC rewrap support in TDFs // Enabling is required to parse KAOs with the `ec-wrapped` type, diff --git a/service/kas/access/publicKey_test.go b/service/kas/access/publicKey_test.go index df46bcda7c..8f01adf2b4 100644 --- a/service/kas/access/publicKey_test.go +++ b/service/kas/access/publicKey_test.go @@ -172,8 +172,8 @@ func TestPublicKeyWithSecurityProvider(t *testing.T) { kasURI := urlHost(t) // Create Provider with the mock security provider - delegator := trust.NewDelegatingKeyService(mockProvider, logger.CreateTestLogger()) - delegator.RegisterKeyManager(mockProvider.Name(), func() (trust.KeyManager, error) { return mockProvider, nil }) + delegator := trust.NewDelegatingKeyService(mockProvider, logger.CreateTestLogger(), nil) + delegator.RegisterKeyManager(mockProvider.Name(), func(_ *trust.KeyManagerFactoryOptions) (trust.KeyManager, error) { return mockProvider, nil }) kas := Provider{ URI: *kasURI, KeyDelegator: delegator, @@ -345,8 +345,10 @@ func TestStandardCertificateHandlerEmpty(t *testing.T) { inProcess := security.NewSecurityProviderAdapter(c, nil, nil) - delegator := trust.NewDelegatingKeyService(inProcess, logger.CreateTestLogger()) - delegator.RegisterKeyManager(inProcess.Name(), func() (trust.KeyManager, error) { return inProcess, nil }) + delegator := trust.NewDelegatingKeyService(inProcess, logger.CreateTestLogger(), nil) + delegator.RegisterKeyManager(inProcess.Name(), func(_ *trust.KeyManagerFactoryOptions) (trust.KeyManager, error) { + return inProcess, nil + }) kas := Provider{ URI: *kasURI, diff --git a/service/kas/access/rewrap_test.go b/service/kas/access/rewrap_test.go index f58115ce42..997a879f99 100644 --- a/service/kas/access/rewrap_test.go +++ b/service/kas/access/rewrap_test.go @@ -97,7 +97,7 @@ func TestListLegacyKeys_KeyIndexPopulated(t *testing.T) { } delegator := trust.NewDelegatingKeyService(&fakeKeyIndex{ keys: fakeKeys, - }, logger.CreateTestLogger()) + }, logger.CreateTestLogger(), nil) p := &Provider{ Logger: testLogger, KeyDelegator: delegator, @@ -108,7 +108,7 @@ func TestListLegacyKeys_KeyIndexPopulated(t *testing.T) { func TestListLegacyKeys_Empty(t *testing.T) { testLogger := logger.CreateTestLogger() - delegator := trust.NewDelegatingKeyService(&fakeKeyIndex{}, logger.CreateTestLogger()) + delegator := trust.NewDelegatingKeyService(&fakeKeyIndex{}, logger.CreateTestLogger(), nil) p := &Provider{ Logger: testLogger, KeyDelegator: delegator, @@ -121,7 +121,7 @@ func TestListLegacyKeys_KeyIndexError(t *testing.T) { testLogger := logger.CreateTestLogger() delegator := trust.NewDelegatingKeyService(&fakeKeyIndex{ err: errors.New("fail"), - }, logger.CreateTestLogger()) + }, logger.CreateTestLogger(), nil) p := &Provider{ Logger: testLogger, KeyDelegator: delegator, diff --git a/service/kas/kas.go b/service/kas/kas.go index c77f6d9ec9..0280e58b37 100644 --- a/service/kas/kas.go +++ b/service/kas/kas.go @@ -14,6 +14,7 @@ import ( "github.com/opentdf/platform/service/internal/security" "github.com/opentdf/platform/service/kas/access" "github.com/opentdf/platform/service/logger" + "github.com/opentdf/platform/service/pkg/cache" "github.com/opentdf/platform/service/pkg/config" "github.com/opentdf/platform/service/pkg/serviceregistry" "github.com/opentdf/platform/service/trust" @@ -89,28 +90,37 @@ func NewRegistration() *serviceregistry.Service[kasconnect.AccessServiceHandler] panic(fmt.Errorf("invalid kas cfg [%v] %w", srp.Config, err)) } // kasURLString will be used for p.URI + var cacheClient *cache.Cache + if kasCfg.KeyCacheExpiration != 0 { + cacheClient, err = srp.NewCacheClient(cache.Options{ + Expiration: kasCfg.KeyCacheExpiration, + }) + if err != nil { + panic(err) + } + } + if kasCfg.Preview.KeyManagement { srp.Logger.Info("Preview Feature: Key management is enabled") // Configure new delegation service - p.KeyDelegator = trust.NewDelegatingKeyService(NewPlatformKeyIndexer(srp.SDK, kasURLString, srp.Logger), srp.Logger) - for _, manager := range srp.KeyManagers { - p.KeyDelegator.RegisterKeyManager(manager.Name(), func() (trust.KeyManager, error) { - return manager, nil - }) + p.KeyDelegator = trust.NewDelegatingKeyService(NewPlatformKeyIndexer(srp.SDK, kasURLString, srp.Logger), srp.Logger, cacheClient) + for _, manager := range srp.KeyManagerFactories { + p.KeyDelegator.RegisterKeyManager(manager.Name, manager.Factory) } // Register Basic Key Manager - bm, err := security.NewBasicManager(srp.Logger.With("process", "basic-key-manager"), kasCfg.RootKey) - if err != nil { - panic(err) - } - p.KeyDelegator.RegisterKeyManager(bm.Name(), func() (trust.KeyManager, error) { + + p.KeyDelegator.RegisterKeyManager(security.BasicManagerName, func(opts *trust.KeyManagerFactoryOptions) (trust.KeyManager, error) { + bm, err := security.NewBasicManager(opts.Logger, opts.Cache, kasCfg.RootKey) + if err != nil { + return nil, err + } return bm, nil }) // Explicitly set the default manager for session key generation. // This should be configurable, e.g., defaulting to BasicManager or an HSM if available. - p.KeyDelegator.SetDefaultMode(bm.Name()) // Example: default to BasicManager + p.KeyDelegator.SetDefaultMode(security.BasicManagerName) // Example: default to BasicManager } else { // Set up both the legacy CryptoProvider and the new SecurityProvider kasCfg.UpgradeMapToKeyring(srp.OTDF.CryptoProvider) @@ -118,8 +128,8 @@ func NewRegistration() *serviceregistry.Service[kasconnect.AccessServiceHandler] inProcessService := initSecurityProviderAdapter(p.CryptoProvider, kasCfg, srp.Logger) - p.KeyDelegator = trust.NewDelegatingKeyService(inProcessService, srp.Logger) - p.KeyDelegator.RegisterKeyManager(inProcessService.Name(), func() (trust.KeyManager, error) { + p.KeyDelegator = trust.NewDelegatingKeyService(inProcessService, srp.Logger, nil) + p.KeyDelegator.RegisterKeyManager(inProcessService.Name(), func(*trust.KeyManagerFactoryOptions) (trust.KeyManager, error) { return inProcessService, nil }) // Set default for non-key-management mode diff --git a/service/pkg/server/options.go b/service/pkg/server/options.go index e8799526ef..79d4bf6aee 100644 --- a/service/pkg/server/options.go +++ b/service/pkg/server/options.go @@ -21,7 +21,7 @@ type StartConfig struct { casbinAdapter persist.Adapter configLoaders []config.Loader - trustKeyManagers []trust.KeyManager + trustKeyManagers []trust.NamedKeyManagerFactory } // Deprecated: Use WithConfigKey @@ -127,10 +127,10 @@ func WithAdditionalConfigLoader(loader config.Loader) StartOptions { } } -// WithTrustKeyManagers option sets the trust key manager to be used for the server. -func WithTrustKeyManagers(managers ...trust.KeyManager) StartOptions { +// WithTrustKeyManagerFactories option provides factories for creating trust key managers. +func WithTrustKeyManagerFactories(factories ...trust.NamedKeyManagerFactory) StartOptions { return func(c StartConfig) StartConfig { - c.trustKeyManagers = append(c.trustKeyManagers, managers...) + c.trustKeyManagers = append(c.trustKeyManagers, factories...) return c } } diff --git a/service/pkg/server/services.go b/service/pkg/server/services.go index 2e67a5d573..6d493f361a 100644 --- a/service/pkg/server/services.go +++ b/service/pkg/server/services.go @@ -119,13 +119,13 @@ func registerCoreServices(reg serviceregistry.Registry, mode []string) ([]string } type startServicesParams struct { - cfg *config.Config - otdf *server.OpenTDFServer - client *sdk.SDK - logger *logging.Logger - reg serviceregistry.Registry - cacheManager *cache.Manager - keyManagers []trust.KeyManager + cfg *config.Config + otdf *server.OpenTDFServer + client *sdk.SDK + logger *logging.Logger + reg serviceregistry.Registry + cacheManager *cache.Manager + keyManagerFactories []trust.NamedKeyManagerFactory } // startServices iterates through the registered namespaces and starts the services @@ -141,7 +141,7 @@ func startServices(ctx context.Context, params startServicesParams) (func(), err logger := params.logger reg := params.reg cacheManager := params.cacheManager - keyManagers := params.keyManagers + keyManagerFactories := params.keyManagerFactories // Iterate through the registered namespaces for ns, namespace := range reg { @@ -214,7 +214,7 @@ func startServices(ctx context.Context, params startServicesParams) (func(), err OTDF: otdf, // TODO: REMOVE THIS Tracer: tracer, NewCacheClient: createCacheClient, - KeyManagers: keyManagers, + KeyManagerFactories: keyManagerFactories, }) if err != nil { return func() {}, err diff --git a/service/pkg/server/services_test.go b/service/pkg/server/services_test.go index f8b294f744..10c9a3c575 100644 --- a/service/pkg/server/services_test.go +++ b/service/pkg/server/services_test.go @@ -276,11 +276,11 @@ func (suite *ServiceTestSuite) TestStartServicesWithVariousCases() { "foobar": {}, }, }, - otdf: otdf, - client: nil, - keyManagers: []trust.KeyManager{}, - logger: newLogger, - reg: registry, + otdf: otdf, + client: nil, + keyManagerFactories: []trust.NamedKeyManagerFactory{}, + logger: newLogger, + reg: registry, }) // call cleanup function diff --git a/service/pkg/server/start.go b/service/pkg/server/start.go index 84c326f334..93d7e32aad 100644 --- a/service/pkg/server/start.go +++ b/service/pkg/server/start.go @@ -299,13 +299,13 @@ func Start(f ...StartOptions) error { logger.Info("starting services") gatewayCleanup, err := startServices(ctx, startServicesParams{ - cfg: cfg, - otdf: otdf, - client: client, - keyManagers: startConfig.trustKeyManagers, - logger: logger, - reg: svcRegistry, - cacheManager: cacheManager, + cfg: cfg, + otdf: otdf, + client: client, + keyManagerFactories: startConfig.trustKeyManagers, + logger: logger, + reg: svcRegistry, + cacheManager: cacheManager, }) if err != nil { logger.Error("issue starting services", slog.String("error", err.Error())) diff --git a/service/pkg/server/start_test.go b/service/pkg/server/start_test.go index 41d6a1f8d5..33163b12ec 100644 --- a/service/pkg/server/start_test.go +++ b/service/pkg/server/start_test.go @@ -362,12 +362,12 @@ func (s *StartTestSuite) Test_Start_When_Extra_Service_Registered() { "test": {}, }, }, - otdf: s, - client: nil, - keyManagers: []trust.KeyManager{}, - logger: logger, - reg: registry, - cacheManager: &cache.Manager{}, + otdf: s, + client: nil, + keyManagerFactories: []trust.NamedKeyManagerFactory{}, + logger: logger, + reg: registry, + cacheManager: &cache.Manager{}, }) require.NoError(t, err) defer cleanup() diff --git a/service/pkg/serviceregistry/serviceregistry.go b/service/pkg/serviceregistry/serviceregistry.go index 701ab0ba65..5b5e885d7e 100644 --- a/service/pkg/serviceregistry/serviceregistry.go +++ b/service/pkg/serviceregistry/serviceregistry.go @@ -47,7 +47,7 @@ type RegistrationParams struct { // NewCacheClient is a function that can be used to create a new cache instance for the service NewCacheClient func(cache.Options) (*cache.Cache, error) - KeyManagers []trust.KeyManager + KeyManagerFactories []trust.NamedKeyManagerFactory ////// The following functions are optional and intended to be called by the service ////// diff --git a/service/trust/delegating_key_service.go b/service/trust/delegating_key_service.go index 097470a076..95394df8cb 100644 --- a/service/trust/delegating_key_service.go +++ b/service/trust/delegating_key_service.go @@ -8,9 +8,16 @@ import ( "sync" "github.com/opentdf/platform/service/logger" + "github.com/opentdf/platform/service/pkg/cache" ) -type KeyManagerFactory func() (KeyManager, error) +type KeyManagerFactoryOptions struct { + Logger *logger.Logger + Cache *cache.Cache +} + +// KeyManagerFactory defines the signature for functions that can create KeyManager instances. +type KeyManagerFactory func(opts *KeyManagerFactoryOptions) (KeyManager, error) // DelegatingKeyService is a key service that multiplexes between key managers based on the key's mode. type DelegatingKeyService struct { @@ -29,16 +36,19 @@ type DelegatingKeyService struct { l *logger.Logger + c *cache.Cache + // Mutex to protect access to the manager cache mutex sync.Mutex } -func NewDelegatingKeyService(index KeyIndex, l *logger.Logger) *DelegatingKeyService { +func NewDelegatingKeyService(index KeyIndex, l *logger.Logger, c *cache.Cache) *DelegatingKeyService { return &DelegatingKeyService{ index: index, managerFactories: make(map[string]KeyManagerFactory), managers: make(map[string]KeyManager), l: l, + c: c, } } @@ -173,21 +183,25 @@ func (d *DelegatingKeyService) getKeyManager(name string) (KeyManager, error) { d.mutex.Unlock() if factoryExists { - managerFromFactory, err := factory() + options := &KeyManagerFactoryOptions{Logger: d.l.With("key-manager", name), Cache: d.c} + managerFromFactory, err := factory(options) if err != nil { return nil, fmt.Errorf("factory for key manager '%s' failed: %w", name, err) } + // If err is nil (checked above) but managerFromFactory is still nil, + // the factory implementation is problematic. + if managerFromFactory == nil { + return nil, fmt.Errorf("factory for key manager '%s' returned nil manager without an error", name) + } d.mutex.Lock() d.managers[name] = managerFromFactory // Cache the newly created manager d.mutex.Unlock() return managerFromFactory, nil } - - if name == currentDefaultMode && name != "" { - return nil, fmt.Errorf("configured default key manager '%s' not found (no factory/cache entry)", name) - } - - d.l.Debug("Key manager not found by name, falling back to default", "requestedName", name, "configuredDefaultName", currentDefaultMode) - return d._defKM() + // Factory for 'name' not found. + // If 'name' was the defaultMode, _defKM will error if its factory is also missing. + // If 'name' was not the defaultMode, we fall back to the default manager. + d.l.Debug("Key manager factory not found for name, attempting to use/load default", "requestedName", name, "configuredDefaultName", currentDefaultMode) + return d._defKM() // _defKM handles erroring if the default manager itself cannot be loaded. } diff --git a/service/trust/delegating_key_service_test.go b/service/trust/delegating_key_service_test.go index 83fe6bee4c..1db122d10f 100644 --- a/service/trust/delegating_key_service_test.go +++ b/service/trust/delegating_key_service_test.go @@ -196,7 +196,7 @@ type DelegatingKeyServiceTestSuite struct { func (suite *DelegatingKeyServiceTestSuite) SetupTest() { suite.mockIndex = &MockKeyIndex{} - suite.service = NewDelegatingKeyService(suite.mockIndex, logger.CreateTestLogger()) + suite.service = NewDelegatingKeyService(suite.mockIndex, logger.CreateTestLogger(), nil) suite.mockManagerA = &MockKeyManager{} suite.mockManagerB = &MockKeyManager{} } @@ -234,7 +234,7 @@ func (suite *DelegatingKeyServiceTestSuite) TestDecrypt() { mockProtectedKey.On("DecryptAESGCM", mock.Anything, mock.Anything, mock.Anything).Return([]byte("decrypted"), nil) suite.mockManagerA.On("Decrypt", mock.Anything, mockKeyDetails, []byte("ciphertext"), []byte("ephemeralKey")).Return(mockProtectedKey, nil) - suite.service.RegisterKeyManager("mockManager", func() (KeyManager, error) { + suite.service.RegisterKeyManager("mockManager", func(_ *KeyManagerFactoryOptions) (KeyManager, error) { return suite.mockManagerA, nil }) @@ -252,7 +252,7 @@ func (suite *DelegatingKeyServiceTestSuite) TestDeriveKey() { mockProtectedKey.On("Export", mock.Anything).Return([]byte("exported"), nil) suite.mockManagerA.On("DeriveKey", mock.Anything, mockKeyDetails, []byte("ephemeralKey"), elliptic.P256()).Return(mockProtectedKey, nil) - suite.service.RegisterKeyManager("mockManager", func() (KeyManager, error) { + suite.service.RegisterKeyManager("mockManager", func(_ *KeyManagerFactoryOptions) (KeyManager, error) { return suite.mockManagerA, nil }) @@ -262,7 +262,7 @@ func (suite *DelegatingKeyServiceTestSuite) TestDeriveKey() { } func (suite *DelegatingKeyServiceTestSuite) TestGenerateECSessionKey() { - suite.service.RegisterKeyManager("default", func() (KeyManager, error) { + suite.service.RegisterKeyManager("default", func(_ *KeyManagerFactoryOptions) (KeyManager, error) { return suite.mockManagerA, nil }) suite.service.defaultMode = "default" diff --git a/service/trust/key_manager.go b/service/trust/key_manager.go index 2f8dc3f9ef..a00b7a1806 100644 --- a/service/trust/key_manager.go +++ b/service/trust/key_manager.go @@ -57,3 +57,9 @@ type KeyService interface { KeyIndex KeyManager } + +// NamedKeyManagerFactory pairs a KeyManagerFactory with its intended registration name. +type NamedKeyManagerFactory struct { + Name string + Factory KeyManagerFactory +} From bfb00617d099bd6c44cdfc5dd14b729dc77b3c7a Mon Sep 17 00:00:00 2001 From: "opentdf-automation[bot]" <149537512+opentdf-automation[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 16:15:46 +0000 Subject: [PATCH 49/55] chore(main): release protocol/go 0.5.0 (#2464) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :robot: I have created a release *beep* *boop* --- ## [0.5.0](https://github.com/opentdf/platform/compare/protocol/go/v0.4.0...protocol/go/v0.5.0) (2025-06-23) ### ⚠ BREAKING CHANGES * **policy:** disable kas grants in favor of key mappings ([#2220](https://github.com/opentdf/platform/issues/2220)) ### Features * **policy:** disable kas grants in favor of key mappings ([#2220](https://github.com/opentdf/platform/issues/2220)) ([30f8cf5](https://github.com/opentdf/platform/commit/30f8cf54abbb1a9def43a6d0fa602ba979dd3053)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: opentdf-automation[bot] <149537512+opentdf-automation[bot]@users.noreply.github.com> --- .github/release-please/release-please-manifest.json | 2 +- protocol/go/CHANGELOG.md | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 protocol/go/CHANGELOG.md diff --git a/.github/release-please/release-please-manifest.json b/.github/release-please/release-please-manifest.json index 5950ddd3f8..8fe3eb3586 100644 --- a/.github/release-please/release-please-manifest.json +++ b/.github/release-please/release-please-manifest.json @@ -3,7 +3,7 @@ "lib/ocrypto": "0.2.0", "lib/flattening": "0.1.3", "lib/identifier": "0.0.2", - "protocol/go": "0.4.0", + "protocol/go": "0.5.0", "sdk": "0.4.7", "service": "0.6.0" } diff --git a/protocol/go/CHANGELOG.md b/protocol/go/CHANGELOG.md new file mode 100644 index 0000000000..15e4923b54 --- /dev/null +++ b/protocol/go/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog + +## [0.5.0](https://github.com/opentdf/platform/compare/protocol/go/v0.4.0...protocol/go/v0.5.0) (2025-06-23) + + +### ⚠ BREAKING CHANGES + +* **policy:** disable kas grants in favor of key mappings ([#2220](https://github.com/opentdf/platform/issues/2220)) + +### Features + +* **policy:** disable kas grants in favor of key mappings ([#2220](https://github.com/opentdf/platform/issues/2220)) ([30f8cf5](https://github.com/opentdf/platform/commit/30f8cf54abbb1a9def43a6d0fa602ba979dd3053)) From 61d46b223acebf3e75f6605ee8768b883e039b17 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy <35498075+elizabethhealy@users.noreply.github.com> Date: Mon, 23 Jun 2025 12:32:19 -0400 Subject: [PATCH 50/55] feat(main): Add Close() method to cache manager (#2465) ### Proposed Changes Add cleanup for underlying Ristretto cache by tracking and explicitly closing it to avoid goroutine leaks and ensure proper resource release. Details: Modified cache.Manager to store a reference to the underlying *ristretto.Cache. Added a Close() method to Manager that calls .Close() on the underlying Ristretto cache. Why: Ristretto starts background goroutines and allocates internal buffers that aren't automatically cleaned up. Explicitly calling .Close() ensures proper shutdown, especially important in tests and long-running services ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions --- service/pkg/cache/cache.go | 12 ++++++++++-- service/pkg/cache/cache_test.go | 17 +++++++++++++++++ service/pkg/server/start.go | 1 + 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/service/pkg/cache/cache.go b/service/pkg/cache/cache.go index 7cc70d9e76..290586fe20 100644 --- a/service/pkg/cache/cache.go +++ b/service/pkg/cache/cache.go @@ -16,7 +16,8 @@ var ErrCacheMiss = errors.New("cache miss") // Manager is a cache manager for any value. type Manager struct { - cache *cache.Cache[any] + cache *cache.Cache[any] + underlyingStore *ristretto.Cache } // Cache is a cache implementation using gocache for any value type. @@ -49,7 +50,8 @@ func NewCacheManager(maxCost int64) (*Manager, error) { } ristrettoStore := ristretto_store.NewRistretto(store) return &Manager{ - cache: cache.New[any](ristrettoStore), + cache: cache.New[any](ristrettoStore), + underlyingStore: store, }, nil } @@ -74,6 +76,12 @@ func (c *Manager) NewCache(serviceName string, log *logger.Logger, options Optio return cache, nil } +func (c *Manager) Close() { + if c.underlyingStore != nil { + c.underlyingStore.Close() + } +} + // Get retrieves a value from the cache func (c *Cache) Get(ctx context.Context, key string) (any, error) { val, err := c.manager.cache.Get(ctx, c.getKey(key)) diff --git a/service/pkg/cache/cache_test.go b/service/pkg/cache/cache_test.go index efdfb517fb..2dc4fe2ac9 100644 --- a/service/pkg/cache/cache_test.go +++ b/service/pkg/cache/cache_test.go @@ -43,3 +43,20 @@ func TestNewCacheManager_NewCacheIntegration(t *testing.T) { require.Equal(t, "testService", cache.serviceName) require.Equal(t, options, cache.cacheOptions) } + +func TestCacheManagerClose(t *testing.T) { + // Create a cache manager + manager, err := NewCacheManager(1024 * 1024) // 1 MB max cost + require.NoError(t, err) + require.NotNil(t, manager) + + // Ensure Close does not panic + require.NotPanics(t, func() { + manager.Close() + }) + + // Calling Close twice should also be safe (defensive test) + require.NotPanics(t, func() { + manager.Close() + }) +} diff --git a/service/pkg/server/start.go b/service/pkg/server/start.go index 93d7e32aad..5b06d76493 100644 --- a/service/pkg/server/start.go +++ b/service/pkg/server/start.go @@ -96,6 +96,7 @@ func Start(f ...StartOptions) error { if err != nil { return fmt.Errorf("could not create cache manager: %w", err) } + defer cacheManager.Close() logger.Info("starting opentdf services") From 002f934e76a1be5a7e8db3804531edae043469de Mon Sep 17 00:00:00 2001 From: Jake Van Vorhis <83739412+jakedoublev@users.noreply.github.com> Date: Mon, 23 Jun 2025 10:01:03 -0700 Subject: [PATCH 51/55] chore(ci): enable sloglint (#2462) ### Proposed Changes * enables sloglint * updates settings to prefer `slog.Attr` over arbitrary key/values * lint fixes throughout * removes `request` logs and `token` logs where we were previously logging them ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions --- .golangci.yaml | 13 +- examples/cmd/attributes.go | 87 ++++++-- examples/cmd/authorization.go | 9 +- examples/cmd/kas.go | 30 ++- lib/fixtures/keycloak.go | 211 ++++++++++++------ sdk/auth/oauth/oauth.go | 2 +- sdk/auth/token_adding_interceptor.go | 8 +- sdk/bulk.go | 4 +- sdk/codegen/runner/generate.go | 10 +- sdk/granter.go | 64 ++++-- sdk/nanotdf.go | 28 +-- sdk/tdf.go | 12 +- sdk/tdf_test.go | 5 +- service/authorization/authorization.go | 14 +- service/authorization/authorization_test.go | 4 +- service/authorization/v2/authorization.go | 4 +- service/cmd/migrate.go | 2 +- service/cmd/provisionKeycloak.go | 2 +- service/entityresolution/entityresolution.go | 1 + .../keycloak/entity_resolution.go | 1 + .../keycloak/v2/entity_resolution.go | 64 ++++-- .../entityresolution/v2/entity_resolution.go | 5 +- service/health/health.go | 12 +- service/integration/main_test.go | 2 + service/internal/access/pdp.go | 1 + service/internal/access/v2/evaluate.go | 2 +- service/internal/access/v2/helpers.go | 5 +- .../internal/access/v2/just_in_time_pdp.go | 8 +- service/internal/access/v2/pdp.go | 18 +- service/internal/auth/authn.go | 59 ++++- service/internal/auth/casbin.go | 53 ++++- service/internal/fixtures/db.go | 10 +- service/internal/fixtures/fixtures.go | 19 +- service/internal/security/basic_manager.go | 12 +- .../internal/security/in_process_provider.go | 10 +- service/internal/security/standard_crypto.go | 10 +- service/internal/security/standard_only.go | 2 +- service/internal/server/server.go | 6 +- .../subject_mapping_builtin.go | 2 +- service/kas/access/accessPdp.go | 10 +- service/kas/access/publicKey.go | 35 ++- service/kas/access/rewrap.go | 138 ++++++++---- service/kas/access/rewrap_test.go | 4 +- service/kas/kas.go | 6 +- service/logger/audit/logger.go | 17 +- service/logger/audit/utils_test.go | 3 +- service/logger/logger.go | 3 + service/pkg/cache/cache.go | 19 +- service/pkg/config/config.go | 4 +- service/pkg/config/loader.go | 26 ++- service/pkg/db/db.go | 5 +- service/pkg/db/db_migration.go | 35 ++- service/pkg/db/errors.go | 9 +- service/pkg/server/services.go | 10 +- service/pkg/server/start.go | 9 +- service/pkg/server/start_test.go | 2 +- .../pkg/serviceregistry/serviceregistry.go | 11 +- service/policy/attributes/attributes.go | 6 +- service/policy/db/grant_mappings.go | 2 +- .../policy/db/key_access_server_registry.go | 3 +- service/policy/db/key_management.go | 3 +- service/policy/db/registered_resources.go | 6 +- .../kasregistry/key_access_server_registry.go | 24 +- .../policy/keymanagement/key_management.go | 14 +- .../policy/subjectmapping/subject_mapping.go | 10 +- service/rttests/rt_test.go | 18 +- service/tracing/otel.go | 50 +++-- service/trust/delegating_key_service.go | 6 +- 68 files changed, 892 insertions(+), 407 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index cb6821f471..a2eb207a30 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -56,7 +56,7 @@ linters: - revive # - recvcheck - rowserrcheck - # - sloglint + - sloglint - spancheck - sqlclosecheck # - staticcheck @@ -127,11 +127,11 @@ linters: packages: - github.com/jackc/pgx/v5 sloglint: - kv-only: true + kv-only: false no-mixed-args: true static-msg: true - attr-only: false - no-raw-keys: true + attr-only: true + no-raw-keys: false msg-style: lowercased key-naming-case: snake forbidden-keys: @@ -139,6 +139,11 @@ linters: - level - msg - source + - request + - req + - auth + - authorization + - token args-on-sep-lines: true tagliatelle: case: diff --git a/examples/cmd/attributes.go b/examples/cmd/attributes.go index 755c6de7c9..fb391b6f24 100644 --- a/examples/cmd/attributes.go +++ b/examples/cmd/attributes.go @@ -92,7 +92,7 @@ func listAttributes(cmd *cobra.Command) error { if err != nil { return err } - slog.Info(fmt.Sprintf("found %d namespaces", len(listResp.GetNamespaces()))) + slog.Info("found namespaces", slog.Int("count", len(listResp.GetNamespaces()))) for _, n := range listResp.GetNamespaces() { nsuris = append(nsuris, n.GetFqn()) } @@ -111,7 +111,10 @@ func listAttributes(cmd *cobra.Command) error { if err != nil { return err } - slog.Info(fmt.Sprintf("found %d attributes in namespace", len(lsr.GetAttributes())), "ns", n) + slog.Info("found attributes in namespace", + slog.Int("count", len(lsr.GetAttributes())), + slog.String("ns", n), + ) for _, a := range lsr.GetAttributes() { if longformat { fmt.Printf("%s\t%s\n", a.GetFqn(), a.GetId()) @@ -133,12 +136,15 @@ func listAttributes(cmd *cobra.Command) error { func nsuuid(ctx context.Context, s *sdk.SDK, u string) (string, error) { url, err := url.Parse(u) if err != nil { - slog.Error("namespace url.Parse", "err", err, "url", u) + slog.Error("namespace url.Parse", + slog.String("url", u), + slog.Any("error", err), + ) return "", errors.Join(err, ErrInvalidArgument) } listResp, err := s.Namespaces.ListNamespaces(ctx, &namespaces.ListNamespacesRequest{}) if err != nil { - slog.Error("ListNamespaces", "err", err) + slog.Error("failed to ListNamespaces", slog.Any("error", err)) return "", errors.Join(err, ErrInvalidArgument) } for _, n := range listResp.GetNamespaces() { @@ -155,7 +161,7 @@ func attruuid(ctx context.Context, s *sdk.SDK, nsu, fqn string) (string, error) State: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ANY, }) if err != nil { - slog.Error("ListAttributes", "err", err) + slog.Error("failed to ListAttributes", slog.Any("error", err)) return "", errors.Join(err, ErrInvalidArgument) } for _, a := range resp.GetAttributes() { @@ -169,7 +175,7 @@ func attruuid(ctx context.Context, s *sdk.SDK, nsu, fqn string) (string, error) func avuuid(ctx context.Context, s *sdk.SDK, auuid, vs string) (string, error) { resp, err := s.Attributes.GetAttribute(ctx, &attributes.GetAttributeRequest{Id: auuid}) if err != nil { - slog.Error("GetAttribute", "err", err) + slog.Error("failed to GetAttribute", slog.Any("error", err)) return "", errors.Join(err, ErrInvalidArgument) } for _, v := range resp.GetAttribute().GetValues() { @@ -183,12 +189,12 @@ func avuuid(ctx context.Context, s *sdk.SDK, auuid, vs string) (string, error) { func addNamespace(ctx context.Context, s *sdk.SDK, u string) (string, error) { url, err := url.Parse(u) if err != nil { - slog.Error("url.Parse", "err", err) + slog.Error("url.Parse", slog.Any("error", err)) return "", errors.Join(err, ErrInvalidArgument) } resp, err := s.Namespaces.CreateNamespace(ctx, &namespaces.CreateNamespaceRequest{Name: url.Hostname()}) if err != nil { - slog.Error("CreateNamespace", "err", err) + slog.Error("failed to CreateNamespace", slog.Any("error", err)) return "", errors.Join(err, ErrInvalidArgument) } return resp.GetNamespace().GetId(), nil @@ -213,26 +219,32 @@ func addAttribute(cmd *cobra.Command) error { nsu, err = addNamespace(cmd.Context(), s, auth) } if err != nil { - slog.Error("upsertNamespace", "err", err) + slog.Error("upsertNamespace", slog.Any("error", err)) return err } attrEl, err := url.PathUnescape(m[2]) if err != nil { - slog.Error("url.PathUnescape(attr)", "err", err, "attr", m[2]) + slog.Error("url.PathUnescape(attr)", + slog.String("attr", m[2]), + slog.Any("error", err), + ) return err } aid, err := upsertAttr(cmd.Context(), s, nsu, attrEl, values) if err != nil { return err } - slog.Info("created attribute", "passedin", attrEl, "id", aid) + slog.Info("created attribute", + slog.String("passedin", attrEl), + slog.String("id", aid), + ) return nil } func removeAttribute(cmd *cobra.Command) error { s, err := newSDK() if err != nil { - slog.Error("could not connect", "err", err) + slog.Error("could not connect", slog.Any("error", err)) return err } defer s.Close() @@ -258,20 +270,32 @@ func removeAttribute(cmd *cobra.Command) error { Fqn: strings.ToLower(attr), }) if err != nil { - slog.Error("UnsafeDeleteAttribute", "err", err, "id", auuid) + slog.Error("failed to UnsafeDeleteAttribute", + slog.String("id", auuid), + slog.Any("error", err), + ) return err } - slog.Info("deleted attribute", "attr", attr, "resp", resp) + slog.Info("deleted attribute", + slog.String("attr", attr), + slog.Any("resp", resp), + ) return nil } resp, err := s.Attributes.DeactivateAttribute(cmd.Context(), &attributes.DeactivateAttributeRequest{ Id: auuid, }) if err != nil { - slog.Error("DeactivateAttribute", "err", err, "id", auuid) + slog.Error("failed to DeactivateAttribute", + slog.String("id", auuid), + slog.Any("error", err), + ) return err } - slog.Info("deactivated attribute", "attr", attr, "resp", resp) + slog.Info("deactivated attribute", + slog.String("attr", attr), + slog.Any("resp", resp), + ) return nil } @@ -286,19 +310,33 @@ func removeAttribute(cmd *cobra.Command) error { Fqn: strings.ToLower(attr + "/value/" + url.PathEscape(v)), }) if err != nil { - slog.Error("UnsafeDeleteAttributeValue", "err", err, "id", avu) + slog.Error("failed to UnsafeDeleteAttributeValue", + slog.Any("error", err), + slog.String("id", avu), + ) return err } - slog.Info("deactivated attribute value", "attr", attr, "value", v, "resp", r) + slog.Info("deactivated attribute value", + slog.String("attr", attr), + slog.String("value", v), + slog.Any("resp", r), + ) } else { r, err := s.Attributes.DeactivateAttributeValue(cmd.Context(), &attributes.DeactivateAttributeValueRequest{ Id: avu, }) if err != nil { - slog.Error("DeactivateAttributeValue", "err", err, "id", avu) + slog.Error("failed to DeactivateAttributeValue", + slog.String("id", avu), + slog.Any("error", err), + ) return err } - slog.Info("deactivated attribute value", "attr", attr, "value", v, "resp", r) + slog.Info("deactivated attribute value", + slog.String("attr", attr), + slog.String("value", v), + slog.Any("resp", r), + ) } } return nil @@ -325,7 +363,14 @@ func upsertAttr(ctx context.Context, s *sdk.SDK, auth, name string, values []str Values: values, }) if err != nil { - slog.Error("CreateAttribute", "err", err, "auth", auth, "name", name, "values", values, "rule", ruler()) + //nolint:sloglint // safe to log auth in examples + slog.Error("failed to CreateAttribute", + slog.String("auth", auth), + slog.String("name", name), + slog.Any("values", values), + slog.Any("rule", ruler()), + slog.Any("error", err), + ) return "", err } return av.GetAttribute().GetId(), nil diff --git a/examples/cmd/authorization.go b/examples/cmd/authorization.go index 27cb2e8465..d8a7f17136 100644 --- a/examples/cmd/authorization.go +++ b/examples/cmd/authorization.go @@ -63,12 +63,13 @@ func authorizationExamples() error { }) decisionRequest := &authorization.GetDecisionsRequest{DecisionRequests: drs} - slog.Info("Submitting decision request: " + protojson.Format(decisionRequest)) + //nolint:sloglint // safe to log request in example code + slog.Info("submitting decision", slog.String("request", protojson.Format(decisionRequest))) decisionResponse, err := s.Authorization.GetDecisions(context.Background(), decisionRequest) if err != nil { return err } - slog.Info("Received decision response: " + protojson.Format(decisionResponse)) + slog.Info("received decision response", slog.String("response", protojson.Format(decisionResponse))) // map response back to entity chain id decisionsByEntityChain := make(map[string]*authorization.DecisionResponse) @@ -76,8 +77,8 @@ func authorizationExamples() error { decisionsByEntityChain[dr.GetEntityChainId()] = dr } - slog.Info("decision for bob: " + protojson.Format(decisionsByEntityChain["ec1"])) - slog.Info("decision for alice: " + protojson.Format(decisionsByEntityChain["ec2"])) + slog.Info("decision for bob", slog.String("decision", protojson.Format(decisionsByEntityChain["ec1"]))) + slog.Info("decision for alice", slog.String("decision", protojson.Format(decisionsByEntityChain["ec2"]))) return nil } diff --git a/examples/cmd/kas.go b/examples/cmd/kas.go index bff3dcb094..b70d077b88 100644 --- a/examples/cmd/kas.go +++ b/examples/cmd/kas.go @@ -68,14 +68,14 @@ func init() { func listKases(cmd *cobra.Command) error { s, err := newSDK() if err != nil { - slog.Error("could not connect", "err", err) + slog.Error("could not connect", slog.Any("error", err)) return err } defer s.Close() r, err := s.KeyAccessServerRegistry.ListKeyAccessServers(cmd.Context(), &kasregistry.ListKeyAccessServersRequest{}) if err != nil { - slog.Error("ListKeyAccessServers", "error", err) + slog.Error("failed to ListKeyAccessServers", slog.Any("error", err)) return err } @@ -99,7 +99,7 @@ func listKases(cmd *cobra.Command) error { func upsertKasRegistration(ctx context.Context, s *sdk.SDK, uri string, pk *policy.PublicKey) (string, error) { r, err := s.KeyAccessServerRegistry.ListKeyAccessServers(ctx, &kasregistry.ListKeyAccessServersRequest{}) if err != nil { - slog.Error("ListKeyAccessServers", "err", err) + slog.Error("failed to ListKeyAccessServers", slog.Any("error", err)) return "", err } for _, ki := range r.GetKeyAccessServers() { @@ -118,7 +118,7 @@ func upsertKasRegistration(ctx context.Context, s *sdk.SDK, uri string, pk *poli } _, err := s.KeyAccessServerRegistry.DeleteKeyAccessServer(ctx, &kasregistry.DeleteKeyAccessServerRequest{Id: ki.GetId()}) if err != nil { - slog.Error("DeleteKeyAccessServer", "err", err) + slog.Error("failed to DeleteKeyAccessServer", slog.Any("error", err)) return "", err } // Do we have a unique constraint on kas uri? @@ -137,7 +137,11 @@ func upsertKasRegistration(ctx context.Context, s *sdk.SDK, uri string, pk *poli PublicKey: pk, }) if err != nil { - slog.Error("CreateKeyAccessServer", "uri", uri, "publicKey", uri+"/v2/kas_public_key") + slog.Error("failed to CreateKeyAccessServer", + slog.String("uri", uri), + slog.String("public_key", uri+"/v2/kas_public_key"), + slog.Any("error", err), + ) return "", err } return ur.GetKeyAccessServer().GetId(), nil @@ -156,7 +160,7 @@ func algString2Proto(a string) policy.KasPublicKeyAlgEnum { func updateKas(cmd *cobra.Command) error { s, err := newSDK() if err != nil { - slog.Error("could not connect", "err", err) + slog.Error("could not connect", slog.Any("error", err)) return err } defer s.Close() @@ -198,21 +202,25 @@ func updateKas(cmd *cobra.Command) error { if err != nil { return err } - slog.Info("registered kas", "passedin", attr, "id", kasid, "kas", kas) + slog.Info("registered kas", + slog.String("passedin", attr), + slog.String("id", kasid), + slog.String("kas", kas), + ) return nil } func removeKas(cmd *cobra.Command) error { s, err := newSDK() if err != nil { - slog.Error("could not connect", "err", err) + slog.Error("could not connect", slog.Any("error", err)) return err } defer s.Close() r, err := s.KeyAccessServerRegistry.ListKeyAccessServers(cmd.Context(), &kasregistry.ListKeyAccessServersRequest{}) if err != nil { - slog.Error("ListKeyAccessServers", "err", err) + slog.Error("failed to ListKeyAccessServers", slog.Any("error", err)) return err } deletedSomething := false @@ -220,7 +228,7 @@ func removeKas(cmd *cobra.Command) error { if strings.ToLower(kas) == strings.ToLower(ki.GetUri()) { _, err := s.KeyAccessServerRegistry.DeleteKeyAccessServer(cmd.Context(), &kasregistry.DeleteKeyAccessServerRequest{Id: ki.GetId()}) if err != nil { - slog.Error("DeleteKeyAccessServer", "err", err) + slog.Error("failed to DeleteKeyAccessServer", slog.Any("error", err)) return err } deletedSomething = true @@ -230,6 +238,6 @@ func removeKas(cmd *cobra.Command) error { return fmt.Errorf("nothing deleted; [%s] not found", kas) } - slog.Info("deleted kas registration", "kas", kas) + slog.Info("deleted kas registration", slog.String("kas", kas)) return nil } diff --git a/lib/fixtures/keycloak.go b/lib/fixtures/keycloak.go index f92762bb98..2017274cd6 100644 --- a/lib/fixtures/keycloak.go +++ b/lib/fixtures/keycloak.go @@ -84,7 +84,7 @@ func SetupKeycloak(ctx context.Context, kcConnectParams KeycloakConnectParams) e if err != nil { switch kcErrCode(err) { case http.StatusConflict: - slog.Info(fmt.Sprintf("⏭️ %s realm already exists, skipping create", kcConnectParams.Realm)) + slog.Info("realm already exists, skipping create", slog.String("realm", kcConnectParams.Realm)) case http.StatusNotFound: // yay! default: @@ -102,7 +102,8 @@ func SetupKeycloak(ctx context.Context, kcConnectParams KeycloakConnectParams) e if _, err := client.CreateRealm(ctx, token.AccessToken, realm); err != nil { return err } - slog.Info("✅ Realm created", slog.String("realm", kcConnectParams.Realm)) + //nolint:sloglint // allow existing emojis + slog.Info("✅ realm created", slog.String("realm", kcConnectParams.Realm)) // update realm users profile via upconfig realmProfileURL := fmt.Sprintf("%s/admin/realms/%s/users/profile", kcConnectParams.BasePath, kcConnectParams.Realm) @@ -120,9 +121,11 @@ func SetupKeycloak(ctx context.Context, kcConnectParams KeycloakConnectParams) e if err != nil { return err } - slog.Info("✅ Realm Users Profile Updated", slog.String("realm", kcConnectParams.Realm)) + //nolint:sloglint // allow existing emojis + slog.Info("✅ realm users profile updated", slog.String("realm", kcConnectParams.Realm)) } else { - slog.Info("⏭️ Realm already exists", slog.String("realm", kcConnectParams.Realm)) + //nolint:sloglint // allow existing emojis + slog.Info("⏭️ realm already exists", slog.String("realm", kcConnectParams.Realm)) } opentdfClientID := "opentdf" @@ -169,12 +172,14 @@ func SetupKeycloak(ctx context.Context, kcConnectParams KeycloakConnectParams) e if err != nil { switch kcErrCode(err) { case http.StatusConflict: - slog.Warn(fmt.Sprintf("⏭️ role %s already exists", role)) + //nolint:sloglint // allow existing emojis + slog.Warn("⏭️ role already exists", slog.String("role", role)) default: return err } } else { - slog.Info("✅ Role created: role = " + role) + //nolint:sloglint // allow existing emojis + slog.Info("✅ role created", slog.String("role", role)) } } @@ -189,7 +194,8 @@ func SetupKeycloak(ctx context.Context, kcConnectParams KeycloakConnectParams) e return err } - slog.Info(fmt.Sprintf("✅ Roles found: %d", len(realmRoles))) // , slog.String("roles", fmt.Sprintf("%v", realmRoles)) + //nolint:sloglint // allow existing emojis + slog.Info("✅ roles found", slog.Int("count", len(realmRoles))) for _, role := range realmRoles { switch *role.Name { case opentdfAdminRoleName: @@ -261,13 +267,13 @@ func SetupKeycloak(ctx context.Context, kcConnectParams KeycloakConnectParams) e err = client.AddOptionalScopeToClient(ctx, token.AccessToken, kcConnectParams.Realm, sdkNumericID, testScopeID) if err != nil { - slog.Error(fmt.Sprintf("Error adding scope to client: %s", err)) + slog.Error("error adding scope to client", slog.Any("error", err)) return err } err = client.CreateClientScopesScopeMappingsRealmRoles(ctx, token.AccessToken, kcConnectParams.Realm, testScopeID, []gocloak.Role{*testingOnlyRole}) if err != nil { - slog.Error(fmt.Sprintf("Error creating a client scope mapping: %s", err)) + slog.Error("error creating a client scope mapping", slog.Any("error", err)) return err } @@ -278,7 +284,7 @@ func SetupKeycloak(ctx context.Context, kcConnectParams KeycloakConnectParams) e } clientRolesToAdd, addErr := getClientRolesByList(ctx, &kcConnectParams, client, token, *realmManagementClientID, []string{"view-clients", "query-clients", "view-users", "query-users"}) if addErr != nil { - slog.Error(fmt.Sprintf("Error getting client roles : %s", err)) + slog.Error("error getting client roles", slog.Any("error", err)) return err } _, err = createClient(ctx, client, token, &kcConnectParams, gocloak.Client{ @@ -310,7 +316,7 @@ func SetupKeycloak(ctx context.Context, kcConnectParams KeycloakConnectParams) e // opentdfSdkClientNumericId, err := getIDOfClient(ctx, client, token, &kcConnectParams, &opentdfClientId) // if err != nil { - // slog.Error(fmt.Sprintf("Error getting the SDK id: %s", err)) + // slog.Error("error getting the sdk id", slog.Any("error", err)) // return err // } @@ -485,7 +491,7 @@ func keycloakLogin(ctx context.Context, connectParams *KeycloakConnectParams) (* // Get Token from master token, err := client.LoginAdmin(ctx, connectParams.Username, connectParams.Password, "master") if err != nil { - slog.Error(fmt.Sprintf("Error logging into keycloak: %s", err)) + slog.Error("error logging into keycloak", slog.Any("error", err)) } return client, token, err } @@ -505,13 +511,13 @@ func createRealm(ctx context.Context, kcConnectParams KeycloakConnectParams, rea case http.StatusNotFound: // yes case http.StatusConflict: - slog.Info(fmt.Sprintf("⏭️ %s realm already exists, skipping create", *realm.Realm)) + slog.Info("realm already exists, skipping create", slog.String("realm", *realm.Realm)) default: return err } } else if err != nil { if kcErr.Code == http.StatusConflict { - slog.Info(fmt.Sprintf("⏭️ %s realm already exists, skipping create", *realm.Realm)) + slog.Info("realm already exists, skipping create", slog.String("realm", *realm.Realm)) } else if kcErr.Code != http.StatusNotFound { return err } @@ -521,16 +527,18 @@ func createRealm(ctx context.Context, kcConnectParams KeycloakConnectParams, rea if _, err := client.CreateRealm(ctx, token.AccessToken, realm); err != nil { return err } - slog.Info("✅ Realm created", slog.String("realm", *realm.Realm)) + //nolint:sloglint // allow existing emojis + slog.Info("✅ realm created", slog.String("realm", *realm.Realm)) } else { - slog.Info("⏭️ Realm already exists", slog.String("realm", *realm.Realm)) + //nolint:sloglint // allow existing emojis + slog.Info("⏭️ realm already exists", slog.String("realm", *realm.Realm)) } // update realm users profile via upconfig realmProfileURL := fmt.Sprintf("%s/admin/realms/%s/users/profile", kcConnectParams.BasePath, *realm.Realm) realmUserProfileResp, err := client.GetRequestWithBearerAuth(ctx, token.AccessToken).Get(realmProfileURL) if err != nil { - slog.Error("Error retrieving realm users profile ", slog.String("realm", *realm.Realm)) + slog.Error("error retrieving realm users profile", slog.String("realm", *realm.Realm)) return err } var upConfig map[string]interface{} @@ -543,7 +551,8 @@ func createRealm(ctx context.Context, kcConnectParams KeycloakConnectParams, rea if err != nil { return err } - slog.Info("✅ Realm Users Profile Updated", slog.String("realm", *realm.Realm)) + //nolint:sloglint // allow existing emojis + slog.Info("✅ realm users profile updated", slog.String("realm", *realm.Realm)) return nil } @@ -556,12 +565,14 @@ func createGroup(ctx context.Context, client *gocloak.GoCloak, token *gocloak.JW if err != nil { kcErr := err.(*gocloak.APIError) //nolint:errcheck,errorlint,forcetypeassert // kc error checked below if kcErr.Code == http.StatusConflict { - slog.Warn(fmt.Sprintf("⏭️ group %s already exists", *group.Name)) + //nolint:sloglint // allow existing emojis + slog.Warn("⏭️ group already exists", slog.String("group", *group.Name)) } else { return err } } else { - slog.Info("✅ Group created: group = " + *group.Name) + //nolint:sloglint // allow existing emojis + slog.Info("✅ group created", slog.String("group", *group.Name)) } return nil } @@ -574,12 +585,14 @@ func createRealmRole(ctx context.Context, client *gocloak.GoCloak, token *gocloa if err != nil { kcErr := err.(*gocloak.APIError) //nolint:errcheck,errorlint,forcetypeassert // kc error checked below if kcErr.Code == http.StatusConflict { - slog.Warn(fmt.Sprintf("⏭️ role %s already exists", *role.Name)) + //nolint:sloglint // allow existing emojis + slog.Warn("⏭️ role already exists", slog.String("role", *role.Name)) } else { return err } } else { - slog.Info("✅ Role created: role = " + *role.Name) + //nolint:sloglint // allow existing emojis + slog.Info("✅ role created", slog.String("role", *role.Name)) } return nil } @@ -590,7 +603,9 @@ func createClientRole(ctx context.Context, client *gocloak.GoCloak, token *goclo } results, err := client.GetClients(ctx, token.AccessToken, realmName, gocloak.GetClientsParams{ClientID: &clientID}) if err != nil || len(results) == 0 { - slog.Error(fmt.Sprintf("Error getting %s's client: %s", clientID, err)) + slog.Error("error getting client", + slog.String("client_id", clientID), + slog.Any("error", err)) return err } idOfClient := results[0].ID @@ -599,12 +614,18 @@ func createClientRole(ctx context.Context, client *gocloak.GoCloak, token *goclo if err != nil { kcErr := err.(*gocloak.APIError) //nolint:errcheck,errorlint,forcetypeassert // kc error checked below if kcErr.Code == http.StatusConflict { - slog.Warn(fmt.Sprintf("⏭️ role %s already exists for client %s", *role.Name, clientID)) + //nolint:sloglint // allow existing emojis + slog.Warn("⏭️ role already exists for client", + slog.String("role", *role.Name), + slog.String("client_id", clientID)) } else { return err } } else { - slog.Info(fmt.Sprintf("✅ Client role created for client %s: role = %s", clientID, *role.Name)) + //nolint:sloglint // allow existing emojis + slog.Info("✅ client role created", + slog.String("client_id", clientID), + slog.String("role", *role.Name)) } return nil } @@ -617,7 +638,8 @@ func createClient(ctx context.Context, client *gocloak.GoCloak, token *gocloak.J if err != nil { switch kcErrCode(err) { case http.StatusConflict: - slog.Warn(fmt.Sprintf("⏭️ client %s already exists", clientID)) + //nolint:sloglint // allow existing emojis + slog.Warn("⏭️ client already exists", slog.String("client_id", clientID)) clients, err := client.GetClients(ctx, token.AccessToken, connectParams.Realm, gocloak.GetClientsParams{ClientID: newClient.ClientID}) if err != nil { return "", err @@ -629,11 +651,16 @@ func createClient(ctx context.Context, client *gocloak.GoCloak, token *gocloak.J return "", err } default: - slog.Error(fmt.Sprintf("❗️ Error creating client %s : %s", clientID, err)) + slog.Error("error creating client", + slog.String("client_id", clientID), + slog.Any("error", err)) return "", err } } else { - slog.Info(fmt.Sprintf("✅ Client created: client id = %s, client identifier=%s", clientID, longClientID)) + //nolint:sloglint // allow existing emojis + slog.Info("✅ client created", + slog.String("client_id", clientID), + slog.String("client_identifier", longClientID)) } // if the client is not public @@ -641,34 +668,50 @@ func createClient(ctx context.Context, client *gocloak.GoCloak, token *gocloak.J // Get service account user user, err := client.GetClientServiceAccount(ctx, token.AccessToken, connectParams.Realm, longClientID) if err != nil { - slog.Error(fmt.Sprintf("Error getting service account user for client %s : %s", clientID, err)) + slog.Error("error getting service account user for client", + slog.String("client_id", clientID), + slog.Any("error", err)) return "", err } - slog.Info(fmt.Sprintf("ℹ️ Service account user for client %s : %s", clientID, *user.Username)) + slog.Info("ℹ️ service account user for client", + slog.String("client_id", clientID), + slog.String("username", *user.Username)) if realmRoles != nil { - slog.Info(fmt.Sprintf("Adding realm roles to client %s via service account %s", longClientID, *user.Username)) + //nolint:sloglint // allow existing emojis + slog.Info("⏭️ adding realm roles to client via service account", + slog.String("client_id", longClientID), + slog.String("username", *user.Username)) if err := client.AddRealmRoleToUser(ctx, token.AccessToken, connectParams.Realm, *user.ID, realmRoles); err != nil { for _, role := range realmRoles { - slog.Warn("Error adding role " + *role.Name) + slog.Warn("error adding role", slog.String("role", *role.Name)) } return "", err } for _, role := range realmRoles { - slog.Info(fmt.Sprintf("✅ Realm Role %s added to client %s", *role.Name, longClientID)) + //nolint:sloglint // allow existing emojis + slog.Info("✅ realm role added to client", + slog.String("role", *role.Name), + slog.String("client_id", longClientID)) } } if clientRoles != nil { - slog.Info(fmt.Sprintf("Adding client roles to client %s via service account %s", longClientID, *user.Username)) + //nolint:sloglint // allow existing emojis + slog.Info("⏭️ adding client roles to client via service account", + slog.String("client_id", longClientID), + slog.String("username", *user.Username)) for clientIDRole, roles := range clientRoles { if err := client.AddClientRolesToUser(ctx, token.AccessToken, connectParams.Realm, clientIDRole, *user.ID, roles); err != nil { for _, role := range roles { - slog.Warn("Error adding role " + *role.Name) + slog.Warn("error adding role", slog.String("role", *role.Name)) } return "", err } for _, role := range roles { - slog.Info(fmt.Sprintf("✅ Client Role %s added to client %s", *role.Name, longClientID)) + //nolint:sloglint // allow existing emojis + slog.Info("✅ client role added to client", + slog.String("role", *role.Name), + slog.String("client_id", longClientID)) } } } @@ -682,7 +725,7 @@ func createUser(ctx context.Context, client *gocloak.GoCloak, token *gocloak.JWT if err != nil { switch kcErrCode(err) { case http.StatusConflict: - slog.Warn(fmt.Sprintf("user %s already exists", username)) + slog.Warn("user already exists", slog.String("username", username)) users, err := client.GetUsers(ctx, token.AccessToken, connectParams.Realm, gocloak.GetUsersParams{Username: newUser.Username}) if err != nil { return nil, err @@ -694,11 +737,16 @@ func createUser(ctx context.Context, client *gocloak.GoCloak, token *gocloak.JWT return nil, err } default: - slog.Error(fmt.Sprintf("Error creating user %s : %s", username, err)) + slog.Error("error creating user", + slog.String("username", username), + slog.Any("error", err)) return nil, err } } else { - slog.Info(fmt.Sprintf("✅ User created: username = %s, user identifier=%s", username, longUserID)) + //nolint:sloglint // allow existing emojis + slog.Info("✅ user created", + slog.String("username", username), + slog.String("user_identifier", longUserID)) } // assign realm roles to user // retrieve the roles by name @@ -709,7 +757,9 @@ func createUser(ctx context.Context, client *gocloak.GoCloak, token *gocloak.JWT } err = client.AddRealmRoleToUser(ctx, token.AccessToken, connectParams.Realm, longUserID, roles) if err != nil { - slog.Error(fmt.Sprintf("Error adding realm roles to user %s : %s", *newUser.RealmRoles, connectParams.Realm)) + slog.Error("error adding realm roles to user", + slog.Any("roles", *newUser.RealmRoles), + slog.String("realm", connectParams.Realm)) return nil, err } } @@ -718,25 +768,30 @@ func createUser(ctx context.Context, client *gocloak.GoCloak, token *gocloak.JWT for clientID, roles := range *newUser.ClientRoles { results, err := client.GetClients(ctx, token.AccessToken, connectParams.Realm, gocloak.GetClientsParams{ClientID: &clientID}) if err != nil || len(results) == 0 { - slog.Error(fmt.Sprintf("Error getting %s's client: %s", clientID, err)) + slog.Error("error getting client", + slog.String("client_id", clientID), + slog.Any("error", err)) return nil, err } idOfClient := results[0].ID clientRoles, err := getClientRolesByList(ctx, connectParams, client, token, *idOfClient, roles) if err != nil { - slog.Error(fmt.Sprintf("Error getting client roles: %s", err)) + slog.Error("error getting client roles", slog.Any("error", err)) return nil, err } if err := client.AddClientRolesToUser(ctx, token.AccessToken, connectParams.Realm, *idOfClient, longUserID, clientRoles); err != nil { for _, role := range clientRoles { - slog.Warn("Error adding role " + *role.Name) + slog.Warn("error adding role", slog.String("role", *role.Name)) } return nil, err } for _, role := range clientRoles { - slog.Info(fmt.Sprintf("✅ Client Role %s added to user %s", *role.Name, longUserID)) + //nolint:sloglint // allow existing emojis + slog.Info("✅ client role added to user", + slog.String("role", *role.Name), + slog.String("user_id", longUserID)) } } } @@ -753,7 +808,9 @@ func getRealmRolesByList(ctx context.Context, realmName string, client *gocloak. realmName, roleName) if err != nil { - slog.Error(fmt.Sprintf("Error getting realm role for realm %s : %s", roleName, realmName)) + slog.Error("error getting realm role for realm", + slog.String("role", roleName), + slog.String("realm", realmName)) return nil, err } roles = append(roles, *role) @@ -791,7 +848,7 @@ searchRole: func getIDOfClient(ctx context.Context, client *gocloak.GoCloak, token *gocloak.JWT, connectParams *KeycloakConnectParams, clientName *string) (*string, error) { results, err := client.GetClients(ctx, token.AccessToken, connectParams.Realm, gocloak.GetClientsParams{ClientID: clientName}) if err != nil || len(results) == 0 { - slog.Error(fmt.Sprintf("Error getting realm management client: %s", err)) + slog.Error("error getting realm management client", slog.Any("error", err)) return nil, err } clientID := results[0].ID @@ -813,23 +870,27 @@ func createTokenExchange(ctx context.Context, connectParams *KeycloakConnectPara connectParams.Realm, *idForTargetClientID, gocloak.ManagementPermissionRepresentation{Enabled: &enabled}) if err != nil { - slog.Error(fmt.Sprintf("Error creating management permissions : %s", err)) + slog.Error("error creating management permissions", slog.Any("error", err)) return err } tokenExchangePolicyPermissionResourceID := mgmtPermissionsRepr.Resource scopePermissions := *mgmtPermissionsRepr.ScopePermissions tokenExchangePolicyScopePermissionID := scopePermissions["token-exchange"] - slog.Debug(fmt.Sprintf("Creating management permission: resource = %s , scope permission id = %s", *tokenExchangePolicyPermissionResourceID, tokenExchangePolicyScopePermissionID)) + slog.Debug("creating management permission", + slog.String("resource", *tokenExchangePolicyPermissionResourceID), + slog.String("scope_permission_id", tokenExchangePolicyScopePermissionID)) - slog.Debug("Step 2 - Get realm mgmt client id") + slog.Debug("step 2 - get realm mgmt client id") realmMangementClientName := "realm-management" realmManagementClientID, err := getIDOfClient(ctx, client, token, connectParams, &realmMangementClientName) if err != nil { return err } - slog.Debug(fmt.Sprintf("%s client id=%s", realmMangementClientName, *realmManagementClientID)) + slog.Debug("client information", + slog.String("client_name", realmMangementClientName), + slog.String("client_id", *realmManagementClientID)) - slog.Debug("Step 3 - Add policy for token exchange") + slog.Debug("step 3 - add policy for token exchange") policyType := "client" policyName := fmt.Sprintf("%s-%s-exchange-policy", targetClientID, startClientID) realmMgmtExchangePolicyRepresentation := gocloak.PolicyRepresentation{ @@ -845,20 +906,22 @@ func createTokenExchange(ctx context.Context, connectParams *KeycloakConnectPara if err != nil { switch kcErrCode(err) { case http.StatusConflict: - slog.Warn(fmt.Sprintf("⏭️ policy %s already exists; skipping remainder of token exchange creation", *realmMgmtExchangePolicyRepresentation.Name)) + //nolint:sloglint // allow existing emojis + slog.Warn("⏭️ policy already exists; skipping remainder of token exchange creation", slog.String("policy", *realmMgmtExchangePolicyRepresentation.Name)) return nil default: - slog.Error(fmt.Sprintf("Error create realm management policy: %s", err)) + slog.Error("error creating realm management policy", slog.Any("error", err)) return err } } tokenExchangePolicyID := realmMgmtPolicy.ID - slog.Info("✅ Created Token Exchange Policy " + *tokenExchangePolicyID) + //nolint:sloglint // allow existing emojis + slog.Info("✅ created token exchange policy", slog.String("policy_id", *tokenExchangePolicyID)) - slog.Debug("Step 4 - Get Token Exchange Scope Identifier") + slog.Debug("step 4 - get token exchange scope identifier") resourceRep, err := client.GetResource(ctx, token.AccessToken, connectParams.Realm, *realmManagementClientID, *tokenExchangePolicyPermissionResourceID) if err != nil { - slog.Error(fmt.Sprintf("Error getting resource : %s", err)) + slog.Error("error getting resource", slog.Any("error", err)) return err } var tokenExchangeScopeID *string @@ -871,7 +934,8 @@ func createTokenExchange(ctx context.Context, connectParams *KeycloakConnectPara if tokenExchangeScopeID == nil { return errors.New("no token exchange scope found") } - slog.Debug("Token exchange scope id =" + *tokenExchangeScopeID) + slog.Debug("token exchange scope information", + slog.String("scope_id", *tokenExchangeScopeID)) clientPermissionName := "token-exchange.permission.client." + *idForTargetClientID clientType := "Scope" @@ -890,7 +954,7 @@ func createTokenExchange(ctx context.Context, connectParams *KeycloakConnectPara } if err := client.UpdatePermissionScope(ctx, token.AccessToken, connectParams.Realm, *realmManagementClientID, tokenExchangePolicyScopePermissionID, permissionScopePolicyRepresentation); err != nil { - slog.Error("Error creating permission scope", "error", err) + slog.Error("error creating permission scope", slog.Any("error", err)) return err } return nil @@ -917,10 +981,11 @@ func createCertExchange(ctx context.Context, connectParams *KeycloakConnectParam }); err != nil { switch kcErrCode(err) { case http.StatusConflict: - slog.Warn(fmt.Sprintf("⏭️ authentication flow %s already exists; skipping remainder of cert exchange creation", topLevelFlowName)) + //nolint:sloglint // allow existing emojis + slog.Warn("⏭️ authentication flow already exists; skipping remainder of cert exchange creation", slog.String("flow_name", topLevelFlowName)) return nil default: - slog.Error(fmt.Sprintf("Error create realm certificate authentication flow: %s", err)) + slog.Error("error creating realm certificate authentication flow", slog.Any("error", err)) return err } } @@ -930,18 +995,18 @@ func createCertExchange(ctx context.Context, connectParams *KeycloakConnectParam topLevelFlowName, gocloak.CreateAuthenticationExecutionRepresentation{ Provider: &provider, }); err != nil { - slog.Error(fmt.Sprintf("Error create realm management policy: %s", err)) + slog.Error("error creating realm management policy", slog.Any("error", err)) return err } authExecutions, err := client.GetAuthenticationExecutions(ctx, token.AccessToken, connectParams.Realm, topLevelFlowName) if err != nil { - slog.Error(fmt.Sprintf("Error gettings executions %s", err)) + slog.Error("error gettings executions", slog.Any("error", err)) return err } if len(authExecutions) != 1 { err = fmt.Errorf("expected a single flow execution for %s", topLevelFlowName) - slog.Error("Error setting up authentication flow", "error", err) + slog.Error("error setting up authentication flow", slog.Any("error", err)) return err } @@ -970,13 +1035,19 @@ func createCertExchange(ctx context.Context, connectParams *KeycloakConnectParam config["x509-cert-auth.certificate-policy-mode"] = "All" executionConfig["config"] = config if err := updateExecutionConfig(ctx, client, execution, connectParams, token.AccessToken, executionConfig); err != nil { - slog.Error(fmt.Sprintf("Error updating x509 auth flow configs %s : %s", clientID, err)) + slog.Error("error updating x509 auth flow configs", + slog.String("client_id", clientID), + slog.Any("error", err), + ) return err } execution.Requirement = &requiredRequirement if err := client.UpdateAuthenticationExecution(ctx, token.AccessToken, connectParams.Realm, topLevelFlowName, *execution); err != nil { - slog.Error(fmt.Sprintf("Error updating x509 auth flow requjirement %s : %s", clientID, err)) + slog.Error("error updating x509 auth flow requjirement", + slog.String("client_id", clientID), + slog.Any("error", err), + ) return err } @@ -1008,11 +1079,17 @@ func createCertExchange(ctx context.Context, connectParams *KeycloakConnectParam flowBindings["direct_grant"] = *flowID updatedClient.AuthenticationFlowBindingOverrides = &flowBindings if err := client.UpdateClient(ctx, token.AccessToken, connectParams.Realm, *updatedClient); err != nil { - slog.Error(fmt.Sprintf("Error updating client auth flow binding overrides %s : %s", clientID, err)) + slog.Error("error updating client auth flow binding overrides", + slog.String("client_id", clientID), + slog.Any("error", err), + ) return err } - slog.Info("✅ Created Cert Exchange Authentication " + *flowID) + //nolint:sloglint // allow existing emojis + slog.Info("✅ created Cert Exchange Authentication", + slog.String("flow_id", *flowID), + ) return nil } diff --git a/sdk/auth/oauth/oauth.go b/sdk/auth/oauth/oauth.go index e29b1670d8..a1d8ab5fea 100644 --- a/sdk/auth/oauth/oauth.go +++ b/sdk/auth/oauth/oauth.go @@ -196,7 +196,7 @@ func processResponse(resp *http.Response) (*Token, error) { } func getDPoPAssertion(dpopJWK jwk.Key, method string, endpoint string, nonce string) (string, error) { - slog.Debug("Building DPoP Proof") + slog.Debug("building DPoP Proof") publicKey, err := jwk.PublicKeyOf(dpopJWK) const expirationTime = 5 * time.Minute diff --git a/sdk/auth/token_adding_interceptor.go b/sdk/auth/token_adding_interceptor.go index 95f7f0620f..afb9a4749c 100644 --- a/sdk/auth/token_adding_interceptor.go +++ b/sdk/auth/token_adding_interceptor.go @@ -60,7 +60,7 @@ func (i TokenAddingInterceptor) AddCredentials( if err == nil { newMetadata = append(newMetadata, "Authorization", fmt.Sprintf("DPoP %s", accessToken)) } else { - slog.ErrorContext(ctx, "error getting access token", "error", err) + slog.ErrorContext(ctx, "error getting access token", slog.Any("error", err)) return status.Error(codes.Unauthenticated, err.Error()) } @@ -71,7 +71,7 @@ func (i TokenAddingInterceptor) AddCredentials( // since we don't have a setting about whether DPoP is in use on the client and this request _could_ succeed if // they are talking to a server where DPoP is not required we will just let this through. this method is extremely // unlikely to fail so hopefully this isn't confusing - slog.ErrorContext(ctx, "error getting DPoP token for outgoing request. Request will not have DPoP token", "error", err) + slog.ErrorContext(ctx, "error getting DPoP token for outgoing request. Request will not have DPoP token", slog.Any("error", err)) } newCtx := metadata.AppendToOutgoingContext(ctx, newMetadata...) @@ -91,7 +91,7 @@ func (i TokenAddingInterceptor) AddCredentialsConnect() connect.UnaryInterceptor ) (connect.AnyResponse, error) { accessToken, err := i.tokenSource.AccessToken(ctx, i.httpClient) if err != nil { - slog.ErrorContext(ctx, "error getting access token", "error", err) + slog.ErrorContext(ctx, "error getting access token", slog.Any("error", err)) return nil, connect.NewError(connect.CodeUnauthenticated, err) } @@ -106,7 +106,7 @@ func (i TokenAddingInterceptor) AddCredentialsConnect() connect.UnaryInterceptor // since we don't have a setting about whether DPoP is in use on the client and this request _could_ succeed if // they are talking to a server where DPoP is not required we will just let this through. this method is extremely // unlikely to fail so hopefully this isn't confusing - slog.ErrorContext(ctx, "error getting DPoP token for outgoing request. Request will not have DPoP token", "error", err) + slog.ErrorContext(ctx, "error getting DPoP token for outgoing request. Request will not have DPoP token", slog.Any("error", err)) } // Proceed with the RPC diff --git a/sdk/bulk.go b/sdk/bulk.go index 20e1421d47..7130cb6acf 100644 --- a/sdk/bulk.go +++ b/sdk/bulk.go @@ -141,7 +141,7 @@ func (s SDK) BulkDecrypt(ctx context.Context, opts ...BulkDecryptOption) error { bulkReq.NanoTDFDecryptOptions = append(bulkReq.NanoTDFDecryptOptions, withNanoKasAllowlist(bulkReq.kasAllowlist)) bulkReq.TDF3DecryptOptions = append(bulkReq.TDF3DecryptOptions, withKasAllowlist(bulkReq.kasAllowlist)) } else { - slog.Error("No KAS allowlist provided and no KeyAccessServerRegistry available") + slog.Error("no KAS allowlist provided and no KeyAccessServerRegistry available") return errors.New("no KAS allowlist provided and no KeyAccessServerRegistry available") } } @@ -172,7 +172,7 @@ func (s SDK) BulkDecrypt(ctx context.Context, opts ...BulkDecryptOption) error { var err error for kasurl, rewrapRequests := range kasRewrapRequests { if bulkReq.ignoreAllowList { - slog.Warn(fmt.Sprintf("KasAllowlist is ignored, kas url %s is allowed", kasurl)) + slog.Warn("kasAllowlist is ignored, kas url is allowed", slog.String("kas_url", kasurl)) } else if !bulkReq.kasAllowlist.IsAllowed(kasurl) { // if kas url is not allowed, the result for each kao in each rewrap request is set to error for _, req := range rewrapRequests { diff --git a/sdk/codegen/runner/generate.go b/sdk/codegen/runner/generate.go index 46cfa3ebcb..ce5cc2156a 100644 --- a/sdk/codegen/runner/generate.go +++ b/sdk/codegen/runner/generate.go @@ -20,7 +20,10 @@ type ClientsToGenerate struct { func Generate(clientsToGenerateList []ClientsToGenerate, outputDir string) error { for _, client := range clientsToGenerateList { - slog.Info("Generating wrapper for", "interface", client.GrpcClientInterface, "package", client.GrpcPackagePath) + slog.Info("generating wrapper for", + slog.String("interface", client.GrpcClientInterface), + slog.String("package", client.GrpcPackagePath), + ) // Load the Go package using the import path cfg := &packages.Config{ Mode: packages.NeedName | @@ -62,7 +65,10 @@ func Generate(clientsToGenerateList []ClientsToGenerate, outputDir string) error outputPath := filepath.Join(outputDir, packageName+".go") err = os.WriteFile(outputPath, []byte(code), 0o644) //nolint:gosec // ignore G306 if err != nil { - slog.Error("Error writing file", "file", outputPath, "error", err) + slog.Error("error writing file", + slog.String("file", outputPath), + slog.Any("error", err), + ) } found = true return false // stop traversal diff --git a/sdk/granter.go b/sdk/granter.go index 18075c5145..6dfa568f55 100644 --- a/sdk/granter.go +++ b/sdk/granter.go @@ -222,7 +222,10 @@ func (r *granter) addGrant(fqn AttributeValueFQN, kas string, attr *policy.Attri func (r *granter) addMappedKey(fqn AttributeValueFQN, sk *policy.SimpleKasKey) error { key := sk.GetPublicKey() if key == nil || key.GetKid() == "" || key.GetPem() == "" { - slog.Debug("invalid cached key in policy service", "kas", sk.GetKasUri(), "value", fqn) + slog.Debug("invalid cached key in policy service", + slog.String("kas", sk.GetKasUri()), + slog.Any("value", fqn), + ) return fmt.Errorf("invalid cached key in policy service associated with [%s]", fqn) } if r.mapTable == nil { @@ -236,11 +239,20 @@ func (r *granter) addMappedKey(fqn AttributeValueFQN, sk *policy.SimpleKasKey) e rl, err := NewResourceLocator(sk.GetKasUri()) if err != nil { - slog.Debug("invalid KAS URL in policy service", "kas", sk.GetKasUri(), "value", fqn, "error", err) + slog.Debug("invalid KAS URL in policy service", + slog.String("kas", sk.GetKasUri()), + slog.Any("value", fqn), + slog.Any("error", err), + ) return fmt.Errorf("invalid KAS URL in policy service associated with [%s]: %w", fqn, err) } rl.identifier = key.GetKid() - slog.Debug("added mapped key", "fqn", fqn, "kas", sk.GetKasUri(), "kid", key.GetKid(), "alg", algProto2String(policy.KasPublicKeyAlgEnum(key.GetAlgorithm()))) + slog.Debug("added mapped key", + slog.Any("fqn", fqn), + slog.String("kas", sk.GetKasUri()), + slog.String("kid", key.GetKid()), + slog.String("alg", algProto2String(policy.KasPublicKeyAlgEnum(key.GetAlgorithm()))), + ) rls = append(rls, rl) r.mapTable[fqn.key] = rls return nil @@ -286,7 +298,10 @@ func (r *granter) addAllGrants(fqn AttributeValueFQN, ag grantableObject, attr * // Check for mapped keys for _, k := range ag.GetKasKeys() { if k == nil || k.GetKasUri() == "" { - slog.Debug("invalid KAS key in policy service", "simpleKasKey", k, "value", fqn) + slog.Debug("invalid KAS key in policy service", + slog.Any("simple_kas_key", k), + slog.Any("value", fqn), + ) continue } kasURI := k.GetKasUri() @@ -294,7 +309,11 @@ func (r *granter) addAllGrants(fqn AttributeValueFQN, ag grantableObject, attr * result = r.typ err := r.addMappedKey(fqn, k) if err != nil { - slog.Debug("failed to add mapped key", "fqn", fqn, "kas", kasURI, "error", err) + slog.Debug("failed to add mapped key", + slog.Any("fqn", fqn), + slog.String("kas", kasURI), + slog.Any("error", err), + ) } if _, present := r.grantTable[fqn.key]; !present { r.grantTable[fqn.key] = &keyAccessGrant{attr, []string{kasURI}} @@ -316,19 +335,30 @@ func (r *granter) addAllGrants(fqn AttributeValueFQN, ag grantableObject, attr * for _, k := range g.GetKasKeys() { err := r.addMappedKey(fqn, k) if err != nil { - slog.Warn("failed to add mapped key", "fqn", fqn, "kas", kasURI, "error", err) + slog.Warn("failed to add mapped key", + slog.Any("fqn", fqn), + slog.String("kas", kasURI), + slog.Any("error", err), + ) } } continue } ks := g.GetPublicKey().GetCached().GetKeys() if len(ks) == 0 { - slog.Debug("no cached key in policy service", "kas", kasURI, "value", fqn) + slog.Debug("no cached key in policy service", + slog.String("kas", kasURI), + slog.Any("value", fqn), + ) continue } for _, k := range ks { if k.GetKid() == "" || k.GetPem() == "" { - slog.Debug("invalid cached key in policy service", "kas", kasURI, "value", fqn, "key", k) + slog.Debug("invalid cached key in policy service", + slog.String("kas", kasURI), + slog.Any("value", fqn), + slog.Any("key", k), + ) continue } sk := &policy.SimpleKasKey{ @@ -342,7 +372,11 @@ func (r *granter) addAllGrants(fqn AttributeValueFQN, ag grantableObject, attr * } err := r.addMappedKey(fqn, sk) if err != nil { - slog.Warn("failed to add mapped key", "fqn", fqn, "kas", kasURI, "error", err) + slog.Warn("failed to add mapped key", + slog.Any("fqn", fqn), + slog.String("kas", kasURI), + slog.Any("error", err), + ) } } } @@ -433,7 +467,7 @@ func storeKeysToCache(kases []*policy.KeyAccessServer, keys []*policy.SimpleKasK for _, kas := range kases { keys := kas.GetPublicKey().GetCached().GetKeys() if len(keys) == 0 { - slog.Debug("no cached key in policy service", "kas", kas.GetUri()) + slog.Debug("no cached key in policy service", slog.String("kas", kas.GetUri())) continue } for _, ki := range keys { @@ -699,7 +733,11 @@ func (r *granter) insertKeysForAttribute(e attributeBooleanExpression) (booleanK var err error rl, err = NewResourceLocator(kas) if err != nil { - slog.Warn("invalid KAS URL in policy service", "kas", kas, "value", term, "error", err) + slog.Warn("invalid KAS URL in policy service", + slog.String("kas", kas), + slog.Any("value", term), + slog.Any("error", err), + ) return booleanKeyExpression{}, fmt.Errorf("invalid KAS URL in policy service associated with [%s]: %w", term, err) } } @@ -708,7 +746,7 @@ func (r *granter) insertKeysForAttribute(e attributeBooleanExpression) (booleanK } op := ruleToOperator(clause.def.GetRule()) if op == unspecified { - slog.Warn("unknown attribute rule type", "rule", clause) + slog.Warn("unknown attribute rule type", slog.Any("rule", clause)) } kc := keyClause{ operator: op, @@ -737,7 +775,7 @@ func (r *granter) assignKeysTo(e attributeBooleanExpression) (booleanKeyExpressi } op := ruleToOperator(clause.def.GetRule()) if op == unspecified { - slog.Warn("unknown attribute rule type", "rule", clause) + slog.Warn("unknown attribute rule type", slog.Any("rule", clause)) } kc := keyClause{ operator: op, diff --git a/sdk/nanotdf.go b/sdk/nanotdf.go index 9889e73c14..32d46d0c50 100644 --- a/sdk/nanotdf.go +++ b/sdk/nanotdf.go @@ -96,7 +96,7 @@ func NewNanoTDFHeaderFromReader(reader io.Reader) (NanoTDFHeader, uint32, error) size += uint32(resource.getLength()) header.kasURL = *resource - slog.Debug("NewNanoTDFHeaderFromReader", slog.Uint64("resource locator", uint64(resource.getLength()))) + slog.Debug("checkpoint NewNanoTDFHeaderFromReader", slog.Uint64("resource_locator", uint64(resource.getLength()))) // Read ECC and Binding Mode oneBytes := make([]byte, 1) @@ -141,7 +141,7 @@ func NewNanoTDFHeaderFromReader(reader io.Reader) (NanoTDFHeader, uint32, error) } size += uint32(l) policyLength := binary.BigEndian.Uint16(twoBytes) - slog.Debug("NewNanoTDFHeaderFromReader", slog.Uint64("policyLength", uint64(policyLength))) + slog.Debug("checkpoint NewNanoTDFHeaderFromReader", slog.Uint64("policy_length", uint64(policyLength))) // Read policy body header.PolicyMode = policyMode @@ -204,7 +204,7 @@ func NewNanoTDFHeaderFromReader(reader io.Reader) (NanoTDFHeader, uint32, error) size += uint32(l) header.EphemeralKey = ephemeralKey - slog.Debug("NewNanoTDFHeaderFromReader", slog.Uint64("header size", uint64(size))) + slog.Debug("checkpoint NewNanoTDFHeaderFromReader", slog.Uint64("header_size", uint64(size))) return header, size, nil } @@ -275,12 +275,12 @@ func (ep embeddedPolicy) writeEmbeddedPolicy(writer io.Writer) error { if _, err := writer.Write(buf); err != nil { return err } - slog.Debug("writeEmbeddedPolicy", slog.Uint64("policy length", uint64(ep.lengthBody))) + slog.Debug("writeEmbeddedPolicy", slog.Uint64("policy_length", uint64(ep.lengthBody))) if _, err := writer.Write(ep.body); err != nil { return err } - slog.Debug("writeEmbeddedPolicy", slog.Uint64("policy body", uint64(len(ep.body)))) + slog.Debug("writeEmbeddedPolicy", slog.Uint64("policy_body", uint64(len(ep.body)))) return nil } @@ -617,7 +617,7 @@ func writeNanoTDFHeader(writer io.Writer, config NanoTDFConfig) ([]byte, uint32, } totalBytes += uint32(l) - slog.Debug("writeNanoTDFHeader", slog.Uint64("magic number", uint64(len(kNanoTDFMagicStringAndVersion)))) + slog.Debug("writeNanoTDFHeader", slog.Uint64("magic_number", uint64(len(kNanoTDFMagicStringAndVersion)))) // Write the kas url err = config.kasURL.writeResourceLocator(writer) @@ -625,7 +625,7 @@ func writeNanoTDFHeader(writer io.Writer, config NanoTDFConfig) ([]byte, uint32, return nil, 0, 0, err } totalBytes += uint32(config.kasURL.getLength()) - slog.Debug("writeNanoTDFHeader", slog.Uint64("resource locator number", uint64(config.kasURL.getLength()))) + slog.Debug("writeNanoTDFHeader", slog.Uint64("resource_locator_number", uint64(config.kasURL.getLength()))) // Write ECC And Binding Mode l, err = writer.Write([]byte{serializeBindingCfg(config.bindCfg)}) @@ -804,7 +804,7 @@ func (s SDK) CreateNanoTDF(writer io.Writer, reader io.Reader, config NanoTDFCon return 0, fmt.Errorf("writeNanoTDFHeader failed:%w", err) } - slog.Debug("CreateNanoTDF", slog.Uint64("Header", uint64(totalSize))) + slog.Debug("checkpoint CreateNanoTDF", slog.Uint64("header", uint64(totalSize))) aesGcm, err := ocrypto.NewAESGcm(key) if err != nil { @@ -846,7 +846,7 @@ func (s SDK) CreateNanoTDF(writer io.Writer, reader io.Reader, config NanoTDFCon } totalSize += uint32(l) - slog.Debug("CreateNanoTDF", slog.Uint64("payloadLength", uint64(len(cipherDataWithoutPadding)))) + slog.Debug("checkpoint CreateNanoTDF", slog.Uint64("payload_length", uint64(len(cipherDataWithoutPadding)))) // write cipher data l, err = writer.Write(cipherDataWithoutPadding) @@ -884,7 +884,7 @@ func createNanoTDFDecryptHandler(reader io.ReadSeeker, writer io.Writer, opts .. }, nil } -func (n *NanoTDFDecryptHandler) CreateRewrapRequest(_ context.Context) (map[string]*kas.UnsignedRewrapRequest_WithPolicyRequest, error) { +func (n *NanoTDFDecryptHandler) CreateRewrapRequest(ctx context.Context) (map[string]*kas.UnsignedRewrapRequest_WithPolicyRequest, error) { var err error var headerSize uint32 n.header, headerSize, err = NewNanoTDFHeaderFromReader(n.reader) @@ -907,7 +907,7 @@ func (n *NanoTDFDecryptHandler) CreateRewrapRequest(_ context.Context) (map[stri } if n.config.ignoreAllowList { - slog.Warn(fmt.Sprintf("KasAllowlist is ignored, kas url %s is allowed", kasURL)) + slog.WarnContext(ctx, "kasAllowlist is ignored, kas url is allowed", slog.String("kas_url", kasURL)) } else if !n.config.kasAllowlist.IsAllowed(kasURL) { return nil, fmt.Errorf("KasAllowlist: kas url %s is not allowed", kasURL) } @@ -927,7 +927,7 @@ func (n *NanoTDFDecryptHandler) CreateRewrapRequest(_ context.Context) (map[stri return map[string]*kas.UnsignedRewrapRequest_WithPolicyRequest{kasURL: req}, nil } -func (n *NanoTDFDecryptHandler) Decrypt(_ context.Context, result []kaoResult) (int, error) { +func (n *NanoTDFDecryptHandler) Decrypt(ctx context.Context, result []kaoResult) (int, error) { var err error if len(result) != 1 { return 0, errors.New("improper result from kas") @@ -948,7 +948,7 @@ func (n *NanoTDFDecryptHandler) Decrypt(_ context.Context, result []kaoResult) ( } payloadLength := binary.BigEndian.Uint32(payloadLengthBuf) - slog.Debug("ReadNanoTDF", slog.Uint64("payloadLength", uint64(payloadLength))) + slog.DebugContext(ctx, "decrypt", slog.Uint64("payload_length", uint64(payloadLength))) cipherData := make([]byte, payloadLength) _, err = n.reader.Read(cipherData) @@ -1014,7 +1014,7 @@ func (s SDK) ReadNanoTDFContext(ctx context.Context, writer io.Writer, reader io } handler.config.kasAllowlist = allowList } else { - slog.Error("No KAS allowlist provided and no KeyAccessServerRegistry available") + slog.ErrorContext(ctx, "no KAS allowlist provided and no KeyAccessServerRegistry available") return 0, errors.New("no KAS allowlist provided and no KeyAccessServerRegistry available") } } diff --git a/sdk/tdf.go b/sdk/tdf.go index 35d9c9286a..0f2d745024 100644 --- a/sdk/tdf.go +++ b/sdk/tdf.go @@ -206,7 +206,7 @@ func (s SDK) CreateTDFContext(ctx context.Context, writer io.Writer, reader io.R if err == nil { err = populateKasInfoFromBaseKey(baseKey, tdfConfig) } else { - slog.Debug("Error getting base key, falling back to default kas.", "error", err) + slog.DebugContext(ctx, "error getting base key, falling back to default kas", slog.Any("error", err)) dk := s.defaultKases(tdfConfig) tdfConfig.kaoTemplate = nil tdfConfig.splitPlan, err = g.plan(dk, uuidSplitIDGenerator) @@ -724,7 +724,7 @@ func allowListFromKASRegistry(ctx context.Context, kasRegistryClient sdkconnect. } } // grpc target does not have a scheme - slog.Debug("Adding platform URL to KAS allowlist", "platformURL", platformURL) + slog.Debug("adding platform URL to KAS allowlist", slog.String("platform_url", platformURL)) err = kasAllowlist.Add(platformURL) if err != nil { return nil, fmt.Errorf("kasAllowlist.Add failed: %w", err) @@ -762,7 +762,7 @@ func (s SDK) LoadTDF(reader io.ReadSeeker, opts ...TDFReaderOption) (*Reader, er } config.kasAllowlist = allowList } else { - slog.Error("No KAS allowlist provided and no KeyAccessServerRegistry available") + slog.Error("no KAS allowlist provided and no KeyAccessServerRegistry available") return nil, errors.New("no KAS allowlist provided and no KeyAccessServerRegistry available") } } @@ -1361,7 +1361,7 @@ func (r *Reader) doPayloadKeyUnwrap(ctx context.Context) error { //nolint:gocogn // if ignoreing allowlist then warn // if kas url is not allowed then return error if r.config.ignoreAllowList { - slog.Warn(fmt.Sprintf("KasAllowlist is ignored, kas url %s is allowed", kasurl)) + slog.WarnContext(ctx, "kasAllowlist is ignored, kas url is allowed", slog.String("kas_url", kasurl)) } else if !r.config.kasAllowlist.IsAllowed(kasurl) { reqFail(fmt.Errorf("KasAllowlist: kas url %s is not allowed", kasurl), req) continue @@ -1461,12 +1461,12 @@ func populateKasInfoFromBaseKey(key *policy.SimpleKasKey, tdfConfig *TDFConfig) // ? Maybe we shouldn't overwrite the key type if tdfConfig.keyType != ocrypto.KeyType(algoString) { - slog.Warn("Base key is enabled, setting key type", "keyType", algoString) + slog.Warn("base key is enabled, setting key type", slog.String("key_type", algoString)) } tdfConfig.keyType = ocrypto.KeyType(algoString) tdfConfig.splitPlan = nil if len(tdfConfig.kasInfoList) > 0 { - slog.Warn("Base key is enabled, overwriting kasInfoList with base key info") + slog.Warn("base key is enabled, overwriting kasInfoList with base key info") } tdfConfig.kasInfoList = []KASInfo{ { diff --git a/sdk/tdf_test.go b/sdk/tdf_test.go index 83a9ab27ac..6e11b765ae 100644 --- a/sdk/tdf_test.go +++ b/sdk/tdf_test.go @@ -2227,7 +2227,10 @@ func (f *FakeAttributes) GetAttributeValuesByFqns(_ context.Context, in *connect for _, fqn := range in.Msg.GetFqns() { av, err := NewAttributeValueFQN(fqn) if err != nil { - slog.Error("invalid fqn", "notfqn", fqn, "error", err) + slog.Error("invalid fqn", + slog.String("fqn", fqn), + slog.Any("error", err), + ) return nil, status.New(codes.InvalidArgument, fmt.Sprintf("invalid attribute fqn [%s]", fqn)).Err() } v := mockValueFor(av) diff --git a/service/authorization/authorization.go b/service/authorization/authorization.go index af49216a2f..ca43f9141d 100644 --- a/service/authorization/authorization.go +++ b/service/authorization/authorization.go @@ -166,7 +166,7 @@ func (as *AuthorizationService) GetDecisionsByToken(ctx context.Context, req *co for _, tdr := range req.Msg.GetDecisionRequests() { ecResp, err := as.sdk.EntityResoution.CreateEntityChainFromJwt(ctx, &entityresolution.CreateEntityChainFromJwtRequest{Tokens: tdr.GetTokens()}) if err != nil { - as.logger.Error("Error calling ERS to get entity chains from jwts") + as.logger.ErrorContext(ctx, "error calling ERS to get entity chains from jwts") return nil, err } @@ -353,7 +353,8 @@ func (as *AuthorizationService) GetEntitlements(ctx context.Context, req *connec FqnAttributeValues: fqnAttrVals, } subjectMappings := avf.GetFqnAttributeValues() - as.logger.DebugContext(ctx, fmt.Sprintf("retrieved %d subject mappings", len(subjectMappings))) + as.logger.DebugContext(ctx, "retrieved subject mappings", slog.Int("count", len(subjectMappings))) + // TODO: this could probably be moved to proto validation https://github.com/opentdf/platform/issues/1057 if req.Msg.Entities == nil { as.logger.ErrorContext(ctx, "requires entities") @@ -366,7 +367,7 @@ func (as *AuthorizationService) GetEntitlements(ctx context.Context, req *connec // call ERS on all entities ersResp, err := as.sdk.EntityResoution.ResolveEntities(ctx, &entityresolution.ResolveEntitiesRequest{Entities: req.Msg.GetEntities()}) if err != nil { - as.logger.ErrorContext(ctx, "error calling ERS to resolve entities", "entities", req.Msg.GetEntities()) + as.logger.ErrorContext(ctx, "error calling ERS to resolve entities", slog.Any("entities", req.Msg.GetEntities())) return nil, err } @@ -433,7 +434,10 @@ func (as *AuthorizationService) GetEntitlements(ctx context.Context, req *connec entitlement, valueOK := value.(string) // If value is not okay skip adding to entitlements if !valueOK { - as.logger.WarnContext(ctx, "issue with adding entitlement", slog.String("entity_id", entity.GetId()), slog.String("entitlement", entitlement)) + as.logger.WarnContext(ctx, "issue with adding entitlement", + slog.String("entity_id", entity.GetId()), + slog.String("entitlement", entitlement), + ) continue } // if comprehensive and a hierarchy attribute is entitled then add the lower entitlements @@ -750,7 +754,7 @@ func getComprehensiveHierarchy(attributesMap map[string]*policy.Attribute, avf * } attrDef := attributesMap[entitlement] if attrDef == nil { - as.logger.Warn("no attribute definition found for entity", "fqn", entitlement) + as.logger.Warn("no attribute definition found for entity", slog.String("fqn", entitlement)) return entitlements } if attrDef.GetRule() == policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY { diff --git a/service/authorization/authorization_test.go b/service/authorization/authorization_test.go index 082448b0ae..1d915742f3 100644 --- a/service/authorization/authorization_test.go +++ b/service/authorization/authorization_test.go @@ -362,7 +362,7 @@ func Test_GetDecisions_AllOf_Fail(t *testing.T) { // NOTE: there should be two decision responses, one for each data attribute value, but authorization service // only responds with one permit/deny at the moment // entitlements only contain the first FQN, so we have a deny decision - as.logger.Debug(resp.Msg.String()) + as.logger.Debug("response", slog.String("response", resp.Msg.String())) assert.Len(t, resp.Msg.GetDecisionResponses(), 1) assert.Equal(t, authorization.DecisionResponse_DECISION_DENY, resp.Msg.GetDecisionResponses()[0].GetDecision()) } @@ -1327,7 +1327,7 @@ func Test_GetDecisionsAllOf_Pass_EC_RA_Length_Mismatch(t *testing.T) { assert.NotNil(t, resp) // one entitlement, one attribute value throughout - slog.Debug(resp.Msg.String()) + slog.Debug("response", slog.String("response", resp.Msg.String())) assert.Len(t, resp.Msg.GetDecisionResponses(), 3) assert.Equal(t, "ec1", resp.Msg.GetDecisionResponses()[0].GetEntityChainId()) assert.Equal(t, "ec2", resp.Msg.GetDecisionResponses()[1].GetEntityChainId()) diff --git a/service/authorization/v2/authorization.go b/service/authorization/v2/authorization.go index 9091528e8c..3ccbda76d5 100644 --- a/service/authorization/v2/authorization.go +++ b/service/authorization/v2/authorization.go @@ -123,7 +123,7 @@ func (as *Service) GetDecision(ctx context.Context, req *connect.Request[authzV2 decisions, permitted, err := pdp.GetDecision(ctx, entityIdentifier, action, []*authzV2.Resource{resource}) if err != nil { - as.logger.ErrorContext(ctx, "failed to get decision", slog.Any("error", err), slog.Any("request", request)) + as.logger.ErrorContext(ctx, "failed to get decision", slog.Any("error", err)) if errors.Is(err, access.ErrFQNNotFound) || errors.Is(err, access.ErrDefinitionNotFound) { return nil, connect.NewError(connect.CodeNotFound, err) } @@ -131,7 +131,7 @@ func (as *Service) GetDecision(ctx context.Context, req *connect.Request[authzV2 } resp, err := rollupSingleResourceDecision(permitted, decisions) if err != nil { - as.logger.ErrorContext(ctx, "failed to rollup single-resource decision", slog.Any("error", err), slog.Any("request", request)) + as.logger.ErrorContext(ctx, "failed to rollup single-resource decision", slog.Any("error", err)) return nil, connect.NewError(connect.CodeInternal, err) } return connect.NewResponse(resp), nil diff --git a/service/cmd/migrate.go b/service/cmd/migrate.go index 57d3b5f9be..a45ea0a443 100644 --- a/service/cmd/migrate.go +++ b/service/cmd/migrate.go @@ -62,7 +62,7 @@ var ( for _, s := range status { slog.Info("migration", slog.String("state", string(s.State)), - slog.String("source", s.Source.Path), + slog.String("source_path", s.Source.Path), slog.String("applied_on", s.AppliedAt.String()), ) diff --git a/service/cmd/provisionKeycloak.go b/service/cmd/provisionKeycloak.go index 31afd19045..faba4ddcb2 100644 --- a/service/cmd/provisionKeycloak.go +++ b/service/cmd/provisionKeycloak.go @@ -69,7 +69,7 @@ var provisionKeycloakCmd = &cobra.Command{ var deprecatedProvisionKeycloakFromConfigCmd = &cobra.Command{ Use: "keycloak-from-config", RunE: func(_ *cobra.Command, _ []string) error { - slog.Info("Command keycloak-from-config has been deprecated. Please use command 'keycloak' instead.") + slog.Info("command keycloak-from-config has been deprecated. Please use command 'keycloak' instead.") return nil }, } diff --git a/service/entityresolution/entityresolution.go b/service/entityresolution/entityresolution.go index defdfdaa93..a5ec84d868 100644 --- a/service/entityresolution/entityresolution.go +++ b/service/entityresolution/entityresolution.go @@ -1,3 +1,4 @@ +//nolint:sloglint // v1 entityresolution will be deprecated soon package entityresolution import ( diff --git a/service/entityresolution/keycloak/entity_resolution.go b/service/entityresolution/keycloak/entity_resolution.go index 5616d70326..52884481eb 100644 --- a/service/entityresolution/keycloak/entity_resolution.go +++ b/service/entityresolution/keycloak/entity_resolution.go @@ -1,3 +1,4 @@ +//nolint:sloglint // v1 entityresolution will be deprecated soon package keycloak import ( diff --git a/service/entityresolution/keycloak/v2/entity_resolution.go b/service/entityresolution/keycloak/v2/entity_resolution.go index 5351d78f11..e96a449108 100644 --- a/service/entityresolution/keycloak/v2/entity_resolution.go +++ b/service/entityresolution/keycloak/v2/entity_resolution.go @@ -73,7 +73,7 @@ func RegisterKeycloakERS(config config.ServiceConfig, logger *logger.Logger, svc if err := mapstructure.Decode(config, &inputIdpConfig); err != nil { panic(err) } - logger.Debug("entity_resolution configuration", "config", inputIdpConfig) + logger.Debug("entity_resolution configuration", slog.Any("config", inputIdpConfig)) keycloakSVC := &EntityResolutionServiceV2{idpConfig: inputIdpConfig, logger: logger, svcCache: svcCache} return keycloakSVC, nil } @@ -159,13 +159,22 @@ func EntityResolution(ctx context.Context, var resolvedEntities []*entityresolutionV2.EntityRepresentation for idx, ident := range payload { - logger.Debug("lookup", "entity", ident.GetEntityType()) + logger.DebugContext(ctx, + "lookup", + slog.Any("entity", ident.GetEntityType()), + ) + var keycloakEntities []*gocloak.User var getUserParams gocloak.GetUsersParams exactMatch := true switch ident.GetEntityType().(type) { case *entity.Entity_ClientId: - logger.Debug("looking up", slog.Any("type", ident.GetEntityType()), slog.String("client_id", ident.GetClientId())) + logger.DebugContext(ctx, + "looking up", + slog.Any("type", ident.GetEntityType()), + slog.String("client_id", ident.GetClientId()), + ) + clientID := ident.GetClientId() clients, err := connector.client.GetClients(ctx, connector.token.AccessToken, kcConfig.Realm, gocloak.GetClientsParams{ ClientID: &clientID, @@ -223,17 +232,19 @@ func EntityResolution(ctx context.Context, users, err := connector.client.GetUsers(ctx, connector.token.AccessToken, kcConfig.Realm, getUserParams) switch { case err != nil: - logger.Error(err.Error()) + logger.ErrorContext(ctx, "error getting users", slog.Any("error", err)) return entityresolutionV2.ResolveEntitiesResponse{}, connect.NewError(connect.CodeInternal, ErrGetRetrievalFailed) case len(users) == 1: user := users[0] - logger.Debug("user found", slog.String("user", *user.ID), slog.String("entity", ident.String())) - logger.Debug("user", slog.Any("details", user)) - logger.Debug("user", slog.Any("attributes", user.Attributes)) + logger.DebugContext(ctx, + "user", + slog.Any("details", user), + slog.String("entity", ident.String()), + ) keycloakEntities = append(keycloakEntities, user) default: - logger.Error("no user found for", slog.Any("entity", ident)) + logger.ErrorContext(ctx, "no user found", slog.Any("entity", ident)) if ident.GetEmailAddress() != "" { //nolint:nestif // this case has many possible outcomes to handle // try by group groups, groupErr := connector.client.GetGroups( @@ -257,7 +268,7 @@ func EntityResolution(ctx context.Context, } keycloakEntities = expandedRepresentations default: - logger.Error("no group found for", slog.String("entity", ident.String())) + logger.ErrorContext(ctx, "no group found for", slog.String("entity", ident.String())) var entityNotFoundErr entityresolutionV2.EntityNotFoundError switch ident.GetEntityType().(type) { case *entity.Entity_EmailAddress: @@ -268,10 +279,11 @@ func EntityResolution(ctx context.Context, // return &entityresolutionV2.IdpPluginResponse{}, // status.Error(codes.InvalidArgument, db.ErrTextNotFound) default: - logger.Error("unsupported/unknown type for", slog.String("entity", ident.String())) + logger.ErrorContext(ctx, "unsupported/unknown type for", slog.String("entity", ident.String())) entityNotFoundErr = entityresolutionV2.EntityNotFoundError{Code: int32(codes.NotFound), Message: ErrGetRetrievalFailed.Error(), Entity: ident.String()} } - logger.Error(entityNotFoundErr.String()) + logger.ErrorContext(ctx, "entity not found", slog.String("error", entityNotFoundErr.String())) + if kcConfig.InferID.From.Email || kcConfig.InferID.From.Username { // user not found -- add json entity to resp instead entityStruct, err := entityToStructPb(ident) @@ -354,7 +366,7 @@ func typeToGenericJSONMap[Marshalable any](inputStruct Marshalable, logger *logg } func expandGroup(ctx context.Context, groupID string, kcConnector *Connector, kcConfig *Config, logger *logger.Logger) ([]*gocloak.User, error) { - logger.Info("expanding group", slog.String("groupID", groupID)) + logger.Info("expanding group", slog.String("group_id", groupID)) var entityRepresentations []*gocloak.User grp, err := kcConnector.client.GetGroup(ctx, kcConnector.token.AccessToken, kcConfig.Realm, groupID) @@ -362,16 +374,20 @@ func expandGroup(ctx context.Context, groupID string, kcConnector *Connector, kc grpMembers, memberErr := kcConnector.client.GetGroupMembers(ctx, kcConnector.token.AccessToken, kcConfig.Realm, *grp.ID, gocloak.GetGroupsParams{}) if memberErr == nil { - logger.Debug("adding members", slog.Int("amount", len(grpMembers)), slog.String("from group", *grp.Name)) + logger.DebugContext(ctx, + "adding members", + slog.Int("amount", len(grpMembers)), + slog.String("from group", *grp.Name), + ) for i := 0; i < len(grpMembers); i++ { user := grpMembers[i] entityRepresentations = append(entityRepresentations, user) } } else { - logger.Error("error getting group members", slog.String("error", memberErr.Error())) + logger.ErrorContext(ctx, "error getting group members", slog.String("error", memberErr.Error())) } } else { - logger.Error("error getting group", slog.String("error", err.Error())) + logger.ErrorContext(ctx, "error getting group", slog.String("error", err.Error())) return nil, err } return entityRepresentations, nil @@ -453,16 +469,16 @@ func getServiceAccountClient(ctx context.Context, username string, kcConfig Conf }) switch { case err != nil: - logger.Error(err.Error()) + logger.ErrorContext(ctx, "connector client error", slog.Any("error", err)) return "", err case len(clients) == 1: client := clients[0] - logger.Debug("client found", slog.String("client", *client.ClientID)) + logger.DebugContext(ctx, "client found", slog.String("client", *client.ClientID)) return *client.ClientID, nil case len(clients) > 1: - logger.Error("more than one client found for ", slog.String("clientid", expectedClientName)) + logger.ErrorContext(ctx, "more than one client found for ", slog.String("clientid", expectedClientName)) default: - logger.Debug("no client found, likely not a service account", slog.String("clientid", expectedClientName)) + logger.DebugContext(ctx, "no client found, likely not a service account", slog.String("clientid", expectedClientName)) } return "", nil @@ -489,11 +505,11 @@ func (s *EntityResolutionServiceV2) getConnector(ctx context.Context, tokenBuffe // Refresh token if it's nil, expired, or about to expire. if s.connector == nil || s.connector.token == nil || time.Now().After(s.connector.expiresAt.Add(-tokenBuffer)) { - s.logger.InfoContext(ctx, "Keycloak connector is nil or token expired/expiring soon. Fetching new token.") + s.logger.InfoContext(ctx, "keycloak connector is nil or token expired/expiring soon - fetching new token") var gocloakClient *gocloak.GoCloak if s.idpConfig.LegacyKeycloak { - s.logger.WarnContext(ctx, "Using legacy connection mode for Keycloak < 17.x.x") + s.logger.WarnContext(ctx, "using legacy connection mode for Keycloak < 17.x.x") gocloakClient = gocloak.NewClient(s.idpConfig.URL) } else { gocloakClient = gocloak.NewClient(s.idpConfig.URL, gocloak.SetAuthAdminRealms("admin/realms"), gocloak.SetAuthRealms("realms")) @@ -501,7 +517,7 @@ func (s *EntityResolutionServiceV2) getConnector(ctx context.Context, tokenBuffe token, err := gocloakClient.LoginClient(ctx, s.idpConfig.ClientID, s.idpConfig.ClientSecret, s.idpConfig.Realm) if err != nil { - s.logger.ErrorContext(ctx, "Error connecting to Keycloak or logging in", slog.String("error", err.Error())) + s.logger.ErrorContext(ctx, "error connecting to Keycloak or logging in", slog.Any("error", err)) return nil, fmt.Errorf("failed to login to Keycloak: %w", err) } @@ -510,9 +526,9 @@ func (s *EntityResolutionServiceV2) getConnector(ctx context.Context, tokenBuffe client: gocloakClient, expiresAt: time.Now().Add(time.Duration(token.ExpiresIn) * time.Second), } - s.logger.InfoContext(ctx, "Successfully fetched new Keycloak token.", "expires_in_seconds", token.ExpiresIn) + s.logger.InfoContext(ctx, "successfully fetched new Keycloak token", slog.Int("expires_in_seconds", token.ExpiresIn)) } else { - s.logger.DebugContext(ctx, "Using existing Keycloak token.") + s.logger.DebugContext(ctx, "using existing Keycloak token") } return s.connector, nil } diff --git a/service/entityresolution/v2/entity_resolution.go b/service/entityresolution/v2/entity_resolution.go index ac2cd15cef..2a30c6927f 100644 --- a/service/entityresolution/v2/entity_resolution.go +++ b/service/entityresolution/v2/entity_resolution.go @@ -1,6 +1,7 @@ package entityresolution import ( + "log/slog" "time" "github.com/go-viper/mapstructure/v2" @@ -52,14 +53,14 @@ func NewRegistration() *serviceregistry.Service[entityresolutionv2connect.Entity if inputConfig.CacheExpiration != "" { exp, err := time.ParseDuration(inputConfig.CacheExpiration) if err != nil { - srp.Logger.Error("Failed to parse cache expiration duration", "error", err) + srp.Logger.Error("failed to parse cache expiration duration", slog.Any("error", err)) panic(err) } ersCache, err = srp.NewCacheClient(cache.Options{ Expiration: exp, }) if err != nil { - srp.Logger.Error("Failed to create cache for Entity Resolution Service", "error", err) + srp.Logger.Error("failed to create cache for Entity Resolution Service", slog.Any("error", err)) panic(err) } } diff --git a/service/health/health.go b/service/health/health.go index ae006d1ac6..fc6ab4d73a 100644 --- a/service/health/health.go +++ b/service/health/health.go @@ -81,7 +81,11 @@ func (s HealthService) Check(ctx context.Context, req *grpchealth.CheckRequest) case "all": for service, check := range serviceHealthChecks { if err := check(ctx); err != nil { - s.logger.ErrorContext(ctx, "service is not ready", slog.String("service", service), slog.String("error", err.Error())) + s.logger.ErrorContext(ctx, + "service is not ready", + slog.String("service", service), + slog.Any("error", err), + ) return &grpchealth.CheckResponse{ Status: grpchealth.StatusNotServing, }, nil @@ -90,7 +94,11 @@ func (s HealthService) Check(ctx context.Context, req *grpchealth.CheckRequest) default: if check, ok := serviceHealthChecks[req.Service]; ok { if err := check(ctx); err != nil { - s.logger.ErrorContext(ctx, "service is not ready", slog.String("service", req.Service), slog.String("error", err.Error())) + s.logger.ErrorContext(ctx, + "service is not ready", + slog.String("service", req.Service), + slog.Any("error", err), + ) return &grpchealth.CheckResponse{ Status: grpchealth.StatusNotServing, }, nil diff --git a/service/integration/main_test.go b/service/integration/main_test.go index 1d50ec338e..a3e3b1b3b5 100644 --- a/service/integration/main_test.go +++ b/service/integration/main_test.go @@ -95,6 +95,7 @@ func TestMain(m *testing.M) { Started: true, } + //nolint:sloglint // emoji slog.Info("📀 starting postgres container") postgres, err := tc.GenericContainer(context.Background(), req) if err != nil { @@ -122,6 +123,7 @@ func TestMain(m *testing.M) { conf.DB.Port = port.Int() + //nolint:sloglint // emoji slog.Info("🏠 loading fixtures") fixtures.LoadFixtureData("../internal/fixtures/policy_fixtures.yaml") diff --git a/service/internal/access/pdp.go b/service/internal/access/pdp.go index 137058c726..ba9e75d52f 100644 --- a/service/internal/access/pdp.go +++ b/service/internal/access/pdp.go @@ -1,3 +1,4 @@ +//nolint:sloglint // v1 PDP will be deprecated soon package access import ( diff --git a/service/internal/access/v2/evaluate.go b/service/internal/access/v2/evaluate.go index 4add265d3e..3e9d9c1df9 100644 --- a/service/internal/access/v2/evaluate.go +++ b/service/internal/access/v2/evaluate.go @@ -155,7 +155,7 @@ func evaluateDefinition( l.DebugContext( ctx, "evaluating definition", - slog.Any("resourceValueFQNs", resourceValueFQNs), + slog.Any("resource_value_fqns", resourceValueFQNs), ) switch attrDefinition.GetRule() { diff --git a/service/internal/access/v2/helpers.go b/service/internal/access/v2/helpers.go index 2b039263c3..69342cadd7 100644 --- a/service/internal/access/v2/helpers.go +++ b/service/internal/access/v2/helpers.go @@ -156,7 +156,10 @@ func populateHigherValuesIfHierarchy( // Pull the value from the lookup store holding subject mappings fullValue, ok := allEntitleableAttributesByValueFQN[value.GetFqn()] if !ok { - l.WarnContext(ctx, "value FQN of hierarchy attribute not found available for lookup, may not have had subject mappings associated or provided", slog.String("value FQN", value.GetFqn())) + l.WarnContext(ctx, + "value FQN of hierarchy attribute not found available for lookup, may not have had subject mappings associated or provided", + slog.String("value_fqn", value.GetFqn()), + ) continue } decisionableAttributes[value.GetFqn()] = &attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue{ diff --git a/service/internal/access/v2/just_in_time_pdp.go b/service/internal/access/v2/just_in_time_pdp.go index 7a499016b6..8eacda5dae 100644 --- a/service/internal/access/v2/just_in_time_pdp.go +++ b/service/internal/access/v2/just_in_time_pdp.go @@ -319,7 +319,11 @@ func (p *JustInTimePDP) resolveEntitiesFromEntityChain( entityChain *entity.EntityChain, skipEnvironmentEntities bool, ) ([]*entityresolutionV2.EntityRepresentation, error) { - p.logger.DebugContext(ctx, "resolving entities from entity chain", slog.String("entityChain ID", entityChain.GetEphemeralId()), slog.Bool("skipEnvironmentEntities", skipEnvironmentEntities)) + p.logger.DebugContext(ctx, + "resolving entities from entity chain", + slog.String("entity_chain_id", entityChain.GetEphemeralId()), + slog.Bool("skip_environment_entities", skipEnvironmentEntities), + ) var filteredEntities []*entity.Entity if skipEnvironmentEntities { @@ -355,7 +359,7 @@ func (p *JustInTimePDP) resolveEntitiesFromToken( skipEnvironmentEntities bool, ) ([]*entityresolutionV2.EntityRepresentation, error) { // WARNING: do not log the token JWT, just its ID - p.logger.DebugContext(ctx, "resolving entities from token", slog.String("token ephemeral id", token.GetEphemeralId())) + p.logger.DebugContext(ctx, "resolving entities from token", slog.String("token_ephemeral_id", token.GetEphemeralId())) ersResp, err := p.sdk.EntityResolutionV2.CreateEntityChainsFromTokens(ctx, &entityresolutionV2.CreateEntityChainsFromTokensRequest{Tokens: []*entity.Token{token}}) if err != nil { return nil, fmt.Errorf("failed to create entity chains from token: %w", err) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index eccf5c0244..c31471d4c9 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -106,7 +106,11 @@ func NewPolicyDecisionPoint( for _, sm := range allSubjectMappings { if err := validateSubjectMapping(sm); err != nil { - l.WarnContext(ctx, "invalid subject mapping - skipping", slog.Any("error", err), slog.Any("subject mapping", sm)) + l.WarnContext(ctx, + "invalid subject mapping - skipping", + slog.Any("subject_mapping", sm), + slog.Any("error", err), + ) continue } mappedValue := sm.GetAttributeValue() @@ -165,7 +169,7 @@ func (p *PolicyDecisionPoint) GetDecision( ) (*Decision, error) { l := p.logger.With("entityID", entityRepresentation.GetOriginalId()) l = l.With("action", action.GetName()) - l.DebugContext(ctx, "getting decision", slog.Int("resourcesCount", len(resources))) + l.DebugContext(ctx, "getting decision", slog.Int("resources_count", len(resources))) if err := validateGetDecision(entityRepresentation, action, resources); err != nil { return nil, err @@ -176,14 +180,14 @@ func (p *PolicyDecisionPoint) GetDecision( if err != nil { return nil, fmt.Errorf("error getting decisionable attributes: %w", err) } - l.DebugContext(ctx, "filtered to only entitlements relevant to decisioning", slog.Int("decisionableAttributeValuesCount", len(decisionableAttributes))) + l.DebugContext(ctx, "filtered to only entitlements relevant to decisioning", slog.Int("decisionable_attribute_values_count", len(decisionableAttributes))) // Resolve them to their entitled FQNs and the actions available on each entitledFQNsToActions, err := subjectmappingbuiltin.EvaluateSubjectMappingsWithActions(decisionableAttributes, entityRepresentation) if err != nil { return nil, fmt.Errorf("error evaluating subject mappings for entitlement: %w", err) } - l.DebugContext(ctx, "evaluated subject mappings", slog.Any("entitledValueFqnsToActions", entitledFQNsToActions)) + l.DebugContext(ctx, "evaluated subject mappings", slog.Any("entitled_value_fqns_to_actions", entitledFQNsToActions)) decision := &Decision{ Access: true, @@ -315,13 +319,13 @@ func (p *PolicyDecisionPoint) GetEntitlements( } l := p.logger.With("withComprehensiveHierarchy", strconv.FormatBool(withComprehensiveHierarchy)) - l.DebugContext(ctx, "getting entitlements", slog.Int("entityRepresentationsCount", len(entityRepresentations))) + l.DebugContext(ctx, "getting entitlements", slog.Int("entity_representations_count", len(entityRepresentations))) var entitleableAttributes map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue // Check entitlement only against the filtered matched subject mappings if provided if optionalMatchedSubjectMappings != nil { - l.DebugContext(ctx, "filtering to provided matched subject mappings", slog.Int("matchedSubjectMappingsCount", len(optionalMatchedSubjectMappings))) + l.DebugContext(ctx, "filtering to provided matched subject mappings", slog.Int("matched_subject_mappings_count", len(optionalMatchedSubjectMappings))) entitleableAttributes, err = getFilteredEntitleableAttributes(optionalMatchedSubjectMappings, p.allEntitleableAttributesByValueFQN) if err != nil { return nil, fmt.Errorf("error filtering entitleable attributes from matched subject mappings: %w", err) @@ -337,7 +341,7 @@ func (p *PolicyDecisionPoint) GetEntitlements( if err != nil { return nil, fmt.Errorf("error evaluating subject mappings for entitlement: %w", err) } - l.DebugContext(ctx, "evaluated subject mappings", slog.Any("entitlementsByEntityID", entityIDsToFQNsToActions)) + l.DebugContext(ctx, "evaluated subject mappings", slog.Any("entitlements_by_entity_id", entityIDsToFQNsToActions)) var result []*authz.EntityEntitlements for entityID, fqnsToActions := range entityIDsToFQNsToActions { diff --git a/service/internal/auth/authn.go b/service/internal/auth/authn.go index d5e4edcc01..5a26aa27e5 100644 --- a/service/internal/auth/authn.go +++ b/service/internal/auth/authn.go @@ -123,7 +123,11 @@ func NewAuthenticator(ctx context.Context, cfg Config, logger *logger.Logger, we cacheInterval, err := time.ParseDuration(cfg.CacheRefresh) if err != nil { - logger.ErrorContext(ctx, fmt.Sprintf("Invalid cache_refresh_interval [%s]", cfg.CacheRefresh), "err", err) + logger.ErrorContext(ctx, + "invalid cache_refresh_interval", + slog.String("cache_refresh_interval", cfg.CacheRefresh), + slog.Any("err", err), + ) cacheInterval = refreshInterval } @@ -229,7 +233,11 @@ func (a Authentication) MuxHandler(handler http.Handler) http.Handler { m: []string{r.Method}, }, dp) if err != nil { - slog.WarnContext(r.Context(), "unauthenticated", "error", err, "dpop", dp, "authorization", header) + slog.WarnContext(r.Context(), + "unauthenticated", + slog.Any("error", err), + slog.Any("dpop", dp), + ) http.Error(w, "unauthenticated", http.StatusUnauthorized) return } @@ -255,7 +263,11 @@ func (a Authentication) MuxHandler(handler http.Handler) http.Handler { } if allow, err := a.enforcer.Enforce(accessTok, r.URL.Path, action); err != nil { if err.Error() == "permission denied" { - a.logger.WarnContext(r.Context(), "permission denied", slog.String("azp", accessTok.Subject()), slog.String("error", err.Error())) + a.logger.WarnContext(r.Context(), + "permission denied", + slog.String("azp", accessTok.Subject()), + slog.Any("error", err), + ) http.Error(w, "permission denied", http.StatusForbidden) return } @@ -320,7 +332,10 @@ func (a Authentication) ConnectUnaryServerInterceptor() connect.UnaryInterceptor // Check if the token is allowed to access the resource if allowed, err := a.enforcer.Enforce(token, resource, action); err != nil { if err.Error() == "permission denied" { - a.logger.Warn("permission denied", slog.String("azp", token.Subject()), slog.String("error", err.Error())) + a.logger.Warn("permission denied", + slog.String("azp", token.Subject()), + slog.Any("error", err), + ) return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied")) } return nil, err @@ -397,7 +412,7 @@ func (a *Authentication) checkToken(ctx context.Context, authHeader []string, dp jwt.WithAcceptableSkew(a.oidcConfiguration.TokenSkew), ) if err != nil { - a.logger.Warn("failed to validate auth token", slog.String("err", err.Error())) + a.logger.Warn("failed to validate auth token", slog.Any("err", err)) return nil, nil, err } @@ -418,7 +433,7 @@ func (a *Authentication) checkToken(ctx context.Context, authHeader []string, dp } key, err := a.validateDPoP(accessToken, tokenRaw, dpopInfo, dpopHeader) if err != nil { - a.logger.Warn("failed to validate dpop", slog.String("token", tokenRaw), slog.Any("err", err)) + a.logger.Warn("failed to validate dpop", slog.Any("err", err)) return nil, nil, err } ctx = ctxAuth.ContextWithAuthNInfo(ctx, key, accessToken, tokenRaw) @@ -551,10 +566,18 @@ func (a Authentication) isPublicRoute(path string) func(string) bool { return func(route string) bool { matched, err := doublestar.Match(route, path) if err != nil { - a.logger.Warn("error matching route", slog.String("route", route), slog.String("path", path), slog.String("error", err.Error())) + a.logger.Warn("error matching route", + slog.String("route", route), + slog.String("path", path), + slog.Any("error", err), + ) return false } - a.logger.Trace("matching route", slog.String("route", route), slog.String("path", path), slog.Bool("matched", matched)) + a.logger.Trace("matching route", + slog.String("route", route), + slog.String("path", path), + slog.Bool("matched", matched), + ) return matched } } @@ -593,10 +616,19 @@ func (a Authentication) lookupGatewayPaths(ctx context.Context, procedure string default: patterns := header["Pattern"] if len(patterns) == 0 { - a.logger.InfoContext(ctx, "underspecified grpc gateway path; no pattern header", slog.Any("origin", origins), slog.String("procedure", procedure)) + a.logger.InfoContext(ctx, + "underspecified grpc gateway path; no pattern header", + slog.Any("origin", origins), + slog.String("procedure", procedure), + ) paths = allowedPublicEndpoints[:] } else { - a.logger.InfoContext(ctx, "underspecified grpc gateway path; patterns found", slog.Any("origin", origins), slog.String("procedure", procedure), slog.Any("patterns", patterns)) + a.logger.InfoContext(ctx, + "underspecified grpc gateway path; patterns found", + slog.Any("origin", origins), + slog.String("procedure", procedure), + slog.Any("patterns", patterns), + ) } for _, pattern := range patterns { if matched := goodPaths.MatchString(pattern); matched { @@ -604,7 +636,12 @@ func (a Authentication) lookupGatewayPaths(ctx context.Context, procedure string } } if len(paths) != len(patterns) { - a.logger.WarnContext(ctx, "invalid grpc gateway path; ignoring one or more invalid patterns", slog.Any("origin", origins), slog.String("procedure", procedure), slog.Any("patterns", patterns)) + a.logger.WarnContext(ctx, + "invalid grpc gateway path; ignoring one or more invalid patterns", + slog.Any("origin", origins), + slog.String("procedure", procedure), + slog.Any("patterns", patterns), + ) } } diff --git a/service/internal/auth/casbin.go b/service/internal/auth/casbin.go index 11109fb57f..fda6b6b9da 100644 --- a/service/internal/auth/casbin.go +++ b/service/internal/auth/casbin.go @@ -133,14 +133,27 @@ func (e *Enforcer) Enforce(token jwt.Token, resource, action string) (bool, erro for _, info := range s { allowed, err := e.Enforcer.Enforce(info, resource, action) if err != nil { - e.logger.Error("enforce by role error", slog.String("subject info", info), slog.String("resource", resource), slog.String("action", action), slog.String("error", err.Error())) + e.logger.Error("enforce by role error", + slog.String("subject_info", info), + slog.String("action", action), + slog.String("resource", resource), + slog.Any("error", err), + ) } if allowed { - e.logger.Debug("allowed by policy", slog.String("subject info", info), slog.String("resource", resource), slog.String("action", action)) + e.logger.Debug("allowed by policy", + slog.String("subject_info", info), + slog.String("action", action), + slog.String("resource", resource), + ) return true, nil } } - e.logger.Debug("permission denied by policy", slog.Any("subject.info", s), slog.String("resource", resource), slog.String("action", action)) + e.logger.Debug("permission denied by policy", + slog.Any("subject_info", s), + slog.String("action", action), + slog.String("resource", resource), + ) return false, errors.New("permission denied") } @@ -148,14 +161,17 @@ func (e *Enforcer) buildSubjectFromToken(t jwt.Token) casbinSubject { var subject string info := casbinSubject{} - e.logger.Debug("building subject from token", slog.Any("token", t)) + e.logger.Debug("building subject from token") roles := e.extractRolesFromToken(t) if claim, found := t.Get(e.Config.UserNameClaim); found { sub, ok := claim.(string) subject = sub if !ok { - e.logger.Warn("username claim not of type string", slog.String("claim", e.Config.UserNameClaim), slog.Any("claims", claim)) + e.logger.Warn("username claim not of type string", + slog.String("claim", e.Config.UserNameClaim), + slog.Any("claims", claim), + ) subject = "" } } @@ -165,7 +181,7 @@ func (e *Enforcer) buildSubjectFromToken(t jwt.Token) casbinSubject { } func (e *Enforcer) extractRolesFromToken(t jwt.Token) []string { - e.logger.Debug("extracting roles from token", slog.Any("token", t)) + e.logger.Debug("extracting roles from token") roles := []string{} roleClaim := e.Config.GroupsClaim @@ -174,20 +190,32 @@ func (e *Enforcer) extractRolesFromToken(t jwt.Token) []string { selectors := strings.Split(roleClaim, ".") claim, exists := t.Get(selectors[0]) if !exists { - e.logger.Warn("claim not found", slog.String("claim", roleClaim), slog.Any("token", t)) + e.logger.Warn("claim not found", + slog.String("claim", roleClaim), + slog.Any("claims", claim), + ) return nil } - e.logger.Debug("root claim found", slog.String("claim", roleClaim), slog.Any("claims", claim)) + e.logger.Debug("root claim found", + slog.String("claim", roleClaim), + slog.Any("claims", claim), + ) // use dotnotation if the claim is nested if len(selectors) > 1 { claimMap, ok := claim.(map[string]interface{}) if !ok { - e.logger.Warn("claim is not of type map[string]interface{}", slog.String("claim", roleClaim), slog.Any("claims", claim)) + e.logger.Warn("claim is not of type map[string]interface{}", + slog.String("claim", roleClaim), + slog.Any("claims", claim), + ) return nil } claim = util.Dotnotation(claimMap, strings.Join(selectors[1:], ".")) if claim == nil { - e.logger.Warn("claim not found", slog.String("claim", roleClaim), slog.Any("claims", claim)) + e.logger.Warn("claim not found", + slog.String("claim", roleClaim), + slog.Any("claims", claim), + ) return nil } } @@ -203,7 +231,10 @@ func (e *Enforcer) extractRolesFromToken(t jwt.Token) []string { } } default: - e.logger.Warn("could not get claim type", slog.String("selector", roleClaim), slog.Any("claims", claim)) + e.logger.Warn("could not get claim type", + slog.String("selector", roleClaim), + slog.Any("claims", claim), + ) return nil } diff --git a/service/internal/fixtures/db.go b/service/internal/fixtures/db.go index 83340ea847..f2c44b7edf 100644 --- a/service/internal/fixtures/db.go +++ b/service/internal/fixtures/db.go @@ -110,7 +110,10 @@ func (d *DBInterface) ExecInsert(table string, columns []string, values ...[]str } pconn, err := d.Client.Pgx.Exec(context.Background(), sql) if err != nil { - slog.Error("insert error", "stmt", sql, "err", err) + slog.Error("insert error", + slog.Any("stmt", sql), + slog.Any("err", err), + ) return 0, err } return pconn.RowsAffected(), err @@ -120,7 +123,10 @@ func (d *DBInterface) DropSchema() error { sql := "DROP SCHEMA IF EXISTS " + d.Schema + " CASCADE" _, err := d.Client.Pgx.Exec(context.Background(), sql) if err != nil { - slog.Error("drop error", "stmt", sql, "err", err) + slog.Error("drop error", + slog.String("stmt", sql), + slog.Any("err", err), + ) return err } return nil diff --git a/service/internal/fixtures/fixtures.go b/service/internal/fixtures/fixtures.go index 19e1ded22e..ca1065c5e8 100644 --- a/service/internal/fixtures/fixtures.go +++ b/service/internal/fixtures/fixtures.go @@ -241,15 +241,21 @@ type FixtureData struct { func LoadFixtureData(file string) { c, err := os.ReadFile(file) if err != nil { - slog.Error("could not read "+fixtureFilename, slog.String("error", err.Error())) + slog.Error("could not read", + slog.String("fixture_file_name", fixtureFilename), + slog.Any("error", err), + ) panic(err) } if err := yaml.Unmarshal(c, &fixtureData); err != nil { - slog.Error("could not unmarshal "+fixtureFilename, slog.String("error", err.Error())) + slog.Error("could not unmarshal", + slog.String("fixture_file_name", fixtureFilename), + slog.Any("error", err), + ) panic(err) } - slog.Info("Fully loaded fixtures", slog.Any("fixtureData", fixtureData)) + slog.Info("fully loaded fixtures", slog.Any("fixture_data", fixtureData)) } type Fixtures struct { @@ -416,6 +422,7 @@ func (f *Fixtures) GetRegisteredResourceValueKey(key string) FixtureDataRegister return rv } +//nolint:sloglint // preserve emoji usage func (f *Fixtures) Provision() { slog.Info("📦 running migrations in schema", slog.String("schema", f.db.Schema)) _, err := f.db.Client.RunMigrations(context.Background(), policy.Migrations) @@ -484,6 +491,7 @@ func (f *Fixtures) Provision() { slog.Info("📚 successfully indexed FQNs") } +//nolint:sloglint // preserve emoji usage func (f *Fixtures) TearDown() { slog.Info("🗑 dropping schema", slog.String("schema", f.db.Schema)) if err := f.db.DropSchema(); err != nil { @@ -533,6 +541,7 @@ func (f *Fixtures) provisionAttributeValues() int64 { return f.provision(fixtureData.AttributeValues.Metadata.TableName, fixtureData.AttributeValues.Metadata.Columns, values) } +//nolint:sloglint // preserve emoji usage func (f *Fixtures) provisionSubjectConditionSet() int64 { values := make([][]string, 0, len(fixtureData.SubjectConditionSet.Data)) for _, d := range fixtureData.SubjectConditionSet.Data { @@ -619,6 +628,7 @@ func (f *Fixtures) provisionResourceMappings() int64 { return f.provision(fixtureData.ResourceMappings.Metadata.TableName, fixtureData.ResourceMappings.Metadata.Columns, values) } +//nolint:sloglint // preserve emoji usage func (f *Fixtures) provisionKasRegistry() int64 { values := make([][]string, 0, len(fixtureData.KasRegistries.Data)) for _, d := range fixtureData.KasRegistries.Data { @@ -662,6 +672,7 @@ func (f *Fixtures) provisionAttributeValueKeyAccessServer() int64 { return f.provision("attribute_value_key_access_grants", []string{"attribute_value_id", "key_access_server_id"}, values) } +//nolint:sloglint // preserve emoji usage func (f *Fixtures) provisionProviderConfigs() int64 { values := make([][]string, 0, len(fixtureData.ProviderConfigs.Data)) for _, d := range fixtureData.ProviderConfigs.Data { @@ -680,6 +691,7 @@ func (f *Fixtures) provisionProviderConfigs() int64 { return f.provision(fixtureData.ProviderConfigs.Metadata.TableName, fixtureData.ProviderConfigs.Metadata.Columns, values) } +//nolint:sloglint // preserve emoji usage func (f *Fixtures) provisionKasRegistryKeys() int64 { values := make([][]string, 0, len(fixtureData.KasRegistryKeys.Data)) for _, d := range fixtureData.KasRegistryKeys.Data { @@ -754,6 +766,7 @@ func (f *Fixtures) provisionRegisteredResourceActionAttributeValues() int64 { return f.provision(fixtureData.RegisteredResourceActionAttributeValues.Metadata.TableName, fixtureData.RegisteredResourceActionAttributeValues.Metadata.Columns, values) } +//nolint:sloglint // preserve emoji usage func (f *Fixtures) provision(t string, c []string, v [][]string) int64 { rows, err := f.db.ExecInsert(t, c, v...) if err != nil { diff --git a/service/internal/security/basic_manager.go b/service/internal/security/basic_manager.go index 68e5acfe76..f860d3f0ea 100644 --- a/service/internal/security/basic_manager.go +++ b/service/internal/security/basic_manager.go @@ -155,7 +155,11 @@ func (b *BasicManager) unwrap(ctx context.Context, kid string, wrappedKey string if privKeyBytes, ok := privKey.([]byte); ok { return privKeyBytes, nil } - b.l.ErrorContext(ctx, "private key in cache is not of type []byte", slog.String("kid", kid), slog.Any("type", fmt.Sprintf("%T", privKey))) + b.l.ErrorContext(ctx, + "private key in cache is not of type []byte", + slog.String("kid", kid), + slog.Any("type", fmt.Sprintf("%T", privKey)), + ) return nil, errors.New("private key in cache is not of type []byte") } b.l.DebugContext(ctx, "private key not found in cache", slog.String("kid", kid)) @@ -181,7 +185,11 @@ func (b *BasicManager) unwrap(ctx context.Context, kid string, wrappedKey string if cacheEnabled { if err := b.cache.Set(ctx, kid, privKey, nil); err != nil { - b.l.ErrorContext(ctx, "failed to cache private key", slog.String("kid", kid), slog.String("error", err.Error())) + b.l.ErrorContext(ctx, + "failed to cache private key", + slog.String("kid", kid), + slog.Any("error", err), + ) } } diff --git a/service/internal/security/in_process_provider.go b/service/internal/security/in_process_provider.go index b26a8af22d..074e615da3 100644 --- a/service/internal/security/in_process_provider.go +++ b/service/internal/security/in_process_provider.go @@ -60,7 +60,7 @@ func (k *InProcessAESKey) Export(encapsulator trust.Encapsulator) ([]byte, error encryptedKey, err := encapsulator.Encrypt(k.rawKey) if err != nil { if k.logger != nil { - k.logger.Warn("failed to encrypt key data for export", "err", err) + k.logger.Warn("failed to encrypt key data for export", slog.Any("err", err)) } return nil, err } @@ -267,7 +267,7 @@ func (a *InProcessProvider) FindKeyByID(_ context.Context, id trust.KeyIdentifie } // ListKeys lists all available keys -func (a *InProcessProvider) ListKeys(_ context.Context) ([]trust.KeyDetails, error) { +func (a *InProcessProvider) ListKeys(ctx context.Context) ([]trust.KeyDetails, error) { // This is a limited implementation as CryptoProvider doesn't expose a list of all keys var keys []trust.KeyDetails @@ -284,7 +284,11 @@ func (a *InProcessProvider) ListKeys(_ context.Context) ([]trust.KeyDetails, err } } else if err != nil { if a.logger != nil { - a.logger.Warn("failed to list keys by algorithm", "algorithm", alg, "error", err) + a.logger.WarnContext(ctx, + "failed to list keys by algorithm", + slog.String("algorithm", alg), + slog.Any("error", err), + ) } } } diff --git a/service/internal/security/standard_crypto.go b/service/internal/security/standard_crypto.go index 888237f58d..246a67e5fa 100644 --- a/service/internal/security/standard_crypto.go +++ b/service/internal/security/standard_crypto.go @@ -113,7 +113,10 @@ func loadKeys(ks []KeyPairInfo) (*StandardCrypto, error) { keysByAlg := make(map[string]keylist) keysByID := make(keylist) for _, k := range ks { - slog.Info("crypto cfg loading", "id", k.KID, "alg", k.Algorithm) + slog.Info("crypto cfg loading", + slog.Any("id", k.KID), + slog.Any("alg", k.Algorithm), + ) if _, ok := keysByID[k.KID]; ok { return nil, fmt.Errorf("duplicate key identifier [%s]", k.KID) } @@ -217,7 +220,10 @@ func loadDeprecatedKeys(rsaKeys map[string]StandardKeyInfo, ecKeys map[string]St keysByID[id] = k } for id, kasInfo := range ecKeys { - slog.Info("cfg.ECKeys", "id", id, "kasInfo", kasInfo) + slog.Info("cfg.ECKeys", + slog.String("id", id), + slog.Any("kasInfo", kasInfo), + ) // private and public EC KAS key privatePemData, err := os.ReadFile(kasInfo.PrivateKeyPath) if err != nil { diff --git a/service/internal/security/standard_only.go b/service/internal/security/standard_only.go index 11fd22415d..743bca23cd 100644 --- a/service/internal/security/standard_only.go +++ b/service/internal/security/standard_only.go @@ -21,7 +21,7 @@ func NewCryptoProvider(cfg Config) (*StandardCrypto, error) { return NewStandardCrypto(cfg.StandardConfig) default: if cfg.Type != "" { - slog.Warn("unsupported crypto type", "crypto.type", cfg.Type) + slog.Warn("unsupported crypto type", slog.String("crypto_type", cfg.Type)) } return NewStandardCrypto(cfg.StandardConfig) } diff --git a/service/internal/server/server.go b/service/internal/server/server.go index b88da4d0cb..eedac5f75e 100644 --- a/service/internal/server/server.go +++ b/service/internal/server/server.go @@ -552,15 +552,15 @@ func (s OpenTDFServer) openHTTPServerPort() (net.Listener, error) { func (s OpenTDFServer) startHTTPServer(ln net.Listener) { var err error if s.HTTPServer.TLSConfig != nil { - s.logger.Info("starting https server", "address", s.HTTPServer.Addr) + s.logger.Info("starting https server", slog.String("address", s.HTTPServer.Addr)) err = s.HTTPServer.ServeTLS(ln, "", "") } else { - s.logger.Info("starting http server", "address", s.HTTPServer.Addr) + s.logger.Info("starting http server", slog.String("address", s.HTTPServer.Addr)) err = s.HTTPServer.Serve(ln) } if err != nil && !errors.Is(err, http.ErrServerClosed) { - s.logger.Error("failed to serve http", slog.String("error", err.Error())) + s.logger.Error("failed to serve http", slog.Any("error", err)) } } diff --git a/service/internal/subjectmappingbuiltin/subject_mapping_builtin.go b/service/internal/subjectmappingbuiltin/subject_mapping_builtin.go index 01e12bcf57..c66f62ac77 100644 --- a/service/internal/subjectmappingbuiltin/subject_mapping_builtin.go +++ b/service/internal/subjectmappingbuiltin/subject_mapping_builtin.go @@ -25,7 +25,7 @@ func SubjectMappingBuiltin() { Memoize: true, Nondeterministic: true, }, func(_ rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error) { - slog.Debug("Subject mapping plugin invoked") + slog.Debug("subject mapping plugin invoked") // input handling var attributeMappingsMap map[string]string diff --git a/service/kas/access/accessPdp.go b/service/kas/access/accessPdp.go index ce603d9530..0c9359bc58 100644 --- a/service/kas/access/accessPdp.go +++ b/service/kas/access/accessPdp.go @@ -35,7 +35,7 @@ func (p *Provider) canAccess(ctx context.Context, token *entity.Token, policies for i, policy := range policies { if len(policy.Body.Dissem) > 0 { // TODO: Move dissems check to the getdecisions endpoint - p.Logger.Error("Dissems check is not enabled in v2 platform kas") + p.Logger.Error("dissems check is not enabled in v2 platform kas") } if len(policy.Body.DataAttributes) > 0 { id := "rewrap-" + strconv.Itoa(i) @@ -60,7 +60,7 @@ func (p *Provider) canAccess(ctx context.Context, token *entity.Token, policies // If no data attributes were found in any policies, return early with the results // instead of roundtripping to get a decision on no resources if len(resources) == 0 { - p.Logger.DebugContext(ctx, "No resources to check") + p.Logger.DebugContext(ctx, "no resources to check") return res, nil } @@ -75,7 +75,7 @@ func (p *Provider) canAccess(ctx context.Context, token *entity.Token, policies for _, decision := range resourceDecisions { policy, ok := idPolicyMap[decision.GetEphemeralResourceId()] if !ok { // this really should not happen - p.Logger.WarnContext(ctx, "Unexpected ephemeral resource id not mapped to a policy", "decision response ephemeral resource ID", decision.GetEphemeralResourceId()) + p.Logger.WarnContext(ctx, "unexpected ephemeral resource id not mapped to a policy") continue } res = append(res, PDPAccessResult{Policy: policy, Access: decision.GetDecision() == authzV2.Decision_DECISION_PERMIT}) @@ -99,7 +99,7 @@ func (p *Provider) checkAttributes(ctx context.Context, resources []*authzV2.Res } dr, err := p.SDK.AuthorizationV2.GetDecision(ctx, req) if err != nil { - p.Logger.ErrorContext(ctx, "Error received from GetDecision", "err", err) + p.Logger.ErrorContext(ctx, "error received from GetDecision") return nil, errors.Join(ErrDecisionUnexpected, err) } return []*authzV2.ResourceDecision{dr.GetDecision()}, nil @@ -116,7 +116,7 @@ func (p *Provider) checkAttributes(ctx context.Context, resources []*authzV2.Res dr, err := p.SDK.AuthorizationV2.GetDecisionMultiResource(ctx, req) if err != nil { - p.Logger.ErrorContext(ctx, "Error received from GetDecisionMultiResource", "err", err) + p.Logger.ErrorContext(ctx, "error received from GetDecisionMultiResource") return nil, errors.Join(ErrDecisionUnexpected, err) } return dr.GetResourceDecisions(), nil diff --git a/service/kas/access/publicKey.go b/service/kas/access/publicKey.go index 30874604d7..eb5b1fe780 100644 --- a/service/kas/access/publicKey.go +++ b/service/kas/access/publicKey.go @@ -7,6 +7,7 @@ import ( "crypto/x509" "encoding/pem" "errors" + "log/slog" "connectrpc.com/connect" kaspb "github.com/opentdf/platform/protocol/go/kas" @@ -26,11 +27,11 @@ func (p *Provider) lookupKid(ctx context.Context, algorithm string) (string, err if err == nil { return string(k.ID()), nil } - p.Logger.WarnContext(ctx, "KeyIndex.FindKeyByAlgorithm failed", "err", err) + p.Logger.WarnContext(ctx, "checkpoint KeyIndex.FindKeyByAlgorithm failed", slog.Any("error", err)) } if len(p.Keyring) == 0 { - p.Logger.WarnContext(ctx, "no default keys found", "algorithm", algorithm) + p.Logger.WarnContext(ctx, "no default keys found", slog.String("algorithm", algorithm)) return "", connect.NewError(connect.CodeNotFound, errors.Join(ErrConfig, errors.New("no default keys configured"))) } @@ -39,7 +40,7 @@ func (p *Provider) lookupKid(ctx context.Context, algorithm string) (string, err return k.KID, nil } } - p.Logger.WarnContext(ctx, "no (non-legacy) key for requested algorithm", "algorithm", algorithm) + p.Logger.WarnContext(ctx, "no (non-legacy) key for requested algorithm", slog.String("algorithm", algorithm)) return "", connect.NewError(connect.CodeNotFound, errors.Join(ErrConfig, errors.New("no default key for algorithm"))) } @@ -63,7 +64,7 @@ func (p *Provider) LegacyPublicKey(ctx context.Context, req *connect.Request[kas // Find the key by ID keyDetails, err := p.KeyDelegator.FindKeyByID(ctx, keyID) if err != nil { - p.Logger.ErrorContext(ctx, "SecurityProvider.FindKeyByID failed", "err", err) + p.Logger.ErrorContext(ctx, "checkpoint SecurityProvider.FindKeyByID failed", slog.Any("error", err)) return nil, connect.NewError(connect.CodeInternal, errors.Join(ErrConfig, errors.New("configuration error"))) } @@ -72,7 +73,7 @@ func (p *Provider) LegacyPublicKey(ctx context.Context, req *connect.Request[kas // For EC keys, return the certificate pem, err = keyDetails.ExportCertificate(ctx) if err != nil { - p.Logger.ErrorContext(ctx, "keyDetails.ExportCertificate failed", "err", err) + p.Logger.ErrorContext(ctx, "keyDetails.ExportCertificate failed", slog.Any("error", err)) return nil, connect.NewError(connect.CodeInternal, errors.Join(ErrConfig, errors.New("configuration error"))) } case security.AlgorithmRSA2048: @@ -81,7 +82,7 @@ func (p *Provider) LegacyPublicKey(ctx context.Context, req *connect.Request[kas // For RSA keys, return the public key in PKCS8 format pem, err = keyDetails.ExportPublicKey(ctx, trust.KeyTypePKCS8) if err != nil { - p.Logger.ErrorContext(ctx, "keyDetails.ExportPublicKey failed", "err", err) + p.Logger.ErrorContext(ctx, "keyDetails.ExportPublicKey failed", slog.Any("error", err)) return nil, connect.NewError(connect.CodeInternal, errors.Join(ErrConfig, errors.New("configuration error"))) } default: @@ -112,14 +113,30 @@ func (p *Provider) PublicKey(ctx context.Context, req *connect.Request[kaspb.Pub r := func(value, kid string, err error) (*connect.Response[kaspb.PublicKeyResponse], error) { if errors.Is(err, security.ErrCertNotFound) { - p.Logger.ErrorContext(ctx, "no key found for", "err", err, "kid", kid, "algorithm", algorithm, "fmt", fmt) + p.Logger.ErrorContext(ctx, + "no key found for", + slog.String("kid", kid), + slog.String("algorithm", algorithm), + slog.String("fmt", fmt), + slog.Any("err", err), + ) return nil, connect.NewError(connect.CodeNotFound, security.ErrCertNotFound) } else if err != nil { - p.Logger.ErrorContext(ctx, "configuration error for key lookup", "err", err, "kid", kid, "algorithm", algorithm, "fmt", fmt) + p.Logger.ErrorContext(ctx, + "configuration error for key lookup", + slog.String("kid", kid), + slog.String("algorithm", algorithm), + slog.String("fmt", fmt), + slog.Any("err", err), + ) return nil, connect.NewError(connect.CodeInternal, ErrInternal) } if req.Msg.GetV() == "1" { - p.Logger.WarnContext(ctx, "hiding kid in public key response for legacy client", "kid", kid, "v", req.Msg.GetV()) + p.Logger.WarnContext(ctx, + "hiding kid in public key response for legacy client", + slog.String("kid", kid), + slog.String("v", req.Msg.GetV()), + ) return connect.NewResponse(&kaspb.PublicKeyResponse{PublicKey: value}), nil } return connect.NewResponse(&kaspb.PublicKeyResponse{PublicKey: value, Kid: kid}), nil diff --git a/service/kas/access/rewrap.go b/service/kas/access/rewrap.go index 60792ec8b0..72777f8b44 100644 --- a/service/kas/access/rewrap.go +++ b/service/kas/access/rewrap.go @@ -116,7 +116,12 @@ var acceptableSkew = 30 * time.Second func verifySRT(ctx context.Context, srt string, dpopJWK jwk.Key, logger logger.Logger) (string, error) { token, err := jwt.Parse([]byte(srt), jwt.WithKey(jwa.RS256, dpopJWK), jwt.WithAcceptableSkew(acceptableSkew)) if err != nil { - logger.WarnContext(ctx, "unable to verify request token", "err", err, "srt", srt, "jwk", dpopJWK) + logger.WarnContext(ctx, + "unable to verify request token", + slog.String("srt", srt), + slog.Any("jwk", dpopJWK), + slog.Any("err", err), + ) return "", err401("unable to verify request token") } return justRequestBody(ctx, token, logger) @@ -125,7 +130,7 @@ func verifySRT(ctx context.Context, srt string, dpopJWK jwk.Key, logger logger.L func noverify(ctx context.Context, srt string, logger logger.Logger) (string, error) { token, err := jwt.Parse([]byte(srt), jwt.WithVerify(false), jwt.WithAcceptableSkew(acceptableSkew)) if err != nil { - logger.WarnContext(ctx, "unable to validate or parse token", "err", err) + logger.WarnContext(ctx, "unable to validate or parse token", slog.Any("error", err)) return "", err401("could not parse token") } return justRequestBody(ctx, token, logger) @@ -193,7 +198,7 @@ func extractSRTBody(ctx context.Context, headers http.Header, in *kaspb.RewrapRe isV1 := false // First load legacy method for verifying SRT if vpk, ok := headers["X-Virtrupubkey"]; ok && len(vpk) == 1 { - logger.InfoContext(ctx, "Legacy Client: Processing X-Virtrupubkey") + logger.InfoContext(ctx, "legacy Client: Processing X-Virtrupubkey") } // get dpop public key from context @@ -210,7 +215,7 @@ func extractSRTBody(ctx context.Context, headers http.Header, in *kaspb.RewrapRe // in either case letting the request through makes sense rbString, err = noverify(ctx, srt, logger) if err != nil { - logger.ErrorContext(ctx, "unable to load RSA verifier", "err", err) + logger.ErrorContext(ctx, "unable to load RSA verifier", slog.Any("error", err)) return nil, false, err } } else { @@ -226,7 +231,11 @@ func extractSRTBody(ctx context.Context, headers http.Header, in *kaspb.RewrapRe err = protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal([]byte(rbString), &requestBody) // if there are no requests then it could be a v1 request if err != nil { - logger.WarnContext(ctx, "invalid SRT", "err.v2", err, "srt", rbString) + logger.WarnContext(ctx, + "invalid SRT", + slog.Any("err_v2", err), + slog.String("srt", rbString), + ) return nil, false, err400("invalid request body") } if len(requestBody.GetRequests()) == 0 { @@ -234,12 +243,22 @@ func extractSRTBody(ctx context.Context, headers http.Header, in *kaspb.RewrapRe var errv1 error if requestBody, errv1 = extractAndConvertV1SRTBody([]byte(rbString)); errv1 != nil { - logger.WarnContext(ctx, "invalid SRT", "err.v1", errv1, "srt", rbString, "rewrap.body", requestBody.String()) + logger.WarnContext(ctx, + "invalid SRT", + slog.Any("err_v1", errv1), + slog.String("srt", rbString), + slog.String("rewrap.body", requestBody.String()), + ) return nil, false, err400("invalid request body") } isV1 = true } - logger.DebugContext(ctx, "extracted request body", slog.String("rewrap.body", requestBody.String()), slog.Any("rewrap.srt", rbString)) + // TODO: this log is too big and should be reconsidered or removed + logger.DebugContext(ctx, + "extracted request body", + slog.String("rewrap_body", requestBody.String()), + slog.String("rewrap_srt", rbString), + ) block, _ := pem.Decode([]byte(requestBody.GetClientPublicKey())) if block == nil { @@ -250,7 +269,7 @@ func extractSRTBody(ctx context.Context, headers http.Header, in *kaspb.RewrapRe // Try to parse the clientPublicKey clientPublicKey, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { - logger.WarnContext(ctx, "failure to parse clientPublicKey", "err", err) + logger.WarnContext(ctx, "failure to parse clientPublicKey", slog.Any("error", err)) return nil, isV1, err400("clientPublicKey parse failure") } // Check to make sure the clientPublicKey is a supported key type @@ -260,7 +279,7 @@ func extractSRTBody(ctx context.Context, headers http.Header, in *kaspb.RewrapRe case *ecdsa.PublicKey: return &requestBody, isV1, nil default: - logger.WarnContext(ctx, fmt.Sprintf("clientPublicKey not a supported key, was [%T]", clientPublicKey)) + logger.WarnContext(ctx, "unsupported clientPublicKey type", slog.String("type", fmt.Sprintf("%T", clientPublicKey))) return nil, isV1, err400("clientPublicKey unsupported type") } } @@ -268,7 +287,7 @@ func extractSRTBody(ctx context.Context, headers http.Header, in *kaspb.RewrapRe func verifyPolicyBinding(ctx context.Context, policy []byte, kao *kaspb.UnsignedRewrapRequest_WithKeyAccessObject, symKey []byte, logger logger.Logger) error { actualHMAC, err := generateHMACDigest(ctx, policy, symKey, logger) if err != nil { - logger.WarnContext(ctx, "unable to generate policy hmac", "err", err) + logger.WarnContext(ctx, "unable to generate policy hmac", slog.Any("error", err)) return err400("bad request") } @@ -280,11 +299,12 @@ func verifyPolicyBinding(ctx context.Context, policy []byte, kao *kaspb.Unsigned } expectedHMAC = expectedHMAC[:n] if err != nil { - logger.WarnContext(ctx, "invalid policy binding", "err", err) + logger.WarnContext(ctx, "invalid policy binding", slog.Any("error", err)) return err400("bad request") } if !hmac.Equal(actualHMAC, expectedHMAC) { - logger.WarnContext(ctx, "policy hmac mismatch", "policyBinding", policyBinding) + //nolint:sloglint // usage of camelCase is intentional + logger.WarnContext(ctx, "policy hmac mismatch", slog.String("policyBinding", policyBinding)) return err400("bad request") } @@ -382,13 +402,13 @@ func (p *Provider) Rewrap(ctx context.Context, req *connect.Request[kaspb.Rewrap body, isV1, err := extractSRTBody(ctx, req.Header(), in, *p.Logger) if err != nil { - p.Logger.DebugContext(ctx, "unverifiable srt", "err", err) + p.Logger.DebugContext(ctx, "unverifiable srt", slog.Any("error", err)) return nil, err } entityInfo, err := getEntityInfo(ctx, p.Logger) if err != nil { - p.Logger.DebugContext(ctx, "no entity info", "err", err) + p.Logger.DebugContext(ctx, "no entity info", slog.Any("error", err)) return nil, err } @@ -418,18 +438,22 @@ func (p *Provider) Rewrap(ctx context.Context, req *connect.Request[kaspb.Rewrap if isV1 { if len(results) != 1 { - p.Logger.WarnContext(ctx, "400 due to wrong result set size", "results", results) + p.Logger.WarnContext(ctx, "status 400 due to wrong result set size", slog.Any("results", results)) return nil, err400("invalid request") } kaoResults := *getMapValue(results) if len(kaoResults) != 1 { - p.Logger.WarnContext(ctx, "400 due to wrong result set size", "kaoResults", kaoResults, "results", results) + p.Logger.WarnContext(ctx, + "status 400 due to wrong result set size", + slog.Any("kao_results", kaoResults), + slog.Any("results", results), + ) return nil, err400("invalid request") } kao := *getMapValue(kaoResults) if kao.Error != nil { - p.Logger.DebugContext(ctx, "forwarding legacy err", "err", kao.Error) + p.Logger.DebugContext(ctx, "forwarding legacy err", slog.Any("error", kao.Error)) return nil, kao.Error } resp.EntityWrappedKey = kao.Encapped //nolint:staticcheck // deprecated but keeping behavior for backwards compatibility @@ -444,7 +468,8 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned results := make(map[string]kaoResult) anyValidKAOs := false - p.Logger.DebugContext(ctx, "extracting policy", "requestBody.policy", req.GetPolicy()) + + p.Logger.DebugContext(ctx, "extracting policy", slog.Any("policy", req.GetPolicy())) sDecPolicy, policyErr := base64.StdEncoding.DecodeString(req.GetPolicy().GetBody()) policy := &Policy{} if policyErr == nil { @@ -474,14 +499,22 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned // Get EC key size and convert to mode keySize, err := ocrypto.GetECKeySize([]byte(ephemeralPubKeyPEM)) if err != nil { - p.Logger.WarnContext(ctx, "failed to get EC key size", "err", err, "kao", kao) + p.Logger.WarnContext(ctx, + "failed to get EC key size", + slog.Any("kao", kao), + slog.Any("error", err), + ) failedKAORewrap(results, kao, err400("bad request")) continue } mode, err := ocrypto.ECSizeToMode(keySize) if err != nil { - p.Logger.WarnContext(ctx, "failed to convert key size to mode", "err", err, "kao", kao) + p.Logger.WarnContext(ctx, + "failed to convert key size to mode", + slog.Any("kao", kao), + slog.Any("error", err), + ) failedKAORewrap(results, kao, err400("bad request")) continue } @@ -489,21 +522,29 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned // Parse the PEM public key block, _ := pem.Decode([]byte(ephemeralPubKeyPEM)) if block == nil { - p.Logger.WarnContext(ctx, "failed to decode PEM block", "err", err, "kao", kao) + p.Logger.WarnContext(ctx, + "failed to decode PEM block", + slog.Any("kao", kao), + slog.Any("error", err), + ) failedKAORewrap(results, kao, err400("bad request")) continue } pub, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { - p.Logger.WarnContext(ctx, "failed to parse public key", "err", err, "kao", kao) + p.Logger.WarnContext(ctx, + "failed to parse public key", + slog.Any("kao", kao), + slog.Any("error", err), + ) failedKAORewrap(results, kao, err400("bad request")) continue } ecPub, ok := pub.(*ecdsa.PublicKey) if !ok { - p.Logger.WarnContext(ctx, "not an EC public key", "err", err) + p.Logger.WarnContext(ctx, "not an EC public key", slog.Any("error", err)) failedKAORewrap(results, kao, err400("bad request")) continue } @@ -511,7 +552,7 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned // Compress the public key compressedKey, err := ocrypto.CompressedECPublicKey(mode, *ecPub) if err != nil { - p.Logger.WarnContext(ctx, "failed to compress public key", "err", err) + p.Logger.WarnContext(ctx, "failed to compress public key", slog.Any("error", err)) failedKAORewrap(results, kao, err400("bad request")) continue } @@ -519,7 +560,7 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned kid := trust.KeyIdentifier(kao.GetKeyAccessObject().GetKid()) dek, err = p.KeyDelegator.Decrypt(ctx, kid, kao.GetKeyAccessObject().GetWrappedKey(), compressedKey) if err != nil { - p.Logger.WarnContext(ctx, "failed to decrypt EC key", "err", err) + p.Logger.WarnContext(ctx, "failed to decrypt EC key", slog.Any("error", err)) failedKAORewrap(results, kao, err400("bad request")) continue } @@ -539,7 +580,7 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned dek, err = p.KeyDelegator.Decrypt(ctx, kidsToCheck[0], kao.GetKeyAccessObject().GetWrappedKey(), nil) for _, kid := range kidsToCheck[1:] { - p.Logger.WarnContext(ctx, "continue paging through legacy KIDs for kid free kao", "err", err) + p.Logger.WarnContext(ctx, "continue paging through legacy KIDs for kid free kao", slog.Any("error", err)) if err == nil { break } @@ -547,7 +588,7 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned } } if err != nil { - p.Logger.WarnContext(ctx, "failure to decrypt dek", "err", err) + p.Logger.WarnContext(ctx, "failure to decrypt dek", slog.Any("error", err)) failedKAORewrap(results, kao, err400("bad request")) continue } @@ -557,7 +598,7 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned policyBinding := make([]byte, base64.StdEncoding.DecodedLen(len(policyBindingB64Encoded))) n, err := base64.StdEncoding.Decode(policyBinding, []byte(policyBindingB64Encoded)) if err != nil { - p.Logger.WarnContext(ctx, "invalid policy binding encoding", "err", err) + p.Logger.WarnContext(ctx, "invalid policy binding encoding", slog.Any("error", err)) failedKAORewrap(results, kao, err400("bad request")) continue } @@ -573,7 +614,7 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned // Verify policy binding using the UnwrappedKeyData interface if err := dek.VerifyBinding(ctx, []byte(req.GetPolicy().GetBody()), policyBinding); err != nil { - p.Logger.WarnContext(ctx, "failure to verify policy binding", "err", err) + p.Logger.WarnContext(ctx, "failure to verify policy binding", slog.Any("error", err)) failedKAORewrap(results, kao, err400("bad request")) continue } @@ -613,7 +654,7 @@ func (p *Provider) listLegacyKeys(ctx context.Context) []trust.KeyIdentifier { k, err := p.KeyDelegator.ListKeys(ctx) if err != nil { - p.Logger.WarnContext(ctx, "KeyIndex.ListKeys failed", "err", err) + p.Logger.WarnContext(ctx, "checkpoint KeyIndex.ListKeys failed", slog.Any("error", err)) } else { for _, key := range k { if key.Algorithm() == security.AlgorithmRSA2048 && key.IsLegacy() { @@ -639,7 +680,11 @@ func (p *Provider) tdf3Rewrap(ctx context.Context, requests []*kaspb.UnsignedRew policyID := req.GetPolicy().GetId() results[policyID] = kaoResults if err != nil { - p.Logger.WarnContext(ctx, "rewrap: verifyRewrapRequests failed", "err", err, "policyID", policyID) + p.Logger.WarnContext(ctx, + "rewrap: verifyRewrapRequests failed", + slog.String("policy_id", policyID), + slog.Any("error", err), + ) continue } policies = append(policies, policy) @@ -652,14 +697,18 @@ func (p *Provider) tdf3Rewrap(ctx context.Context, requests []*kaspb.UnsignedRew } pdpAccessResults, accessErr := p.canAccess(ctx, tok, policies) if accessErr != nil { - p.Logger.DebugContext(ctx, "tdf3rewrap: cannot access policy", "err", accessErr, "policies", policies) + p.Logger.DebugContext(ctx, + "tdf3rewrap: cannot access policy", + slog.Any("policies", policies), + slog.Any("error", accessErr), + ) failAllKaos(requests, results, err500("could not perform access")) return "", results } asymEncrypt, err := ocrypto.FromPublicPEMWithSalt(clientPublicKey, security.TDFSalt(), nil) if err != nil { - p.Logger.WarnContext(ctx, "ocrypto.NewAsymEncryption:", "err", err) + p.Logger.WarnContext(ctx, "ocrypto.NewAsymEncryption", slog.Any("error", err)) failAllKaos(requests, results, err400("invalid request")) return "", results } @@ -668,7 +717,7 @@ func (p *Provider) tdf3Rewrap(ctx context.Context, requests []*kaspb.UnsignedRew if e, ok := asymEncrypt.(ocrypto.ECEncryptor); ok { sessionKey, err = e.PublicKeyInPemFormat() if err != nil { - p.Logger.ErrorContext(ctx, "unable to serialize ephemeral key", "err", err) + p.Logger.ErrorContext(ctx, "unable to serialize ephemeral key", slog.Any("error", err)) // This may be a 500, but could also be caused by a bad clientPublicKey failAllKaos(requests, results, err400("invalid request")) return "", results @@ -684,11 +733,13 @@ func (p *Provider) tdf3Rewrap(ctx context.Context, requests []*kaspb.UnsignedRew policy := pdpAccess.Policy req, ok := policyReqs[policy] if !ok { + //nolint:sloglint // reference to key is intentional p.Logger.WarnContext(ctx, "policy not found in policyReqs", "policy.uuid", policy.UUID) continue } kaoResults, ok := results[req.GetPolicy().GetId()] if !ok { // this should not happen + //nolint:sloglint // reference to key is intentional p.Logger.WarnContext(ctx, "policy not found in policyReq response", "policy.uuid", policy.UUID) continue } @@ -722,7 +773,8 @@ func (p *Provider) tdf3Rewrap(ctx context.Context, requests []*kaspb.UnsignedRew // Use the Export method with the asymEncrypt encryptor encryptedKey, err := kaoRes.DEK.Export(asymEncrypt) if err != nil { - p.Logger.WarnContext(ctx, "rewrap: Export with encryptor failed", "err", err, "clientPublicKey", clientPublicKey) + //nolint:sloglint // reference to camelcase key is intentional + p.Logger.WarnContext(ctx, "rewrap: Export with encryptor failed", slog.String("clientPublicKey", clientPublicKey), slog.Any("error", err)) p.Logger.Audit.RewrapFailure(ctx, auditEventParams) failedKAORewrap(kaoResults, kao, err400("bad key for rewrap")) continue @@ -770,13 +822,13 @@ func (p *Provider) nanoTDFRewrap(ctx context.Context, requests []*kaspb.Unsigned sessionKey, err := p.KeyDelegator.GenerateECSessionKey(ctx, clientPublicKey) if err != nil { - p.Logger.WarnContext(ctx, "failure in GenerateNanoTDFSessionKey", "err", err) + p.Logger.WarnContext(ctx, "failure in GenerateNanoTDFSessionKey", slog.Any("error", err)) failAllKaos(requests, results, err400("keypair mismatch")) return "", results } sessionKeyPEM, err := sessionKey.PublicKeyInPemFormat() if err != nil { - p.Logger.WarnContext(ctx, "failure in PublicKeyToPem", "err", err) + p.Logger.WarnContext(ctx, "failure in PublicKeyToPem", slog.Any("error", err)) failAllKaos(requests, results, err500("")) return "", results } @@ -848,17 +900,21 @@ func (p *Provider) verifyNanoRewrapRequests(ctx context.Context, req *kaspb.Unsi // Lookup KID from nano header kid, err := header.GetKasURL().GetIdentifier() if err != nil { - p.Logger.DebugContext(ctx, "nanoTDFRewrap GetIdentifier", "kid", kid, "err", err) + p.Logger.DebugContext(ctx, + "nanoTDFRewrap GetIdentifier", + slog.String("kid", kid), + slog.Any("error", err), + ) // legacy nano with KID kid, err = p.lookupKid(ctx, security.AlgorithmECP256R1) if err != nil { - p.Logger.ErrorContext(ctx, "failure to find default kid for ec", "err", err) + p.Logger.ErrorContext(ctx, "failure to find default kid for ec", slog.Any("error", err)) failedKAORewrap(results, kao, err400("bad request")) continue } - p.Logger.DebugContext(ctx, "nanoTDFRewrap lookupKid", "kid", kid) + p.Logger.DebugContext(ctx, "nanoTDFRewrap lookupKid", slog.String("kid", kid)) } - p.Logger.DebugContext(ctx, "nanoTDFRewrap", "kid", kid) + p.Logger.DebugContext(ctx, "nanoTDFRewrap", slog.String("kid", kid)) ecCurve, err := header.ECCurve() if err != nil { failedKAORewrap(results, kao, fmt.Errorf("ECCurve failed: %w", err)) diff --git a/service/kas/access/rewrap_test.go b/service/kas/access/rewrap_test.go index 997a879f99..521003ae9c 100644 --- a/service/kas/access/rewrap_test.go +++ b/service/kas/access/rewrap_test.go @@ -344,10 +344,10 @@ func keyAccessWrappedRaw(t *testing.T, policyBindingAsString bool) kaspb.Unsigne type RSAPublicKey rsa.PublicKey -func (publicKey *RSAPublicKey) VerifySignature(_ context.Context, raw string) ([]byte, error) { +func (publicKey *RSAPublicKey) VerifySignature(ctx context.Context, raw string) ([]byte, error) { tok, err := jws.Verify([]byte(raw), jws.WithKey(jwa.RS256, rsa.PublicKey(*publicKey))) if err != nil { - slog.Error("jws.Verify fail", "raw", raw) + slog.ErrorContext(ctx, "jws.Verify fail", slog.String("raw", raw)) return nil, err } return tok, nil diff --git a/service/kas/kas.go b/service/kas/kas.go index 0280e58b37..25f9e1526c 100644 --- a/service/kas/kas.go +++ b/service/kas/kas.go @@ -101,7 +101,7 @@ func NewRegistration() *serviceregistry.Service[kasconnect.AccessServiceHandler] } if kasCfg.Preview.KeyManagement { - srp.Logger.Info("Preview Feature: Key management is enabled") + srp.Logger.Info("preview feature: key management is enabled") // Configure new delegation service p.KeyDelegator = trust.NewDelegatingKeyService(NewPlatformKeyIndexer(srp.SDK, kasURLString, srp.Logger), srp.Logger, cacheClient) @@ -142,7 +142,7 @@ func NewRegistration() *serviceregistry.Service[kasconnect.AccessServiceHandler] p.KASConfig = kasCfg p.Tracer = srp.Tracer - srp.Logger.Debug("kas config", "config", kasCfg) + srp.Logger.Debug("kas config", slog.Any("config", kasCfg)) if err := srp.RegisterReadinessCheck("kas", p.IsReady); err != nil { srp.Logger.Error("failed to register kas readiness check", slog.String("error", err.Error())) @@ -170,7 +170,7 @@ func initSecurityProviderAdapter(cryptoProvider *security.StandardCrypto, kasCfg if kid != "" { defaults = append(defaults, kid) } else { - l.Warn("no default key found for algorithm", "algorithm", alg) + l.Warn("no default key found for algorithm", slog.String("algorithm", alg)) } } } diff --git a/service/logger/audit/logger.go b/service/logger/audit/logger.go index b763f00907..e7b6a92b99 100644 --- a/service/logger/audit/logger.go +++ b/service/logger/audit/logger.go @@ -49,6 +49,7 @@ func CreateAuditLogger(logger slog.Logger) *Logger { func (a *Logger) With(key string, value string) *Logger { return &Logger{ + //nolint:sloglint // custom logger should support key/value pairs in With attributes logger: a.logger.With(key, value), } } @@ -73,38 +74,38 @@ func (a *Logger) PolicyCRUDFailure(ctx context.Context, eventParams PolicyEventP func (a *Logger) GetDecision(ctx context.Context, eventParams GetDecisionEventParams) { auditEvent, err := CreateGetDecisionEvent(ctx, eventParams) if err != nil { - a.logger.ErrorContext(ctx, "error creating get decision audit event", "err", err) + a.logger.ErrorContext(ctx, "error creating get decision audit event", slog.Any("error", err)) return } - a.logger.Log(ctx, LevelAudit, "decision", "audit", *auditEvent) + a.logger.Log(ctx, LevelAudit, "decision", slog.Any("audit", *auditEvent)) } func (a *Logger) GetDecisionV2(ctx context.Context, eventParams GetDecisionV2EventParams) { event, err := CreateV2GetDecisionEvent(ctx, eventParams) if err != nil { - a.logger.ErrorContext(ctx, "error creating v2 get decision audit event", "err", err) + a.logger.ErrorContext(ctx, "error creating v2 get decision audit event", slog.Any("error", err)) return } - a.logger.Log(ctx, LevelAudit, "decision", "audit", *event) + a.logger.Log(ctx, LevelAudit, "decision", slog.Any("audit", *event)) } func (a *Logger) rewrapBase(ctx context.Context, eventParams RewrapAuditEventParams) { auditEvent, err := CreateRewrapAuditEvent(ctx, eventParams) if err != nil { - a.logger.ErrorContext(ctx, "error creating rewrap audit event", "err", err) + a.logger.ErrorContext(ctx, "error creating rewrap audit event", slog.Any("error", err)) return } - a.logger.Log(ctx, LevelAudit, "rewrap", "audit", *auditEvent) + a.logger.Log(ctx, LevelAudit, "rewrap", slog.Any("audit", *auditEvent)) } func (a *Logger) policyCrudBase(ctx context.Context, isSuccess bool, eventParams PolicyEventParams) { auditEvent, err := CreatePolicyEvent(ctx, isSuccess, eventParams) if err != nil { - a.logger.ErrorContext(ctx, "error creating policy attribute audit event", "err", err) + a.logger.ErrorContext(ctx, "error creating policy attribute audit event", slog.Any("error", err)) return } - a.logger.Log(ctx, LevelAudit, "policy crud", "audit", *auditEvent) + a.logger.Log(ctx, LevelAudit, "policy crud", slog.Any("audit", *auditEvent)) } diff --git a/service/logger/audit/utils_test.go b/service/logger/audit/utils_test.go index 51da797423..a2bb3d5545 100644 --- a/service/logger/audit/utils_test.go +++ b/service/logger/audit/utils_test.go @@ -2,7 +2,6 @@ package audit import ( "context" - "fmt" "log/slog" "net" "testing" @@ -25,7 +24,7 @@ func TestGetAuditDataFromContextHappyPath(t *testing.T) { ctx = context.WithValue(ctx, realip.ClientIP{}, testRequestIP) ctx = context.WithValue(ctx, sdkAudit.ActorIDContextKey, testActorID) - slog.Info(fmt.Sprintf("Test: %v", ctx.Value(sdkAudit.RequestIDContextKey))) + slog.Info("test", slog.Any("test", ctx.Value(sdkAudit.RequestIDContextKey))) auditData := GetAuditDataFromContext(ctx) diff --git a/service/logger/logger.go b/service/logger/logger.go index f5789f5a58..d829753aee 100644 --- a/service/logger/logger.go +++ b/service/logger/logger.go @@ -91,6 +91,7 @@ func NewLogger(config Config) (*Logger, error) { return logger, nil } +//nolint:sloglint // explicitly add key/value pairs to propagate to both loggers func (l *Logger) With(key string, value string) *Logger { return &Logger{ Logger: l.Logger.With(key, value), @@ -123,10 +124,12 @@ func getLevel(config Config) (slog.Leveler, error) { } func (l *Logger) Trace(msg string, args ...any) { + //nolint:sloglint // explicitly match the signature of slog.Log l.Log(context.Background(), LevelTrace, msg, args...) } func (l *Logger) TraceContext(ctx context.Context, msg string, args ...any) { + //nolint:sloglint // explicitly match the signature of slog.Log l.Log(ctx, LevelTrace, msg, args...) } diff --git a/service/pkg/cache/cache.go b/service/pkg/cache/cache.go index 290586fe20..31b14e7566 100644 --- a/service/pkg/cache/cache.go +++ b/service/pkg/cache/cache.go @@ -3,6 +3,7 @@ package cache import ( "context" "errors" + "log/slog" "time" "github.com/dgraph-io/ristretto" @@ -87,10 +88,17 @@ func (c *Cache) Get(ctx context.Context, key string) (any, error) { val, err := c.manager.cache.Get(ctx, c.getKey(key)) if err != nil { // All errors are a cache miss in the gocache library. - c.logger.Debug("cache miss", "key", key, "error", err) + c.logger.DebugContext(ctx, + "cache miss", + slog.Any("key", key), + slog.Any("error", err), + ) return nil, ErrCacheMiss } - c.logger.Debug("cache hit", "key", key) + c.logger.DebugContext(ctx, + "cache hit", + slog.Any("key", key), + ) return val, nil } @@ -104,10 +112,13 @@ func (c *Cache) Set(ctx context.Context, key string, object any, tags []string) err := c.manager.cache.Set(ctx, c.getKey(key), object, opts...) if err != nil { - c.logger.Error("set error", "key", key, "error", err) + c.logger.ErrorContext(ctx, "set error", + slog.Any("key", key), + slog.Any("error", err), + ) return err } - c.logger.Debug("set cache", "key", key) + c.logger.DebugContext(ctx, "set cache", slog.Any("key", key)) return nil } diff --git a/service/pkg/config/config.go b/service/pkg/config/config.go index 50f49b62a3..4983e2c0ef 100644 --- a/service/pkg/config/config.go +++ b/service/pkg/config/config.go @@ -124,11 +124,11 @@ func (c *Config) Watch(ctx context.Context) error { } // Close invokes close method on all config loaders. -func (c *Config) Close(_ context.Context) error { +func (c *Config) Close(ctx context.Context) error { if len(c.loaders) == 0 { return nil } - slog.Debug("Closing config loaders") + slog.DebugContext(ctx, "closing config loaders") for _, loader := range c.loaders { if err := loader.Close(); err != nil { return err diff --git a/service/pkg/config/loader.go b/service/pkg/config/loader.go index 5bb0f9ddee..6a6454de0b 100644 --- a/service/pkg/config/loader.go +++ b/service/pkg/config/loader.go @@ -93,9 +93,9 @@ func (l *EnvironmentLoader) Load(cfg *Config) error { } // Watch starts watching the config file for configuration changes -func (l *EnvironmentLoader) Watch(_ context.Context, cfg *Config, onChange func(context.Context) error) error { +func (l *EnvironmentLoader) Watch(ctx context.Context, cfg *Config, onChange func(context.Context) error) error { if len(cfg.onConfigChangeHooks) == 0 { - slog.Debug("No config change hooks registered. Skipping environment config watch.") + slog.DebugContext(ctx, "no config change hooks registered. Skipping environment config watch") return nil } @@ -104,28 +104,32 @@ func (l *EnvironmentLoader) Watch(_ context.Context, cfg *Config, onChange func( // If config changes, reload it and invoke all hooks //nolint:contextcheck // false positive with external library function signature l.viper.OnConfigChange(func(e fsnotify.Event) { - slog.Debug("Environment config file changed", "file", e.Name) + slog.DebugContext(ctx, "environment config file changed", slog.String("file", e.Name)) // First reload and validate the config if err := l.Load(cfg); err != nil { - slog.Error("Error reloading environment config", "error", err) + slog.ErrorContext(ctx, "error reloading environment config", slog.Any("error", err)) return } - slog.Info("Environment config successfully reloaded", + slog.InfoContext(ctx, + "environment config successfully reloaded", slog.Any("config", cfg.LogValue()), - slog.String("config loader changed", l.Name()), + slog.String("config_loader_changed", l.Name()), ) // Then execute all registered hooks with the event if err := onChange(context.Background()); err != nil { - slog.Error( - "Error executing config change hooks", - slog.String("error", err.Error()), - slog.String("config loader changed", l.Name()), + slog.ErrorContext(ctx, + "error executing config change hooks", + slog.Any("error", err), + slog.String("config_loader_changed", l.Name()), ) } else { - slog.Debug("Config change hooks executed successfully", slog.String("config loader changed", l.Name())) + slog.DebugContext(ctx, + "config change hooks executed successfully", + slog.String("config_loader_changed", l.Name()), + ) } }) diff --git a/service/pkg/db/db.go b/service/pkg/db/db.go index 3b5bdb74aa..75c8704f15 100644 --- a/service/pkg/db/db.go +++ b/service/pkg/db/db.go @@ -254,7 +254,10 @@ func (c Config) buildConfig() (*pgxpool.Config, error) { parsed.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error { _, err := conn.Exec(ctx, "SET search_path TO "+c.Schema) if err != nil { - slog.Error("failed to set database client search_path", slog.String("schema", c.Schema), slog.String("error", err.Error())) + slog.Error("failed to set database client search_path", + slog.String("schema", c.Schema), + slog.Any("error", err), + ) return err } slog.Debug("successfully set database client search_path", slog.String("schema", c.Schema)) diff --git a/service/pkg/db/db_migration.go b/service/pkg/db/db_migration.go index 888c42b904..8cbf05f51c 100644 --- a/service/pkg/db/db_migration.go +++ b/service/pkg/db/db_migration.go @@ -35,12 +35,12 @@ func migrationInit(ctx context.Context, c *Client, migrations *embed.FS) (*goose if e != nil { return nil, 0, nil, errors.Join(errors.New("failed to get current version"), e) } - slog.Info("migration db info", slog.Any("current version", v)) + slog.Info("migration db info", slog.Int64("current_version", v)) // Return the provider, version, and close function return provider, v, func() { if err := conn.Close(); err != nil { - slog.Error("failed to close connection", "err", err) + slog.Error("failed to close connection", slog.Any("err", err)) } }, nil } @@ -51,20 +51,27 @@ func (c *Client) RunMigrations(ctx context.Context, migrations *embed.FS) (int, if migrations == nil { return 0, errors.New("migrations FS is required to run migrations") } - slog.Info("running migration up", slog.String("schema", c.config.Schema), slog.String("database", c.config.Database)) + slog.Info("running migration up", + slog.String("schema", c.config.Schema), + slog.String("database", c.config.Database), + ) // Create schema if it doesn't exist q := "CREATE SCHEMA IF NOT EXISTS " + c.config.Schema tag, err := c.Pgx.Exec(ctx, q) if err != nil { - slog.ErrorContext(ctx, "Error while running command", slog.String("command", q), slog.String("error", err.Error())) + slog.ErrorContext(ctx, + "error while running command", + slog.String("command", q), + slog.Any("error", err), + ) return 0, err } applied := int(tag.RowsAffected()) provider, version, closeProvider, err := migrationInit(ctx, c, migrations) if err != nil { - slog.Error("failed to create goose provider", "err", err) + slog.Error("failed to create goose provider", slog.Any("err", err)) return 0, err } defer closeProvider() @@ -87,15 +94,18 @@ func (c *Client) RunMigrations(ctx context.Context, migrations *embed.FS) (int, } } c.ranMigrations = true - slog.Info("migration up complete", slog.Any("post-op version", version)) + slog.Info("migration up complete", slog.Int64("post_op_version", version)) return applied, nil } func (c *Client) MigrationStatus(ctx context.Context) ([]*goose.MigrationStatus, error) { - slog.Info("running migrations status", slog.String("schema", c.config.Schema), slog.String("database", c.config.Database)) + slog.Info("running migrations status", + slog.String("schema", c.config.Schema), + slog.String("database", c.config.Database), + ) provider, _, closeProvider, err := migrationInit(ctx, c, nil) if err != nil { - slog.Error("failed to create goose provider", "err", err) + slog.Error("failed to create goose provider", slog.Any("err", err)) return nil, err } defer closeProvider() @@ -104,10 +114,13 @@ func (c *Client) MigrationStatus(ctx context.Context) ([]*goose.MigrationStatus, } func (c *Client) MigrationDown(ctx context.Context, migrations *embed.FS) error { - slog.Info("running migration down", slog.String("schema", c.config.Schema), slog.String("database", c.config.Database)) + slog.Info("running migration down", + slog.String("schema", c.config.Schema), + slog.String("database", c.config.Database), + ) provider, _, closeProvider, err := migrationInit(ctx, c, migrations) if err != nil { - slog.Error("failed to create goose provider", "err", err) + slog.Error("failed to create goose provider", slog.Any("err", err)) return err } defer closeProvider() @@ -120,7 +133,7 @@ func (c *Client) MigrationDown(ctx context.Context, migrations *embed.FS) error return errors.Join(errors.New("failed to run migrations"), res.Error) } - slog.Info("migration down complete ", slog.Any("post-op version", res.Source.Version)) + slog.Info("migration down complete", slog.Int64("post_op_version", res.Source.Version)) return nil } diff --git a/service/pkg/db/errors.go b/service/pkg/db/errors.go index 96adbc5595..4aaa2c69b7 100644 --- a/service/pkg/db/errors.go +++ b/service/pkg/db/errors.go @@ -40,7 +40,7 @@ var ( // Get helpful error message for PostgreSQL violation func WrapIfKnownInvalidQueryErr(err error) error { if e := isPgError(err); e != nil { - slog.Error("Encountered database error", slog.String("error", e.Error())) + slog.Error("encountered database error", slog.Any("error", e)) switch e.Code { case pgerrcode.UniqueViolation: return errors.Join(ErrUniqueConstraintViolation, e) @@ -58,7 +58,10 @@ func WrapIfKnownInvalidQueryErr(err error) error { } return errors.Join(ErrEnumValueInvalid, e) default: - slog.Error("Unknown error code", slog.String("error", e.Message), slog.String("code", e.Code)) + slog.Error("unknown error code", + slog.String("error", e.Message), + slog.String("code", e.Code), + ) return e } } @@ -176,6 +179,6 @@ func StatusifyError(ctx context.Context, l *logger.Logger, err error, fallbackEr l.ErrorContext(ctx, ErrorTextMarshalFailed, logs...) return connect.NewError(connect.CodeInvalidArgument, errors.New(ErrorTextMarshalFailed)) } - l.ErrorContext(ctx, err.Error(), logs...) + l.ErrorContext(ctx, "request error", append(logs, slog.Any("error", err))...) return connect.NewError(connect.CodeInternal, errors.New(fallbackErr)) } diff --git a/service/pkg/server/services.go b/service/pkg/server/services.go index 6d493f361a..2678d25058 100644 --- a/service/pkg/server/services.go +++ b/service/pkg/server/services.go @@ -156,7 +156,10 @@ func startServices(ctx context.Context, params startServicesParams) (func(), err // Skip the namespace if the mode is not enabled if !modeEnabled { - logger.Info("skipping namespace", slog.String("namespace", ns), slog.String("mode", namespace.Mode)) + logger.Info("skipping namespace", + slog.String("namespace", ns), + slog.String("mode", namespace.Mode), + ) continue } @@ -196,7 +199,10 @@ func startServices(ctx context.Context, params startServicesParams) (func(), err // Function to create a cache given cache options var createCacheClient func(cache.Options) (*cache.Cache, error) = func(options cache.Options) (*cache.Cache, error) { - slog.Info("creating cache client for", slog.String("namespace", ns), slog.String("service", svc.GetServiceDesc().ServiceName)) + slog.Info("creating cache client for", + slog.String("namespace", ns), + slog.String("service", svc.GetServiceDesc().ServiceName), + ) cacheClient, err := cacheManager.NewCache(ns, svcLogger, options) if err != nil { return nil, fmt.Errorf("issue creating cache client for %s: %w", ns, err) diff --git a/service/pkg/server/start.go b/service/pkg/server/start.go index 5b06d76493..060e4e2ba0 100644 --- a/service/pkg/server/start.go +++ b/service/pkg/server/start.go @@ -172,7 +172,10 @@ func Start(f ...StartOptions) error { for _, service := range startConfig.extraServices { err := svcRegistry.RegisterService(service, service.GetNamespace()) if err != nil { - logger.Error("could not register extra service", slog.String("namespace", service.GetNamespace()), slog.String("error", err.Error())) + logger.Error("could not register extra service", + slog.String("namespace", service.GetNamespace()), + slog.Any("error", err), + ) return fmt.Errorf("could not register extra service: %w", err) } } @@ -273,12 +276,12 @@ func Start(f ...StartOptions) error { ersConnectRPCConn.Endpoint = cfg.SDKConfig.EntityResolutionConnection.Endpoint sdkOptions = append(sdkOptions, sdk.WithCustomEntityResolutionConnection(&ersConnectRPCConn)) - logger.Info("added with custom ers connection for ", "", ersConnectRPCConn.Endpoint) + logger.Info("added with custom ers connection", slog.String("ers_connection_endpoint", ersConnectRPCConn.Endpoint)) } client, err = sdk.New("", sdkOptions...) if err != nil { - logger.Error("issue creating sdk client", slog.String("error", err.Error())) + logger.Error("issue creating sdk client", slog.Any("error", err)) return fmt.Errorf("issue creating sdk client: %w", err) } } else { diff --git a/service/pkg/server/start_test.go b/service/pkg/server/start_test.go index 33163b12ec..09c8bac2db 100644 --- a/service/pkg/server/start_test.go +++ b/service/pkg/server/start_test.go @@ -382,7 +382,7 @@ func (s *StartTestSuite) Test_Start_When_Extra_Service_Registered() { if err == nil { break } - slog.Info("not yet ready", "err", err) + slog.Error("not yet ready", slog.Any("err", err)) // retry after a blip time.Sleep(100 * time.Millisecond) } diff --git a/service/pkg/serviceregistry/serviceregistry.go b/service/pkg/serviceregistry/serviceregistry.go index 5b5e885d7e..1430877578 100644 --- a/service/pkg/serviceregistry/serviceregistry.go +++ b/service/pkg/serviceregistry/serviceregistry.go @@ -292,9 +292,16 @@ func (reg Registry) Shutdown() { for name, ns := range reg { for _, svc := range ns.Services { if svc.IsStarted() { - slog.Info("stopping service", slog.String("namespace", name), slog.String("service", svc.GetServiceDesc().ServiceName)) + slog.Info("stopping service", + slog.String("namespace", name), + slog.String("service", svc.GetServiceDesc().ServiceName), + ) if err := svc.Shutdown(); err != nil { - slog.Error("error stopping service", slog.String("namespace", name), slog.String("service", svc.GetServiceDesc().ServiceName), slog.String("error", err.Error())) + slog.Error("error stopping service", + slog.String("namespace", name), + slog.String("service", svc.GetServiceDesc().ServiceName), + slog.Any("error", err), + ) } } } diff --git a/service/policy/attributes/attributes.go b/service/policy/attributes/attributes.go index e36e0cb9c3..5f7cc21e78 100644 --- a/service/policy/attributes/attributes.go +++ b/service/policy/attributes/attributes.go @@ -276,7 +276,11 @@ func (s *AttributesService) CreateAttributeValue(ctx context.Context, req *conne func (s *AttributesService) ListAttributeValues(ctx context.Context, req *connect.Request[attributes.ListAttributeValuesRequest]) (*connect.Response[attributes.ListAttributeValuesResponse], error) { state := req.Msg.GetState().String() - s.logger.DebugContext(ctx, "listing attribute values", slog.String("attributeId", req.Msg.GetAttributeId()), slog.String("state", state)) + s.logger.DebugContext(ctx, + "listing attribute values", + slog.String("attribute_id", req.Msg.GetAttributeId()), + slog.String("state", state), + ) rsp, err := s.dbClient.ListAttributeValues(ctx, req.Msg) if err != nil { return nil, db.StatusifyError(ctx, s.logger, err, db.ErrTextListRetrievalFailed, slog.String("attributeId", req.Msg.GetAttributeId())) diff --git a/service/policy/db/grant_mappings.go b/service/policy/db/grant_mappings.go index 2ee3fc6b90..11cd413e9d 100644 --- a/service/policy/db/grant_mappings.go +++ b/service/policy/db/grant_mappings.go @@ -41,7 +41,7 @@ func mapKasKeysToGrants(keys []*policy.SimpleKasKey, existingGrants []*policy.Ke for _, key := range keys { if key == nil { - l.Debug("Skipping nil key when mapping keys to grants") + l.Debug("skipping nil key when mapping keys to grants") continue } if key.GetKasUri() == "" || key.GetKasId() == "" { diff --git a/service/policy/db/key_access_server_registry.go b/service/policy/db/key_access_server_registry.go index 0f7dfc68a7..de5c79159a 100644 --- a/service/policy/db/key_access_server_registry.go +++ b/service/policy/db/key_access_server_registry.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "strings" "github.com/opentdf/platform/protocol/go/common" @@ -804,7 +805,7 @@ func (c PolicyDBClient) updateKeyInternal(ctx context.Context, params updateKeyP if count == 0 { return nil, db.ErrNotFound } else if count > 1 { - c.logger.Warn("UpdateKey updated more than one row", "count", count) + c.logger.Warn("updateKey updated more than one row", slog.Int64("count", count)) } return c.GetKey(ctx, &kasregistry.GetKeyRequest_Id{ diff --git a/service/policy/db/key_management.go b/service/policy/db/key_management.go index a5dd5c608c..75f087eb91 100644 --- a/service/policy/db/key_management.go +++ b/service/policy/db/key_management.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log/slog" "strings" "github.com/opentdf/platform/protocol/go/common" @@ -162,7 +163,7 @@ func (c PolicyDBClient) UpdateProviderConfig(ctx context.Context, r *keymanageme if count == 0 { return nil, db.ErrNotFound } else if count > 1 { - c.logger.Warn("UpdateProviderConfig updated more than one row", "count", count) + c.logger.Warn("updateProviderConfig updated more than one row", slog.Int64("count", count)) } return c.GetProviderConfig(ctx, &keymanagement.GetProviderConfigRequest_Id{ diff --git a/service/policy/db/registered_resources.go b/service/policy/db/registered_resources.go index e168a42a94..07b77825f3 100644 --- a/service/policy/db/registered_resources.go +++ b/service/policy/db/registered_resources.go @@ -325,7 +325,11 @@ func (c PolicyDBClient) GetRegisteredResourceValuesByFQNs(ctx context.Context, r }, }) if err != nil { - c.logger.ErrorContext(ctx, "registered resource value for FQN not found", slog.String("fqn", fqn), slog.Any("err", err)) + c.logger.ErrorContext(ctx, + "registered resource value for FQN not found", + slog.String("fqn", fqn), + slog.Any("err", err), + ) return nil, db.WrapIfKnownInvalidQueryErr(err) } diff --git a/service/policy/kasregistry/key_access_server_registry.go b/service/policy/kasregistry/key_access_server_registry.go index 66b4e9ec6e..144276cced 100644 --- a/service/policy/kasregistry/key_access_server_registry.go +++ b/service/policy/kasregistry/key_access_server_registry.go @@ -227,7 +227,7 @@ func (s KeyAccessServerRegistry) ListKeyAccessServerGrants(ctx context.Context, } func (s KeyAccessServerRegistry) CreateKey(ctx context.Context, r *connect.Request[kasr.CreateKeyRequest]) (*connect.Response[kasr.CreateKeyResponse], error) { - s.logger.DebugContext(ctx, "creating key", slog.String("keyAccessServer Keys", r.Msg.GetKasId())) + s.logger.DebugContext(ctx, "creating key", slog.String("kas_keys", r.Msg.GetKasId())) resp := &kasr.CreateKeyResponse{} auditParams := audit.PolicyEventParams{ @@ -275,7 +275,7 @@ func (s KeyAccessServerRegistry) CreateKey(ctx context.Context, r *connect.Reque func (s KeyAccessServerRegistry) UpdateKey(ctx context.Context, req *connect.Request[kasr.UpdateKeyRequest]) (*connect.Response[kasr.UpdateKeyResponse], error) { rsp := &kasr.UpdateKeyResponse{} - s.logger.DebugContext(ctx, "updating key", slog.String("keyAccessServer Keys", req.Msg.GetId())) + s.logger.DebugContext(ctx, "updating key", slog.String("kas_keys", req.Msg.GetId())) auditParams := audit.PolicyEventParams{ ActionType: audit.ActionTypeUpdate, @@ -326,9 +326,9 @@ func (s KeyAccessServerRegistry) GetKey(ctx context.Context, r *connect.Request[ switch i := r.Msg.GetIdentifier().(type) { case *kasr.GetKeyRequest_Id: - s.logger.DebugContext(ctx, "Getting keyAccessServer key by ID", slog.String("ID", i.Id)) + s.logger.DebugContext(ctx, "getting keyAccessServer key by ID", slog.String("id", i.Id)) case *kasr.GetKeyRequest_Key: - s.logger.DebugContext(ctx, "Getting keyAccessServer by Key", slog.String("Key Id", i.Key.GetKid())) + s.logger.DebugContext(ctx, "getting keyAccessServer by Key", slog.String("key_id", i.Key.GetKid())) default: return nil, connect.NewError(connect.CodeInvalidArgument, nil) } @@ -353,7 +353,7 @@ func (s KeyAccessServerRegistry) GetKey(ctx context.Context, r *connect.Request[ } func (s KeyAccessServerRegistry) ListKeys(ctx context.Context, r *connect.Request[kasr.ListKeysRequest]) (*connect.Response[kasr.ListKeysResponse], error) { - s.logger.DebugContext(ctx, "Listing KAS Keys") + s.logger.DebugContext(ctx, "listing KAS Keys") resp, err := s.dbClient.ListKeys(ctx, r.Msg) if err != nil { return nil, db.StatusifyError(ctx, s.logger, err, db.ErrTextListRetrievalFailed, slog.String("keyAccessServer Keys", r.Msg.String())) @@ -369,13 +369,17 @@ func (s KeyAccessServerRegistry) RotateKey(ctx context.Context, r *connect.Reque switch i := r.Msg.GetActiveKey().(type) { case *kasr.RotateKeyRequest_Id: - s.logger.DebugContext(ctx, "Rotating key by ID", slog.String("ID", i.Id)) + s.logger.DebugContext(ctx, "rotating key by ID", slog.String("id", i.Id)) objectID = i.Id identifier = &kasr.GetKeyRequest_Id{ Id: i.Id, } case *kasr.RotateKeyRequest_Key: - s.logger.DebugContext(ctx, "Rotating key by Kas Key", slog.String("Active Key ID", i.Key.GetKid()), slog.String("New Key ID", r.Msg.GetNewKey().GetKeyId())) + s.logger.DebugContext(ctx, + "rotating key by Kas Key", + slog.String("active_key_id", i.Key.GetKid()), + slog.String("new_key_id", r.Msg.GetNewKey().GetKeyId()), + ) objectID = i.Key.GetKid() identifier = &kasr.GetKeyRequest_Key{ Key: i.Key, @@ -450,10 +454,10 @@ func (s KeyAccessServerRegistry) SetBaseKey(ctx context.Context, r *connect.Requ var objectID string switch i := r.Msg.GetActiveKey().(type) { case *kasr.SetBaseKeyRequest_Id: - s.logger.DebugContext(ctx, "Setting base key by ID", slog.String("ID", i.Id)) + s.logger.DebugContext(ctx, "setting base key by ID", slog.String("id", i.Id)) objectID = i.Id case *kasr.SetBaseKeyRequest_Key: - s.logger.DebugContext(ctx, "Setting base key by Key ID", slog.String("Active Key ID", i.Key.GetKid())) + s.logger.DebugContext(ctx, "setting base key by Key ID", slog.String("active_key_id", i.Key.GetKid())) objectID = i.Key.GetKid() default: return nil, connect.NewError(connect.CodeInvalidArgument, nil) @@ -488,7 +492,7 @@ func (s KeyAccessServerRegistry) SetBaseKey(ctx context.Context, r *connect.Requ } func (s KeyAccessServerRegistry) GetBaseKey(ctx context.Context, _ *connect.Request[kasr.GetBaseKeyRequest]) (*connect.Response[kasr.GetBaseKeyResponse], error) { - s.logger.DebugContext(ctx, "Getting Base Key") + s.logger.DebugContext(ctx, "getting Base Key") resp := &kasr.GetBaseKeyResponse{} key, err := s.dbClient.GetBaseKey(ctx) diff --git a/service/policy/keymanagement/key_management.go b/service/policy/keymanagement/key_management.go index 7507a5ca67..5feb429b7f 100644 --- a/service/policy/keymanagement/key_management.go +++ b/service/policy/keymanagement/key_management.go @@ -52,7 +52,7 @@ func NewRegistration(ns string, dbRegister serviceregistry.DBRegister) *servicer RegisterFunc: func(srp serviceregistry.RegistrationParams) (keyMgmtConnect.KeyManagementServiceHandler, serviceregistry.HandlerServer) { cfg, err := policyconfig.GetSharedPolicyConfig(srp.Config) if err != nil { - srp.Logger.Error("Failed to get shared policy config", slog.String("error", err.Error())) + srp.Logger.Error("failed to get shared policy config", slog.Any("error", err)) panic(err) } ksvc.logger = srp.Logger @@ -74,7 +74,7 @@ func (ksvc *Service) Close() { func (ksvc Service) CreateProviderConfig(ctx context.Context, req *connect.Request[keyMgmtProto.CreateProviderConfigRequest]) (*connect.Response[keyMgmtProto.CreateProviderConfigResponse], error) { rsp := &keyMgmtProto.CreateProviderConfigResponse{} - ksvc.logger.DebugContext(ctx, "Creating Provider Config") + ksvc.logger.DebugContext(ctx, "creating Provider Config") auditParams := audit.PolicyEventParams{ ActionType: audit.ActionTypeCreate, @@ -111,9 +111,9 @@ func (ksvc Service) GetProviderConfig(ctx context.Context, req *connect.Request[ switch req := req.Msg.GetIdentifier().(type) { case *keyMgmtProto.GetProviderConfigRequest_Id: - ksvc.logger.DebugContext(ctx, "Getting Provider config by ID", slog.String("ID", req.Id)) + ksvc.logger.DebugContext(ctx, "getting provider config by ID", slog.String("id", req.Id)) case *keyMgmtProto.GetProviderConfigRequest_Name: - ksvc.logger.DebugContext(ctx, "Getting Provider config by Name", slog.String("Name", req.Name)) + ksvc.logger.DebugContext(ctx, "getting provider config by Name", slog.String("name", req.Name)) default: return nil, connect.NewError(connect.CodeInvalidArgument, nil) } @@ -128,7 +128,7 @@ func (ksvc Service) GetProviderConfig(ctx context.Context, req *connect.Request[ } func (ksvc Service) ListProviderConfigs(ctx context.Context, req *connect.Request[keyMgmtProto.ListProviderConfigsRequest]) (*connect.Response[keyMgmtProto.ListProviderConfigsResponse], error) { - ksvc.logger.DebugContext(ctx, "Listing Provider Configs") + ksvc.logger.DebugContext(ctx, "listing Provider Configs") resp, err := ksvc.dbClient.ListProviderConfigs(ctx, req.Msg.GetPagination()) if err != nil { @@ -142,7 +142,7 @@ func (ksvc Service) UpdateProviderConfig(ctx context.Context, req *connect.Reque rsp := &keyMgmtProto.UpdateProviderConfigResponse{} providerConfigID := req.Msg.GetId() - ksvc.logger.DebugContext(ctx, "Updating Provider Config", slog.String("id", req.Msg.GetId())) + ksvc.logger.DebugContext(ctx, "updating Provider Config", slog.String("id", req.Msg.GetId())) auditParams := audit.PolicyEventParams{ ActionType: audit.ActionTypeUpdate, @@ -192,7 +192,7 @@ func (ksvc Service) UpdateProviderConfig(ctx context.Context, req *connect.Reque func (ksvc Service) DeleteProviderConfig(ctx context.Context, req *connect.Request[keyMgmtProto.DeleteProviderConfigRequest]) (*connect.Response[keyMgmtProto.DeleteProviderConfigResponse], error) { rsp := &keyMgmtProto.DeleteProviderConfigResponse{} - ksvc.logger.DebugContext(ctx, "Deleting Provider Config", slog.String("id", req.Msg.GetId())) + ksvc.logger.DebugContext(ctx, "deleting Provider Config", slog.String("id", req.Msg.GetId())) auditParams := audit.PolicyEventParams{ ActionType: audit.ActionTypeDelete, diff --git a/service/policy/subjectmapping/subject_mapping.go b/service/policy/subjectmapping/subject_mapping.go index b299468d54..8176448986 100644 --- a/service/policy/subjectmapping/subject_mapping.go +++ b/service/policy/subjectmapping/subject_mapping.go @@ -145,7 +145,7 @@ func (s SubjectMappingService) UpdateSubjectMapping(ctx context.Context, rsp := &sm.UpdateSubjectMappingResponse{} subjectMappingID := req.Msg.GetId() - s.logger.DebugContext(ctx, "updating subject mapping", slog.String("subjectMapping", req.Msg.String())) + s.logger.DebugContext(ctx, "updating subject mapping", slog.Any("subject_mapping_update", req.Msg)) auditParams := audit.PolicyEventParams{ ActionType: audit.ActionTypeUpdate, @@ -164,7 +164,7 @@ func (s SubjectMappingService) UpdateSubjectMapping(ctx context.Context, updated, err := txClient.UpdateSubjectMapping(ctx, req.Msg) if err != nil { s.logger.Audit.PolicyCRUDFailure(ctx, auditParams) - return db.StatusifyError(ctx, s.logger, err, db.ErrTextUpdateFailed, slog.String("id", req.Msg.GetId()), slog.String("subjectMapping fields", req.Msg.String())) + return db.StatusifyError(ctx, s.logger, err, db.ErrTextUpdateFailed, slog.String("id", req.Msg.GetId()), slog.String("subject_mapping_fields", req.Msg.String())) } auditParams.Original = original @@ -213,7 +213,7 @@ func (s SubjectMappingService) MatchSubjectMappings(ctx context.Context, req *connect.Request[sm.MatchSubjectMappingsRequest], ) (*connect.Response[sm.MatchSubjectMappingsResponse], error) { rsp := &sm.MatchSubjectMappingsResponse{} - s.logger.DebugContext(ctx, "matching subject mappings", slog.Any("subjectProperties", req.Msg.GetSubjectProperties())) + s.logger.DebugContext(ctx, "matching subject mappings", slog.Any("subject_properties", req.Msg.GetSubjectProperties())) smList, err := s.dbClient.GetMatchedSubjectMappings(ctx, req.Msg.GetSubjectProperties()) if err != nil { @@ -260,7 +260,7 @@ func (s SubjectMappingService) CreateSubjectConditionSet(ctx context.Context, req *connect.Request[sm.CreateSubjectConditionSetRequest], ) (*connect.Response[sm.CreateSubjectConditionSetResponse], error) { rsp := &sm.CreateSubjectConditionSetResponse{} - s.logger.DebugContext(ctx, "creating subject condition set", slog.String("subjectConditionSet", req.Msg.String())) + s.logger.DebugContext(ctx, "creating subject condition set", slog.Any("subject_condition_set", req.Msg)) auditParams := audit.PolicyEventParams{ ActionType: audit.ActionTypeCreate, @@ -285,7 +285,7 @@ func (s SubjectMappingService) UpdateSubjectConditionSet(ctx context.Context, req *connect.Request[sm.UpdateSubjectConditionSetRequest], ) (*connect.Response[sm.UpdateSubjectConditionSetResponse], error) { rsp := &sm.UpdateSubjectConditionSetResponse{} - s.logger.DebugContext(ctx, "updating subject condition set", slog.String("subjectConditionSet", req.Msg.String())) + s.logger.DebugContext(ctx, "updating subject condition set", slog.Any("subject_condition_set", req.Msg)) subjectConditionSetID := req.Msg.GetId() auditParams := audit.PolicyEventParams{ diff --git a/service/rttests/rt_test.go b/service/rttests/rt_test.go index 4e4c5bcd52..169be30ef0 100644 --- a/service/rttests/rt_test.go +++ b/service/rttests/rt_test.go @@ -107,7 +107,7 @@ type RoundtripSuite struct { func (s *RoundtripSuite) SetupSuite() { s.TestConfig = newTestConfig() - slog.Info("Test config", "", s.TestConfig) + slog.Info("test config", slog.Any("config", s.TestConfig)) opts := []sdk.Option{} if os.Getenv("TLS_ENABLED") == "" { @@ -174,9 +174,13 @@ func (s *RoundtripSuite) CreateTestData() error { if err != nil { return err } - slog.Info(fmt.Sprintf("found %d namespaces", len(listResp.GetNamespaces()))) + slog.Info("found namespaces", slog.Int("count", len(listResp.GetNamespaces()))) for _, ns := range listResp.GetNamespaces() { - slog.Info(fmt.Sprintf("existing namespace; name: %s, id: %s", ns.GetName(), ns.GetId())) + slog.Info("existing namespace", + slog.String("name", ns.GetName()), + slog.String("id", ns.GetId()), + ) + if ns.GetName() == "example.com" { exampleNamespace = ns } @@ -269,7 +273,7 @@ func (s *RoundtripSuite) CreateTestData() error { slog.Error("could not list attributes", slog.String("error", err.Error())) return err } - slog.Info("list attributes response: " + protojson.Format(allAttr)) + slog.Info("list attributes", slog.String("response", protojson.Format(allAttr))) slog.Info("##################################\n#######################################") @@ -288,7 +292,7 @@ func (s *RoundtripSuite) CreateTestData() error { } // create subject mappings - slog.Info("creating subject mappings for client " + s.TestConfig.ClientID) + slog.Info("creating subject mappings", slog.String("client_id", s.TestConfig.ClientID)) for _, attributeID := range attributeValueIDs { _, err = client.SubjectMapping.CreateSubjectMapping(context.Background(), &subjectmapping.CreateSubjectMappingRequest{ AttributeValueId: attributeID, @@ -350,7 +354,7 @@ func (s *RoundtripSuite) CreateTestData() error { resp := &subjectmapping.ListSubjectMappingsResponse{ SubjectMappings: smList, } - slog.Info("list all subject mappings: " + protojson.Format(resp)) + slog.InfoContext(ctx, "list all subject mappings", slog.String("subject_mappings", protojson.Format(resp))) return nil } @@ -461,7 +465,7 @@ func bulk(client *sdk.SDK, tdfSuccess []string, tdfFail []string, plaintext stri if tdf.Error == nil { return errors.New("no expected err") } - slog.Error("pass tdf error", "error", tdf.Error.Error()) + slog.Error("pass tdf error", slog.Any("error", tdf.Error)) if !strings.Contains(tdf.Error.Error(), "KasAllowlist") { return errors.New("did not receive kas allowlist error") } diff --git a/service/tracing/otel.go b/service/tracing/otel.go index 0ba8ea023d..224cffb94b 100644 --- a/service/tracing/otel.go +++ b/service/tracing/otel.go @@ -85,16 +85,16 @@ func InitTracer(ctx context.Context, cfg Config) (func(), error) { logger := slog.Default() if !cfg.Enabled { - logger.Info("Tracing disabled.") + logger.Info("tracing disabled.") otel.SetTracerProvider(noop.NewTracerProvider()) otel.SetTextMapPropagator(propagation.TraceContext{}) otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) { - logger.Error("[otel SDK Error]", "error", err) + logger.ErrorContext(ctx, "otel SDK Error", slog.Any("error", err)) })) return func() {}, nil } - logger.Info("Initializing tracing", "provider", cfg.Provider.Name) + logger.InfoContext(ctx, "initializing tracing", slog.String("provider", cfg.Provider.Name)) var exporter sdktrace.SpanExporter var err error @@ -138,7 +138,7 @@ func InitTracer(ctx context.Context, cfg Config) (func(), error) { envRes, err := resource.New(ctx, resource.WithFromEnv()) // Attributes from OTEL_RESOURCE_ATTRIBUTES if err != nil { - logger.Warn("Failed to create resource from env vars (OTEL_RESOURCE_ATTRIBUTES)", "error", err) + logger.WarnContext(ctx, "failed to create resource from env vars (OTEL_RESOURCE_ATTRIBUTES)", slog.Any("error", err)) envRes = resource.Empty() } @@ -161,7 +161,7 @@ func InitTracer(ctx context.Context, cfg Config) (func(), error) { return nil, fmt.Errorf("failed to merge environment resource: %w", err) } - logger.Info("Initialized resource with attributes", "attributes", res.Encoded(attribute.DefaultEncoder())) + logger.InfoContext(ctx, "initialized resource with attributes", slog.String("attributes", res.Encoded(attribute.DefaultEncoder()))) // 4. Create Tracer Provider tp := sdktrace.NewTracerProvider( @@ -177,24 +177,24 @@ func InitTracer(ctx context.Context, cfg Config) (func(), error) { propagation.Baggage{}, )) - logger.Info("Tracing successfully initialized.") + logger.Info("tracing successfully initialized.") // 6. Return Shutdown Function return func() { - logger.Info("Shutting down tracing...") + logger.InfoContext(ctx, "shutting down tracing...") // Use a separate context for shutdown, typically context.Background() or a context with a timeout shutdownCtx, cancel := context.WithTimeout(ctx, ShutdownTimeout) // Example timeout defer cancel() if err := tp.Shutdown(shutdownCtx); err != nil { - logger.Error("Error shutting down tracer provider", "error", err) + logger.ErrorContext(ctx, "error shutting down tracer provider", slog.Any("error", err)) } if writerCloser != nil { if err := writerCloser.Close(); err != nil { - logger.Error("Error closing trace file writer", "error", err) + logger.ErrorContext(ctx, "error closing trace file writer", slog.Any("error", err)) } } - logger.Info("Tracing shutdown complete.") + logger.InfoContext(ctx, "tracing shutdown complete.") }, nil } @@ -211,10 +211,11 @@ func createOTLPExporter(ctx context.Context, cfg *OTLPConfig) (sdktrace.SpanExpo if protocol == "" { protocol = "grpc" // Default to gRPC } - logger.Info("Configuring OTLP exporter", - "protocol", protocol, - "endpoint", cfg.Endpoint, - "insecure", cfg.Insecure, + logger.InfoContext(ctx, + "configuring OTLP exporter", + slog.String("protocol", protocol), + slog.String("endpoint", cfg.Endpoint), + slog.Bool("insecure", cfg.Insecure), ) switch protocol { @@ -224,7 +225,7 @@ func createOTLPExporter(ctx context.Context, cfg *OTLPConfig) (sdktrace.SpanExpo grpcOpts = append(grpcOpts, otlptracegrpc.WithInsecure()) } // Else: Uses default secure credentials if len(cfg.Headers) > 0 { - logger.Info("Adding OTLP headers", "count", len(cfg.Headers)) + logger.InfoContext(ctx, "adding OTLP headers", slog.Int("count", len(cfg.Headers))) grpcOpts = append(grpcOpts, otlptracegrpc.WithHeaders(cfg.Headers)) } // Add WithDialOption if needing custom grpc.DialOptions @@ -237,7 +238,10 @@ func createOTLPExporter(ctx context.Context, cfg *OTLPConfig) (sdktrace.SpanExpo httpOpts = append(httpOpts, otlptracehttp.WithInsecure()) } // Else: Uses default secure credentials (HTTPS) if len(cfg.Headers) > 0 { - logger.Info("Adding OTLP headers", "count", len(cfg.Headers)) + logger.InfoContext(ctx, + "adding OTLP headers", + slog.Int("count", len(cfg.Headers)), + ) httpOpts = append(httpOpts, otlptracehttp.WithHeaders(cfg.Headers)) } // Add WithTLSClientConfig, WithTimeout, etc. if needed @@ -299,13 +303,13 @@ func createFileExporter(cfg *FileConfig) (sdktrace.SpanExporter, io.Closer, erro return nil, nil, fmt.Errorf("failed create stdouttrace exporter for file: %w", err) } - logger.Info("Configuring file trace exporter", - "path", cfg.Path, - "prettyPrint", cfg.PrettyPrint, - "maxSizeMB", maxSize, - "maxBackups", maxBackups, - "maxAgeDays", maxAge, - "compress", cfg.Compress, + logger.Info("configuring file trace exporter", + slog.String("path", cfg.Path), + slog.Bool("prettyPrint", cfg.PrettyPrint), + slog.Int("maxSizeMB", maxSize), + slog.Int("maxBackups", maxBackups), + slog.Int("maxAgeDays", maxAge), + slog.Bool("compress", cfg.Compress), ) return exporter, lumberjackLogger, nil // Return lumberjack for closing on shutdown diff --git a/service/trust/delegating_key_service.go b/service/trust/delegating_key_service.go index 95394df8cb..d8c97edf3e 100644 --- a/service/trust/delegating_key_service.go +++ b/service/trust/delegating_key_service.go @@ -5,6 +5,7 @@ import ( "crypto/elliptic" "errors" "fmt" + "log/slog" "sync" "github.com/opentdf/platform/service/logger" @@ -202,6 +203,9 @@ func (d *DelegatingKeyService) getKeyManager(name string) (KeyManager, error) { // Factory for 'name' not found. // If 'name' was the defaultMode, _defKM will error if its factory is also missing. // If 'name' was not the defaultMode, we fall back to the default manager. - d.l.Debug("Key manager factory not found for name, attempting to use/load default", "requestedName", name, "configuredDefaultName", currentDefaultMode) + d.l.Debug("key manager factory not found for name, attempting to use/load default", + slog.String("requested_name", name), + slog.String("configured_default_mode", currentDefaultMode), + ) return d._defKM() // _defKM handles erroring if the default manager itself cannot be loaded. } From 03a108f5138246c85df9b4d1230ee3015e57db6f Mon Sep 17 00:00:00 2001 From: jakedoublev Date: Mon, 23 Jun 2025 11:06:57 -0700 Subject: [PATCH 52/55] lint fixes --- service/internal/access/v2/pdp.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/service/internal/access/v2/pdp.go b/service/internal/access/v2/pdp.go index c31471d4c9..30235e86dd 100644 --- a/service/internal/access/v2/pdp.go +++ b/service/internal/access/v2/pdp.go @@ -167,7 +167,7 @@ func (p *PolicyDecisionPoint) GetDecision( action *policy.Action, resources []*authz.Resource, ) (*Decision, error) { - l := p.logger.With("entityID", entityRepresentation.GetOriginalId()) + l := p.logger.With("entity_id", entityRepresentation.GetOriginalId()) l = l.With("action", action.GetName()) l.DebugContext(ctx, "getting decision", slog.Int("resources_count", len(resources))) @@ -208,8 +208,8 @@ func (p *PolicyDecisionPoint) GetDecision( ctx, "resourceDecision result", slog.Bool("passed", resourceDecision.Passed), - slog.String("resourceID", resourceDecision.ResourceID), - slog.Int("dataRuleResultsCount", len(resourceDecision.DataRuleResults)), + slog.String("resource_id", resourceDecision.ResourceID), + slog.Int("data_rule_results_count", len(resourceDecision.DataRuleResults)), ) decision.Results[idx] = *resourceDecision } @@ -236,9 +236,9 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( action *policy.Action, resources []*authz.Resource, ) (*Decision, error) { - l := p.logger.With("entityRegisteredResourceValueFQN", entityRegisteredResourceValueFQN) + l := p.logger.With("entity_registered_resource_value_fqn", entityRegisteredResourceValueFQN) l = l.With("action", action.GetName()) - l.DebugContext(ctx, "getting decision", slog.Int("resourcesCount", len(resources))) + l.DebugContext(ctx, "getting decision", slog.Int("resources_count", len(resources))) if err := validateGetDecisionRegisteredResource(entityRegisteredResourceValueFQN, action, resources); err != nil { return nil, err @@ -254,13 +254,13 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( if err != nil { return nil, fmt.Errorf("error getting decisionable attributes: %w", err) } - l.DebugContext(ctx, "filtered to only entitlements relevant to decisioning", slog.Int("decisionableAttributeValuesCount", len(decisionableAttributes))) + l.DebugContext(ctx, "filtered to only entitlements relevant to decisioning", slog.Int("decisionable_attribute_values_count", len(decisionableAttributes))) entitledFQNsToActions := make(map[string][]*policy.Action) for _, aav := range entityRegisteredResourceValue.GetActionAttributeValues() { aavAction := aav.GetAction() if action.GetName() != aavAction.GetName() { - l.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("actionName", aavAction.GetName())) + l.DebugContext(ctx, "skipping action not matching Decision Request action", slog.String("action_name", aavAction.GetName())) continue } @@ -298,8 +298,8 @@ func (p *PolicyDecisionPoint) GetDecisionRegisteredResource( ctx, "resourceDecision result", slog.Bool("passed", resourceDecision.Passed), - slog.String("resourceID", resourceDecision.ResourceID), - slog.Int("dataRuleResultsCount", len(resourceDecision.DataRuleResults)), + slog.String("resource_id", resourceDecision.ResourceID), + slog.Int("data_rule_results_count", len(resourceDecision.DataRuleResults)), ) decision.Results[idx] = *resourceDecision } @@ -318,7 +318,7 @@ func (p *PolicyDecisionPoint) GetEntitlements( return nil, fmt.Errorf("invalid input parameters: %w", err) } - l := p.logger.With("withComprehensiveHierarchy", strconv.FormatBool(withComprehensiveHierarchy)) + l := p.logger.With("with_comprehensive_hierarchy", strconv.FormatBool(withComprehensiveHierarchy)) l.DebugContext(ctx, "getting entitlements", slog.Int("entity_representations_count", len(entityRepresentations))) var entitleableAttributes map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue @@ -385,7 +385,7 @@ func (p *PolicyDecisionPoint) GetEntitlementsRegisteredResource( registeredResourceValueFQN string, withComprehensiveHierarchy bool, ) ([]*authz.EntityEntitlements, error) { - l := p.logger.With("withComprehensiveHierarchy", strconv.FormatBool(withComprehensiveHierarchy)) + l := p.logger.With("with_comprehensive_hierarchy", strconv.FormatBool(withComprehensiveHierarchy)) l.DebugContext(ctx, "getting entitlements for registered resource value", slog.String("fqn", registeredResourceValueFQN)) if _, err := identifier.Parse[*identifier.FullyQualifiedRegisteredResourceValue](registeredResourceValueFQN); err != nil { From 2c16db17b97e8204f2b90fa390652de9de206057 Mon Sep 17 00:00:00 2001 From: jakedoublev Date: Mon, 23 Jun 2025 11:38:39 -0700 Subject: [PATCH 53/55] rm 'request' logs --- service/authorization/v2/authorization.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/service/authorization/v2/authorization.go b/service/authorization/v2/authorization.go index 3ccbda76d5..0bb613450b 100644 --- a/service/authorization/v2/authorization.go +++ b/service/authorization/v2/authorization.go @@ -159,12 +159,12 @@ func (as *Service) GetDecisionMultiResource(ctx context.Context, req *connect.Re decisions, allPermitted, err := pdp.GetDecision(ctx, entityIdentifier, action, resources) if err != nil { - return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to get decision"), err), slog.Any("request", request)) + return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to get decision"), err)) } resourceDecisions, err := rollupMultiResourceDecisions(decisions) if err != nil { - return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to rollup multi-resource decision"), err), slog.Any("request", request)) + return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to rollup multi-resource decision"), err)) } resp := &authzV2.GetDecisionMultiResourceResponse{ @@ -205,12 +205,12 @@ func (as *Service) GetDecisionBulk(ctx context.Context, req *connect.Request[aut decisions, allPermitted, err := pdp.GetDecision(ctx, entityIdentifier, action, resources) if err != nil { - return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to get bulk decision"), err), slog.Any("request", request)) + return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to get bulk decision"), err)) } resourceDecisions, err := rollupMultiResourceDecisions(decisions) if err != nil { - return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to rollup bulk multi-resource decision"), err), slog.Any("request", request), slog.Int("index", idx)) + return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to rollup bulk multi-resource decision"), err), slog.Int("index", idx)) } decisionResponse := &authzV2.GetDecisionMultiResourceResponse{ From cce6704f179a4fac7e8f928e06fcadb048225b11 Mon Sep 17 00:00:00 2001 From: jakedoublev Date: Mon, 23 Jun 2025 11:47:46 -0700 Subject: [PATCH 54/55] fix log message --- service/internal/access/v2/just_in_time_pdp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/internal/access/v2/just_in_time_pdp.go b/service/internal/access/v2/just_in_time_pdp.go index 8eacda5dae..81d789235f 100644 --- a/service/internal/access/v2/just_in_time_pdp.go +++ b/service/internal/access/v2/just_in_time_pdp.go @@ -162,7 +162,7 @@ func (p *JustInTimePDP) GetEntitlements( entityRepresentations, err = p.resolveEntitiesFromToken(ctx, entityIdentifier.GetToken(), skipEnvironmentEntities) case *authzV2.EntityIdentifier_RegisteredResourceValueFqn: - p.logger.DebugContext(ctx, "getting decision - resolving registered resource value FQN") + p.logger.DebugContext(ctx, "getting entitlements - resolving registered resource value FQN") regResValueFQN := strings.ToLower(entityIdentifier.GetRegisteredResourceValueFqn()) // registered resources do not have entity representations, so we can skip the remaining logic return p.pdp.GetEntitlementsRegisteredResource(ctx, regResValueFQN, withComprehensiveHierarchy) From 7cac3b064984e51034bfdc61ee75e3558359865e Mon Sep 17 00:00:00 2001 From: Jake Van Vorhis <83739412+jakedoublev@users.noreply.github.com> Date: Mon, 23 Jun 2025 12:23:29 -0700 Subject: [PATCH 55/55] feat(policy): allow ListRegisteredResources to return ActionAttributeValues on RegisteredResourceValues (#2469) ### Proposed Changes * ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions --- .../integration/registered_resources_test.go | 86 +++++++++++++++++++ service/policy/db/query.sql | 27 +++++- service/policy/db/query.sql.go | 54 +++++++++++- 3 files changed, 163 insertions(+), 4 deletions(-) diff --git a/service/integration/registered_resources_test.go b/service/integration/registered_resources_test.go index bd41b797bb..92b97c15aa 100644 --- a/service/integration/registered_resources_test.go +++ b/service/integration/registered_resources_test.go @@ -246,6 +246,92 @@ func (s *RegisteredResourcesSuite) Test_ListRegisteredResources_NoPagination_Suc s.Equal(2, foundCount) } +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", + }) + s.Require().NoError(err) + s.NotNil(newRegRes) + regResID := newRegRes.GetId() + + val1, err := s.db.PolicyClient.CreateRegisteredResourceValue(s.ctx, ®isteredresources.CreateRegisteredResourceValueRequest{ + ResourceId: regResID, + Value: "test_value_1", + ActionAttributeValues: []*registeredresources.ActionAttributeValue{ + { + ActionIdentifier: ®isteredresources.ActionAttributeValue_ActionName{ + ActionName: actions.ActionNameCreate, + }, + AttributeValueIdentifier: ®isteredresources.ActionAttributeValue_AttributeValueFqn{ + AttributeValueFqn: "https://example.com/attr/attr1/value/value1", + }, + }, + }, + }) + s.Require().NoError(err) + s.NotNil(val1) + + val2, err := s.db.PolicyClient.CreateRegisteredResourceValue(s.ctx, ®isteredresources.CreateRegisteredResourceValueRequest{ + ResourceId: regResID, + Value: "test_value_2", + ActionAttributeValues: []*registeredresources.ActionAttributeValue{ + { + ActionIdentifier: ®isteredresources.ActionAttributeValue_ActionName{ + ActionName: actions.ActionNameUpdate, + }, + AttributeValueIdentifier: ®isteredresources.ActionAttributeValue_AttributeValueFqn{ + AttributeValueFqn: "https://example.com/attr/attr2/value/value2", + }, + }, + }, + }) + s.Require().NoError(err) + s.NotNil(val2) + + // List registered resources and check if values contain action attribute values + list, err := s.db.PolicyClient.ListRegisteredResources(s.ctx, ®isteredresources.ListRegisteredResourcesRequest{}) + s.Require().NoError(err) + s.NotNil(list) + + foundRegRes := false + foundVal1 := false + foundVal2 := false + for _, r := range list.GetResources() { + if r.GetId() == regResID { + s.Equal("test_list_reg_res_with_action_attr_values", r.GetName()) + values := r.GetValues() + s.Require().Len(values, 2) + foundRegRes = true + + // Check if action attribute values are present in the values + for _, v := range values { + if v.GetId() == val1.GetId() { + foundVal1 = true + actionAttrValues := v.GetActionAttributeValues() + s.Require().NotEmpty(actionAttrValues) + for _, aav := range actionAttrValues { + s.NotNil(aav.GetAction()) + s.NotNil(aav.GetAttributeValue()) + } + } + if v.GetId() == val2.GetId() { + foundVal2 = true + actionAttrValues := v.GetActionAttributeValues() + s.Require().NotEmpty(actionAttrValues) + for _, aav := range actionAttrValues { + s.NotNil(aav.GetAction()) + s.NotNil(aav.GetAttributeValue()) + } + } + } + } + } + s.True(foundRegRes, "Registered resource not found in list") + s.True(foundVal1, "Value 1 not found in registered resource values") + s.True(foundVal2, "Value 2 not found in registered resource values") +} + func (s *RegisteredResourcesSuite) Test_ListRegisteredResources_Limit_Succeeds() { var limit int32 = 1 list, err := s.db.PolicyClient.ListRegisteredResources(s.ctx, ®isteredresources.ListRegisteredResourcesRequest{ diff --git a/service/policy/db/query.sql b/service/policy/db/query.sql index 9a5c12b61e..87b72af4ba 100644 --- a/service/policy/db/query.sql +++ b/service/policy/db/query.sql @@ -1415,16 +1415,41 @@ 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, + -- Aggregate all values for this resource into a JSON array, filtering NULL entries JSON_AGG( JSON_BUILD_OBJECT( 'id', v.id, - 'value', v.value + 'value', v.value, + 'action_attribute_values', action_attrs.values ) ) FILTER (WHERE v.id IS NOT NULL) as values, counted.total FROM registered_resources r 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 +LEFT JOIN LATERAL ( + SELECT JSON_AGG( + JSON_BUILD_OBJECT( + 'action', JSON_BUILD_OBJECT( + 'id', a.id, + 'name', a.name + ), + 'attribute_value', JSON_BUILD_OBJECT( + 'id', av.id, + 'value', av.value, + 'fqn', fqns.fqn + ) + ) + ) AS values + -- Join to get all action-attribute relationships for this resource value + FROM registered_resource_action_attribute_values rav + 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 + -- 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 LIMIT @limit_ OFFSET @offset_; diff --git a/service/policy/db/query.sql.go b/service/policy/db/query.sql.go index 7ff01345e0..fa3a4c06b5 100644 --- a/service/policy/db/query.sql.go +++ b/service/policy/db/query.sql.go @@ -4530,16 +4530,40 @@ 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, + -- Aggregate all values for this resource into a JSON array, filtering NULL entries JSON_AGG( JSON_BUILD_OBJECT( 'id', v.id, - 'value', v.value + 'value', v.value, + 'action_attribute_values', action_attrs.values ) ) FILTER (WHERE v.id IS NOT NULL) as values, counted.total FROM registered_resources r CROSS JOIN counted LEFT JOIN registered_resource_values v ON v.registered_resource_id = r.id +LEFT JOIN LATERAL ( + SELECT JSON_AGG( + JSON_BUILD_OBJECT( + 'action', JSON_BUILD_OBJECT( + 'id', a.id, + 'name', a.name + ), + 'attribute_value', JSON_BUILD_OBJECT( + 'id', av.id, + 'value', av.value, + 'fqn', fqns.fqn + ) + ) + ) AS values + -- Join to get all action-attribute relationships for this resource value + FROM registered_resource_action_attribute_values rav + 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 + -- 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 LIMIT $2 OFFSET $1 @@ -4558,7 +4582,7 @@ type listRegisteredResourcesRow struct { Total int64 `json:"total"` } -// listRegisteredResources +// Build a JSON array of action/attribute pairs for each resource value // // WITH counted AS ( // SELECT COUNT(id) AS total @@ -4568,16 +4592,40 @@ type listRegisteredResourcesRow 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, +// -- Aggregate all values for this resource into a JSON array, filtering NULL entries // JSON_AGG( // JSON_BUILD_OBJECT( // 'id', v.id, -// 'value', v.value +// 'value', v.value, +// 'action_attribute_values', action_attrs.values // ) // ) FILTER (WHERE v.id IS NOT NULL) as values, // counted.total // FROM registered_resources r // CROSS JOIN counted // LEFT JOIN registered_resource_values v ON v.registered_resource_id = r.id +// LEFT JOIN LATERAL ( +// SELECT JSON_AGG( +// JSON_BUILD_OBJECT( +// 'action', JSON_BUILD_OBJECT( +// 'id', a.id, +// 'name', a.name +// ), +// 'attribute_value', JSON_BUILD_OBJECT( +// 'id', av.id, +// 'value', av.value, +// 'fqn', fqns.fqn +// ) +// ) +// ) AS values +// -- Join to get all action-attribute relationships for this resource value +// FROM registered_resource_action_attribute_values rav +// 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 +// -- 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 // LIMIT $2 // OFFSET $1