passbolt_test.go 8.4 KB

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