terraform: Unmark provider configuration arguments

Before configuring a provider, we need to unmark the configuration
object, in case it includes any sensitive values. This is required
because configuration occurs over gRPC, which doesn't support sensitive
marks.
This commit is contained in:
Alisdair McDiarmid 2020-11-16 13:11:30 -05:00
parent c1d30401c5
commit 1c7f412d13
2 changed files with 86 additions and 4 deletions

View File

@ -68,8 +68,12 @@ func (n *NodeApplyableProvider) ValidateProvider(ctx EvalContext, provider provi
return diags return diags
} }
// If our config value contains any marked values, ensure those are
// stripped out before sending this to the provider
unmarkedConfigVal, _ := configVal.UnmarkDeep()
req := providers.PrepareProviderConfigRequest{ req := providers.PrepareProviderConfigRequest{
Config: configVal, Config: unmarkedConfigVal,
} }
validateResp := provider.PrepareProviderConfig(req) validateResp := provider.PrepareProviderConfig(req)
@ -109,10 +113,14 @@ func (n *NodeApplyableProvider) ConfigureProvider(ctx EvalContext, provider prov
return diags return diags
} }
// If our config value contains any marked values, ensure those are
// stripped out before sending this to the provider
unmarkedConfigVal, _ := configVal.UnmarkDeep()
// Allow the provider to validate and insert any defaults into the full // Allow the provider to validate and insert any defaults into the full
// configuration. // configuration.
req := providers.PrepareProviderConfigRequest{ req := providers.PrepareProviderConfigRequest{
Config: configVal, Config: unmarkedConfigVal,
} }
// PrepareProviderConfig is only used for validation. We are intentionally // PrepareProviderConfig is only used for validation. We are intentionally
@ -126,11 +134,11 @@ func (n *NodeApplyableProvider) ConfigureProvider(ctx EvalContext, provider prov
// If the provider returns something different, log a warning to help // If the provider returns something different, log a warning to help
// indicate to provider developers that the value is not used. // indicate to provider developers that the value is not used.
preparedCfg := prepareResp.PreparedConfig preparedCfg := prepareResp.PreparedConfig
if preparedCfg != cty.NilVal && !preparedCfg.IsNull() && !preparedCfg.RawEquals(configVal) { if preparedCfg != cty.NilVal && !preparedCfg.IsNull() && !preparedCfg.RawEquals(unmarkedConfigVal) {
log.Printf("[WARN] PrepareProviderConfig from %q changed the config value, but that value is unused", n.Addr) log.Printf("[WARN] PrepareProviderConfig from %q changed the config value, but that value is unused", n.Addr)
} }
configDiags := ctx.ConfigureProvider(n.Addr, configVal) configDiags := ctx.ConfigureProvider(n.Addr, unmarkedConfigVal)
diags = diags.Append(configDiags.InConfigBody(configBody)) diags = diags.Append(configDiags.InConfigBody(configBody))
return diags return diags

View File

@ -115,3 +115,77 @@ func TestNodeApplyableProviderExecute_unknownApply(t *testing.T) {
t.Errorf("wrong configuration value\ngot: %#v\nwant: %#v", got, want) t.Errorf("wrong configuration value\ngot: %#v\nwant: %#v", got, want)
} }
} }
func TestNodeApplyableProviderExecute_sensitive(t *testing.T) {
config := &configs.Provider{
Name: "foo",
Config: configs.SynthBody("", map[string]cty.Value{
"test_string": cty.StringVal("hello").Mark("sensitive"),
}),
}
provider := mockProviderWithConfigSchema(simpleTestSchema())
providerAddr := addrs.AbsProviderConfig{
Module: addrs.RootModule,
Provider: addrs.NewDefaultProvider("foo"),
}
n := &NodeApplyableProvider{&NodeAbstractProvider{
Addr: providerAddr,
Config: config,
}}
ctx := &MockEvalContext{ProviderProvider: provider}
ctx.installSimpleEval()
if err := n.Execute(ctx, walkApply); err != nil {
t.Fatalf("err: %s", err)
}
if !ctx.ConfigureProviderCalled {
t.Fatal("should be called")
}
gotObj := ctx.ConfigureProviderConfig
if !gotObj.Type().HasAttribute("test_string") {
t.Fatal("configuration object does not have \"test_string\" attribute")
}
if got, want := gotObj.GetAttr("test_string"), cty.StringVal("hello"); !got.RawEquals(want) {
t.Errorf("wrong configuration value\ngot: %#v\nwant: %#v", got, want)
}
}
func TestNodeApplyableProviderExecute_sensitiveValidate(t *testing.T) {
config := &configs.Provider{
Name: "foo",
Config: configs.SynthBody("", map[string]cty.Value{
"test_string": cty.StringVal("hello").Mark("sensitive"),
}),
}
provider := mockProviderWithConfigSchema(simpleTestSchema())
providerAddr := addrs.AbsProviderConfig{
Module: addrs.RootModule,
Provider: addrs.NewDefaultProvider("foo"),
}
n := &NodeApplyableProvider{&NodeAbstractProvider{
Addr: providerAddr,
Config: config,
}}
ctx := &MockEvalContext{ProviderProvider: provider}
ctx.installSimpleEval()
if err := n.Execute(ctx, walkValidate); err != nil {
t.Fatalf("err: %s", err)
}
if !provider.PrepareProviderConfigCalled {
t.Fatal("should be called")
}
gotObj := provider.PrepareProviderConfigRequest.Config
if !gotObj.Type().HasAttribute("test_string") {
t.Fatal("configuration object does not have \"test_string\" attribute")
}
if got, want := gotObj.GetAttr("test_string"), cty.StringVal("hello"); !got.RawEquals(want) {
t.Errorf("wrong configuration value\ngot: %#v\nwant: %#v", got, want)
}
}