client.go 7.4 KB

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