Browse Source

feat: add yaml based encoding for get secrets as map (#4001)

Gergely Brautigam 1 year ago
parent
commit
dc6be312cf
2 changed files with 120 additions and 2 deletions
  1. 34 2
      pkg/provider/bitwarden/client.go
  2. 86 0
      pkg/provider/bitwarden/client_test.go

+ 34 - 2
pkg/provider/bitwarden/client.go

@@ -15,11 +15,13 @@ limitations under the License.
 package bitwarden
 package bitwarden
 
 
 import (
 import (
+	"bytes"
 	"context"
 	"context"
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 
 
+	"gopkg.in/yaml.v3"
 	corev1 "k8s.io/api/core/v1"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/kube-openapi/pkg/validation/strfmt"
 	"k8s.io/kube-openapi/pkg/validation/strfmt"
 
 
@@ -238,9 +240,12 @@ func (p *Provider) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecre
 		return nil, err
 		return nil, err
 	}
 	}
 
 
+	if err := yaml.Unmarshal(data, map[string]any{}); err == nil {
+		return p.parseYamlSecretData(data)
+	}
+
 	kv := make(map[string]json.RawMessage)
 	kv := make(map[string]json.RawMessage)
-	err = json.Unmarshal(data, &kv)
-	if err != nil {
+	if err := json.Unmarshal(data, &kv); err != nil {
 		return nil, fmt.Errorf("error unmarshalling secret: %w", err)
 		return nil, fmt.Errorf("error unmarshalling secret: %w", err)
 	}
 	}
 
 
@@ -258,6 +263,33 @@ func (p *Provider) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecre
 	return secretData, nil
 	return secretData, nil
 }
 }
 
 
+func (p *Provider) parseYamlSecretData(data []byte) (map[string][]byte, error) {
+	kv := make(map[string]any)
+	if err := yaml.Unmarshal(data, &kv); err != nil {
+		return nil, fmt.Errorf("error unmarshalling secret: %w", err)
+	}
+
+	secretData := make(map[string][]byte)
+	for k, v := range kv {
+		switch t := v.(type) {
+		case string:
+			secretData[k] = []byte(t)
+		case []byte:
+			secretData[k] = t
+		case map[string]any:
+			d, err := yaml.Marshal(t)
+			if err != nil {
+				return nil, fmt.Errorf("error marshaling secret: %w", err)
+			}
+			secretData[k] = bytes.TrimSpace(d)
+		default:
+			secretData[k] = []byte(fmt.Sprintf("%v", t)) // Convert to string and then []byte
+		}
+	}
+
+	return secretData, nil
+}
+
 // GetAllSecrets gets multiple secrets from the provider and loads into a kubernetes secret.
 // GetAllSecrets gets multiple secrets from the provider and loads into a kubernetes secret.
 // First load all secrets from secretStore path configuration
 // First load all secrets from secretStore path configuration
 // Then, gets secrets from a matching name or matching custom_metadata.
 // Then, gets secrets from a matching name or matching custom_metadata.

+ 86 - 0
pkg/provider/bitwarden/client_test.go

@@ -935,6 +935,92 @@ func TestProviderGetSecretMap(t *testing.T) {
 			want: []byte("value"),
 			want: []byte("value"),
 		},
 		},
 		{
 		{
+			name: "get secret map with yaml",
+			fields: fields{
+				kube: func() client.Client {
+					return fake.NewFakeClient()
+				},
+				namespace: "default",
+				store:     &v1beta1.SecretStore{},
+				mock: func(c *FakeClient) {
+					c.GetSecretReturnsOnCallN(0, &SecretResponse{
+						ID:             "d8f29773-3019-4973-9bbc-66327d077fe2",
+						Key:            "key",
+						Note:           "note",
+						OrganizationID: "org",
+						Value:          `key: value`,
+					})
+				},
+			},
+			args: args{
+				ctx: context.Background(),
+				ref: v1beta1.ExternalSecretDataRemoteRef{
+					Key:      "d8f29773-3019-4973-9bbc-66327d077fe2",
+					Property: "key",
+				},
+				key: "key",
+			},
+			want: []byte("value"),
+		},
+		{
+			name: "get secret map with nested yaml",
+			fields: fields{
+				kube: func() client.Client {
+					return fake.NewFakeClient()
+				},
+				namespace: "default",
+				store:     &v1beta1.SecretStore{},
+				mock: func(c *FakeClient) {
+					c.GetSecretReturnsOnCallN(0, &SecretResponse{
+						ID:             "d8f29773-3019-4973-9bbc-66327d077fe2",
+						Key:            "key",
+						Note:           "note",
+						OrganizationID: "org",
+						Value: `key:
+  key2: value`,
+					})
+				},
+			},
+			args: args{
+				ctx: context.Background(),
+				ref: v1beta1.ExternalSecretDataRemoteRef{
+					Key:      "d8f29773-3019-4973-9bbc-66327d077fe2",
+					Property: "key",
+				},
+				key: "key",
+			},
+			want: []byte("key2: value"),
+		},
+		{
+			name: "get secret map with binary yaml data",
+			fields: fields{
+				kube: func() client.Client {
+					return fake.NewFakeClient()
+				},
+				namespace: "default",
+				store:     &v1beta1.SecretStore{},
+				mock: func(c *FakeClient) {
+					c.GetSecretReturnsOnCallN(0, &SecretResponse{
+						ID:             "d8f29773-3019-4973-9bbc-66327d077fe2",
+						Key:            "key",
+						Note:           "note",
+						OrganizationID: "org",
+						Value: `key: value
+key2: !!binary VGhpcyBpcyBhIHRlc3Q=`,
+					})
+				},
+			},
+			args: args{
+				ctx: context.Background(),
+				ref: v1beta1.ExternalSecretDataRemoteRef{
+					Key:      "d8f29773-3019-4973-9bbc-66327d077fe2",
+					Property: "key2",
+				},
+				key: "key2",
+			},
+			want: []byte(`This is a test`),
+		},
+		{
 			name: "get secret map - missing key",
 			name: "get secret map - missing key",
 			fields: fields{
 			fields: fields{
 				kube: func() client.Client {
 				kube: func() client.Client {