provider.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. /*
  2. Copyright © 2025 ESO Maintainer Team
  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 beyondtrust provides a Password Safe secrets provider for External Secrets Operator.
  14. package beyondtrust
  15. import (
  16. "context"
  17. "encoding/json"
  18. "errors"
  19. "fmt"
  20. "net/url"
  21. "strings"
  22. "time"
  23. auth "github.com/BeyondTrust/go-client-library-passwordsafe/api/authentication"
  24. "github.com/BeyondTrust/go-client-library-passwordsafe/api/entities"
  25. v1 "k8s.io/api/core/v1"
  26. "github.com/BeyondTrust/go-client-library-passwordsafe/api/logging"
  27. managedaccount "github.com/BeyondTrust/go-client-library-passwordsafe/api/managed_account"
  28. "github.com/BeyondTrust/go-client-library-passwordsafe/api/secrets"
  29. "github.com/BeyondTrust/go-client-library-passwordsafe/api/utils"
  30. "github.com/cenkalti/backoff/v4"
  31. ctrl "sigs.k8s.io/controller-runtime"
  32. "sigs.k8s.io/controller-runtime/pkg/client"
  33. "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
  34. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  35. esutils "github.com/external-secrets/external-secrets/runtime/esutils"
  36. "github.com/external-secrets/external-secrets/runtime/esutils/resolvers"
  37. )
  38. const (
  39. errNilStore = "nil store found"
  40. errMissingStoreSpec = "store is missing spec"
  41. errMissingProvider = "storeSpec is missing provider"
  42. errInvalidProvider = "invalid provider spec. Missing field in store %s"
  43. errInvalidHostURL = "invalid host URL"
  44. errNoSuchKeyFmt = "no such key in secret: %q"
  45. errInvalidRetrievalPath = "invalid retrieval path. Provide one path, separator and name"
  46. errNotImplemented = "not implemented"
  47. usernameFieldName = "username"
  48. folderNameFieldName = "folder_name"
  49. fileNameFieldName = "file_name"
  50. titleFieldName = "title"
  51. descriptionFieldName = "description"
  52. ownerIDFieldName = "owner_id"
  53. groupIDFieldName = "group_id"
  54. ownerTypeFieldName = "owner_type"
  55. secretTypeFieldName = "secret_type"
  56. secretTypeCredential = "CREDENTIAL"
  57. )
  58. var (
  59. errSecretRefAndValueConflict = errors.New("cannot specify both secret reference and value")
  60. errMissingSecretName = errors.New("must specify a secret name")
  61. errMissingSecretKey = errors.New("must specify a secret key")
  62. // ESOLogger is the logger instance for the Beyondtrust provider.
  63. ESOLogger = ctrl.Log.WithName("provider").WithName("beyondtrust")
  64. maxFileSecretSizeBytes = 5000000
  65. )
  66. // Provider is a Password Safe secrets provider implementing NewClient and ValidateStore for the esv1.Provider interface.
  67. type Provider struct {
  68. apiURL string
  69. retrievaltype string
  70. authenticate auth.AuthenticationObj
  71. log logging.LogrLogger
  72. separator string
  73. }
  74. // AuthenticatorInput is used to pass parameters to the getAuthenticator function.
  75. type AuthenticatorInput struct {
  76. Config *esv1.BeyondtrustProvider
  77. HTTPClientObj utils.HttpClientObj
  78. BackoffDefinition *backoff.ExponentialBackOff
  79. APIURL string
  80. APIVersion string
  81. ClientID string
  82. ClientSecret string
  83. APIKey string
  84. Logger *logging.LogrLogger
  85. RetryMaxElapsedTimeMinutes int
  86. }
  87. // Capabilities implements v1beta1.Provider.
  88. func (*Provider) Capabilities() esv1.SecretStoreCapabilities {
  89. return esv1.SecretStoreReadWrite
  90. }
  91. // Close implements v1beta1.SecretsClient.
  92. func (*Provider) Close(_ context.Context) error {
  93. return nil
  94. }
  95. // DeleteSecret implements v1beta1.SecretsClient.
  96. func (*Provider) DeleteSecret(_ context.Context, _ esv1.PushSecretRemoteRef) error {
  97. return errors.New(errNotImplemented)
  98. }
  99. // GetSecretMap implements v1beta1.SecretsClient.
  100. func (*Provider) GetSecretMap(_ context.Context, _ esv1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  101. return make(map[string][]byte), errors.New(errNotImplemented)
  102. }
  103. // Validate implements v1beta1.SecretsClient.
  104. func (p *Provider) Validate() (esv1.ValidationResult, error) {
  105. timeout := 15 * time.Second
  106. clientURL := p.apiURL
  107. if err := esutils.NetworkValidate(clientURL, timeout); err != nil {
  108. ESOLogger.Error(err, "Network Validate", "clientURL:", clientURL)
  109. return esv1.ValidationResultError, err
  110. }
  111. return esv1.ValidationResultReady, nil
  112. }
  113. // SecretExists checks if a secret exists in the provider.
  114. func (p *Provider) SecretExists(_ context.Context, pushSecretRef esv1.PushSecretRemoteRef) (bool, error) {
  115. logger := logging.NewLogrLogger(&ESOLogger)
  116. secretObj, err := secrets.NewSecretObj(p.authenticate, logger, maxFileSecretSizeBytes)
  117. if err != nil {
  118. return false, err
  119. }
  120. _, err = secretObj.SearchSecretByTitleFlow(pushSecretRef.GetRemoteKey())
  121. if err == nil {
  122. return true, nil
  123. }
  124. return false, nil
  125. }
  126. // NewClient this is where we initialize the SecretClient and return it for the controller to use.
  127. func (p *Provider) NewClient(ctx context.Context, store esv1.GenericStore, kube client.Client, namespace string) (esv1.SecretsClient, error) {
  128. config := store.GetSpec().Provider.Beyondtrust
  129. logger := logging.NewLogrLogger(&ESOLogger)
  130. storeKind := store.GetKind()
  131. clientID, clientSecret, apiKey, err := loadCredentialsFromConfig(ctx, config, kube, namespace, storeKind)
  132. if err != nil {
  133. return nil, fmt.Errorf("error loading credentials: %w", err)
  134. }
  135. certificate, certificateKey, err := loadCertificateFromConfig(ctx, config, kube, namespace, storeKind)
  136. if err != nil {
  137. return nil, fmt.Errorf("error loading certificate: %w", err)
  138. }
  139. clientTimeOutInSeconds, separator, retryMaxElapsedTimeMinutes := getConfigValues(config)
  140. backoffDefinition := getBackoffDefinition(retryMaxElapsedTimeMinutes)
  141. params := utils.ValidationParams{
  142. ApiKey: apiKey,
  143. ClientID: clientID,
  144. ClientSecret: clientSecret,
  145. ApiUrl: &config.Server.APIURL,
  146. ApiVersion: config.Server.APIVersion,
  147. ClientTimeOutInSeconds: clientTimeOutInSeconds,
  148. Separator: &separator,
  149. VerifyCa: config.Server.VerifyCA,
  150. Logger: logger,
  151. Certificate: certificate,
  152. CertificateKey: certificateKey,
  153. RetryMaxElapsedTimeMinutes: &retryMaxElapsedTimeMinutes,
  154. MaxFileSecretSizeBytes: &maxFileSecretSizeBytes,
  155. }
  156. if err := validateInputs(params); err != nil {
  157. return nil, fmt.Errorf("error in Inputs: %w", err)
  158. }
  159. httpClient, err := utils.GetHttpClient(clientTimeOutInSeconds, config.Server.VerifyCA, certificate, certificateKey, logger)
  160. if err != nil {
  161. return nil, fmt.Errorf("error creating HTTP client: %w", err)
  162. }
  163. authenticatorInput := AuthenticatorInput{
  164. Config: config,
  165. HTTPClientObj: *httpClient,
  166. BackoffDefinition: backoffDefinition,
  167. APIURL: config.Server.APIURL,
  168. APIVersion: config.Server.APIVersion,
  169. ClientID: clientID,
  170. ClientSecret: clientSecret,
  171. APIKey: apiKey,
  172. Logger: logger,
  173. RetryMaxElapsedTimeMinutes: retryMaxElapsedTimeMinutes,
  174. }
  175. authenticate, err := getAuthenticator(authenticatorInput)
  176. if err != nil {
  177. return nil, fmt.Errorf("error authenticating: %w", err)
  178. }
  179. return &Provider{
  180. apiURL: config.Server.APIURL,
  181. retrievaltype: config.Server.RetrievalType,
  182. authenticate: *authenticate,
  183. log: *logger,
  184. separator: separator,
  185. }, nil
  186. }
  187. func loadCredentialsFromConfig(ctx context.Context, config *esv1.BeyondtrustProvider, kube client.Client, namespace, storeKind string) (string, string, string, error) {
  188. if config.Auth.APIKey != nil {
  189. apiKey, err := loadConfigSecret(ctx, config.Auth.APIKey, kube, namespace, storeKind)
  190. return "", "", apiKey, err
  191. }
  192. clientID, err := loadConfigSecret(ctx, config.Auth.ClientID, kube, namespace, storeKind)
  193. if err != nil {
  194. return "", "", "", fmt.Errorf("error loading clientID: %w", err)
  195. }
  196. clientSecret, err := loadConfigSecret(ctx, config.Auth.ClientSecret, kube, namespace, storeKind)
  197. if err != nil {
  198. return "", "", "", fmt.Errorf("error loading clientSecret: %w", err)
  199. }
  200. return clientID, clientSecret, "", nil
  201. }
  202. func loadCertificateFromConfig(ctx context.Context, config *esv1.BeyondtrustProvider, kube client.Client, namespace, storeKind string) (string, string, error) {
  203. if config.Auth.Certificate == nil || config.Auth.CertificateKey == nil {
  204. return "", "", nil
  205. }
  206. certificate, err := loadConfigSecret(ctx, config.Auth.Certificate, kube, namespace, storeKind)
  207. if err != nil {
  208. return "", "", fmt.Errorf("error loading Certificate: %w", err)
  209. }
  210. certificateKey, err := loadConfigSecret(ctx, config.Auth.CertificateKey, kube, namespace, storeKind)
  211. if err != nil {
  212. return "", "", fmt.Errorf("error loading Certificate Key: %w", err)
  213. }
  214. return certificate, certificateKey, nil
  215. }
  216. func getConfigValues(config *esv1.BeyondtrustProvider) (int, string, int) {
  217. clientTimeOutInSeconds := 45
  218. separator := "/"
  219. retryMaxElapsedTimeMinutes := 15
  220. if config.Server.ClientTimeOutSeconds != 0 {
  221. clientTimeOutInSeconds = config.Server.ClientTimeOutSeconds
  222. }
  223. if config.Server.Separator != "" {
  224. separator = config.Server.Separator
  225. }
  226. return clientTimeOutInSeconds, separator, retryMaxElapsedTimeMinutes
  227. }
  228. func getBackoffDefinition(retryMaxElapsedTimeMinutes int) *backoff.ExponentialBackOff {
  229. backoffDefinition := backoff.NewExponentialBackOff()
  230. backoffDefinition.InitialInterval = 1 * time.Second
  231. backoffDefinition.MaxElapsedTime = time.Duration(retryMaxElapsedTimeMinutes) * time.Minute
  232. backoffDefinition.RandomizationFactor = 0.5
  233. return backoffDefinition
  234. }
  235. func validateInputs(params utils.ValidationParams) error {
  236. return utils.ValidateInputs(params)
  237. }
  238. func getAuthenticator(input AuthenticatorInput) (*auth.AuthenticationObj, error) {
  239. parametersObj := auth.AuthenticationParametersObj{
  240. HTTPClient: input.HTTPClientObj,
  241. BackoffDefinition: input.BackoffDefinition,
  242. EndpointURL: input.APIURL,
  243. APIVersion: input.APIVersion,
  244. ApiKey: input.APIKey,
  245. Logger: input.Logger,
  246. RetryMaxElapsedTimeSeconds: input.RetryMaxElapsedTimeMinutes,
  247. }
  248. if input.Config.Auth.APIKey != nil {
  249. parametersObj.ApiKey = input.APIKey
  250. return auth.AuthenticateUsingApiKey(parametersObj)
  251. }
  252. parametersObj.ClientID = input.ClientID
  253. parametersObj.ClientSecret = input.ClientSecret
  254. return auth.Authenticate(parametersObj)
  255. }
  256. func loadConfigSecret(ctx context.Context, ref *esv1.BeyondTrustProviderSecretRef, kube client.Client, defaultNamespace, storeKind string) (string, error) {
  257. if ref.SecretRef == nil {
  258. return ref.Value, nil
  259. }
  260. if err := validateSecretRef(ref); err != nil {
  261. return "", err
  262. }
  263. return resolvers.SecretKeyRef(ctx, kube, storeKind, defaultNamespace, ref.SecretRef)
  264. }
  265. func validateSecretRef(ref *esv1.BeyondTrustProviderSecretRef) error {
  266. if ref.SecretRef != nil {
  267. if ref.Value != "" {
  268. return errSecretRefAndValueConflict
  269. }
  270. if ref.SecretRef.Name == "" {
  271. return errMissingSecretName
  272. }
  273. if ref.SecretRef.Key == "" {
  274. return errMissingSecretKey
  275. }
  276. }
  277. return nil
  278. }
  279. // GetAllSecrets retrieves all secrets from Beyondtrust.
  280. func (p *Provider) GetAllSecrets(_ context.Context, _ esv1.ExternalSecretFind) (map[string][]byte, error) {
  281. return nil, errors.New("GetAllSecrets not implemented")
  282. }
  283. // GetSecret reads the secret from the Password Safe server and returns it. The controller uses the value here to
  284. // create the Kubernetes secret.
  285. func (p *Provider) GetSecret(_ context.Context, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
  286. managedAccountType := !strings.EqualFold(p.retrievaltype, "SECRET")
  287. retrievalPaths := utils.ValidatePaths([]string{ref.Key}, managedAccountType, p.separator, &p.log)
  288. if len(retrievalPaths) != 1 {
  289. return nil, errors.New(errInvalidRetrievalPath)
  290. }
  291. retrievalPath := retrievalPaths[0]
  292. _, err := p.authenticate.GetPasswordSafeAuthentication()
  293. if err != nil {
  294. return nil, fmt.Errorf("error getting authentication: %w", err)
  295. }
  296. managedFetch := func() (string, error) {
  297. ESOLogger.Info("retrieve managed account value", "retrievalPath:", retrievalPath)
  298. manageAccountObj, _ := managedaccount.NewManagedAccountObj(p.authenticate, &p.log)
  299. return manageAccountObj.GetSecret(retrievalPath, p.separator)
  300. }
  301. unmanagedFetch := func() (string, error) {
  302. ESOLogger.Info("retrieve secrets safe value", "retrievalPath:", retrievalPath)
  303. secretObj, _ := secrets.NewSecretObj(p.authenticate, &p.log, maxFileSecretSizeBytes)
  304. return secretObj.GetSecret(retrievalPath, p.separator)
  305. }
  306. fetch := unmanagedFetch
  307. if managedAccountType {
  308. fetch = managedFetch
  309. }
  310. returnSecret, err := fetch()
  311. if err != nil {
  312. if serr := p.authenticate.SignOut(); serr != nil {
  313. return nil, errors.Join(err, serr)
  314. }
  315. return nil, fmt.Errorf("error getting secret/managed account: %w", err)
  316. }
  317. return []byte(returnSecret), nil
  318. }
  319. // ValidateStore validates the store configuration to prevent unexpected errors.
  320. func (p *Provider) ValidateStore(store esv1.GenericStore) (admission.Warnings, error) {
  321. if store == nil {
  322. return nil, errors.New(errNilStore)
  323. }
  324. spec := store.GetSpec()
  325. if spec == nil {
  326. return nil, errors.New(errMissingStoreSpec)
  327. }
  328. if spec.Provider == nil {
  329. return nil, errors.New(errMissingProvider)
  330. }
  331. provider := spec.Provider.Beyondtrust
  332. if provider == nil {
  333. return nil, fmt.Errorf(errInvalidProvider, store.GetObjectMeta().String())
  334. }
  335. apiURL, err := url.Parse(provider.Server.APIURL)
  336. if err != nil {
  337. return nil, errors.New(errInvalidHostURL)
  338. }
  339. if provider.Auth.ClientID.SecretRef != nil {
  340. return nil, err
  341. }
  342. if provider.Auth.ClientSecret.SecretRef != nil {
  343. return nil, err
  344. }
  345. if apiURL.Host == "" {
  346. return nil, errors.New(errInvalidHostURL)
  347. }
  348. return nil, nil
  349. }
  350. // NewProvider creates a new Provider instance.
  351. func NewProvider() esv1.Provider {
  352. return &Provider{}
  353. }
  354. // ProviderSpec returns the provider specification for registration.
  355. func ProviderSpec() *esv1.SecretStoreProvider {
  356. return &esv1.SecretStoreProvider{
  357. Beyondtrust: &esv1.BeyondtrustProvider{},
  358. }
  359. }
  360. // MaintenanceStatus returns the maintenance status of the provider.
  361. func MaintenanceStatus() esv1.MaintenanceStatus {
  362. return esv1.MaintenanceStatusMaintained
  363. }
  364. // PushSecret implements v1beta1.SecretsClient.
  365. func (p *Provider) PushSecret(_ context.Context, secret *v1.Secret, psd esv1.PushSecretData) error {
  366. ESOLogger.Info("Pushing secret to BeyondTrust Password Safe")
  367. value, err := esutils.ExtractSecretData(psd, secret)
  368. if err != nil {
  369. return fmt.Errorf("extract secret data failed: %w", err)
  370. }
  371. secretValue := string(value)
  372. metadata := psd.GetMetadata()
  373. data, err := json.Marshal(metadata)
  374. if err != nil {
  375. return fmt.Errorf("Error getting metadata: %w", err)
  376. }
  377. var metaDataObject map[string]interface{}
  378. err = json.Unmarshal(data, &metaDataObject)
  379. if err != nil {
  380. return fmt.Errorf("Error in parameters: %w", err)
  381. }
  382. signAppinResponse, err := p.authenticate.GetPasswordSafeAuthentication()
  383. if err != nil {
  384. return fmt.Errorf("Error in authentication: %w", err)
  385. }
  386. err = p.CreateSecret(secretValue, metaDataObject, signAppinResponse)
  387. if err != nil {
  388. return fmt.Errorf("Error in creating the secret: %w", err)
  389. }
  390. return nil
  391. }
  392. // CreateSecret creates a secret in BeyondTrust Password Safe.
  393. func (p *Provider) CreateSecret(secret string, data map[string]interface{}, signAppinResponse entities.SignAppinResponse) error {
  394. logger := logging.NewLogrLogger(&ESOLogger)
  395. secretObj, err := secrets.NewSecretObj(p.authenticate, logger, maxFileSecretSizeBytes)
  396. if err != nil {
  397. return err
  398. }
  399. username := utils.GetStringField(data, usernameFieldName, "")
  400. folderName := utils.GetStringField(data, folderNameFieldName, "")
  401. fileName := utils.GetStringField(data, fileNameFieldName, "")
  402. title := utils.GetStringField(data, titleFieldName, "")
  403. description := utils.GetStringField(data, descriptionFieldName, "")
  404. ownerID := utils.GetIntField(data, ownerIDFieldName, 0)
  405. groupID := utils.GetIntField(data, groupIDFieldName, 0)
  406. ownerType := utils.GetStringField(data, ownerTypeFieldName, "")
  407. secretType := utils.GetStringField(data, secretTypeFieldName, secretTypeCredential)
  408. var notes string
  409. var urls []entities.UrlDetails
  410. var ownerDetailsOwnerID []entities.OwnerDetailsOwnerId
  411. var ownerDetailsGroupID []entities.OwnerDetailsGroupId
  412. _, ok := data["notes"]
  413. if ok {
  414. notes = data["notes"].(string)
  415. }
  416. _, ok = data["urls"]
  417. if ok {
  418. urls = utils.GetUrlsDetailsList(data)
  419. }
  420. ownerDetailsOwnerID = utils.GetOwnerDetailsOwnerIdList(data, signAppinResponse)
  421. ownerDetailsGroupID = utils.GetOwnerDetailsGroupIdList(data, groupID, signAppinResponse)
  422. secretDetailsConfig := entities.SecretDetailsBaseConfig{
  423. Title: title,
  424. Description: description,
  425. Urls: urls,
  426. Notes: notes,
  427. }
  428. var configMap map[string]interface{}
  429. switch strings.ToUpper(secretType) {
  430. case "CREDENTIAL":
  431. secretCredentialDetailsConfig30 := entities.SecretCredentialDetailsConfig30{
  432. SecretDetailsBaseConfig: secretDetailsConfig,
  433. Username: username,
  434. Password: secret,
  435. OwnerId: ownerID,
  436. OwnerType: ownerType,
  437. Owners: ownerDetailsOwnerID,
  438. }
  439. secretCredentialDetailsConfig31 := entities.SecretCredentialDetailsConfig31{
  440. SecretDetailsBaseConfig: secretDetailsConfig,
  441. Username: username,
  442. Password: secret,
  443. Owners: ownerDetailsGroupID,
  444. }
  445. configMap = map[string]interface{}{
  446. "3.0": secretCredentialDetailsConfig30,
  447. "3.1": secretCredentialDetailsConfig31,
  448. }
  449. case "FILE":
  450. secretFileDetailsConfig30 := entities.SecretFileDetailsConfig30{
  451. SecretDetailsBaseConfig: secretDetailsConfig,
  452. FileContent: secret,
  453. FileName: fileName,
  454. OwnerId: ownerID,
  455. OwnerType: ownerType,
  456. Owners: ownerDetailsOwnerID,
  457. }
  458. secretFileDetailsConfig31 := entities.SecretFileDetailsConfig31{
  459. SecretDetailsBaseConfig: secretDetailsConfig,
  460. FileContent: secret,
  461. FileName: fileName,
  462. Owners: ownerDetailsGroupID,
  463. }
  464. configMap = map[string]interface{}{
  465. "3.0": secretFileDetailsConfig30,
  466. "3.1": secretFileDetailsConfig31,
  467. }
  468. case "TEXT":
  469. secretTextDetailsConfig30 := entities.SecretTextDetailsConfig30{
  470. SecretDetailsBaseConfig: secretDetailsConfig,
  471. Text: secret,
  472. OwnerId: ownerID,
  473. OwnerType: ownerType,
  474. Owners: ownerDetailsOwnerID,
  475. }
  476. secretTextDetailsConfig31 := entities.SecretTextDetailsConfig31{
  477. SecretDetailsBaseConfig: secretDetailsConfig,
  478. Text: secret,
  479. Owners: ownerDetailsGroupID,
  480. }
  481. configMap = map[string]interface{}{
  482. "3.0": secretTextDetailsConfig30,
  483. "3.1": secretTextDetailsConfig31,
  484. }
  485. default:
  486. return fmt.Errorf("Unknown secret type")
  487. }
  488. secretDetails, exists := configMap[p.authenticate.ApiVersion]
  489. if !exists {
  490. return fmt.Errorf("unsupported API version: %v", &p.authenticate.ApiVersion)
  491. }
  492. _, err = secretObj.CreateSecretFlow(folderName, secretDetails)
  493. if err != nil {
  494. return err
  495. }
  496. ESOLogger.Info("Secret pushed to BeyondTrust Password Safe")
  497. return nil
  498. }