gitlab_test.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  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 gitlab
  13. import (
  14. "context"
  15. "encoding/json"
  16. "fmt"
  17. "net/http"
  18. "reflect"
  19. "strings"
  20. "testing"
  21. "github.com/google/uuid"
  22. tassert "github.com/stretchr/testify/assert"
  23. "github.com/xanzy/go-gitlab"
  24. "github.com/yandex-cloud/go-sdk/iamkey"
  25. corev1 "k8s.io/api/core/v1"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
  28. clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
  29. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  30. esv1meta "github.com/external-secrets/external-secrets/apis/meta/v1"
  31. fakegitlab "github.com/external-secrets/external-secrets/pkg/provider/gitlab/fake"
  32. )
  33. const (
  34. project = "my-Project"
  35. username = "user-name"
  36. userkey = "user-key"
  37. environment = "prod"
  38. projectvalue = "projectvalue"
  39. groupvalue = "groupvalue"
  40. groupid = "groupId"
  41. defaultErrorMessage = "[%d] unexpected error: [%s], expected: [%s]"
  42. errMissingCredentials = "credentials are empty"
  43. findTestPrefix = "test.*"
  44. )
  45. type secretManagerTestCase struct {
  46. mockProjectsClient *fakegitlab.GitlabMockProjectsClient
  47. mockProjectVarClient *fakegitlab.GitlabMockProjectVariablesClient
  48. mockGroupVarClient *fakegitlab.GitlabMockGroupVariablesClient
  49. apiInputProjectID string
  50. apiInputKey string
  51. apiInputEnv string
  52. projectAPIOutput *gitlab.ProjectVariable
  53. projectAPIResponse *gitlab.Response
  54. projectGroupsAPIOutput []*gitlab.ProjectGroup
  55. projectGroupsAPIResponse *gitlab.Response
  56. groupAPIOutput *gitlab.GroupVariable
  57. groupAPIResponse *gitlab.Response
  58. ref *esv1beta1.ExternalSecretDataRemoteRef
  59. refFind *esv1beta1.ExternalSecretFind
  60. projectID string
  61. groupIDs []string
  62. inheritFromGroups bool
  63. apiErr error
  64. expectError string
  65. expectedSecret string
  66. expectedValidationResult esv1beta1.ValidationResult
  67. // for testing secretmap
  68. expectedData map[string][]byte
  69. }
  70. func makeValidSecretManagerTestCase() *secretManagerTestCase {
  71. smtc := secretManagerTestCase{
  72. mockProjectsClient: &fakegitlab.GitlabMockProjectsClient{},
  73. mockProjectVarClient: &fakegitlab.GitlabMockProjectVariablesClient{},
  74. mockGroupVarClient: &fakegitlab.GitlabMockGroupVariablesClient{},
  75. apiInputProjectID: makeValidAPIInputProjectID(),
  76. apiInputKey: makeValidAPIInputKey(),
  77. apiInputEnv: makeValidEnvironment(),
  78. ref: makeValidRef(),
  79. refFind: makeValidFindRef(),
  80. projectID: makeValidProjectID(),
  81. groupIDs: makeEmptyGroupIds(),
  82. projectAPIOutput: makeValidProjectAPIOutput(),
  83. projectAPIResponse: makeValidProjectAPIResponse(),
  84. projectGroupsAPIOutput: makeValidProjectGroupsAPIOutput(),
  85. projectGroupsAPIResponse: makeValidProjectGroupsAPIResponse(),
  86. groupAPIOutput: makeValidGroupAPIOutput(),
  87. groupAPIResponse: makeValidGroupAPIResponse(),
  88. apiErr: nil,
  89. expectError: "",
  90. expectedSecret: "",
  91. expectedValidationResult: esv1beta1.ValidationResultReady,
  92. expectedData: map[string][]byte{},
  93. }
  94. smtc.mockProjectVarClient.WithValue(smtc.projectAPIOutput, smtc.projectAPIResponse, smtc.apiErr)
  95. smtc.mockGroupVarClient.WithValue(smtc.groupAPIOutput, smtc.groupAPIResponse, smtc.apiErr)
  96. return &smtc
  97. }
  98. func makeValidRef() *esv1beta1.ExternalSecretDataRemoteRef {
  99. return &esv1beta1.ExternalSecretDataRemoteRef{
  100. Key: "test-secret",
  101. Version: "default",
  102. }
  103. }
  104. func makeValidFindRef() *esv1beta1.ExternalSecretFind {
  105. return &esv1beta1.ExternalSecretFind{}
  106. }
  107. func makeValidProjectID() string {
  108. return "projectId"
  109. }
  110. func makeEmptyGroupIds() []string {
  111. return []string{}
  112. }
  113. func makeFindName(regexp string) *esv1beta1.FindName {
  114. return &esv1beta1.FindName{
  115. RegExp: regexp,
  116. }
  117. }
  118. func makeValidAPIInputProjectID() string {
  119. return "testID"
  120. }
  121. func makeValidAPIInputKey() string {
  122. return "testKey"
  123. }
  124. func makeValidEnvironment() string {
  125. return environment
  126. }
  127. func makeValidProjectAPIResponse() *gitlab.Response {
  128. return &gitlab.Response{
  129. Response: &http.Response{
  130. StatusCode: http.StatusOK,
  131. },
  132. }
  133. }
  134. func makeValidProjectGroupsAPIResponse() *gitlab.Response {
  135. return &gitlab.Response{
  136. Response: &http.Response{
  137. StatusCode: http.StatusOK,
  138. },
  139. }
  140. }
  141. func makeValidGroupAPIResponse() *gitlab.Response {
  142. return &gitlab.Response{
  143. Response: &http.Response{
  144. StatusCode: http.StatusOK,
  145. },
  146. }
  147. }
  148. func makeValidProjectAPIOutput() *gitlab.ProjectVariable {
  149. return &gitlab.ProjectVariable{
  150. Key: "testKey",
  151. Value: "",
  152. EnvironmentScope: environment,
  153. }
  154. }
  155. func makeValidProjectGroupsAPIOutput() []*gitlab.ProjectGroup {
  156. return []*gitlab.ProjectGroup{{
  157. ID: 1,
  158. Name: "Group (1)",
  159. FullPath: "foo",
  160. }, {
  161. ID: 100,
  162. Name: "Group (100)",
  163. FullPath: "foo/bar/baz",
  164. }, {
  165. ID: 10,
  166. Name: "Group (10)",
  167. FullPath: "foo/bar",
  168. }}
  169. }
  170. func makeValidGroupAPIOutput() *gitlab.GroupVariable {
  171. return &gitlab.GroupVariable{
  172. Key: "groupKey",
  173. Value: "",
  174. EnvironmentScope: environment,
  175. }
  176. }
  177. func makeValidSecretManagerTestCaseCustom(tweaks ...func(smtc *secretManagerTestCase)) *secretManagerTestCase {
  178. smtc := makeValidSecretManagerTestCase()
  179. for _, fn := range tweaks {
  180. fn(smtc)
  181. }
  182. smtc.mockProjectsClient.WithValue(smtc.projectGroupsAPIOutput, smtc.projectGroupsAPIResponse, smtc.apiErr)
  183. smtc.mockProjectVarClient.WithValue(smtc.projectAPIOutput, smtc.projectAPIResponse, smtc.apiErr)
  184. smtc.mockGroupVarClient.WithValue(smtc.groupAPIOutput, smtc.groupAPIResponse, smtc.apiErr)
  185. return smtc
  186. }
  187. func makeValidSecretManagerGetAllTestCaseCustom(tweaks ...func(smtc *secretManagerTestCase)) *secretManagerTestCase {
  188. smtc := makeValidSecretManagerTestCase()
  189. smtc.ref = nil
  190. smtc.refFind.Name = makeFindName(".*")
  191. for _, fn := range tweaks {
  192. fn(smtc)
  193. }
  194. smtc.mockProjectVarClient.WithValue(smtc.projectAPIOutput, smtc.projectAPIResponse, smtc.apiErr)
  195. smtc.mockGroupVarClient.WithValue(smtc.groupAPIOutput, smtc.groupAPIResponse, smtc.apiErr)
  196. return smtc
  197. }
  198. // This case can be shared by both GetSecret and GetSecretMap tests.
  199. // bad case: set apiErr.
  200. var setAPIErr = func(smtc *secretManagerTestCase) {
  201. smtc.apiErr = fmt.Errorf("oh no")
  202. smtc.expectError = "oh no"
  203. smtc.projectAPIResponse.Response.StatusCode = http.StatusInternalServerError
  204. smtc.expectedValidationResult = esv1beta1.ValidationResultError
  205. }
  206. var setListAPIErr = func(smtc *secretManagerTestCase) {
  207. err := fmt.Errorf("oh no")
  208. smtc.apiErr = err
  209. smtc.expectError = fmt.Errorf(errList, err).Error()
  210. smtc.expectedValidationResult = esv1beta1.ValidationResultError
  211. }
  212. var setProjectListAPIRespNil = func(smtc *secretManagerTestCase) {
  213. smtc.projectAPIResponse = nil
  214. smtc.expectError = fmt.Errorf(errProjectAuth, smtc.projectID).Error()
  215. smtc.expectedValidationResult = esv1beta1.ValidationResultError
  216. }
  217. var setGroupListAPIRespNil = func(smtc *secretManagerTestCase) {
  218. smtc.groupIDs = []string{groupid}
  219. smtc.groupAPIResponse = nil
  220. smtc.expectError = fmt.Errorf(errGroupAuth, groupid).Error()
  221. smtc.expectedValidationResult = esv1beta1.ValidationResultError
  222. }
  223. var setProjectAndGroup = func(smtc *secretManagerTestCase) {
  224. smtc.groupIDs = []string{groupid}
  225. }
  226. var setProjectAndInheritFromGroups = func(smtc *secretManagerTestCase) {
  227. smtc.groupIDs = nil
  228. smtc.inheritFromGroups = true
  229. }
  230. var setProjectListAPIRespBadCode = func(smtc *secretManagerTestCase) {
  231. smtc.projectAPIResponse.StatusCode = http.StatusUnauthorized
  232. smtc.expectError = fmt.Errorf(errProjectAuth, smtc.projectID).Error()
  233. smtc.expectedValidationResult = esv1beta1.ValidationResultError
  234. }
  235. var setGroupListAPIRespBadCode = func(smtc *secretManagerTestCase) {
  236. smtc.groupIDs = []string{groupid}
  237. smtc.groupAPIResponse.StatusCode = http.StatusUnauthorized
  238. smtc.expectError = fmt.Errorf(errGroupAuth, groupid).Error()
  239. smtc.expectedValidationResult = esv1beta1.ValidationResultError
  240. }
  241. var setNilMockClient = func(smtc *secretManagerTestCase) {
  242. smtc.mockProjectVarClient = nil
  243. smtc.mockGroupVarClient = nil
  244. smtc.expectError = errUninitializedGitlabProvider
  245. }
  246. func TestNewClient(t *testing.T) {
  247. ctx := context.Background()
  248. const namespace = "namespace"
  249. store := &esv1beta1.SecretStore{
  250. ObjectMeta: metav1.ObjectMeta{
  251. Namespace: namespace,
  252. },
  253. Spec: esv1beta1.SecretStoreSpec{
  254. Provider: &esv1beta1.SecretStoreProvider{
  255. Gitlab: &esv1beta1.GitlabProvider{},
  256. },
  257. },
  258. }
  259. provider, err := esv1beta1.GetProvider(store)
  260. tassert.Nil(t, err)
  261. k8sClient := clientfake.NewClientBuilder().Build()
  262. secretClient, err := provider.NewClient(context.Background(), store, k8sClient, namespace)
  263. tassert.EqualError(t, err, errMissingCredentials)
  264. tassert.Nil(t, secretClient)
  265. store.Spec.Provider.Gitlab.Auth = esv1beta1.GitlabAuth{}
  266. secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
  267. tassert.EqualError(t, err, errMissingCredentials)
  268. tassert.Nil(t, secretClient)
  269. store.Spec.Provider.Gitlab.Auth.SecretRef = esv1beta1.GitlabSecretRef{}
  270. secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
  271. tassert.EqualError(t, err, errMissingCredentials)
  272. tassert.Nil(t, secretClient)
  273. store.Spec.Provider.Gitlab.Auth.SecretRef.AccessToken = esv1meta.SecretKeySelector{}
  274. secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
  275. tassert.EqualError(t, err, errMissingCredentials)
  276. tassert.Nil(t, secretClient)
  277. const authorizedKeySecretName = "authorizedKeySecretName"
  278. const authorizedKeySecretKey = "authorizedKeySecretKey"
  279. store.Spec.Provider.Gitlab.Auth.SecretRef.AccessToken.Name = authorizedKeySecretName
  280. store.Spec.Provider.Gitlab.Auth.SecretRef.AccessToken.Key = authorizedKeySecretKey
  281. secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
  282. tassert.EqualError(t, err, "couldn't find secret on cluster: secrets \"authorizedKeySecretName\" not found")
  283. tassert.Nil(t, secretClient)
  284. err = createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, newFakeAuthorizedKey()))
  285. tassert.Nil(t, err)
  286. secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
  287. tassert.Nil(t, err)
  288. tassert.NotNil(t, secretClient)
  289. }
  290. func toJSON(t *testing.T, v interface{}) []byte {
  291. jsonBytes, err := json.Marshal(v)
  292. tassert.Nil(t, err)
  293. return jsonBytes
  294. }
  295. func createK8sSecret(ctx context.Context, t *testing.T, k8sClient k8sclient.Client, namespace, secretName, secretKey string, secretValue []byte) error {
  296. err := k8sClient.Create(ctx, &corev1.Secret{
  297. ObjectMeta: metav1.ObjectMeta{
  298. Namespace: namespace,
  299. Name: secretName,
  300. },
  301. Data: map[string][]byte{secretKey: secretValue},
  302. })
  303. tassert.Nil(t, err)
  304. return nil
  305. }
  306. func newFakeAuthorizedKey() *iamkey.Key {
  307. uniqueLabel := uuid.NewString()
  308. return &iamkey.Key{
  309. Id: uniqueLabel,
  310. Subject: &iamkey.Key_ServiceAccountId{
  311. ServiceAccountId: uniqueLabel,
  312. },
  313. PrivateKey: uniqueLabel,
  314. }
  315. }
  316. // test the sm<->gcp interface
  317. // make sure correct values are passed and errors are handled accordingly.
  318. func TestGetSecret(t *testing.T) {
  319. // good case: default version is set
  320. // key is passed in, output is sent back
  321. onlyProjectSecret := func(smtc *secretManagerTestCase) {
  322. smtc.projectAPIOutput.Value = projectvalue
  323. smtc.groupAPIResponse = nil
  324. smtc.groupAPIOutput = nil
  325. smtc.expectedSecret = smtc.projectAPIOutput.Value
  326. }
  327. onlyWildcardSecret := func(smtc *secretManagerTestCase) {
  328. smtc.projectAPIOutput.Value = ""
  329. smtc.projectAPIResponse.Response.StatusCode = 404
  330. smtc.groupAPIResponse = nil
  331. smtc.groupAPIOutput = nil
  332. smtc.expectedSecret = smtc.projectAPIOutput.Value
  333. }
  334. groupSecretProjectOverride := func(smtc *secretManagerTestCase) {
  335. smtc.projectAPIOutput.Value = projectvalue
  336. smtc.groupAPIOutput.Key = "testkey"
  337. smtc.groupAPIOutput.Value = groupvalue
  338. smtc.expectedSecret = smtc.projectAPIOutput.Value
  339. }
  340. groupWithoutProjectOverride := func(smtc *secretManagerTestCase) {
  341. smtc.groupIDs = []string{groupid}
  342. smtc.projectAPIResponse.Response.StatusCode = 404
  343. smtc.groupAPIOutput.Key = "testkey"
  344. smtc.groupAPIOutput.Value = groupvalue
  345. smtc.expectedSecret = smtc.groupAPIOutput.Value
  346. }
  347. successCases := []*secretManagerTestCase{
  348. makeValidSecretManagerTestCaseCustom(onlyProjectSecret),
  349. makeValidSecretManagerTestCaseCustom(onlyWildcardSecret),
  350. makeValidSecretManagerTestCaseCustom(groupSecretProjectOverride),
  351. makeValidSecretManagerTestCaseCustom(groupWithoutProjectOverride),
  352. makeValidSecretManagerTestCaseCustom(setAPIErr),
  353. makeValidSecretManagerTestCaseCustom(setNilMockClient),
  354. }
  355. sm := Gitlab{}
  356. for k, v := range successCases {
  357. sm.projectVariablesClient = v.mockProjectVarClient
  358. sm.groupVariablesClient = v.mockGroupVarClient
  359. sm.projectID = v.projectID
  360. sm.groupIDs = v.groupIDs
  361. sm.environment = v.apiInputEnv
  362. out, err := sm.GetSecret(context.Background(), *v.ref)
  363. if !ErrorContains(err, v.expectError) {
  364. t.Errorf(defaultErrorMessage, k, err.Error(), v.expectError)
  365. }
  366. if string(out) != v.expectedSecret {
  367. t.Errorf("[%d] unexpected secret: [%s], expected [%s]", k, string(out), v.expectedSecret)
  368. }
  369. }
  370. }
  371. func TestResolveGroupIds(t *testing.T) {
  372. v := makeValidSecretManagerTestCaseCustom()
  373. sm := Gitlab{}
  374. sm.projectsClient = v.mockProjectsClient
  375. sm.projectID = v.projectID
  376. sm.inheritFromGroups = true
  377. err := sm.ResolveGroupIds()
  378. if err != nil {
  379. t.Errorf(defaultErrorMessage, 0, err.Error(), "")
  380. }
  381. if !reflect.DeepEqual(sm.groupIDs, []string{"1", "10", "100"}) {
  382. t.Errorf("unexpected groupIds: %s, expected %s", sm.groupIDs, []string{"1", "10", "100"})
  383. }
  384. }
  385. func TestGetAllSecrets(t *testing.T) {
  386. // good case: default version is set
  387. // key is passed in, output is sent back
  388. setMissingFindRegex := func(smtc *secretManagerTestCase) {
  389. smtc.refFind.Name = nil
  390. smtc.expectError = "'find.name' is mandatory"
  391. }
  392. setUnsupportedFindPath := func(smtc *secretManagerTestCase) {
  393. path := "path"
  394. smtc.refFind.Path = &path
  395. smtc.expectError = "'find.path' is not implemented in the Gitlab provider"
  396. }
  397. setUnsupportedFindTag := func(smtc *secretManagerTestCase) {
  398. smtc.expectError = "'find.tags' only supports 'environment_scope"
  399. smtc.refFind.Tags = map[string]string{"foo": ""}
  400. }
  401. setMatchingSecretFindString := func(smtc *secretManagerTestCase) {
  402. smtc.projectAPIOutput = &gitlab.ProjectVariable{
  403. Key: "testkey",
  404. Value: projectvalue,
  405. EnvironmentScope: environment,
  406. }
  407. smtc.expectedSecret = projectvalue
  408. smtc.refFind.Name = makeFindName(findTestPrefix)
  409. }
  410. setNoMatchingRegexpFindString := func(smtc *secretManagerTestCase) {
  411. smtc.projectAPIOutput = &gitlab.ProjectVariable{
  412. Key: "testkey",
  413. Value: projectvalue,
  414. EnvironmentScope: "test",
  415. }
  416. smtc.expectedSecret = ""
  417. smtc.refFind.Name = makeFindName("foo.*")
  418. }
  419. setUnmatchedEnvironmentFindString := func(smtc *secretManagerTestCase) {
  420. smtc.projectAPIOutput = &gitlab.ProjectVariable{
  421. Key: "testkey",
  422. Value: projectvalue,
  423. EnvironmentScope: "test",
  424. }
  425. smtc.expectedSecret = ""
  426. smtc.refFind.Name = makeFindName(findTestPrefix)
  427. }
  428. setMatchingSecretFindTags := func(smtc *secretManagerTestCase) {
  429. smtc.projectAPIOutput = &gitlab.ProjectVariable{
  430. Key: "testkey",
  431. Value: projectvalue,
  432. EnvironmentScope: environment,
  433. }
  434. smtc.apiInputEnv = "*"
  435. smtc.expectedSecret = projectvalue
  436. smtc.refFind.Tags = map[string]string{"environment_scope": environment}
  437. }
  438. setEnvironmentConstrainedByStore := func(smtc *secretManagerTestCase) {
  439. smtc.expectedSecret = projectvalue
  440. smtc.expectError = "'find.tags' is constrained by 'environment_scope' of the store"
  441. smtc.refFind.Tags = map[string]string{"environment_scope": environment}
  442. }
  443. cases := []*secretManagerTestCase{
  444. makeValidSecretManagerGetAllTestCaseCustom(setMissingFindRegex),
  445. makeValidSecretManagerGetAllTestCaseCustom(setUnsupportedFindPath),
  446. makeValidSecretManagerGetAllTestCaseCustom(setUnsupportedFindTag),
  447. makeValidSecretManagerGetAllTestCaseCustom(setMatchingSecretFindString),
  448. makeValidSecretManagerGetAllTestCaseCustom(setNoMatchingRegexpFindString),
  449. makeValidSecretManagerGetAllTestCaseCustom(setUnmatchedEnvironmentFindString),
  450. makeValidSecretManagerGetAllTestCaseCustom(setMatchingSecretFindTags),
  451. makeValidSecretManagerGetAllTestCaseCustom(setEnvironmentConstrainedByStore),
  452. makeValidSecretManagerGetAllTestCaseCustom(setAPIErr),
  453. makeValidSecretManagerGetAllTestCaseCustom(setNilMockClient),
  454. }
  455. sm := Gitlab{}
  456. for k, v := range cases {
  457. sm.environment = v.apiInputEnv
  458. sm.projectVariablesClient = v.mockProjectVarClient
  459. sm.groupVariablesClient = v.mockGroupVarClient
  460. out, err := sm.GetAllSecrets(context.Background(), *v.refFind)
  461. if !ErrorContains(err, v.expectError) {
  462. t.Errorf(defaultErrorMessage, k, err.Error(), v.expectError)
  463. }
  464. if v.expectError == "" && string(out[v.projectAPIOutput.Key]) != v.expectedSecret {
  465. t.Errorf("[%d] unexpected secret: [%s], expected [%s]", k, string(out[v.projectAPIOutput.Key]), v.expectedSecret)
  466. }
  467. }
  468. }
  469. func TestGetAllSecretsWithGroups(t *testing.T) {
  470. onlyProjectSecret := func(smtc *secretManagerTestCase) {
  471. smtc.projectAPIOutput.Value = projectvalue
  472. smtc.refFind.Name = makeFindName(findTestPrefix)
  473. smtc.groupAPIResponse = nil
  474. smtc.groupAPIOutput = nil
  475. smtc.expectedSecret = smtc.projectAPIOutput.Value
  476. }
  477. groupAndProjectSecrets := func(smtc *secretManagerTestCase) {
  478. smtc.groupIDs = []string{groupid}
  479. smtc.projectAPIOutput.Value = projectvalue
  480. smtc.groupAPIOutput.Value = groupvalue
  481. smtc.expectedData = map[string][]byte{"testKey": []byte(projectvalue), "groupKey": []byte(groupvalue)}
  482. smtc.refFind.Name = makeFindName(".*Key")
  483. }
  484. groupAndOverrideProjectSecrets := func(smtc *secretManagerTestCase) {
  485. smtc.groupIDs = []string{groupid}
  486. smtc.projectAPIOutput.Value = projectvalue
  487. smtc.groupAPIOutput.Key = smtc.projectAPIOutput.Key
  488. smtc.groupAPIOutput.Value = groupvalue
  489. smtc.expectedData = map[string][]byte{"testKey": []byte(projectvalue)}
  490. smtc.refFind.Name = makeFindName(".*Key")
  491. }
  492. groupAndProjectWithDifferentEnvSecrets := func(smtc *secretManagerTestCase) {
  493. smtc.groupIDs = []string{groupid}
  494. smtc.projectAPIOutput.Value = projectvalue
  495. smtc.projectAPIOutput.EnvironmentScope = "test"
  496. smtc.groupAPIOutput.Key = smtc.projectAPIOutput.Key
  497. smtc.groupAPIOutput.Value = groupvalue
  498. smtc.expectedData = map[string][]byte{"testKey": []byte(groupvalue)}
  499. smtc.refFind.Name = makeFindName(".*Key")
  500. }
  501. cases := []*secretManagerTestCase{
  502. makeValidSecretManagerGetAllTestCaseCustom(onlyProjectSecret),
  503. makeValidSecretManagerGetAllTestCaseCustom(groupAndProjectSecrets),
  504. makeValidSecretManagerGetAllTestCaseCustom(groupAndOverrideProjectSecrets),
  505. makeValidSecretManagerGetAllTestCaseCustom(groupAndProjectWithDifferentEnvSecrets),
  506. }
  507. sm := Gitlab{}
  508. sm.environment = "prod"
  509. for k, v := range cases {
  510. sm.projectVariablesClient = v.mockProjectVarClient
  511. sm.groupVariablesClient = v.mockGroupVarClient
  512. sm.projectID = v.projectID
  513. sm.groupIDs = v.groupIDs
  514. out, err := sm.GetAllSecrets(context.Background(), *v.refFind)
  515. if !ErrorContains(err, v.expectError) {
  516. t.Errorf(defaultErrorMessage, k, err.Error(), v.expectError)
  517. }
  518. if v.expectError == "" {
  519. if len(v.expectedData) > 0 {
  520. if !reflect.DeepEqual(v.expectedData, out) {
  521. t.Errorf("[%d] unexpected secrets: [%s], expected [%s]", k, out, v.expectedData)
  522. }
  523. } else if string(out[v.projectAPIOutput.Key]) != v.expectedSecret {
  524. t.Errorf("[%d] unexpected secret: [%s], expected [%s]", k, string(out[v.projectAPIOutput.Key]), v.expectedSecret)
  525. }
  526. }
  527. }
  528. }
  529. func TestValidate(t *testing.T) {
  530. successCases := []*secretManagerTestCase{
  531. makeValidSecretManagerTestCaseCustom(),
  532. makeValidSecretManagerTestCaseCustom(setProjectAndInheritFromGroups),
  533. makeValidSecretManagerTestCaseCustom(setProjectAndGroup),
  534. makeValidSecretManagerTestCaseCustom(setListAPIErr),
  535. makeValidSecretManagerTestCaseCustom(setProjectListAPIRespNil),
  536. makeValidSecretManagerTestCaseCustom(setProjectListAPIRespBadCode),
  537. makeValidSecretManagerTestCaseCustom(setGroupListAPIRespNil),
  538. makeValidSecretManagerTestCaseCustom(setGroupListAPIRespBadCode),
  539. }
  540. sm := Gitlab{}
  541. for k, v := range successCases {
  542. sm.projectsClient = v.mockProjectsClient
  543. sm.projectVariablesClient = v.mockProjectVarClient
  544. sm.groupVariablesClient = v.mockGroupVarClient
  545. sm.projectID = v.projectID
  546. sm.groupIDs = v.groupIDs
  547. sm.inheritFromGroups = v.inheritFromGroups
  548. t.Logf("%+v", v)
  549. validationResult, err := sm.Validate()
  550. if !ErrorContains(err, v.expectError) {
  551. t.Errorf(defaultErrorMessage, k, err.Error(), v.expectError)
  552. }
  553. if validationResult != v.expectedValidationResult {
  554. t.Errorf("[%d], unexpected validationResult: [%s], expected: [%s]", k, validationResult, v.expectedValidationResult)
  555. }
  556. if sm.inheritFromGroups && sm.groupIDs[0] != "1" {
  557. t.Errorf("[%d], unexpected groupID: [%s], expected [1]", k, sm.groupIDs[0])
  558. }
  559. }
  560. }
  561. func TestGetSecretMap(t *testing.T) {
  562. // good case: default version & deserialization
  563. setDeserialization := func(smtc *secretManagerTestCase) {
  564. smtc.projectAPIOutput.Value = `{"foo":"bar"}`
  565. smtc.expectedData["foo"] = []byte("bar")
  566. }
  567. // bad case: invalid json
  568. setInvalidJSON := func(smtc *secretManagerTestCase) {
  569. smtc.projectAPIOutput.Value = `-----------------`
  570. smtc.expectError = "unable to unmarshal secret"
  571. }
  572. successCases := []*secretManagerTestCase{
  573. makeValidSecretManagerTestCaseCustom(setDeserialization),
  574. makeValidSecretManagerTestCaseCustom(setInvalidJSON),
  575. makeValidSecretManagerTestCaseCustom(setNilMockClient),
  576. makeValidSecretManagerTestCaseCustom(setAPIErr),
  577. }
  578. sm := Gitlab{}
  579. for k, v := range successCases {
  580. sm.projectVariablesClient = v.mockProjectVarClient
  581. sm.groupVariablesClient = v.mockGroupVarClient
  582. out, err := sm.GetSecretMap(context.Background(), *v.ref)
  583. if !ErrorContains(err, v.expectError) {
  584. t.Errorf(defaultErrorMessage, k, err.Error(), v.expectError)
  585. }
  586. if err == nil && !reflect.DeepEqual(out, v.expectedData) {
  587. t.Errorf("[%d] unexpected secret data: [%#v], expected [%#v]", k, out, v.expectedData)
  588. }
  589. }
  590. }
  591. func ErrorContains(out error, want string) bool {
  592. if out == nil {
  593. return want == ""
  594. }
  595. if want == "" {
  596. return false
  597. }
  598. return strings.Contains(out.Error(), want)
  599. }
  600. type storeModifier func(*esv1beta1.SecretStore) *esv1beta1.SecretStore
  601. func makeSecretStore(projectID, environment string, fn ...storeModifier) *esv1beta1.SecretStore {
  602. store := &esv1beta1.SecretStore{
  603. Spec: esv1beta1.SecretStoreSpec{
  604. Provider: &esv1beta1.SecretStoreProvider{
  605. Gitlab: &esv1beta1.GitlabProvider{
  606. Auth: esv1beta1.GitlabAuth{},
  607. ProjectID: projectID,
  608. Environment: environment,
  609. },
  610. },
  611. },
  612. }
  613. for _, f := range fn {
  614. store = f(store)
  615. }
  616. return store
  617. }
  618. func withAccessToken(name, key string, namespace *string) storeModifier {
  619. return func(store *esv1beta1.SecretStore) *esv1beta1.SecretStore {
  620. store.Spec.Provider.Gitlab.Auth.SecretRef.AccessToken = esv1meta.SecretKeySelector{
  621. Name: name,
  622. Key: key,
  623. Namespace: namespace,
  624. }
  625. return store
  626. }
  627. }
  628. func withGroups(ids []string, inherit bool) storeModifier {
  629. return func(store *esv1beta1.SecretStore) *esv1beta1.SecretStore {
  630. store.Spec.Provider.Gitlab.GroupIDs = ids
  631. store.Spec.Provider.Gitlab.InheritFromGroups = inherit
  632. return store
  633. }
  634. }
  635. type ValidateStoreTestCase struct {
  636. store *esv1beta1.SecretStore
  637. err error
  638. }
  639. func TestValidateStore(t *testing.T) {
  640. namespace := "my-namespace"
  641. testCases := []ValidateStoreTestCase{
  642. {
  643. store: makeSecretStore("", environment),
  644. err: fmt.Errorf("projectID and groupIDs must not both be empty"),
  645. },
  646. {
  647. store: makeSecretStore(project, environment, withGroups([]string{"group1"}, true)),
  648. err: fmt.Errorf("defining groupIDs and inheritFromGroups = true is not allowed"),
  649. },
  650. {
  651. store: makeSecretStore(project, environment, withAccessToken("", userkey, nil)),
  652. err: fmt.Errorf("accessToken.name cannot be empty"),
  653. },
  654. {
  655. store: makeSecretStore(project, environment, withAccessToken(username, "", nil)),
  656. err: fmt.Errorf("accessToken.key cannot be empty"),
  657. },
  658. {
  659. store: makeSecretStore(project, environment, withAccessToken("userName", "userKey", &namespace)),
  660. err: fmt.Errorf("namespace not allowed with namespaced SecretStore"),
  661. },
  662. {
  663. store: makeSecretStore(project, environment, withAccessToken("userName", "userKey", nil)),
  664. err: nil,
  665. },
  666. {
  667. store: makeSecretStore("", environment, withGroups([]string{"group1"}, false), withAccessToken("userName", "userKey", nil)),
  668. err: nil,
  669. },
  670. }
  671. p := Gitlab{}
  672. for _, tc := range testCases {
  673. err := p.ValidateStore(tc.store)
  674. if tc.err != nil && err != nil && err.Error() != tc.err.Error() {
  675. t.Errorf("test failed! want %v, got %v", tc.err, err)
  676. } else if tc.err == nil && err != nil {
  677. t.Errorf("want nil got err %v", err)
  678. } else if tc.err != nil && err == nil {
  679. t.Errorf("want err %v got nil", tc.err)
  680. }
  681. }
  682. }