provider_test.go 22 KB

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