secretsmanager_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  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 secretsmanager
  13. import (
  14. "context"
  15. "fmt"
  16. "os"
  17. "strings"
  18. "testing"
  19. "time"
  20. "github.com/aws/aws-sdk-go/aws"
  21. "github.com/aws/aws-sdk-go/aws/credentials/stscreds"
  22. "github.com/aws/aws-sdk-go/aws/session"
  23. awssm "github.com/aws/aws-sdk-go/service/secretsmanager"
  24. "github.com/aws/aws-sdk-go/service/sts"
  25. "github.com/google/go-cmp/cmp"
  26. "github.com/stretchr/testify/assert"
  27. v1 "k8s.io/api/core/v1"
  28. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  29. clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
  30. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  31. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  32. fakesm "github.com/external-secrets/external-secrets/pkg/provider/aws/secretsmanager/fake"
  33. awssess "github.com/external-secrets/external-secrets/pkg/provider/aws/session"
  34. )
  35. func TestConstructor(t *testing.T) {
  36. rows := []ConstructorRow{
  37. {
  38. name: "nil store",
  39. expectErr: "found nil store",
  40. store: nil,
  41. },
  42. {
  43. name: "not store spec",
  44. expectErr: "storeSpec is missing provider",
  45. store: &esv1alpha1.SecretStore{},
  46. },
  47. {
  48. name: "store spec has no provider",
  49. expectErr: "storeSpec is missing provider",
  50. store: &esv1alpha1.SecretStore{
  51. Spec: esv1alpha1.SecretStoreSpec{},
  52. },
  53. },
  54. {
  55. name: "spec has no awssm field",
  56. expectErr: "Missing AWSSM field",
  57. store: &esv1alpha1.SecretStore{
  58. Spec: esv1alpha1.SecretStoreSpec{
  59. Provider: &esv1alpha1.SecretStoreProvider{},
  60. },
  61. },
  62. },
  63. {
  64. name: "configure aws using environment variables",
  65. store: &esv1alpha1.SecretStore{
  66. Spec: esv1alpha1.SecretStoreSpec{
  67. Provider: &esv1alpha1.SecretStoreProvider{
  68. AWS: &esv1alpha1.AWSProvider{},
  69. },
  70. },
  71. },
  72. env: map[string]string{
  73. "AWS_ACCESS_KEY_ID": "1111",
  74. "AWS_SECRET_ACCESS_KEY": "2222",
  75. },
  76. expectProvider: true,
  77. expectedKeyID: "1111",
  78. expectedSecretKey: "2222",
  79. },
  80. {
  81. name: "configure aws using environment variables + assume role",
  82. stsProvider: func(*session.Session) stscreds.AssumeRoler {
  83. return &fakesm.AssumeRoler{
  84. AssumeRoleFunc: func(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) {
  85. assert.Equal(t, *input.RoleArn, "foo-bar-baz")
  86. return &sts.AssumeRoleOutput{
  87. AssumedRoleUser: &sts.AssumedRoleUser{
  88. Arn: aws.String("1123132"),
  89. AssumedRoleId: aws.String("xxxxx"),
  90. },
  91. Credentials: &sts.Credentials{
  92. AccessKeyId: aws.String("3333"),
  93. SecretAccessKey: aws.String("4444"),
  94. Expiration: aws.Time(time.Now().Add(time.Hour)),
  95. SessionToken: aws.String("6666"),
  96. },
  97. }, nil
  98. },
  99. }
  100. },
  101. store: &esv1alpha1.SecretStore{
  102. Spec: esv1alpha1.SecretStoreSpec{
  103. Provider: &esv1alpha1.SecretStoreProvider{
  104. AWS: &esv1alpha1.AWSProvider{
  105. Role: "foo-bar-baz",
  106. },
  107. },
  108. },
  109. },
  110. env: map[string]string{
  111. "AWS_ACCESS_KEY_ID": "1111",
  112. "AWS_SECRET_ACCESS_KEY": "2222",
  113. },
  114. expectProvider: true,
  115. expectedKeyID: "3333",
  116. expectedSecretKey: "4444",
  117. },
  118. {
  119. name: "error out when secret with credentials does not exist",
  120. namespace: "foo",
  121. store: &esv1alpha1.SecretStore{
  122. Spec: esv1alpha1.SecretStoreSpec{
  123. Provider: &esv1alpha1.SecretStoreProvider{
  124. AWS: &esv1alpha1.AWSProvider{
  125. Auth: &esv1alpha1.AWSAuth{
  126. SecretRef: esv1alpha1.AWSAuthSecretRef{
  127. AccessKeyID: esmeta.SecretKeySelector{
  128. Name: "othersecret",
  129. Key: "one",
  130. },
  131. SecretAccessKey: esmeta.SecretKeySelector{
  132. Name: "othersecret",
  133. Key: "two",
  134. },
  135. },
  136. },
  137. },
  138. },
  139. },
  140. },
  141. expectErr: `secrets "othersecret" not found`,
  142. },
  143. {
  144. name: "use credentials from secret to configure aws",
  145. namespace: "foo",
  146. store: &esv1alpha1.SecretStore{
  147. Spec: esv1alpha1.SecretStoreSpec{
  148. Provider: &esv1alpha1.SecretStoreProvider{
  149. AWS: &esv1alpha1.AWSProvider{
  150. Auth: &esv1alpha1.AWSAuth{
  151. SecretRef: esv1alpha1.AWSAuthSecretRef{
  152. AccessKeyID: esmeta.SecretKeySelector{
  153. Name: "onesecret",
  154. // Namespace is not set
  155. Key: "one",
  156. },
  157. SecretAccessKey: esmeta.SecretKeySelector{
  158. Name: "onesecret",
  159. // Namespace is not set
  160. Key: "two",
  161. },
  162. },
  163. },
  164. },
  165. },
  166. },
  167. },
  168. secrets: []v1.Secret{
  169. {
  170. ObjectMeta: metav1.ObjectMeta{
  171. Name: "onesecret",
  172. Namespace: "foo",
  173. },
  174. Data: map[string][]byte{
  175. "one": []byte("1111"),
  176. "two": []byte("2222"),
  177. },
  178. },
  179. },
  180. expectProvider: true,
  181. expectedKeyID: "1111",
  182. expectedSecretKey: "2222",
  183. },
  184. {
  185. name: "error out when secret key does not exist",
  186. namespace: "foo",
  187. store: &esv1alpha1.SecretStore{
  188. Spec: esv1alpha1.SecretStoreSpec{
  189. Provider: &esv1alpha1.SecretStoreProvider{
  190. AWS: &esv1alpha1.AWSProvider{
  191. Auth: &esv1alpha1.AWSAuth{
  192. SecretRef: esv1alpha1.AWSAuthSecretRef{
  193. AccessKeyID: esmeta.SecretKeySelector{
  194. Name: "brokensecret",
  195. Key: "one",
  196. },
  197. SecretAccessKey: esmeta.SecretKeySelector{
  198. Name: "brokensecret",
  199. Key: "two",
  200. },
  201. },
  202. },
  203. },
  204. },
  205. },
  206. },
  207. secrets: []v1.Secret{
  208. {
  209. ObjectMeta: metav1.ObjectMeta{
  210. Name: "brokensecret",
  211. Namespace: "foo",
  212. },
  213. Data: map[string][]byte{},
  214. },
  215. },
  216. expectErr: "missing SecretAccessKey",
  217. },
  218. {
  219. name: "should not be able to access secrets from different namespace",
  220. namespace: "foo",
  221. store: &esv1alpha1.SecretStore{
  222. Spec: esv1alpha1.SecretStoreSpec{
  223. Provider: &esv1alpha1.SecretStoreProvider{
  224. AWS: &esv1alpha1.AWSProvider{
  225. Auth: &esv1alpha1.AWSAuth{
  226. SecretRef: esv1alpha1.AWSAuthSecretRef{
  227. AccessKeyID: esmeta.SecretKeySelector{
  228. Name: "onesecret",
  229. Namespace: aws.String("evil"), // this should not be possible!
  230. Key: "one",
  231. },
  232. SecretAccessKey: esmeta.SecretKeySelector{
  233. Name: "onesecret",
  234. Namespace: aws.String("evil"),
  235. Key: "two",
  236. },
  237. },
  238. },
  239. },
  240. },
  241. },
  242. },
  243. secrets: []v1.Secret{
  244. {
  245. ObjectMeta: metav1.ObjectMeta{
  246. Name: "onesecret",
  247. Namespace: "evil",
  248. },
  249. Data: map[string][]byte{
  250. "one": []byte("1111"),
  251. "two": []byte("2222"),
  252. },
  253. },
  254. },
  255. expectErr: `secrets "onesecret" not found`,
  256. },
  257. {
  258. name: "ClusterStore should use credentials from a specific namespace",
  259. namespace: "es-namespace",
  260. store: &esv1alpha1.ClusterSecretStore{
  261. TypeMeta: metav1.TypeMeta{
  262. APIVersion: esv1alpha1.ClusterSecretStoreKindAPIVersion,
  263. Kind: esv1alpha1.ClusterSecretStoreKind,
  264. },
  265. Spec: esv1alpha1.SecretStoreSpec{
  266. Provider: &esv1alpha1.SecretStoreProvider{
  267. AWS: &esv1alpha1.AWSProvider{
  268. Auth: &esv1alpha1.AWSAuth{
  269. SecretRef: esv1alpha1.AWSAuthSecretRef{
  270. AccessKeyID: esmeta.SecretKeySelector{
  271. Name: "onesecret",
  272. Namespace: aws.String("platform-team-ns"),
  273. Key: "one",
  274. },
  275. SecretAccessKey: esmeta.SecretKeySelector{
  276. Name: "onesecret",
  277. Namespace: aws.String("platform-team-ns"),
  278. Key: "two",
  279. },
  280. },
  281. },
  282. },
  283. },
  284. },
  285. },
  286. secrets: []v1.Secret{
  287. {
  288. ObjectMeta: metav1.ObjectMeta{
  289. Name: "onesecret",
  290. Namespace: "platform-team-ns",
  291. },
  292. Data: map[string][]byte{
  293. "one": []byte("1111"),
  294. "two": []byte("2222"),
  295. },
  296. },
  297. },
  298. expectProvider: true,
  299. expectedKeyID: "1111",
  300. expectedSecretKey: "2222",
  301. },
  302. {
  303. name: "namespace is mandatory when using ClusterStore with SecretKeySelector",
  304. namespace: "es-namespace",
  305. store: &esv1alpha1.ClusterSecretStore{
  306. TypeMeta: metav1.TypeMeta{
  307. APIVersion: esv1alpha1.ClusterSecretStoreKindAPIVersion,
  308. Kind: esv1alpha1.ClusterSecretStoreKind,
  309. },
  310. Spec: esv1alpha1.SecretStoreSpec{
  311. Provider: &esv1alpha1.SecretStoreProvider{
  312. AWS: &esv1alpha1.AWSProvider{
  313. Auth: &esv1alpha1.AWSAuth{
  314. SecretRef: esv1alpha1.AWSAuthSecretRef{
  315. AccessKeyID: esmeta.SecretKeySelector{
  316. Name: "onesecret",
  317. Key: "one",
  318. },
  319. SecretAccessKey: esmeta.SecretKeySelector{
  320. Name: "onesecret",
  321. Key: "two",
  322. },
  323. },
  324. },
  325. },
  326. },
  327. },
  328. },
  329. expectErr: "invalid ClusterSecretStore: missing AWSSM AccessKeyID Namespace",
  330. },
  331. }
  332. for i := range rows {
  333. row := rows[i]
  334. t.Run(row.name, func(t *testing.T) {
  335. testRow(t, row)
  336. })
  337. }
  338. }
  339. type ConstructorRow struct {
  340. name string
  341. store esv1alpha1.GenericStore
  342. secrets []v1.Secret
  343. namespace string
  344. stsProvider awssess.STSProvider
  345. expectProvider bool
  346. expectErr string
  347. expectedKeyID string
  348. expectedSecretKey string
  349. env map[string]string
  350. }
  351. func testRow(t *testing.T, row ConstructorRow) {
  352. kc := clientfake.NewClientBuilder().Build()
  353. for i := range row.secrets {
  354. err := kc.Create(context.Background(), &row.secrets[i])
  355. assert.Nil(t, err)
  356. }
  357. for k, v := range row.env {
  358. os.Setenv(k, v)
  359. }
  360. defer func() {
  361. for k := range row.env {
  362. os.Unsetenv(k)
  363. }
  364. }()
  365. newsm, err := New(context.Background(), row.store, kc, row.namespace, row.stsProvider)
  366. if !ErrorContains(err, row.expectErr) {
  367. t.Errorf("expected error %s but found %s", row.expectErr, err.Error())
  368. }
  369. // pass test on expected error
  370. if err != nil {
  371. return
  372. }
  373. if row.expectProvider && newsm == nil {
  374. t.Errorf("expected provider object, found nil")
  375. return
  376. }
  377. creds, _ := newsm.(*SecretsManager).session.Config.Credentials.Get()
  378. assert.Equal(t, creds.AccessKeyID, row.expectedKeyID)
  379. assert.Equal(t, creds.SecretAccessKey, row.expectedSecretKey)
  380. }
  381. func TestSMEnvCredentials(t *testing.T) {
  382. k8sClient := clientfake.NewClientBuilder().Build()
  383. os.Setenv("AWS_SECRET_ACCESS_KEY", "1111")
  384. os.Setenv("AWS_ACCESS_KEY_ID", "2222")
  385. defer os.Unsetenv("AWS_SECRET_ACCESS_KEY")
  386. defer os.Unsetenv("AWS_ACCESS_KEY_ID")
  387. smi, err := New(context.Background(), &esv1alpha1.SecretStore{
  388. Spec: esv1alpha1.SecretStoreSpec{
  389. Provider: &esv1alpha1.SecretStoreProvider{
  390. // defaults
  391. AWS: &esv1alpha1.AWSProvider{},
  392. },
  393. },
  394. }, k8sClient, "example-ns", awssess.DefaultSTSProvider)
  395. assert.Nil(t, err)
  396. assert.NotNil(t, smi)
  397. sm, ok := smi.(*SecretsManager)
  398. assert.True(t, ok)
  399. creds, err := sm.session.Config.Credentials.Get()
  400. assert.Nil(t, err)
  401. assert.Equal(t, creds.AccessKeyID, "2222")
  402. assert.Equal(t, creds.SecretAccessKey, "1111")
  403. }
  404. func TestSMAssumeRole(t *testing.T) {
  405. k8sClient := clientfake.NewClientBuilder().Build()
  406. sts := &fakesm.AssumeRoler{
  407. AssumeRoleFunc: func(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) {
  408. // make sure the correct role is passed in
  409. assert.Equal(t, *input.RoleArn, "my-awesome-role")
  410. return &sts.AssumeRoleOutput{
  411. AssumedRoleUser: &sts.AssumedRoleUser{
  412. Arn: aws.String("1123132"),
  413. AssumedRoleId: aws.String("xxxxx"),
  414. },
  415. Credentials: &sts.Credentials{
  416. AccessKeyId: aws.String("3333"),
  417. SecretAccessKey: aws.String("4444"),
  418. Expiration: aws.Time(time.Now().Add(time.Hour)),
  419. SessionToken: aws.String("6666"),
  420. },
  421. }, nil
  422. },
  423. }
  424. os.Setenv("AWS_SECRET_ACCESS_KEY", "1111")
  425. os.Setenv("AWS_ACCESS_KEY_ID", "2222")
  426. defer os.Unsetenv("AWS_SECRET_ACCESS_KEY")
  427. defer os.Unsetenv("AWS_ACCESS_KEY_ID")
  428. smi, err := New(context.Background(), &esv1alpha1.SecretStore{
  429. Spec: esv1alpha1.SecretStoreSpec{
  430. Provider: &esv1alpha1.SecretStoreProvider{
  431. // do assume role!
  432. AWS: &esv1alpha1.AWSProvider{
  433. Role: "my-awesome-role",
  434. },
  435. },
  436. },
  437. }, k8sClient, "example-ns", func(se *session.Session) stscreds.AssumeRoler {
  438. // check if the correct temporary credentials were used
  439. creds, err := se.Config.Credentials.Get()
  440. assert.Nil(t, err)
  441. assert.Equal(t, creds.AccessKeyID, "2222")
  442. assert.Equal(t, creds.SecretAccessKey, "1111")
  443. return sts
  444. })
  445. assert.Nil(t, err)
  446. assert.NotNil(t, smi)
  447. sm, ok := smi.(*SecretsManager)
  448. assert.True(t, ok)
  449. creds, err := sm.session.Config.Credentials.Get()
  450. assert.Nil(t, err)
  451. assert.Equal(t, creds.AccessKeyID, "3333")
  452. assert.Equal(t, creds.SecretAccessKey, "4444")
  453. }
  454. // test the sm<->aws interface
  455. // make sure correct values are passed and errors are handled accordingly.
  456. func TestGetSecret(t *testing.T) {
  457. fake := &fakesm.Client{}
  458. p := &SecretsManager{
  459. client: fake,
  460. }
  461. for i, row := range []struct {
  462. apiInput *awssm.GetSecretValueInput
  463. apiOutput *awssm.GetSecretValueOutput
  464. rr esv1alpha1.ExternalSecretDataRemoteRef
  465. apiErr error
  466. expectError string
  467. expectedSecret string
  468. }{
  469. {
  470. // good case: default version is set
  471. // key is passed in, output is sent back
  472. apiInput: &awssm.GetSecretValueInput{
  473. SecretId: aws.String("/baz"),
  474. VersionStage: aws.String("AWSCURRENT"),
  475. },
  476. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  477. Key: "/baz",
  478. },
  479. apiOutput: &awssm.GetSecretValueOutput{
  480. SecretString: aws.String("RRRRR"),
  481. },
  482. apiErr: nil,
  483. expectError: "",
  484. expectedSecret: "RRRRR",
  485. },
  486. {
  487. // good case: extract property
  488. apiInput: &awssm.GetSecretValueInput{
  489. SecretId: aws.String("/baz"),
  490. VersionStage: aws.String("AWSCURRENT"),
  491. },
  492. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  493. Key: "/baz",
  494. Property: "/shmoo",
  495. },
  496. apiOutput: &awssm.GetSecretValueOutput{
  497. SecretString: aws.String(`{"/shmoo": "bang"}`),
  498. },
  499. apiErr: nil,
  500. expectError: "",
  501. expectedSecret: "bang",
  502. },
  503. {
  504. // bad case: missing property
  505. apiInput: &awssm.GetSecretValueInput{
  506. SecretId: aws.String("/baz"),
  507. VersionStage: aws.String("AWSCURRENT"),
  508. },
  509. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  510. Key: "/baz",
  511. Property: "INVALPROP",
  512. },
  513. apiOutput: &awssm.GetSecretValueOutput{
  514. SecretString: aws.String(`{"/shmoo": "bang"}`),
  515. },
  516. apiErr: nil,
  517. expectError: "key INVALPROP does not exist in secret",
  518. expectedSecret: "",
  519. },
  520. {
  521. // bad case: extract property failure due to invalid json
  522. apiInput: &awssm.GetSecretValueInput{
  523. SecretId: aws.String("/baz"),
  524. VersionStage: aws.String("AWSCURRENT"),
  525. },
  526. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  527. Key: "/baz",
  528. Property: "INVALPROP",
  529. },
  530. apiOutput: &awssm.GetSecretValueOutput{
  531. SecretString: aws.String(`------`),
  532. },
  533. apiErr: nil,
  534. expectError: "key INVALPROP does not exist in secret",
  535. expectedSecret: "",
  536. },
  537. {
  538. // case: ssm.SecretString may be nil but binary is set
  539. apiInput: &awssm.GetSecretValueInput{
  540. SecretId: aws.String("/baz"),
  541. VersionStage: aws.String("AWSCURRENT"),
  542. },
  543. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  544. Key: "/baz",
  545. },
  546. apiOutput: &awssm.GetSecretValueOutput{
  547. SecretString: nil,
  548. SecretBinary: []byte("yesplease"),
  549. },
  550. apiErr: nil,
  551. expectError: "",
  552. expectedSecret: "yesplease",
  553. },
  554. {
  555. // case: both .SecretString and .SecretBinary is nil
  556. apiInput: &awssm.GetSecretValueInput{
  557. SecretId: aws.String("/baz"),
  558. VersionStage: aws.String("AWSCURRENT"),
  559. },
  560. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  561. Key: "/baz",
  562. },
  563. apiOutput: &awssm.GetSecretValueOutput{
  564. SecretString: nil,
  565. SecretBinary: nil,
  566. },
  567. apiErr: nil,
  568. expectError: "no secret string nor binary for key",
  569. expectedSecret: "",
  570. },
  571. {
  572. // case: secretOut.SecretBinary JSON parsing
  573. apiInput: &awssm.GetSecretValueInput{
  574. SecretId: aws.String("/baz"),
  575. VersionStage: aws.String("AWSCURRENT"),
  576. },
  577. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  578. Key: "/baz",
  579. Property: "foobar.baz",
  580. },
  581. apiOutput: &awssm.GetSecretValueOutput{
  582. SecretString: nil,
  583. SecretBinary: []byte(`{"foobar":{"baz":"nestedval"}}`),
  584. },
  585. apiErr: nil,
  586. expectError: "",
  587. expectedSecret: "nestedval",
  588. },
  589. {
  590. // should pass version
  591. apiInput: &awssm.GetSecretValueInput{
  592. SecretId: aws.String("/foo/bar"),
  593. VersionStage: aws.String("1234"),
  594. },
  595. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  596. Key: "/foo/bar",
  597. Version: "1234",
  598. },
  599. apiOutput: &awssm.GetSecretValueOutput{
  600. SecretString: aws.String("FOOBA!"),
  601. },
  602. apiErr: nil,
  603. expectError: "",
  604. expectedSecret: "FOOBA!",
  605. },
  606. {
  607. // should return err
  608. apiInput: &awssm.GetSecretValueInput{
  609. SecretId: aws.String("/foo/bar"),
  610. VersionStage: aws.String("AWSCURRENT"),
  611. },
  612. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  613. Key: "/foo/bar",
  614. },
  615. apiOutput: &awssm.GetSecretValueOutput{},
  616. apiErr: fmt.Errorf("oh no"),
  617. expectError: "oh no",
  618. },
  619. } {
  620. fake.WithValue(row.apiInput, row.apiOutput, row.apiErr)
  621. out, err := p.GetSecret(context.Background(), row.rr)
  622. if !ErrorContains(err, row.expectError) {
  623. t.Errorf("[%d] unexpected error: %s, expected: '%s'", i, err.Error(), row.expectError)
  624. }
  625. if string(out) != row.expectedSecret {
  626. t.Errorf("[%d] unexpected secret: expected %s, got %s", i, row.expectedSecret, string(out))
  627. }
  628. }
  629. }
  630. func TestGetSecretMap(t *testing.T) {
  631. fake := &fakesm.Client{}
  632. p := &SecretsManager{
  633. client: fake,
  634. }
  635. for i, row := range []struct {
  636. apiInput *awssm.GetSecretValueInput
  637. apiOutput *awssm.GetSecretValueOutput
  638. rr esv1alpha1.ExternalSecretDataRemoteRef
  639. expectedData map[string]string
  640. apiErr error
  641. expectError string
  642. }{
  643. {
  644. // good case: default version & deserialization
  645. apiInput: &awssm.GetSecretValueInput{
  646. SecretId: aws.String("/baz"),
  647. VersionStage: aws.String("AWSCURRENT"),
  648. },
  649. apiOutput: &awssm.GetSecretValueOutput{
  650. SecretString: aws.String(`{"foo":"bar"}`),
  651. },
  652. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  653. Key: "/baz",
  654. },
  655. expectedData: map[string]string{
  656. "foo": "bar",
  657. },
  658. apiErr: nil,
  659. expectError: "",
  660. },
  661. {
  662. // bad case: api error returned
  663. apiInput: &awssm.GetSecretValueInput{
  664. SecretId: aws.String("/baz"),
  665. VersionStage: aws.String("AWSCURRENT"),
  666. },
  667. apiOutput: &awssm.GetSecretValueOutput{
  668. SecretString: aws.String(`{"foo":"bar"}`),
  669. },
  670. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  671. Key: "/baz",
  672. },
  673. expectedData: map[string]string{
  674. "foo": "bar",
  675. },
  676. apiErr: fmt.Errorf("some api err"),
  677. expectError: "some api err",
  678. },
  679. {
  680. // bad case: invalid json
  681. apiInput: &awssm.GetSecretValueInput{
  682. SecretId: aws.String("/baz"),
  683. VersionStage: aws.String("AWSCURRENT"),
  684. },
  685. apiOutput: &awssm.GetSecretValueOutput{
  686. SecretString: aws.String(`-----------------`),
  687. },
  688. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  689. Key: "/baz",
  690. },
  691. expectedData: map[string]string{},
  692. apiErr: nil,
  693. expectError: "unable to unmarshal secret",
  694. },
  695. } {
  696. fake.WithValue(row.apiInput, row.apiOutput, row.apiErr)
  697. out, err := p.GetSecretMap(context.Background(), row.rr)
  698. if !ErrorContains(err, row.expectError) {
  699. t.Errorf("[%d] unexpected error: %s, expected: '%s'", i, err.Error(), row.expectError)
  700. }
  701. if cmp.Equal(out, row.expectedData) {
  702. t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", i, row.expectedData, out)
  703. }
  704. }
  705. }
  706. func ErrorContains(out error, want string) bool {
  707. if out == nil {
  708. return want == ""
  709. }
  710. if want == "" {
  711. return false
  712. }
  713. return strings.Contains(out.Error(), want)
  714. }