diff --git a/pkg/backends/ibmsecretsmanager.go b/pkg/backends/ibmsecretsmanager.go index 099127bc..305aa63f 100644 --- a/pkg/backends/ibmsecretsmanager.go +++ b/pkg/backends/ibmsecretsmanager.go @@ -160,7 +160,9 @@ func (d IBMSecretData) GetSecret() (map[string]interface{}, error) { } case *ibmsm.KVSecret: { - result["data"] = v.Data + for k, v := range v.Data { + result[k] = v + } } default: { @@ -254,7 +256,9 @@ func (d IBMVersionedSecretData) GetSecret() (map[string]interface{}, error) { case *ibmsm.KVSecretVersion: { if *v.PayloadAvailable { - result["data"] = v.Data + for k, v := range v.Data { + result[k] = v + } } } default: diff --git a/pkg/backends/ibmsecretsmanager_test.go b/pkg/backends/ibmsecretsmanager_test.go index af3c339b..175c9fb4 100644 --- a/pkg/backends/ibmsecretsmanager_test.go +++ b/pkg/backends/ibmsecretsmanager_test.go @@ -4,8 +4,8 @@ import ( "fmt" "reflect" "strings" - "testing" "sync" + "testing" "github.com/IBM/go-sdk-core/v5/core" ibmsm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv2" @@ -20,10 +20,10 @@ type MockIBMSMClient struct { // It is shared b/w both GetSecret and GetSecretVersion for simplicity, even though each writes to a different field GetSecretLock sync.RWMutex - GetSecretCalledWith *ibmsm.GetSecretOptions - GetSecretCallCount int - GetSecretVersionCalledWith *ibmsm.GetSecretVersionOptions - GetSecretVersionCallCount int + GetSecretCalledWith *ibmsm.GetSecretOptions + GetSecretCallCount int + GetSecretVersionCalledWith *ibmsm.GetSecretVersionOptions + GetSecretVersionCallCount int } var BIG_GROUP_LEN int = types.IBMMaxPerPage + 1 @@ -61,6 +61,7 @@ func (m *MockIBMSMClient) ListSecrets(listAllSecretsOptions *ibmsm.ListSecretsOp otype := "username_password" ctype := "public_cert" itype := "iam_credentials" + ktype := "kv" smallGroupSecrets := []ibmsm.SecretMetadataIntf{ &ibmsm.ArbitrarySecretMetadata{ Name: &name, @@ -86,6 +87,12 @@ func (m *MockIBMSMClient) ListSecrets(listAllSecretsOptions *ibmsm.ListSecretsOp SecretGroupID: &smallGroup, ID: &itype, }, + &ibmsm.KVSecretMetadata{ + Name: &name, + SecretType: &ktype, + SecretGroupID: &smallGroup, + ID: &ktype, + }, } // Empty secret group @@ -141,6 +148,17 @@ func (m *MockIBMSMClient) GetSecret(getSecretOptions *ibmsm.GetSecretOptions) (r ID: &id, ApiKey: &payload, }, nil, nil + } else if *getSecretOptions.ID == "kv" { + name := "my-secret" + id := "kv" + payload := map[string]interface{}{ + "hello": "there", + } + return &ibmsm.KVSecret{ + Name: &name, + ID: &id, + Data: payload, + }, nil, nil } else { name := "my-secret" id := "username_password" @@ -366,6 +384,48 @@ func TestIBMSecretsManagerGetSecrets(t *testing.T) { } }) + t.Run("Retrieves payload of KV secrets", func(t *testing.T) { + mock := MockIBMSMClient{} + sm := backends.NewIBMSecretsManagerBackend(&mock) + + res, err := sm.GetSecrets("ibmcloud/kv/secrets/groups/small-group", "", nil) + if err != nil { + t.Fatalf("%s", err) + } + + // Properly calls ListSecrets + var offset int64 = 0 + expectedListArgs := &ibmsm.ListSecretsOptions{ + Groups: []string{"small-group"}, + Offset: &offset, + } + if !reflect.DeepEqual(mock.ListSecretsOptionCalledWith[0], expectedListArgs) { + t.Errorf("expectedListArgs: %s, got: %s.", expectedListArgs.Groups, mock.ListSecretsOptionCalledWith[0].Groups) + } + if len(mock.ListSecretsOptionCalledWith) > 1 { + t.Errorf("ListSecrets should be called %d times got %d", 1, len(mock.ListSecretsOptionCalledWith)) + } + + // Properly calls GetSecret + id := "kv" + expectedGetArgs := &ibmsm.GetSecretOptions{ + ID: &id, + } + if !reflect.DeepEqual(mock.GetSecretCalledWith, expectedGetArgs) { + t.Errorf("Retrieved ID and SecretType do not match expected") + } + + // Correct data + expected := map[string]interface{}{ + "my-secret": map[string]interface{}{ + "hello": "there", + }, + } + if !reflect.DeepEqual(res, expected) { + t.Errorf("expected: %s, got: %s.", expected, res) + } + }) + t.Run("Properly retrieves versioned secrets", func(t *testing.T) { mock := MockIBMSMClient{} sm := backends.NewIBMSecretsManagerBackend(&mock)