secretsmanager_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  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. awsprovider "github.com/external-secrets/external-secrets/pkg/provider/aws"
  33. fakesm "github.com/external-secrets/external-secrets/pkg/provider/aws/secretsmanager/fake"
  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. AWSSM: &esv1alpha1.AWSSMProvider{},
  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. AWSSM: &esv1alpha1.AWSSMProvider{
  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. AWSSM: &esv1alpha1.AWSSMProvider{
  125. Auth: &esv1alpha1.AWSSMAuth{
  126. SecretRef: esv1alpha1.AWSSMAuthSecretRef{
  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. AWSSM: &esv1alpha1.AWSSMProvider{
  150. Auth: &esv1alpha1.AWSSMAuth{
  151. SecretRef: esv1alpha1.AWSSMAuthSecretRef{
  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. AWSSM: &esv1alpha1.AWSSMProvider{
  191. Auth: &esv1alpha1.AWSSMAuth{
  192. SecretRef: esv1alpha1.AWSSMAuthSecretRef{
  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. AWSSM: &esv1alpha1.AWSSMProvider{
  225. Auth: &esv1alpha1.AWSSMAuth{
  226. SecretRef: esv1alpha1.AWSSMAuthSecretRef{
  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. AWSSM: &esv1alpha1.AWSSMProvider{
  268. Auth: &esv1alpha1.AWSSMAuth{
  269. SecretRef: esv1alpha1.AWSSMAuthSecretRef{
  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. AWSSM: &esv1alpha1.AWSSMProvider{
  313. Auth: &esv1alpha1.AWSSMAuth{
  314. SecretRef: esv1alpha1.AWSSMAuthSecretRef{
  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 awsprovider.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. sm := SecretsManager{
  366. stsProvider: row.stsProvider,
  367. }
  368. newsm, err := sm.New(context.Background(), row.store, kc, row.namespace)
  369. if !ErrorContains(err, row.expectErr) {
  370. t.Errorf("expected error %s but found %s", row.expectErr, err.Error())
  371. }
  372. // pass test on expected error
  373. if err != nil {
  374. return
  375. }
  376. if row.expectProvider && newsm == nil {
  377. t.Errorf("expected provider object, found nil")
  378. return
  379. }
  380. creds, _ := newsm.(*SecretsManager).session.Config.Credentials.Get()
  381. assert.Equal(t, creds.AccessKeyID, row.expectedKeyID)
  382. assert.Equal(t, creds.SecretAccessKey, row.expectedSecretKey)
  383. }
  384. func TestSMEnvCredentials(t *testing.T) {
  385. k8sClient := clientfake.NewClientBuilder().Build()
  386. sm := &SecretsManager{}
  387. os.Setenv("AWS_SECRET_ACCESS_KEY", "1111")
  388. os.Setenv("AWS_ACCESS_KEY_ID", "2222")
  389. defer os.Unsetenv("AWS_SECRET_ACCESS_KEY")
  390. defer os.Unsetenv("AWS_ACCESS_KEY_ID")
  391. smi, err := sm.New(context.Background(), &esv1alpha1.SecretStore{
  392. Spec: esv1alpha1.SecretStoreSpec{
  393. Provider: &esv1alpha1.SecretStoreProvider{
  394. // defaults
  395. AWSSM: &esv1alpha1.AWSSMProvider{},
  396. },
  397. },
  398. }, k8sClient, "example-ns")
  399. assert.Nil(t, err)
  400. assert.NotNil(t, smi)
  401. creds, err := sm.session.Config.Credentials.Get()
  402. assert.Nil(t, err)
  403. assert.Equal(t, creds.AccessKeyID, "2222")
  404. assert.Equal(t, creds.SecretAccessKey, "1111")
  405. }
  406. func TestSMAssumeRole(t *testing.T) {
  407. k8sClient := clientfake.NewClientBuilder().Build()
  408. sts := &fakesm.AssumeRoler{
  409. AssumeRoleFunc: func(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) {
  410. // make sure the correct role is passed in
  411. assert.Equal(t, *input.RoleArn, "my-awesome-role")
  412. return &sts.AssumeRoleOutput{
  413. AssumedRoleUser: &sts.AssumedRoleUser{
  414. Arn: aws.String("1123132"),
  415. AssumedRoleId: aws.String("xxxxx"),
  416. },
  417. Credentials: &sts.Credentials{
  418. AccessKeyId: aws.String("3333"),
  419. SecretAccessKey: aws.String("4444"),
  420. Expiration: aws.Time(time.Now().Add(time.Hour)),
  421. SessionToken: aws.String("6666"),
  422. },
  423. }, nil
  424. },
  425. }
  426. sm := &SecretsManager{
  427. stsProvider: func(se *session.Session) stscreds.AssumeRoler {
  428. // check if the correct temporary credentials were used
  429. creds, err := se.Config.Credentials.Get()
  430. assert.Nil(t, err)
  431. assert.Equal(t, creds.AccessKeyID, "2222")
  432. assert.Equal(t, creds.SecretAccessKey, "1111")
  433. return sts
  434. },
  435. }
  436. os.Setenv("AWS_SECRET_ACCESS_KEY", "1111")
  437. os.Setenv("AWS_ACCESS_KEY_ID", "2222")
  438. defer os.Unsetenv("AWS_SECRET_ACCESS_KEY")
  439. defer os.Unsetenv("AWS_ACCESS_KEY_ID")
  440. smi, err := sm.New(context.Background(), &esv1alpha1.SecretStore{
  441. Spec: esv1alpha1.SecretStoreSpec{
  442. Provider: &esv1alpha1.SecretStoreProvider{
  443. // do assume role!
  444. AWSSM: &esv1alpha1.AWSSMProvider{
  445. Role: "my-awesome-role",
  446. },
  447. },
  448. },
  449. }, k8sClient, "example-ns")
  450. assert.Nil(t, err)
  451. assert.NotNil(t, smi)
  452. creds, err := sm.session.Config.Credentials.Get()
  453. assert.Nil(t, err)
  454. assert.Equal(t, creds.AccessKeyID, "3333")
  455. assert.Equal(t, creds.SecretAccessKey, "4444")
  456. }
  457. // test the sm<->aws interface
  458. // make sure correct values are passed and errors are handled accordingly.
  459. func TestGetSecret(t *testing.T) {
  460. fake := &fakesm.Client{}
  461. p := &SecretsManager{
  462. client: fake,
  463. }
  464. for i, row := range []struct {
  465. apiInput *awssm.GetSecretValueInput
  466. apiOutput *awssm.GetSecretValueOutput
  467. rr esv1alpha1.ExternalSecretDataRemoteRef
  468. apiErr error
  469. expectError string
  470. expectedSecret string
  471. }{
  472. {
  473. // good case: default version is set
  474. // key is passed in, output is sent back
  475. apiInput: &awssm.GetSecretValueInput{
  476. SecretId: aws.String("/baz"),
  477. VersionStage: aws.String("AWSCURRENT"),
  478. },
  479. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  480. Key: "/baz",
  481. },
  482. apiOutput: &awssm.GetSecretValueOutput{
  483. SecretString: aws.String("RRRRR"),
  484. },
  485. apiErr: nil,
  486. expectError: "",
  487. expectedSecret: "RRRRR",
  488. },
  489. {
  490. // good case: extract property
  491. apiInput: &awssm.GetSecretValueInput{
  492. SecretId: aws.String("/baz"),
  493. VersionStage: aws.String("AWSCURRENT"),
  494. },
  495. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  496. Key: "/baz",
  497. Property: "/shmoo",
  498. },
  499. apiOutput: &awssm.GetSecretValueOutput{
  500. SecretString: aws.String(`{"/shmoo": "bang"}`),
  501. },
  502. apiErr: nil,
  503. expectError: "",
  504. expectedSecret: "bang",
  505. },
  506. {
  507. // bad case: missing property
  508. apiInput: &awssm.GetSecretValueInput{
  509. SecretId: aws.String("/baz"),
  510. VersionStage: aws.String("AWSCURRENT"),
  511. },
  512. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  513. Key: "/baz",
  514. Property: "INVALPROP",
  515. },
  516. apiOutput: &awssm.GetSecretValueOutput{
  517. SecretString: aws.String(`{"/shmoo": "bang"}`),
  518. },
  519. apiErr: nil,
  520. expectError: "key INVALPROP does not exist in secret",
  521. expectedSecret: "",
  522. },
  523. {
  524. // bad case: extract property failure due to invalid json
  525. apiInput: &awssm.GetSecretValueInput{
  526. SecretId: aws.String("/baz"),
  527. VersionStage: aws.String("AWSCURRENT"),
  528. },
  529. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  530. Key: "/baz",
  531. Property: "INVALPROP",
  532. },
  533. apiOutput: &awssm.GetSecretValueOutput{
  534. SecretString: aws.String(`------`),
  535. },
  536. apiErr: nil,
  537. expectError: "key INVALPROP does not exist in secret",
  538. expectedSecret: "",
  539. },
  540. {
  541. // case: ssm.SecretString may be nil but binary is set
  542. apiInput: &awssm.GetSecretValueInput{
  543. SecretId: aws.String("/baz"),
  544. VersionStage: aws.String("AWSCURRENT"),
  545. },
  546. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  547. Key: "/baz",
  548. },
  549. apiOutput: &awssm.GetSecretValueOutput{
  550. SecretString: nil,
  551. SecretBinary: []byte("yesplease"),
  552. },
  553. apiErr: nil,
  554. expectError: "",
  555. expectedSecret: "yesplease",
  556. },
  557. {
  558. // case: both .SecretString and .SecretBinary is nil
  559. apiInput: &awssm.GetSecretValueInput{
  560. SecretId: aws.String("/baz"),
  561. VersionStage: aws.String("AWSCURRENT"),
  562. },
  563. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  564. Key: "/baz",
  565. },
  566. apiOutput: &awssm.GetSecretValueOutput{
  567. SecretString: nil,
  568. SecretBinary: nil,
  569. },
  570. apiErr: nil,
  571. expectError: "no secret string nor binary for key",
  572. expectedSecret: "",
  573. },
  574. {
  575. // case: secretOut.SecretBinary JSON parsing
  576. apiInput: &awssm.GetSecretValueInput{
  577. SecretId: aws.String("/baz"),
  578. VersionStage: aws.String("AWSCURRENT"),
  579. },
  580. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  581. Key: "/baz",
  582. Property: "foobar.baz",
  583. },
  584. apiOutput: &awssm.GetSecretValueOutput{
  585. SecretString: nil,
  586. SecretBinary: []byte(`{"foobar":{"baz":"nestedval"}}`),
  587. },
  588. apiErr: nil,
  589. expectError: "",
  590. expectedSecret: "nestedval",
  591. },
  592. {
  593. // should pass version
  594. apiInput: &awssm.GetSecretValueInput{
  595. SecretId: aws.String("/foo/bar"),
  596. VersionStage: aws.String("1234"),
  597. },
  598. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  599. Key: "/foo/bar",
  600. Version: "1234",
  601. },
  602. apiOutput: &awssm.GetSecretValueOutput{
  603. SecretString: aws.String("FOOBA!"),
  604. },
  605. apiErr: nil,
  606. expectError: "",
  607. expectedSecret: "FOOBA!",
  608. },
  609. {
  610. // should return err
  611. apiInput: &awssm.GetSecretValueInput{
  612. SecretId: aws.String("/foo/bar"),
  613. VersionStage: aws.String("AWSCURRENT"),
  614. },
  615. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  616. Key: "/foo/bar",
  617. },
  618. apiOutput: &awssm.GetSecretValueOutput{},
  619. apiErr: fmt.Errorf("oh no"),
  620. expectError: "oh no",
  621. },
  622. } {
  623. fake.WithValue(row.apiInput, row.apiOutput, row.apiErr)
  624. out, err := p.GetSecret(context.Background(), row.rr)
  625. if !ErrorContains(err, row.expectError) {
  626. t.Errorf("[%d] unexpected error: %s, expected: '%s'", i, err.Error(), row.expectError)
  627. }
  628. if string(out) != row.expectedSecret {
  629. t.Errorf("[%d] unexpected secret: expected %s, got %s", i, row.expectedSecret, string(out))
  630. }
  631. }
  632. }
  633. func TestGetSecretMap(t *testing.T) {
  634. fake := &fakesm.Client{}
  635. p := &SecretsManager{
  636. client: fake,
  637. }
  638. for i, row := range []struct {
  639. apiInput *awssm.GetSecretValueInput
  640. apiOutput *awssm.GetSecretValueOutput
  641. rr esv1alpha1.ExternalSecretDataRemoteRef
  642. expectedData map[string]string
  643. apiErr error
  644. expectError string
  645. }{
  646. {
  647. // good case: default version & deserialization
  648. apiInput: &awssm.GetSecretValueInput{
  649. SecretId: aws.String("/baz"),
  650. VersionStage: aws.String("AWSCURRENT"),
  651. },
  652. apiOutput: &awssm.GetSecretValueOutput{
  653. SecretString: aws.String(`{"foo":"bar"}`),
  654. },
  655. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  656. Key: "/baz",
  657. },
  658. expectedData: map[string]string{
  659. "foo": "bar",
  660. },
  661. apiErr: nil,
  662. expectError: "",
  663. },
  664. {
  665. // bad case: api error returned
  666. apiInput: &awssm.GetSecretValueInput{
  667. SecretId: aws.String("/baz"),
  668. VersionStage: aws.String("AWSCURRENT"),
  669. },
  670. apiOutput: &awssm.GetSecretValueOutput{
  671. SecretString: aws.String(`{"foo":"bar"}`),
  672. },
  673. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  674. Key: "/baz",
  675. },
  676. expectedData: map[string]string{
  677. "foo": "bar",
  678. },
  679. apiErr: fmt.Errorf("some api err"),
  680. expectError: "some api err",
  681. },
  682. {
  683. // bad case: invalid json
  684. apiInput: &awssm.GetSecretValueInput{
  685. SecretId: aws.String("/baz"),
  686. VersionStage: aws.String("AWSCURRENT"),
  687. },
  688. apiOutput: &awssm.GetSecretValueOutput{
  689. SecretString: aws.String(`-----------------`),
  690. },
  691. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  692. Key: "/baz",
  693. },
  694. expectedData: map[string]string{},
  695. apiErr: nil,
  696. expectError: "unable to unmarshal secret",
  697. },
  698. } {
  699. fake.WithValue(row.apiInput, row.apiOutput, row.apiErr)
  700. out, err := p.GetSecretMap(context.Background(), row.rr)
  701. if !ErrorContains(err, row.expectError) {
  702. t.Errorf("[%d] unexpected error: %s, expected: '%s'", i, err.Error(), row.expectError)
  703. }
  704. if cmp.Equal(out, row.expectedData) {
  705. t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", i, row.expectedData, out)
  706. }
  707. }
  708. }
  709. func ErrorContains(out error, want string) bool {
  710. if out == nil {
  711. return want == ""
  712. }
  713. if want == "" {
  714. return false
  715. }
  716. return strings.Contains(out.Error(), want)
  717. }