client.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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 barbican client implementation.
  14. package barbican
  15. import (
  16. "context"
  17. "encoding/json"
  18. "errors"
  19. "fmt"
  20. "strings"
  21. "github.com/gophercloud/gophercloud/v2"
  22. "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/secrets"
  23. corev1 "k8s.io/api/core/v1"
  24. esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  25. )
  26. const (
  27. errClientGeneric = "barbican client: %w"
  28. errClientMissingField = "barbican client: missing field %w"
  29. errClientListAllSecrets = "barbican client: failed to list all secrets: %w"
  30. errClientExtractSecrets = "barbican client: failed to extract secrets: %w"
  31. errClientGetSecretPayload = "barbican client: failed to get secret payload: %w"
  32. errClientGetSecretPayloadProperty = "barbican client: failed to get secret payload property: %w"
  33. errClientJSONUnmarshal = "barbican client: failed to unmarshal json: %w"
  34. )
  35. var _ esapi.SecretsClient = &Client{}
  36. // Client is a Barbican secrets client.
  37. type Client struct {
  38. keyManager *gophercloud.ServiceClient
  39. }
  40. // GetAllSecrets retrieves all secrets matching the given name.
  41. func (c *Client) GetAllSecrets(ctx context.Context, ref esapi.ExternalSecretFind) (map[string][]byte, error) {
  42. if ref.Name == nil || ref.Name.RegExp == "" {
  43. return nil, fmt.Errorf(errClientMissingField, errors.New("name and/or regexp"))
  44. }
  45. opts := secrets.ListOpts{
  46. Name: ref.Name.RegExp,
  47. }
  48. allPages, err := secrets.List(c.keyManager, opts).AllPages(ctx)
  49. if err != nil {
  50. return nil, fmt.Errorf(errClientListAllSecrets, err)
  51. }
  52. allSecrets, err := secrets.ExtractSecrets(allPages)
  53. if err != nil {
  54. return nil, fmt.Errorf(errClientExtractSecrets, err)
  55. }
  56. if len(allSecrets) == 0 {
  57. return nil, fmt.Errorf(errClientGeneric, errors.New("no secrets found"))
  58. }
  59. var secretsMap = make(map[string][]byte)
  60. // return a secret map with all found secrets.
  61. for _, secret := range allSecrets {
  62. secretUUID := extractUUIDFromRef(secret.SecretRef)
  63. secretsMap[secretUUID], err = secrets.GetPayload(ctx, c.keyManager, secretUUID, nil).Extract()
  64. if err != nil {
  65. return nil, fmt.Errorf(errClientGetSecretPayload, fmt.Errorf("failed to get secret payload for secret %s: %w", secretUUID, err))
  66. }
  67. }
  68. return secretsMap, nil
  69. }
  70. // GetSecret retrieves a secret from Barbican.
  71. func (c *Client) GetSecret(ctx context.Context, ref esapi.ExternalSecretDataRemoteRef) ([]byte, error) {
  72. payload, err := secrets.GetPayload(ctx, c.keyManager, ref.Key, nil).Extract()
  73. if err != nil {
  74. return nil, fmt.Errorf(errClientGetSecretPayload, err)
  75. }
  76. if ref.Property == "" {
  77. return payload, nil
  78. }
  79. propertyValue, err := getSecretPayloadProperty(payload, ref.Property)
  80. if err != nil {
  81. return nil, fmt.Errorf(errClientGetSecretPayloadProperty, fmt.Errorf("failed to get property %s from secret payload: %w", ref.Property, err))
  82. }
  83. return propertyValue, nil
  84. }
  85. // GetSecretMap retrieves a secret and parses it as a JSON object.
  86. func (c *Client) GetSecretMap(ctx context.Context, ref esapi.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  87. payload, err := c.GetSecret(ctx, ref)
  88. if err != nil {
  89. return nil, fmt.Errorf(errClientGeneric, err)
  90. }
  91. var rawJSON map[string]json.RawMessage
  92. if err := json.Unmarshal(payload, &rawJSON); err != nil {
  93. return nil, fmt.Errorf(errClientJSONUnmarshal, err)
  94. }
  95. secretMap := make(map[string][]byte, len(rawJSON))
  96. for k, v := range rawJSON {
  97. secretMap[k] = []byte(v)
  98. }
  99. return secretMap, nil
  100. }
  101. // PushSecret is not implemented right now for Barbican.
  102. func (c *Client) PushSecret(_ context.Context, _ *corev1.Secret, _ esapi.PushSecretData) error {
  103. return fmt.Errorf("barbican provider does not support pushing secrets")
  104. }
  105. // SecretExists is not implemented right now for Barbican.
  106. func (c *Client) SecretExists(_ context.Context, _ esapi.PushSecretRemoteRef) (bool, error) {
  107. return false, fmt.Errorf("barbican provider does not pushing secrets with update policy IfNotExists")
  108. }
  109. // DeleteSecret is not implemented right now for Barbican.
  110. func (c *Client) DeleteSecret(_ context.Context, _ esapi.PushSecretRemoteRef) error {
  111. return fmt.Errorf("barbican provider does not support deleting secrets (delete policy Delete)")
  112. }
  113. // Validate checks if the client is properly configured.
  114. func (c *Client) Validate() (esapi.ValidationResult, error) {
  115. return esapi.ValidationResultUnknown, nil
  116. }
  117. // Close closes the client and any underlying connections.
  118. func (c *Client) Close(_ context.Context) error {
  119. return nil
  120. }
  121. // getSecretPayloadProperty extracts a property from a JSON payload.
  122. func getSecretPayloadProperty(payload []byte, property string) ([]byte, error) {
  123. if property == "" {
  124. return payload, nil
  125. }
  126. var rawJSON map[string]json.RawMessage
  127. if err := json.Unmarshal(payload, &rawJSON); err != nil {
  128. return nil, fmt.Errorf(errClientJSONUnmarshal, err)
  129. }
  130. value, ok := rawJSON[property]
  131. if !ok {
  132. return nil, fmt.Errorf(errClientGeneric, fmt.Errorf("property %s not found in secret payload", property))
  133. }
  134. return value, nil
  135. }
  136. // extractUUIDFromRef extracts the UUID from a Barbican secret reference URL.
  137. func extractUUIDFromRef(secretRef string) string {
  138. // Barbican secret refs are usually of the form: https://<endpoint>/v1/secrets/<uuid>
  139. // We'll just take the last part after the last '/'
  140. // If there's a trailing slash, the UUID part would be empty, so return empty string
  141. lastSlash := strings.LastIndex(secretRef, "/")
  142. if lastSlash > -1 {
  143. return secretRef[lastSlash+1:] // <- will not result in overflow even if it's the last `/`
  144. }
  145. return ""
  146. }