client.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /*
  2. Copyright © The ESO Authors
  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 volcengine
  14. import (
  15. "context"
  16. "encoding/json"
  17. "errors"
  18. "fmt"
  19. "github.com/volcengine/volcengine-go-sdk/service/kms"
  20. corev1 "k8s.io/api/core/v1"
  21. esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  22. )
  23. const (
  24. notImplemented = "not implemented"
  25. )
  26. var _ esapi.SecretsClient = &Client{}
  27. // Client is a client for the Volcengine provider.
  28. type Client struct {
  29. kms kms.KMSAPI
  30. }
  31. // NewClient creates a new Volcengine client.
  32. func NewClient(kms kms.KMSAPI) *Client {
  33. return &Client{
  34. kms: kms,
  35. }
  36. }
  37. // GetSecret retrieves a secret value from Volcengine Secrets Manager.
  38. func (c *Client) GetSecret(ctx context.Context, ref esapi.ExternalSecretDataRemoteRef) ([]byte, error) {
  39. return c.getSecretValue(ctx, ref)
  40. }
  41. // SecretExists checks if a secret exists in Volcengine Secrets Manager.
  42. func (c *Client) SecretExists(ctx context.Context, remoteRef esapi.PushSecretRemoteRef) (bool, error) {
  43. secretName := remoteRef.GetRemoteKey()
  44. if secretName == "" {
  45. return false, errors.New("secret name is empty")
  46. }
  47. _, err := c.kms.DescribeSecretWithContext(ctx, &kms.DescribeSecretInput{
  48. SecretName: &secretName,
  49. })
  50. if err != nil {
  51. return false, err
  52. }
  53. return true, nil
  54. }
  55. // Validate checks if the provider is configured correctly.
  56. func (c *Client) Validate() (esapi.ValidationResult, error) {
  57. if c.kms != nil {
  58. return esapi.ValidationResultReady, nil
  59. }
  60. return esapi.ValidationResultError, errors.New("kms client is not initialized")
  61. }
  62. // GetSecretMap retrieves a secret value and unmarshals it as a map.
  63. func (c *Client) GetSecretMap(ctx context.Context, ref esapi.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  64. value, err := c.getSecretValue(ctx, ref)
  65. if err != nil {
  66. return nil, err
  67. }
  68. var rawSecretMap map[string]json.RawMessage
  69. if err := json.Unmarshal(value, &rawSecretMap); err != nil {
  70. // Do not wrap the original error as json.Unmarshal errors may contain
  71. // sensitive secret data in the error message
  72. return nil, errors.New("failed to unmarshal secret: invalid JSON format")
  73. }
  74. secretMap := make(map[string][]byte, len(rawSecretMap))
  75. for key, value := range rawSecretMap {
  76. secretMap[key] = []byte(value)
  77. }
  78. return secretMap, nil
  79. }
  80. // GetAllSecrets retrieves all secrets matching the given criteria.
  81. func (c *Client) GetAllSecrets(_ context.Context, _ esapi.ExternalSecretFind) (map[string][]byte, error) {
  82. return nil, errors.New(notImplemented)
  83. }
  84. // PushSecret creates or updates a secret in Volcengine Secrets Manager.
  85. func (c *Client) PushSecret(_ context.Context, _ *corev1.Secret, _ esapi.PushSecretData) error {
  86. return errors.New(notImplemented)
  87. }
  88. // DeleteSecret deletes a secret from Volcengine Secrets Manager.
  89. func (c *Client) DeleteSecret(_ context.Context, _ esapi.PushSecretRemoteRef) error {
  90. return errors.New(notImplemented)
  91. }
  92. // Close is a no-op for the Volcengine client.
  93. func (c *Client) Close(_ context.Context) error {
  94. return nil
  95. }
  96. func (c *Client) getSecretValue(ctx context.Context, ref esapi.ExternalSecretDataRemoteRef) ([]byte, error) {
  97. output, err := c.kms.GetSecretValueWithContext(ctx, &kms.GetSecretValueInput{
  98. SecretName: &ref.Key,
  99. VersionID: resolveVersion(ref),
  100. })
  101. if err != nil {
  102. return nil, err
  103. }
  104. if output.SecretValue == nil {
  105. return nil, fmt.Errorf("secret %s has no value", ref.Key)
  106. }
  107. secret := []byte(*output.SecretValue)
  108. if ref.Property == "" {
  109. return secret, nil
  110. }
  111. return extractProperty(secret, ref.Property)
  112. }
  113. func extractProperty(secret []byte, property string) ([]byte, error) {
  114. var secretMap map[string]json.RawMessage
  115. if err := json.Unmarshal(secret, &secretMap); err != nil {
  116. // Do not wrap the original error as json.Unmarshal errors may contain
  117. // sensitive secret data in the error message
  118. return nil, errors.New("failed to unmarshal secret: invalid JSON format")
  119. }
  120. value, ok := secretMap[property]
  121. if !ok {
  122. return nil, fmt.Errorf("property %q not found in secret", property)
  123. }
  124. var s string
  125. if json.Unmarshal(value, &s) == nil {
  126. return []byte(s), nil
  127. }
  128. return value, nil
  129. }
  130. func resolveVersion(ref esapi.ExternalSecretDataRemoteRef) *string {
  131. if ref.Version != "" {
  132. return &ref.Version
  133. }
  134. return nil
  135. }