diff --git a/go.mod b/go.mod index 497a9c5ab..819a71971 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 github.com/IBM/go-sdk-core/v5 v5.13.1 - github.com/IBM/secrets-manager-go-sdk v1.2.0 + github.com/IBM/secrets-manager-go-sdk/v2 v2.0.0 github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 github.com/PaesslerAG/jsonpath v0.1.1 diff --git a/go.sum b/go.sum index e910c0205..733f61821 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,8 @@ github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/IBM/go-sdk-core/v5 v5.13.1 h1:zD6p3t1whAlRJo/VBmE69c8RcH9LCHL1n0/sO1MWlpw= github.com/IBM/go-sdk-core/v5 v5.13.1/go.mod h1:pVkN7IGmsSdmR1ZCU4E/cLcCclqRKMYgg7ya+O2Mk6g= -github.com/IBM/secrets-manager-go-sdk v1.2.0 h1:bgFfBF+LjHLtUfV3hTLkfgE8EjFsJaeU2icA2Hg+M50= -github.com/IBM/secrets-manager-go-sdk v1.2.0/go.mod h1:qv+tQg8Z3Vb11DQYxDjEGeROHDtTLQxUWuOIrIdWg6E= +github.com/IBM/secrets-manager-go-sdk/v2 v2.0.0 h1:Lx4Bvim/MfoHEYR+n312bty5DirAJypBGGS9YZo3zCw= +github.com/IBM/secrets-manager-go-sdk/v2 v2.0.0/go.mod h1:jagqWmjZ0zUEqh5jdGB42ApSQS40fu2LWw6pdg8JJko= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= diff --git a/pkg/provider/ibm/fake/fake.go b/pkg/provider/ibm/fake/fake.go index 91bedbf38..08239561e 100644 --- a/pkg/provider/ibm/fake/fake.go +++ b/pkg/provider/ibm/fake/fake.go @@ -14,31 +14,38 @@ limitations under the License. package fake import ( + "context" "fmt" "github.com/IBM/go-sdk-core/v5/core" - sm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv1" + sm "github.com/IBM/secrets-manager-go-sdk/v2/secretsmanagerv2" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" ) type IBMMockClient struct { - getSecret func(getSecretOptions *sm.GetSecretOptions) (result *sm.GetSecret, response *core.DetailedResponse, err error) + getSecretWithContext func(ctx context.Context, getSecretOptions *sm.GetSecretOptions) (result sm.SecretIntf, response *core.DetailedResponse, err error) } -func (mc *IBMMockClient) GetSecret(getSecretOptions *sm.GetSecretOptions) (result *sm.GetSecret, response *core.DetailedResponse, err error) { - return mc.getSecret(getSecretOptions) +type IBMMockClientParams struct { + GetSecretOptions *sm.GetSecretOptions + GetSecretOutput sm.SecretIntf + GetSecretErr error } -func (mc *IBMMockClient) WithValue(input *sm.GetSecretOptions, output *sm.GetSecret, err error) { +func (mc *IBMMockClient) GetSecretWithContext(ctx context.Context, getSecretOptions *sm.GetSecretOptions) (result sm.SecretIntf, response *core.DetailedResponse, err error) { + return mc.getSecretWithContext(ctx, getSecretOptions) +} + +func (mc *IBMMockClient) WithValue(params IBMMockClientParams) { if mc != nil { - mc.getSecret = func(paramReq *sm.GetSecretOptions) (*sm.GetSecret, *core.DetailedResponse, error) { + mc.getSecretWithContext = func(ctx context.Context, paramReq *sm.GetSecretOptions) (sm.SecretIntf, *core.DetailedResponse, error) { // type secretmanagerpb.AccessSecretVersionRequest contains unexported fields // use cmpopts.IgnoreUnexported to ignore all the unexported fields in the cmp. - if !cmp.Equal(paramReq, input, cmpopts.IgnoreUnexported(sm.GetSecret{})) { - return nil, nil, fmt.Errorf("unexpected test argument") + if !cmp.Equal(paramReq, params.GetSecretOptions, cmpopts.IgnoreUnexported(sm.Secret{})) { + return nil, nil, fmt.Errorf("unexpected test argument for GetSecret: %s, %s", *paramReq.ID, *params.GetSecretOptions.ID) } - return output, nil, err + return params.GetSecretOutput, nil, params.GetSecretErr } } } diff --git a/pkg/provider/ibm/provider.go b/pkg/provider/ibm/provider.go index 2b5e8fa14..ec427431c 100644 --- a/pkg/provider/ibm/provider.go +++ b/pkg/provider/ibm/provider.go @@ -23,11 +23,10 @@ import ( "time" core "github.com/IBM/go-sdk-core/v5/core" - sm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv1" + sm "github.com/IBM/secrets-manager-go-sdk/v2/secretsmanagerv2" gjson "github.com/tidwall/gjson" corev1 "k8s.io/api/core/v1" types "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" kclient "sigs.k8s.io/controller-runtime/pkg/client" esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" @@ -40,6 +39,14 @@ const ( STSEndpointEnv = "IBM_STS_ENDPOINT" SSMEndpointEnv = "IBM_SSM_ENDPOINT" + certificateConst = "certificate" + intermediateConst = "intermediate" + privateKeyConst = "private_key" + usernameConst = "username" + passwordConst = "password" + apikeyConst = "apikey" + arbitraryConst = "arbitrary" + errIBMClient = "cannot setup new ibm client: %w" errIBMCredSecretName = "invalid IBM SecretStore resource: missing IBM APIKey" errUninitalizedIBMProvider = "provider IBM is not initialized" @@ -47,14 +54,17 @@ const ( errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w" errMissingSAK = "missing SecretAccessKey" errJSONSecretUnmarshal = "unable to unmarshal secret: %w" + errExtractingSecret = "unable to extract the fetched secret %s of type %s" ) +var contextTimeout = time.Minute * 2 + // https://github.com/external-secrets/external-secrets/issues/644 var _ esv1beta1.SecretsClient = &providerIBM{} var _ esv1beta1.Provider = &providerIBM{} type SecretManagerClient interface { - GetSecret(getSecretOptions *sm.GetSecretOptions) (result *sm.GetSecret, response *core.DetailedResponse, err error) + GetSecretWithContext(ctx context.Context, getSecretOptions *sm.GetSecretOptions) (result sm.SecretIntf, response *core.DetailedResponse, err error) } type providerIBM struct { @@ -69,8 +79,6 @@ type client struct { credentials []byte } -var log = ctrl.Log.WithName("provider").WithName("ibm").WithName("secretsmanager") - func (c *client) setAuth(ctx context.Context) error { credentialsSecret := &corev1.Secret{} credentialsSecretName := c.store.Auth.SecretRef.SecretAPIKey.Name @@ -122,7 +130,7 @@ func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecre return nil, fmt.Errorf(errUninitalizedIBMProvider) } - secretType := sm.GetSecretOptionsSecretTypeArbitraryConst + secretType := sm.Secret_SecretType_Arbitrary secretName := ref.Key nameSplitted := strings.Split(secretName, "/") @@ -132,22 +140,21 @@ func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecre } switch secretType { - case sm.GetSecretOptionsSecretTypeArbitraryConst: - + case sm.Secret_SecretType_Arbitrary: return getArbitrarySecret(ibm, &secretName) - case sm.CreateSecretOptionsSecretTypeUsernamePasswordConst: + case sm.Secret_SecretType_UsernamePassword: if ref.Property == "" { return nil, fmt.Errorf("remoteRef.property required for secret type username_password") } return getUsernamePasswordSecret(ibm, &secretName, ref) - case sm.CreateSecretOptionsSecretTypeIamCredentialsConst: + case sm.Secret_SecretType_IamCredentials: return getIamCredentialsSecret(ibm, &secretName) - case sm.CreateSecretOptionsSecretTypeImportedCertConst: + case sm.Secret_SecretType_ImportedCert: if ref.Property == "" { return nil, fmt.Errorf("remoteRef.property required for secret type imported_cert") @@ -155,7 +162,7 @@ func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecre return getImportCertSecret(ibm, &secretName, ref) - case sm.CreateSecretOptionsSecretTypePublicCertConst: + case sm.Secret_SecretType_PublicCert: if ref.Property == "" { return nil, fmt.Errorf("remoteRef.property required for secret type public_cert") @@ -163,7 +170,7 @@ func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecre return getPublicCertSecret(ibm, &secretName, ref) - case sm.CreateSecretOptionsSecretTypePrivateCertConst: + case sm.Secret_SecretType_PrivateCert: if ref.Property == "" { return nil, fmt.Errorf("remoteRef.property required for secret type private_cert") @@ -171,7 +178,7 @@ func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecre return getPrivateCertSecret(ibm, &secretName, ref) - case sm.CreateSecretOptionsSecretTypeKvConst: + case sm.Secret_SecretType_Kv: return getKVSecret(ibm, &secretName, ref) @@ -181,150 +188,130 @@ func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecre } func getArbitrarySecret(ibm *providerIBM, secretName *string) ([]byte, error) { - response, _, err := ibm.IBMClient.GetSecret( - &sm.GetSecretOptions{ - SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst), - ID: secretName, - }) - metrics.ObserveAPICall(metrics.ProviderIBMSM, metrics.CallIBMSMGetSecret, err) + response, err := getSecretData(ibm, secretName) if err != nil { return nil, err } + secret, ok := response.(*sm.ArbitrarySecret) + if !ok { + return nil, fmt.Errorf(errExtractingSecret, *secretName, sm.Secret_SecretType_Arbitrary) + } - secret := response.Resources[0].(*sm.SecretResource) - secretData := secret.SecretData - arbitrarySecretPayload := secretData["payload"].(string) - return []byte(arbitrarySecretPayload), nil + return []byte(*secret.Payload), nil } func getImportCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) { - response, _, err := ibm.IBMClient.GetSecret( - &sm.GetSecretOptions{ - SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeImportedCertConst), - ID: secretName, - }) - metrics.ObserveAPICall(metrics.ProviderIBMSM, metrics.CallIBMSMGetSecret, err) + response, err := getSecretData(ibm, secretName) if err != nil { return nil, err } - - secret := response.Resources[0].(*sm.SecretResource) - secretData := secret.SecretData - - if val, ok := secretData[ref.Property]; ok { - return []byte(val.(string)), nil + secret, ok := response.(*sm.ImportedCertificate) + if !ok { + return nil, fmt.Errorf(errExtractingSecret, *secretName, sm.Secret_SecretType_ImportedCert) + } + switch ref.Property { + case certificateConst: + return []byte(*secret.Certificate), nil + case intermediateConst: + return []byte(*secret.Intermediate), nil + case privateKeyConst: + return []byte(*secret.PrivateKey), nil + default: + return nil, fmt.Errorf("unknown property type %s", ref.Property) } - return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key) } func getPublicCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) { - response, _, err := ibm.IBMClient.GetSecret( - &sm.GetSecretOptions{ - SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypePublicCertConst), - ID: secretName, - }) - metrics.ObserveAPICall(metrics.ProviderIBMSM, metrics.CallIBMSMGetSecret, err) + response, err := getSecretData(ibm, secretName) if err != nil { return nil, err } - - secret := response.Resources[0].(*sm.SecretResource) - secretData := secret.SecretData - - if val, ok := secretData[ref.Property]; ok { - return []byte(val.(string)), nil + secret, ok := response.(*sm.PublicCertificate) + if !ok { + return nil, fmt.Errorf(errExtractingSecret, *secretName, sm.Secret_SecretType_PublicCert) + } + + switch ref.Property { + case certificateConst: + return []byte(*secret.Certificate), nil + case intermediateConst: + return []byte(*secret.Intermediate), nil + case privateKeyConst: + return []byte(*secret.PrivateKey), nil + default: + return nil, fmt.Errorf("unknown property type %s", ref.Property) } - return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key) } func getPrivateCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) { - response, _, err := ibm.IBMClient.GetSecret( - &sm.GetSecretOptions{ - SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypePrivateCertConst), - ID: secretName, - }) - metrics.ObserveAPICall(metrics.ProviderIBMSM, metrics.CallIBMSMGetSecret, err) + response, err := getSecretData(ibm, secretName) if err != nil { return nil, err } - - secret := response.Resources[0].(*sm.SecretResource) - secretData := secret.SecretData - - if val, ok := secretData[ref.Property]; ok { - return []byte(val.(string)), nil + secret, ok := response.(*sm.PrivateCertificate) + if !ok { + return nil, fmt.Errorf(errExtractingSecret, *secretName, sm.Secret_SecretType_PrivateCert) + } + switch ref.Property { + case certificateConst: + return []byte(*secret.Certificate), nil + case privateKeyConst: + return []byte(*secret.PrivateKey), nil + default: + return nil, fmt.Errorf("unknown property type %s", ref.Property) } - return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key) } func getIamCredentialsSecret(ibm *providerIBM, secretName *string) ([]byte, error) { - response, _, err := ibm.IBMClient.GetSecret( - &sm.GetSecretOptions{ - SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst), - ID: secretName, - }) - metrics.ObserveAPICall(metrics.ProviderIBMSM, metrics.CallIBMSMGetSecret, err) + response, err := getSecretData(ibm, secretName) if err != nil { return nil, err } - - secret := response.Resources[0].(*sm.SecretResource) - secretData := *secret.APIKey - - return []byte(secretData), nil + secret, ok := response.(*sm.IAMCredentialsSecret) + if !ok { + return nil, fmt.Errorf(errExtractingSecret, *secretName, sm.Secret_SecretType_IamCredentials) + } + return []byte(*secret.ApiKey), nil } func getUsernamePasswordSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) { - response, _, err := ibm.IBMClient.GetSecret( - &sm.GetSecretOptions{ - SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst), - ID: secretName, - }) - metrics.ObserveAPICall(metrics.ProviderIBMSM, metrics.CallIBMSMGetSecret, err) + response, err := getSecretData(ibm, secretName) if err != nil { return nil, err } - - secret := response.Resources[0].(*sm.SecretResource) - secretData := secret.SecretData - - if val, ok := secretData[ref.Property]; ok { - return []byte(val.(string)), nil + secret, ok := response.(*sm.UsernamePasswordSecret) + if !ok { + return nil, fmt.Errorf(errExtractingSecret, *secretName, sm.Secret_SecretType_UsernamePassword) + } + switch ref.Property { + case "username": + return []byte(*secret.Username), nil + case "password": + return []byte(*secret.Password), nil + default: + return nil, fmt.Errorf("unknown property type %s", ref.Property) } - return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key) } // Returns a secret of type kv and supports json path. func getKVSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) { - secret, err := getSecretByType(ibm, secretName, sm.CreateSecretOptionsSecretTypeKvConst) + response, err := getSecretData(ibm, secretName) if err != nil { return nil, err } - - log.Info("fetching secret", "secretName", secretName, "key", ref.Key) - - secretData := secret.SecretData - - payload, ok := secretData["payload"] + secret, ok := response.(*sm.KVSecret) if !ok { - return nil, fmt.Errorf("no payload returned for secret %s", ref.Key) + return nil, fmt.Errorf(errExtractingSecret, *secretName, sm.Secret_SecretType_Kv) } - - payloadJSON := payload - - payloadJSONMap, ok := payloadJSON.(map[string]interface{}) - if ok { - var payloadJSONByte []byte - payloadJSONByte, err = json.Marshal(payloadJSONMap) - if err != nil { - return nil, fmt.Errorf("marshaling payload from secret failed. %w", err) - } - payloadJSON = string(payloadJSONByte) + payloadJSONByte, err := json.Marshal(secret.Data) + if err != nil { + return nil, fmt.Errorf("marshaling payload from secret failed. %w", err) } + payloadJSON := string(payloadJSONByte) // no property requested, return the entire payload if ref.Property == "" { - return []byte(payloadJSON.(string)), nil + return []byte(payloadJSON), nil } // returns the requested key @@ -340,7 +327,7 @@ func getKVSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSec if idx > 0 { refProperty = strings.ReplaceAll(refProperty, ".", "\\.") - val := gjson.Get(payloadJSON.(string), refProperty) + val := gjson.Get(payloadJSON, refProperty) if val.Exists() { return []byte(val.String()), nil } @@ -348,7 +335,7 @@ func getKVSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSec // b) "." is symbole for JSON path // try to get value for this path - val := gjson.Get(payloadJSON.(string), ref.Property) + val := gjson.Get(payloadJSON, ref.Property) if !val.Exists() { return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key) } @@ -358,20 +345,19 @@ func getKVSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSec return nil, fmt.Errorf("no property provided for secret %s", ref.Key) } -func getSecretByType(ibm *providerIBM, secretName *string, secretType string) (*sm.SecretResource, error) { - response, _, err := ibm.IBMClient.GetSecret( +func getSecretData(ibm *providerIBM, secretName *string) (sm.SecretIntf, error) { + ctx, cancel := context.WithTimeout(context.Background(), contextTimeout) + defer cancel() + response, _, err := ibm.IBMClient.GetSecretWithContext( + ctx, &sm.GetSecretOptions{ - SecretType: core.StringPtr(secretType), - ID: secretName, + ID: secretName, }) metrics.ObserveAPICall(metrics.ProviderIBMSM, metrics.CallIBMSMGetSecret, err) if err != nil { return nil, err } - - secret := response.Resources[0].(*sm.SecretResource) - - return secret, nil + return response, nil } func (ibm *providerIBM) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) { @@ -379,7 +365,7 @@ func (ibm *providerIBM) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSe return nil, fmt.Errorf(errUninitalizedIBMProvider) } - secretType := sm.GetSecretOptionsSecretTypeArbitraryConst + secretType := sm.Secret_SecretType_Arbitrary secretName := ref.Key nameSplitted := strings.Split(secretName, "/") @@ -388,124 +374,73 @@ func (ibm *providerIBM) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSe secretName = nameSplitted[1] } + secretMap := make(map[string][]byte) + response, err := getSecretData(ibm, &secretName) + if err != nil { + return nil, err + } + switch secretType { - case sm.GetSecretOptionsSecretTypeArbitraryConst: - response, _, err := ibm.IBMClient.GetSecret( - &sm.GetSecretOptions{ - SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst), - ID: &ref.Key, - }) - metrics.ObserveAPICall(metrics.ProviderIBMSM, metrics.CallIBMSMGetSecret, err) - if err != nil { - return nil, err + case sm.Secret_SecretType_Arbitrary: + secretData, ok := response.(*sm.ArbitrarySecret) + if !ok { + return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_Arbitrary) } + secretMap[arbitraryConst] = []byte(*secretData.Payload) + return secretMap, nil - secret := response.Resources[0].(*sm.SecretResource) - secretData := secret.SecretData - arbitrarySecretPayload := secretData["payload"].(string) - - kv := make(map[string]interface{}) - err = json.Unmarshal([]byte(arbitrarySecretPayload), &kv) - if err != nil { - return nil, fmt.Errorf(errJSONSecretUnmarshal, err) + case sm.Secret_SecretType_UsernamePassword: + secretData, ok := response.(*sm.UsernamePasswordSecret) + if !ok { + return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_UsernamePassword) } - - secretMap := byteArrayMap(kv) + secretMap[usernameConst] = []byte(*secretData.Username) + secretMap[passwordConst] = []byte(*secretData.Password) return secretMap, nil - case sm.CreateSecretOptionsSecretTypeUsernamePasswordConst: - response, _, err := ibm.IBMClient.GetSecret( - &sm.GetSecretOptions{ - SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst), - ID: &secretName, - }) - metrics.ObserveAPICall(metrics.ProviderIBMSM, metrics.CallIBMSMGetSecret, err) - if err != nil { - return nil, err + case sm.Secret_SecretType_IamCredentials: + secretData, ok := response.(*sm.IAMCredentialsSecret) + if !ok { + return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_IamCredentials) } - - secret := response.Resources[0].(*sm.SecretResource) - secretData := secret.SecretData - - secretMap := byteArrayMap(secretData) + secretMap[apikeyConst] = []byte(*secretData.ApiKey) return secretMap, nil - case sm.CreateSecretOptionsSecretTypeIamCredentialsConst: - response, _, err := ibm.IBMClient.GetSecret( - &sm.GetSecretOptions{ - SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst), - ID: &secretName, - }) - metrics.ObserveAPICall(metrics.ProviderIBMSM, metrics.CallIBMSMGetSecret, err) - if err != nil { - return nil, err + case sm.Secret_SecretType_ImportedCert: + secretData, ok := response.(*sm.ImportedCertificate) + if !ok { + return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_ImportedCert) } - - secret := response.Resources[0].(*sm.SecretResource) - secretData := *secret.APIKey - - secretMap := make(map[string][]byte) - secretMap["apikey"] = []byte(secretData) + secretMap[certificateConst] = []byte(*secretData.Certificate) + secretMap[intermediateConst] = []byte(*secretData.Intermediate) + secretMap[privateKeyConst] = []byte(*secretData.PrivateKey) return secretMap, nil - case sm.CreateSecretOptionsSecretTypeImportedCertConst: - response, _, err := ibm.IBMClient.GetSecret( - &sm.GetSecretOptions{ - SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeImportedCertConst), - ID: &secretName, - }) - metrics.ObserveAPICall(metrics.ProviderIBMSM, metrics.CallIBMSMGetSecret, err) - if err != nil { - return nil, err + case sm.Secret_SecretType_PublicCert: + secretData, ok := response.(*sm.PublicCertificate) + if !ok { + return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_PublicCert) } - - secret := response.Resources[0].(*sm.SecretResource) - secretData := secret.SecretData - - secretMap := byteArrayMap(secretData) + secretMap[certificateConst] = []byte(*secretData.Certificate) + secretMap[intermediateConst] = []byte(*secretData.Intermediate) + secretMap[privateKeyConst] = []byte(*secretData.PrivateKey) return secretMap, nil - case sm.CreateSecretOptionsSecretTypePublicCertConst: - response, _, err := ibm.IBMClient.GetSecret( - &sm.GetSecretOptions{ - SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypePublicCertConst), - ID: &secretName, - }) - metrics.ObserveAPICall(metrics.ProviderIBMSM, metrics.CallIBMSMGetSecret, err) - if err != nil { - return nil, err + case sm.Secret_SecretType_PrivateCert: + secretData, ok := response.(*sm.PrivateCertificate) + if !ok { + return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_PrivateCert) } - - secret := response.Resources[0].(*sm.SecretResource) - secretData := secret.SecretData - - secretMap := byteArrayMap(secretData) + secretMap[certificateConst] = []byte(*secretData.Certificate) + secretMap[privateKeyConst] = []byte(*secretData.PrivateKey) return secretMap, nil - case sm.CreateSecretOptionsSecretTypePrivateCertConst: - response, _, err := ibm.IBMClient.GetSecret( - &sm.GetSecretOptions{ - SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypePrivateCertConst), - ID: &secretName, - }) - metrics.ObserveAPICall(metrics.ProviderIBMSM, metrics.CallIBMSMGetSecret, err) - if err != nil { - return nil, err - } - - secret := response.Resources[0].(*sm.SecretResource) - secretData := secret.SecretData - - secretMap := byteArrayMap(secretData) - - return secretMap, nil - - case sm.CreateSecretOptionsSecretTypeKvConst: + case sm.Secret_SecretType_Kv: secret, err := getKVSecret(ibm, &secretName, ref) if err != nil { return nil, err @@ -513,7 +448,7 @@ func (ibm *providerIBM) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSe m := make(map[string]interface{}) err = json.Unmarshal(secret, &m) if err != nil { - return nil, err + return nil, fmt.Errorf(errJSONSecretUnmarshal, err) } secretMap := byteArrayMap(m) @@ -618,7 +553,7 @@ func (ibm *providerIBM) NewClient(ctx context.Context, store esv1beta1.GenericSt } var err error - var secretsManager *sm.SecretsManagerV1 + var secretsManager *sm.SecretsManagerV2 containerAuthProfile := iStore.store.Auth.ContainerAuth.Profile if containerAuthProfile != "" { // container-based auth @@ -642,7 +577,7 @@ func (ibm *providerIBM) NewClient(ctx context.Context, store esv1beta1.GenericSt if err != nil { return nil, fmt.Errorf(errIBMClient, err) } - secretsManager, err = sm.NewSecretsManagerV1(&sm.SecretsManagerV1Options{ + secretsManager, err = sm.NewSecretsManagerV2(&sm.SecretsManagerV2Options{ URL: *storeSpec.Provider.IBM.ServiceURL, Authenticator: authenticator, }) @@ -655,7 +590,7 @@ func (ibm *providerIBM) NewClient(ctx context.Context, store esv1beta1.GenericSt return nil, err } - secretsManager, err = sm.NewSecretsManagerV1(&sm.SecretsManagerV1Options{ + secretsManager, err = sm.NewSecretsManagerV2(&sm.SecretsManagerV2Options{ URL: *storeSpec.Provider.IBM.ServiceURL, Authenticator: &core.IamAuthenticator{ ApiKey: string(iStore.credentials), diff --git a/pkg/provider/ibm/provider_test.go b/pkg/provider/ibm/provider_test.go index e970ed5f7..1242c547a 100644 --- a/pkg/provider/ibm/provider_test.go +++ b/pkg/provider/ibm/provider_test.go @@ -15,13 +15,14 @@ package ibm import ( "context" + "encoding/json" "fmt" "reflect" "strings" "testing" "github.com/IBM/go-sdk-core/v5/core" - sm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv1" + sm "github.com/IBM/secrets-manager-go-sdk/v2/secretsmanagerv2" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilpointer "k8s.io/utils/pointer" @@ -35,12 +36,14 @@ import ( const ( errExpectedErr = "wanted error got nil" secretKey = "test-secret" + secretUUID = "d5deb37a-7883-4fe2-a5e7-3c15420adc76" ) type secretManagerTestCase struct { + name string mockClient *fakesm.IBMMockClient apiInput *sm.GetSecretOptions - apiOutput *sm.GetSecret + apiOutput sm.SecretIntf ref *esv1beta1.ExternalSecretDataRemoteRef serviceURL *string apiErr error @@ -62,37 +65,36 @@ func makeValidSecretManagerTestCase() *secretManagerTestCase { expectedSecret: "", expectedData: map[string][]byte{}, } - smtc.mockClient.WithValue(smtc.apiInput, smtc.apiOutput, smtc.apiErr) + mcParams := fakesm.IBMMockClientParams{ + GetSecretOptions: smtc.apiInput, + GetSecretOutput: smtc.apiOutput, + GetSecretErr: smtc.apiErr, + } + smtc.mockClient.WithValue(mcParams) return &smtc } func makeValidRef() *esv1beta1.ExternalSecretDataRemoteRef { return &esv1beta1.ExternalSecretDataRemoteRef{ - Key: secretKey, + Key: secretUUID, Version: "default", } } func makeValidAPIInput() *sm.GetSecretOptions { return &sm.GetSecretOptions{ - SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst), - ID: utilpointer.String(secretKey), + ID: utilpointer.String(secretUUID), } } -func makeValidAPIOutput() *sm.GetSecret { - secretData := make(map[string]interface{}) - secretData["payload"] = "" - - return &sm.GetSecret{ - Resources: []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String("testytype"), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }, - }, +func makeValidAPIOutput() sm.SecretIntf { + secret := &sm.Secret{ + SecretType: utilpointer.String(sm.Secret_SecretType_Arbitrary), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), } + var i sm.SecretIntf = secret + return i } func makeValidSecretManagerTestCaseCustom(tweaks ...func(smtc *secretManagerTestCase)) *secretManagerTestCase { @@ -100,7 +102,12 @@ func makeValidSecretManagerTestCaseCustom(tweaks ...func(smtc *secretManagerTest for _, fn := range tweaks { fn(smtc) } - smtc.mockClient.WithValue(smtc.apiInput, smtc.apiOutput, smtc.apiErr) + mcParams := fakesm.IBMMockClientParams{ + GetSecretOptions: smtc.apiInput, + GetSecretOutput: smtc.apiOutput, + GetSecretErr: smtc.apiErr, + } + smtc.mockClient.WithValue(mcParams) return smtc } @@ -168,71 +175,71 @@ func TestValidateStore(t *testing.T) { // test the sm<->gcp interface // make sure correct values are passed and errors are handled accordingly. func TestIBMSecretManagerGetSecret(t *testing.T) { - secretData := make(map[string]interface{}) secretString := "changedvalue" + secretUsername := "userName" secretPassword := "P@ssw0rd" secretAPIKey := "01234567890" secretCertificate := "certificate_value" - secretData["payload"] = secretString - secretData["password"] = secretPassword - secretData["certificate"] = secretCertificate - // good case: default version is set // key is passed in, output is sent back setSecretString := func(smtc *secretManagerTestCase) { - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String("testytype"), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }} - - smtc.apiOutput.Resources = resources + secret := &sm.ArbitrarySecret{ + SecretType: utilpointer.String(sm.Secret_SecretType_Arbitrary), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Payload: &secretString, + } + smtc.name = "good case: default version is set" + smtc.apiOutput = secret + smtc.apiInput.ID = utilpointer.String(secretUUID) smtc.expectedSecret = secretString } // good case: custom version set setCustomKey := func(smtc *secretManagerTestCase) { - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String("testytype"), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }} - smtc.ref.Key = "testyname" - smtc.apiInput.ID = utilpointer.String("testyname") - smtc.apiOutput.Resources = resources + secret := &sm.ArbitrarySecret{ + SecretType: utilpointer.String(sm.Secret_SecretType_Arbitrary), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Payload: &secretString, + } + smtc.name = "good case: custom version set" + smtc.ref.Key = "arbitrary/" + secretUUID + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret smtc.expectedSecret = secretString } // bad case: username_password type without property - secretUserPass := "username_password/test-secret" + secretUserPass := "username_password/" + secretUUID badSecretUserPass := func(smtc *secretManagerTestCase) { - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst) - smtc.apiOutput.Resources = resources + secret := &sm.UsernamePasswordSecret{ + SecretType: utilpointer.String(sm.Secret_SecretType_UsernamePassword), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Username: &secretUsername, + Password: &secretPassword, + } + smtc.name = "bad case: username_password type without property" + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret smtc.ref.Key = secretUserPass smtc.expectError = "remoteRef.property required for secret type username_password" } // good case: username_password type with property setSecretUserPass := func(smtc *secretManagerTestCase) { - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst) - smtc.apiOutput.Resources = resources + secret := &sm.UsernamePasswordSecret{ + SecretType: utilpointer.String(sm.Secret_SecretType_UsernamePassword), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Username: &secretUsername, + Password: &secretPassword, + } + smtc.name = "good case: username_password type with property" + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret smtc.ref.Key = secretUserPass smtc.ref.Property = "password" smtc.expectedSecret = secretPassword @@ -240,31 +247,25 @@ func TestIBMSecretManagerGetSecret(t *testing.T) { // good case: iam_credenatials type setSecretIam := func(smtc *secretManagerTestCase) { - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeIamCredentialsConst), - Name: utilpointer.String("testyname"), - APIKey: utilpointer.String(secretAPIKey), - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst) - smtc.apiOutput.Resources = resources - smtc.ref.Key = "iam_credentials/test-secret" + secret := &sm.IAMCredentialsSecret{ + SecretType: utilpointer.String(sm.Secret_SecretType_IamCredentials), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + ApiKey: utilpointer.String(secretAPIKey), + } + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.name = "good case: iam_credenatials type" + smtc.apiOutput = secret + smtc.ref.Key = "iam_credentials/" + secretUUID smtc.expectedSecret = secretAPIKey } - funcSetCertSecretTest := func(certType string, good bool) func(*secretManagerTestCase) { + funcSetCertSecretTest := func(secret sm.SecretIntf, name, certType string, good bool) func(*secretManagerTestCase) { return func(smtc *secretManagerTestCase) { - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(certType), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }} - - smtc.apiInput.SecretType = core.StringPtr(certType) - smtc.apiOutput.Resources = resources - smtc.ref.Key = certType + "/" + secretKey + smtc.name = name + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret + smtc.ref.Key = certType + "/" + secretUUID if good { smtc.ref.Property = "certificate" smtc.expectedSecret = secretCertificate @@ -275,61 +276,82 @@ func TestIBMSecretManagerGetSecret(t *testing.T) { } // good case: imported_cert type with property - setSecretCert := funcSetCertSecretTest(sm.CreateSecretOptionsSecretTypeImportedCertConst, true) + importedCert := &sm.ImportedCertificate{ + SecretType: utilpointer.String(sm.Secret_SecretType_ImportedCert), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Certificate: utilpointer.String(secretCertificate), + Intermediate: utilpointer.String("intermediate"), + PrivateKey: utilpointer.String("private_key"), + } + setSecretCert := funcSetCertSecretTest(importedCert, "good case: imported_cert type with property", sm.Secret_SecretType_ImportedCert, true) // bad case: imported_cert type without property - badSecretCert := funcSetCertSecretTest(sm.CreateSecretOptionsSecretTypeImportedCertConst, false) + badSecretCert := funcSetCertSecretTest(importedCert, "bad case: imported_cert type without property", sm.Secret_SecretType_ImportedCert, false) // good case: public_cert type with property - setSecretPublicCert := funcSetCertSecretTest(sm.CreateSecretOptionsSecretTypePublicCertConst, true) + publicCert := &sm.PublicCertificate{ + SecretType: utilpointer.String(sm.Secret_SecretType_PublicCert), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Certificate: utilpointer.String(secretCertificate), + Intermediate: utilpointer.String("intermediate"), + PrivateKey: utilpointer.String("private_key"), + } + setSecretPublicCert := funcSetCertSecretTest(publicCert, "good case: public_cert type with property", sm.Secret_SecretType_PublicCert, true) // bad case: public_cert type without property - badSecretPublicCert := funcSetCertSecretTest(sm.CreateSecretOptionsSecretTypePublicCertConst, false) + badSecretPublicCert := funcSetCertSecretTest(publicCert, "bad case: public_cert type without property", sm.Secret_SecretType_PublicCert, false) // good case: private_cert type with property - setSecretPrivateCert := funcSetCertSecretTest(sm.CreateSecretOptionsSecretTypePrivateCertConst, true) + privateCert := &sm.PrivateCertificate{ + SecretType: utilpointer.String(sm.Secret_SecretType_PublicCert), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Certificate: utilpointer.String(secretCertificate), + PrivateKey: utilpointer.String("private_key"), + } + setSecretPrivateCert := funcSetCertSecretTest(privateCert, "good case: private_cert type with property", sm.Secret_SecretType_PrivateCert, true) // bad case: private_cert type without property - badSecretPrivateCert := funcSetCertSecretTest(sm.CreateSecretOptionsSecretTypePrivateCertConst, false) + badSecretPrivateCert := funcSetCertSecretTest(privateCert, "bad case: private_cert type without property", sm.Secret_SecretType_PrivateCert, false) secretDataKV := make(map[string]interface{}) - secretKVPayload := make(map[string]interface{}) - secretKVPayload["key1"] = "val1" - secretDataKV["payload"] = secretKVPayload + secretDataKV["key1"] = "val1" secretDataKVComplex := make(map[string]interface{}) - secretKVComplex := `{"key1":"val1","key2":"val2","key3":"val3","keyC":{"keyC1":"valC1", "keyC2":"valC2"}, "special.log": "file-content"}` + secretKVComplex := `{"key1":"val1","key2":"val2","key3":"val3","keyC":{"keyC1":"valC1","keyC2":"valC2"},"special.log":"file-content"}` + json.Unmarshal([]byte(secretKVComplex), &secretDataKVComplex) - secretDataKVComplex["payload"] = secretKVComplex + secretKV := "kv/" + secretUUID - secretKV := "kv/test-secret" // bad case: kv type with key which is not in payload badSecretKV := func(smtc *secretManagerTestCase) { - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeKvConst), - Name: utilpointer.String("testyname"), - SecretData: secretDataKV, - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeKvConst) - smtc.apiOutput.Resources = resources + secret := &sm.KVSecret{ + SecretType: utilpointer.String(sm.Secret_SecretType_Kv), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Data: secretDataKV, + } + smtc.name = "bad case: kv type with key which is not in payload" + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret smtc.ref.Key = secretKV smtc.ref.Property = "other-key" - smtc.expectError = "key other-key does not exist in secret kv/test-secret" + smtc.expectError = "key other-key does not exist in secret kv/" + secretUUID } // good case: kv type with property setSecretKV := func(smtc *secretManagerTestCase) { - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeKvConst), - Name: utilpointer.String("testyname"), - SecretData: secretDataKV, - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeKvConst) - smtc.apiOutput.Resources = resources + secret := &sm.KVSecret{ + SecretType: utilpointer.String(sm.Secret_SecretType_Kv), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Data: secretDataKV, + } + smtc.name = "good case: kv type with property" + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret smtc.ref.Key = secretKV smtc.ref.Property = "key1" smtc.expectedSecret = "val1" @@ -337,15 +359,15 @@ func TestIBMSecretManagerGetSecret(t *testing.T) { // good case: kv type with property, returns specific value setSecretKVWithKey := func(smtc *secretManagerTestCase) { - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeKvConst), - Name: utilpointer.String("testyname"), - SecretData: secretDataKVComplex, - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeKvConst) - smtc.apiOutput.Resources = resources + secret := &sm.KVSecret{ + SecretType: utilpointer.String(sm.Secret_SecretType_Kv), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Data: secretDataKVComplex, + } + smtc.name = "good case: kv type with property, returns specific value" + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret smtc.ref.Key = secretKV smtc.ref.Property = "key2" smtc.expectedSecret = "val2" @@ -353,15 +375,15 @@ func TestIBMSecretManagerGetSecret(t *testing.T) { // good case: kv type with property and path, returns specific value setSecretKVWithKeyPath := func(smtc *secretManagerTestCase) { - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeKvConst), - Name: utilpointer.String("testyname"), - SecretData: secretDataKVComplex, - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeKvConst) - smtc.apiOutput.Resources = resources + secret := &sm.KVSecret{ + SecretType: utilpointer.String(sm.Secret_SecretType_Kv), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Data: secretDataKVComplex, + } + smtc.name = "good case: kv type with property and path, returns specific value" + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret smtc.ref.Key = secretKV smtc.ref.Property = "keyC.keyC2" smtc.expectedSecret = "valC2" @@ -369,15 +391,15 @@ func TestIBMSecretManagerGetSecret(t *testing.T) { // good case: kv type with property and dot, returns specific value setSecretKVWithKeyDot := func(smtc *secretManagerTestCase) { - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeKvConst), - Name: utilpointer.String("testyname"), - SecretData: secretDataKVComplex, - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeKvConst) - smtc.apiOutput.Resources = resources + secret := &sm.KVSecret{ + SecretType: utilpointer.String(sm.Secret_SecretType_Kv), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Data: secretDataKVComplex, + } + smtc.name = "good case: kv type with property and dot, returns specific value" + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret smtc.ref.Key = secretKV smtc.ref.Property = "special.log" smtc.expectedSecret = "file-content" @@ -385,21 +407,20 @@ func TestIBMSecretManagerGetSecret(t *testing.T) { // good case: kv type without property, returns all setSecretKVWithOutKey := func(smtc *secretManagerTestCase) { - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeKvConst), - Name: utilpointer.String("testyname"), - SecretData: secretDataKVComplex, - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeKvConst) - smtc.apiOutput.Resources = resources + secret := &sm.KVSecret{ + SecretType: utilpointer.String(sm.Secret_SecretType_Kv), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Data: secretDataKVComplex, + } + smtc.name = "good case: kv type without property, returns all" + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret smtc.ref.Key = secretKV smtc.expectedSecret = secretKVComplex } successCases := []*secretManagerTestCase{ - makeValidSecretManagerTestCase(), makeValidSecretManagerTestCaseCustom(setSecretString), makeValidSecretManagerTestCaseCustom(setCustomKey), makeValidSecretManagerTestCaseCustom(setAPIErr), @@ -408,13 +429,13 @@ func TestIBMSecretManagerGetSecret(t *testing.T) { makeValidSecretManagerTestCaseCustom(setSecretUserPass), makeValidSecretManagerTestCaseCustom(setSecretIam), makeValidSecretManagerTestCaseCustom(setSecretCert), - makeValidSecretManagerTestCaseCustom(badSecretCert), makeValidSecretManagerTestCaseCustom(setSecretKV), makeValidSecretManagerTestCaseCustom(setSecretKVWithKey), makeValidSecretManagerTestCaseCustom(setSecretKVWithKeyPath), makeValidSecretManagerTestCaseCustom(setSecretKVWithKeyDot), makeValidSecretManagerTestCaseCustom(setSecretKVWithOutKey), makeValidSecretManagerTestCaseCustom(badSecretKV), + makeValidSecretManagerTestCaseCustom(badSecretCert), makeValidSecretManagerTestCaseCustom(setSecretPublicCert), makeValidSecretManagerTestCaseCustom(badSecretPublicCert), makeValidSecretManagerTestCaseCustom(setSecretPrivateCert), @@ -423,19 +444,20 @@ func TestIBMSecretManagerGetSecret(t *testing.T) { sm := providerIBM{} for k, v := range successCases { - sm.IBMClient = v.mockClient - out, err := sm.GetSecret(context.Background(), *v.ref) - if !ErrorContains(err, v.expectError) { - t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError) - } - if string(out) != v.expectedSecret { - t.Errorf("[%d] unexpected secret: expected %s, got %s", k, v.expectedSecret, string(out)) - } + t.Run(v.name, func(t *testing.T) { + sm.IBMClient = v.mockClient + out, err := sm.GetSecret(context.Background(), *v.ref) + if !ErrorContains(err, v.expectError) { + t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError) + } + if string(out) != v.expectedSecret { + t.Errorf("[%d] unexpected secret: expected %s, got %s", k, v.expectedSecret, string(out)) + } + }) } } func TestGetSecretMap(t *testing.T) { - secretKeyName := "kv/test-secret" secretUsername := "user1" secretPassword := "P@ssw0rd" secretAPIKey := "01234567890" @@ -454,89 +476,60 @@ func TestGetSecretMap(t *testing.T) { }, } - // good case: default version & deserialization - setDeserialization := func(smtc *secretManagerTestCase) { - secretData := make(map[string]interface{}) - secretData["payload"] = `{"foo":"bar"}` - - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String("testytype"), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }} - - smtc.apiOutput.Resources = resources - smtc.expectedData["foo"] = []byte("bar") - } - - // bad case: invalid json - setInvalidJSON := func(smtc *secretManagerTestCase) { - secretData := make(map[string]interface{}) - secretData["payload"] = `-----------------` - - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String("testytype"), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }} - - smtc.apiOutput.Resources = resources - smtc.expectError = "unable to unmarshal secret: invalid character '-' in numeric literal" + // good case: arbitrary + setArbitrary := func(smtc *secretManagerTestCase) { + payload := `{"foo":"bar"}` + secret := &sm.ArbitrarySecret{ + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + SecretType: utilpointer.String(sm.Secret_SecretType_Arbitrary), + Payload: &payload, + } + smtc.name = "good case: arbitrary" + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret + smtc.ref.Key = secretUUID + smtc.expectedData["arbitrary"] = []byte(payload) } // good case: username_password setSecretUserPass := func(smtc *secretManagerTestCase) { - secretData := make(map[string]interface{}) - secretData["username"] = secretUsername - secretData["password"] = secretPassword - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst) - smtc.apiOutput.Resources = resources - smtc.ref.Key = "username_password/test-secret" + secret := &sm.UsernamePasswordSecret{ + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + SecretType: utilpointer.String(sm.Secret_SecretType_UsernamePassword), + Username: &secretUsername, + Password: &secretPassword, + } + smtc.name = "good case: username_password" + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret + smtc.ref.Key = "username_password/" + secretUUID smtc.expectedData["username"] = []byte(secretUsername) smtc.expectedData["password"] = []byte(secretPassword) } // good case: iam_credentials setSecretIam := func(smtc *secretManagerTestCase) { - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeIamCredentialsConst), - Name: utilpointer.String("testyname"), - APIKey: utilpointer.String(secretAPIKey), - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst) - smtc.apiOutput.Resources = resources - smtc.ref.Key = "iam_credentials/test-secret" + secret := &sm.IAMCredentialsSecret{ + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + SecretType: utilpointer.String(sm.Secret_SecretType_IamCredentials), + ApiKey: utilpointer.String(secretAPIKey), + } + smtc.name = "good case: iam_credentials" + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret + smtc.ref.Key = "iam_credentials/" + secretUUID smtc.expectedData["apikey"] = []byte(secretAPIKey) } - funcCertTest := func(certType string) func(*secretManagerTestCase) { + funcCertTest := func(secret sm.SecretIntf, name, certType string) func(*secretManagerTestCase) { return func(smtc *secretManagerTestCase) { - secretData := make(map[string]interface{}) - secretData["certificate"] = secretCertificate - secretData["private_key"] = secretPrivateKey - secretData["intermediate"] = secretIntermediate - - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(certType), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }} - - smtc.apiInput.SecretType = core.StringPtr(certType) - smtc.apiOutput.Resources = resources - smtc.ref.Key = certType + "/test-secret" + smtc.name = name + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret + smtc.ref.Key = certType + "/" + secretUUID smtc.expectedData["certificate"] = []byte(secretCertificate) smtc.expectedData["private_key"] = []byte(secretPrivateKey) smtc.expectedData["intermediate"] = []byte(secretIntermediate) @@ -544,27 +537,57 @@ func TestGetSecretMap(t *testing.T) { } // good case: imported_cert - setSecretCert := funcCertTest(sm.CreateSecretOptionsSecretTypeImportedCertConst) - // good case: public_cert - setSecretPublicCert := funcCertTest(sm.CreateSecretOptionsSecretTypePublicCertConst) - // good case: public_cert - setSecretPrivateCert := funcCertTest(sm.CreateSecretOptionsSecretTypePrivateCertConst) + importedCert := &sm.ImportedCertificate{ + SecretType: utilpointer.String(sm.Secret_SecretType_ImportedCert), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Certificate: utilpointer.String(secretCertificate), + Intermediate: utilpointer.String(secretIntermediate), + PrivateKey: utilpointer.String(secretPrivateKey), + } + setSecretCert := funcCertTest(importedCert, "good case: imported_cert", sm.Secret_SecretType_ImportedCert) + // good case: public_cert + publicCert := &sm.PublicCertificate{ + SecretType: utilpointer.String(sm.Secret_SecretType_PublicCert), + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + Certificate: utilpointer.String(secretCertificate), + Intermediate: utilpointer.String(secretIntermediate), + PrivateKey: utilpointer.String(secretPrivateKey), + } + setSecretPublicCert := funcCertTest(publicCert, "good case: public_cert", sm.Secret_SecretType_PublicCert) + + // good case: private_cert + setSecretPrivateCert := func(smtc *secretManagerTestCase) { + secret := &sm.PrivateCertificate{ + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + SecretType: utilpointer.String(sm.Secret_SecretType_PrivateCert), + Certificate: &secretCertificate, + PrivateKey: &secretPrivateKey, + } + smtc.name = "good case: private_cert" + smtc.apiInput.ID = utilpointer.String(secretUUID) + smtc.apiOutput = secret + smtc.ref.Key = "private_cert/" + secretUUID + smtc.expectedData["certificate"] = []byte(secretCertificate) + smtc.expectedData["private_key"] = []byte(secretPrivateKey) + } + + secretKeyKV := "kv/" + secretUUID // good case: kv, no property, return entire payload as key:value pairs setSecretKV := func(smtc *secretManagerTestCase) { - secretData := make(map[string]interface{}) - secretData["payload"] = secretComplex - - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeKvConst), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeKvConst) - smtc.apiOutput.Resources = resources - smtc.ref.Key = secretKeyName + secret := &sm.KVSecret{ + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + SecretType: utilpointer.String(sm.Secret_SecretType_Kv), + Data: secretComplex, + } + smtc.name = "good case: kv, no property, return entire payload as key:value pairs" + smtc.apiInput.ID = core.StringPtr(secretUUID) + smtc.apiOutput = secret + smtc.ref.Key = secretKeyKV smtc.expectedData["key1"] = []byte("val1") smtc.expectedData["key2"] = []byte("val2") smtc.expectedData["keyC"] = []byte(`{"keyC1":{"keyA":"valA","keyB":"valB"}}`) @@ -572,65 +595,55 @@ func TestGetSecretMap(t *testing.T) { // good case: kv, with property setSecretKVWithProperty := func(smtc *secretManagerTestCase) { - secretData := make(map[string]interface{}) - secretData["payload"] = secretComplex - - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeKvConst), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeKvConst) + secret := &sm.KVSecret{ + Name: utilpointer.String("d5deb37a-7883-4fe2-a5e7-3c15420adc76"), + ID: utilpointer.String(secretUUID), + SecretType: utilpointer.String(sm.Secret_SecretType_Kv), + Data: secretComplex, + } + smtc.name = "good case: kv, with property" + smtc.apiInput.ID = core.StringPtr(secretUUID) smtc.ref.Property = "keyC" - smtc.apiOutput.Resources = resources - smtc.ref.Key = secretKeyName + smtc.apiOutput = secret + smtc.ref.Key = secretKeyKV smtc.expectedData["keyC1"] = []byte(`{"keyA":"valA","keyB":"valB"}`) } // good case: kv, with property and path setSecretKVWithPathAndProperty := func(smtc *secretManagerTestCase) { - secretData := make(map[string]interface{}) - secretData["payload"] = secretComplex - - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeKvConst), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeKvConst) + secret := &sm.KVSecret{ + Name: utilpointer.String(secretUUID), + ID: utilpointer.String(secretUUID), + SecretType: utilpointer.String(sm.Secret_SecretType_Kv), + Data: secretComplex, + } + smtc.name = "good case: kv, with property and path" + smtc.apiInput.ID = core.StringPtr(secretUUID) smtc.ref.Property = "keyC.keyC1" - smtc.apiOutput.Resources = resources - smtc.ref.Key = secretKeyName + smtc.apiOutput = secret + smtc.ref.Key = secretKeyKV smtc.expectedData["keyA"] = []byte("valA") smtc.expectedData["keyB"] = []byte("valB") } // bad case: kv, with property and path badSecretKVWithUnknownProperty := func(smtc *secretManagerTestCase) { - secretData := make(map[string]interface{}) - secretData["payload"] = secretComplex - - resources := []sm.SecretResourceIntf{ - &sm.SecretResource{ - SecretType: utilpointer.String(sm.CreateSecretOptionsSecretTypeKvConst), - Name: utilpointer.String("testyname"), - SecretData: secretData, - }} - - smtc.apiInput.SecretType = core.StringPtr(sm.CreateSecretOptionsSecretTypeKvConst) + secret := &sm.KVSecret{ + Name: utilpointer.String("testyname"), + ID: utilpointer.String(secretUUID), + SecretType: utilpointer.String(sm.Secret_SecretType_Kv), + Data: secretComplex, + } + smtc.name = "bad case: kv, with property and path" + smtc.apiInput.ID = core.StringPtr(secretUUID) smtc.ref.Property = "unknown.property" - smtc.apiOutput.Resources = resources - smtc.ref.Key = secretKeyName - smtc.expectError = "key unknown.property does not exist in secret kv/test-secret" + smtc.apiOutput = secret + smtc.ref.Key = secretKeyKV + smtc.expectError = "key unknown.property does not exist in secret " + secretKeyKV } successCases := []*secretManagerTestCase{ - makeValidSecretManagerTestCaseCustom(setDeserialization), - makeValidSecretManagerTestCaseCustom(setInvalidJSON), + makeValidSecretManagerTestCaseCustom(setArbitrary), makeValidSecretManagerTestCaseCustom(setNilMockClient), makeValidSecretManagerTestCaseCustom(setAPIErr), makeValidSecretManagerTestCaseCustom(setSecretUserPass), @@ -646,14 +659,16 @@ func TestGetSecretMap(t *testing.T) { sm := providerIBM{} for k, v := range successCases { - sm.IBMClient = v.mockClient - out, err := sm.GetSecretMap(context.Background(), *v.ref) - if !ErrorContains(err, v.expectError) { - t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError) - } - if err == nil && !reflect.DeepEqual(out, v.expectedData) { - t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", k, v.expectedData, out) - } + t.Run(v.name, func(t *testing.T) { + sm.IBMClient = v.mockClient + out, err := sm.GetSecretMap(context.Background(), *v.ref) + if !ErrorContains(err, v.expectError) { + t.Errorf(" unexpected error: %s, expected: '%s'", err.Error(), v.expectError) + } + if err == nil && !reflect.DeepEqual(out, v.expectedData) { + t.Errorf("[%d] unexpected secret data: expected %+v, got %v", k, v.expectedData, out) + } + }) } }