provider_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. /*
  2. Copyright © The ESO Authors
  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 conjur
  14. import (
  15. "context"
  16. "errors"
  17. "fmt"
  18. "reflect"
  19. "testing"
  20. "time"
  21. "github.com/cyberark/conjur-api-go/conjurapi"
  22. "github.com/cyberark/conjur-api-go/conjurapi/authn"
  23. "github.com/golang-jwt/jwt/v5"
  24. "github.com/google/go-cmp/cmp"
  25. corev1 "k8s.io/api/core/v1"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
  28. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  29. clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
  30. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  31. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  32. "github.com/external-secrets/external-secrets/providers/v1/conjur/fake"
  33. utilfake "github.com/external-secrets/external-secrets/runtime/util/fake"
  34. )
  35. var (
  36. svcURL = "https://example.com"
  37. svcUser = "user"
  38. svcApikey = "apikey"
  39. svcAccount = "account1"
  40. jwtAuthenticator = "jwt-authenticator"
  41. jwtAuthnService = "jwt-auth-service"
  42. jwtSecretName = "jwt-secret"
  43. )
  44. func makeValidRef(k string) *esv1.ExternalSecretDataRemoteRef {
  45. return &esv1.ExternalSecretDataRemoteRef{
  46. Key: k,
  47. Version: "default",
  48. }
  49. }
  50. func makeValidFindRef(search string, tags map[string]string) *esv1.ExternalSecretFind {
  51. var name *esv1.FindName
  52. if search != "" {
  53. name = &esv1.FindName{
  54. RegExp: search,
  55. }
  56. }
  57. return &esv1.ExternalSecretFind{
  58. Name: name,
  59. Tags: tags,
  60. }
  61. }
  62. func TestGetSecret(t *testing.T) {
  63. type args struct {
  64. store esv1.GenericStore
  65. kube kclient.Client
  66. corev1 typedcorev1.CoreV1Interface
  67. namespace string
  68. secretPath string
  69. }
  70. type want struct {
  71. err error
  72. value string
  73. }
  74. type testCase struct {
  75. reason string
  76. args args
  77. want want
  78. }
  79. cases := map[string]testCase{
  80. "ApiKeyReadSecretSuccess": {
  81. reason: "Should read a secret successfully using an ApiKey auth secret store.",
  82. args: args{
  83. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  84. kube: clientfake.NewClientBuilder().
  85. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  86. namespace: "default",
  87. secretPath: "path/to/secret",
  88. },
  89. want: want{
  90. err: nil,
  91. value: "secret",
  92. },
  93. },
  94. "ApiKeyReadSecretFailure": {
  95. reason: "Should fail to read secret using ApiKey auth secret store.",
  96. args: args{
  97. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  98. kube: clientfake.NewClientBuilder().
  99. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  100. namespace: "default",
  101. secretPath: "error",
  102. },
  103. want: want{
  104. err: errors.New("error"),
  105. value: "",
  106. },
  107. },
  108. "JwtWithServiceAccountRefReadSecretSuccess": {
  109. reason: "Should read a secret successfully using a JWT auth secret store that references a k8s service account.",
  110. args: args{
  111. store: makeJWTSecretStore(svcURL, svcAccount, "", jwtAuthenticator, "", "myconjuraccount"),
  112. kube: clientfake.NewClientBuilder().
  113. WithObjects().Build(),
  114. namespace: "default",
  115. secretPath: "path/to/secret",
  116. corev1: utilfake.NewCreateTokenMock().WithToken(createFakeJwtToken(true)),
  117. },
  118. want: want{
  119. err: nil,
  120. value: "secret",
  121. },
  122. },
  123. "JwtWithServiceAccountRefWithHostIdReadSecretSuccess": {
  124. reason: "Should read a secret successfully using a JWT auth secret store that references a k8s service account and uses a host ID.",
  125. args: args{
  126. store: makeJWTSecretStore(svcURL, svcAccount, "", jwtAuthenticator, "myhostid", "myconjuraccount"),
  127. kube: clientfake.NewClientBuilder().
  128. WithObjects().Build(),
  129. namespace: "default",
  130. secretPath: "path/to/secret",
  131. corev1: utilfake.NewCreateTokenMock().WithToken(createFakeJwtToken(true)),
  132. },
  133. want: want{
  134. err: nil,
  135. value: "secret",
  136. },
  137. },
  138. "JwtWithSecretRefReadSecretSuccess": {
  139. reason: "Should read a secret successfully using an JWT auth secret store that references a k8s secret.",
  140. args: args{
  141. store: makeJWTSecretStore(svcURL, "", jwtSecretName, jwtAuthenticator, "", "myconjuraccount"),
  142. kube: clientfake.NewClientBuilder().
  143. WithObjects(&corev1.Secret{
  144. ObjectMeta: metav1.ObjectMeta{
  145. Name: jwtSecretName,
  146. Namespace: "default",
  147. },
  148. Data: map[string][]byte{
  149. "token": []byte(createFakeJwtToken(true)),
  150. },
  151. }).Build(),
  152. namespace: "default",
  153. secretPath: "path/to/secret",
  154. },
  155. want: want{
  156. err: nil,
  157. value: "secret",
  158. },
  159. },
  160. "JwtWithCABundleSuccess": {
  161. reason: "Should read a secret successfully using a JWT auth secret store that references a k8s service account.",
  162. args: args{
  163. store: makeJWTSecretStore(svcURL, svcAccount, "", jwtAuthenticator, "", "myconjuraccount"),
  164. kube: clientfake.NewClientBuilder().
  165. WithObjects().Build(),
  166. namespace: "default",
  167. secretPath: "path/to/secret",
  168. corev1: utilfake.NewCreateTokenMock().WithToken(createFakeJwtToken(true)),
  169. },
  170. want: want{
  171. err: nil,
  172. value: "secret",
  173. },
  174. },
  175. }
  176. runTest := func(t *testing.T, _ string, tc testCase) {
  177. provider, _ := newConjurProvider(context.Background(), tc.args.store, tc.args.kube, tc.args.namespace, tc.args.corev1, &ConjurMockAPIClient{})
  178. ref := makeValidRef(tc.args.secretPath)
  179. secret, err := provider.GetSecret(context.Background(), *ref)
  180. if diff := cmp.Diff(tc.want.err, err, EquateErrors()); diff != "" {
  181. t.Errorf("\n%s\nconjur.GetSecret(...): -want error, +got error:\n%s", tc.reason, diff)
  182. }
  183. secretString := string(secret)
  184. if secretString != tc.want.value {
  185. t.Errorf("\n%s\nconjur.GetSecret(...): want value %v got %v", tc.reason, tc.want.value, secretString)
  186. }
  187. }
  188. for name, tc := range cases {
  189. t.Run(name, func(t *testing.T) {
  190. runTest(t, name, tc)
  191. })
  192. }
  193. }
  194. func TestGetAllSecrets(t *testing.T) {
  195. type args struct {
  196. store esv1.GenericStore
  197. kube kclient.Client
  198. corev1 typedcorev1.CoreV1Interface
  199. namespace string
  200. search string
  201. tags map[string]string
  202. }
  203. type want struct {
  204. err error
  205. values map[string][]byte
  206. }
  207. type testCase struct {
  208. reason string
  209. args args
  210. want want
  211. }
  212. cases := map[string]testCase{
  213. "SimpleSearchSingleResultSuccess": {
  214. reason: "Should search for secrets successfully using a simple string.",
  215. args: args{
  216. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  217. kube: clientfake.NewClientBuilder().
  218. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  219. namespace: "default",
  220. search: "secret1",
  221. },
  222. want: want{
  223. err: nil,
  224. values: map[string][]byte{
  225. "secret1": []byte("secret"),
  226. },
  227. },
  228. },
  229. "RegexSearchMultipleResultsSuccess": {
  230. reason: "Should search for secrets successfully using a regex and return multiple results.",
  231. args: args{
  232. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  233. kube: clientfake.NewClientBuilder().
  234. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  235. namespace: "default",
  236. search: "^secret[1,2]$",
  237. },
  238. want: want{
  239. err: nil,
  240. values: map[string][]byte{
  241. "secret1": []byte("secret"),
  242. "secret2": []byte("secret"),
  243. },
  244. },
  245. },
  246. "RegexSearchInvalidRegexFailure": {
  247. reason: "Should fail to search for secrets using an invalid regex.",
  248. args: args{
  249. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  250. kube: clientfake.NewClientBuilder().
  251. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  252. namespace: "default",
  253. search: "^secret[1,2", // Missing `]`
  254. },
  255. want: want{
  256. err: fmt.Errorf("could not compile find.name.regexp [%s]: %w", "^secret[1,2", errors.New("error parsing regexp: missing closing ]: `[1,2`")),
  257. values: nil,
  258. },
  259. },
  260. "SimpleSearchNoResultsSuccess": {
  261. reason: "Should search for secrets successfully using a simple string and return no results.",
  262. args: args{
  263. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  264. kube: clientfake.NewClientBuilder().
  265. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  266. namespace: "default",
  267. search: "nonexistent",
  268. },
  269. want: want{
  270. err: nil,
  271. values: map[string][]byte{},
  272. },
  273. },
  274. "TagSearchSingleResultSuccess": {
  275. reason: "Should search for secrets successfully using a tag.",
  276. args: args{
  277. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  278. kube: clientfake.NewClientBuilder().
  279. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  280. namespace: "default",
  281. tags: map[string]string{
  282. "conjur/kind": "password",
  283. },
  284. },
  285. want: want{
  286. err: nil,
  287. values: map[string][]byte{
  288. "secret2": []byte("secret"),
  289. },
  290. },
  291. },
  292. }
  293. runTest := func(t *testing.T, _ string, tc testCase) {
  294. provider, _ := newConjurProvider(context.Background(), tc.args.store, tc.args.kube, tc.args.namespace, tc.args.corev1, &ConjurMockAPIClient{})
  295. ref := makeValidFindRef(tc.args.search, tc.args.tags)
  296. secrets, err := provider.GetAllSecrets(context.Background(), *ref)
  297. if diff := cmp.Diff(tc.want.err, err, EquateErrors()); diff != "" {
  298. t.Errorf("\n%s\nconjur.GetAllSecrets(...): -want error, +got error:\n%s", tc.reason, diff)
  299. }
  300. if diff := cmp.Diff(tc.want.values, secrets); diff != "" {
  301. t.Errorf("\n%s\nconjur.GetAllSecrets(...): -want, +got:\n%s", tc.reason, diff)
  302. }
  303. }
  304. for name, tc := range cases {
  305. t.Run(name, func(t *testing.T) {
  306. runTest(t, name, tc)
  307. })
  308. }
  309. }
  310. func TestGetSecretMap(t *testing.T) {
  311. type args struct {
  312. store esv1.GenericStore
  313. kube kclient.Client
  314. corev1 typedcorev1.CoreV1Interface
  315. namespace string
  316. ref *esv1.ExternalSecretDataRemoteRef
  317. }
  318. type want struct {
  319. err error
  320. val map[string][]byte
  321. }
  322. type testCase struct {
  323. reason string
  324. args args
  325. want want
  326. }
  327. cases := map[string]testCase{
  328. "ReadJsonSecret": {
  329. reason: "Should read a JSON key value secret.",
  330. args: args{
  331. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  332. kube: clientfake.NewClientBuilder().
  333. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  334. namespace: "default",
  335. ref: makeValidRef("json_map"),
  336. },
  337. want: want{
  338. err: nil,
  339. val: map[string][]byte{
  340. "key1": []byte("value1"),
  341. "key2": []byte("value2"),
  342. },
  343. },
  344. },
  345. "ReadJsonSecretFailure": {
  346. reason: "Should fail to read a non JSON secret",
  347. args: args{
  348. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  349. kube: clientfake.NewClientBuilder().
  350. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  351. namespace: "default",
  352. ref: makeValidRef("secret1"),
  353. },
  354. want: want{
  355. err: fmt.Errorf("%w", fmt.Errorf("unable to unmarshal secret secret1: invalid character 's' looking for beginning of value")),
  356. val: nil,
  357. },
  358. },
  359. "ReadJsonSecretSpecificKey": {
  360. reason: "Should read a specific key from a JSON secret.",
  361. args: args{
  362. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  363. kube: clientfake.NewClientBuilder().
  364. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  365. namespace: "default",
  366. ref: &esv1.ExternalSecretDataRemoteRef{
  367. Key: "json_nested",
  368. Version: "default",
  369. Property: "key2",
  370. },
  371. },
  372. want: want{
  373. err: nil,
  374. val: map[string][]byte{
  375. "key3": []byte("value3"),
  376. "key4": []byte("value4"),
  377. },
  378. },
  379. },
  380. "ReadJsonSecretSpecificKeyNotFound": {
  381. reason: "Should fail to read a nonexistent key from a JSON secret.",
  382. args: args{
  383. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  384. kube: clientfake.NewClientBuilder().
  385. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  386. namespace: "default",
  387. ref: &esv1.ExternalSecretDataRemoteRef{
  388. Key: "json_map",
  389. Version: "default",
  390. Property: "key3",
  391. },
  392. },
  393. want: want{
  394. err: fmt.Errorf("%w", errors.New("error getting secret json_map: cannot find secret data for key: \"key3\"")),
  395. val: nil,
  396. },
  397. },
  398. }
  399. runTest := func(t *testing.T, _ string, tc testCase) {
  400. provider, _ := newConjurProvider(context.Background(), tc.args.store, tc.args.kube, tc.args.namespace, tc.args.corev1, &ConjurMockAPIClient{})
  401. val, err := provider.GetSecretMap(context.Background(), *tc.args.ref)
  402. if diff := cmp.Diff(tc.want.err, err, EquateErrors()); diff != "" {
  403. t.Errorf("\n%s\nconjur.GetSecretMap(...): -want error, +got error:\n%s", tc.reason, diff)
  404. }
  405. if diff := cmp.Diff(tc.want.val, val); diff != "" {
  406. t.Errorf("\n%s\nconjur.GetSecretMap(...): -want val, +got val:\n%s", tc.reason, diff)
  407. }
  408. }
  409. for name, tc := range cases {
  410. t.Run(name, func(t *testing.T) {
  411. runTest(t, name, tc)
  412. })
  413. }
  414. }
  415. func TestGetCA(t *testing.T) {
  416. type args struct {
  417. store esv1.GenericStore
  418. kube kclient.Client
  419. corev1 typedcorev1.CoreV1Interface
  420. namespace string
  421. }
  422. type want struct {
  423. err error
  424. cert string
  425. }
  426. type testCase struct {
  427. reason string
  428. args args
  429. want want
  430. }
  431. certData := `-----BEGIN CERTIFICATE-----
  432. MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw
  433. CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp
  434. Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2
  435. MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
  436. bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG
  437. ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS
  438. 7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp
  439. 0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS
  440. B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49
  441. BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ
  442. LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4
  443. DXZDjC5Ty3zfDBeWUA==
  444. -----END CERTIFICATE-----`
  445. certDataEncoded := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNHVENDQVorZ0F3SUJBZ0lRQ2VDVFphejMyY2k1UGh3TEJDb3U4ekFLQmdncWhrak9QUVFEQXpCT01Rc3cKQ1FZRFZRUUdFd0pWVXpFWE1CVUdBMVVFQ2hNT1JHbG5hVU5sY25Rc0lFbHVZeTR4SmpBa0JnTlZCQU1USFVScApaMmxEWlhKMElGUk1VeUJGUTBNZ1VETTROQ0JTYjI5MElFYzFNQjRYRFRJeE1ERXhOVEF3TURBd01Gb1hEVFEyCk1ERXhOREl6TlRrMU9Wb3dUakVMTUFrR0ExVUVCaE1DVlZNeEZ6QVZCZ05WQkFvVERrUnBaMmxEWlhKMExDQkoKYm1NdU1TWXdKQVlEVlFRREV4MUVhV2RwUTJWeWRDQlVURk1nUlVORElGQXpPRFFnVW05dmRDQkhOVEIyTUJBRwpCeXFHU000OUFnRUdCU3VCQkFBaUEySUFCTUZFb2M4UmwxQ2EzaU9DTlFmTjBNc1luZEx4ZjNjMVR6dmRsSEpTCjdjSTcrT3o2ZTJ0WUlPeVpyc244YUxOMXVkc0o3TWdUOVU3R0NoMW1NRXk3SDBjS1BHRVFRaWw4cFFnTzRDTHAKMHpWb3pwdGpuNFMxbVUxWW9JNzFWT2VWeWFOQ01FQXdIUVlEVlIwT0JCWUVGTUZSUlZCWnF6N25MRnI2SUNJUwpCNENJZkJGcU1BNEdBMVVkRHdFQi93UUVBd0lCaGpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5CkJBTURBMmdBTUdVQ01RQ0phbzFINSt6OGJsVUQyV2RzSms2RHh2M0oreXNUdkxkNmpMUmwwbWxwWXhOak95WlEKTGdHaGVRYVJuVWkvd3I0Q01FZkRGWHV4b0pHWlNaT29QSHpvUmdhTExQSXhBSlNkWXNpSnZSbUVGT21sK3dHNApEWFpEakM1VHkzemZEQmVXVUE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t"
  446. cases := map[string]testCase{
  447. "UseCABundleSuccess": {
  448. reason: "Should read a caBundle successfully.",
  449. args: args{
  450. store: makeStoreWithCA("cabundle", certDataEncoded),
  451. kube: clientfake.NewClientBuilder().
  452. WithObjects().Build(),
  453. namespace: "default",
  454. corev1: utilfake.NewCreateTokenMock().WithToken(createFakeJwtToken(true)),
  455. },
  456. want: want{
  457. err: nil,
  458. cert: certDataEncoded,
  459. },
  460. },
  461. "UseCAProviderConfigMapSuccess": {
  462. reason: "Should read a ca from a ConfigMap successfully.",
  463. args: args{
  464. store: makeStoreWithCA("configmap", ""),
  465. kube: clientfake.NewClientBuilder().
  466. WithObjects(makeFakeCASource("configmap", certData)).Build(),
  467. namespace: "default",
  468. corev1: utilfake.NewCreateTokenMock().WithToken(createFakeJwtToken(true)),
  469. },
  470. want: want{
  471. err: nil,
  472. cert: certDataEncoded,
  473. },
  474. },
  475. "UseCAProviderSecretSuccess": {
  476. reason: "Should read a ca from a Secret successfully.",
  477. args: args{
  478. store: makeStoreWithCA("secret", ""),
  479. kube: clientfake.NewClientBuilder().
  480. WithObjects(makeFakeCASource("secret", certData)).Build(),
  481. namespace: "default",
  482. corev1: utilfake.NewCreateTokenMock().WithToken(createFakeJwtToken(true)),
  483. },
  484. want: want{
  485. err: nil,
  486. cert: certDataEncoded,
  487. },
  488. },
  489. }
  490. runTest := func(t *testing.T, _ string, tc testCase) {
  491. provider, _ := newConjurProvider(context.Background(), tc.args.store, tc.args.kube, tc.args.namespace, tc.args.corev1, &ConjurMockAPIClient{})
  492. _, err := provider.GetSecret(context.Background(), esv1.ExternalSecretDataRemoteRef{
  493. Key: "path/to/secret",
  494. })
  495. if diff := cmp.Diff(tc.want.err, err, EquateErrors()); diff != "" {
  496. t.Errorf("\n%s\nconjur.GetCA(...): -want error, +got error:\n%s", tc.reason, diff)
  497. }
  498. }
  499. for name, tc := range cases {
  500. t.Run(name, func(t *testing.T) {
  501. runTest(t, name, tc)
  502. })
  503. }
  504. }
  505. func makeAPIKeySecretStore(svcURL, svcUser, svcApikey, svcAccount string) *esv1.SecretStore {
  506. uref := &esmeta.SecretKeySelector{
  507. Name: "user",
  508. Key: "conjur-hostid",
  509. }
  510. if svcUser == "" {
  511. uref = nil
  512. }
  513. aref := &esmeta.SecretKeySelector{
  514. Name: "apikey",
  515. Key: "conjur-apikey",
  516. }
  517. if svcApikey == "" {
  518. aref = nil
  519. }
  520. store := &esv1.SecretStore{
  521. Spec: esv1.SecretStoreSpec{
  522. Provider: &esv1.SecretStoreProvider{
  523. Conjur: &esv1.ConjurProvider{
  524. URL: svcURL,
  525. Auth: esv1.ConjurAuth{
  526. APIKey: &esv1.ConjurAPIKey{
  527. Account: svcAccount,
  528. UserRef: uref,
  529. APIKeyRef: aref,
  530. },
  531. },
  532. },
  533. },
  534. },
  535. }
  536. return store
  537. }
  538. func makeJWTSecretStore(svcURL, serviceAccountName, secretName, jwtServiceID, jwtHostID, conjurAccount string) *esv1.SecretStore {
  539. serviceAccountRef := &esmeta.ServiceAccountSelector{
  540. Name: serviceAccountName,
  541. Audiences: []string{"conjur"},
  542. }
  543. if serviceAccountName == "" {
  544. serviceAccountRef = nil
  545. }
  546. secretRef := &esmeta.SecretKeySelector{
  547. Name: secretName,
  548. Key: "token",
  549. }
  550. if secretName == "" {
  551. secretRef = nil
  552. }
  553. store := &esv1.SecretStore{
  554. Spec: esv1.SecretStoreSpec{
  555. Provider: &esv1.SecretStoreProvider{
  556. Conjur: &esv1.ConjurProvider{
  557. URL: svcURL,
  558. Auth: esv1.ConjurAuth{
  559. Jwt: &esv1.ConjurJWT{
  560. Account: conjurAccount,
  561. ServiceID: jwtServiceID,
  562. ServiceAccountRef: serviceAccountRef,
  563. SecretRef: secretRef,
  564. HostID: jwtHostID,
  565. },
  566. },
  567. },
  568. },
  569. },
  570. }
  571. return store
  572. }
  573. func makeStoreWithCA(caSource, caData string) *esv1.SecretStore {
  574. store := makeJWTSecretStore(svcURL, "conjur", "", jwtAuthnService, "", "myconjuraccount")
  575. if caSource == "secret" {
  576. store.Spec.Provider.Conjur.CAProvider = &esv1.CAProvider{
  577. Type: esv1.CAProviderTypeSecret,
  578. Name: "conjur-cert",
  579. Key: "ca",
  580. }
  581. } else if caSource == "configmap" {
  582. store.Spec.Provider.Conjur.CAProvider = &esv1.CAProvider{
  583. Type: esv1.CAProviderTypeConfigMap,
  584. Name: "conjur-cert",
  585. Key: "ca",
  586. }
  587. } else {
  588. store.Spec.Provider.Conjur.CABundle = caData
  589. }
  590. return store
  591. }
  592. func makeNoAuthSecretStore(svcURL string) *esv1.SecretStore {
  593. store := &esv1.SecretStore{
  594. Spec: esv1.SecretStoreSpec{
  595. Provider: &esv1.SecretStoreProvider{
  596. Conjur: &esv1.ConjurProvider{
  597. URL: svcURL,
  598. },
  599. },
  600. },
  601. }
  602. return store
  603. }
  604. func makeFakeAPIKeySecrets() []kclient.Object {
  605. return []kclient.Object{
  606. &corev1.Secret{
  607. ObjectMeta: metav1.ObjectMeta{
  608. Name: "user",
  609. Namespace: "default",
  610. },
  611. Data: map[string][]byte{
  612. "conjur-hostid": []byte("myhostid"),
  613. },
  614. },
  615. &corev1.Secret{
  616. ObjectMeta: metav1.ObjectMeta{
  617. Name: "apikey",
  618. Namespace: "default",
  619. },
  620. Data: map[string][]byte{
  621. "conjur-apikey": []byte("apikey"),
  622. },
  623. },
  624. }
  625. }
  626. func makeFakeCASource(kind, caData string) kclient.Object {
  627. if kind == "secret" {
  628. return &corev1.Secret{
  629. ObjectMeta: metav1.ObjectMeta{
  630. Name: "conjur-cert",
  631. Namespace: "default",
  632. },
  633. Data: map[string][]byte{
  634. "ca": []byte(caData),
  635. },
  636. }
  637. }
  638. return &corev1.ConfigMap{
  639. ObjectMeta: metav1.ObjectMeta{
  640. Name: "conjur-cert",
  641. Namespace: "default",
  642. },
  643. Data: map[string]string{
  644. "ca": caData,
  645. },
  646. }
  647. }
  648. func createFakeJwtToken(expires bool) string {
  649. signingKey := []byte("fakekey")
  650. token := jwt.New(jwt.SigningMethodHS256)
  651. claims := token.Claims.(jwt.MapClaims)
  652. if expires {
  653. claims["exp"] = time.Now().Add(time.Minute * 30).Unix()
  654. }
  655. jwtTokenString, err := token.SignedString(signingKey)
  656. if err != nil {
  657. panic(err)
  658. }
  659. return jwtTokenString
  660. }
  661. // ConjurMockAPIClient is a mock implementation of the ApiClient interface.
  662. type ConjurMockAPIClient struct {
  663. }
  664. func (c *ConjurMockAPIClient) NewClientFromKey(_ conjurapi.Config, _ authn.LoginPair) (SecretsClient, error) {
  665. return &fake.ConjurMockClient{}, nil
  666. }
  667. func (c *ConjurMockAPIClient) NewClientFromJWT(_ conjurapi.Config) (SecretsClient, error) {
  668. return &fake.ConjurMockClient{}, nil
  669. }
  670. // EquateErrors returns true if the supplied errors are of the same type and
  671. // produce identical strings. This mirrors the error comparison behavior of
  672. // https://github.com/go-test/deep, which most Crossplane tests targeted before
  673. // we switched to go-cmp.
  674. //
  675. // This differs from cmpopts.EquateErrors, which does not test for error strings
  676. // and instead returns whether one error 'is' (in the errors.Is sense) the
  677. // other.
  678. func EquateErrors() cmp.Option {
  679. return cmp.Comparer(func(a, b error) bool {
  680. if a == nil || b == nil {
  681. return a == nil && b == nil
  682. }
  683. av := reflect.ValueOf(a)
  684. bv := reflect.ValueOf(b)
  685. if av.Type() != bv.Type() {
  686. return false
  687. }
  688. return a.Error() == b.Error()
  689. })
  690. }