Skip to content

Commit

Permalink
Do not panic on Vault PKI roles without the cn_validations field
Browse files Browse the repository at this point in the history
 - When we added support for the cn_validations field within PR1820
   the code was tested against Vault versions 1.12 and higher.
 - If Vault is an earlier version than 1.12 TFVP will panic as we
   attempt to cast the missing field to an []interface{} value.
 - This fix hardens the parsing path for slice values within the
   PKI role parsing to properly extract out a []string value
  • Loading branch information
stevendpclark committed Jan 27, 2025
1 parent 4b26c7a commit 605f4f9
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 5 deletions.
38 changes: 38 additions & 0 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"time"

"github.com/cenkalti/backoff/v4"
"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/vault/api"
)
Expand Down Expand Up @@ -450,3 +451,40 @@ func RetryWrite(client *api.Client, path string, data map[string]interface{}, re
log.Printf("[WARN] Writing to path %q failed, retrying in %s", path, duration)
})
}

// GetStringSliceFromSecret will return a string slice from the secret data within the provided field name if it exists.
// The bool return value will be false if the field does not exist or is not a string slice. It will be true if the field
// exists and was an empty slice.
func GetStringSliceFromSecret(secret *api.Secret, fieldName string) ([]string, bool) {
if secret == nil || secret.Data == nil {
return nil, false
}

rawVal, exists := secret.Data[fieldName]
if !exists {
return nil, false
}

rv := reflect.ValueOf(rawVal)
switch rv.Kind() {
case reflect.Slice:
if rv.IsNil() {
return nil, false
}
case reflect.Array:
default:
return nil, false
}

output := make([]string, rv.Len())

for i := 0; i < rv.Len(); i++ {
myStr, err := parseutil.ParseString(rv.Index(i).Interface())
if err != nil {
return nil, false
}
output[i] = myStr
}

return output, true
}
36 changes: 36 additions & 0 deletions util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -871,3 +871,39 @@ func TestRetryWrite(t *testing.T) {
})
}
}

func TestGetStringSliceFromSecret(t *testing.T) {
fieldName := "foo"
var testArray [2]string
testArray[0] = "1"
testArray[1] = "2"

tests := []struct {
name string
secretData map[string]interface{}
want []string
wantOk bool
}{
{"nil-data", nil, nil, false},
{"field-missing", map[string]interface{}{}, nil, false},
{"nil-element", map[string]interface{}{fieldName: nil}, nil, false},
{"not-a-slice", map[string]interface{}{fieldName: "test-value"}, nil, false},
{"array-val", map[string]interface{}{fieldName: testArray}, []string{"1", "2"}, true},
{"mixed-slice", map[string]interface{}{fieldName: []interface{}{1, "2"}}, []string{"1", "2"}, true},
{"int-slice", map[string]interface{}{fieldName: []int{1, 2}}, []string{"1", "2"}, true},
{"string-slice", map[string]interface{}{fieldName: []string{"test1", "test2"}}, []string{"test1", "test2"}, true},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
secret := &api.Secret{Data: tt.secretData}
got, gotOk := GetStringSliceFromSecret(secret, fieldName)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetStringSliceFromSecret() got = %v, want %v", got, tt.want)
}
if gotOk != tt.wantOk {
t.Errorf("GetStringSliceFromSecret() gotOk = %v, wantOk %v", gotOk, tt.wantOk)
}
})
}
}
10 changes: 5 additions & 5 deletions vault/resource_pki_secret_backend_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"

"github.com/hashicorp/terraform-provider-vault/internal/consts"
"github.com/hashicorp/terraform-provider-vault/internal/pki"
"github.com/hashicorp/terraform-provider-vault/internal/provider"
"github.com/hashicorp/terraform-provider-vault/util"
)

var (
Expand Down Expand Up @@ -575,10 +575,10 @@ func pkiSecretBackendRoleRead(_ context.Context, d *schema.ResourceData, meta in
listFields := append(pkiSecretListFields, consts.FieldKeyUsage)
// handle TypeList
for _, k := range listFields {
list := expandStringSlice(secret.Data[k].([]interface{}))

if len(list) > 0 {
d.Set(k, list)
if list, ok := util.GetStringSliceFromSecret(secret, k); ok {
if len(list) > 0 {
d.Set(k, list)
}
}
}

Expand Down

0 comments on commit 605f4f9

Please sign in to comment.