Browse Source

Add support for IBM Cloud Service Credentials secret type (#2950)

Nitzan Nissim 2 years ago
parent
commit
b0bdef20b5

+ 5 - 0
docs/provider/ibm-secrets-manager.md

@@ -108,6 +108,7 @@ We support the following secret types of [IBM Secrets Manager](https://cloud.ibm
 * `arbitrary`
 * `username_password`
 * `iam_credentials`
+* `service_credentials`
 * `imported_cert`
 * `public_cert`
 * `private_cert`
@@ -135,6 +136,10 @@ The behavior for the different secret types is as following:
 * `remoteRef` retrieves an apikey from secrets manager and sets it for specified `secretKey`
 * `dataFrom` retrieves an apikey from secrets manager and sets it for the `apikey` Kubernetes secret key
 
+#### service_credentials
+* `remoteRef` retrieves the credentials object from secrets manager and sets it for specified `secretKey`
+* `dataFrom` retrieves the credential object as a map from secrets manager and sets appropriate key:value pairs in the resulting Kubernetes secret
+
 #### imported_cert, public_cert, and private_cert
 * `remoteRef` requires a `property` to be set for either `certificate`, `private_key` or `intermediate` to retrieve respective fields from the secrets manager secret and set in specified `secretKey`
 * `dataFrom` retrieves all `certificate`, `private_key` and `intermediate` fields from the secrets manager secret and sets appropriate key:value pairs in the resulting Kubernetes secret

+ 3 - 0
docs/snippets/ibm-es-types.yaml

@@ -16,6 +16,9 @@ spec:
   - secretKey: iam_cred
     remoteRef:
       key: iam_credentials/zzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz
+  - secretKey: srv_cred
+    remoteRef:
+      key: service_credentials/zzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz
   - secretKey: imp_cert
     remoteRef:
       key: imported_cert/zzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz

+ 7 - 0
pkg/provider/ibm/helper.go

@@ -41,6 +41,13 @@ func extractSecretMetadata(response sm.SecretMetadataIntf, givenName *string, se
 		}
 		return metadata.ID, metadata.Name, nil
 
+	case sm.Secret_SecretType_ServiceCredentials:
+		metadata, ok := response.(*sm.ServiceCredentialsSecretMetadata)
+		if !ok {
+			return nil, nil, fmt.Errorf(errExtractingSecret, *givenName, sm.Secret_SecretType_ServiceCredentials, "extractSecretMetadata")
+		}
+		return metadata.ID, metadata.Name, nil
+
 	case sm.Secret_SecretType_ImportedCert:
 		metadata, ok := response.(*sm.ImportedCertificateMetadata)
 		if !ok {

+ 32 - 0
pkg/provider/ibm/provider.go

@@ -46,6 +46,7 @@ const (
 	usernameConst     = "username"
 	passwordConst     = "password"
 	apikeyConst       = "apikey"
+	credentialsConst  = "credentials"
 	arbitraryConst    = "arbitrary"
 	payloadConst      = "payload"
 	smAPIKeyConst     = "api_key"
@@ -172,6 +173,10 @@ func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecre
 
 		return getIamCredentialsSecret(ibm, &secretName, secretGroupName)
 
+	case sm.Secret_SecretType_ServiceCredentials:
+
+		return getServiceCredentialsSecret(ibm, &secretName, secretGroupName)
+
 	case sm.Secret_SecretType_ImportedCert:
 
 		if ref.Property == "" {
@@ -294,6 +299,26 @@ func getIamCredentialsSecret(ibm *providerIBM, secretName *string, secretGroupNa
 	return nil, fmt.Errorf("key %s does not exist in secret %s", smAPIKeyConst, *secretName)
 }
 
+func getServiceCredentialsSecret(ibm *providerIBM, secretName *string, secretGroupName string) ([]byte, error) {
+	response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_ServiceCredentials, secretGroupName)
+	if err != nil {
+		return nil, err
+	}
+	secMap, err := formSecretMap(response)
+	if err != nil {
+		return nil, err
+	}
+	if val, ok := secMap[credentialsConst]; ok {
+		var mval []byte
+		mval, err = json.Marshal(val)
+		if err != nil {
+			return nil, err
+		}
+		return mval, nil
+	}
+	return nil, fmt.Errorf("key %s does not exist in secret %s", credentialsConst, *secretName)
+}
+
 func getUsernamePasswordSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
 	response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_UsernamePassword, secretGroupName)
 	if err != nil {
@@ -508,6 +533,13 @@ func (ibm *providerIBM) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSe
 		secretMap[apikeyConst] = secMapBytes[smAPIKeyConst]
 		return secretMap, nil
 
+	case sm.Secret_SecretType_ServiceCredentials:
+		if err := checkNilFn([]string{credentialsConst}); err != nil {
+			return nil, err
+		}
+		secretMap[credentialsConst] = secMapBytes[credentialsConst]
+		return secretMap, nil
+
 	case sm.Secret_SecretType_ImportedCert:
 		if err := checkNilFn([]string{certificateConst, intermediateConst}); err != nil {
 			return nil, err

+ 54 - 3
pkg/provider/ibm/provider_test.go

@@ -278,12 +278,12 @@ func TestIBMSecretManagerGetSecret(t *testing.T) {
 
 	// bad case: arbitrary type secret which is destroyed
 	badArbitSecret := func(smtc *secretManagerTestCase) {
-		secret := &sm.UsernamePasswordSecret{
-			SecretType: utilpointer.To(sm.Secret_SecretType_UsernamePassword),
+		secret := &sm.ArbitrarySecret{
+			SecretType: utilpointer.To(sm.Secret_SecretType_Arbitrary),
 			Name:       utilpointer.To("testyname"),
 			ID:         utilpointer.To(secretUUID),
 		}
-		smtc.name = "bad case: username_password type without property"
+		smtc.name = "bad case: arbitrary type without property"
 		smtc.apiInput.ID = utilpointer.To(secretUUID)
 		smtc.apiOutput = secret
 		smtc.ref.Key = secretUUID
@@ -387,6 +387,36 @@ func TestIBMSecretManagerGetSecret(t *testing.T) {
 	}
 	setSecretIamByNameNew := funcSetSecretIamNew("testyname", "testGroup", "good case: iam_credenatials type - get API Key by name - new mechanism")
 
+	// good case: service_credentials type
+	dummySrvCreds := &sm.ServiceCredentialsSecretCredentials{
+		Apikey: &secretAPIKey,
+	}
+
+	funcSetSecretSrvCred := func(secretName, name string) func(*secretManagerTestCase) {
+		return func(smtc *secretManagerTestCase) {
+			secret := &sm.ServiceCredentialsSecret{
+				SecretType:  utilpointer.To(sm.Secret_SecretType_ServiceCredentials),
+				Name:        utilpointer.To("testyname"),
+				ID:          utilpointer.To(secretUUID),
+				Credentials: dummySrvCreds,
+			}
+			secretMetadata := &sm.ServiceCredentialsSecretMetadata{
+				Name: utilpointer.To("testyname"),
+				ID:   utilpointer.To(secretUUID),
+			}
+			smtc.apiInput.ID = utilpointer.To(secretUUID)
+			smtc.name = name
+			smtc.apiOutput = secret
+			smtc.listInput.Search = utilpointer.To("testyname")
+			smtc.listOutput.Secrets = make([]sm.SecretMetadataIntf, 1)
+			smtc.listOutput.Secrets[0] = secretMetadata
+			smtc.ref.Key = "service_credentials/" + secretName
+			smtc.expectedSecret = "{\"apikey\":\"01234567890\"}"
+		}
+	}
+
+	setSecretSrvCredByID := funcSetSecretSrvCred(secretUUID, "good case: service_credentials type - get creds by ID")
+
 	funcSetCertSecretTest := func(secret sm.SecretIntf, name, certType string, good bool) func(*secretManagerTestCase) {
 		return func(smtc *secretManagerTestCase) {
 			smtc.name = name
@@ -589,6 +619,7 @@ func TestIBMSecretManagerGetSecret(t *testing.T) {
 		makeValidSecretManagerTestCaseCustom(setSecretPrivateCert),
 		makeValidSecretManagerTestCaseCustom(badSecretPrivateCert),
 		makeValidSecretManagerTestCaseCustom(setSecretIamByNameNew),
+		makeValidSecretManagerTestCaseCustom(setSecretSrvCredByID),
 	}
 
 	sm := providerIBM{}
@@ -628,6 +659,10 @@ func TestGetSecretMap(t *testing.T) {
 		},
 	}
 
+	dummySrvCreds := &sm.ServiceCredentialsSecretCredentials{
+		Apikey: &secretAPIKey,
+	}
+
 	// good case: arbitrary
 	setArbitrary := func(smtc *secretManagerTestCase) {
 		payload := `{"foo":"bar"}`
@@ -721,6 +756,21 @@ func TestGetSecretMap(t *testing.T) {
 		}
 	}
 
+	//good case: service_credentials
+	setSecretSrvCreds := func(smtc *secretManagerTestCase) {
+		secret := &sm.ServiceCredentialsSecret{
+			Name:        utilpointer.To("testyname"),
+			ID:          utilpointer.To(secretUUID),
+			SecretType:  utilpointer.To(sm.Secret_SecretType_IamCredentials),
+			Credentials: dummySrvCreds,
+		}
+		smtc.name = "good case: service_credentials"
+		smtc.apiInput.ID = utilpointer.To(secretUUID)
+		smtc.apiOutput = secret
+		smtc.ref.Key = "service_credentials/" + secretUUID
+		smtc.expectedData["credentials"] = []byte(fmt.Sprintf("%+v", map[string]string{"apikey": secretAPIKey}))
+	}
+
 	// good case: imported_cert
 	importedCert := &sm.ImportedCertificate{
 		SecretType:   utilpointer.To(sm.Secret_SecretType_ImportedCert),
@@ -1131,6 +1181,7 @@ func TestGetSecretMap(t *testing.T) {
 
 	successCases := []*secretManagerTestCase{
 		makeValidSecretManagerTestCaseCustom(badSecretIam),
+		makeValidSecretManagerTestCaseCustom(setSecretSrvCreds),
 		makeValidSecretManagerTestCaseCustom(setArbitrary),
 		makeValidSecretManagerTestCaseCustom(setNilMockClient),
 		makeValidSecretManagerTestCaseCustom(setAPIErr),