Sfoglia il codice sorgente

ref(beyondtrust): BIPS-32651 add API v3.2 support for create Secrets (#6309)

* feat: BIPS-32651 add API v3.2 support for create Secrets

Signed-off-by: Felipe Hernandez <fhernandez@beyondtrust.com>

* fix: update .mod and .sum files

Signed-off-by: Felipe Hernandez <fhernandez@beyondtrust.com>

* test(beyondtrust): cover v3.2 push paths and owner-field propagation

Signed-off-by: Felipe Hernandez <fhernandez@beyondtrust.com>

* test(beyondtrust): cover v3.2 push paths and owner-field propagation

Signed-off-by: Felipe Hernandez <fhernandez@beyondtrust.com>

* fix: solve linting issues on BeyondTrust provider folder

Signed-off-by: Felipe Hernandez <fhernandez@beyondtrust.com>

* make check-diff

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

---------

Signed-off-by: Felipe Hernandez <fhernandez@beyondtrust.com>
Signed-off-by: Gergely Bräutigam <gergely.brautigam@sap.com>
Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
Co-authored-by: Gergely Bräutigam <gergely.brautigam@sap.com>
Co-authored-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
btfhernandez 3 settimane fa
parent
commit
7f7726b852

+ 4 - 4
go.mod

@@ -96,7 +96,7 @@ require (
 	github.com/yandex-cloud/go-genproto v0.34.0 // indirect
 	github.com/yandex-cloud/go-sdk v0.27.0 // indirect
 	github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
-	go.uber.org/zap v1.27.0
+	go.uber.org/zap v1.28.0
 	golang.org/x/crypto v0.51.0 // indirect
 	golang.org/x/oauth2 v0.36.0 // indirect
 	google.golang.org/api v0.254.0 // indirect
@@ -191,7 +191,7 @@ require (
 	github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect
 	github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
-	github.com/BeyondTrust/go-client-library-passwordsafe v1.0.0 // indirect
+	github.com/BeyondTrust/go-client-library-passwordsafe v1.3.0 // indirect
 	github.com/BurntSushi/toml v1.5.0 // indirect
 	github.com/DelineaXPM/dsv-sdk-go/v2 v2.2.0 // indirect
 	github.com/DelineaXPM/tss-sdk-go/v3 v3.0.2 // indirect
@@ -251,7 +251,7 @@ require (
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/fortanix/sdkms-client-go v0.4.1 // indirect
 	github.com/fxamacker/cbor/v2 v2.9.0 // indirect
-	github.com/gabriel-vasile/mimetype v1.4.11 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.13 // indirect
 	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
 	github.com/go-git/go-billy/v5 v5.9.0 // indirect
 	github.com/go-git/go-git/v5 v5.19.0 // indirect
@@ -274,7 +274,7 @@ require (
 	github.com/go-openapi/swag/typeutils v0.25.5 // indirect
 	github.com/go-openapi/swag/yamlutils v0.25.5 // indirect
 	github.com/go-openapi/validate v0.25.0 // indirect
-	github.com/go-playground/validator/v10 v10.28.0 // indirect
+	github.com/go-playground/validator/v10 v10.30.2 // indirect
 	github.com/go-resty/resty/v2 v2.16.5 // indirect
 	github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
 	github.com/go-viper/mapstructure/v2 v2.4.0 // indirect

+ 8 - 8
go.sum

@@ -110,8 +110,8 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ
 github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
 github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 h1:XkkQbfMyuH2jTSjQjSoihryI8GINRcs4xp8lNawg0FI=
 github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
-github.com/BeyondTrust/go-client-library-passwordsafe v1.0.0 h1:khr3BeVQscPf5MBhVaNPhobcdmo7pdQAzhmwQPeXNQ0=
-github.com/BeyondTrust/go-client-library-passwordsafe v1.0.0/go.mod h1:ntgg5j8QRG0XyF8WUTa57T1TwYJOJjerLMCc1XvJO0M=
+github.com/BeyondTrust/go-client-library-passwordsafe v1.3.0 h1:prfGhBOM9fZRhAOlRWSaRWv6N02B5xlwgbKxldXVv+U=
+github.com/BeyondTrust/go-client-library-passwordsafe v1.3.0/go.mod h1:TyIl8xhIygOjeIwxLlxtmEvirf9puiedmMN45DvxVP0=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
 github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
@@ -382,8 +382,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
 github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
 github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
 github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
-github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
-github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
+github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
+github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
 github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
@@ -481,8 +481,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
-github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
+github.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ=
+github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc=
 github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
 github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
 github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
@@ -1164,8 +1164,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
 go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
 go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
 go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
+go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q=
 go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
 go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
 go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=

+ 10 - 10
providers/v1/beyondtrust/go.mod

@@ -3,12 +3,12 @@ module github.com/external-secrets/external-secrets/providers/v1/beyondtrust
 go 1.26.3
 
 require (
-	github.com/BeyondTrust/go-client-library-passwordsafe v1.0.0
+	github.com/BeyondTrust/go-client-library-passwordsafe v1.3.0
 	github.com/cenkalti/backoff/v4 v4.3.0
 	github.com/external-secrets/external-secrets/apis v0.0.0
 	github.com/external-secrets/external-secrets/runtime v0.0.0
 	github.com/stretchr/testify v1.11.1
-	go.uber.org/zap v1.27.0
+	go.uber.org/zap v1.28.0
 	k8s.io/api v0.35.0
 	k8s.io/apiextensions-apiserver v0.35.0
 	k8s.io/apimachinery v0.35.0
@@ -28,7 +28,7 @@ require (
 	github.com/evanphx/json-patch/v5 v5.9.11 // indirect
 	github.com/fsnotify/fsnotify v1.9.0 // indirect
 	github.com/fxamacker/cbor/v2 v2.9.0 // indirect
-	github.com/gabriel-vasile/mimetype v1.4.11 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.13 // indirect
 	github.com/go-logr/logr v1.4.3 // indirect
 	github.com/go-openapi/jsonpointer v0.22.4 // indirect
 	github.com/go-openapi/jsonreference v0.21.4 // indirect
@@ -46,7 +46,7 @@ require (
 	github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
 	github.com/go-playground/universal-translator v0.18.1 // indirect
-	github.com/go-playground/validator/v10 v10.28.0 // indirect
+	github.com/go-playground/validator/v10 v10.30.2 // indirect
 	github.com/goccy/go-json v0.10.5 // indirect
 	github.com/google/btree v1.1.3 // indirect
 	github.com/google/gnostic-models v0.7.1 // indirect
@@ -79,13 +79,13 @@ require (
 	go.uber.org/multierr v1.11.0 // indirect
 	go.yaml.in/yaml/v2 v2.4.3 // indirect
 	go.yaml.in/yaml/v3 v3.0.4 // indirect
-	golang.org/x/crypto v0.47.0 // indirect
-	golang.org/x/net v0.49.0 // indirect
+	golang.org/x/crypto v0.50.0 // indirect
+	golang.org/x/net v0.52.0 // indirect
 	golang.org/x/oauth2 v0.34.0 // indirect
-	golang.org/x/sync v0.19.0 // indirect
-	golang.org/x/sys v0.40.0 // indirect
-	golang.org/x/term v0.39.0 // indirect
-	golang.org/x/text v0.33.0 // indirect
+	golang.org/x/sync v0.20.0 // indirect
+	golang.org/x/sys v0.43.0 // indirect
+	golang.org/x/term v0.42.0 // indirect
+	golang.org/x/text v0.36.0 // indirect
 	golang.org/x/time v0.14.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
 	google.golang.org/protobuf v1.36.11 // indirect

+ 24 - 24
providers/v1/beyondtrust/go.sum

@@ -1,7 +1,7 @@
 dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
 dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
-github.com/BeyondTrust/go-client-library-passwordsafe v1.0.0 h1:khr3BeVQscPf5MBhVaNPhobcdmo7pdQAzhmwQPeXNQ0=
-github.com/BeyondTrust/go-client-library-passwordsafe v1.0.0/go.mod h1:ntgg5j8QRG0XyF8WUTa57T1TwYJOJjerLMCc1XvJO0M=
+github.com/BeyondTrust/go-client-library-passwordsafe v1.3.0 h1:prfGhBOM9fZRhAOlRWSaRWv6N02B5xlwgbKxldXVv+U=
+github.com/BeyondTrust/go-client-library-passwordsafe v1.3.0/go.mod h1:TyIl8xhIygOjeIwxLlxtmEvirf9puiedmMN45DvxVP0=
 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.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
@@ -34,8 +34,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
 github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
 github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
 github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
-github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
-github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
+github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
+github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
 github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
 github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
@@ -80,8 +80,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
-github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
+github.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ=
+github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc=
 github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
 github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
@@ -186,32 +186,32 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
 go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
+go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q=
 go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
 go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
 go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
-golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
-golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
-golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
-golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
-golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
-golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
+golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
+golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
+golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
+golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
+golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
+golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
 golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
 golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
-golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
-golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
-golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
-golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
-golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
-golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
-golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
+golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
+golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
+golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
+golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
+golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
 golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
 golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
-golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
-golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
+golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
+golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
 gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
 gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
 google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

+ 17 - 55
providers/v1/beyondtrust/provider.go

@@ -63,6 +63,7 @@ const (
 	ownerTypeFieldName   = "owner_type"
 	secretTypeFieldName  = "secret_type"
 	secretTypeCredential = "CREDENTIAL"
+	secretTypeSecret     = "SECRET"
 )
 
 var (
@@ -340,7 +341,7 @@ func (p *Provider) GetAllSecrets(_ context.Context, _ esv1.ExternalSecretFind) (
 // GetSecret reads the secret from the Password Safe server and returns it. The controller uses the value here to
 // create the Kubernetes secret.
 func (p *Provider) GetSecret(_ context.Context, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
-	managedAccountType := !strings.EqualFold(p.retrievaltype, "SECRET")
+	managedAccountType := !strings.EqualFold(p.retrievaltype, secretTypeSecret)
 
 	retrievalPaths := utils.ValidatePaths([]string{ref.Key}, managedAccountType, p.separator, &p.log)
 
@@ -519,86 +520,47 @@ func (p *Provider) CreateSecret(secret string, data map[string]any, signAppinRes
 		Notes:       notes,
 	}
 
-	var configMap map[string]any
+	// Build a version-neutral input per secret type; CreateSecretFlow
+	// selects Config30/Config31 from p.authenticate.ApiVersion internally.
+	var secretInput any
 	switch strings.ToUpper(secretType) {
-	case "CREDENTIAL":
-
-		secretCredentialDetailsConfig30 := entities.SecretCredentialDetailsConfig30{
+	case secretTypeCredential:
+		secretInput = entities.SecretCredentialInput{
 			SecretDetailsBaseConfig: secretDetailsConfig,
 			Username:                username,
 			Password:                secret,
 			OwnerId:                 ownerID,
 			OwnerType:               ownerType,
-			Owners:                  ownerDetailsOwnerID,
-		}
-
-		secretCredentialDetailsConfig31 := entities.SecretCredentialDetailsConfig31{
-			SecretDetailsBaseConfig: secretDetailsConfig,
-			Username:                username,
-			Password:                secret,
-			Owners:                  ownerDetailsGroupID,
-		}
-
-		configMap = map[string]any{
-			"3.0": secretCredentialDetailsConfig30,
-			"3.1": secretCredentialDetailsConfig31,
+			OwnersByOwnerId:         ownerDetailsOwnerID,
+			OwnersByGroupId:         ownerDetailsGroupID,
 		}
 
 	case "FILE":
-
-		secretFileDetailsConfig30 := entities.SecretFileDetailsConfig30{
+		secretInput = entities.SecretFileInput{
 			SecretDetailsBaseConfig: secretDetailsConfig,
-			FileContent:             secret,
 			FileName:                fileName,
+			FileContent:             secret,
 			OwnerId:                 ownerID,
 			OwnerType:               ownerType,
-			Owners:                  ownerDetailsOwnerID,
-		}
-
-		secretFileDetailsConfig31 := entities.SecretFileDetailsConfig31{
-			SecretDetailsBaseConfig: secretDetailsConfig,
-			FileContent:             secret,
-			FileName:                fileName,
-			Owners:                  ownerDetailsGroupID,
-		}
-
-		configMap = map[string]any{
-			"3.0": secretFileDetailsConfig30,
-			"3.1": secretFileDetailsConfig31,
+			OwnersByOwnerId:         ownerDetailsOwnerID,
+			OwnersByGroupId:         ownerDetailsGroupID,
 		}
 
 	case "TEXT":
-
-		secretTextDetailsConfig30 := entities.SecretTextDetailsConfig30{
+		secretInput = entities.SecretTextInput{
 			SecretDetailsBaseConfig: secretDetailsConfig,
 			Text:                    secret,
 			OwnerId:                 ownerID,
 			OwnerType:               ownerType,
-			Owners:                  ownerDetailsOwnerID,
-		}
-
-		secretTextDetailsConfig31 := entities.SecretTextDetailsConfig31{
-			SecretDetailsBaseConfig: secretDetailsConfig,
-			Text:                    secret,
-			Owners:                  ownerDetailsGroupID,
-		}
-
-		configMap = map[string]any{
-			"3.0": secretTextDetailsConfig30,
-			"3.1": secretTextDetailsConfig31,
+			OwnersByOwnerId:         ownerDetailsOwnerID,
+			OwnersByGroupId:         ownerDetailsGroupID,
 		}
 
 	default:
 		return fmt.Errorf("Unknown secret type")
 	}
 
-	secretDetails, exists := configMap[p.authenticate.ApiVersion]
-
-	if !exists {
-		return fmt.Errorf("unsupported API version: %v", &p.authenticate.ApiVersion)
-	}
-
-	_, err = secretObj.CreateSecretFlow(folderName, secretDetails)
+	_, err = secretObj.CreateSecretFlow(folderName, secretInput)
 
 	if err != nil {
 		return err

+ 324 - 28
providers/v1/beyondtrust/provider_test.go

@@ -18,6 +18,9 @@ package beyondtrust
 
 import (
 	"context"
+	"encoding/json"
+	"fmt"
+	"io"
 	"net/http"
 	"net/http/httptest"
 	"testing"
@@ -53,6 +56,12 @@ const (
 	authSignAppInPath      = "/Auth/SignAppIn"
 	secretsSafeFoldersPath = "/secrets-safe/folders/"
 	secretsSafeSecretsPath = "/secrets-safe/secrets"
+	folderSecretsPath      = "/secrets-safe/folders/cb871861-8b40-4556-820c-1ca6d522adfa/secrets"
+	apiVersion32           = "3.2"
+	fakeClientSecret       = "fake_client_secret"
+	passwordKey            = "password"
+	testCredentialKey      = "test-credential"
+	testName               = "test"
 )
 
 func createMockPasswordSafeClient(t *testing.T) kubeclient.Client {
@@ -90,22 +99,22 @@ func createMockPasswordSafeClient(t *testing.T) kubeclient.Client {
 
 	clientConfig := clientcmd.NewDefaultClientConfig(clientcmdapi.Config{
 		Clusters: map[string]*clientcmdapi.Cluster{
-			"test": {
+			testName: {
 				Server: server.URL,
 			},
 		},
 		AuthInfos: map[string]*clientcmdapi.AuthInfo{
-			"test": {
+			testName: {
 				Token: "token",
 			},
 		},
 		Contexts: map[string]*clientcmdapi.Context{
-			"test": {
-				Cluster:  "test",
-				AuthInfo: "test",
+			testName: {
+				Cluster:  testName,
+				AuthInfo: testName,
 			},
 		},
-		CurrentContext: "test",
+		CurrentContext: testName,
 	}, &clientcmd.ConfigOverrides{})
 
 	restConfig, err := clientConfig.ClientConfig()
@@ -132,7 +141,7 @@ func TestNewClient(t *testing.T) {
 	}{
 		{
 			name:      "Client ok",
-			nameSpace: "test",
+			nameSpace: testName,
 			args: args{
 				store: esv1.SecretStore{
 					Spec: esv1.SecretStoreSpec{
@@ -140,7 +149,7 @@ func TestNewClient(t *testing.T) {
 							Beyondtrust: &esv1.BeyondtrustProvider{
 								Server: &esv1.BeyondtrustServer{
 									APIURL:        fakeAPIURL,
-									RetrievalType: "SECRET",
+									RetrievalType: secretTypeSecret,
 								},
 
 								Auth: &esv1.BeyondtrustAuth{
@@ -163,7 +172,7 @@ func TestNewClient(t *testing.T) {
 		},
 		{
 			name:      "Bad Client Id",
-			nameSpace: "test",
+			nameSpace: testName,
 			args: args{
 				store: esv1.SecretStore{
 					Spec: esv1.SecretStoreSpec{
@@ -171,7 +180,7 @@ func TestNewClient(t *testing.T) {
 							Beyondtrust: &esv1.BeyondtrustProvider{
 								Server: &esv1.BeyondtrustServer{
 									APIURL:        fakeAPIURL,
-									RetrievalType: "SECRET",
+									RetrievalType: secretTypeSecret,
 								},
 
 								Auth: &esv1.BeyondtrustAuth{
@@ -195,7 +204,7 @@ func TestNewClient(t *testing.T) {
 		},
 		{
 			name:      "Bad Client Secret",
-			nameSpace: "test",
+			nameSpace: testName,
 			args: args{
 				store: esv1.SecretStore{
 					Spec: esv1.SecretStoreSpec{
@@ -203,7 +212,7 @@ func TestNewClient(t *testing.T) {
 							Beyondtrust: &esv1.BeyondtrustProvider{
 								Server: &esv1.BeyondtrustServer{
 									APIURL:        fakeAPIURL,
-									RetrievalType: "SECRET",
+									RetrievalType: secretTypeSecret,
 								},
 
 								Auth: &esv1.BeyondtrustAuth{
@@ -227,7 +236,7 @@ func TestNewClient(t *testing.T) {
 		},
 		{
 			name:      "Bad Separator",
-			nameSpace: "test",
+			nameSpace: testName,
 			args: args{
 				store: esv1.SecretStore{
 					Spec: esv1.SecretStoreSpec{
@@ -236,7 +245,7 @@ func TestNewClient(t *testing.T) {
 								Server: &esv1.BeyondtrustServer{
 									APIURL:        fakeAPIURL,
 									Separator:     "//",
-									RetrievalType: "SECRET",
+									RetrievalType: secretTypeSecret,
 								},
 								Auth: &esv1.BeyondtrustAuth{
 									ClientID: &esv1.BeyondTrustProviderSecretRef{
@@ -259,7 +268,7 @@ func TestNewClient(t *testing.T) {
 		},
 		{
 			name:      "Time Out",
-			nameSpace: "test",
+			nameSpace: testName,
 			args: args{
 				store: esv1.SecretStore{
 					Spec: esv1.SecretStoreSpec{
@@ -269,7 +278,7 @@ func TestNewClient(t *testing.T) {
 									APIURL:               fakeAPIURL,
 									Separator:            "/",
 									ClientTimeOutSeconds: 400,
-									RetrievalType:        "SECRET",
+									RetrievalType:        secretTypeSecret,
 								},
 								Auth: &esv1.BeyondtrustAuth{
 									ClientID: &esv1.BeyondTrustProviderSecretRef{
@@ -292,7 +301,7 @@ func TestNewClient(t *testing.T) {
 		},
 		{
 			name:      "ApiKey ok",
-			nameSpace: "test",
+			nameSpace: testName,
 			args: args{
 				store: esv1.SecretStore{
 					Spec: esv1.SecretStoreSpec{
@@ -300,7 +309,7 @@ func TestNewClient(t *testing.T) {
 							Beyondtrust: &esv1.BeyondtrustProvider{
 								Server: &esv1.BeyondtrustServer{
 									APIURL:        fakeAPIURL,
-									RetrievalType: "SECRET",
+									RetrievalType: secretTypeSecret,
 								},
 
 								Auth: &esv1.BeyondtrustAuth{
@@ -320,7 +329,7 @@ func TestNewClient(t *testing.T) {
 		},
 		{
 			name:      "Bad ApiKey",
-			nameSpace: "test",
+			nameSpace: testName,
 			args: args{
 				store: esv1.SecretStore{
 					Spec: esv1.SecretStoreSpec{
@@ -328,7 +337,7 @@ func TestNewClient(t *testing.T) {
 							Beyondtrust: &esv1.BeyondtrustProvider{
 								Server: &esv1.BeyondtrustServer{
 									APIURL:        fakeAPIURL,
-									RetrievalType: "SECRET",
+									RetrievalType: secretTypeSecret,
 								},
 
 								Auth: &esv1.BeyondtrustAuth{
@@ -403,6 +412,9 @@ func TestPushSecret(t *testing.T) {
 		serverHandler http.HandlerFunc
 		metadata      apiextensionsv1.JSON
 		expectedError bool
+		// apiVersion lets a case target a specific Password Safe API version.
+		// Empty string keeps the historical default of 3.1.
+		apiVersion string
 	}
 
 	tests := []testCase{
@@ -425,7 +437,7 @@ func TestPushSecret(t *testing.T) {
 					if err != nil {
 						t.Error(err)
 					}
-				case "/secrets-safe/folders/cb871861-8b40-4556-820c-1ca6d522adfa/secrets":
+				case folderSecretsPath:
 					_, err := w.Write([]byte(`{"Id": "01ca9cf3-0751-4a90-4856-08dcf22d7472","Title": "Secret Title"}`))
 					if err != nil {
 						t.Error(err)
@@ -525,6 +537,127 @@ func TestPushSecret(t *testing.T) {
 			},
 		},
 		{
+			name: "successfully pushes credential secret v3.2",
+			serverHandler: func(w http.ResponseWriter, r *http.Request) {
+				switch r.URL.Path {
+				case authConnectTokenPath:
+					_, err := w.Write([]byte(`{"access_token": "fake_token", "expires_in": 600, "token_type": "Bearer"}`))
+					if err != nil {
+						t.Error(err)
+					}
+				case authSignAppInPath:
+					_, err := w.Write([]byte(`{"UserId":1, "EmailAddress":"test@beyondtrust.com"}`))
+					if err != nil {
+						t.Error(err)
+					}
+				case secretsSafeFoldersPath:
+					_, err := w.Write([]byte(`[{"Id": "cb871861-8b40-4556-820c-1ca6d522adfa","Name": "folder1"}]`))
+					if err != nil {
+						t.Error(err)
+					}
+				case folderSecretsPath:
+					_, err := w.Write([]byte(`{"Id": "01ca9cf3-0751-4a90-4856-08dcf22d7472","Title": "Secret Title"}`))
+					if err != nil {
+						t.Error(err)
+					}
+				default:
+					http.Error(w, "not found", http.StatusNotFound)
+				}
+			},
+			expectedError: false,
+			apiVersion:    apiVersion32,
+			metadata: apiextensionsv1.JSON{
+				Raw: []byte(`{
+					"title": "Test Credential v3.2",
+					"username": "admin",
+					"description": "Test Credential Secret description",
+					"secret_type": "CREDENTIAL",
+					"folder_name": "folder1"
+				}`),
+			},
+		},
+		{
+			name: "successfully pushes file secret v3.2",
+			serverHandler: func(w http.ResponseWriter, r *http.Request) {
+				switch r.URL.Path {
+				case authConnectTokenPath:
+					_, err := w.Write([]byte(`{"access_token": "fake_token", "expires_in": 600, "token_type": "Bearer"}`))
+					if err != nil {
+						t.Error(err)
+					}
+				case authSignAppInPath:
+					_, err := w.Write([]byte(`{"UserId":1, "EmailAddress":"test@beyondtrust.com"}`))
+					if err != nil {
+						t.Error(err)
+					}
+				case secretsSafeFoldersPath:
+					_, err := w.Write([]byte(`[{"Id": "cb871861-8b40-4556-820c-1ca6d522adfa","Name": "folder1"}]`))
+					if err != nil {
+						t.Error(err)
+					}
+				case "/secrets-safe/folders/cb871861-8b40-4556-820c-1ca6d522adfa/secrets/file":
+					_, err := w.Write([]byte(`{"Id": "01ca9cf3-0751-4a90-4856-08dcf22d7472","Title": "Secret Title"}`))
+					if err != nil {
+						t.Error(err)
+					}
+				default:
+					http.Error(w, "not found", http.StatusNotFound)
+				}
+			},
+			expectedError: false,
+			apiVersion:    apiVersion32,
+			metadata: apiextensionsv1.JSON{
+				Raw: []byte(`{
+					"title": "Test File Secret v3.2",
+					"username": "admin",
+					"description": "Test File Secret description",
+					"secret_type": "FILE",
+					"folder_name": "folder1",
+					"file_name": "credentials.txt"
+				}`),
+			},
+		},
+		{
+			name: "successfully pushes text secret v3.2",
+			serverHandler: func(w http.ResponseWriter, r *http.Request) {
+				switch r.URL.Path {
+				case authConnectTokenPath:
+					_, err := w.Write([]byte(`{"access_token": "fake_token", "expires_in": 600, "token_type": "Bearer"}`))
+					if err != nil {
+						t.Error(err)
+					}
+				case authSignAppInPath:
+					_, err := w.Write([]byte(`{"UserId":1, "EmailAddress":"test@beyondtrust.com"}`))
+					if err != nil {
+						t.Error(err)
+					}
+				case secretsSafeFoldersPath:
+					_, err := w.Write([]byte(`[{"Id": "cb871861-8b40-4556-820c-1ca6d522adfa","Name": "folder1"}]`))
+					if err != nil {
+						t.Error(err)
+					}
+				case "/secrets-safe/folders/cb871861-8b40-4556-820c-1ca6d522adfa/secrets/text":
+					_, err := w.Write([]byte(`{"Id": "01ca9cf3-0751-4a90-4856-08dcf22d7472","Title": "Secret Title"}`))
+					if err != nil {
+						t.Error(err)
+					}
+				default:
+					http.Error(w, "not found", http.StatusNotFound)
+				}
+			},
+			expectedError: false,
+			apiVersion:    apiVersion32,
+			metadata: apiextensionsv1.JSON{
+				Raw: []byte(`{
+					"title": "Test Text Secret v3.2",
+					"username": "admin",
+					"description": "Test Text Secret description",
+					"secret_type": "TEXT",
+					"folder_name": "folder1"
+				}`),
+			},
+		},
+		{
 			name: "successfully pushes text secret - 404 error",
 			serverHandler: func(w http.ResponseWriter, r *http.Request) {
 				switch r.URL.Path {
@@ -595,13 +728,18 @@ func TestPushSecret(t *testing.T) {
 				t.Error(err)
 			}
 
+			apiVersion := tt.apiVersion
+			if apiVersion == "" {
+				apiVersion = "3.1"
+			}
+
 			params := authentication.AuthenticationParametersObj{
 				HTTPClient:                 *httpClientObj,
 				BackoffDefinition:          backoffDefinition,
 				EndpointURL:                fakeServer.URL,
-				APIVersion:                 "3.1",
+				APIVersion:                 apiVersion,
 				ClientID:                   "fake_clinet_id",
-				ClientSecret:               "fake_client_secret",
+				ClientSecret:               fakeClientSecret,
 				Logger:                     zapLogger,
 				RetryMaxElapsedTimeSeconds: 30,
 			}
@@ -612,16 +750,16 @@ func TestPushSecret(t *testing.T) {
 			p := &Provider{authenticate: *authObj}
 
 			secret := &v1.Secret{
-				Data: map[string][]byte{"password": []byte("supersecret")},
+				Data: map[string][]byte{passwordKey: []byte("supersecret")},
 			}
 
 			metadataJSON := &tt.metadata
 
 			psd := v1alpha1.PushSecretData{
 				Match: v1alpha1.PushSecretMatch{
-					SecretKey: "password",
+					SecretKey: passwordKey,
 					RemoteRef: v1alpha1.PushSecretRemoteRef{
-						RemoteKey: "test-credential",
+						RemoteKey: testCredentialKey,
 					},
 				},
 				Metadata: metadataJSON,
@@ -729,7 +867,7 @@ func TestSecretExists(t *testing.T) {
 				EndpointURL:                fakeServer.URL,
 				APIVersion:                 "3.1",
 				ClientID:                   "fake_clinet_id",
-				ClientSecret:               "fake_client_secret",
+				ClientSecret:               fakeClientSecret,
 				Logger:                     zapLogger,
 				RetryMaxElapsedTimeSeconds: 30,
 			}
@@ -740,7 +878,7 @@ func TestSecretExists(t *testing.T) {
 			p := &Provider{authenticate: *authObj}
 
 			remoteRef := v1alpha1.PushSecretRemoteRef{
-				RemoteKey: "test-credential",
+				RemoteKey: testCredentialKey,
 			}
 
 			exists, err := p.SecretExists(context.Background(), remoteRef)
@@ -757,3 +895,161 @@ func TestSecretExists(t *testing.T) {
 		})
 	}
 }
+
+// writeBody is a small helper for the mock servers below. It fails the test
+// if the response cannot be written, instead of nesting an err-check inside
+// every switch case in the handler.
+func writeBody(t *testing.T, w http.ResponseWriter, body string) {
+	t.Helper()
+	if _, err := w.Write([]byte(body)); err != nil {
+		t.Error(err)
+	}
+}
+
+// newOwnerFieldsMockServer returns an httptest.Server that satisfies the
+// auth + folder lookup + create-secret request flow. The body of the
+// create-secret POST is captured into *captured for assertion.
+func newOwnerFieldsMockServer(t *testing.T, secretsPath string, captured map[string]any) *httptest.Server {
+	t.Helper()
+	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		switch r.URL.Path {
+		case authConnectTokenPath:
+			writeBody(t, w, `{"access_token": "fake_token", "expires_in": 600, "token_type": "Bearer"}`)
+		case authSignAppInPath:
+			writeBody(t, w, `{"UserId":1, "EmailAddress":"test@beyondtrust.com"}`)
+		case secretsSafeFoldersPath:
+			writeBody(t, w, `[{"Id": "cb871861-8b40-4556-820c-1ca6d522adfa","Name": "folder1"}]`)
+		case secretsPath:
+			bodyBytes, err := io.ReadAll(r.Body)
+			require.NoError(t, err)
+			require.NoError(t, json.Unmarshal(bodyBytes, &captured))
+			writeBody(t, w, `{"Id": "01ca9cf3-0751-4a90-4856-08dcf22d7472","Title": "Secret Title"}`)
+		default:
+			http.Error(w, "not found", http.StatusNotFound)
+		}
+	}))
+}
+
+// newAuthenticatedProvider builds a Provider authenticated against serverURL
+// with the requested API version. Used by tests that need a ready-to-call
+// Provider without inlining the full auth/HTTP-client wiring.
+func newAuthenticatedProvider(t *testing.T, serverURL, apiVersion string) *Provider {
+	t.Helper()
+	logger, err := zap.NewDevelopment()
+	require.NoError(t, err)
+	zapLogger := logging.NewZapLogger(logger)
+
+	backoffDefinition := backoff.NewExponentialBackOff()
+	backoffDefinition.InitialInterval = 1 * time.Second
+	backoffDefinition.MaxElapsedTime = 2 * time.Second
+	backoffDefinition.RandomizationFactor = 0.5
+
+	httpClientObj, err := utils.GetHttpClient(30, true, "", "", zapLogger)
+	require.NoError(t, err)
+
+	authObj, err := authentication.Authenticate(authentication.AuthenticationParametersObj{
+		HTTPClient:                 *httpClientObj,
+		BackoffDefinition:          backoffDefinition,
+		EndpointURL:                serverURL,
+		APIVersion:                 apiVersion,
+		ClientID:                   "fake_client_id",
+		ClientSecret:               fakeClientSecret,
+		Logger:                     zapLogger,
+		RetryMaxElapsedTimeSeconds: 30,
+	})
+	require.NoError(t, err)
+
+	return &Provider{authenticate: *authObj}
+}
+
+func ownerFieldsMetadata(secretType string) apiextensionsv1.JSON {
+	return apiextensionsv1.JSON{
+		Raw: fmt.Appendf(nil, `{
+			"title": "Owner Fields Test",
+			"username": "admin",
+			"secret_type": %q,
+			"folder_name": "folder1",
+			"owner_type": "User",
+			"owner_id": 7,
+			"group_id": 42
+		}`, secretType),
+	}
+}
+
+// TestPushSecret_OwnerFieldsArePropagated guards the dual-field migration in CreateSecret
+// where every secret-type input carries both OwnersByOwnerId and OwnersByGroupId. The SDK
+// then narrows down to one shape based on API version: v3.0 → OwnerDetailsOwnerId,
+// v3.1/v3.2 → OwnerDetailsGroupId. We assert the request body the SDK actually emitted.
+func TestPushSecret_OwnerFieldsArePropagated(t *testing.T) {
+	type testCase struct {
+		name        string
+		apiVersion  string
+		secretType  string
+		secretsPath string
+		assertBody  func(t *testing.T, body map[string]any)
+	}
+
+	tests := []testCase{
+		{
+			name:        "v3.0 propagates OwnersByOwnerId and top-level OwnerId/OwnerType for CREDENTIAL",
+			apiVersion:  "3.0",
+			secretType:  secretTypeCredential,
+			secretsPath: folderSecretsPath,
+			assertBody: func(t *testing.T, body map[string]any) {
+				assert.Equal(t, float64(7), body["OwnerId"], "top-level OwnerId should come from metadata.owner_id")
+				assert.Equal(t, "User", body["OwnerType"], "top-level OwnerType should come from metadata.owner_type")
+
+				owners, ok := body["Owners"].([]any)
+				require.True(t, ok, "Owners array missing or wrong type")
+				require.NotEmpty(t, owners, "Owners array empty")
+				first := owners[0].(map[string]any)
+				assert.Equal(t, float64(1), first["OwnerId"], "main owner OwnerId should be UserId=1 from sign-in")
+				assert.Equal(t, "test@beyondtrust.com", first["Email"])
+			},
+		},
+		{
+			name:        "v3.2 propagates OwnersByGroupId for CREDENTIAL",
+			apiVersion:  apiVersion32,
+			secretType:  secretTypeCredential,
+			secretsPath: folderSecretsPath,
+			assertBody: func(t *testing.T, body map[string]any) {
+				_, hasOwnerId := body["OwnerId"]
+				_, hasOwnerType := body["OwnerType"]
+				assert.False(t, hasOwnerId, "v3.2 Config32 should not emit top-level OwnerId")
+				assert.False(t, hasOwnerType, "v3.2 Config32 should not emit top-level OwnerType")
+
+				owners, ok := body["Owners"].([]any)
+				require.True(t, ok, "Owners array missing or wrong type")
+				require.NotEmpty(t, owners, "Owners array empty")
+				first := owners[0].(map[string]any)
+				assert.Equal(t, float64(42), first["GroupId"], "main owner GroupId should come from metadata.group_id")
+				assert.Equal(t, float64(1), first["UserId"], "main owner UserId should be UserId=1 from sign-in")
+				assert.Equal(t, "test@beyondtrust.com", first["Email"])
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			capturedBody := make(map[string]any)
+			fakeServer := newOwnerFieldsMockServer(t, tt.secretsPath, capturedBody)
+			defer fakeServer.Close()
+
+			p := newAuthenticatedProvider(t, fakeServer.URL, tt.apiVersion)
+			metadata := ownerFieldsMetadata(tt.secretType)
+
+			psd := v1alpha1.PushSecretData{
+				Match: v1alpha1.PushSecretMatch{
+					SecretKey: passwordKey,
+					RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: testCredentialKey},
+				},
+				Metadata: &metadata,
+			}
+			secret := &v1.Secret{Data: map[string][]byte{passwordKey: []byte("supersecret")}}
+
+			require.NoError(t, p.PushSecret(context.Background(), secret, psd))
+			require.NotNil(t, capturedBody, "create-secret endpoint was never hit")
+			tt.assertBody(t, capturedBody)
+		})
+	}
+}