provider_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /*
  2. Copyright © 2025 ESO Maintainer Team
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package secretserver
  14. import (
  15. "context"
  16. "math/rand"
  17. "testing"
  18. "github.com/DelineaXPM/tss-sdk-go/v3/server"
  19. "github.com/stretchr/testify/assert"
  20. corev1 "k8s.io/api/core/v1"
  21. kubeErrors "k8s.io/apimachinery/pkg/api/errors"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. kubeClient "sigs.k8s.io/controller-runtime/pkg/client"
  24. clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
  25. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  26. v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
  27. "github.com/external-secrets/external-secrets/pkg/esutils"
  28. )
  29. func TestDoesConfigDependOnNamespace(t *testing.T) {
  30. tests := map[string]struct {
  31. cfg esv1.SecretServerProvider
  32. want bool
  33. }{
  34. "true when Username references a secret without explicit namespace": {
  35. cfg: esv1.SecretServerProvider{
  36. Username: &esv1.SecretServerProviderRef{
  37. SecretRef: &v1.SecretKeySelector{Name: "foo"},
  38. },
  39. Password: &esv1.SecretServerProviderRef{SecretRef: nil},
  40. },
  41. want: true,
  42. },
  43. "true when password references a secret without explicit namespace": {
  44. cfg: esv1.SecretServerProvider{
  45. Username: &esv1.SecretServerProviderRef{SecretRef: nil},
  46. Password: &esv1.SecretServerProviderRef{
  47. SecretRef: &v1.SecretKeySelector{Name: "foo"},
  48. },
  49. },
  50. want: true,
  51. },
  52. "false when neither Username or Password reference a secret": {
  53. cfg: esv1.SecretServerProvider{
  54. Username: &esv1.SecretServerProviderRef{SecretRef: nil},
  55. Password: &esv1.SecretServerProviderRef{SecretRef: nil},
  56. },
  57. want: false,
  58. },
  59. }
  60. for name, tc := range tests {
  61. t.Run(name, func(t *testing.T) {
  62. got := doesConfigDependOnNamespace(&tc.cfg)
  63. assert.Equal(t, tc.want, got)
  64. })
  65. }
  66. }
  67. func TestValidateStore(t *testing.T) {
  68. validSecretRefUsingValue := makeSecretRefUsingValue("foo")
  69. ambiguousSecretRef := &esv1.SecretServerProviderRef{
  70. SecretRef: &v1.SecretKeySelector{Name: "foo"}, Value: "foo",
  71. }
  72. testURL := "https://example.com"
  73. tests := map[string]struct {
  74. cfg esv1.SecretServerProvider
  75. want error
  76. }{
  77. "invalid without username": {
  78. cfg: esv1.SecretServerProvider{
  79. Username: nil,
  80. Password: validSecretRefUsingValue,
  81. ServerURL: testURL,
  82. },
  83. want: errEmptyUserName,
  84. },
  85. "invalid without password": {
  86. cfg: esv1.SecretServerProvider{
  87. Username: validSecretRefUsingValue,
  88. Password: nil,
  89. ServerURL: testURL,
  90. },
  91. want: errEmptyPassword,
  92. },
  93. "invalid without serverURL": {
  94. cfg: esv1.SecretServerProvider{
  95. Username: validSecretRefUsingValue,
  96. Password: validSecretRefUsingValue,
  97. /*ServerURL: testURL,*/
  98. },
  99. want: errEmptyServerURL,
  100. },
  101. "invalid with ambiguous Username": {
  102. cfg: esv1.SecretServerProvider{
  103. Username: ambiguousSecretRef,
  104. Password: validSecretRefUsingValue,
  105. ServerURL: testURL,
  106. },
  107. want: errSecretRefAndValueConflict,
  108. },
  109. "invalid with ambiguous Password": {
  110. cfg: esv1.SecretServerProvider{
  111. Username: validSecretRefUsingValue,
  112. Password: ambiguousSecretRef,
  113. ServerURL: testURL,
  114. },
  115. want: errSecretRefAndValueConflict,
  116. },
  117. "invalid with invalid Username": {
  118. cfg: esv1.SecretServerProvider{
  119. Username: makeSecretRefUsingValue(""),
  120. Password: validSecretRefUsingValue,
  121. ServerURL: testURL,
  122. },
  123. want: errSecretRefAndValueMissing,
  124. },
  125. "invalid with invalid Password": {
  126. cfg: esv1.SecretServerProvider{
  127. Username: validSecretRefUsingValue,
  128. Password: makeSecretRefUsingValue(""),
  129. ServerURL: testURL,
  130. },
  131. want: errSecretRefAndValueMissing,
  132. },
  133. "valid with tenant/clientID/clientSecret": {
  134. cfg: esv1.SecretServerProvider{
  135. Username: validSecretRefUsingValue,
  136. Password: validSecretRefUsingValue,
  137. ServerURL: testURL,
  138. },
  139. want: nil,
  140. },
  141. }
  142. for name, tc := range tests {
  143. t.Run(name, func(t *testing.T) {
  144. s := esv1.SecretStore{
  145. Spec: esv1.SecretStoreSpec{
  146. Provider: &esv1.SecretStoreProvider{
  147. SecretServer: &tc.cfg,
  148. },
  149. },
  150. }
  151. p := &Provider{}
  152. _, got := p.ValidateStore(&s)
  153. assert.Equal(t, tc.want, got)
  154. })
  155. }
  156. }
  157. func TestNewClient(t *testing.T) {
  158. userNameKey := "username"
  159. userNameValue := "foo"
  160. passwordKey := "password"
  161. passwordValue := generateRandomString()
  162. domain := "domain1"
  163. clientSecret := &corev1.Secret{
  164. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
  165. Data: map[string][]byte{
  166. userNameKey: []byte(userNameValue),
  167. passwordKey: []byte(passwordValue),
  168. },
  169. }
  170. validProvider := &esv1.SecretServerProvider{
  171. Username: makeSecretRefUsingRef(clientSecret.Name, userNameKey),
  172. Password: makeSecretRefUsingRef(clientSecret.Name, passwordKey),
  173. ServerURL: "https://example.com",
  174. }
  175. clientSecretWithDomain := &corev1.Secret{
  176. ObjectMeta: metav1.ObjectMeta{Name: "with-domain", Namespace: "default"},
  177. Data: map[string][]byte{
  178. userNameKey: []byte(userNameValue),
  179. passwordKey: []byte(passwordValue),
  180. domain: []byte(domain),
  181. },
  182. }
  183. validProviderWithDomain := &esv1.SecretServerProvider{
  184. Username: makeSecretRefUsingRef(clientSecretWithDomain.Name, userNameKey),
  185. Password: makeSecretRefUsingRef(clientSecretWithDomain.Name, passwordKey),
  186. Domain: domain,
  187. ServerURL: "https://example.com",
  188. }
  189. tests := map[string]struct {
  190. store esv1.GenericStore // leave nil for namespaced store
  191. provider *esv1.SecretServerProvider // discarded when store is set
  192. kube kubeClient.Client
  193. errCheck func(t *testing.T, err error)
  194. }{
  195. "missing provider config": {
  196. provider: nil,
  197. errCheck: func(t *testing.T, err error) {
  198. assert.ErrorIs(t, err, errInvalidSpec)
  199. },
  200. },
  201. "namespace-dependent cluster secret store": {
  202. store: &esv1.ClusterSecretStore{
  203. TypeMeta: metav1.TypeMeta{Kind: esv1.ClusterSecretStoreKind},
  204. Spec: esv1.SecretStoreSpec{
  205. Provider: &esv1.SecretStoreProvider{
  206. SecretServer: validProvider,
  207. },
  208. },
  209. },
  210. errCheck: func(t *testing.T, err error) {
  211. assert.ErrorIs(t, err, errClusterStoreRequiresNamespace)
  212. },
  213. },
  214. "dangling password ref": {
  215. provider: &esv1.SecretServerProvider{
  216. Username: validProvider.Username,
  217. Password: makeSecretRefUsingRef("typo", passwordKey),
  218. ServerURL: validProvider.ServerURL,
  219. },
  220. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  221. errCheck: func(t *testing.T, err error) {
  222. assert.True(t, kubeErrors.IsNotFound(err))
  223. },
  224. },
  225. "dangling username ref": {
  226. provider: &esv1.SecretServerProvider{
  227. Username: makeSecretRefUsingRef("typo", userNameKey),
  228. Password: validProvider.Password,
  229. ServerURL: validProvider.ServerURL,
  230. },
  231. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  232. errCheck: func(t *testing.T, err error) {
  233. assert.True(t, kubeErrors.IsNotFound(err))
  234. },
  235. },
  236. "secret ref without name": {
  237. provider: &esv1.SecretServerProvider{
  238. Username: makeSecretRefUsingRef("", userNameKey),
  239. Password: validProvider.Password,
  240. ServerURL: validProvider.ServerURL,
  241. },
  242. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  243. errCheck: func(t *testing.T, err error) {
  244. assert.ErrorIs(t, err, errMissingSecretName)
  245. },
  246. },
  247. "secret ref without key": {
  248. provider: &esv1.SecretServerProvider{
  249. Username: validProvider.Password,
  250. Password: makeSecretRefUsingRef(clientSecret.Name, ""),
  251. ServerURL: validProvider.ServerURL,
  252. },
  253. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  254. errCheck: func(t *testing.T, err error) {
  255. assert.ErrorIs(t, err, errMissingSecretKey)
  256. },
  257. },
  258. "secret ref with non-existent keys": {
  259. provider: &esv1.SecretServerProvider{
  260. Username: makeSecretRefUsingRef(clientSecret.Name, "typo"),
  261. Password: makeSecretRefUsingRef(clientSecret.Name, passwordKey),
  262. ServerURL: validProvider.ServerURL,
  263. },
  264. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  265. errCheck: func(t *testing.T, err error) {
  266. assert.EqualError(t, err, "cannot find secret data for key: \"typo\"")
  267. },
  268. },
  269. "valid secret refs": {
  270. provider: validProvider,
  271. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  272. },
  273. "secret values": {
  274. provider: &esv1.SecretServerProvider{
  275. Username: makeSecretRefUsingValue(userNameValue),
  276. Password: makeSecretRefUsingValue(passwordValue),
  277. ServerURL: validProvider.ServerURL,
  278. },
  279. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  280. },
  281. "cluster secret store": {
  282. store: &esv1.ClusterSecretStore{
  283. TypeMeta: metav1.TypeMeta{Kind: esv1.ClusterSecretStoreKind},
  284. Spec: esv1.SecretStoreSpec{
  285. Provider: &esv1.SecretStoreProvider{
  286. SecretServer: &esv1.SecretServerProvider{
  287. Username: makeSecretRefUsingNamespacedRef(clientSecret.Namespace, clientSecret.Name, userNameKey),
  288. Password: makeSecretRefUsingNamespacedRef(clientSecret.Namespace, clientSecret.Name, passwordKey),
  289. ServerURL: validProvider.ServerURL,
  290. },
  291. },
  292. },
  293. },
  294. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  295. },
  296. "cluster secret store with domain": {
  297. store: &esv1.ClusterSecretStore{
  298. TypeMeta: metav1.TypeMeta{Kind: esv1.ClusterSecretStoreKind},
  299. Spec: esv1.SecretStoreSpec{
  300. Provider: &esv1.SecretStoreProvider{
  301. SecretServer: &esv1.SecretServerProvider{
  302. Username: makeSecretRefUsingNamespacedRef(clientSecretWithDomain.Namespace, clientSecretWithDomain.Name, userNameKey),
  303. Password: makeSecretRefUsingNamespacedRef(clientSecretWithDomain.Namespace, clientSecretWithDomain.Name, passwordKey),
  304. Domain: validProviderWithDomain.Domain,
  305. ServerURL: validProviderWithDomain.ServerURL,
  306. },
  307. },
  308. },
  309. },
  310. kube: clientfake.NewClientBuilder().WithObjects(clientSecret, clientSecretWithDomain).Build(),
  311. },
  312. }
  313. for name, tc := range tests {
  314. t.Run(name, func(t *testing.T) {
  315. p := &Provider{}
  316. store := tc.store
  317. if store == nil {
  318. store = &esv1.SecretStore{
  319. TypeMeta: metav1.TypeMeta{Kind: esv1.SecretStoreKind},
  320. Spec: esv1.SecretStoreSpec{
  321. Provider: &esv1.SecretStoreProvider{
  322. SecretServer: tc.provider,
  323. },
  324. },
  325. }
  326. }
  327. sc, err := p.NewClient(context.Background(), store, tc.kube, clientSecret.Namespace)
  328. if tc.errCheck == nil {
  329. assert.NoError(t, err)
  330. delineaClient, ok := sc.(*client)
  331. assert.True(t, ok)
  332. secretServerClient, ok := delineaClient.api.(*server.Server)
  333. assert.True(t, ok)
  334. expectedCredentials := server.UserCredential{
  335. Username: userNameValue,
  336. Password: passwordValue,
  337. }
  338. if name == "cluster secret store with domain" {
  339. expectedCredentials.Domain = domain
  340. }
  341. assert.Equal(t, expectedCredentials, secretServerClient.Configuration.Credentials)
  342. } else {
  343. assert.Nil(t, sc)
  344. tc.errCheck(t, err)
  345. }
  346. })
  347. }
  348. }
  349. func makeSecretRefUsingNamespacedRef(namespace, name, key string) *esv1.SecretServerProviderRef {
  350. return &esv1.SecretServerProviderRef{
  351. SecretRef: &v1.SecretKeySelector{Namespace: esutils.Ptr(namespace), Name: name, Key: key},
  352. }
  353. }
  354. func makeSecretRefUsingValue(val string) *esv1.SecretServerProviderRef {
  355. return &esv1.SecretServerProviderRef{Value: val}
  356. }
  357. func makeSecretRefUsingRef(name, key string) *esv1.SecretServerProviderRef {
  358. return &esv1.SecretServerProviderRef{
  359. SecretRef: &v1.SecretKeySelector{Name: name, Key: key},
  360. }
  361. }
  362. func generateRandomString() string {
  363. var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
  364. b := make([]rune, 10)
  365. for i := range b {
  366. b[i] = letters[rand.Intn(len(letters))]
  367. }
  368. return string(b)
  369. }