client.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  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 implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package secretmanager
  13. import (
  14. "context"
  15. "encoding/json"
  16. "errors"
  17. "fmt"
  18. "maps"
  19. "slices"
  20. "strconv"
  21. "strings"
  22. secretmanager "cloud.google.com/go/secretmanager/apiv1"
  23. "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb"
  24. "github.com/googleapis/gax-go/v2"
  25. "github.com/googleapis/gax-go/v2/apierror"
  26. "github.com/tidwall/gjson"
  27. "google.golang.org/api/iterator"
  28. "google.golang.org/genproto/protobuf/field_mask"
  29. "google.golang.org/grpc/codes"
  30. "google.golang.org/grpc/status"
  31. corev1 "k8s.io/api/core/v1"
  32. ctrl "sigs.k8s.io/controller-runtime"
  33. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  34. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  35. "github.com/external-secrets/external-secrets/pkg/constants"
  36. "github.com/external-secrets/external-secrets/pkg/find"
  37. "github.com/external-secrets/external-secrets/pkg/metrics"
  38. "github.com/external-secrets/external-secrets/pkg/provider/util/locks"
  39. "github.com/external-secrets/external-secrets/pkg/utils"
  40. "github.com/external-secrets/external-secrets/pkg/utils/metadata"
  41. )
  42. const (
  43. CloudPlatformRole = "https://www.googleapis.com/auth/cloud-platform"
  44. defaultVersion = "latest"
  45. errGCPSMStore = "received invalid GCPSM SecretStore resource"
  46. errUnableGetCredentials = "unable to get credentials: %w"
  47. errClientClose = "unable to close SecretManager client: %w"
  48. errUnableProcessJSONCredentials = "failed to process the provided JSON credentials: %w"
  49. errUnableCreateGCPSMClient = "failed to create GCP secretmanager client: %w"
  50. errUninitalizedGCPProvider = "provider GCP is not initialized"
  51. errClientGetSecretAccess = "unable to access Secret from SecretManager Client: %w"
  52. errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
  53. errInvalidStore = "invalid store"
  54. errInvalidStoreSpec = "invalid store spec"
  55. errInvalidStoreProv = "invalid store provider"
  56. errInvalidGCPProv = "invalid gcp secrets manager provider"
  57. errInvalidAuthSecretRef = "invalid auth secret data: %w"
  58. errInvalidWISARef = "invalid workload identity service account reference: %w"
  59. errUnexpectedFindOperator = "unexpected find operator"
  60. managedByKey = "managed-by"
  61. managedByValue = "external-secrets"
  62. providerName = "GCPSecretManager"
  63. topicsKey = "topics"
  64. )
  65. type Client struct {
  66. smClient GoogleSecretManagerClient
  67. kube kclient.Client
  68. store *esv1beta1.GCPSMProvider
  69. storeKind string
  70. // namespace of the external secret
  71. namespace string
  72. workloadIdentity *workloadIdentity
  73. }
  74. type GoogleSecretManagerClient interface {
  75. DeleteSecret(ctx context.Context, req *secretmanagerpb.DeleteSecretRequest, opts ...gax.CallOption) error
  76. AccessSecretVersion(ctx context.Context, req *secretmanagerpb.AccessSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error)
  77. ListSecrets(ctx context.Context, req *secretmanagerpb.ListSecretsRequest, opts ...gax.CallOption) *secretmanager.SecretIterator
  78. AddSecretVersion(ctx context.Context, req *secretmanagerpb.AddSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.SecretVersion, error)
  79. CreateSecret(ctx context.Context, req *secretmanagerpb.CreateSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
  80. Close() error
  81. GetSecret(ctx context.Context, req *secretmanagerpb.GetSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
  82. UpdateSecret(context.Context, *secretmanagerpb.UpdateSecretRequest, ...gax.CallOption) (*secretmanagerpb.Secret, error)
  83. }
  84. var log = ctrl.Log.WithName("provider").WithName("gcp").WithName("secretsmanager")
  85. func (c *Client) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushSecretRemoteRef) error {
  86. gcpSecret, err := c.smClient.GetSecret(ctx, &secretmanagerpb.GetSecretRequest{
  87. Name: fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, remoteRef.GetRemoteKey()),
  88. })
  89. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMGetSecret, err)
  90. if err != nil {
  91. if status.Code(err) == codes.NotFound {
  92. return nil
  93. }
  94. return err
  95. }
  96. if manager, ok := gcpSecret.Labels[managedByKey]; !ok || manager != managedByValue {
  97. return nil
  98. }
  99. deleteSecretVersionReq := &secretmanagerpb.DeleteSecretRequest{
  100. Name: fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, remoteRef.GetRemoteKey()),
  101. Etag: gcpSecret.Etag,
  102. }
  103. err = c.smClient.DeleteSecret(ctx, deleteSecretVersionReq)
  104. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMDeleteSecret, err)
  105. return err
  106. }
  107. func parseError(err error) error {
  108. var gerr *apierror.APIError
  109. if errors.As(err, &gerr) && gerr.GRPCStatus().Code() == codes.NotFound {
  110. return esv1beta1.NoSecretError{}
  111. }
  112. return err
  113. }
  114. func (c *Client) SecretExists(ctx context.Context, ref esv1beta1.PushSecretRemoteRef) (bool, error) {
  115. secretName := fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, ref.GetRemoteKey())
  116. gcpSecret, err := c.smClient.GetSecret(ctx, &secretmanagerpb.GetSecretRequest{
  117. Name: secretName,
  118. })
  119. if err != nil {
  120. if status.Code(err) == codes.NotFound {
  121. return false, nil
  122. }
  123. return false, err
  124. }
  125. return gcpSecret != nil, nil
  126. }
  127. // PushSecret pushes a kubernetes secret key into gcp provider Secret.
  128. func (c *Client) PushSecret(ctx context.Context, secret *corev1.Secret, pushSecretData esv1beta1.PushSecretData) error {
  129. var (
  130. payload []byte
  131. err error
  132. )
  133. if pushSecretData.GetSecretKey() == "" {
  134. // Must convert secret values to string, otherwise data will be sent as base64 to Vault
  135. secretStringVal := make(map[string]string)
  136. for k, v := range secret.Data {
  137. secretStringVal[k] = string(v)
  138. }
  139. payload, err = utils.JSONMarshal(secretStringVal)
  140. if err != nil {
  141. return fmt.Errorf("failed to serialize secret content as JSON: %w", err)
  142. }
  143. } else {
  144. payload = secret.Data[pushSecretData.GetSecretKey()]
  145. }
  146. secretName := fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, pushSecretData.GetRemoteKey())
  147. gcpSecret, err := c.smClient.GetSecret(ctx, &secretmanagerpb.GetSecretRequest{
  148. Name: secretName,
  149. })
  150. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMGetSecret, err)
  151. if err != nil {
  152. if status.Code(err) != codes.NotFound {
  153. return err
  154. }
  155. var replication = &secretmanagerpb.Replication{
  156. Replication: &secretmanagerpb.Replication_Automatic_{
  157. Automatic: &secretmanagerpb.Replication_Automatic{},
  158. },
  159. }
  160. if c.store.Location != "" {
  161. replica := &secretmanagerpb.Replication_UserManaged_Replica{
  162. Location: c.store.Location,
  163. }
  164. if pushSecretData.GetMetadata() != nil {
  165. var err error
  166. meta, err := metadata.ParseMetadataParameters[PushSecretMetadataSpec](pushSecretData.GetMetadata())
  167. if err != nil {
  168. return fmt.Errorf("failed to parse PushSecret metadata: %w", err)
  169. }
  170. if meta != nil && meta.Spec.CMEKKeyName != "" {
  171. replica.CustomerManagedEncryption = &secretmanagerpb.CustomerManagedEncryption{
  172. KmsKeyName: meta.Spec.CMEKKeyName,
  173. }
  174. }
  175. }
  176. replication = &secretmanagerpb.Replication{
  177. Replication: &secretmanagerpb.Replication_UserManaged_{
  178. UserManaged: &secretmanagerpb.Replication_UserManaged{
  179. Replicas: []*secretmanagerpb.Replication_UserManaged_Replica{
  180. replica,
  181. },
  182. },
  183. },
  184. }
  185. }
  186. scrt := &secretmanagerpb.Secret{
  187. Labels: map[string]string{
  188. managedByKey: managedByValue,
  189. },
  190. Replication: replication,
  191. }
  192. topics, err := utils.FetchValueFromMetadata(topicsKey, pushSecretData.GetMetadata(), []any{})
  193. if err != nil {
  194. return fmt.Errorf("failed to fetch topics from metadata: %w", err)
  195. }
  196. for _, t := range topics {
  197. name, ok := t.(string)
  198. if !ok {
  199. return fmt.Errorf("invalid topic type")
  200. }
  201. scrt.Topics = append(scrt.Topics, &secretmanagerpb.Topic{
  202. Name: name,
  203. })
  204. }
  205. gcpSecret, err = c.smClient.CreateSecret(ctx, &secretmanagerpb.CreateSecretRequest{
  206. Parent: fmt.Sprintf("projects/%s", c.store.ProjectID),
  207. SecretId: pushSecretData.GetRemoteKey(),
  208. Secret: scrt,
  209. })
  210. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMCreateSecret, err)
  211. if err != nil {
  212. return err
  213. }
  214. }
  215. builder, err := newPushSecretBuilder(payload, pushSecretData)
  216. if err != nil {
  217. return err
  218. }
  219. annotations, labels, topics, err := builder.buildMetadata(gcpSecret.Annotations, gcpSecret.Labels, gcpSecret.Topics)
  220. if err != nil {
  221. return err
  222. }
  223. // Comparing with a pointer based slice doesn't work so we are converting
  224. // it to a string slice.
  225. existingTopics := make([]string, 0, len(gcpSecret.Topics))
  226. for _, t := range gcpSecret.Topics {
  227. existingTopics = append(existingTopics, t.Name)
  228. }
  229. if !maps.Equal(gcpSecret.Annotations, annotations) || !maps.Equal(gcpSecret.Labels, labels) || !slices.Equal(existingTopics, topics) {
  230. scrt := &secretmanagerpb.Secret{
  231. Name: gcpSecret.Name,
  232. Etag: gcpSecret.Etag,
  233. Labels: labels,
  234. Annotations: annotations,
  235. Topics: gcpSecret.Topics,
  236. }
  237. if c.store.Location != "" {
  238. scrt.Replication = &secretmanagerpb.Replication{
  239. Replication: &secretmanagerpb.Replication_UserManaged_{
  240. UserManaged: &secretmanagerpb.Replication_UserManaged{
  241. Replicas: []*secretmanagerpb.Replication_UserManaged_Replica{
  242. {
  243. Location: c.store.Location,
  244. },
  245. },
  246. },
  247. },
  248. }
  249. }
  250. _, err = c.smClient.UpdateSecret(ctx, &secretmanagerpb.UpdateSecretRequest{
  251. Secret: scrt,
  252. UpdateMask: &field_mask.FieldMask{
  253. Paths: []string{"labels", "annotations"},
  254. },
  255. })
  256. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMUpdateSecret, err)
  257. if err != nil {
  258. return err
  259. }
  260. }
  261. unlock, err := locks.TryLock(providerName, secretName)
  262. if err != nil {
  263. return err
  264. }
  265. defer unlock()
  266. gcpVersion, err := c.smClient.AccessSecretVersion(ctx, &secretmanagerpb.AccessSecretVersionRequest{
  267. Name: fmt.Sprintf("%s/versions/latest", secretName),
  268. })
  269. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMAccessSecretVersion, err)
  270. if err != nil && status.Code(err) != codes.NotFound {
  271. return err
  272. }
  273. if gcpVersion != nil && gcpVersion.Payload != nil && !builder.needUpdate(gcpVersion.Payload.Data) {
  274. return nil
  275. }
  276. var original []byte
  277. if gcpVersion != nil && gcpVersion.Payload != nil {
  278. original = gcpVersion.Payload.Data
  279. }
  280. data, err := builder.buildData(original)
  281. if err != nil {
  282. return err
  283. }
  284. addSecretVersionReq := &secretmanagerpb.AddSecretVersionRequest{
  285. Parent: fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, pushSecretData.GetRemoteKey()),
  286. Payload: &secretmanagerpb.SecretPayload{
  287. Data: data,
  288. },
  289. }
  290. _, err = c.smClient.AddSecretVersion(ctx, addSecretVersionReq)
  291. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMAddSecretVersion, err)
  292. return err
  293. }
  294. // GetAllSecrets syncs multiple secrets from gcp provider into a single Kubernetes Secret.
  295. func (c *Client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  296. if ref.Name != nil {
  297. return c.findByName(ctx, ref)
  298. }
  299. if len(ref.Tags) > 0 {
  300. return c.findByTags(ctx, ref)
  301. }
  302. return nil, errors.New(errUnexpectedFindOperator)
  303. }
  304. func (c *Client) findByName(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  305. // regex matcher
  306. matcher, err := find.New(*ref.Name)
  307. if err != nil {
  308. return nil, err
  309. }
  310. req := &secretmanagerpb.ListSecretsRequest{
  311. Parent: fmt.Sprintf("projects/%s", c.store.ProjectID),
  312. }
  313. if ref.Path != nil {
  314. req.Filter = fmt.Sprintf("name:%s", *ref.Path)
  315. }
  316. // Call the API.
  317. it := c.smClient.ListSecrets(ctx, req)
  318. secretMap := make(map[string][]byte)
  319. var resp *secretmanagerpb.Secret
  320. defer metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMListSecrets, err)
  321. for {
  322. resp, err = it.Next()
  323. if errors.Is(err, iterator.Done) {
  324. break
  325. }
  326. if err != nil {
  327. return nil, fmt.Errorf("failed to list secrets: %w", err)
  328. }
  329. log.V(1).Info("gcp sm findByName found", "secrets", strconv.Itoa(it.PageInfo().Remaining()))
  330. key := c.trimName(resp.Name)
  331. // If we don't match we skip.
  332. // Also, if we have path, and it is not at the beguining we skip.
  333. // We have to check if path is at the beguining of the key because
  334. // there is no way to create a `name:%s*` (starts with) filter
  335. // At https://cloud.google.com/secret-manager/docs/filtering you can use `*`
  336. // but not like that it seems.
  337. if !matcher.MatchName(key) || (ref.Path != nil && !strings.HasPrefix(key, *ref.Path)) {
  338. continue
  339. }
  340. log.V(1).Info("gcp sm findByName matches", "name", resp.Name)
  341. secretMap[key], err = c.getData(ctx, key)
  342. if err != nil {
  343. return nil, err
  344. }
  345. }
  346. return utils.ConvertKeys(ref.ConversionStrategy, secretMap)
  347. }
  348. func (c *Client) getData(ctx context.Context, key string) ([]byte, error) {
  349. dataRef := esv1beta1.ExternalSecretDataRemoteRef{
  350. Key: key,
  351. }
  352. data, err := c.GetSecret(ctx, dataRef)
  353. if err != nil {
  354. return []byte(""), err
  355. }
  356. return data, nil
  357. }
  358. func (c *Client) findByTags(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  359. var tagFilter string
  360. for k, v := range ref.Tags {
  361. tagFilter = fmt.Sprintf("%slabels.%s=%s ", tagFilter, k, v)
  362. }
  363. tagFilter = strings.TrimSuffix(tagFilter, " ")
  364. if ref.Path != nil {
  365. tagFilter = fmt.Sprintf("%s name:%s", tagFilter, *ref.Path)
  366. }
  367. req := &secretmanagerpb.ListSecretsRequest{
  368. Parent: fmt.Sprintf("projects/%s", c.store.ProjectID),
  369. }
  370. log.V(1).Info("gcp sm findByTags", "tagFilter", tagFilter)
  371. req.Filter = tagFilter
  372. // Call the API.
  373. it := c.smClient.ListSecrets(ctx, req)
  374. var resp *secretmanagerpb.Secret
  375. var err error
  376. defer metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMListSecrets, err)
  377. secretMap := make(map[string][]byte)
  378. for {
  379. resp, err = it.Next()
  380. if errors.Is(err, iterator.Done) {
  381. break
  382. }
  383. if err != nil {
  384. return nil, fmt.Errorf("failed to list secrets: %w", err)
  385. }
  386. key := c.trimName(resp.Name)
  387. if ref.Path != nil && !strings.HasPrefix(key, *ref.Path) {
  388. continue
  389. }
  390. log.V(1).Info("gcp sm findByTags matches tags", "name", resp.Name)
  391. secretMap[key], err = c.getData(ctx, key)
  392. if err != nil {
  393. return nil, err
  394. }
  395. }
  396. return utils.ConvertKeys(ref.ConversionStrategy, secretMap)
  397. }
  398. func (c *Client) trimName(name string) string {
  399. projectIDNumuber := c.extractProjectIDNumber(name)
  400. key := strings.TrimPrefix(name, fmt.Sprintf("projects/%s/secrets/", projectIDNumuber))
  401. return key
  402. }
  403. // extractProjectIDNumber grabs the project id from the full name returned by gcp api
  404. // gcp api seems to always return the number and not the project name
  405. // (and users would always use the name, while requests accept both).
  406. func (c *Client) extractProjectIDNumber(secretFullName string) string {
  407. s := strings.Split(secretFullName, "/")
  408. ProjectIDNumuber := s[1]
  409. return ProjectIDNumuber
  410. }
  411. // GetSecret returns a single secret from the provider.
  412. func (c *Client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  413. if utils.IsNil(c.smClient) || c.store.ProjectID == "" {
  414. return nil, errors.New(errUninitalizedGCPProvider)
  415. }
  416. if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
  417. return c.getSecretMetadata(ctx, ref)
  418. }
  419. version := ref.Version
  420. if version == "" {
  421. version = defaultVersion
  422. }
  423. req := &secretmanagerpb.AccessSecretVersionRequest{
  424. Name: fmt.Sprintf("projects/%s/secrets/%s/versions/%s", c.store.ProjectID, ref.Key, version),
  425. }
  426. result, err := c.smClient.AccessSecretVersion(ctx, req)
  427. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMAccessSecretVersion, err)
  428. err = parseError(err)
  429. if err != nil {
  430. return nil, fmt.Errorf(errClientGetSecretAccess, err)
  431. }
  432. if ref.Property == "" {
  433. if result.Payload.Data != nil {
  434. return result.Payload.Data, nil
  435. }
  436. return nil, fmt.Errorf("invalid secret received. no secret string for key: %s", ref.Key)
  437. }
  438. val := getDataByProperty(result.Payload.Data, ref.Property)
  439. if !val.Exists() {
  440. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  441. }
  442. return []byte(val.String()), nil
  443. }
  444. func (c *Client) getSecretMetadata(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  445. secret, err := c.smClient.GetSecret(ctx, &secretmanagerpb.GetSecretRequest{
  446. Name: fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, ref.Key),
  447. })
  448. err = parseError(err)
  449. if err != nil {
  450. return nil, fmt.Errorf(errClientGetSecretAccess, err)
  451. }
  452. const (
  453. annotations = "annotations"
  454. labels = "labels"
  455. )
  456. if annotation := c.extractMetadataKey(ref.Property, annotations); annotation != "" {
  457. v, ok := secret.GetAnnotations()[annotation]
  458. if !ok {
  459. return nil, fmt.Errorf("annotation with key %s does not exist in secret %s", annotation, ref.Key)
  460. }
  461. return []byte(v), nil
  462. }
  463. if label := c.extractMetadataKey(ref.Property, labels); label != "" {
  464. v, ok := secret.GetLabels()[label]
  465. if !ok {
  466. return nil, fmt.Errorf("label with key %s does not exist in secret %s", label, ref.Key)
  467. }
  468. return []byte(v), nil
  469. }
  470. if ref.Property == annotations {
  471. j, err := json.Marshal(secret.GetAnnotations())
  472. if err != nil {
  473. return nil, fmt.Errorf("faild marshaling annotations into json: %w", err)
  474. }
  475. return j, nil
  476. }
  477. if ref.Property == labels {
  478. j, err := json.Marshal(secret.GetLabels())
  479. if err != nil {
  480. return nil, fmt.Errorf("faild marshaling labels into json: %w", err)
  481. }
  482. return j, nil
  483. }
  484. if ref.Property != "" {
  485. return nil, fmt.Errorf("invalid property %s: metadata property should start with either %s or %s", ref.Property, annotations, labels)
  486. }
  487. j, err := json.Marshal(map[string]map[string]string{
  488. "annotations": secret.GetAnnotations(),
  489. "labels": secret.GetLabels(),
  490. })
  491. if err != nil {
  492. return nil, fmt.Errorf("faild marshaling metadata map into json: %w", err)
  493. }
  494. return j, nil
  495. }
  496. func (c *Client) extractMetadataKey(s, p string) string {
  497. prefix := p + "."
  498. if !strings.HasPrefix(s, prefix) {
  499. return ""
  500. }
  501. return strings.TrimPrefix(s, prefix)
  502. }
  503. // GetSecretMap returns multiple k/v pairs from the provider.
  504. func (c *Client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  505. if c.smClient == nil || c.store.ProjectID == "" {
  506. return nil, errors.New(errUninitalizedGCPProvider)
  507. }
  508. data, err := c.GetSecret(ctx, ref)
  509. if err != nil {
  510. return nil, err
  511. }
  512. kv := make(map[string]json.RawMessage)
  513. err = json.Unmarshal(data, &kv)
  514. if err != nil {
  515. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  516. }
  517. secretData := make(map[string][]byte)
  518. for k, v := range kv {
  519. var strVal string
  520. err = json.Unmarshal(v, &strVal)
  521. if err == nil {
  522. secretData[k] = []byte(strVal)
  523. } else {
  524. secretData[k] = v
  525. }
  526. }
  527. return secretData, nil
  528. }
  529. func (c *Client) Close(_ context.Context) error {
  530. var err error
  531. if c.smClient != nil {
  532. err = c.smClient.Close()
  533. }
  534. if c.workloadIdentity != nil {
  535. err = c.workloadIdentity.Close()
  536. }
  537. useMu.Unlock()
  538. if err != nil {
  539. return fmt.Errorf(errClientClose, err)
  540. }
  541. return nil
  542. }
  543. func (c *Client) Validate() (esv1beta1.ValidationResult, error) {
  544. if c.storeKind == esv1beta1.ClusterSecretStoreKind && isReferentSpec(c.store) {
  545. return esv1beta1.ValidationResultUnknown, nil
  546. }
  547. return esv1beta1.ValidationResultReady, nil
  548. }
  549. func getDataByProperty(data []byte, property string) gjson.Result {
  550. var payload string
  551. if data != nil {
  552. payload = string(data)
  553. }
  554. idx := strings.Index(property, ".")
  555. refProperty := property
  556. if idx > 0 {
  557. refProperty = strings.ReplaceAll(refProperty, ".", "\\.")
  558. val := gjson.Get(payload, refProperty)
  559. if val.Exists() {
  560. return val
  561. }
  562. }
  563. return gjson.Get(payload, property)
  564. }