mirror of
https://github.com/status-im/consul.git
synced 2025-01-10 05:45:46 +00:00
5698353652
Add some generic type hook wrappers to first decode the data There seems to be a pattern for Validation, Mutation and Write Authorization hooks where they first need to decode the Any data before doing the domain specific work. This PR introduces 3 new functions to generate wrappers around the other hooks to pre-decode the data into a DecodedResource and pass that in instead of the original pbresource.Resource. This PR also updates the various catalog data types to use the new hook generators.
108 lines
4.2 KiB
Go
108 lines
4.2 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package resource
|
|
|
|
import (
|
|
"github.com/hashicorp/consul/acl"
|
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
// DecodedValidationHook is the function signature needed for usage with the DecodeAndValidate function
|
|
type DecodedValidationHook[T proto.Message] func(*DecodedResource[T]) error
|
|
|
|
// DecodeAndValidate will generate a validation hook function that decodes the specified type and
|
|
// passes it off to another validation hook. This is mainly a convenience to avoid many other
|
|
// validation hooks needing to attempt decoding the data and erroring in a consistent manner.
|
|
func DecodeAndValidate[T proto.Message](fn DecodedValidationHook[T]) ValidationHook {
|
|
return func(res *pbresource.Resource) error {
|
|
decoded, err := Decode[T](res)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return fn(decoded)
|
|
}
|
|
}
|
|
|
|
// DecodedMutationHook is the function signature needed for usage with the DecodeAndMutate function
|
|
// The boolean return value indicates whether the Data field within the DecodedResource was modified.
|
|
// When true, the DecodeAndMutate hook function will automatically re-encode the Any data and store
|
|
// it on the internal Resource's Data field.
|
|
type DecodedMutationHook[T proto.Message] func(*DecodedResource[T]) (bool, error)
|
|
|
|
// DecodeAndMutate will generate a MutationHook that decodes the specified type and passes it
|
|
// off to another mutation hook. This is mainly a convenience to avoid other mutation hooks
|
|
// needing to decode and potentially reencode the Any data. When the inner mutation hook returns
|
|
// no error and that the Data was modified (true for the boolean return value), the generated
|
|
// hook will reencode the Any data back into the Resource wrapper
|
|
func DecodeAndMutate[T proto.Message](fn DecodedMutationHook[T]) MutationHook {
|
|
return func(res *pbresource.Resource) error {
|
|
decoded, err := Decode[T](res)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
modified, err := fn(decoded)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if modified {
|
|
return decoded.Resource.Data.MarshalFrom(decoded.Data)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// DecodedAuthorizationHook is the function signature needed for usage with the DecodeAndAuthorizeWrite
|
|
// and DecodeAndAuthorizeRead functions.
|
|
type DecodedAuthorizationHook[T proto.Message] func(acl.Authorizer, *acl.AuthorizerContext, *DecodedResource[T]) error
|
|
|
|
// DecodeAndAuthorizeWrite will generate an ACLAuthorizeWriteHook that decodes the specified type and passes
|
|
// it off to another authorization hook. This is mainly a convenience to avoid many other write authorization
|
|
// hooks needing to attempt decoding the data and erroring in a consistent manner.
|
|
func DecodeAndAuthorizeWrite[T proto.Message](fn DecodedAuthorizationHook[T]) ACLAuthorizeWriteHook {
|
|
return func(authz acl.Authorizer, ctx *acl.AuthorizerContext, res *pbresource.Resource) error {
|
|
decoded, err := Decode[T](res)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return fn(authz, ctx, decoded)
|
|
}
|
|
}
|
|
|
|
// DecodeAndAuthorizeRead will generate an ACLAuthorizeReadHook that decodes the specified type and passes
|
|
// it off to another authorization hook. This is mainly a convenience to avoid many other read authorization
|
|
// hooks needing to attempt decoding the data and erroring in a consistent manner.
|
|
func DecodeAndAuthorizeRead[T proto.Message](fn DecodedAuthorizationHook[T]) ACLAuthorizeReadHook {
|
|
return func(authz acl.Authorizer, ctx *acl.AuthorizerContext, _ *pbresource.ID, res *pbresource.Resource) error {
|
|
if res == nil {
|
|
return ErrNeedResource
|
|
}
|
|
|
|
decoded, err := Decode[T](res)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return fn(authz, ctx, decoded)
|
|
}
|
|
}
|
|
|
|
type ReadAuthorizationWithResourceHook func(acl.Authorizer, *acl.AuthorizerContext, *pbresource.Resource) error
|
|
|
|
// AuthorizeReadWithResource is a small wrapper to ensure that the authorization function is
|
|
// invoked with the full resource being read instead of just an id.
|
|
func AuthorizeReadWithResource(fn ReadAuthorizationWithResourceHook) ACLAuthorizeReadHook {
|
|
return func(authz acl.Authorizer, ctx *acl.AuthorizerContext, id *pbresource.ID, res *pbresource.Resource) error {
|
|
if res == nil {
|
|
return ErrNeedResource
|
|
}
|
|
|
|
return fn(authz, ctx, res)
|
|
}
|
|
}
|