| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- /*
- Copyright © The ESO Authors
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- https://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- // Package onboardbase implements a client for interacting with Onboardbase secrets management service.
- package onboardbase
- import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "net/url"
- "strings"
- "time"
- "github.com/tidwall/gjson"
- corev1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/types"
- kclient "sigs.k8s.io/controller-runtime/pkg/client"
- esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
- obclient "github.com/external-secrets/external-secrets/providers/v1/onboardbase/client"
- "github.com/external-secrets/external-secrets/runtime/esutils"
- "github.com/external-secrets/external-secrets/runtime/find"
- )
- const (
- errGetSecret = "could not get secret %s: %s"
- errGetSecrets = "could not get secrets %s"
- errUnmarshalSecretMap = "unable to unmarshal secret %s: %w"
- errOnboardbaseAPIKeySecretName = "missing auth.secretRef.onboardbaseAPIKey.name"
- errInvalidClusterStoreMissingOnboardbaseAPIKeyNamespace = "missing auth.secretRef.onboardbaseAPIKey.namespace"
- errFetchOnboardbaseAPIKeySecret = "unable to find find OnboardbaseAPIKey secret: %w"
- errMissingOnboardbaseAPIKey = "auth.secretRef.onboardbaseAPIKey.key '%s' not found in secret '%s'"
- errMissingOnboardbasePasscode = "auth.secretRef.onboardbasePasscode.key '%s' not found in secret '%s'"
- errSecretKeyFmt = "cannot find property %s in secret data for key: %q"
- )
- // Client implements the Onboardbase secrets client.
- type Client struct {
- onboardbase SecretsClientInterface
- onboardbaseAPIKey string
- onboardbasePasscode string
- project string
- environment string
- kube kclient.Client
- store *esv1.OnboardbaseProvider
- namespace string
- storeKind string
- }
- // SecretsClientInterface defines the required Onboardbase Client methods.
- type SecretsClientInterface interface {
- BaseURL() *url.URL
- Authenticate() error
- GetSecret(request obclient.SecretRequest) (*obclient.SecretResponse, error)
- DeleteSecret(request obclient.SecretRequest) error
- GetSecrets(request obclient.SecretsRequest) (*obclient.SecretsResponse, error)
- }
- func (c *Client) setAuth(ctx context.Context) error {
- credentialsSecret := &corev1.Secret{}
- credentialsSecretName := c.store.Auth.OnboardbaseAPIKeyRef.Name
- if credentialsSecretName == "" {
- return errors.New(errOnboardbaseAPIKeySecretName)
- }
- objectKey := types.NamespacedName{
- Name: credentialsSecretName,
- Namespace: c.namespace,
- }
- // only ClusterStore is allowed to set namespace (and then it's required)
- if c.storeKind == esv1.ClusterSecretStoreKind {
- if c.store.Auth.OnboardbaseAPIKeyRef.Namespace == nil {
- return errors.New(errInvalidClusterStoreMissingOnboardbaseAPIKeyNamespace)
- }
- objectKey.Namespace = *c.store.Auth.OnboardbaseAPIKeyRef.Namespace
- }
- err := c.kube.Get(ctx, objectKey, credentialsSecret)
- if err != nil {
- return fmt.Errorf(errFetchOnboardbaseAPIKeySecret, err)
- }
- onboardbaseAPIKey := credentialsSecret.Data[c.store.Auth.OnboardbaseAPIKeyRef.Key]
- if (onboardbaseAPIKey == nil) || (len(onboardbaseAPIKey) == 0) {
- return fmt.Errorf(errMissingOnboardbaseAPIKey, c.store.Auth.OnboardbaseAPIKeyRef.Key, credentialsSecretName)
- }
- c.onboardbaseAPIKey = string(onboardbaseAPIKey)
- onboardbasePasscode := credentialsSecret.Data[c.store.Auth.OnboardbasePasscodeRef.Key]
- if (onboardbasePasscode == nil) || (len(onboardbasePasscode) == 0) {
- return fmt.Errorf(errMissingOnboardbasePasscode, c.store.Auth.OnboardbasePasscodeRef.Key, credentialsSecretName)
- }
- c.onboardbasePasscode = string(onboardbasePasscode)
- return nil
- }
- // Validate performs validation of the Onboardbase client configuration.
- func (c *Client) Validate() (esv1.ValidationResult, error) {
- timeout := 15 * time.Second
- clientURL := c.onboardbase.BaseURL().String()
- if err := esutils.NetworkValidate(clientURL, timeout); err != nil {
- return esv1.ValidationResultError, err
- }
- if err := c.onboardbase.Authenticate(); err != nil {
- return esv1.ValidationResultError, err
- }
- return esv1.ValidationResultReady, nil
- }
- // DeleteSecret removes a secret from Onboardbase.
- func (c *Client) DeleteSecret(_ context.Context, _ esv1.PushSecretRemoteRef) error {
- // not implemented
- return nil
- }
- // SecretExists checks if a secret exists in Onboardbase.
- func (c *Client) SecretExists(_ context.Context, _ esv1.PushSecretRemoteRef) (bool, error) {
- // not implemented
- return false, nil
- }
- // PushSecret creates or updates a secret in Onboardbase.
- func (c *Client) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1.PushSecretData) error {
- // not implemented
- return nil
- }
- // GetSecret retrieves a secret from Onboardbase by its reference.
- func (c *Client) GetSecret(_ context.Context, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
- request := obclient.SecretRequest{
- Project: c.project,
- Environment: c.environment,
- Name: ref.Key,
- }
- secret, err := c.onboardbase.GetSecret(request)
- if err != nil {
- return nil, fmt.Errorf(errGetSecret, ref.Key, err)
- }
- value := secret.Value
- if ref.Property != "" {
- jsonRes := gjson.Get(secret.Value, ref.Property)
- if !jsonRes.Exists() {
- return nil, fmt.Errorf(errSecretKeyFmt, ref.Property, ref.Key)
- }
- value = jsonRes.Raw
- }
- return []byte(value), nil
- }
- // GetSecretMap retrieves a secret from Onboardbase and returns it as a map.
- func (c *Client) GetSecretMap(ctx context.Context, ref esv1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
- data, err := c.GetSecret(ctx, ref)
- if err != nil {
- return nil, err
- }
- kv := make(map[string]json.RawMessage)
- err = json.Unmarshal(data, &kv)
- if err != nil {
- return nil, fmt.Errorf(errUnmarshalSecretMap, ref.Key, err)
- }
- secretData := make(map[string][]byte)
- for k, v := range kv {
- var strVal string
- err = json.Unmarshal(v, &strVal)
- if err == nil {
- secretData[k] = []byte(strVal)
- } else {
- secretData[k] = v
- }
- }
- return secretData, nil
- }
- // GetAllSecrets retrieves all secrets from Onboardbase that match the given criteria.
- func (c *Client) GetAllSecrets(ctx context.Context, ref esv1.ExternalSecretFind) (map[string][]byte, error) {
- if len(ref.Tags) > 0 {
- return nil, errors.New("find by tags not supported")
- }
- secrets, err := c.getSecrets(ctx)
- if err != nil {
- return nil, err
- }
- if ref.Name == nil && ref.Path == nil {
- return secrets, nil
- }
- var matcher *find.Matcher
- if ref.Name != nil {
- m, err := find.New(*ref.Name)
- if err != nil {
- return nil, err
- }
- matcher = m
- }
- selected := map[string][]byte{}
- for key, value := range secrets {
- if (matcher != nil && !matcher.MatchName(key)) || (ref.Path != nil && !strings.HasPrefix(key, *ref.Path)) {
- continue
- }
- selected[key] = value
- }
- return selected, nil
- }
- // Close implements cleanup operations for the Onboardbase client.
- func (c *Client) Close(_ context.Context) error {
- return nil
- }
- func (c *Client) getSecrets(_ context.Context) (map[string][]byte, error) {
- request := obclient.SecretsRequest{
- Project: c.project,
- Environment: c.environment,
- }
- response, err := c.onboardbase.GetSecrets(request)
- if err != nil {
- return nil, fmt.Errorf(errGetSecrets, err)
- }
- return externalSecretsFormat(response.Secrets), nil
- }
- func externalSecretsFormat(secrets obclient.Secrets) map[string][]byte {
- converted := make(map[string][]byte, len(secrets))
- for key, value := range secrets {
- converted[key] = []byte(value)
- }
- return converted
- }
|