provider_test.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915
  1. /*
  2. Copyright © The ESO Authors
  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. "github.com/stretchr/testify/require"
  21. corev1 "k8s.io/api/core/v1"
  22. kubeErrors "k8s.io/apimachinery/pkg/api/errors"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. kubeClient "sigs.k8s.io/controller-runtime/pkg/client"
  25. clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
  26. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  27. v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
  28. "github.com/external-secrets/external-secrets/runtime/esutils"
  29. )
  30. func TestDoesConfigDependOnNamespace(t *testing.T) {
  31. tests := map[string]struct {
  32. cfg esv1.SecretServerProvider
  33. want bool
  34. }{
  35. "true when Username references a secret without explicit namespace": {
  36. cfg: esv1.SecretServerProvider{
  37. Username: &esv1.SecretServerProviderRef{
  38. SecretRef: &v1.SecretKeySelector{Name: "foo"},
  39. },
  40. Password: &esv1.SecretServerProviderRef{SecretRef: nil},
  41. },
  42. want: true,
  43. },
  44. "true when password references a secret without explicit namespace": {
  45. cfg: esv1.SecretServerProvider{
  46. Username: &esv1.SecretServerProviderRef{SecretRef: nil},
  47. Password: &esv1.SecretServerProviderRef{
  48. SecretRef: &v1.SecretKeySelector{Name: "foo"},
  49. },
  50. },
  51. want: true,
  52. },
  53. "false when neither Username or Password reference a secret": {
  54. cfg: esv1.SecretServerProvider{
  55. Username: &esv1.SecretServerProviderRef{SecretRef: nil},
  56. Password: &esv1.SecretServerProviderRef{SecretRef: nil},
  57. },
  58. want: false,
  59. },
  60. "true when Token references a secret without explicit namespace": {
  61. cfg: esv1.SecretServerProvider{
  62. Token: &esv1.SecretServerProviderRef{
  63. SecretRef: &v1.SecretKeySelector{Name: "foo"},
  64. },
  65. },
  66. want: true,
  67. },
  68. "false when Token uses a direct value": {
  69. cfg: esv1.SecretServerProvider{
  70. Token: &esv1.SecretServerProviderRef{Value: "foo"},
  71. },
  72. want: false,
  73. },
  74. "false when Token has explicit namespace even if Username ref lacks one": {
  75. // Token takes precedence, so the ignored Username ref must not
  76. // introduce a namespace dependency.
  77. cfg: esv1.SecretServerProvider{
  78. Token: &esv1.SecretServerProviderRef{
  79. SecretRef: &v1.SecretKeySelector{Name: "foo", Namespace: new("ns")},
  80. },
  81. Username: &esv1.SecretServerProviderRef{
  82. SecretRef: &v1.SecretKeySelector{Name: "bar"},
  83. },
  84. },
  85. want: false,
  86. },
  87. "true when Token ref lacks a namespace even if Username has one": {
  88. cfg: esv1.SecretServerProvider{
  89. Token: &esv1.SecretServerProviderRef{
  90. SecretRef: &v1.SecretKeySelector{Name: "foo"},
  91. },
  92. Username: &esv1.SecretServerProviderRef{
  93. SecretRef: &v1.SecretKeySelector{Name: "bar", Namespace: new("ns")},
  94. },
  95. },
  96. want: true,
  97. },
  98. }
  99. for name, tc := range tests {
  100. t.Run(name, func(t *testing.T) {
  101. got := doesConfigDependOnNamespace(&tc.cfg)
  102. assert.Equal(t, tc.want, got)
  103. })
  104. }
  105. }
  106. func TestValidateStore(t *testing.T) {
  107. validSecretRefUsingValue := makeSecretRefUsingValue("foo")
  108. ambiguousSecretRef := &esv1.SecretServerProviderRef{
  109. SecretRef: &v1.SecretKeySelector{Name: "foo"}, Value: "foo",
  110. }
  111. testURL := "https://example.com"
  112. tests := map[string]struct {
  113. cfg esv1.SecretServerProvider
  114. want error
  115. }{
  116. "invalid without username": {
  117. cfg: esv1.SecretServerProvider{
  118. Username: nil,
  119. Password: validSecretRefUsingValue,
  120. ServerURL: testURL,
  121. },
  122. want: errEmptyUserName,
  123. },
  124. "invalid without password": {
  125. cfg: esv1.SecretServerProvider{
  126. Username: validSecretRefUsingValue,
  127. Password: nil,
  128. ServerURL: testURL,
  129. },
  130. want: errEmptyPassword,
  131. },
  132. "invalid without serverURL": {
  133. cfg: esv1.SecretServerProvider{
  134. Username: validSecretRefUsingValue,
  135. Password: validSecretRefUsingValue,
  136. /*ServerURL: testURL,*/
  137. },
  138. want: errEmptyServerURL,
  139. },
  140. "invalid with ambiguous Username": {
  141. cfg: esv1.SecretServerProvider{
  142. Username: ambiguousSecretRef,
  143. Password: validSecretRefUsingValue,
  144. ServerURL: testURL,
  145. },
  146. want: esutils.ErrValueAndRefConflict,
  147. },
  148. "invalid with ambiguous Password": {
  149. cfg: esv1.SecretServerProvider{
  150. Username: validSecretRefUsingValue,
  151. Password: ambiguousSecretRef,
  152. ServerURL: testURL,
  153. },
  154. want: esutils.ErrValueAndRefConflict,
  155. },
  156. "invalid with invalid Username": {
  157. cfg: esv1.SecretServerProvider{
  158. Username: makeSecretRefUsingValue(""),
  159. Password: validSecretRefUsingValue,
  160. ServerURL: testURL,
  161. },
  162. want: esutils.ErrValueOrRefMissing,
  163. },
  164. "invalid with invalid Password": {
  165. cfg: esv1.SecretServerProvider{
  166. Username: validSecretRefUsingValue,
  167. Password: makeSecretRefUsingValue(""),
  168. ServerURL: testURL,
  169. },
  170. want: esutils.ErrValueOrRefMissing,
  171. },
  172. "valid with tenant/clientID/clientSecret": {
  173. cfg: esv1.SecretServerProvider{
  174. Username: validSecretRefUsingValue,
  175. Password: validSecretRefUsingValue,
  176. ServerURL: testURL,
  177. },
  178. want: nil,
  179. },
  180. "valid with token and no username/password": {
  181. cfg: esv1.SecretServerProvider{
  182. Token: validSecretRefUsingValue,
  183. ServerURL: testURL,
  184. },
  185. want: nil,
  186. },
  187. "invalid without serverURL when using token": {
  188. cfg: esv1.SecretServerProvider{
  189. Token: validSecretRefUsingValue,
  190. /*ServerURL: testURL,*/
  191. },
  192. want: errEmptyServerURL,
  193. },
  194. "invalid with ambiguous token": {
  195. cfg: esv1.SecretServerProvider{
  196. Token: ambiguousSecretRef,
  197. ServerURL: testURL,
  198. },
  199. want: esutils.ErrValueAndRefConflict,
  200. },
  201. "invalid with invalid token": {
  202. cfg: esv1.SecretServerProvider{
  203. Token: makeSecretRefUsingValue(""),
  204. ServerURL: testURL,
  205. },
  206. want: esutils.ErrValueOrRefMissing,
  207. },
  208. "invalid with zero site ID": {
  209. cfg: esv1.SecretServerProvider{
  210. Token: validSecretRefUsingValue,
  211. ServerURL: testURL,
  212. SiteID: new(0),
  213. },
  214. want: errInvalidSiteID,
  215. },
  216. }
  217. for name, tc := range tests {
  218. t.Run(name, func(t *testing.T) {
  219. s := esv1.SecretStore{
  220. Spec: esv1.SecretStoreSpec{
  221. Provider: &esv1.SecretStoreProvider{
  222. SecretServer: &tc.cfg,
  223. },
  224. },
  225. }
  226. p := &Provider{}
  227. _, got := p.ValidateStore(&s)
  228. assert.Equal(t, tc.want, got)
  229. })
  230. }
  231. }
  232. func TestNewClientSiteIDConfiguration(t *testing.T) {
  233. newProvider := func(siteID *int, disableValidation bool) *esv1.SecretServerProvider {
  234. return &esv1.SecretServerProvider{
  235. Token: makeSecretRefUsingValue("token"),
  236. ServerURL: "https://example.com",
  237. SiteID: siteID,
  238. DisableSiteIDValidation: disableValidation,
  239. }
  240. }
  241. tests := map[string]struct {
  242. store esv1.GenericStore
  243. wantSiteID int
  244. wantDisabled bool
  245. }{
  246. "SecretStore uses default": {
  247. store: &esv1.SecretStore{
  248. TypeMeta: metav1.TypeMeta{Kind: esv1.SecretStoreKind},
  249. Spec: esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{
  250. SecretServer: newProvider(nil, false),
  251. }},
  252. },
  253. wantSiteID: defaultSiteID,
  254. },
  255. "SecretStore uses configured value": {
  256. store: &esv1.SecretStore{
  257. TypeMeta: metav1.TypeMeta{Kind: esv1.SecretStoreKind},
  258. Spec: esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{
  259. SecretServer: newProvider(new(7), false),
  260. }},
  261. },
  262. wantSiteID: 7,
  263. },
  264. "ClusterSecretStore uses configured value": {
  265. store: &esv1.ClusterSecretStore{
  266. TypeMeta: metav1.TypeMeta{Kind: esv1.ClusterSecretStoreKind},
  267. Spec: esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{
  268. SecretServer: newProvider(new(9), false),
  269. }},
  270. },
  271. wantSiteID: 9,
  272. },
  273. "disabled validation preserves missing value": {
  274. store: &esv1.SecretStore{
  275. TypeMeta: metav1.TypeMeta{Kind: esv1.SecretStoreKind},
  276. Spec: esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{
  277. SecretServer: newProvider(nil, true),
  278. }},
  279. },
  280. wantDisabled: true,
  281. },
  282. }
  283. for name, tc := range tests {
  284. t.Run(name, func(t *testing.T) {
  285. provider := &Provider{}
  286. secretsClient, err := provider.NewClient(t.Context(), tc.store, clientfake.NewClientBuilder().Build(), "default")
  287. require.NoError(t, err)
  288. secretServerClient, ok := secretsClient.(*client)
  289. require.True(t, ok)
  290. assert.Equal(t, tc.wantSiteID, secretServerClient.siteID)
  291. assert.Equal(t, tc.wantDisabled, secretServerClient.disableSiteIDValidation)
  292. })
  293. }
  294. }
  295. func TestNewClient(t *testing.T) {
  296. userNameKey := "username"
  297. userNameValue := "foo"
  298. passwordKey := passwordSlug
  299. passwordValue := generateRandomString()
  300. domain := "domain1"
  301. tokenKey := "token"
  302. tokenValue := generateRandomString()
  303. clientSecret := &corev1.Secret{
  304. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
  305. Data: map[string][]byte{
  306. userNameKey: []byte(userNameValue),
  307. passwordKey: []byte(passwordValue),
  308. },
  309. }
  310. tokenSecret := &corev1.Secret{
  311. ObjectMeta: metav1.ObjectMeta{Name: "token-secret", Namespace: "default"},
  312. Data: map[string][]byte{
  313. tokenKey: []byte(tokenValue),
  314. },
  315. }
  316. validProvider := &esv1.SecretServerProvider{
  317. Username: makeSecretRefUsingRef(clientSecret.Name, userNameKey),
  318. Password: makeSecretRefUsingRef(clientSecret.Name, passwordKey),
  319. ServerURL: "https://example.com",
  320. }
  321. clientSecretWithDomain := &corev1.Secret{
  322. ObjectMeta: metav1.ObjectMeta{Name: "with-domain", Namespace: "default"},
  323. Data: map[string][]byte{
  324. userNameKey: []byte(userNameValue),
  325. passwordKey: []byte(passwordValue),
  326. domain: []byte(domain),
  327. },
  328. }
  329. validProviderWithDomain := &esv1.SecretServerProvider{
  330. Username: makeSecretRefUsingRef(clientSecretWithDomain.Name, userNameKey),
  331. Password: makeSecretRefUsingRef(clientSecretWithDomain.Name, passwordKey),
  332. Domain: domain,
  333. ServerURL: "https://example.com",
  334. }
  335. // Valid test CA certificate
  336. testCABundle := []byte(`-----BEGIN CERTIFICATE-----
  337. MIIDHTCCAgWgAwIBAgIRAKC4yxy9QGocND+6avTf7BgwDQYJKoZIhvcNAQELBQAw
  338. EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0yMTAzMjAyMDA4MDhaFw0yMTAzMjAyMDM4
  339. MDhaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
  340. ggEKAoIBAQC3o6/JdZEqNbqNRkopHhJtJG5c4qS5d0tQ/kZYpfD/v/izAYum4Nzj
  341. aG15owr92/11W0pxPUliRLti3y6iScTs+ofm2D7p4UXj/Fnho/2xoWSOoWAodgvW
  342. Y8jh8A0LQALZiV/9QsrJdXZdS47DYZLsQ3z9yFC/CdXkg1l7AQ3fIVGKdrQBr9kE
  343. 1gEDqnKfRxXI8DEQKXr+CKPUwCAytegmy0SHp53zNAvY+kopHytzmJpXLoEhxq4e
  344. ugHe52vXHdh/HJ9VjNp0xOH1waAgAGxHlltCW0PVd5AJ0SXROBS/a3V9sZCbCrJa
  345. YOOonQSEswveSv6PcG9AHvpNPot2Xs6hAgMBAAGjbjBsMA4GA1UdDwEB/wQEAwIC
  346. pDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
  347. BBR00805mrpoonp95RmC3B6oLl+cGTAVBgNVHREEDjAMggpnb29ibGUuY29tMA0G
  348. CSqGSIb3DQEBCwUAA4IBAQAipc1b6JrEDayPjpz5GM5krcI8dCWVd8re0a9bGjjN
  349. ioWGlu/eTr5El0ffwCNZ2WLmL9rewfHf/bMvYz3ioFZJ2OTxfazqYXNggQz6cMfa
  350. lbedDCdt5XLVX2TyerGvFram+9Uyvk3l0uM7rZnwAmdirG4Tv94QRaD3q4xTj/c0
  351. mv+AggtK0aRFb9o47z/BypLdk5mhbf3Mmr88C8XBzEnfdYyf4JpTlZrYLBmDCu5d
  352. 9RLLsjXxhag8xqMtd1uLUM8XOTGzVWacw8iGY+CTtBKqyA+AE6/bDwZvEwVtsKtC
  353. QJ85ioEpy00NioqcF0WyMZH80uMsPycfpnl5uF7RkW8u
  354. -----END CERTIFICATE-----`)
  355. caSecretName := "ca-secret"
  356. caSecretKey := "ca.crt"
  357. caSecret := &corev1.Secret{
  358. ObjectMeta: metav1.ObjectMeta{Name: caSecretName, Namespace: "default"},
  359. Data: map[string][]byte{
  360. caSecretKey: testCABundle,
  361. },
  362. }
  363. caConfigMapName := "ca-configmap"
  364. caConfigMapKey := "ca.crt"
  365. caConfigMap := &corev1.ConfigMap{
  366. ObjectMeta: metav1.ObjectMeta{Name: caConfigMapName, Namespace: "default"},
  367. Data: map[string]string{
  368. caConfigMapKey: string(testCABundle),
  369. },
  370. }
  371. tests := map[string]struct {
  372. store esv1.GenericStore // leave nil for namespaced store
  373. provider *esv1.SecretServerProvider // discarded when store is set
  374. kube kubeClient.Client
  375. errCheck func(t *testing.T, err error)
  376. }{
  377. "missing provider config": {
  378. provider: nil,
  379. errCheck: func(t *testing.T, err error) {
  380. assert.ErrorIs(t, err, errInvalidSpec)
  381. },
  382. },
  383. "namespace-dependent cluster secret store": {
  384. store: &esv1.ClusterSecretStore{
  385. TypeMeta: metav1.TypeMeta{Kind: esv1.ClusterSecretStoreKind},
  386. Spec: esv1.SecretStoreSpec{
  387. Provider: &esv1.SecretStoreProvider{
  388. SecretServer: validProvider,
  389. },
  390. },
  391. },
  392. errCheck: func(t *testing.T, err error) {
  393. assert.ErrorIs(t, err, errClusterStoreRequiresNamespace)
  394. },
  395. },
  396. "dangling password ref": {
  397. provider: &esv1.SecretServerProvider{
  398. Username: validProvider.Username,
  399. Password: makeSecretRefUsingRef("typo", passwordKey),
  400. ServerURL: validProvider.ServerURL,
  401. },
  402. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  403. errCheck: func(t *testing.T, err error) {
  404. assert.True(t, kubeErrors.IsNotFound(err))
  405. },
  406. },
  407. "dangling username ref": {
  408. provider: &esv1.SecretServerProvider{
  409. Username: makeSecretRefUsingRef("typo", userNameKey),
  410. Password: validProvider.Password,
  411. ServerURL: validProvider.ServerURL,
  412. },
  413. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  414. errCheck: func(t *testing.T, err error) {
  415. assert.True(t, kubeErrors.IsNotFound(err))
  416. },
  417. },
  418. "secret ref without name": {
  419. provider: &esv1.SecretServerProvider{
  420. Username: makeSecretRefUsingRef("", userNameKey),
  421. Password: validProvider.Password,
  422. ServerURL: validProvider.ServerURL,
  423. },
  424. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  425. errCheck: func(t *testing.T, err error) {
  426. assert.ErrorIs(t, err, errMissingSecretName)
  427. },
  428. },
  429. "secret ref without key": {
  430. provider: &esv1.SecretServerProvider{
  431. Username: validProvider.Password,
  432. Password: makeSecretRefUsingRef(clientSecret.Name, ""),
  433. ServerURL: validProvider.ServerURL,
  434. },
  435. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  436. errCheck: func(t *testing.T, err error) {
  437. assert.ErrorIs(t, err, errMissingSecretKey)
  438. },
  439. },
  440. "secret ref with non-existent keys": {
  441. provider: &esv1.SecretServerProvider{
  442. Username: makeSecretRefUsingRef(clientSecret.Name, "typo"),
  443. Password: makeSecretRefUsingRef(clientSecret.Name, passwordKey),
  444. ServerURL: validProvider.ServerURL,
  445. },
  446. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  447. errCheck: func(t *testing.T, err error) {
  448. assert.EqualError(t, err, "cannot find secret data for key: \"typo\"")
  449. },
  450. },
  451. "valid secret refs": {
  452. provider: validProvider,
  453. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  454. },
  455. "secret values": {
  456. provider: &esv1.SecretServerProvider{
  457. Username: makeSecretRefUsingValue(userNameValue),
  458. Password: makeSecretRefUsingValue(passwordValue),
  459. ServerURL: validProvider.ServerURL,
  460. },
  461. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  462. },
  463. "valid token via value": {
  464. provider: &esv1.SecretServerProvider{
  465. Token: makeSecretRefUsingValue(tokenValue),
  466. ServerURL: validProvider.ServerURL,
  467. },
  468. kube: clientfake.NewClientBuilder().Build(),
  469. },
  470. "valid token via secret ref": {
  471. provider: &esv1.SecretServerProvider{
  472. Token: makeSecretRefUsingRef(tokenSecret.Name, tokenKey),
  473. ServerURL: validProvider.ServerURL,
  474. },
  475. kube: clientfake.NewClientBuilder().WithObjects(tokenSecret).Build(),
  476. },
  477. "dangling token ref": {
  478. provider: &esv1.SecretServerProvider{
  479. Token: makeSecretRefUsingRef("typo", tokenKey),
  480. ServerURL: validProvider.ServerURL,
  481. },
  482. kube: clientfake.NewClientBuilder().WithObjects(tokenSecret).Build(),
  483. errCheck: func(t *testing.T, err error) {
  484. assert.True(t, kubeErrors.IsNotFound(err))
  485. },
  486. },
  487. "cluster secret store": {
  488. store: &esv1.ClusterSecretStore{
  489. TypeMeta: metav1.TypeMeta{Kind: esv1.ClusterSecretStoreKind},
  490. Spec: esv1.SecretStoreSpec{
  491. Provider: &esv1.SecretStoreProvider{
  492. SecretServer: &esv1.SecretServerProvider{
  493. Username: makeSecretRefUsingNamespacedRef(clientSecret.Namespace, clientSecret.Name, userNameKey),
  494. Password: makeSecretRefUsingNamespacedRef(clientSecret.Namespace, clientSecret.Name, passwordKey),
  495. ServerURL: validProvider.ServerURL,
  496. },
  497. },
  498. },
  499. },
  500. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  501. },
  502. "cluster secret store with domain": {
  503. store: &esv1.ClusterSecretStore{
  504. TypeMeta: metav1.TypeMeta{Kind: esv1.ClusterSecretStoreKind},
  505. Spec: esv1.SecretStoreSpec{
  506. Provider: &esv1.SecretStoreProvider{
  507. SecretServer: &esv1.SecretServerProvider{
  508. Username: makeSecretRefUsingNamespacedRef(clientSecretWithDomain.Namespace, clientSecretWithDomain.Name, userNameKey),
  509. Password: makeSecretRefUsingNamespacedRef(clientSecretWithDomain.Namespace, clientSecretWithDomain.Name, passwordKey),
  510. Domain: validProviderWithDomain.Domain,
  511. ServerURL: validProviderWithDomain.ServerURL,
  512. },
  513. },
  514. },
  515. },
  516. kube: clientfake.NewClientBuilder().WithObjects(clientSecret, clientSecretWithDomain).Build(),
  517. },
  518. "valid with CABundle and CAProvider using Secret": {
  519. provider: &esv1.SecretServerProvider{
  520. Username: validProvider.Username,
  521. Password: validProvider.Password,
  522. ServerURL: validProvider.ServerURL,
  523. CABundle: testCABundle,
  524. CAProvider: &esv1.CAProvider{
  525. Type: esv1.CAProviderTypeSecret,
  526. Name: caSecretName,
  527. Key: caSecretKey,
  528. },
  529. },
  530. kube: clientfake.NewClientBuilder().WithObjects(clientSecret, caSecret).Build(),
  531. },
  532. "valid with CABundle and CAProvider using ConfigMap": {
  533. provider: &esv1.SecretServerProvider{
  534. Username: validProvider.Username,
  535. Password: validProvider.Password,
  536. ServerURL: validProvider.ServerURL,
  537. CABundle: testCABundle,
  538. CAProvider: &esv1.CAProvider{
  539. Type: esv1.CAProviderTypeConfigMap,
  540. Name: caConfigMapName,
  541. Key: caConfigMapKey,
  542. },
  543. },
  544. kube: clientfake.NewClientBuilder().WithObjects(clientSecret, caConfigMap).Build(),
  545. },
  546. "CABundle without CAProvider is ignored": {
  547. provider: &esv1.SecretServerProvider{
  548. Username: validProvider.Username,
  549. Password: validProvider.Password,
  550. ServerURL: validProvider.ServerURL,
  551. CABundle: testCABundle,
  552. },
  553. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  554. },
  555. "CAProvider without CABundle is ignored": {
  556. provider: &esv1.SecretServerProvider{
  557. Username: validProvider.Username,
  558. Password: validProvider.Password,
  559. ServerURL: validProvider.ServerURL,
  560. CAProvider: &esv1.CAProvider{
  561. Type: esv1.CAProviderTypeSecret,
  562. Name: caSecretName,
  563. Key: caSecretKey,
  564. },
  565. },
  566. kube: clientfake.NewClientBuilder().WithObjects(clientSecret, caSecret).Build(),
  567. },
  568. "invalid CABundle format with CAProvider": {
  569. provider: &esv1.SecretServerProvider{
  570. Username: validProvider.Username,
  571. Password: validProvider.Password,
  572. ServerURL: validProvider.ServerURL,
  573. CABundle: []byte("invalid certificate data"),
  574. CAProvider: &esv1.CAProvider{
  575. Type: esv1.CAProviderTypeSecret,
  576. Name: caSecretName,
  577. Key: caSecretKey,
  578. },
  579. },
  580. kube: clientfake.NewClientBuilder().WithObjects(clientSecret, caSecret).Build(),
  581. errCheck: func(t *testing.T, err error) {
  582. assert.Error(t, err)
  583. assert.Contains(t, err.Error(), "failed to decode ca bundle")
  584. },
  585. },
  586. "missing CAProvider Secret with valid CABundle": {
  587. provider: &esv1.SecretServerProvider{
  588. Username: validProvider.Username,
  589. Password: validProvider.Password,
  590. ServerURL: validProvider.ServerURL,
  591. CABundle: testCABundle,
  592. CAProvider: &esv1.CAProvider{
  593. Type: esv1.CAProviderTypeSecret,
  594. Name: "non-existent-secret",
  595. Key: caSecretKey,
  596. },
  597. },
  598. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  599. // CABundle takes precedence, so even if the secret doesn't exist, CABundle is used
  600. },
  601. "only CAProvider without CABundle is ignored": {
  602. provider: &esv1.SecretServerProvider{
  603. Username: validProvider.Username,
  604. Password: validProvider.Password,
  605. ServerURL: validProvider.ServerURL,
  606. CAProvider: &esv1.CAProvider{
  607. Type: esv1.CAProviderTypeSecret,
  608. Name: caSecretName,
  609. Key: caSecretKey,
  610. },
  611. },
  612. kube: clientfake.NewClientBuilder().WithObjects(clientSecret, caSecret).Build(),
  613. // No error expected because both CABundle AND CAProvider must be set for TLS config
  614. },
  615. "cluster secret store with CABundle and CAProvider": {
  616. store: &esv1.ClusterSecretStore{
  617. TypeMeta: metav1.TypeMeta{Kind: esv1.ClusterSecretStoreKind},
  618. Spec: esv1.SecretStoreSpec{
  619. Provider: &esv1.SecretStoreProvider{
  620. SecretServer: &esv1.SecretServerProvider{
  621. Username: makeSecretRefUsingNamespacedRef(clientSecret.Namespace, clientSecret.Name, userNameKey),
  622. Password: makeSecretRefUsingNamespacedRef(clientSecret.Namespace, clientSecret.Name, passwordKey),
  623. ServerURL: validProvider.ServerURL,
  624. CABundle: testCABundle,
  625. CAProvider: &esv1.CAProvider{
  626. Type: esv1.CAProviderTypeSecret,
  627. Name: caSecretName,
  628. Key: caSecretKey,
  629. Namespace: new("default"),
  630. },
  631. },
  632. },
  633. },
  634. },
  635. kube: clientfake.NewClientBuilder().WithObjects(clientSecret, caSecret).Build(),
  636. },
  637. }
  638. for name, tc := range tests {
  639. t.Run(name, func(t *testing.T) {
  640. p := &Provider{}
  641. store := tc.store
  642. if store == nil {
  643. store = &esv1.SecretStore{
  644. TypeMeta: metav1.TypeMeta{Kind: esv1.SecretStoreKind},
  645. Spec: esv1.SecretStoreSpec{
  646. Provider: &esv1.SecretStoreProvider{
  647. SecretServer: tc.provider,
  648. },
  649. },
  650. }
  651. }
  652. sc, err := p.NewClient(context.Background(), store, tc.kube, clientSecret.Namespace)
  653. if tc.errCheck == nil {
  654. assert.NoError(t, err)
  655. delineaClient, ok := sc.(*client)
  656. assert.True(t, ok)
  657. secretServerClient, ok := delineaClient.api.(*server.Server)
  658. assert.True(t, ok)
  659. expectedCredentials := server.UserCredential{
  660. Username: userNameValue,
  661. Password: passwordValue,
  662. }
  663. switch name {
  664. case "cluster secret store with domain":
  665. expectedCredentials.Domain = domain
  666. case "valid token via value", "valid token via secret ref":
  667. expectedCredentials = server.UserCredential{Token: tokenValue}
  668. }
  669. assert.Equal(t, expectedCredentials, secretServerClient.Configuration.Credentials)
  670. } else {
  671. assert.Nil(t, sc)
  672. tc.errCheck(t, err)
  673. }
  674. })
  675. }
  676. }
  677. func makeSecretRefUsingNamespacedRef(namespace, name, key string) *esv1.SecretServerProviderRef {
  678. return &esv1.SecretServerProviderRef{
  679. SecretRef: &v1.SecretKeySelector{Namespace: new(namespace), Name: name, Key: key},
  680. }
  681. }
  682. func makeSecretRefUsingValue(val string) *esv1.SecretServerProviderRef {
  683. return &esv1.SecretServerProviderRef{Value: val}
  684. }
  685. func makeSecretRefUsingRef(name, key string) *esv1.SecretServerProviderRef {
  686. return &esv1.SecretServerProviderRef{
  687. SecretRef: &v1.SecretKeySelector{Name: name, Key: key},
  688. }
  689. }
  690. func generateRandomString() string {
  691. var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
  692. b := make([]rune, 10)
  693. for i := range b {
  694. b[i] = letters[rand.Intn(len(letters))]
  695. }
  696. return string(b)
  697. }
  698. // TestValidateStoreSecretRef tests the validateStoreSecretRef function.
  699. func TestValidateStoreSecretRef(t *testing.T) {
  700. tests := map[string]struct {
  701. store esv1.GenericStore
  702. ref *esv1.SecretServerProviderRef
  703. wantErr error
  704. }{
  705. "valid secret ref for SecretStore": {
  706. store: &esv1.SecretStore{
  707. ObjectMeta: metav1.ObjectMeta{
  708. Name: "test-store",
  709. Namespace: "default",
  710. },
  711. },
  712. ref: &esv1.SecretServerProviderRef{
  713. SecretRef: &v1.SecretKeySelector{
  714. Name: "secret-name",
  715. Key: "secret-key",
  716. },
  717. },
  718. wantErr: nil,
  719. },
  720. "error when secret ref missing name": {
  721. store: &esv1.SecretStore{
  722. ObjectMeta: metav1.ObjectMeta{
  723. Name: "test-store",
  724. Namespace: "default",
  725. },
  726. },
  727. ref: &esv1.SecretServerProviderRef{
  728. SecretRef: &v1.SecretKeySelector{
  729. Name: "",
  730. Key: "secret-key",
  731. },
  732. },
  733. wantErr: errMissingSecretName,
  734. },
  735. "error when secret ref missing key": {
  736. store: &esv1.SecretStore{
  737. ObjectMeta: metav1.ObjectMeta{
  738. Name: "test-store",
  739. Namespace: "default",
  740. },
  741. },
  742. ref: &esv1.SecretServerProviderRef{
  743. SecretRef: &v1.SecretKeySelector{
  744. Name: "secret-name",
  745. Key: "",
  746. },
  747. },
  748. wantErr: errMissingSecretKey,
  749. },
  750. "error when both value and secret ref are set": {
  751. store: &esv1.SecretStore{
  752. ObjectMeta: metav1.ObjectMeta{
  753. Name: "test-store",
  754. Namespace: "default",
  755. },
  756. },
  757. ref: &esv1.SecretServerProviderRef{
  758. SecretRef: &v1.SecretKeySelector{
  759. Name: "secret-name",
  760. Key: "secret-key",
  761. },
  762. Value: "some-value",
  763. },
  764. wantErr: esutils.ErrValueAndRefConflict,
  765. },
  766. }
  767. for name, tc := range tests {
  768. t.Run(name, func(t *testing.T) {
  769. err := validateStoreSecretRef(tc.store, tc.ref)
  770. if tc.wantErr == nil {
  771. assert.NoError(t, err)
  772. } else {
  773. assert.ErrorIs(t, err, tc.wantErr)
  774. }
  775. })
  776. }
  777. }
  778. // TestLoadConfigSecretReferentValidation ensures the credential-loading path
  779. // enforces esutils referent validation. A namespaced SecretStore must not be
  780. // able to resolve a secret that lives in a different namespace, even if that
  781. // secret exists — this is the exfiltration guard.
  782. func TestLoadConfigSecretReferentValidation(t *testing.T) {
  783. const storeNamespace = "default"
  784. const otherNamespace = "other-ns"
  785. // A secret in a namespace the store must NOT be allowed to read.
  786. stolenSecret := &corev1.Secret{
  787. ObjectMeta: metav1.ObjectMeta{Name: "stolen", Namespace: otherNamespace},
  788. Data: map[string][]byte{"token": []byte("super-secret")},
  789. }
  790. kube := clientfake.NewClientBuilder().WithObjects(stolenSecret).Build()
  791. // Namespaced SecretStore living in storeNamespace.
  792. store := &esv1.SecretStore{
  793. TypeMeta: metav1.TypeMeta{Kind: esv1.SecretStoreKind},
  794. ObjectMeta: metav1.ObjectMeta{Namespace: storeNamespace},
  795. }
  796. // Token ref that tries to reach into the other namespace.
  797. ref := makeSecretRefUsingNamespacedRef(otherNamespace, stolenSecret.Name, "token")
  798. val, err := loadConfigSecret(context.Background(), store, ref, kube, storeNamespace)
  799. assert.Error(t, err)
  800. assert.Empty(t, val, "guard must block resolution before the value is read")
  801. assert.ErrorContains(t, err, "namespace should either be empty or match")
  802. }
  803. // TestCapabilities tests the Capabilities function.
  804. func TestCapabilities(t *testing.T) {
  805. tests := map[string]struct {
  806. want esv1.SecretStoreCapabilities
  807. }{
  808. "returns ReadWrite capability": {
  809. want: esv1.SecretStoreReadWrite,
  810. },
  811. }
  812. for name, tc := range tests {
  813. t.Run(name, func(t *testing.T) {
  814. p := &Provider{}
  815. got := p.Capabilities()
  816. assert.Equal(t, tc.want, got)
  817. // Edge: call Capabilities on nil Provider
  818. var nilP *Provider
  819. assert.Equal(t, esv1.SecretStoreReadWrite, nilP.Capabilities())
  820. })
  821. }
  822. }
  823. // TestNewProvider tests the NewProvider function.
  824. func TestNewProvider(t *testing.T) {
  825. tests := map[string]struct {
  826. want esv1.Provider
  827. }{
  828. "creates a new provider instance": {
  829. want: &Provider{},
  830. },
  831. }
  832. for name, tc := range tests {
  833. t.Run(name, func(t *testing.T) {
  834. got := NewProvider()
  835. assert.NotNil(t, got)
  836. assert.IsType(t, tc.want, got)
  837. // Edge: call NewProvider multiple times
  838. got2 := NewProvider()
  839. assert.IsType(t, tc.want, got2)
  840. })
  841. }
  842. }
  843. // TestProviderSpec tests the ProviderSpec function.
  844. func TestProviderSpec(t *testing.T) {
  845. tests := map[string]struct {
  846. wantType *esv1.SecretStoreProvider
  847. }{
  848. "returns correct provider spec": {
  849. wantType: &esv1.SecretStoreProvider{
  850. SecretServer: &esv1.SecretServerProvider{},
  851. },
  852. },
  853. }
  854. for name, tc := range tests {
  855. t.Run(name, func(t *testing.T) {
  856. got := ProviderSpec()
  857. assert.NotNil(t, got)
  858. assert.NotNil(t, got.SecretServer)
  859. assert.IsType(t, tc.wantType, got)
  860. // Ensure ProviderSpec returns a fresh instance (no shared mutable state)
  861. // Mutate the returned object and verify a subsequent call is unaffected.
  862. got.SecretServer.ServerURL = "http://modified.local"
  863. got2 := ProviderSpec()
  864. assert.IsType(t, tc.wantType, got2)
  865. // If ProviderSpec reused a shared object, this would be equal.
  866. assert.NotEqual(t, got.SecretServer.ServerURL, got2.SecretServer.ServerURL)
  867. })
  868. }
  869. }