client_test.go 26 KB

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