client.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  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 doppler
  14. import (
  15. "context"
  16. "encoding/json"
  17. "errors"
  18. "fmt"
  19. "net/url"
  20. "strings"
  21. "time"
  22. corev1 "k8s.io/api/core/v1"
  23. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  24. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  25. "github.com/external-secrets/external-secrets/pkg/find"
  26. dClient "github.com/external-secrets/external-secrets/pkg/provider/doppler/client"
  27. "github.com/external-secrets/external-secrets/pkg/utils"
  28. "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
  29. )
  30. const (
  31. customBaseURLEnvVar = "DOPPLER_BASE_URL"
  32. verifyTLSOverrideEnvVar = "DOPPLER_VERIFY_TLS"
  33. errGetSecret = "could not get secret %s: %w"
  34. errGetSecrets = "could not get secrets %w"
  35. errDeleteSecrets = "could not delete secrets %s: %w"
  36. errPushSecrets = "could not push secrets %s: %w"
  37. errUnmarshalSecretMap = "unable to unmarshal secret %s: %w"
  38. secretsDownloadFileKey = "DOPPLER_SECRETS_FILE"
  39. errDopplerTokenSecretName = "missing auth.secretRef.dopplerToken.name"
  40. errInvalidClusterStoreMissingDopplerTokenNamespace = "missing auth.secretRef.dopplerToken.namespace"
  41. )
  42. type Client struct {
  43. doppler SecretsClientInterface
  44. dopplerToken string
  45. project string
  46. config string
  47. nameTransformer string
  48. format string
  49. kube kclient.Client
  50. store *esv1.DopplerProvider
  51. namespace string
  52. storeKind string
  53. }
  54. // SecretsClientInterface defines the required Doppler Client methods.
  55. type SecretsClientInterface interface {
  56. BaseURL() *url.URL
  57. Authenticate() error
  58. GetSecret(request dClient.SecretRequest) (*dClient.SecretResponse, error)
  59. GetSecrets(request dClient.SecretsRequest) (*dClient.SecretsResponse, error)
  60. UpdateSecrets(request dClient.UpdateSecretsRequest) error
  61. }
  62. func (c *Client) setAuth(ctx context.Context) error {
  63. token, err := resolvers.SecretKeyRef(
  64. ctx,
  65. c.kube,
  66. c.storeKind,
  67. c.namespace,
  68. &c.store.Auth.SecretRef.DopplerToken)
  69. if err != nil {
  70. return err
  71. }
  72. c.dopplerToken = token
  73. return nil
  74. }
  75. func (c *Client) Validate() (esv1.ValidationResult, error) {
  76. timeout := 15 * time.Second
  77. clientURL := c.doppler.BaseURL().String()
  78. if err := utils.NetworkValidate(clientURL, timeout); err != nil {
  79. return esv1.ValidationResultError, err
  80. }
  81. if err := c.doppler.Authenticate(); err != nil {
  82. return esv1.ValidationResultError, err
  83. }
  84. return esv1.ValidationResultReady, nil
  85. }
  86. func (c *Client) DeleteSecret(_ context.Context, ref esv1.PushSecretRemoteRef) error {
  87. request := dClient.UpdateSecretsRequest{
  88. ChangeRequests: []dClient.Change{
  89. {
  90. Name: ref.GetRemoteKey(),
  91. OriginalName: ref.GetRemoteKey(),
  92. ShouldDelete: true,
  93. },
  94. },
  95. Project: c.project,
  96. Config: c.config,
  97. }
  98. err := c.doppler.UpdateSecrets(request)
  99. if err != nil {
  100. return fmt.Errorf(errDeleteSecrets, ref.GetRemoteKey(), err)
  101. }
  102. return nil
  103. }
  104. func (c *Client) SecretExists(_ context.Context, _ esv1.PushSecretRemoteRef) (bool, error) {
  105. return false, errors.New("not implemented")
  106. }
  107. func (c *Client) PushSecret(_ context.Context, secret *corev1.Secret, data esv1.PushSecretData) error {
  108. value := secret.Data[data.GetSecretKey()]
  109. request := dClient.UpdateSecretsRequest{
  110. Secrets: dClient.Secrets{
  111. data.GetRemoteKey(): string(value),
  112. },
  113. Project: c.project,
  114. Config: c.config,
  115. }
  116. err := c.doppler.UpdateSecrets(request)
  117. if err != nil {
  118. return fmt.Errorf(errPushSecrets, data.GetRemoteKey(), err)
  119. }
  120. return nil
  121. }
  122. func (c *Client) GetSecret(_ context.Context, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
  123. request := dClient.SecretRequest{
  124. Name: ref.Key,
  125. Project: c.project,
  126. Config: c.config,
  127. }
  128. secret, err := c.doppler.GetSecret(request)
  129. if err != nil {
  130. return nil, fmt.Errorf(errGetSecret, ref.Key, err)
  131. }
  132. return []byte(secret.Value), nil
  133. }
  134. func (c *Client) GetSecretMap(ctx context.Context, ref esv1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  135. data, err := c.GetSecret(ctx, ref)
  136. if err != nil {
  137. return nil, err
  138. }
  139. kv := make(map[string]json.RawMessage)
  140. err = json.Unmarshal(data, &kv)
  141. if err != nil {
  142. return nil, fmt.Errorf(errUnmarshalSecretMap, ref.Key, err)
  143. }
  144. secretData := make(map[string][]byte)
  145. for k, v := range kv {
  146. var strVal string
  147. err = json.Unmarshal(v, &strVal)
  148. if err == nil {
  149. secretData[k] = []byte(strVal)
  150. } else {
  151. secretData[k] = v
  152. }
  153. }
  154. return secretData, nil
  155. }
  156. func (c *Client) GetAllSecrets(ctx context.Context, ref esv1.ExternalSecretFind) (map[string][]byte, error) {
  157. secrets, err := c.getSecrets(ctx)
  158. selected := map[string][]byte{}
  159. if err != nil {
  160. return nil, err
  161. }
  162. if ref.Name == nil && ref.Path == nil {
  163. return secrets, nil
  164. }
  165. var matcher *find.Matcher
  166. if ref.Name != nil {
  167. m, err := find.New(*ref.Name)
  168. if err != nil {
  169. return nil, err
  170. }
  171. matcher = m
  172. }
  173. for key, value := range secrets {
  174. if (matcher != nil && !matcher.MatchName(key)) || (ref.Path != nil && !strings.HasPrefix(key, *ref.Path)) {
  175. continue
  176. }
  177. selected[key] = value
  178. }
  179. return selected, nil
  180. }
  181. func (c *Client) Close(_ context.Context) error {
  182. return nil
  183. }
  184. func (c *Client) getSecrets(_ context.Context) (map[string][]byte, error) {
  185. request := dClient.SecretsRequest{
  186. Project: c.project,
  187. Config: c.config,
  188. NameTransformer: c.nameTransformer,
  189. Format: c.format,
  190. }
  191. response, err := c.doppler.GetSecrets(request)
  192. if err != nil {
  193. return nil, fmt.Errorf(errGetSecrets, err)
  194. }
  195. if c.format != "" {
  196. return map[string][]byte{
  197. secretsDownloadFileKey: response.Body,
  198. }, nil
  199. }
  200. return externalSecretsFormat(response.Secrets), nil
  201. }
  202. func externalSecretsFormat(secrets dClient.Secrets) map[string][]byte {
  203. converted := make(map[string][]byte, len(secrets))
  204. for key, value := range secrets {
  205. converted[key] = []byte(value)
  206. }
  207. return converted
  208. }