mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-25 01:00:16 -05:00
Remove usage of gohcl from method decoding
This allows us to build more powerful decode methods that give better errors Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
committed by
Andrei Ciobanu
parent
265b9003a5
commit
1be062b4bf
@@ -5,7 +5,14 @@
|
||||
|
||||
package keyprovider
|
||||
|
||||
import "github.com/zclconf/go-cty/cty"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// Output is the standardized structure a key provider must return when providing a key.
|
||||
// It contains two keys because some key providers may prefer include random data (e.g. salt)
|
||||
@@ -15,6 +22,38 @@ type Output struct {
|
||||
DecryptionKey []byte `hcl:"decryption_key,optional" cty:"decryption_key" json:"decryption_key,omitempty" yaml:"decryption_key"`
|
||||
}
|
||||
|
||||
func DecodeOutput(val cty.Value, subject hcl.Range) (Output, hcl.Diagnostics) {
|
||||
var out Output
|
||||
if !val.CanIterateElements() {
|
||||
return out, hcl.Diagnostics{{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Expected key_provider value",
|
||||
Detail: fmt.Sprintf("Expected a key_provider compatible value, found %s instead", val.Type().FriendlyName()),
|
||||
Subject: &subject,
|
||||
}}
|
||||
}
|
||||
|
||||
var diags hcl.Diagnostics
|
||||
mapVal := val.AsValueMap()
|
||||
if attr, ok := mapVal["encryption_key"]; ok {
|
||||
decodeDiags := gohcl.DecodeExpression(&hclsyntax.LiteralValueExpr{Val: attr, SrcRange: subject}, nil, &out.EncryptionKey)
|
||||
diags = diags.Extend(decodeDiags)
|
||||
} else {
|
||||
return out, hcl.Diagnostics{{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Missing encryption_key value",
|
||||
Detail: "A encryption_key value is required in the key_provider compatible object at this location",
|
||||
Subject: &subject,
|
||||
}}
|
||||
}
|
||||
|
||||
if attr, ok := mapVal["decryption_key"]; ok {
|
||||
decodeDiags := gohcl.DecodeExpression(&hclsyntax.LiteralValueExpr{Val: attr, SrcRange: subject}, nil, &out.DecryptionKey)
|
||||
diags = diags.Extend(decodeDiags)
|
||||
}
|
||||
return out, diags
|
||||
}
|
||||
|
||||
// Cty turns the Output struct into a CTY value.
|
||||
func (o *Output) Cty() cty.Value {
|
||||
return cty.ObjectVal(map[string]cty.Value{
|
||||
|
||||
@@ -180,16 +180,6 @@ func TestCompliance(t *testing.T) {
|
||||
Validate: nil,
|
||||
},
|
||||
},
|
||||
ConfigStructTestCases: map[string]compliancetest.ConfigStructTestCase[*Config, *aesgcm]{
|
||||
"empty": {
|
||||
Config: &Config{
|
||||
Keys: keyprovider.Output{},
|
||||
AAD: nil,
|
||||
},
|
||||
ValidBuild: false,
|
||||
Validate: nil,
|
||||
},
|
||||
},
|
||||
EncryptDecryptTestCase: compliancetest.EncryptDecryptTestCase[*Config, *aesgcm]{
|
||||
ValidEncryptOnlyConfig: &Config{
|
||||
Keys: keyprovider.Output{
|
||||
|
||||
@@ -22,13 +22,13 @@ var validKeyLengths = collections.NewSet[int](16, 24, 32)
|
||||
type Config struct {
|
||||
// Key is the encryption key for the AES-GCM encryption. It has to be 16, 24, or 32 bytes long for AES-128, 192, or
|
||||
// 256, respectively.
|
||||
Keys keyprovider.Output `hcl:"keys" json:"keys" yaml:"keys"`
|
||||
Keys keyprovider.Output
|
||||
|
||||
// AAD is the Additional Authenticated Data that is authenticated, but not encrypted. In the Go implementation, this
|
||||
// data serves as a canary value against replay attacks. The AAD value on decryption must match this setting,
|
||||
// otherwise the decryption will fail. (Note: this is Go-specific and differs from the NIST SP 800-38D description
|
||||
// of the AAD.)
|
||||
AAD []byte `hcl:"aad,optional" json:"aad,omitempty" yaml:"aad,omitempty"`
|
||||
AAD []byte
|
||||
}
|
||||
|
||||
// Build checks the validity of the configuration and returns a ready-to-use AES-GCM implementation.
|
||||
|
||||
@@ -6,37 +6,58 @@
|
||||
package aesgcm
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
"github.com/opentofu/opentofu/internal/encryption/keyprovider"
|
||||
"github.com/opentofu/opentofu/internal/encryption/method"
|
||||
)
|
||||
|
||||
// Descriptor integrates the method.Descriptor and provides a TypedConfig for easier configuration.
|
||||
type Descriptor interface {
|
||||
method.Descriptor
|
||||
|
||||
// TypedConfig returns a config typed for this method.
|
||||
TypedConfig() *Config
|
||||
}
|
||||
|
||||
// New creates a new descriptor for the AES-GCM encryption method, which requires a 32-byte key.
|
||||
func New() Descriptor {
|
||||
func New() method.Descriptor {
|
||||
return &descriptor{}
|
||||
}
|
||||
|
||||
type descriptor struct {
|
||||
}
|
||||
|
||||
func (f *descriptor) TypedConfig() *Config {
|
||||
return &Config{
|
||||
Keys: keyprovider.Output{},
|
||||
AAD: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *descriptor) ID() method.ID {
|
||||
return "aes_gcm"
|
||||
}
|
||||
|
||||
func (f *descriptor) ConfigStruct() method.Config {
|
||||
return f.TypedConfig()
|
||||
func (f *descriptor) DecodeConfig(methodCtx method.EvalContext, body hcl.Body) (method.Config, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
methodCfg := &Config{}
|
||||
|
||||
content, contentDiags := body.Content(&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{Name: "keys", Required: true},
|
||||
{Name: "aad", Required: false},
|
||||
},
|
||||
})
|
||||
diags = diags.Extend(contentDiags)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
keyExpr := content.Attributes["keys"].Expr
|
||||
// keyExpr can either be raw data/references to raw data or a string reference to a key provider (JSON support)
|
||||
keyVal, keyDiags := methodCtx.ValueForExpression(keyExpr)
|
||||
diags = diags.Extend(keyDiags)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
methodCfg.Keys, keyDiags = keyprovider.DecodeOutput(keyVal, keyExpr.Range())
|
||||
diags = diags.Extend(keyDiags)
|
||||
|
||||
if attr, ok := content.Attributes["aad"]; ok {
|
||||
attrVal, attrDiags := methodCtx.ValueForExpression(attr.Expr)
|
||||
diags = diags.Extend(attrDiags)
|
||||
|
||||
decodeDiags := gohcl.DecodeExpression(&hclsyntax.LiteralValueExpr{Val: attrVal, SrcRange: attr.Expr.Range()}, nil, &methodCfg.AAD)
|
||||
diags = diags.Extend(decodeDiags)
|
||||
}
|
||||
|
||||
return methodCfg, diags
|
||||
}
|
||||
|
||||
@@ -6,62 +6,19 @@
|
||||
package aesgcm_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
"github.com/opentofu/opentofu/internal/encryption/keyprovider"
|
||||
"github.com/opentofu/opentofu/internal/encryption/method"
|
||||
"github.com/opentofu/opentofu/internal/encryption/method/aesgcm"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
descriptor := aesgcm.New()
|
||||
|
||||
// Get the config struct. You can fill it manually by type-asserting it to aesgcm.Config, but you could also use
|
||||
// it as JSON.
|
||||
config := descriptor.ConfigStruct()
|
||||
|
||||
if err := json.Unmarshal(
|
||||
// Set up a randomly generated 32-byte key. In JSON, you can base64-encode the value.
|
||||
[]byte(`{
|
||||
"keys": {
|
||||
"encryption_key": "Y29veTRhaXZ1NWFpeW9vMWlhMG9vR29vVGFlM1BhaTQ=",
|
||||
"decryption_key": "Y29veTRhaXZ1NWFpeW9vMWlhMG9vR29vVGFlM1BhaTQ="
|
||||
}
|
||||
}`), &config); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
method, err := config.Build()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Encrypt some data:
|
||||
encrypted, err := method.Encrypt([]byte("Hello world!"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Now decrypt it:
|
||||
decrypted, err := method.Decrypt(encrypted)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s", decrypted)
|
||||
// Output: Hello world!
|
||||
}
|
||||
|
||||
func Example_config() {
|
||||
// First, get the descriptor to make sure we always have the default values.
|
||||
descriptor := aesgcm.New()
|
||||
|
||||
// Obtain a modifiable, buildable config. Alternatively, you can also use ConfigStruct() method to obtain a
|
||||
// struct you can fill with HCL or JSON tags.
|
||||
config := descriptor.TypedConfig()
|
||||
// Obtain a modifiable, buildable config.
|
||||
config := aesgcm.Config{}
|
||||
|
||||
// Set up an encryption key:
|
||||
config.Keys = keyprovider.Output{
|
||||
@@ -91,54 +48,10 @@ func Example_config() {
|
||||
// Output: Hello world!
|
||||
}
|
||||
|
||||
func Example_config_json() {
|
||||
// First, get the descriptor to make sure we always have the default values.
|
||||
descriptor := aesgcm.New()
|
||||
|
||||
// Get an untyped config struct you can use for JSON unmarshalling:
|
||||
config := descriptor.ConfigStruct()
|
||||
|
||||
// Unmarshal JSON into the config struct:
|
||||
if err := json.Unmarshal(
|
||||
// Set up a randomly generated 32-byte key. In JSON, you can base64-encode the value.
|
||||
[]byte(`{
|
||||
"keys": {
|
||||
"encryption_key": "Y29veTRhaXZ1NWFpeW9vMWlhMG9vR29vVGFlM1BhaTQ=",
|
||||
"decryption_key": "Y29veTRhaXZ1NWFpeW9vMWlhMG9vR29vVGFlM1BhaTQ="
|
||||
}
|
||||
}`), &config); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Now you can build a method:
|
||||
method, err := config.Build()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Encrypt something:
|
||||
encrypted, err := method.Encrypt([]byte("Hello world!"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Decrypt it:
|
||||
decrypted, err := method.Decrypt(encrypted)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s", decrypted)
|
||||
// Output: Hello world!
|
||||
}
|
||||
|
||||
func Example_config_hcl() {
|
||||
// First, get the descriptor to make sure we always have the default values.
|
||||
descriptor := aesgcm.New()
|
||||
|
||||
// Get an untyped config struct you can use for HCL unmarshalling:
|
||||
config := descriptor.ConfigStruct()
|
||||
|
||||
// Unmarshal HCL code into the config struct. The input must be a list of bytes, so in a real world scenario
|
||||
// you may want to put in a hex-decoding function:
|
||||
rawHCLInput := `keys = {
|
||||
@@ -153,7 +66,12 @@ func Example_config_hcl() {
|
||||
if diags.HasErrors() {
|
||||
panic(diags)
|
||||
}
|
||||
if diags := gohcl.DecodeBody(file.Body, nil, config); diags.HasErrors() {
|
||||
|
||||
methodCtx := method.EvalContext{func(expr hcl.Expression) (cty.Value, hcl.Diagnostics) {
|
||||
return expr.Value(nil)
|
||||
}}
|
||||
config, diags := descriptor.DecodeConfig(methodCtx, file.Body)
|
||||
if diags.HasErrors() {
|
||||
panic(diags)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,11 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/opentofu/opentofu/internal/encryption/compliancetest"
|
||||
"github.com/opentofu/opentofu/internal/encryption/config"
|
||||
"github.com/opentofu/opentofu/internal/encryption/method"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// ComplianceTest tests the functionality of a method to make sure it conforms to the expectations of the method
|
||||
@@ -32,9 +33,6 @@ type TestConfiguration[TDescriptor method.Descriptor, TConfig method.Config, TMe
|
||||
// function.
|
||||
HCLParseTestCases map[string]HCLParseTestCase[TDescriptor, TConfig, TMethod]
|
||||
|
||||
// ConfigStructT validates that a certain config results or does not result in a valid Build() call.
|
||||
ConfigStructTestCases map[string]ConfigStructTestCase[TConfig, TMethod]
|
||||
|
||||
// ProvideTestCase exercises the entire chain and generates two keys.
|
||||
EncryptDecryptTestCase EncryptDecryptTestCase[TConfig, TMethod]
|
||||
}
|
||||
@@ -46,9 +44,6 @@ func (cfg *TestConfiguration[TDescriptor, TConfig, TMethod]) execute(t *testing.
|
||||
t.Run("hcl", func(t *testing.T) {
|
||||
cfg.testHCL(t)
|
||||
})
|
||||
t.Run("config-struct", func(t *testing.T) {
|
||||
cfg.testConfigStruct(t)
|
||||
})
|
||||
t.Run("encrypt-decrypt", func(t *testing.T) {
|
||||
cfg.EncryptDecryptTestCase.execute(t)
|
||||
})
|
||||
@@ -100,20 +95,6 @@ func (cfg *TestConfiguration[TDescriptor, TConfig, TMethod]) testHCL(t *testing.
|
||||
})
|
||||
}
|
||||
|
||||
func (cfg *TestConfiguration[TDescriptor, TConfig, TMethod]) testConfigStruct(t *testing.T) {
|
||||
compliancetest.ConfigStruct[TConfig](t, cfg.Descriptor.ConfigStruct())
|
||||
|
||||
if cfg.ConfigStructTestCases == nil {
|
||||
compliancetest.Fail(t, "Please provide a map to ConfigStructTestCases.")
|
||||
}
|
||||
|
||||
for name, tc := range cfg.ConfigStructTestCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
tc.execute(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// HCLParseTestCase contains a test case that parses HCL into a configuration.
|
||||
type HCLParseTestCase[TDescriptor method.Descriptor, TConfig method.Config, TMethod method.Method] struct {
|
||||
// HCL contains the code that should be parsed into the configuration structure.
|
||||
@@ -143,12 +124,11 @@ func (h *HCLParseTestCase[TDescriptor, TConfig, TMethod]) execute(t *testing.T,
|
||||
}
|
||||
}
|
||||
|
||||
configStruct := descriptor.ConfigStruct()
|
||||
diags = gohcl.DecodeBody(
|
||||
parsedConfig.MethodConfigs[0].Body,
|
||||
nil,
|
||||
configStruct,
|
||||
)
|
||||
methodCtx := method.EvalContext{func(expr hcl.Expression) (cty.Value, hcl.Diagnostics) {
|
||||
return expr.Value(nil)
|
||||
}}
|
||||
configStruct, diags := descriptor.DecodeConfig(methodCtx, parsedConfig.MethodConfigs[0].Body)
|
||||
|
||||
var m TMethod
|
||||
if h.ValidHCL {
|
||||
if diags.HasErrors() {
|
||||
@@ -177,22 +157,6 @@ func (h *HCLParseTestCase[TDescriptor, TConfig, TMethod]) execute(t *testing.T,
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigStructTestCase validates that the config struct is behaving correctly when Build() is called.
|
||||
type ConfigStructTestCase[TConfig method.Config, TMethod method.Method] struct {
|
||||
Config TConfig
|
||||
ValidBuild bool
|
||||
Validate func(method TMethod) error
|
||||
}
|
||||
|
||||
func (m ConfigStructTestCase[TConfig, TMethod]) execute(t *testing.T) {
|
||||
newMethod := buildConfigAndValidate[TMethod, TConfig](t, m.Config, m.ValidBuild)
|
||||
if m.Validate != nil {
|
||||
if err := m.Validate(newMethod); err != nil {
|
||||
compliancetest.Fail(t, "method validation failed (%v)", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EncryptDecryptTestCase handles a full encryption-decryption cycle.
|
||||
type EncryptDecryptTestCase[TConfig method.Config, TMethod method.Method] struct {
|
||||
// ValidEncryptOnlyConfig is a configuration that has no decryption key and can only be used for encryption. The
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
|
||||
package method
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// Descriptor contains the details on an encryption method and produces a configuration structure with default values.
|
||||
type Descriptor interface {
|
||||
// ID returns the unique identifier used when parsing HCL or JSON configs.
|
||||
@@ -16,5 +21,9 @@ type Descriptor interface {
|
||||
// Common errors:
|
||||
// - Returning a struct without a pointer
|
||||
// - Returning a non-struct
|
||||
ConfigStruct() Config
|
||||
DecodeConfig(methodCtx EvalContext, body hcl.Body) (Config, hcl.Diagnostics)
|
||||
}
|
||||
|
||||
type EvalContext struct {
|
||||
ValueForExpression func(expr hcl.Expression) (cty.Value, hcl.Diagnostics)
|
||||
}
|
||||
|
||||
@@ -74,13 +74,6 @@ func runTest(t *testing.T, cmd []string) {
|
||||
},
|
||||
},
|
||||
},
|
||||
ConfigStructTestCases: map[string]compliancetest.ConfigStructTestCase[*Config, *command]{
|
||||
"empty": {
|
||||
Config: &Config{},
|
||||
ValidBuild: false,
|
||||
Validate: nil,
|
||||
},
|
||||
},
|
||||
EncryptDecryptTestCase: compliancetest.EncryptDecryptTestCase[*Config, *command]{
|
||||
ValidEncryptOnlyConfig: &Config{
|
||||
Keys: &keyprovider.Output{
|
||||
|
||||
@@ -6,33 +6,76 @@
|
||||
package external
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
"github.com/opentofu/opentofu/internal/encryption/keyprovider"
|
||||
"github.com/opentofu/opentofu/internal/encryption/method"
|
||||
)
|
||||
|
||||
// Descriptor integrates the method.Descriptor and provides a TypedConfig for easier configuration.
|
||||
type Descriptor interface {
|
||||
method.Descriptor
|
||||
|
||||
// TypedConfig returns a config typed for this method.
|
||||
TypedConfig() *Config
|
||||
}
|
||||
|
||||
// New creates a new descriptor for the AES-GCM encryption method, which requires a 32-byte key.
|
||||
func New() Descriptor {
|
||||
func New() method.Descriptor {
|
||||
return &descriptor{}
|
||||
}
|
||||
|
||||
type descriptor struct {
|
||||
}
|
||||
|
||||
func (f *descriptor) TypedConfig() *Config {
|
||||
return &Config{}
|
||||
}
|
||||
|
||||
func (f *descriptor) ID() method.ID {
|
||||
return "external"
|
||||
}
|
||||
|
||||
func (f *descriptor) ConfigStruct() method.Config {
|
||||
return f.TypedConfig()
|
||||
func (d *descriptor) DecodeConfig(methodCtx method.EvalContext, body hcl.Body) (method.Config, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
methodCfg := &Config{}
|
||||
|
||||
content, contentDiags := body.Content(&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{Name: "keys", Required: false},
|
||||
{Name: "encrypt_command", Required: true},
|
||||
{Name: "decrypt_command", Required: true},
|
||||
},
|
||||
})
|
||||
diags = diags.Extend(contentDiags)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
if keyAttr, ok := content.Attributes["keys"]; ok {
|
||||
keyExpr := keyAttr.Expr
|
||||
// keyExpr can either be raw data/references to raw data or a string reference to a key provider (JSON support)
|
||||
keyVal, keyDiags := methodCtx.ValueForExpression(keyExpr)
|
||||
diags = diags.Extend(keyDiags)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
keys, err := keyprovider.DecodeOutput(keyVal, keyExpr.Range())
|
||||
if err != nil {
|
||||
// TODO diags
|
||||
panic(err)
|
||||
}
|
||||
methodCfg.Keys = &keys
|
||||
}
|
||||
|
||||
encryptAttr := content.Attributes["encrypt_command"]
|
||||
encryptVal, valueDiags := methodCtx.ValueForExpression(encryptAttr.Expr)
|
||||
diags = diags.Extend(valueDiags)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
decodeDiags := gohcl.DecodeExpression(&hclsyntax.LiteralValueExpr{Val: encryptVal, SrcRange: encryptAttr.Expr.Range()}, nil, &methodCfg.EncryptCommand)
|
||||
diags = diags.Extend(decodeDiags)
|
||||
|
||||
decryptAttr := content.Attributes["decrypt_command"]
|
||||
decryptVal, valueDiags := methodCtx.ValueForExpression(decryptAttr.Expr)
|
||||
diags = diags.Extend(valueDiags)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
decodeDiags = gohcl.DecodeExpression(&hclsyntax.LiteralValueExpr{Val: decryptVal, SrcRange: decryptAttr.Expr.Range()}, nil, &methodCfg.DecryptCommand)
|
||||
diags = diags.Extend(decodeDiags)
|
||||
|
||||
return methodCfg, diags
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package unencrypted
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/opentofu/opentofu/internal/encryption/config"
|
||||
"github.com/opentofu/opentofu/internal/encryption/method"
|
||||
)
|
||||
@@ -19,8 +20,8 @@ type descriptor struct{}
|
||||
func (f *descriptor) ID() method.ID {
|
||||
return "unencrypted"
|
||||
}
|
||||
func (f *descriptor) ConfigStruct() method.Config {
|
||||
return new(methodConfig)
|
||||
func (d *descriptor) DecodeConfig(methodCtx method.EvalContext, body hcl.Body) (method.Config, hcl.Diagnostics) {
|
||||
return new(methodConfig), nil
|
||||
}
|
||||
|
||||
type methodConfig struct{}
|
||||
|
||||
@@ -11,12 +11,12 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/opentofu/opentofu/internal/configs"
|
||||
"github.com/opentofu/opentofu/internal/encryption/config"
|
||||
"github.com/opentofu/opentofu/internal/encryption/method"
|
||||
"github.com/opentofu/opentofu/internal/encryption/registry"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// setupMethod sets up a single method for encryption. It returns a list of diagnostics if the method is invalid.
|
||||
@@ -43,37 +43,45 @@ func setupMethod(ctx context.Context, enc *config.EncryptionConfig, cfg config.M
|
||||
}}
|
||||
}
|
||||
|
||||
methodConfig := encryptionMethod.ConfigStruct()
|
||||
methodCtx := method.EvalContext{func(expr hcl.Expression) (cty.Value, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
deps, diags := gohcl.VariablesInBody(cfg.Body, methodConfig)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
deps := expr.Variables()
|
||||
|
||||
kpConfigs, refs, kpDiags := filterKeyProviderReferences(enc, deps)
|
||||
diags = diags.Extend(kpDiags)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
kpConfigs, refs, kpDiags := filterKeyProviderReferences(enc, deps)
|
||||
diags = diags.Extend(kpDiags)
|
||||
if diags.HasErrors() {
|
||||
return cty.NilVal, diags
|
||||
}
|
||||
|
||||
hclCtx, kpDiags := setupKeyProviders(ctx, enc, kpConfigs, meta, reg, staticEval)
|
||||
diags = diags.Extend(kpDiags)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
hclCtx, kpDiags := setupKeyProviders(ctx, enc, kpConfigs, meta, reg, staticEval)
|
||||
diags = diags.Extend(kpDiags)
|
||||
if diags.HasErrors() {
|
||||
return cty.NilVal, diags
|
||||
}
|
||||
|
||||
hclCtx, evalDiags := staticEval.EvalContextWithParent(ctx, hclCtx, configs.StaticIdentifier{
|
||||
Module: addrs.RootModule,
|
||||
Subject: fmt.Sprintf("encryption.method.%s.%s", cfg.Type, cfg.Name),
|
||||
DeclRange: enc.DeclRange,
|
||||
}, refs)
|
||||
diags = diags.Extend(evalDiags)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
hclCtx, evalDiags := staticEval.EvalContextWithParent(ctx, hclCtx, configs.StaticIdentifier{
|
||||
Module: addrs.RootModule,
|
||||
Subject: fmt.Sprintf("encryption.method.%s.%s", cfg.Type, cfg.Name),
|
||||
DeclRange: enc.DeclRange,
|
||||
}, refs)
|
||||
diags = diags.Extend(evalDiags)
|
||||
if diags.HasErrors() {
|
||||
return cty.NilVal, diags
|
||||
}
|
||||
|
||||
methodDiags := gohcl.DecodeBody(cfg.Body, hclCtx, methodConfig)
|
||||
diags = diags.Extend(methodDiags)
|
||||
val, valDiags := expr.Value(hclCtx)
|
||||
diags = diags.Extend(valDiags)
|
||||
if diags.HasErrors() {
|
||||
return cty.NilVal, diags
|
||||
}
|
||||
|
||||
// TODO inspect to see if KP string!
|
||||
|
||||
return val, diags
|
||||
}}
|
||||
|
||||
methodConfig, diags := encryptionMethod.DecodeConfig(methodCtx, cfg.Body)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/opentofu/opentofu/internal/encryption/method"
|
||||
"github.com/opentofu/opentofu/internal/encryption/registry"
|
||||
)
|
||||
@@ -124,8 +125,8 @@ func (t testMethodDescriptor) ID() method.ID {
|
||||
return t.id
|
||||
}
|
||||
|
||||
func (t testMethodDescriptor) ConfigStruct() method.Config {
|
||||
return &testMethodConfig{}
|
||||
func (t testMethodDescriptor) DecodeConfig(method.EvalContext, hcl.Body) (method.Config, hcl.Diagnostics) {
|
||||
return &testMethodConfig{}, nil
|
||||
}
|
||||
|
||||
type testMethodConfig struct {
|
||||
|
||||
Reference in New Issue
Block a user