client.go 21 KB

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