client.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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 impliec.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package onboardbase
  13. import (
  14. "context"
  15. "encoding/json"
  16. "fmt"
  17. "net/url"
  18. "strings"
  19. "time"
  20. "github.com/tidwall/gjson"
  21. corev1 "k8s.io/api/core/v1"
  22. "k8s.io/apimachinery/pkg/types"
  23. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  24. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  25. "github.com/external-secrets/external-secrets/pkg/find"
  26. onboardbaseClient "github.com/external-secrets/external-secrets/pkg/provider/onboardbase/client"
  27. "github.com/external-secrets/external-secrets/pkg/utils"
  28. )
  29. const (
  30. errGetSecret = "could not get secret %s: %s"
  31. errGetSecrets = "could not get secrets %s"
  32. errUnmarshalSecretMap = "unable to unmarshal secret %s: %w"
  33. errOnboardbaseAPIKeySecretName = "missing auth.secretRef.onboardbaseAPIKey.name"
  34. errInvalidClusterStoreMissingOnboardbaseAPIKeyNamespace = "missing auth.secretRef.onboardbaseAPIKey.namespace"
  35. errFetchOnboardbaseAPIKeySecret = "unable to find find OnboardbaseAPIKey secret: %w"
  36. errMissingOnboardbaseAPIKey = "auth.secretRef.onboardbaseAPIKey.key '%s' not found in secret '%s'"
  37. errMissingOnboardbasePasscode = "auth.secretRef.onboardbasePasscode.key '%s' not found in secret '%s'"
  38. errSecretKeyFmt = "cannot find property %s in secret data for key: %q"
  39. )
  40. type Client struct {
  41. onboardbase SecretsClientInterface
  42. onboardbaseAPIKey string
  43. onboardbasePasscode string
  44. project string
  45. environment string
  46. kube kclient.Client
  47. store *esv1beta1.OnboardbaseProvider
  48. namespace string
  49. storeKind string
  50. }
  51. // SecretsClientInterface defines the required Onboardbase Client methods.
  52. type SecretsClientInterface interface {
  53. BaseURL() *url.URL
  54. Authenticate() error
  55. GetSecret(request onboardbaseClient.SecretRequest) (*onboardbaseClient.SecretResponse, error)
  56. DeleteSecret(request onboardbaseClient.SecretRequest) error
  57. GetSecrets(request onboardbaseClient.SecretsRequest) (*onboardbaseClient.SecretsResponse, error)
  58. }
  59. func (c *Client) setAuth(ctx context.Context) error {
  60. credentialsSecret := &corev1.Secret{}
  61. credentialsSecretName := c.store.Auth.OnboardbaseAPIKeyRef.Name
  62. if credentialsSecretName == "" {
  63. return fmt.Errorf(errOnboardbaseAPIKeySecretName)
  64. }
  65. objectKey := types.NamespacedName{
  66. Name: credentialsSecretName,
  67. Namespace: c.namespace,
  68. }
  69. // only ClusterStore is allowed to set namespace (and then it's required)
  70. if c.storeKind == esv1beta1.ClusterSecretStoreKind {
  71. if c.store.Auth.OnboardbaseAPIKeyRef.Namespace == nil {
  72. return fmt.Errorf(errInvalidClusterStoreMissingOnboardbaseAPIKeyNamespace)
  73. }
  74. objectKey.Namespace = *c.store.Auth.OnboardbaseAPIKeyRef.Namespace
  75. }
  76. err := c.kube.Get(ctx, objectKey, credentialsSecret)
  77. if err != nil {
  78. return fmt.Errorf(errFetchOnboardbaseAPIKeySecret, err)
  79. }
  80. onboardbaseAPIKey := credentialsSecret.Data[c.store.Auth.OnboardbaseAPIKeyRef.Key]
  81. if (onboardbaseAPIKey == nil) || (len(onboardbaseAPIKey) == 0) {
  82. return fmt.Errorf(errMissingOnboardbaseAPIKey, c.store.Auth.OnboardbaseAPIKeyRef.Key, credentialsSecretName)
  83. }
  84. c.onboardbaseAPIKey = string(onboardbaseAPIKey)
  85. onboardbasePasscode := credentialsSecret.Data[c.store.Auth.OnboardbasePasscodeRef.Key]
  86. if (onboardbasePasscode == nil) || (len(onboardbasePasscode) == 0) {
  87. return fmt.Errorf(errMissingOnboardbasePasscode, c.store.Auth.OnboardbasePasscodeRef.Key, credentialsSecretName)
  88. }
  89. c.onboardbasePasscode = string(onboardbasePasscode)
  90. return nil
  91. }
  92. func (c *Client) Validate() (esv1beta1.ValidationResult, error) {
  93. timeout := 15 * time.Second
  94. clientURL := c.onboardbase.BaseURL().String()
  95. if err := utils.NetworkValidate(clientURL, timeout); err != nil {
  96. return esv1beta1.ValidationResultError, err
  97. }
  98. if err := c.onboardbase.Authenticate(); err != nil {
  99. return esv1beta1.ValidationResultError, err
  100. }
  101. return esv1beta1.ValidationResultReady, nil
  102. }
  103. func (c *Client) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
  104. // not implemented
  105. return nil
  106. }
  107. func (c *Client) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
  108. // not implemented
  109. return nil
  110. }
  111. func (c *Client) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  112. request := onboardbaseClient.SecretRequest{
  113. Project: c.project,
  114. Environment: c.environment,
  115. Name: ref.Key,
  116. }
  117. secret, err := c.onboardbase.GetSecret(request)
  118. if err != nil {
  119. return nil, fmt.Errorf(errGetSecret, ref.Key, err)
  120. }
  121. value := secret.Value
  122. if len(ref.Property) > 0 {
  123. jsonRes := gjson.Get(secret.Value, ref.Property)
  124. if !jsonRes.Exists() {
  125. return nil, fmt.Errorf(errSecretKeyFmt, ref.Property, ref.Key)
  126. }
  127. value = jsonRes.Raw
  128. }
  129. return []byte(value), nil
  130. }
  131. func (c *Client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  132. data, err := c.GetSecret(ctx, ref)
  133. if err != nil {
  134. return nil, err
  135. }
  136. kv := make(map[string]json.RawMessage)
  137. err = json.Unmarshal(data, &kv)
  138. if err != nil {
  139. return nil, fmt.Errorf(errUnmarshalSecretMap, ref.Key, err)
  140. }
  141. secretData := make(map[string][]byte)
  142. for k, v := range kv {
  143. var strVal string
  144. err = json.Unmarshal(v, &strVal)
  145. if err == nil {
  146. secretData[k] = []byte(strVal)
  147. } else {
  148. secretData[k] = v
  149. }
  150. }
  151. return secretData, nil
  152. }
  153. func (c *Client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  154. if len(ref.Tags) > 0 {
  155. return nil, fmt.Errorf("find by tags not supported")
  156. }
  157. secrets, err := c.getSecrets(ctx)
  158. if err != nil {
  159. return nil, err
  160. }
  161. if ref.Name == nil && ref.Path == nil {
  162. return secrets, nil
  163. }
  164. var matcher *find.Matcher
  165. if ref.Name != nil {
  166. m, err := find.New(*ref.Name)
  167. if err != nil {
  168. return nil, err
  169. }
  170. matcher = m
  171. }
  172. selected := map[string][]byte{}
  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 := onboardbaseClient.SecretsRequest{
  186. Project: c.project,
  187. Environment: c.environment,
  188. }
  189. response, err := c.onboardbase.GetSecrets(request)
  190. if err != nil {
  191. return nil, fmt.Errorf(errGetSecrets, err)
  192. }
  193. return externalSecretsFormat(response.Secrets), nil
  194. }
  195. func externalSecretsFormat(secrets onboardbaseClient.Secrets) map[string][]byte {
  196. converted := make(map[string][]byte, len(secrets))
  197. for key, value := range secrets {
  198. converted[key] = []byte(value)
  199. }
  200. return converted
  201. }