internal/services/ephemeral_body_private.go (86 lines of code) (raw):

package services import ( "context" "crypto/sha256" "encoding/base64" "encoding/json" "github.com/hashicorp/terraform-plugin-framework/diag" ) const ( pkEphemeralBody = "sensitive_body" ) type PrivateData interface { GetKey(ctx context.Context, key string) ([]byte, diag.Diagnostics) SetKey(ctx context.Context, key string, value []byte) diag.Diagnostics } type EphemeralBodyPrivateMgr struct{} var ephemeralBodyPrivateMgr = EphemeralBodyPrivateMgr{} func (m EphemeralBodyPrivateMgr) Exists(ctx context.Context, d PrivateData) (bool, diag.Diagnostics) { b, diags := d.GetKey(ctx, pkEphemeralBody) if diags.HasError() { return false, diags } return b != nil, diags } // Set sets the hash of the sensitive_body to the private state. // If `ebody` is nil, it removes the hash from the private state. func (m EphemeralBodyPrivateMgr) Set(ctx context.Context, d PrivateData, ebody []byte) (diags diag.Diagnostics) { if ebody == nil { d.SetKey(ctx, pkEphemeralBody, nil) return } // Calculate the hash of the ephemeral body h := sha256.New() if _, err := h.Write(ebody); err != nil { diags.AddError( `Error to hash "sensitive_body"`, err.Error(), ) return } hash := h.Sum(nil) b, err := json.Marshal(map[string]interface{}{ // []byte will be marshaled to base64 encoded string "hash": hash, }) if err != nil { diags.AddError( `Error to marshal "sensitive_body" private data`, err.Error(), ) return } return d.SetKey(ctx, pkEphemeralBody, b) } // Diff tells whether the sensitive_body is different than the hash stored in the private state. // In case private state doesn't have the record, regard the record as "nil" (i.e. will return true if ebody is non-nil). // In case private state has the record (guaranteed to be non-nil), while ebody is nil, it also returns true. func (m EphemeralBodyPrivateMgr) Diff(ctx context.Context, d PrivateData, ebody []byte) (bool, diag.Diagnostics) { b, diags := d.GetKey(ctx, pkEphemeralBody) if diags.HasError() { return false, diags } if b == nil { // In case private state doesn't store the key yet, it only diffs when the ebody is not nil. return ebody != nil && string(ebody) != "null", diags } var mm map[string]interface{} if err := json.Unmarshal(b, &mm); err != nil { diags.AddError( `Error to unmarshal "sensitive_body" private data`, err.Error(), ) return false, diags } privateHashEnc, ok := mm["hash"] if !ok { diags.AddError( `Invalid "sensitive_body" private data`, `Key "hash" not found`, ) return false, diags } h := sha256.New() if _, err := h.Write(ebody); err != nil { diags.AddError( `Error to hash "sensitive_body"`, err.Error(), ) return false, diags } hash := h.Sum(nil) hashEnc := base64.StdEncoding.EncodeToString(hash) return hashEnc != privateHashEnc.(string), diags }