passbolt_test.go 8.3 KB

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