mirror of https://github.com/hashicorp/consul
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)
|
|
}
|
|
}
|