passbolt_test.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /*
  2. Copyright © 2025 ESO Maintainer Team
  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 passbolt
  14. import (
  15. "context"
  16. "errors"
  17. "strings"
  18. "testing"
  19. "github.com/google/go-cmp/cmp"
  20. g "github.com/onsi/gomega"
  21. "github.com/passbolt/go-passbolt/api"
  22. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  23. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  24. )
  25. const (
  26. someKey1 = "some-key1"
  27. someKey2 = "some-key2"
  28. someURI1 = "some-uri1"
  29. someURI2 = "some-uri2"
  30. )
  31. type PassboltClientMock struct {
  32. }
  33. func (p *PassboltClientMock) CheckSession(_ context.Context) bool {
  34. return true
  35. }
  36. func (p *PassboltClientMock) Login(_ context.Context) error {
  37. return nil
  38. }
  39. func (p *PassboltClientMock) Logout(_ context.Context) error {
  40. return nil
  41. }
  42. func (p *PassboltClientMock) GetResource(_ context.Context, resourceID string) (*api.Resource, error) {
  43. resmap := map[string]api.Resource{
  44. someKey1: {ID: someKey1, Name: "some-name1", URI: someURI1},
  45. someKey2: {ID: someKey2, Name: "some-name2", URI: someURI2},
  46. }
  47. if res, ok := resmap[resourceID]; ok {
  48. return &res, nil
  49. }
  50. return nil, errors.New("ID not found")
  51. }
  52. func (p *PassboltClientMock) GetResources(_ context.Context, _ *api.GetResourcesOptions) ([]api.Resource, error) {
  53. res := []api.Resource{
  54. {ID: someKey1, Name: "some-name1", URI: someURI1},
  55. {ID: someKey2, Name: "some-name2", URI: someURI2},
  56. }
  57. return res, nil
  58. }
  59. func (p *PassboltClientMock) GetResourceType(_ context.Context, _ string) (*api.ResourceType, error) {
  60. res := &api.ResourceType{Slug: "password-and-description"}
  61. return res, nil
  62. }
  63. func (p *PassboltClientMock) DecryptMessage(message string) (string, error) {
  64. return message, nil
  65. }
  66. func (p *PassboltClientMock) GetSecret(_ context.Context, resourceID string) (*api.Secret, error) {
  67. resmap := map[string]api.Secret{
  68. someKey1: {Data: `{"password": "some-password1", "description": "some-description1"}`},
  69. someKey2: {Data: `{"password": "some-password2", "description": "some-description2"}`},
  70. }
  71. if res, ok := resmap[resourceID]; ok {
  72. return &res, nil
  73. }
  74. return nil, errors.New("ID not found")
  75. }
  76. var clientMock = &PassboltClientMock{}
  77. func TestValidateStore(t *testing.T) {
  78. p := &ProviderPassbolt{client: clientMock}
  79. g.RegisterTestingT(t)
  80. store := &esv1.SecretStore{
  81. Spec: esv1.SecretStoreSpec{
  82. Provider: &esv1.SecretStoreProvider{
  83. Passbolt: &esv1.PassboltProvider{},
  84. },
  85. },
  86. }
  87. // missing auth
  88. _, err := p.ValidateStore(store)
  89. g.Expect(err).To(g.BeEquivalentTo(errors.New(errPassboltStoreMissingAuth)))
  90. // missing password
  91. store.Spec.Provider.Passbolt.Auth = &esv1.PassboltAuth{
  92. PrivateKeySecretRef: &esmeta.SecretKeySelector{Key: "some-secret", Name: "privatekey"},
  93. }
  94. _, err = p.ValidateStore(store)
  95. g.Expect(err).To(g.BeEquivalentTo(errors.New(errPassboltStoreMissingAuthPassword)))
  96. // missing privateKey
  97. store.Spec.Provider.Passbolt.Auth = &esv1.PassboltAuth{
  98. PasswordSecretRef: &esmeta.SecretKeySelector{Key: "some-secret", Name: "password"},
  99. }
  100. _, err = p.ValidateStore(store)
  101. g.Expect(err).To(g.BeEquivalentTo(errors.New(errPassboltStoreMissingAuthPrivateKey)))
  102. store.Spec.Provider.Passbolt.Auth = &esv1.PassboltAuth{
  103. PasswordSecretRef: &esmeta.SecretKeySelector{Key: "some-secret", Name: "password"},
  104. PrivateKeySecretRef: &esmeta.SecretKeySelector{Key: "some-secret", Name: "privatekey"},
  105. }
  106. // missing host
  107. _, err = p.ValidateStore(store)
  108. g.Expect(err).To(g.BeEquivalentTo(errors.New(errPassboltStoreMissingHost)))
  109. // not https
  110. store.Spec.Provider.Passbolt.Host = "http://passbolt.test"
  111. _, err = p.ValidateStore(store)
  112. g.Expect(err).To(g.BeEquivalentTo(errors.New(errPassboltStoreHostSchemeNotHTTPS)))
  113. // spec ok
  114. store.Spec.Provider.Passbolt.Host = "https://passbolt.test"
  115. _, err = p.ValidateStore(store)
  116. g.Expect(err).To(g.BeNil())
  117. }
  118. func TestClose(t *testing.T) {
  119. p := &ProviderPassbolt{client: clientMock}
  120. g.RegisterTestingT(t)
  121. err := p.Close(context.TODO())
  122. g.Expect(err).To(g.BeNil())
  123. }
  124. func TestGetAllSecrets(t *testing.T) {
  125. cases := []struct {
  126. desc string
  127. ref esv1.ExternalSecretFind
  128. expected map[string][]byte
  129. expectedErr string
  130. }{
  131. {
  132. desc: "no matches",
  133. ref: esv1.ExternalSecretFind{
  134. Name: &esv1.FindName{
  135. RegExp: "nonexistant",
  136. },
  137. },
  138. expected: map[string][]byte{},
  139. },
  140. {
  141. desc: "matches",
  142. ref: esv1.ExternalSecretFind{
  143. Name: &esv1.FindName{
  144. RegExp: "some-name.*",
  145. },
  146. },
  147. expected: map[string][]byte{
  148. someKey1: []byte(`{"name":"some-name1","username":"","password":"some-password1","uri":"some-uri1","description":"some-description1"}`),
  149. someKey2: []byte(`{"name":"some-name2","username":"","password":"some-password2","uri":"some-uri2","description":"some-description2"}`),
  150. },
  151. },
  152. {
  153. desc: "missing find.name",
  154. ref: esv1.ExternalSecretFind{},
  155. expectedErr: errPassboltExternalSecretMissingFindNameRegExp,
  156. },
  157. {
  158. desc: "empty find.name.regexp",
  159. ref: esv1.ExternalSecretFind{
  160. Name: &esv1.FindName{
  161. RegExp: "",
  162. },
  163. },
  164. expectedErr: errPassboltExternalSecretMissingFindNameRegExp,
  165. },
  166. }
  167. for _, tc := range cases {
  168. t.Run(tc.desc, func(t *testing.T) {
  169. ctx := context.Background()
  170. p := ProviderPassbolt{client: clientMock}
  171. got, err := p.GetAllSecrets(ctx, tc.ref)
  172. if err != nil {
  173. if tc.expectedErr == "" {
  174. t.Fatalf("failed to call GetAllSecrets: %v", err)
  175. }
  176. if !strings.Contains(err.Error(), tc.expectedErr) {
  177. t.Fatalf("%q expected to contain substring %q", err.Error(), tc.expectedErr)
  178. }
  179. return
  180. }
  181. if tc.expectedErr != "" {
  182. t.Fatal("expected to receive an error but got nil")
  183. }
  184. if diff := cmp.Diff(tc.expected, got); diff != "" {
  185. t.Fatalf("(-got, +want)\n%s", diff)
  186. }
  187. })
  188. }
  189. }
  190. func TestGetSecret(t *testing.T) {
  191. g.RegisterTestingT(t)
  192. tbl := []struct {
  193. name string
  194. request esv1.ExternalSecretDataRemoteRef
  195. expValue string
  196. expErr string
  197. }{
  198. {
  199. name: "return err when not found",
  200. request: esv1.ExternalSecretDataRemoteRef{
  201. Key: "nonexistent",
  202. },
  203. expErr: "ID not found",
  204. },
  205. {
  206. name: "get property from secret",
  207. request: esv1.ExternalSecretDataRemoteRef{
  208. Key: someKey1,
  209. Property: "password",
  210. },
  211. expValue: "some-password1",
  212. },
  213. {
  214. name: "get full secret",
  215. request: esv1.ExternalSecretDataRemoteRef{
  216. Key: someKey1,
  217. },
  218. expValue: `{"name":"some-name1","username":"","password":"some-password1","uri":"some-uri1","description":"some-description1"}`,
  219. },
  220. {
  221. name: "return err when using invalid property",
  222. request: esv1.ExternalSecretDataRemoteRef{
  223. Key: someKey1,
  224. Property: "invalid",
  225. },
  226. expErr: errPassboltSecretPropertyInvalid,
  227. },
  228. }
  229. for _, row := range tbl {
  230. t.Run(row.name, func(_ *testing.T) {
  231. p := &ProviderPassbolt{client: clientMock}
  232. out, err := p.GetSecret(context.Background(), row.request)
  233. if row.expErr != "" {
  234. g.Expect(err).To(g.MatchError(row.expErr))
  235. } else {
  236. g.Expect(err).ToNot(g.HaveOccurred())
  237. }
  238. g.Expect(string(out)).To(g.Equal(row.expValue))
  239. })
  240. }
  241. }
  242. func TestSecretExists(t *testing.T) {
  243. p := &ProviderPassbolt{client: clientMock}
  244. g.RegisterTestingT(t)
  245. _, err := p.SecretExists(context.TODO(), nil)
  246. g.Expect(err).To(g.BeEquivalentTo(errors.New(errNotImplemented)))
  247. }
  248. func TestPushSecret(t *testing.T) {
  249. p := &ProviderPassbolt{client: clientMock}
  250. g.RegisterTestingT(t)
  251. err := p.PushSecret(context.TODO(), nil, nil)
  252. g.Expect(err).To(g.BeEquivalentTo(errors.New(errNotImplemented)))
  253. }
  254. func TestDeleteSecret(t *testing.T) {
  255. p := &ProviderPassbolt{client: clientMock}
  256. g.RegisterTestingT(t)
  257. err := p.DeleteSecret(context.TODO(), nil)
  258. g.Expect(err).To(g.BeEquivalentTo(errors.New(errNotImplemented)))
  259. }
  260. func TestGetSecretMap(t *testing.T) {
  261. p := &ProviderPassbolt{client: clientMock}
  262. g.RegisterTestingT(t)
  263. _, err := p.GetSecretMap(context.TODO(), esv1.ExternalSecretDataRemoteRef{})
  264. g.Expect(err).To(g.BeEquivalentTo(errors.New(errNotImplemented)))
  265. }