diff --git a/apis/externalsecrets/v1beta1/secretstore_beyondtrust_types.go b/apis/externalsecrets/v1beta1/secretstore_beyondtrust_types.go index 2d9663e22..9c94b6583 100644 --- a/apis/externalsecrets/v1beta1/secretstore_beyondtrust_types.go +++ b/apis/externalsecrets/v1beta1/secretstore_beyondtrust_types.go @@ -29,11 +29,13 @@ type BeyondTrustProviderSecretRef struct { // Configures a store to sync secrets using BeyondTrust Password Safe. type BeyondtrustAuth struct { - // +required - API OAuth Client ID. - ClientID *BeyondTrustProviderSecretRef `json:"clientId"` - // +required - API OAuth Client Secret. - ClientSecret *BeyondTrustProviderSecretRef `json:"clientSecret"` - // Content of the certificate (cert.pem) for use when authenticating with an OAuth client Id using a Client Certificate. + // APIKey If not provided then ClientID/ClientSecret become required. + APIKey *BeyondTrustProviderSecretRef `json:"apiKey,omitempty"` + // ClientID is the API OAuth Client ID. + ClientID *BeyondTrustProviderSecretRef `json:"clientId,omitempty"` + // ClientSecret is the API OAuth Client Secret. + ClientSecret *BeyondTrustProviderSecretRef `json:"clientSecret,omitempty"` + // Certificate (cert.pem) for use when authenticating with an OAuth client Id using a Client Certificate. Certificate *BeyondTrustProviderSecretRef `json:"certificate,omitempty"` // Certificate private key (key.pem). For use when authenticating with an OAuth client Id CertificateKey *BeyondTrustProviderSecretRef `json:"certificateKey,omitempty"` diff --git a/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go b/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go index 06f043190..8208b7f52 100644 --- a/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go +++ b/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go @@ -414,6 +414,11 @@ func (in *BeyondTrustProviderSecretRef) DeepCopy() *BeyondTrustProviderSecretRef // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BeyondtrustAuth) DeepCopyInto(out *BeyondtrustAuth) { *out = *in + if in.APIKey != nil { + in, out := &in.APIKey, &out.APIKey + *out = new(BeyondTrustProviderSecretRef) + (*in).DeepCopyInto(*out) + } if in.ClientID != nil { in, out := &in.ClientID, &out.ClientID *out = new(BeyondTrustProviderSecretRef) diff --git a/config/crds/bases/external-secrets.io_clustersecretstores.yaml b/config/crds/bases/external-secrets.io_clustersecretstores.yaml index 47d0e9731..33f6d5cc5 100644 --- a/config/crds/bases/external-secrets.io_clustersecretstores.yaml +++ b/config/crds/bases/external-secrets.io_clustersecretstores.yaml @@ -2305,10 +2305,37 @@ spec: description: Auth configures how the operator authenticates with Beyondtrust. properties: + apiKey: + description: APIKey If not provided then ClientID/ClientSecret + become required. + properties: + secretRef: + description: SecretRef references a key in a secret + that will be used as value. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set + a value without using a secret. + type: string + type: object certificate: - description: Content of the certificate (cert.pem) for - use when authenticating with an OAuth client Id using - a Client Certificate. + description: Certificate (cert.pem) for use when authenticating + with an OAuth client Id using a Client Certificate. properties: secretRef: description: SecretRef references a key in a secret @@ -2363,6 +2390,7 @@ spec: type: string type: object clientId: + description: ClientID is the API OAuth Client ID. properties: secretRef: description: SecretRef references a key in a secret @@ -2389,6 +2417,7 @@ spec: type: string type: object clientSecret: + description: ClientSecret is the API OAuth Client Secret. properties: secretRef: description: SecretRef references a key in a secret @@ -2414,9 +2443,6 @@ spec: a value without using a secret. type: string type: object - required: - - clientId - - clientSecret type: object server: description: Auth configures how API server works. diff --git a/config/crds/bases/external-secrets.io_secretstores.yaml b/config/crds/bases/external-secrets.io_secretstores.yaml index 361ad5141..f8576921c 100644 --- a/config/crds/bases/external-secrets.io_secretstores.yaml +++ b/config/crds/bases/external-secrets.io_secretstores.yaml @@ -2305,10 +2305,37 @@ spec: description: Auth configures how the operator authenticates with Beyondtrust. properties: + apiKey: + description: APIKey If not provided then ClientID/ClientSecret + become required. + properties: + secretRef: + description: SecretRef references a key in a secret + that will be used as value. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set + a value without using a secret. + type: string + type: object certificate: - description: Content of the certificate (cert.pem) for - use when authenticating with an OAuth client Id using - a Client Certificate. + description: Certificate (cert.pem) for use when authenticating + with an OAuth client Id using a Client Certificate. properties: secretRef: description: SecretRef references a key in a secret @@ -2363,6 +2390,7 @@ spec: type: string type: object clientId: + description: ClientID is the API OAuth Client ID. properties: secretRef: description: SecretRef references a key in a secret @@ -2389,6 +2417,7 @@ spec: type: string type: object clientSecret: + description: ClientSecret is the API OAuth Client Secret. properties: secretRef: description: SecretRef references a key in a secret @@ -2414,9 +2443,6 @@ spec: a value without using a secret. type: string type: object - required: - - clientId - - clientSecret type: object server: description: Auth configures how API server works. diff --git a/deploy/crds/bundle.yaml b/deploy/crds/bundle.yaml index 0f05f69e9..79e37f529 100644 --- a/deploy/crds/bundle.yaml +++ b/deploy/crds/bundle.yaml @@ -2814,8 +2814,32 @@ spec: auth: description: Auth configures how the operator authenticates with Beyondtrust. properties: + apiKey: + description: APIKey If not provided then ClientID/ClientSecret become required. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object certificate: - description: Content of the certificate (cert.pem) for use when authenticating with an OAuth client Id using a Client Certificate. + description: Certificate (cert.pem) for use when authenticating with an OAuth client Id using a Client Certificate. properties: secretRef: description: SecretRef references a key in a secret that will be used as value. @@ -2863,6 +2887,7 @@ spec: type: string type: object clientId: + description: ClientID is the API OAuth Client ID. properties: secretRef: description: SecretRef references a key in a secret that will be used as value. @@ -2886,6 +2911,7 @@ spec: type: string type: object clientSecret: + description: ClientSecret is the API OAuth Client Secret. properties: secretRef: description: SecretRef references a key in a secret that will be used as value. @@ -2908,9 +2934,6 @@ spec: description: Value can be specified directly to set a value without using a secret. type: string type: object - required: - - clientId - - clientSecret type: object server: description: Auth configures how API server works. @@ -8657,8 +8680,32 @@ spec: auth: description: Auth configures how the operator authenticates with Beyondtrust. properties: + apiKey: + description: APIKey If not provided then ClientID/ClientSecret become required. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object certificate: - description: Content of the certificate (cert.pem) for use when authenticating with an OAuth client Id using a Client Certificate. + description: Certificate (cert.pem) for use when authenticating with an OAuth client Id using a Client Certificate. properties: secretRef: description: SecretRef references a key in a secret that will be used as value. @@ -8706,6 +8753,7 @@ spec: type: string type: object clientId: + description: ClientID is the API OAuth Client ID. properties: secretRef: description: SecretRef references a key in a secret that will be used as value. @@ -8729,6 +8777,7 @@ spec: type: string type: object clientSecret: + description: ClientSecret is the API OAuth Client Secret. properties: secretRef: description: SecretRef references a key in a secret that will be used as value. @@ -8751,9 +8800,6 @@ spec: description: Value can be specified directly to set a value without using a secret. type: string type: object - required: - - clientId - - clientSecret type: object server: description: Auth configures how API server works. diff --git a/docs/api/spec.md b/docs/api/spec.md index d993fe775..c68aae94f 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -1076,6 +1076,19 @@ External Secrets meta/v1.SecretKeySelector +apiKey
+ + +BeyondTrustProviderSecretRef + + + + +

APIKey If not provided then ClientID/ClientSecret become required.

+ + + + clientId
@@ -1084,6 +1097,7 @@ BeyondTrustProviderSecretRef +

ClientID is the API OAuth Client ID.

@@ -1096,6 +1110,7 @@ BeyondTrustProviderSecretRef +

ClientSecret is the API OAuth Client Secret.

@@ -1108,7 +1123,7 @@ BeyondTrustProviderSecretRef -

Content of the certificate (cert.pem) for use when authenticating with an OAuth client Id using a Client Certificate.

+

Certificate (cert.pem) for use when authenticating with an OAuth client Id using a Client Certificate.

diff --git a/docs/provider/beyondtrust.md b/docs/provider/beyondtrust.md index 104c4bde8..1f21a4e43 100644 --- a/docs/provider/beyondtrust.md +++ b/docs/provider/beyondtrust.md @@ -22,13 +22,19 @@ BeyondTrust [OAuth Authentication](https://www.beyondtrust.com/docs/beyondinsigh 5. Add the user to the group 6. Add the Secrets Safe Feature to the group -> NOTE: The ClentID and ClientSecret must be stored in a Kubernetes secret in order for the SecretStore to read the configuration. +> NOTE: The ClientID and ClientSecret must be stored in a Kubernetes secret in order for the SecretStore to read the configuration. +If you're using client credentials authentication: ```sh kubectl create secret generic bt-secret --from-literal ClientSecret="" kubectl create secret generic bt-id --from-literal ClientId="" ``` +If you're using API Key authentication: +```sh +kubectl create secret generic bt-apikey --from-literal ApiKey="" +``` + ### Client Certificate If using `retrievalType: MANAGED_ACCOUNT`, you will also need to download the pfx certificate from Secrets Safe, extract that certificate and create two Kubernetes secrets. @@ -70,7 +76,7 @@ spec: beyondtrust: server: apiUrl: https://example.com:443/BeyondTrust/api/public/v3/ - retrievalType: MANAGED_ACCOUNT # or SECRET + retrievalType: MANAGED_ACCOUNT # or SECRET verifyCA: true clientTimeOutSeconds: 45 auth: @@ -82,17 +88,21 @@ spec: secretRef: name: bt-certificatekey key: ClientCertificateKey - clientSecret: + clientSecret: # define this section if using client credentials authentication secretRef: name: bt-secret key: ClientSecret - clientId: + clientId: # define this section if using client credentials authentication secretRef: name: bt-id key: ClientId + apiKey: # define this section if using Api Key authentication + secretRef: + name: bt-apikey + key: ApiKey ``` -### Creating a ExternalSecret +### Creating an ExternalSecret You can follow the below example to create a `ExternalSecret` resource. Secrets can be referenced by path. You can also use a `ClusterExternalSecret` allowing you to reference secrets from all namespaces. diff --git a/docs/snippets/beyondtrust-secret-store.yaml b/docs/snippets/beyondtrust-secret-store.yaml index 9b4deccf4..ea163a3ea 100644 --- a/docs/snippets/beyondtrust-secret-store.yaml +++ b/docs/snippets/beyondtrust-secret-store.yaml @@ -22,6 +22,10 @@ spec: secretRef: name: bt-id key: ClientId + apiKey: + secretRef: + name: bt-apikey + key: ApiKey server: retrievalType: MANAGED_ACCOUNT verifyCA: true diff --git a/pkg/provider/beyondtrust/provider.go b/pkg/provider/beyondtrust/provider.go index c8fe32fd1..a32fed024 100644 --- a/pkg/provider/beyondtrust/provider.go +++ b/pkg/provider/beyondtrust/provider.go @@ -65,6 +65,18 @@ type Provider struct { separator string } +type AuthenticatorInput struct { + Config *esv1beta1.BeyondtrustProvider + HTTPClientObj utils.HttpClientObj + BackoffDefinition *backoff.ExponentialBackOff + APIURL string + ClientID string + ClientSecret string + APIKey string + Logger *logging.LogrLogger + RetryMaxElapsedTimeMinutes int +} + // Capabilities implements v1beta1.Provider. func (*Provider) Capabilities() esv1beta1.SecretStoreCapabilities { return esv1beta1.SecretStoreReadOnly @@ -111,57 +123,30 @@ func (*Provider) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) { config := store.GetSpec().Provider.Beyondtrust logger := logging.NewLogrLogger(&ESOLogger) - apiURL := config.Server.APIURL - certificate := "" - certificateKey := "" - clientTimeOutInSeconds := 45 - retryMaxElapsedTimeMinutes := 15 - separator := "/" - if config.Server.Separator != "" { - separator = config.Server.Separator - } - - if config.Server.ClientTimeOutSeconds != 0 { - clientTimeOutInSeconds = config.Server.ClientTimeOutSeconds - } - - backoffDefinition := backoff.NewExponentialBackOff() - backoffDefinition.InitialInterval = 1 * time.Second - backoffDefinition.MaxElapsedTime = time.Duration(retryMaxElapsedTimeMinutes) * time.Second - backoffDefinition.RandomizationFactor = 0.5 - - clientID, err := loadConfigSecret(ctx, config.Auth.ClientID, kube, namespace) + clientID, clientSecret, apiKey, err := loadCredentialsFromConfig(ctx, config, kube, namespace) if err != nil { - return nil, fmt.Errorf("error loading clientID: %w", err) + return nil, fmt.Errorf("error loading credentials: %w", err) } - clientSecret, err := loadConfigSecret(ctx, config.Auth.ClientSecret, kube, namespace) + certificate, certificateKey, err := loadCertificateFromConfig(ctx, config, kube, namespace) if err != nil { - return nil, fmt.Errorf("error loading clientSecret: %w", err) + return nil, fmt.Errorf("error loading certificate: %w", err) } - if config.Auth.Certificate != nil && config.Auth.CertificateKey != nil { - loadedCertificate, err := loadConfigSecret(ctx, config.Auth.Certificate, kube, namespace) - if err != nil { - return nil, fmt.Errorf("error loading Certificate: %w", err) - } - - certificate = loadedCertificate - - loadedCertificateKey, err := loadConfigSecret(ctx, config.Auth.CertificateKey, kube, namespace) - if err != nil { - return nil, fmt.Errorf("error loading Certificate Key: %w", err) - } - - certificateKey = loadedCertificateKey + if err != nil { + return nil, fmt.Errorf("error loading secrets: %w", err) } - // Create an instance of ValidationParams + clientTimeOutInSeconds, separator, retryMaxElapsedTimeMinutes := getConfigValues(config) + + backoffDefinition := getBackoffDefinition(retryMaxElapsedTimeMinutes) + params := utils.ValidationParams{ + ApiKey: apiKey, ClientID: clientID, ClientSecret: clientSecret, - ApiUrl: &apiURL, + ApiUrl: &config.Server.APIURL, ClientTimeOutInSeconds: clientTimeOutInSeconds, Separator: &separator, VerifyCa: config.Server.VerifyCA, @@ -172,22 +157,33 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, MaxFileSecretSizeBytes: &maxFileSecretSizeBytes, } - errorsInInputs := utils.ValidateInputs(params) - - if errorsInInputs != nil { - return nil, fmt.Errorf("error in Inputs: %w", errorsInInputs) + if err := validateInputs(params); err != nil { + return nil, fmt.Errorf("error in Inputs: %w", err) } - // creating a http client - httpClientObj, err := utils.GetHttpClient(clientTimeOutInSeconds, config.Server.VerifyCA, certificate, certificateKey, logger) + httpClient, err := utils.GetHttpClient(clientTimeOutInSeconds, config.Server.VerifyCA, certificate, certificateKey, logger) + if err != nil { + return nil, fmt.Errorf("error creating HTTP client: %w", err) + } + + authenticatorInput := AuthenticatorInput{ + Config: config, + HTTPClientObj: *httpClient, + BackoffDefinition: backoffDefinition, + APIURL: config.Server.APIURL, + ClientID: clientID, + ClientSecret: clientSecret, + APIKey: apiKey, + Logger: logger, + RetryMaxElapsedTimeMinutes: retryMaxElapsedTimeMinutes, + } + + authenticate, err := getAuthenticator(authenticatorInput) if err != nil { - return nil, fmt.Errorf("error creating http client: %w", err) + return nil, fmt.Errorf("error authenticating: %w", err) } - // instantiating authenticate obj, injecting httpClient object - authenticate, _ := auth.Authenticate(*httpClientObj, backoffDefinition, apiURL, clientID, clientSecret, logger, retryMaxElapsedTimeMinutes) - return &Provider{ apiURL: config.Server.APIURL, retrievaltype: config.Server.RetrievalType, @@ -197,6 +193,86 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, }, nil } +func loadCredentialsFromConfig(ctx context.Context, config *esv1beta1.BeyondtrustProvider, kube client.Client, namespace string) (string, string, string, error) { + var clientID, clientSecret, apiKey string + var err error + + if config.Auth.APIKey != nil { + apiKey, err = loadConfigSecret(ctx, config.Auth.APIKey, kube, namespace) + if err != nil { + return "", "", "", fmt.Errorf("error loading apiKey: %w", err) + } + } else { + clientID, err = loadConfigSecret(ctx, config.Auth.ClientID, kube, namespace) + if err != nil { + return "", "", "", fmt.Errorf("error loading clientID: %w", err) + } + + clientSecret, err = loadConfigSecret(ctx, config.Auth.ClientSecret, kube, namespace) + if err != nil { + return "", "", "", fmt.Errorf("error loading clientSecret: %w", err) + } + } + + return clientID, clientSecret, apiKey, nil +} + +func loadCertificateFromConfig(ctx context.Context, config *esv1beta1.BeyondtrustProvider, kube client.Client, namespace string) (string, string, error) { + var certificate, certificateKey string + var err error + + if config.Auth.Certificate != nil && config.Auth.CertificateKey != nil { + certificate, err = loadConfigSecret(ctx, config.Auth.Certificate, kube, namespace) + if err != nil { + return "", "", fmt.Errorf("error loading Certificate: %w", err) + } + + certificateKey, err = loadConfigSecret(ctx, config.Auth.CertificateKey, kube, namespace) + if err != nil { + return "", "", fmt.Errorf("error loading Certificate Key: %w", err) + } + } + + return certificate, certificateKey, nil +} + +func getConfigValues(config *esv1beta1.BeyondtrustProvider) (int, string, int) { + clientTimeOutInSeconds := 45 + separator := "/" + retryMaxElapsedTimeMinutes := 15 + + if config.Server.ClientTimeOutSeconds != 0 { + clientTimeOutInSeconds = config.Server.ClientTimeOutSeconds + } + + if config.Server.Separator != "" { + separator = config.Server.Separator + } + + return clientTimeOutInSeconds, separator, retryMaxElapsedTimeMinutes +} + +func getBackoffDefinition(retryMaxElapsedTimeMinutes int) *backoff.ExponentialBackOff { + backoffDefinition := backoff.NewExponentialBackOff() + backoffDefinition.InitialInterval = 1 * time.Second + backoffDefinition.MaxElapsedTime = time.Duration(retryMaxElapsedTimeMinutes) * time.Minute + backoffDefinition.RandomizationFactor = 0.5 + + return backoffDefinition +} + +func validateInputs(params utils.ValidationParams) error { + return utils.ValidateInputs(params) +} + +func getAuthenticator(input AuthenticatorInput) (*auth.AuthenticationObj, error) { + if input.Config.Auth.APIKey != nil { + return auth.AuthenticateUsingApiKey(input.HTTPClientObj, input.BackoffDefinition, input.APIURL, input.Logger, input.RetryMaxElapsedTimeMinutes, input.APIKey) + } + + return auth.Authenticate(input.HTTPClientObj, input.BackoffDefinition, input.APIURL, input.ClientID, input.ClientSecret, input.Logger, input.RetryMaxElapsedTimeMinutes) +} + func loadConfigSecret(ctx context.Context, ref *esv1beta1.BeyondTrustProviderSecretRef, kube client.Client, defaultNamespace string) (string, error) { if ref.SecretRef == nil { return ref.Value, nil diff --git a/pkg/provider/beyondtrust/provider_test.go b/pkg/provider/beyondtrust/provider_test.go index 37bb6a4af..5ec5bda16 100644 --- a/pkg/provider/beyondtrust/provider_test.go +++ b/pkg/provider/beyondtrust/provider_test.go @@ -29,6 +29,7 @@ import ( const ( errTestCase = "Test case Failed" fakeAPIURL = "https://example.com:443/BeyondTrust/api/public/v3/" + apiKey = "fakeapikey00fakeapikeydd0000000000065b010f20fakeapikey0000000008700000a93fb5d74fddc0000000000000000000000000000000000000;runas=test_user" clientID = "12345678-25fg-4b05-9ced-35e7dd5093ae" clientSecret = "12345678-25fg-4b05-9ced-35e7dd5093ae" ) @@ -268,6 +269,63 @@ func TestNewClient(t *testing.T) { validateErrorText: true, expectedErrorText: "error in Inputs: Key: 'UserInputValidaton.ClientTimeOutinSeconds' Error:Field validation for 'ClientTimeOutinSeconds' failed on the 'lte' tag", }, + { + name: "ApiKey ok", + nameSpace: "test", + args: args{ + store: esv1beta1.SecretStore{ + Spec: esv1beta1.SecretStoreSpec{ + Provider: &esv1beta1.SecretStoreProvider{ + Beyondtrust: &esv1beta1.BeyondtrustProvider{ + Server: &esv1beta1.BeyondtrustServer{ + APIURL: fakeAPIURL, + RetrievalType: "SECRET", + }, + + Auth: &esv1beta1.BeyondtrustAuth{ + APIKey: &esv1beta1.BeyondTrustProviderSecretRef{ + Value: apiKey, + }, + }, + }, + }, + }, + }, + kube: createMockPasswordSafeClient(t), + provider: &Provider{}, + }, + validateErrorNil: true, + validateErrorText: false, + }, + { + name: "Bad ApiKey", + nameSpace: "test", + args: args{ + store: esv1beta1.SecretStore{ + Spec: esv1beta1.SecretStoreSpec{ + Provider: &esv1beta1.SecretStoreProvider{ + Beyondtrust: &esv1beta1.BeyondtrustProvider{ + Server: &esv1beta1.BeyondtrustServer{ + APIURL: fakeAPIURL, + RetrievalType: "SECRET", + }, + + Auth: &esv1beta1.BeyondtrustAuth{ + APIKey: &esv1beta1.BeyondTrustProviderSecretRef{ + Value: "bad_api_key", + }, + }, + }, + }, + }, + }, + kube: createMockPasswordSafeClient(t), + provider: &Provider{}, + }, + validateErrorNil: false, + validateErrorText: true, + expectedErrorText: "error in Inputs: Key: 'UserInputValidaton.ApiKey' Error:Field validation for 'ApiKey' failed on the 'min' tag", + }, } for _, tt := range tests {