diff --git a/pkg/generator/secret_template_reconciler.go b/pkg/generator/secret_template_reconciler.go index 6e96f02c7..dddb4d4ed 100644 --- a/pkg/generator/secret_template_reconciler.go +++ b/pkg/generator/secret_template_reconciler.go @@ -6,6 +6,7 @@ package generator import ( "context" "encoding/base64" + "encoding/json" "fmt" "time" @@ -283,7 +284,12 @@ func evaluateTemplate(template *sg2v1alpha1.JSONPathTemplate, values map[string] } // Template Secret StringData - stringData, err := evaluate(template.StringData, values) + decodedValues, err := decodeSecrets(values) + if err != nil { + return corev1.Secret{}, fmt.Errorf("decoding secrets: %w", err) + } + + stringData, err := evaluate(template.StringData, decodedValues) if err != nil { return corev1.Secret{}, fmt.Errorf("templating stringData: %w", err) } @@ -349,3 +355,34 @@ func evaluateBytes(mapping map[string]string, values map[string]interface{}) (ma return evaluatedMapping, nil } + +func decodeSecrets(values map[string]interface{}) (map[string]interface{}, error) { + decodedValues := make(map[string]interface{}) + for valueKey, value := range values { + jsonData, err := json.Marshal(value) + if err != nil { + return nil, fmt.Errorf("failed to marshal values into JSON: %w", err) + } + + obj := &unstructured.Unstructured{} + if err := json.Unmarshal(jsonData, obj); err != nil { + return nil, fmt.Errorf("failed to unmarshal JSON into Unstructured object: %w", err) + } + + if obj.GetKind() == "Secret" { + data, _, _ := unstructured.NestedStringMap(obj.Object, "data") + for dataKey, encodedValue := range data { + decodedValue, err := base64.StdEncoding.DecodeString(string(encodedValue)) + if err != nil { + return nil, fmt.Errorf("failed decoding base64 from a Secret: %w", err) + } + unstructured.SetNestedStringMap(obj.Object, map[string]string{dataKey: string(decodedValue)}, "data") + } + decodedValues[valueKey] = obj.Object + } else { + decodedValues[valueKey] = value + } + } + + return decodedValues, nil +} diff --git a/pkg/generator/secret_template_test.go b/pkg/generator/secret_template_test.go index a24eed5ff..52f7458c8 100644 --- a/pkg/generator/secret_template_test.go +++ b/pkg/generator/secret_template_test.go @@ -89,6 +89,62 @@ func Test_SecretTemplate(t *testing.T) { }, }, }, + { + name: "reconciling secret template with input from another secret decoded in stringData", + template: sg2v1alpha1.SecretTemplate{ + ObjectMeta: metav1.ObjectMeta{ + Name: "secretTemplate", + Namespace: "test", + }, + Spec: sg2v1alpha1.SecretTemplateSpec{ + InputResources: []sg2v1alpha1.InputResource{{ + Name: "creds", + Ref: sg2v1alpha1.InputResourceRef{ + APIVersion: "v1", + Kind: "Secret", + Name: "existingSecret", + }, + }}, + JSONPathTemplate: &sg2v1alpha1.JSONPathTemplate{ + Data: map[string]string{ + "key1": "$( .creds.data.inputKey1 )", + "key2": "$( .creds.data.inputKey2 )", + }, + StringData: map[string]string{ + "key3": "test-$( .creds.data.inputKey3 )", + }, + }, + }, + }, + existingObjects: []client.Object{ + secret("existingSecret", map[string]string{ + "inputKey1": "value1", + "inputKey2": "value2", + "inputKey3": "value3", + }), + }, + expectedSecret: corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "secretTemplate", + Namespace: "test", + ResourceVersion: "1", + OwnerReferences: []metav1.OwnerReference{ + secretTemplateOwnerRef("secretTemplate"), + }, + }, + Data: map[string][]byte{ + "key1": []byte("value1"), + "key2": []byte("value2"), + }, + StringData: map[string]string{ + "key3": "test-value3", + }, + }, + }, { name: "reconciling secret template with input from two inputs with dynamic inputname", template: sg2v1alpha1.SecretTemplate{