client_utils.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  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 ovh
  14. import (
  15. "context"
  16. "encoding/json"
  17. "errors"
  18. "fmt"
  19. "strconv"
  20. "github.com/google/uuid"
  21. "github.com/ovh/okms-sdk-go"
  22. "github.com/tidwall/gjson"
  23. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  24. )
  25. func (cl *ovhClient) getSecretWithOvhSDK(ctx context.Context, okmsID uuid.UUID, ref esv1.ExternalSecretDataRemoteRef) ([]byte, *uint32, error) {
  26. // Check if the remoteRef key is empty.
  27. if ref.Key == "" {
  28. return []byte{}, nil, errors.New("remote key cannot be empty (spec.data.remoteRef.key)")
  29. }
  30. // Check MetaDataPolicy (not supported).
  31. if ref.MetadataPolicy == esv1.ExternalSecretMetadataPolicyFetch {
  32. return []byte{}, nil, errors.New("fetch metadata policy not supported")
  33. }
  34. // Decode the secret version.
  35. versionAddr, err := decodeSecretVersion(ref.Version)
  36. if err != nil {
  37. return []byte{}, nil, err
  38. }
  39. // Retrieve the KMS secret.
  40. includeData := true
  41. secret, err := cl.okmsClient.GetSecretV2(ctx, okmsID, ref.Key, versionAddr, &includeData)
  42. if err != nil {
  43. return []byte{}, nil, handleOkmsError(err)
  44. }
  45. if secret == nil {
  46. return []byte{}, nil, esv1.NoSecretErr
  47. }
  48. if secret.Version == nil || secret.Version.Data == nil || len(*secret.Version.Data) == 0 {
  49. return []byte{}, nil, errors.New("secret version data is missing")
  50. }
  51. // Retrieve KMS Secret property if needed.
  52. var secretData []byte
  53. if ref.Property == "" {
  54. secretData, err = json.Marshal(secret.Version.Data)
  55. } else {
  56. secretData, err = getPropertyValue(*secret.Version.Data, ref.Property)
  57. }
  58. var currentVersion *uint32
  59. if secret.Metadata != nil {
  60. currentVersion = secret.Metadata.CurrentVersion
  61. }
  62. return secretData, currentVersion, err
  63. }
  64. // Decode a secret version.
  65. //
  66. // Returns nil if no version is provided; in that case, the OVH SDK uses the latest version.
  67. func decodeSecretVersion(strVersion string) (*uint32, error) {
  68. if strVersion == "" {
  69. return nil, nil
  70. }
  71. v, err := strconv.ParseUint(strVersion, 10, 32)
  72. if err != nil {
  73. return nil, fmt.Errorf("invalid secret version %q: %w", strVersion, err)
  74. }
  75. version := uint32(v)
  76. return &version, nil
  77. }
  78. // Retrieve the value of the secret property.
  79. func getPropertyValue(data map[string]any, property string) ([]byte, error) {
  80. // Marshal data into bytes so it can be passed to gjson.Get.
  81. secretData, err := json.Marshal(data)
  82. if err != nil {
  83. return []byte{}, err
  84. }
  85. // Retrieve the property value if it exists.
  86. secretDataResult := gjson.Get(string(secretData), property)
  87. if !secretDataResult.Exists() {
  88. return []byte{}, fmt.Errorf("secret property %q not found", property)
  89. }
  90. return []byte(secretDataResult.String()), nil
  91. }
  92. // Returns an okms.KmsError struct representing the KMS response
  93. // (error_code, error_id, errors, request_id).
  94. func handleOkmsError(err error) error {
  95. okmsError := okms.AsKmsError(err)
  96. if okmsError == nil {
  97. return fmt.Errorf("failed to parse the following okms error: %w", err)
  98. } else if okmsError.ErrorCode == 17125377 { // 17125377: returned by OKMS when secret was not found
  99. return esv1.NoSecretErr
  100. }
  101. return okmsError
  102. }