// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package targets import ( "context" "fmt" "sync" "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/daemon/controller/handlers" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets" "google.golang.org/protobuf/proto" ) // Attributes represent the subtype specific request/response attributes. type Attributes interface { proto.Message // Options create target.Options to be used to create a target.Target. Options() []target.Option // Vet validates the Attributes and returns a map of fields to error messages // if any fields are invalid. Vet() map[string]string // VetForUpdate validates the Attributes for an updated resource with the // provided mask paths and returns a map of fields to error messages if any // fields are invalid. VetForUpdate([]string) map[string]string } type attributeFunc func(any) Attributes type setAttributeFunc func(target.Target, *pb.Target) error // validateSessionStateFunc validates a session's state for the specific target // type. type validateSessionStateFunc func(context.Context, *session.Session) error type registryEntry struct { maskManager handlers.MaskManager attrFunc attributeFunc setAttrFunc setAttributeFunc validateSessionStateFunc validateSessionStateFunc } type registry struct { *sync.Map } func (r registry) get(s globals.Subtype) (*registryEntry, error) { v, ok := r.Load(s) if !ok { return nil, fmt.Errorf("subtype %q not registered", s) } re, ok := v.(*registryEntry) if !ok { return nil, fmt.Errorf("malformed registry subtypye %q registered as incorrect type %T", s, v) } return re, nil } func (r registry) maskManager(s globals.Subtype) (handlers.MaskManager, error) { re, err := r.get(s) if err != nil { return nil, err } return re.maskManager, nil } // newAttribute creates an Attribute for the given subtype. It delegates the // allocation of the Attribute to the registered attrFunc for the given // subtype. An error is returned if the provided subtype is not registered func (r registry) newAttribute(s globals.Subtype, m any) (Attributes, error) { re, err := r.get(s) if err != nil { return nil, err } attr := re.attrFunc(m) return attr, nil } // setAttributes is used to set the Attrs field on a pb.Target. It delegates the // setting of the specific attribute type to the registered setAttributeFunc for // the given subtype. An error is returned if the provided subtype is not // registered. func (r registry) setAttributes(s globals.Subtype, in target.Target, out *pb.Target) error { re, err := r.get(s) if err != nil { return err } return re.setAttrFunc(in, out) } var subtypeRegistry = registry{ Map: new(sync.Map), } // Register registers a subtype for used by the service handler. func Register(s globals.Subtype, maskManager handlers.MaskManager, af attributeFunc, sf setAttributeFunc, vsf validateSessionStateFunc) { if _, existed := subtypeRegistry.LoadOrStore(s, ®istryEntry{ maskManager: maskManager, attrFunc: af, setAttrFunc: sf, validateSessionStateFunc: vsf, }); existed { panic(fmt.Sprintf("subtype %s already registered", s)) } }