provider.go 21 KB

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