keyvault_auth_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  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 keyvault
  14. import (
  15. "context"
  16. "net/http"
  17. "os"
  18. "strings"
  19. "testing"
  20. "github.com/Azure/go-autorest/autorest"
  21. "github.com/Azure/go-autorest/autorest/adal"
  22. tassert "github.com/stretchr/testify/assert"
  23. corev1 "k8s.io/api/core/v1"
  24. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  25. pointer "k8s.io/utils/ptr"
  26. "sigs.k8s.io/controller-runtime/pkg/client"
  27. clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
  28. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  29. v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
  30. utilfake "github.com/external-secrets/external-secrets/runtime/util/fake"
  31. )
  32. var vaultURL = "https://local.vault.url"
  33. var mockCertificate = `
  34. -----BEGIN CERTIFICATE-----
  35. MIICBzCCAbGgAwIBAgIUSoCD1fgywDbmeRaGrkYzGWUd1wMwDQYJKoZIhvcNAQEL
  36. BQAwcTELMAkGA1UEBhMCQVoxGTAXBgNVBAgMEE1vY2sgQ2VydGlmaWNhdGUxMzAx
  37. BgNVBAoMKkV4dGVybmFsIFNlY3JldHMgT3BlcmF0b3IgTW9jayBDZXJ0aWZpY2F0
  38. ZTESMBAGA1UEAwwJTW9jayBDZXJ0MB4XDTI0MDUwODA4NDkzMFoXDTI1MDUwODA4
  39. NDkzMFowcTELMAkGA1UEBhMCQVoxGTAXBgNVBAgMEE1vY2sgQ2VydGlmaWNhdGUx
  40. MzAxBgNVBAoMKkV4dGVybmFsIFNlY3JldHMgT3BlcmF0b3IgTW9jayBDZXJ0aWZp
  41. Y2F0ZTESMBAGA1UEAwwJTW9jayBDZXJ0MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
  42. ALkU1YgMk1Dk149F/HsHA0TjzLwfDa9tT0cfqA1u0hoJkb2r9jdWUyiugGaEz/PU
  43. TGWrvp8aiXPrGuu5Y6PY27ECAwEAAaMhMB8wHQYDVR0OBBYEFAMB0YwnYjUm00og
  44. kGce8Yhr4I03MA0GCSqGSIb3DQEBCwUAA0EAr0BMs/3hIOdZc0WHZUCTZ0GGor3G
  45. ViYUPHOw8z6UZGPGN6qiAejmkT6uP3LkkSW+7TIIQ1pkQxcn5xfFJXBexw==
  46. -----END CERTIFICATE-----
  47. -----BEGIN PRIVATE KEY-----
  48. MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAuRTViAyTUOTXj0X8
  49. ewcDROPMvB8Nr21PRx+oDW7SGgmRvav2N1ZTKK6AZoTP89RMZau+nxqJc+sa67lj
  50. o9jbsQIDAQABAkA35CnDpwCJykGqW5kuUeTT1fMK0FnioyDwuoeWXuQFxmB6Md89
  51. +ABxyjAt3nmwRRVBrVFdNibb9asR5KFHwn1NAiEA4NlrSnJrY1xODIjEXf0fLTwu
  52. wpyUO1lX585OjYDiOYsCIQDSuP4ttH/1Hg3f9veEE4RgDEk+QcisrzF8q4Oa5sDP
  53. MwIgfejiTtcR0ZsPza8Mn0EuIyuPV8VMsItQUWtSy6R/ig8CIQC86cBmNUXp+HGz
  54. 8fLg46ZvfVREjjFcLwwMmq83tdvxZQIgPAbezuRCrduH19xgMO8BXndS5DAovgvE
  55. /MpQnEyQtVA=
  56. -----END PRIVATE KEY-----
  57. `
  58. func TestNewClientManagedIdentityNoNeedForCredentials(t *testing.T) {
  59. namespace := "internal"
  60. identityID := "1234"
  61. authType := esv1.AzureManagedIdentity
  62. store := esv1.SecretStore{
  63. ObjectMeta: metav1.ObjectMeta{
  64. Namespace: namespace,
  65. },
  66. Spec: esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{AzureKV: &esv1.AzureKVProvider{
  67. AuthType: &authType,
  68. IdentityID: &identityID,
  69. VaultURL: &vaultURL,
  70. }}},
  71. }
  72. k8sClient := clientfake.NewClientBuilder().Build()
  73. az := &Azure{
  74. crClient: k8sClient,
  75. namespace: namespace,
  76. provider: store.Spec.Provider.AzureKV,
  77. store: &store,
  78. }
  79. authorizer, err := az.authorizerForManagedIdentity()
  80. if err != nil {
  81. // On non Azure environment, MSI auth not available, so this error should be returned
  82. tassert.EqualError(t, err, "failed to get oauth token from MSI: MSI not available")
  83. } else {
  84. // On Azure (where GitHub Actions are running) a secretClient is returned, as only an Authorizer is configured, but no token is requested for MI
  85. tassert.NotNil(t, authorizer)
  86. }
  87. }
  88. func TestGetAuthorizorForWorkloadIdentity(t *testing.T) {
  89. const (
  90. tenantID = "my-tenant-id"
  91. clientID = "my-client-id"
  92. azAccessToken = "my-access-token"
  93. saToken = "FAKETOKEN"
  94. saName = "az-wi"
  95. namespace = "default"
  96. secretName = "mi-spec"
  97. )
  98. // create a temporary file to imitate
  99. // azure workload identity webhook
  100. // see AZURE_FEDERATED_TOKEN_FILE
  101. tf, err := os.CreateTemp("", "")
  102. tassert.Nil(t, err)
  103. defer os.RemoveAll(tf.Name())
  104. _, err = tf.WriteString(saToken)
  105. tassert.Nil(t, err)
  106. tokenFile := tf.Name()
  107. authType := esv1.AzureWorkloadIdentity
  108. defaultProvider := &esv1.AzureKVProvider{
  109. VaultURL: &vaultURL,
  110. AuthType: &authType,
  111. ServiceAccountRef: &v1.ServiceAccountSelector{
  112. Name: saName,
  113. },
  114. }
  115. type testCase struct {
  116. name string
  117. provider *esv1.AzureKVProvider
  118. k8sObjects []client.Object
  119. prep func(*testing.T)
  120. expErr string
  121. }
  122. for _, row := range []testCase{
  123. {
  124. name: "missing service account",
  125. provider: defaultProvider,
  126. expErr: "serviceaccounts \"" + saName + "\" not found",
  127. },
  128. {
  129. name: "missing webhook env vars",
  130. provider: &esv1.AzureKVProvider{},
  131. expErr: "missing environment variables. AZURE_CLIENT_ID, AZURE_TENANT_ID and AZURE_FEDERATED_TOKEN_FILE must be set",
  132. },
  133. {
  134. name: "missing workload identity token file",
  135. provider: &esv1.AzureKVProvider{},
  136. prep: func(t *testing.T) {
  137. t.Setenv("AZURE_CLIENT_ID", clientID)
  138. t.Setenv("AZURE_TENANT_ID", tenantID)
  139. t.Setenv("AZURE_FEDERATED_TOKEN_FILE", "invalid file")
  140. },
  141. expErr: "unable to read token file invalid file: open invalid file: no such file or directory",
  142. },
  143. {
  144. name: "correct workload identity",
  145. provider: &esv1.AzureKVProvider{},
  146. prep: func(t *testing.T) {
  147. t.Setenv("AZURE_CLIENT_ID", clientID)
  148. t.Setenv("AZURE_TENANT_ID", tenantID)
  149. t.Setenv("AZURE_FEDERATED_TOKEN_FILE", tokenFile)
  150. },
  151. },
  152. {
  153. name: "missing sa annotations, tenantID, and clientId/tenantId AuthSecretRef",
  154. provider: defaultProvider,
  155. k8sObjects: []client.Object{
  156. &corev1.ServiceAccount{
  157. ObjectMeta: metav1.ObjectMeta{
  158. Name: saName,
  159. Namespace: namespace,
  160. Annotations: map[string]string{},
  161. },
  162. },
  163. },
  164. expErr: "missing clientID: either serviceAccountRef or service account annotation 'azure.workload.identity/client-id' is missing",
  165. },
  166. {
  167. name: "duplicated clientId",
  168. provider: &esv1.AzureKVProvider{
  169. VaultURL: &vaultURL,
  170. AuthType: &authType,
  171. TenantID: pointer.To(tenantID),
  172. ServiceAccountRef: &v1.ServiceAccountSelector{
  173. Name: saName,
  174. },
  175. AuthSecretRef: &esv1.AzureKVAuth{
  176. ClientID: &v1.SecretKeySelector{Name: secretName, Namespace: pointer.To(namespace), Key: clientID},
  177. TenantID: &v1.SecretKeySelector{Name: secretName, Namespace: pointer.To(namespace), Key: tenantID},
  178. },
  179. },
  180. k8sObjects: []client.Object{
  181. &corev1.ServiceAccount{
  182. ObjectMeta: metav1.ObjectMeta{
  183. Name: saName,
  184. Namespace: namespace,
  185. Annotations: map[string]string{
  186. AnnotationClientID: clientID,
  187. AnnotationTenantID: tenantID,
  188. },
  189. },
  190. },
  191. &corev1.Secret{
  192. ObjectMeta: metav1.ObjectMeta{
  193. Name: secretName,
  194. Namespace: namespace,
  195. },
  196. Data: map[string][]byte{
  197. clientID: []byte("clientid"),
  198. tenantID: []byte("tenantid"),
  199. },
  200. },
  201. },
  202. expErr: "multiple clientID found. Check secretRef and serviceAccountRef",
  203. },
  204. {
  205. name: "duplicated tenantId",
  206. provider: &esv1.AzureKVProvider{
  207. VaultURL: &vaultURL,
  208. AuthType: &authType,
  209. TenantID: pointer.To(tenantID),
  210. ServiceAccountRef: &v1.ServiceAccountSelector{
  211. Name: saName,
  212. },
  213. },
  214. k8sObjects: []client.Object{
  215. &corev1.ServiceAccount{
  216. ObjectMeta: metav1.ObjectMeta{
  217. Name: saName,
  218. Namespace: namespace,
  219. Annotations: map[string]string{
  220. AnnotationClientID: clientID,
  221. AnnotationTenantID: tenantID,
  222. },
  223. },
  224. },
  225. },
  226. expErr: "multiple tenantID found. Check secretRef, 'spec.provider.azurekv.tenantId', and serviceAccountRef",
  227. },
  228. {
  229. name: "successful case #1: ClientID, TenantID from ServiceAccountRef",
  230. provider: defaultProvider,
  231. k8sObjects: []client.Object{
  232. &corev1.ServiceAccount{
  233. ObjectMeta: metav1.ObjectMeta{
  234. Name: saName,
  235. Namespace: namespace,
  236. Annotations: map[string]string{
  237. AnnotationClientID: clientID,
  238. AnnotationTenantID: tenantID,
  239. },
  240. },
  241. },
  242. },
  243. },
  244. {
  245. name: "successful case #2: ClientID, TenantID from AuthSecretRef",
  246. provider: &esv1.AzureKVProvider{
  247. VaultURL: &vaultURL,
  248. AuthType: &authType,
  249. ServiceAccountRef: &v1.ServiceAccountSelector{
  250. Name: saName,
  251. },
  252. AuthSecretRef: &esv1.AzureKVAuth{
  253. ClientID: &v1.SecretKeySelector{Name: secretName, Namespace: pointer.To(namespace), Key: clientID},
  254. TenantID: &v1.SecretKeySelector{Name: secretName, Namespace: pointer.To(namespace), Key: tenantID},
  255. },
  256. },
  257. k8sObjects: []client.Object{
  258. &corev1.ServiceAccount{
  259. ObjectMeta: metav1.ObjectMeta{
  260. Name: saName,
  261. Namespace: namespace,
  262. Annotations: map[string]string{},
  263. },
  264. },
  265. &corev1.Secret{
  266. ObjectMeta: metav1.ObjectMeta{
  267. Name: secretName,
  268. Namespace: namespace,
  269. },
  270. Data: map[string][]byte{
  271. clientID: []byte("clientid"),
  272. tenantID: []byte("tenantid"),
  273. },
  274. },
  275. },
  276. },
  277. {
  278. name: "successful case #3: ClientID from AuthSecretRef, TenantID from provider",
  279. provider: &esv1.AzureKVProvider{
  280. VaultURL: &vaultURL,
  281. AuthType: &authType,
  282. TenantID: pointer.To(tenantID),
  283. ServiceAccountRef: &v1.ServiceAccountSelector{
  284. Name: saName,
  285. },
  286. AuthSecretRef: &esv1.AzureKVAuth{
  287. ClientID: &v1.SecretKeySelector{Name: secretName, Namespace: pointer.To(namespace), Key: clientID},
  288. },
  289. },
  290. k8sObjects: []client.Object{
  291. &corev1.ServiceAccount{
  292. ObjectMeta: metav1.ObjectMeta{
  293. Name: saName,
  294. Namespace: namespace,
  295. Annotations: map[string]string{},
  296. },
  297. },
  298. &corev1.Secret{
  299. ObjectMeta: metav1.ObjectMeta{
  300. Name: secretName,
  301. Namespace: namespace,
  302. },
  303. Data: map[string][]byte{
  304. clientID: []byte("clientid"),
  305. },
  306. },
  307. },
  308. },
  309. } {
  310. t.Run(row.name, func(t *testing.T) {
  311. store := esv1.SecretStore{
  312. Spec: esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{
  313. AzureKV: row.provider,
  314. }},
  315. }
  316. k8sClient := clientfake.NewClientBuilder().
  317. WithObjects(row.k8sObjects...).
  318. Build()
  319. az := &Azure{
  320. store: &store,
  321. namespace: namespace,
  322. crClient: k8sClient,
  323. kubeClient: utilfake.NewCreateTokenMock().WithToken(saToken),
  324. provider: store.Spec.Provider.AzureKV,
  325. }
  326. tokenProvider := func(ctx context.Context, token, clientID, tenantID, aadEndpoint, kvResource string) (adal.OAuthTokenProvider, error) {
  327. tassert.Equal(t, token, saToken)
  328. tassert.Equal(t, clientID, clientID)
  329. tassert.Equal(t, tenantID, tenantID)
  330. return &tokenProvider{accessToken: azAccessToken}, nil
  331. }
  332. if row.prep != nil {
  333. row.prep(t)
  334. }
  335. authorizer, err := az.authorizerForWorkloadIdentity(context.Background(), tokenProvider)
  336. if row.expErr == "" {
  337. tassert.NotNil(t, authorizer)
  338. tassert.Equal(t, getTokenFromAuthorizer(t, authorizer), azAccessToken)
  339. } else {
  340. tassert.EqualError(t, err, row.expErr)
  341. }
  342. })
  343. }
  344. }
  345. func TestAuth(t *testing.T) {
  346. defaultStore := esv1.SecretStore{
  347. ObjectMeta: metav1.ObjectMeta{
  348. Namespace: "default",
  349. },
  350. Spec: esv1.SecretStoreSpec{
  351. Provider: &esv1.SecretStoreProvider{},
  352. },
  353. }
  354. authType := esv1.AzureServicePrincipal
  355. type testCase struct {
  356. name string
  357. provider *esv1.AzureKVProvider
  358. store esv1.GenericStore
  359. objects []client.Object
  360. expErr string
  361. }
  362. for _, row := range []testCase{
  363. {
  364. name: "bad config",
  365. expErr: "missing secretRef in provider config",
  366. store: &defaultStore,
  367. provider: &esv1.AzureKVProvider{
  368. AuthType: &authType,
  369. VaultURL: &vaultURL,
  370. TenantID: new("mytenant"),
  371. },
  372. },
  373. {
  374. name: "bad config",
  375. expErr: "missing accessKeyID/secretAccessKey in store config",
  376. store: &defaultStore,
  377. provider: &esv1.AzureKVProvider{
  378. AuthType: &authType,
  379. VaultURL: &vaultURL,
  380. TenantID: new("mytenant"),
  381. AuthSecretRef: &esv1.AzureKVAuth{},
  382. },
  383. },
  384. {
  385. name: "bad config: missing secret",
  386. expErr: "cannot get Kubernetes secret \"password\" from namespace \"default\": secrets \"password\" not found",
  387. store: &defaultStore,
  388. provider: &esv1.AzureKVProvider{
  389. AuthType: &authType,
  390. VaultURL: &vaultURL,
  391. TenantID: new("mytenant"),
  392. AuthSecretRef: &esv1.AzureKVAuth{
  393. ClientSecret: &v1.SecretKeySelector{Name: "password"},
  394. ClientID: &v1.SecretKeySelector{Name: "password"},
  395. },
  396. },
  397. },
  398. {
  399. name: "cluster secret store",
  400. expErr: "cannot get Kubernetes secret \"password\" from namespace \"foo\": secrets \"password\" not found",
  401. store: &esv1.ClusterSecretStore{
  402. TypeMeta: metav1.TypeMeta{
  403. Kind: esv1.ClusterSecretStoreKind,
  404. },
  405. Spec: esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{}},
  406. },
  407. provider: &esv1.AzureKVProvider{
  408. AuthType: &authType,
  409. VaultURL: &vaultURL,
  410. TenantID: new("mytenant"),
  411. AuthSecretRef: &esv1.AzureKVAuth{
  412. ClientSecret: &v1.SecretKeySelector{Name: "password", Namespace: new("foo")},
  413. ClientID: &v1.SecretKeySelector{Name: "password", Namespace: new("foo")},
  414. },
  415. },
  416. },
  417. {
  418. name: "correct cluster secret store with ClientSecret",
  419. objects: []client.Object{&corev1.Secret{
  420. ObjectMeta: metav1.ObjectMeta{
  421. Name: "password",
  422. Namespace: "foo",
  423. },
  424. Data: map[string][]byte{
  425. "id": []byte("foo"),
  426. "secret": []byte("bar"),
  427. },
  428. }},
  429. store: &esv1.ClusterSecretStore{
  430. TypeMeta: metav1.TypeMeta{
  431. Kind: esv1.ClusterSecretStoreKind,
  432. },
  433. Spec: esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{}},
  434. },
  435. provider: &esv1.AzureKVProvider{
  436. AuthType: &authType,
  437. VaultURL: &vaultURL,
  438. TenantID: new("mytenant"),
  439. AuthSecretRef: &esv1.AzureKVAuth{
  440. ClientSecret: &v1.SecretKeySelector{Name: "password", Namespace: new("foo"), Key: "secret"},
  441. ClientID: &v1.SecretKeySelector{Name: "password", Namespace: new("foo"), Key: "id"},
  442. },
  443. },
  444. },
  445. {
  446. name: "bad config: both clientSecret and clientCredentials are configured",
  447. expErr: "both clientSecret and clientCredentials set",
  448. objects: []client.Object{&corev1.Secret{
  449. ObjectMeta: metav1.ObjectMeta{
  450. Name: "password",
  451. Namespace: "foo",
  452. },
  453. Data: map[string][]byte{
  454. "id": []byte("foo"),
  455. "certificate": []byte("bar"),
  456. "secret": []byte("bar"),
  457. },
  458. }},
  459. store: &esv1.ClusterSecretStore{
  460. TypeMeta: metav1.TypeMeta{
  461. Kind: esv1.ClusterSecretStoreKind,
  462. },
  463. Spec: esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{}},
  464. },
  465. provider: &esv1.AzureKVProvider{
  466. AuthType: &authType,
  467. VaultURL: &vaultURL,
  468. TenantID: new("mytenant"),
  469. AuthSecretRef: &esv1.AzureKVAuth{
  470. ClientID: &v1.SecretKeySelector{Name: "password", Namespace: new("foo"), Key: "id"},
  471. ClientCertificate: &v1.SecretKeySelector{Name: "password", Namespace: new("foo"), Key: "certificate"},
  472. ClientSecret: &v1.SecretKeySelector{Name: "password", Namespace: new("foo"), Key: "secret"},
  473. },
  474. },
  475. },
  476. {
  477. name: "bad config: no valid client certificate in pem file",
  478. expErr: "failed to get oauth token from certificate auth: failed to decode certificate: no certificate found in PEM file",
  479. objects: []client.Object{&corev1.Secret{
  480. ObjectMeta: metav1.ObjectMeta{
  481. Name: "password",
  482. Namespace: "foo",
  483. },
  484. Data: map[string][]byte{
  485. "id": []byte("foo"),
  486. "certificate": []byte("bar"),
  487. },
  488. }},
  489. store: &esv1.ClusterSecretStore{
  490. TypeMeta: metav1.TypeMeta{
  491. Kind: esv1.ClusterSecretStoreKind,
  492. },
  493. Spec: esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{}},
  494. },
  495. provider: &esv1.AzureKVProvider{
  496. AuthType: &authType,
  497. VaultURL: &vaultURL,
  498. TenantID: new("mytenant"),
  499. AuthSecretRef: &esv1.AzureKVAuth{
  500. ClientID: &v1.SecretKeySelector{Name: "password", Namespace: new("foo"), Key: "id"},
  501. ClientCertificate: &v1.SecretKeySelector{Name: "password", Namespace: new("foo"), Key: "certificate"},
  502. },
  503. },
  504. },
  505. {
  506. name: "correct configuration with certificate authentication",
  507. objects: []client.Object{&corev1.Secret{
  508. ObjectMeta: metav1.ObjectMeta{
  509. Name: "password",
  510. Namespace: "foo",
  511. },
  512. Data: map[string][]byte{
  513. "id": []byte("foo"),
  514. "certificate": []byte(mockCertificate),
  515. },
  516. }},
  517. store: &esv1.ClusterSecretStore{
  518. TypeMeta: metav1.TypeMeta{
  519. Kind: esv1.ClusterSecretStoreKind,
  520. },
  521. Spec: esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{}},
  522. },
  523. provider: &esv1.AzureKVProvider{
  524. AuthType: &authType,
  525. VaultURL: &vaultURL,
  526. TenantID: new("mytenant"),
  527. AuthSecretRef: &esv1.AzureKVAuth{
  528. ClientID: &v1.SecretKeySelector{Name: "password", Namespace: new("foo"), Key: "id"},
  529. ClientCertificate: &v1.SecretKeySelector{Name: "password", Namespace: new("foo"), Key: "certificate"},
  530. },
  531. },
  532. },
  533. } {
  534. t.Run(row.name, func(t *testing.T) {
  535. k8sClient := clientfake.NewClientBuilder().WithObjects(row.objects...).Build()
  536. spec := row.store.GetSpec()
  537. spec.Provider.AzureKV = row.provider
  538. az := &Azure{
  539. crClient: k8sClient,
  540. namespace: "default",
  541. provider: spec.Provider.AzureKV,
  542. store: row.store,
  543. }
  544. authorizer, err := az.authorizerForServicePrincipal(context.Background())
  545. if row.expErr == "" {
  546. tassert.Nil(t, err)
  547. tassert.NotNil(t, authorizer)
  548. } else {
  549. tassert.EqualError(t, err, row.expErr)
  550. }
  551. })
  552. }
  553. }
  554. func getTokenFromAuthorizer(t *testing.T, authorizer autorest.Authorizer) string {
  555. rq, _ := http.NewRequest("POST", "http://example.com", http.NoBody)
  556. _, err := authorizer.WithAuthorization()(
  557. autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
  558. return rq, nil
  559. })).Prepare(rq)
  560. tassert.Nil(t, err)
  561. return strings.TrimPrefix(rq.Header.Get("Authorization"), "Bearer ")
  562. }