client_test.go 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package bitwarden
  13. import (
  14. "context"
  15. "reflect"
  16. "testing"
  17. "github.com/stretchr/testify/assert"
  18. corev1 "k8s.io/api/core/v1"
  19. "sigs.k8s.io/controller-runtime/pkg/client"
  20. "sigs.k8s.io/controller-runtime/pkg/client/fake"
  21. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  22. "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  23. )
  24. const (
  25. remoteID = "d8f29773-3019-4973-9bbc-66327d077fe2"
  26. testKey = "this-is-a-name"
  27. )
  28. var projectID = "e8fc8f9c-2208-446e-9e89-9bc358f39b47"
  29. func TestProviderDeleteSecret(t *testing.T) {
  30. type fields struct {
  31. kube client.Client
  32. namespace string
  33. store esv1.GenericStore
  34. mock func(c *FakeClient)
  35. assertMock func(t *testing.T, c *FakeClient)
  36. }
  37. type args struct {
  38. ctx context.Context
  39. ref esv1.PushSecretRemoteRef
  40. }
  41. tests := []struct {
  42. name string
  43. fields fields
  44. args args
  45. wantErr bool
  46. }{
  47. {
  48. name: "delete secret is successfully with UUID",
  49. fields: fields{
  50. namespace: "default",
  51. store: &esv1.SecretStore{
  52. Spec: esv1.SecretStoreSpec{
  53. Provider: &esv1.SecretStoreProvider{
  54. BitwardenSecretsManager: &esv1.BitwardenSecretsManagerProvider{
  55. OrganizationID: "orgid",
  56. ProjectID: projectID,
  57. },
  58. },
  59. },
  60. },
  61. mock: func(c *FakeClient) {
  62. c.DeleteSecretReturnsOnCallN(0, &SecretsDeleteResponse{})
  63. },
  64. assertMock: func(t *testing.T, c *FakeClient) {
  65. assert.Equal(t, 1, c.deleteSecretCalledN)
  66. },
  67. },
  68. args: args{
  69. ctx: context.TODO(),
  70. ref: v1alpha1.PushSecretRemoteRef{
  71. RemoteKey: remoteID,
  72. },
  73. },
  74. },
  75. {
  76. name: "delete secret by name",
  77. fields: fields{
  78. namespace: "default",
  79. store: &esv1.SecretStore{
  80. Spec: esv1.SecretStoreSpec{
  81. Provider: &esv1.SecretStoreProvider{
  82. BitwardenSecretsManager: &esv1.BitwardenSecretsManagerProvider{
  83. OrganizationID: "orgid",
  84. ProjectID: projectID,
  85. },
  86. },
  87. },
  88. },
  89. mock: func(c *FakeClient) {
  90. c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
  91. Data: []SecretIdentifierResponse{
  92. {
  93. ID: remoteID,
  94. Key: testKey,
  95. OrganizationID: "orgid",
  96. },
  97. },
  98. })
  99. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  100. ID: remoteID,
  101. Key: "key",
  102. Note: "note",
  103. OrganizationID: "org",
  104. Value: "value",
  105. ProjectID: &projectID,
  106. })
  107. c.DeleteSecretReturnsOnCallN(0, &SecretsDeleteResponse{})
  108. },
  109. assertMock: func(t *testing.T, c *FakeClient) {
  110. assert.Equal(t, 1, c.deleteSecretCalledN)
  111. },
  112. },
  113. args: args{
  114. ctx: context.TODO(),
  115. ref: v1alpha1.PushSecretRemoteRef{
  116. RemoteKey: remoteID,
  117. },
  118. },
  119. },
  120. {
  121. name: "delete secret by name will not delete if something doesn't match",
  122. fields: fields{
  123. namespace: "default",
  124. store: &esv1.SecretStore{
  125. Spec: esv1.SecretStoreSpec{
  126. Provider: &esv1.SecretStoreProvider{
  127. BitwardenSecretsManager: &esv1.BitwardenSecretsManagerProvider{
  128. OrganizationID: "orgid",
  129. ProjectID: projectID,
  130. },
  131. },
  132. },
  133. },
  134. mock: func(c *FakeClient) {
  135. c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
  136. Data: []SecretIdentifierResponse{
  137. {
  138. ID: remoteID,
  139. Key: testKey,
  140. OrganizationID: "orgid",
  141. },
  142. },
  143. })
  144. projectID := "another-project"
  145. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  146. ID: remoteID,
  147. Key: testKey,
  148. Note: "note",
  149. OrganizationID: "orgid",
  150. Value: "value",
  151. ProjectID: &projectID,
  152. })
  153. },
  154. assertMock: func(t *testing.T, c *FakeClient) {
  155. assert.Equal(t, 0, c.deleteSecretCalledN)
  156. },
  157. },
  158. wantErr: true, // no secret found
  159. args: args{
  160. ctx: context.TODO(),
  161. ref: v1alpha1.PushSecretRemoteRef{
  162. RemoteKey: testKey,
  163. },
  164. },
  165. },
  166. }
  167. for _, tt := range tests {
  168. t.Run(tt.name, func(t *testing.T) {
  169. fakeClient := &FakeClient{}
  170. tt.fields.mock(fakeClient)
  171. p := &Provider{
  172. kube: tt.fields.kube,
  173. namespace: tt.fields.namespace,
  174. store: tt.fields.store,
  175. bitwardenSdkClient: fakeClient,
  176. }
  177. if err := p.DeleteSecret(tt.args.ctx, tt.args.ref); (err != nil) != tt.wantErr {
  178. t.Errorf("DeleteSecret() error = %v, wantErr %v", err, tt.wantErr)
  179. }
  180. tt.fields.assertMock(t, fakeClient)
  181. })
  182. }
  183. }
  184. func TestProviderGetAllSecrets(t *testing.T) {
  185. type fields struct {
  186. kube client.Client
  187. namespace string
  188. store esv1.GenericStore
  189. mock func(c *FakeClient)
  190. }
  191. type args struct {
  192. ctx context.Context
  193. ref esv1.ExternalSecretFind
  194. }
  195. tests := []struct {
  196. name string
  197. fields fields
  198. args args
  199. want map[string][]byte
  200. wantErr bool
  201. }{
  202. {
  203. name: "get all secrets",
  204. fields: fields{
  205. namespace: "default",
  206. store: &esv1.SecretStore{
  207. Spec: esv1.SecretStoreSpec{
  208. Provider: &esv1.SecretStoreProvider{
  209. BitwardenSecretsManager: &esv1.BitwardenSecretsManagerProvider{
  210. OrganizationID: "orgid",
  211. ProjectID: projectID,
  212. },
  213. },
  214. },
  215. },
  216. mock: func(c *FakeClient) {
  217. c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
  218. Data: []SecretIdentifierResponse{
  219. {
  220. ID: remoteID,
  221. Key: "key1",
  222. OrganizationID: "orgid",
  223. },
  224. {
  225. ID: "7c0d21ec-10d9-4972-bdf8-ec52df99cc86",
  226. Key: "key2",
  227. OrganizationID: "orgid",
  228. },
  229. },
  230. })
  231. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  232. ID: remoteID,
  233. Key: "key1",
  234. Value: "value1",
  235. })
  236. c.GetSecretReturnsOnCallN(1, &SecretResponse{
  237. ID: "7c0d21ec-10d9-4972-bdf8-ec52df99cc86",
  238. Key: "key2",
  239. Value: "value2",
  240. })
  241. },
  242. },
  243. args: args{
  244. ctx: context.TODO(),
  245. ref: esv1.ExternalSecretFind{},
  246. },
  247. want: map[string][]byte{
  248. remoteID: []byte("value1"),
  249. "7c0d21ec-10d9-4972-bdf8-ec52df99cc86": []byte("value2"),
  250. },
  251. },
  252. }
  253. for _, tt := range tests {
  254. t.Run(tt.name, func(t *testing.T) {
  255. fakeClient := &FakeClient{}
  256. tt.fields.mock(fakeClient)
  257. p := &Provider{
  258. kube: tt.fields.kube,
  259. namespace: tt.fields.namespace,
  260. store: tt.fields.store,
  261. bitwardenSdkClient: fakeClient,
  262. }
  263. got, err := p.GetAllSecrets(tt.args.ctx, tt.args.ref)
  264. if (err != nil) != tt.wantErr {
  265. t.Errorf("GetAllSecrets() error = %v, wantErr %v", err, tt.wantErr)
  266. return
  267. }
  268. if !reflect.DeepEqual(got, tt.want) {
  269. t.Errorf("GetAllSecrets() got = %v, want %v", got, tt.want)
  270. }
  271. })
  272. }
  273. }
  274. func TestProviderGetSecret(t *testing.T) {
  275. type fields struct {
  276. kube func() client.Client
  277. namespace string
  278. store esv1.GenericStore
  279. mock func(c *FakeClient)
  280. }
  281. type args struct {
  282. ctx context.Context
  283. ref esv1.ExternalSecretDataRemoteRef
  284. }
  285. tests := []struct {
  286. name string
  287. fields fields
  288. args args
  289. want []byte
  290. wantErr bool
  291. }{
  292. {
  293. name: "get secret with UUID",
  294. fields: fields{
  295. kube: func() client.Client {
  296. return fake.NewFakeClient()
  297. },
  298. namespace: "default",
  299. store: &esv1.SecretStore{},
  300. mock: func(c *FakeClient) {
  301. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  302. ID: "id",
  303. Key: "key",
  304. Note: "note",
  305. OrganizationID: "org",
  306. Value: "value",
  307. })
  308. },
  309. },
  310. args: args{
  311. ctx: context.Background(),
  312. ref: esv1.ExternalSecretDataRemoteRef{
  313. Key: remoteID,
  314. },
  315. },
  316. want: []byte("value"),
  317. },
  318. {
  319. name: "get secret by name",
  320. fields: fields{
  321. kube: func() client.Client {
  322. return fake.NewFakeClient()
  323. },
  324. namespace: "default",
  325. store: &esv1.SecretStore{
  326. Spec: esv1.SecretStoreSpec{
  327. Provider: &esv1.SecretStoreProvider{
  328. BitwardenSecretsManager: &esv1.BitwardenSecretsManagerProvider{
  329. OrganizationID: "orgid",
  330. ProjectID: projectID,
  331. },
  332. },
  333. },
  334. },
  335. mock: func(c *FakeClient) {
  336. c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
  337. Data: []SecretIdentifierResponse{
  338. {
  339. ID: remoteID,
  340. Key: testKey,
  341. OrganizationID: "orgid",
  342. },
  343. },
  344. })
  345. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  346. ID: remoteID,
  347. Key: "key",
  348. Note: "note",
  349. OrganizationID: "org",
  350. Value: "value",
  351. ProjectID: &projectID,
  352. })
  353. },
  354. },
  355. args: args{
  356. ctx: context.Background(),
  357. ref: esv1.ExternalSecretDataRemoteRef{
  358. Key: testKey,
  359. },
  360. },
  361. want: []byte("value"),
  362. },
  363. }
  364. for _, tt := range tests {
  365. t.Run(tt.name, func(t *testing.T) {
  366. fakeClient := &FakeClient{}
  367. tt.fields.mock(fakeClient)
  368. p := &Provider{
  369. kube: tt.fields.kube(),
  370. namespace: tt.fields.namespace,
  371. store: tt.fields.store,
  372. bitwardenSdkClient: fakeClient,
  373. }
  374. got, err := p.GetSecret(tt.args.ctx, tt.args.ref)
  375. if (err != nil) != tt.wantErr {
  376. t.Errorf("GetSecret() error = %v, wantErr %v", err, tt.wantErr)
  377. return
  378. }
  379. if !reflect.DeepEqual(got, tt.want) {
  380. t.Errorf("GetSecret() got = %v, want %v", got, tt.want)
  381. }
  382. })
  383. }
  384. }
  385. func TestProviderPushSecret(t *testing.T) {
  386. type fields struct {
  387. kube func() client.Client
  388. namespace string
  389. store esv1.GenericStore
  390. mock func(c *FakeClient)
  391. assertMock func(t *testing.T, c *FakeClient)
  392. }
  393. type args struct {
  394. ctx context.Context
  395. secret *corev1.Secret
  396. data esv1.PushSecretData
  397. }
  398. tests := []struct {
  399. name string
  400. fields fields
  401. args args
  402. wantErr bool
  403. }{
  404. {
  405. name: "push secret is successful for a none existent remote secret",
  406. args: args{
  407. ctx: context.Background(),
  408. secret: &corev1.Secret{
  409. Data: map[string][]byte{
  410. "key": []byte("value"),
  411. },
  412. },
  413. data: v1alpha1.PushSecretData{
  414. Match: v1alpha1.PushSecretMatch{
  415. SecretKey: "key",
  416. RemoteRef: v1alpha1.PushSecretRemoteRef{
  417. RemoteKey: testKey,
  418. },
  419. },
  420. },
  421. },
  422. fields: fields{
  423. kube: func() client.Client {
  424. return fake.NewFakeClient()
  425. },
  426. namespace: "default",
  427. store: &esv1.SecretStore{
  428. Spec: esv1.SecretStoreSpec{
  429. Provider: &esv1.SecretStoreProvider{
  430. BitwardenSecretsManager: &esv1.BitwardenSecretsManagerProvider{
  431. OrganizationID: "orgid",
  432. ProjectID: projectID,
  433. },
  434. },
  435. },
  436. },
  437. mock: func(c *FakeClient) {
  438. c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
  439. Data: []SecretIdentifierResponse{
  440. {
  441. ID: remoteID,
  442. Key: testKey,
  443. OrganizationID: "orgid",
  444. },
  445. },
  446. })
  447. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  448. ID: remoteID,
  449. Key: "no-match", // if this is this-is-a-name it would match
  450. Note: "",
  451. OrganizationID: "orgid",
  452. Value: "value",
  453. ProjectID: &projectID,
  454. })
  455. c.CreateSecretReturnsOnCallN(0, &SecretResponse{})
  456. },
  457. assertMock: func(t *testing.T, c *FakeClient) {
  458. cargs := c.createSecretCallArguments[0]
  459. assert.Equal(t, cargs, SecretCreateRequest{
  460. Key: testKey,
  461. Note: "",
  462. OrganizationID: "orgid",
  463. ProjectIDS: []string{projectID},
  464. Value: "value",
  465. })
  466. },
  467. },
  468. },
  469. {
  470. name: "push entire secret succeeds",
  471. args: args{
  472. ctx: context.Background(),
  473. secret: &corev1.Secret{
  474. Data: map[string][]byte{
  475. "key": []byte("value"),
  476. },
  477. },
  478. data: v1alpha1.PushSecretData{
  479. Match: v1alpha1.PushSecretMatch{
  480. RemoteRef: v1alpha1.PushSecretRemoteRef{
  481. RemoteKey: testKey,
  482. },
  483. },
  484. },
  485. },
  486. fields: fields{
  487. kube: func() client.Client {
  488. return fake.NewFakeClient()
  489. },
  490. namespace: "default",
  491. store: &esv1.SecretStore{
  492. Spec: esv1.SecretStoreSpec{
  493. Provider: &esv1.SecretStoreProvider{
  494. BitwardenSecretsManager: &esv1.BitwardenSecretsManagerProvider{
  495. OrganizationID: "orgid",
  496. ProjectID: projectID,
  497. },
  498. },
  499. },
  500. },
  501. mock: func(c *FakeClient) {
  502. c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
  503. Data: []SecretIdentifierResponse{
  504. {
  505. ID: remoteID,
  506. Key: testKey,
  507. OrganizationID: "orgid",
  508. },
  509. },
  510. })
  511. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  512. ID: remoteID,
  513. Key: "no-match", // if this is this-is-a-name it would match
  514. Note: "",
  515. OrganizationID: "orgid",
  516. Value: "value",
  517. ProjectID: &projectID,
  518. })
  519. c.CreateSecretReturnsOnCallN(0, &SecretResponse{})
  520. },
  521. assertMock: func(t *testing.T, c *FakeClient) {
  522. cargs := c.createSecretCallArguments[0]
  523. assert.Equal(t, SecretCreateRequest{
  524. Key: testKey,
  525. Note: "",
  526. OrganizationID: "orgid",
  527. ProjectIDS: []string{projectID},
  528. Value: `{"key":"value"}`,
  529. }, cargs)
  530. },
  531. },
  532. },
  533. {
  534. name: "push secret is successful for an existing remote secret but only the value differs will call update",
  535. args: args{
  536. ctx: context.Background(),
  537. secret: &corev1.Secret{
  538. Data: map[string][]byte{
  539. "key": []byte("new-value"),
  540. },
  541. },
  542. data: v1alpha1.PushSecretData{
  543. Match: v1alpha1.PushSecretMatch{
  544. SecretKey: "key",
  545. RemoteRef: v1alpha1.PushSecretRemoteRef{
  546. RemoteKey: testKey,
  547. },
  548. },
  549. },
  550. },
  551. fields: fields{
  552. kube: func() client.Client {
  553. return fake.NewFakeClient()
  554. },
  555. namespace: "default",
  556. store: &esv1.SecretStore{
  557. Spec: esv1.SecretStoreSpec{
  558. Provider: &esv1.SecretStoreProvider{
  559. BitwardenSecretsManager: &esv1.BitwardenSecretsManagerProvider{
  560. OrganizationID: "orgid",
  561. ProjectID: projectID,
  562. },
  563. },
  564. },
  565. },
  566. mock: func(c *FakeClient) {
  567. c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
  568. Data: []SecretIdentifierResponse{
  569. {
  570. ID: remoteID,
  571. Key: testKey,
  572. OrganizationID: "orgid",
  573. },
  574. },
  575. })
  576. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  577. ID: remoteID,
  578. Key: testKey,
  579. Note: "",
  580. OrganizationID: "orgid",
  581. Value: "value",
  582. ProjectID: &projectID,
  583. })
  584. c.UpdateSecretReturnsOnCallN(0, &SecretResponse{})
  585. },
  586. assertMock: func(t *testing.T, c *FakeClient) {
  587. pargs := c.updateSecretCallArguments[0]
  588. assert.Equal(t, pargs, SecretPutRequest{
  589. ID: remoteID,
  590. Key: testKey,
  591. Note: "",
  592. OrganizationID: "orgid",
  593. ProjectIDS: []string{projectID},
  594. Value: "new-value",
  595. })
  596. },
  597. },
  598. },
  599. {
  600. name: "push secret will not push if the same secret already exists",
  601. args: args{
  602. ctx: context.Background(),
  603. secret: &corev1.Secret{
  604. Data: map[string][]byte{
  605. "key": []byte("value"),
  606. },
  607. },
  608. data: v1alpha1.PushSecretData{
  609. Match: v1alpha1.PushSecretMatch{
  610. SecretKey: "key",
  611. RemoteRef: v1alpha1.PushSecretRemoteRef{
  612. RemoteKey: testKey,
  613. },
  614. },
  615. },
  616. },
  617. fields: fields{
  618. kube: func() client.Client {
  619. return fake.NewFakeClient()
  620. },
  621. namespace: "default",
  622. store: &esv1.SecretStore{
  623. Spec: esv1.SecretStoreSpec{
  624. Provider: &esv1.SecretStoreProvider{
  625. BitwardenSecretsManager: &esv1.BitwardenSecretsManagerProvider{
  626. OrganizationID: "orgid",
  627. ProjectID: projectID,
  628. },
  629. },
  630. },
  631. },
  632. mock: func(c *FakeClient) {
  633. c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
  634. Data: []SecretIdentifierResponse{
  635. {
  636. ID: remoteID,
  637. Key: testKey,
  638. OrganizationID: "orgid",
  639. },
  640. },
  641. })
  642. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  643. ID: remoteID,
  644. Key: testKey,
  645. OrganizationID: "orgid",
  646. Value: "value",
  647. ProjectID: &projectID,
  648. })
  649. c.UpdateSecretReturnsOnCallN(0, &SecretResponse{})
  650. },
  651. assertMock: func(t *testing.T, c *FakeClient) {
  652. assert.Equal(t, 0, c.createSecretCalledN)
  653. assert.Equal(t, 0, c.updateSecretCalledN)
  654. },
  655. },
  656. },
  657. }
  658. for _, tt := range tests {
  659. t.Run(tt.name, func(t *testing.T) {
  660. fakeClient := &FakeClient{}
  661. tt.fields.mock(fakeClient)
  662. p := &Provider{
  663. kube: tt.fields.kube(),
  664. namespace: tt.fields.namespace,
  665. store: tt.fields.store,
  666. bitwardenSdkClient: fakeClient,
  667. }
  668. if err := p.PushSecret(tt.args.ctx, tt.args.secret, tt.args.data); (err != nil) != tt.wantErr {
  669. t.Errorf("PushSecret() error = %v, wantErr %v", err, tt.wantErr)
  670. }
  671. tt.fields.assertMock(t, fakeClient)
  672. })
  673. }
  674. }
  675. func TestProviderSecretExists(t *testing.T) {
  676. type fields struct {
  677. kube client.Client
  678. namespace string
  679. store esv1.GenericStore
  680. mock func(c *FakeClient)
  681. assertMock func(t *testing.T, c *FakeClient)
  682. }
  683. type args struct {
  684. ctx context.Context
  685. ref v1alpha1.PushSecretData
  686. }
  687. tests := []struct {
  688. name string
  689. fields fields
  690. args args
  691. want bool
  692. wantErr bool
  693. }{
  694. {
  695. name: "secret exists",
  696. fields: fields{
  697. store: &esv1.SecretStore{
  698. Spec: esv1.SecretStoreSpec{
  699. Provider: &esv1.SecretStoreProvider{
  700. BitwardenSecretsManager: &esv1.BitwardenSecretsManagerProvider{
  701. OrganizationID: "orgid",
  702. ProjectID: projectID,
  703. },
  704. },
  705. },
  706. },
  707. mock: func(c *FakeClient) {
  708. c.GetSecretReturnsOnCallN(0, &SecretResponse{})
  709. },
  710. assertMock: func(t *testing.T, c *FakeClient) {
  711. assert.Equal(t, 0, c.listSecretsCalledN)
  712. },
  713. },
  714. args: args{
  715. ctx: nil,
  716. ref: v1alpha1.PushSecretData{
  717. Match: v1alpha1.PushSecretMatch{
  718. RemoteRef: v1alpha1.PushSecretRemoteRef{
  719. RemoteKey: remoteID,
  720. },
  721. },
  722. },
  723. },
  724. want: true,
  725. },
  726. {
  727. name: "secret exists by name",
  728. fields: fields{
  729. store: &esv1.SecretStore{
  730. Spec: esv1.SecretStoreSpec{
  731. Provider: &esv1.SecretStoreProvider{
  732. BitwardenSecretsManager: &esv1.BitwardenSecretsManagerProvider{
  733. OrganizationID: "orgid",
  734. ProjectID: projectID,
  735. },
  736. },
  737. },
  738. },
  739. mock: func(c *FakeClient) {
  740. c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
  741. Data: []SecretIdentifierResponse{
  742. {
  743. ID: remoteID,
  744. Key: "name",
  745. OrganizationID: "orgid",
  746. },
  747. },
  748. })
  749. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  750. ID: remoteID,
  751. Key: "name",
  752. OrganizationID: "orgid",
  753. Value: "value",
  754. ProjectID: &projectID,
  755. })
  756. },
  757. },
  758. args: args{
  759. ctx: nil,
  760. ref: v1alpha1.PushSecretData{
  761. Match: v1alpha1.PushSecretMatch{
  762. RemoteRef: v1alpha1.PushSecretRemoteRef{
  763. RemoteKey: "name",
  764. },
  765. },
  766. },
  767. },
  768. want: true,
  769. },
  770. {
  771. name: "secret not found by name",
  772. fields: fields{
  773. store: &esv1.SecretStore{
  774. Spec: esv1.SecretStoreSpec{
  775. Provider: &esv1.SecretStoreProvider{
  776. BitwardenSecretsManager: &esv1.BitwardenSecretsManagerProvider{
  777. OrganizationID: "orgid",
  778. ProjectID: projectID,
  779. },
  780. },
  781. },
  782. },
  783. mock: func(c *FakeClient) {
  784. c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
  785. Data: []SecretIdentifierResponse{
  786. {
  787. ID: remoteID,
  788. Key: "name",
  789. OrganizationID: "orgid",
  790. },
  791. },
  792. })
  793. projectIDDifferent := "different-project"
  794. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  795. ID: remoteID,
  796. Key: "name",
  797. OrganizationID: "orgid",
  798. Value: "value",
  799. ProjectID: &projectIDDifferent,
  800. })
  801. },
  802. },
  803. args: args{
  804. ctx: nil,
  805. ref: v1alpha1.PushSecretData{
  806. Match: v1alpha1.PushSecretMatch{
  807. RemoteRef: v1alpha1.PushSecretRemoteRef{
  808. RemoteKey: "name",
  809. },
  810. },
  811. },
  812. },
  813. want: false,
  814. },
  815. {
  816. name: "invalid name format should error",
  817. fields: fields{
  818. store: &esv1.SecretStore{
  819. Spec: esv1.SecretStoreSpec{
  820. Provider: &esv1.SecretStoreProvider{
  821. BitwardenSecretsManager: &esv1.BitwardenSecretsManagerProvider{
  822. OrganizationID: "orgid",
  823. ProjectID: projectID,
  824. },
  825. },
  826. },
  827. },
  828. mock: func(c *FakeClient) {
  829. // no mocking needed
  830. },
  831. assertMock: func(t *testing.T, c *FakeClient) {
  832. assert.Equal(t, 0, c.listSecretsCalledN)
  833. },
  834. },
  835. args: args{
  836. ctx: nil,
  837. ref: v1alpha1.PushSecretData{
  838. Match: v1alpha1.PushSecretMatch{
  839. RemoteRef: v1alpha1.PushSecretRemoteRef{
  840. RemoteKey: "name",
  841. },
  842. },
  843. },
  844. },
  845. want: false,
  846. wantErr: true, // invalid remote key format
  847. },
  848. }
  849. for _, tt := range tests {
  850. t.Run(tt.name, func(t *testing.T) {
  851. fakeClient := &FakeClient{}
  852. tt.fields.mock(fakeClient)
  853. p := &Provider{
  854. kube: tt.fields.kube,
  855. namespace: tt.fields.namespace,
  856. store: tt.fields.store,
  857. bitwardenSdkClient: fakeClient,
  858. }
  859. got, err := p.SecretExists(tt.args.ctx, tt.args.ref)
  860. if (err != nil) != tt.wantErr {
  861. t.Errorf("SecretExists() error = %v, wantErr %v", err, tt.wantErr)
  862. return
  863. }
  864. if got != tt.want {
  865. t.Errorf("SecretExists() got = %v, want %v", got, tt.want)
  866. }
  867. })
  868. }
  869. }
  870. func TestProviderGetSecretMap(t *testing.T) {
  871. type fields struct {
  872. kube func() client.Client
  873. namespace string
  874. store esv1.GenericStore
  875. mock func(c *FakeClient)
  876. }
  877. type args struct {
  878. ctx context.Context
  879. ref esv1.ExternalSecretDataRemoteRef
  880. key string
  881. }
  882. tests := []struct {
  883. name string
  884. fields fields
  885. args args
  886. want []byte
  887. wantErr bool
  888. }{
  889. {
  890. name: "get secret map",
  891. fields: fields{
  892. kube: func() client.Client {
  893. return fake.NewFakeClient()
  894. },
  895. namespace: "default",
  896. store: &esv1.SecretStore{},
  897. mock: func(c *FakeClient) {
  898. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  899. ID: remoteID,
  900. Key: "key",
  901. Note: "note",
  902. OrganizationID: "org",
  903. Value: `{"key": "value"}`,
  904. })
  905. },
  906. },
  907. args: args{
  908. ctx: context.Background(),
  909. ref: esv1.ExternalSecretDataRemoteRef{
  910. Key: remoteID,
  911. Property: "key",
  912. },
  913. key: "key",
  914. },
  915. want: []byte("value"),
  916. },
  917. {
  918. name: "get secret map with yaml",
  919. fields: fields{
  920. kube: func() client.Client {
  921. return fake.NewFakeClient()
  922. },
  923. namespace: "default",
  924. store: &esv1.SecretStore{},
  925. mock: func(c *FakeClient) {
  926. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  927. ID: remoteID,
  928. Key: "key",
  929. Note: "note",
  930. OrganizationID: "org",
  931. Value: `key: value`,
  932. })
  933. },
  934. },
  935. args: args{
  936. ctx: context.Background(),
  937. ref: esv1.ExternalSecretDataRemoteRef{
  938. Key: remoteID,
  939. Property: "key",
  940. },
  941. key: "key",
  942. },
  943. want: []byte("value"),
  944. },
  945. {
  946. name: "get secret map with nested yaml",
  947. fields: fields{
  948. kube: func() client.Client {
  949. return fake.NewFakeClient()
  950. },
  951. namespace: "default",
  952. store: &esv1.SecretStore{},
  953. mock: func(c *FakeClient) {
  954. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  955. ID: remoteID,
  956. Key: "key",
  957. Note: "note",
  958. OrganizationID: "org",
  959. Value: `key:
  960. key2: value`,
  961. })
  962. },
  963. },
  964. args: args{
  965. ctx: context.Background(),
  966. ref: esv1.ExternalSecretDataRemoteRef{
  967. Key: remoteID,
  968. Property: "key",
  969. },
  970. key: "key",
  971. },
  972. want: []byte("key2: value"),
  973. },
  974. {
  975. name: "get secret map with binary yaml data",
  976. fields: fields{
  977. kube: func() client.Client {
  978. return fake.NewFakeClient()
  979. },
  980. namespace: "default",
  981. store: &esv1.SecretStore{},
  982. mock: func(c *FakeClient) {
  983. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  984. ID: remoteID,
  985. Key: "key",
  986. Note: "note",
  987. OrganizationID: "org",
  988. Value: `key: value
  989. key2: !!binary VGhpcyBpcyBhIHRlc3Q=`,
  990. })
  991. },
  992. },
  993. args: args{
  994. ctx: context.Background(),
  995. ref: esv1.ExternalSecretDataRemoteRef{
  996. Key: remoteID,
  997. Property: "key2",
  998. },
  999. key: "key2",
  1000. },
  1001. want: []byte(`This is a test`),
  1002. },
  1003. {
  1004. name: "get secret map - missing key",
  1005. fields: fields{
  1006. kube: func() client.Client {
  1007. return fake.NewFakeClient()
  1008. },
  1009. namespace: "default",
  1010. store: &esv1.SecretStore{},
  1011. mock: func(c *FakeClient) {
  1012. c.GetSecretReturnsOnCallN(0, &SecretResponse{
  1013. ID: remoteID,
  1014. Key: "key",
  1015. Note: "note",
  1016. OrganizationID: "org",
  1017. Value: `{"key": "value"}`,
  1018. })
  1019. },
  1020. },
  1021. args: args{
  1022. ctx: context.Background(),
  1023. ref: esv1.ExternalSecretDataRemoteRef{
  1024. Key: remoteID,
  1025. Property: "nope",
  1026. },
  1027. },
  1028. },
  1029. }
  1030. for _, tt := range tests {
  1031. t.Run(tt.name, func(t *testing.T) {
  1032. fakeClient := &FakeClient{}
  1033. tt.fields.mock(fakeClient)
  1034. p := &Provider{
  1035. kube: tt.fields.kube(),
  1036. namespace: tt.fields.namespace,
  1037. store: tt.fields.store,
  1038. bitwardenSdkClient: fakeClient,
  1039. }
  1040. got, err := p.GetSecretMap(tt.args.ctx, tt.args.ref)
  1041. if (err != nil) != tt.wantErr {
  1042. t.Errorf("GetSecret() error = %v, wantErr %v", err, tt.wantErr)
  1043. return
  1044. }
  1045. assert.Equal(t, tt.want, got[tt.args.key])
  1046. })
  1047. }
  1048. }