client_test.go 28 KB

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