From e57cb3c8360aa432b848fed904ca0e7b6c962cd8 Mon Sep 17 00:00:00 2001 From: Erik Ostien Date: Fri, 13 Dec 2024 12:09:05 -0700 Subject: [PATCH] PDI-1814: Add PingFederate Resource Exports - Add pingfederate_idp_sts_request_parameters_contract export - Add pingfederate_identity_store_provisioner export - Add pingfederate_configuration_encryption_keys_rotate export - Various typos and fixes --- .../pingfederate/pingfederate_connector.go | 3 + .../pingfederate_connector_test.go | 15 +++ ...federate_authentication_api_application.go | 6 +- ...derate_authentication_policies_fragment.go | 6 +- ...federate_authentication_policy_contract.go | 6 +- .../pingfederate_authentication_selector.go | 6 +- .../pingfederate_captcha_provider.go | 6 +- .../resources/pingfederate_certificate_ca.go | 6 +- ...te_configuration_encryption_keys_rotate.go | 53 +++++++++++ ...nfiguration_encryption_keys_rotate_test.go | 26 ++++++ .../resources/pingfederate_data_store.go | 6 +- ...pingfederate_identity_store_provisioner.go | 88 ++++++++++++++++++ ...ederate_identity_store_provisioner_test.go | 25 +++++ .../resources/pingfederate_idp_adapter.go | 6 +- .../pingfederate_idp_sp_connection.go | 6 +- ...ate_idp_sts_request_parameters_contract.go | 88 ++++++++++++++++++ ...dp_sts_request_parameters_contract_test.go | 25 +++++ .../resources/pingfederate_kerberos_realm.go | 10 +- .../pingfederate_local_identity_profile.go | 10 +- ...pingfederate_oauth_access_token_manager.go | 10 +- ...pingfederate_oauth_access_token_mapping.go | 4 +- .../resources/pingfederate_oauth_client.go | 10 +- .../resources/pingfederate_oauth_issuer.go | 6 +- .../pingfederate_openid_connect_policy.go | 10 +- ...gfederate_password_credential_validator.go | 10 +- .../pingfederate_pingone_connection.go | 6 +- ..._authentication_policy_contract_mapping.go | 6 +- server-profiles/12.1/data.json.subst | 54 +++++++++++ ...ty-store-provisioner-12.2.0.4-SNAPSHOT.jar | Bin 0 -> 13333 bytes 29 files changed, 445 insertions(+), 68 deletions(-) create mode 100644 internal/connector/pingfederate/resources/pingfederate_configuration_encryption_keys_rotate.go create mode 100644 internal/connector/pingfederate/resources/pingfederate_configuration_encryption_keys_rotate_test.go create mode 100644 internal/connector/pingfederate/resources/pingfederate_identity_store_provisioner.go create mode 100644 internal/connector/pingfederate/resources/pingfederate_identity_store_provisioner_test.go create mode 100644 internal/connector/pingfederate/resources/pingfederate_idp_sts_request_parameters_contract.go create mode 100644 internal/connector/pingfederate/resources/pingfederate_idp_sts_request_parameters_contract_test.go create mode 100644 server-profiles/shared-profile/instance/server/default/deploy/pf-sdk-example-identity-store-provisioner-12.2.0.4-SNAPSHOT.jar diff --git a/internal/connector/pingfederate/pingfederate_connector.go b/internal/connector/pingfederate/pingfederate_connector.go index c55901f4..fdcf058d 100644 --- a/internal/connector/pingfederate/pingfederate_connector.go +++ b/internal/connector/pingfederate/pingfederate_connector.go @@ -53,11 +53,14 @@ func (c *PingFederateConnector) Export(format, outputDir string, overwriteExport resources.CertificatesRevocationOCSPCertificate(&c.clientInfo), resources.CertificatesRevocationSettings(&c.clientInfo), resources.ClusterSettings(&c.clientInfo), + resources.ConfigurationEncryptionKeysRotate(&c.clientInfo), resources.DataStore(&c.clientInfo), resources.DefaultURLs(&c.clientInfo), resources.ExtendedProperties(&c.clientInfo), + resources.IdentityStoreProvisioner(&c.clientInfo), resources.IDPAdapter(&c.clientInfo), resources.IDPSPConnection(&c.clientInfo), + resources.IdpStsRequestParametersContract(&c.clientInfo), resources.IncomingProxySettings(&c.clientInfo), resources.KerberosRealm(&c.clientInfo), resources.LocalIdentityProfile(&c.clientInfo), diff --git a/internal/connector/pingfederate/pingfederate_connector_test.go b/internal/connector/pingfederate/pingfederate_connector_test.go index 25f0a3da..17dd5f8e 100644 --- a/internal/connector/pingfederate/pingfederate_connector_test.go +++ b/internal/connector/pingfederate/pingfederate_connector_test.go @@ -90,6 +90,11 @@ func TestPingFederateTerraformPlan(t *testing.T) { "Error: PingFederate API error", }, }, + { + name: "PingFederateConfigurationEncryptionKeysRotate", + resource: resources.ConfigurationEncryptionKeysRotate(PingFederateClientInfo), + ignoredErrors: nil, + }, { name: "PingFederateDataStore", resource: resources.DataStore(PingFederateClientInfo), @@ -105,6 +110,11 @@ func TestPingFederateTerraformPlan(t *testing.T) { resource: resources.ExtendedProperties(PingFederateClientInfo), ignoredErrors: nil, }, + { + name: "PingFederateIdentityStoreProvisioner", + resource: resources.IdentityStoreProvisioner(PingFederateClientInfo), + ignoredErrors: nil, + }, { name: "PingFederateIDPAdapter", resource: resources.IDPAdapter(PingFederateClientInfo), @@ -115,6 +125,11 @@ func TestPingFederateTerraformPlan(t *testing.T) { resource: resources.IDPSPConnection(PingFederateClientInfo), ignoredErrors: nil, }, + { + name: "PingFederateIdpStsRequestParametersContract", + resource: resources.IdpStsRequestParametersContract(PingFederateClientInfo), + ignoredErrors: nil, + }, { name: "PingFederateIncomingProxySettings", resource: resources.IncomingProxySettings(PingFederateClientInfo), diff --git a/internal/connector/pingfederate/resources/pingfederate_authentication_api_application.go b/internal/connector/pingfederate/resources/pingfederate_authentication_api_application.go index b8555db6..49ab9b5e 100644 --- a/internal/connector/pingfederate/resources/pingfederate_authentication_api_application.go +++ b/internal/connector/pingfederate/resources/pingfederate_authentication_api_application.go @@ -39,9 +39,9 @@ func (r *PingFederateAuthenticationApiApplicationResource) ExportAll() (*[]conne for appId, appName := range *applicationData { commentData := map[string]string{ - "Authentication API Application Resource ID": appId, - "Authentication API Application Resource Name": appName, - "Resource Type": r.ResourceType(), + "Authentication API Application ID": appId, + "Authentication API Application Name": appName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ diff --git a/internal/connector/pingfederate/resources/pingfederate_authentication_policies_fragment.go b/internal/connector/pingfederate/resources/pingfederate_authentication_policies_fragment.go index bb59aa65..89ab1416 100644 --- a/internal/connector/pingfederate/resources/pingfederate_authentication_policies_fragment.go +++ b/internal/connector/pingfederate/resources/pingfederate_authentication_policies_fragment.go @@ -39,9 +39,9 @@ func (r *PingFederateAuthenticationPoliciesFragmentResource) ExportAll() (*[]con for fragmentId, fragmentName := range *fragmentData { commentData := map[string]string{ - "Authentication Policies Fragment Resource ID": fragmentId, - "Authentication Policies Fragment Resource Name": fragmentName, - "Resource Type": r.ResourceType(), + "Authentication Policies Fragment ID": fragmentId, + "Authentication Policies Fragment Name": fragmentName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ diff --git a/internal/connector/pingfederate/resources/pingfederate_authentication_policy_contract.go b/internal/connector/pingfederate/resources/pingfederate_authentication_policy_contract.go index 63fc547f..819f0f08 100644 --- a/internal/connector/pingfederate/resources/pingfederate_authentication_policy_contract.go +++ b/internal/connector/pingfederate/resources/pingfederate_authentication_policy_contract.go @@ -39,9 +39,9 @@ func (r *PingFederateAuthenticationPolicyContractResource) ExportAll() (*[]conne for authnPolicyContractId, authnPolicyContractName := range *authenticationPolicyContractData { commentData := map[string]string{ - "Authentication Policy Contract Resource ID": authnPolicyContractId, - "Authentication Policy Contract Resource Name": authnPolicyContractName, - "Resource Type": r.ResourceType(), + "Authentication Policy Contract ID": authnPolicyContractId, + "Authentication Policy Contract Name": authnPolicyContractName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ diff --git a/internal/connector/pingfederate/resources/pingfederate_authentication_selector.go b/internal/connector/pingfederate/resources/pingfederate_authentication_selector.go index 8c135e1e..9fb02cd5 100644 --- a/internal/connector/pingfederate/resources/pingfederate_authentication_selector.go +++ b/internal/connector/pingfederate/resources/pingfederate_authentication_selector.go @@ -39,9 +39,9 @@ func (r *PingFederateAuthenticationSelectorResource) ExportAll() (*[]connector.I for authnSelectorId, authnSelectorName := range *authenticationSelectorData { commentData := map[string]string{ - "Authentication Selector Resource ID": authnSelectorId, - "Authentication Selector Resource Name": authnSelectorName, - "Resource Type": r.ResourceType(), + "Authentication Selector ID": authnSelectorId, + "Authentication Selector Name": authnSelectorName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ diff --git a/internal/connector/pingfederate/resources/pingfederate_captcha_provider.go b/internal/connector/pingfederate/resources/pingfederate_captcha_provider.go index 3774e56d..2c355d63 100644 --- a/internal/connector/pingfederate/resources/pingfederate_captcha_provider.go +++ b/internal/connector/pingfederate/resources/pingfederate_captcha_provider.go @@ -39,9 +39,9 @@ func (r *PingFederateCaptchaProviderResource) ExportAll() (*[]connector.ImportBl for captchaProviderId, captchaProviderName := range *captchaProviderData { commentData := map[string]string{ - "Captcha Provider Resource ID": captchaProviderId, - "Captcha Provider Resource Name": captchaProviderId, - "Resource Type": r.ResourceType(), + "Captcha Provider ID": captchaProviderId, + "Captcha Provider Name": captchaProviderId, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ diff --git a/internal/connector/pingfederate/resources/pingfederate_certificate_ca.go b/internal/connector/pingfederate/resources/pingfederate_certificate_ca.go index 16280cb1..27c6329b 100644 --- a/internal/connector/pingfederate/resources/pingfederate_certificate_ca.go +++ b/internal/connector/pingfederate/resources/pingfederate_certificate_ca.go @@ -45,7 +45,7 @@ func (r *PingFederateCertificateCAResource) ExportAll() (*[]connector.ImportBloc commentData := map[string]string{ "Certificate CA Issuer DN": certViewIssuerDN, - "Certificate CA Resource ID": certViewId, + "Certificate CA ID": certViewId, "Certificate CA Serial Number": certViewSerialNumber, "Resource Type": r.ResourceType(), } @@ -76,8 +76,8 @@ func (r *PingFederateCertificateCAResource) getTrustedCAData() (*map[string][]st return nil, common.DataNilError(r.ResourceType(), response) } - certViewsItems, ok := certViews.GetItemsOk() - if !ok { + certViewsItems, certViewsItemsOk := certViews.GetItemsOk() + if !certViewsItemsOk { return nil, common.DataNilError(r.ResourceType(), response) } diff --git a/internal/connector/pingfederate/resources/pingfederate_configuration_encryption_keys_rotate.go b/internal/connector/pingfederate/resources/pingfederate_configuration_encryption_keys_rotate.go new file mode 100644 index 00000000..af038dfd --- /dev/null +++ b/internal/connector/pingfederate/resources/pingfederate_configuration_encryption_keys_rotate.go @@ -0,0 +1,53 @@ +package resources + +import ( + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/common" + "github.com/pingidentity/pingcli/internal/logger" +) + +// Verify that the resource satisfies the exportable resource interface +var ( + _ connector.ExportableResource = &PingFederateConfigurationEncryptionKeysRotateResource{} +) + +type PingFederateConfigurationEncryptionKeysRotateResource struct { + clientInfo *connector.PingFederateClientInfo +} + +// Utility method for creating a PingFederateConfigurationEncryptionKeysRotateResource +func ConfigurationEncryptionKeysRotate(clientInfo *connector.PingFederateClientInfo) *PingFederateConfigurationEncryptionKeysRotateResource { + return &PingFederateConfigurationEncryptionKeysRotateResource{ + clientInfo: clientInfo, + } +} + +func (r *PingFederateConfigurationEncryptionKeysRotateResource) ResourceType() string { + return "pingfederate_configuration_encryption_keys_rotate" +} + +func (r *PingFederateConfigurationEncryptionKeysRotateResource) ExportAll() (*[]connector.ImportBlock, error) { + l := logger.Get() + l.Debug().Msgf("Exporting all '%s' Resources...", r.ResourceType()) + + importBlocks := []connector.ImportBlock{} + + configurationEncryptionKeysRotateId := "configuration_encryption_keys_rotate_singleton_id" + configurationEncryptionKeysRotateName := "Configuration Encryption Keys Rotate" + + commentData := map[string]string{ + "Resource Type": r.ResourceType(), + "Singleton ID": common.SINGLETON_ID_COMMENT_DATA, + } + + importBlock := connector.ImportBlock{ + ResourceType: r.ResourceType(), + ResourceName: configurationEncryptionKeysRotateName, + ResourceID: configurationEncryptionKeysRotateId, + CommentInformation: common.GenerateCommentInformation(commentData), + } + + importBlocks = append(importBlocks, importBlock) + + return &importBlocks, nil +} diff --git a/internal/connector/pingfederate/resources/pingfederate_configuration_encryption_keys_rotate_test.go b/internal/connector/pingfederate/resources/pingfederate_configuration_encryption_keys_rotate_test.go new file mode 100644 index 00000000..67e0a389 --- /dev/null +++ b/internal/connector/pingfederate/resources/pingfederate_configuration_encryption_keys_rotate_test.go @@ -0,0 +1,26 @@ +package resources_test + +import ( + "testing" + + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/pingfederate/resources" + "github.com/pingidentity/pingcli/internal/testing/testutils" +) + +func TestPingFederateConfigurationEncryptionKeysRotateExport(t *testing.T) { + // Get initialized apiClient and resource + PingFederateClientInfo := testutils.GetPingFederateClientInfo(t) + resource := resources.ConfigurationEncryptionKeysRotate(PingFederateClientInfo) + + // Defined the expected ImportBlocks for the resource + expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingfederate_configuration_encryption_keys_rotate", + ResourceName: "Configuration Encryption Keys Rotate", + ResourceID: "configuration_encryption_keys_rotate_singleton_id", + }, + } + + testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks) +} diff --git a/internal/connector/pingfederate/resources/pingfederate_data_store.go b/internal/connector/pingfederate/resources/pingfederate_data_store.go index ed3e0eec..2de8e196 100644 --- a/internal/connector/pingfederate/resources/pingfederate_data_store.go +++ b/internal/connector/pingfederate/resources/pingfederate_data_store.go @@ -41,9 +41,9 @@ func (r *PingFederateDataStoreResource) ExportAll() (*[]connector.ImportBlock, e for dataStoreId, dataStoreType := range *dataStoreData { commentData := map[string]string{ - "Data Store Resource ID": dataStoreId, - "Data Store Type": dataStoreType, - "Resource Type": r.ResourceType(), + "Data Store ID": dataStoreId, + "Data Store Type": dataStoreType, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ diff --git a/internal/connector/pingfederate/resources/pingfederate_identity_store_provisioner.go b/internal/connector/pingfederate/resources/pingfederate_identity_store_provisioner.go new file mode 100644 index 00000000..0552db4e --- /dev/null +++ b/internal/connector/pingfederate/resources/pingfederate_identity_store_provisioner.go @@ -0,0 +1,88 @@ +package resources + +import ( + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/common" + "github.com/pingidentity/pingcli/internal/logger" +) + +// Verify that the resource satisfies the exportable resource interface +var ( + _ connector.ExportableResource = &PingFederateIdentityStoreProvisionerResource{} +) + +type PingFederateIdentityStoreProvisionerResource struct { + clientInfo *connector.PingFederateClientInfo +} + +// Utility method for creating a PingFederateIdentityStoreProvisionerResource +func IdentityStoreProvisioner(clientInfo *connector.PingFederateClientInfo) *PingFederateIdentityStoreProvisionerResource { + return &PingFederateIdentityStoreProvisionerResource{ + clientInfo: clientInfo, + } +} + +func (r *PingFederateIdentityStoreProvisionerResource) ResourceType() string { + return "pingfederate_identity_store_provisioner" +} + +func (r *PingFederateIdentityStoreProvisionerResource) ExportAll() (*[]connector.ImportBlock, error) { + l := logger.Get() + l.Debug().Msgf("Exporting all '%s' Resources...", r.ResourceType()) + + importBlocks := []connector.ImportBlock{} + + identityStoreProvisionerData, err := r.getIdentityStoreProvisionerData() + if err != nil { + return nil, err + } + + for identityStoreProvisionerId, identityStoreProvisionerName := range *identityStoreProvisionerData { + commentData := map[string]string{ + "Identity Store Provisioner ID": identityStoreProvisionerId, + "Identity Store Provisioner Name": identityStoreProvisionerName, + "Resource Type": r.ResourceType(), + } + + importBlock := connector.ImportBlock{ + ResourceType: r.ResourceType(), + ResourceName: identityStoreProvisionerName, + ResourceID: identityStoreProvisionerId, + CommentInformation: common.GenerateCommentInformation(commentData), + } + + importBlocks = append(importBlocks, importBlock) + } + + return &importBlocks, nil +} + +func (r *PingFederateIdentityStoreProvisionerResource) getIdentityStoreProvisionerData() (*map[string]string, error) { + identityStoreProvisionerData := make(map[string]string) + + identityStoreProvisioners, response, err := r.clientInfo.ApiClient.IdentityStoreProvisionersAPI.GetIdentityStoreProvisioners(r.clientInfo.Context).Execute() + err = common.HandleClientResponse(response, err, "GetIdentityStoreProvisioners", r.ResourceType()) + if err != nil { + return nil, err + } + + if identityStoreProvisioners == nil { + return nil, common.DataNilError(r.ResourceType(), response) + } + + identityStoreProvisionersItems, identityStoreProvisionersItemsOk := identityStoreProvisioners.GetItemsOk() + if !identityStoreProvisionersItemsOk { + return nil, common.DataNilError(r.ResourceType(), response) + } + + for _, identityStoreProvisioner := range identityStoreProvisionersItems { + identityStoreProvisionerId, identityStoreProvisionerIdOk := identityStoreProvisioner.GetIdOk() + identityStoreProvisionerName, identityStoreProvisionerNameOk := identityStoreProvisioner.GetNameOk() + + if identityStoreProvisionerIdOk && identityStoreProvisionerNameOk { + identityStoreProvisionerData[*identityStoreProvisionerId] = *identityStoreProvisionerName + } + } + + return &identityStoreProvisionerData, nil +} diff --git a/internal/connector/pingfederate/resources/pingfederate_identity_store_provisioner_test.go b/internal/connector/pingfederate/resources/pingfederate_identity_store_provisioner_test.go new file mode 100644 index 00000000..362420d5 --- /dev/null +++ b/internal/connector/pingfederate/resources/pingfederate_identity_store_provisioner_test.go @@ -0,0 +1,25 @@ +package resources_test + +import ( + "testing" + + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/pingfederate/resources" + "github.com/pingidentity/pingcli/internal/testing/testutils" +) + +func TestPingFederateIdentityStoreProvisionerExport(t *testing.T) { + // Get initialized apiClient and resource + PingFederateClientInfo := testutils.GetPingFederateClientInfo(t) + resource := resources.IdentityStoreProvisioner(PingFederateClientInfo) + + // Defined the expected ImportBlocks for the resource + expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingfederate_identity_store_provisioner", + ResourceName: "ISP TestName", + ResourceID: "ISPTestID", + }, + } + testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks) +} diff --git a/internal/connector/pingfederate/resources/pingfederate_idp_adapter.go b/internal/connector/pingfederate/resources/pingfederate_idp_adapter.go index 89d984b1..92799094 100644 --- a/internal/connector/pingfederate/resources/pingfederate_idp_adapter.go +++ b/internal/connector/pingfederate/resources/pingfederate_idp_adapter.go @@ -39,9 +39,9 @@ func (r *PingFederateIDPAdapterResource) ExportAll() (*[]connector.ImportBlock, for idpAdapterId, idpAdapterName := range *idpAdapterData { commentData := map[string]string{ - "IDP Adapter Resource ID": idpAdapterId, - "IDP Adapter Resource Name": idpAdapterName, - "Resource Type": r.ResourceType(), + "IDP Adapter ID": idpAdapterId, + "IDP Adapter Name": idpAdapterName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ diff --git a/internal/connector/pingfederate/resources/pingfederate_idp_sp_connection.go b/internal/connector/pingfederate/resources/pingfederate_idp_sp_connection.go index 145a56b9..eca20af7 100644 --- a/internal/connector/pingfederate/resources/pingfederate_idp_sp_connection.go +++ b/internal/connector/pingfederate/resources/pingfederate_idp_sp_connection.go @@ -39,9 +39,9 @@ func (r *PingFederateIDPSPConnectionResource) ExportAll() (*[]connector.ImportBl for spConnectionId, spConnectionName := range *spConnectionData { commentData := map[string]string{ - "IDP SP Connection Resource ID": spConnectionId, - "IDP SP Connection Resource Name": spConnectionName, - "Resource Type": r.ResourceType(), + "IDP SP Connection ID": spConnectionId, + "IDP SP Connection Name": spConnectionName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ diff --git a/internal/connector/pingfederate/resources/pingfederate_idp_sts_request_parameters_contract.go b/internal/connector/pingfederate/resources/pingfederate_idp_sts_request_parameters_contract.go new file mode 100644 index 00000000..512c8d97 --- /dev/null +++ b/internal/connector/pingfederate/resources/pingfederate_idp_sts_request_parameters_contract.go @@ -0,0 +1,88 @@ +package resources + +import ( + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/common" + "github.com/pingidentity/pingcli/internal/logger" +) + +// Verify that the resource satisfies the exportable resource interface +var ( + _ connector.ExportableResource = &PingFederateIdpStsRequestParametersContractResource{} +) + +type PingFederateIdpStsRequestParametersContractResource struct { + clientInfo *connector.PingFederateClientInfo +} + +// Utility method for creating a PingFederateIdpStsRequestParametersContractResource +func IdpStsRequestParametersContract(clientInfo *connector.PingFederateClientInfo) *PingFederateIdpStsRequestParametersContractResource { + return &PingFederateIdpStsRequestParametersContractResource{ + clientInfo: clientInfo, + } +} + +func (r *PingFederateIdpStsRequestParametersContractResource) ResourceType() string { + return "pingfederate_idp_sts_request_parameters_contract" +} + +func (r *PingFederateIdpStsRequestParametersContractResource) ExportAll() (*[]connector.ImportBlock, error) { + l := logger.Get() + l.Debug().Msgf("Exporting all '%s' Resources...", r.ResourceType()) + + importBlocks := []connector.ImportBlock{} + + stsRequestParamContractData, err := r.getStsRequestParamContractData() + if err != nil { + return nil, err + } + + for stsRequestParamContractId, stsRequestParamContractName := range *stsRequestParamContractData { + commentData := map[string]string{ + "IDP STS Request Parameters Contract ID": stsRequestParamContractId, + "IDP STS Request Parameters Contract Name": stsRequestParamContractName, + "Resource Type": r.ResourceType(), + } + + importBlock := connector.ImportBlock{ + ResourceType: r.ResourceType(), + ResourceName: stsRequestParamContractName, + ResourceID: stsRequestParamContractId, + CommentInformation: common.GenerateCommentInformation(commentData), + } + + importBlocks = append(importBlocks, importBlock) + } + + return &importBlocks, nil +} + +func (r *PingFederateIdpStsRequestParametersContractResource) getStsRequestParamContractData() (*map[string]string, error) { + stsRequestParamContractData := make(map[string]string) + + stsRequestParamContracts, response, err := r.clientInfo.ApiClient.IdpStsRequestParametersContractsAPI.GetStsRequestParamContracts(r.clientInfo.Context).Execute() + err = common.HandleClientResponse(response, err, "GetStsRequestParamContracts", r.ResourceType()) + if err != nil { + return nil, err + } + + if stsRequestParamContracts == nil { + return nil, common.DataNilError(r.ResourceType(), response) + } + + stsRequestParamContractsItems, stsRequestParamContractsOk := stsRequestParamContracts.GetItemsOk() + if !stsRequestParamContractsOk { + return nil, common.DataNilError(r.ResourceType(), response) + } + + for _, stsRequestParamContract := range stsRequestParamContractsItems { + stsRequestParamContractId, stsRequestParamContractIdOk := stsRequestParamContract.GetIdOk() + stsRequestParamContractName, stsRequestParamContractNameOk := stsRequestParamContract.GetNameOk() + + if stsRequestParamContractIdOk && stsRequestParamContractNameOk { + stsRequestParamContractData[*stsRequestParamContractId] = *stsRequestParamContractName + } + } + + return &stsRequestParamContractData, nil +} diff --git a/internal/connector/pingfederate/resources/pingfederate_idp_sts_request_parameters_contract_test.go b/internal/connector/pingfederate/resources/pingfederate_idp_sts_request_parameters_contract_test.go new file mode 100644 index 00000000..c1796e9d --- /dev/null +++ b/internal/connector/pingfederate/resources/pingfederate_idp_sts_request_parameters_contract_test.go @@ -0,0 +1,25 @@ +package resources_test + +import ( + "testing" + + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/pingfederate/resources" + "github.com/pingidentity/pingcli/internal/testing/testutils" +) + +func TestPingFederateIdpStsRequestParametersContractExport(t *testing.T) { + // Get initialized apiClient and resource + PingFederateClientInfo := testutils.GetPingFederateClientInfo(t) + resource := resources.IdpStsRequestParametersContract(PingFederateClientInfo) + + // Defined the expected ImportBlocks for the resource + expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingfederate_idp_sts_request_parameters_contract", + ResourceName: "STS TestName", + ResourceID: "STSTestID", + }, + } + testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks) +} diff --git a/internal/connector/pingfederate/resources/pingfederate_kerberos_realm.go b/internal/connector/pingfederate/resources/pingfederate_kerberos_realm.go index f48b3db1..e5218c09 100644 --- a/internal/connector/pingfederate/resources/pingfederate_kerberos_realm.go +++ b/internal/connector/pingfederate/resources/pingfederate_kerberos_realm.go @@ -39,9 +39,9 @@ func (r *PingFederateKerberosRealmResource) ExportAll() (*[]connector.ImportBloc for kerberosRealmId, kerberosRealmName := range *kerberosRealmData { commentData := map[string]string{ - "Kerberos Realm Resource ID": kerberosRealmId, - "Kerberos Realm Resource Name": kerberosRealmName, - "Resource Type": r.ResourceType(), + "Kerberos Realm ID": kerberosRealmId, + "Kerberos Realm Name": kerberosRealmName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ @@ -70,8 +70,8 @@ func (r *PingFederateKerberosRealmResource) getKerberosRealmData() (*map[string] return nil, common.DataNilError(r.ResourceType(), response) } - kerberosRealmsItems, ok := kerberosRealms.GetItemsOk() - if !ok { + kerberosRealmsItems, kerberosRealmsItemsOk := kerberosRealms.GetItemsOk() + if !kerberosRealmsItemsOk { return nil, common.DataNilError(r.ResourceType(), response) } diff --git a/internal/connector/pingfederate/resources/pingfederate_local_identity_profile.go b/internal/connector/pingfederate/resources/pingfederate_local_identity_profile.go index 252179e8..d0ebe8ae 100644 --- a/internal/connector/pingfederate/resources/pingfederate_local_identity_profile.go +++ b/internal/connector/pingfederate/resources/pingfederate_local_identity_profile.go @@ -39,9 +39,9 @@ func (r *PingFederateLocalIdentityProfileResource) ExportAll() (*[]connector.Imp for identityProfileId, identityProfileName := range *identityProfileData { commentData := map[string]string{ - "Local Identity Profile Resource ID": identityProfileId, - "Local Identity Profile Resource Name": identityProfileName, - "Resource Type": r.ResourceType(), + "Local Identity Profile ID": identityProfileId, + "Local Identity Profile Name": identityProfileName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ @@ -70,8 +70,8 @@ func (r *PingFederateLocalIdentityProfileResource) getIdentityProfileData() (*ma return nil, common.DataNilError(r.ResourceType(), response) } - identityProfilesItems, ok := identityProfiles.GetItemsOk() - if !ok { + identityProfilesItems, identityProfilesItemsOk := identityProfiles.GetItemsOk() + if !identityProfilesItemsOk { return nil, common.DataNilError(r.ResourceType(), response) } diff --git a/internal/connector/pingfederate/resources/pingfederate_oauth_access_token_manager.go b/internal/connector/pingfederate/resources/pingfederate_oauth_access_token_manager.go index add169f0..e3eeec22 100644 --- a/internal/connector/pingfederate/resources/pingfederate_oauth_access_token_manager.go +++ b/internal/connector/pingfederate/resources/pingfederate_oauth_access_token_manager.go @@ -39,9 +39,9 @@ func (r *PingFederateOAuthAccessTokenManagerResource) ExportAll() (*[]connector. for tokenManagerId, tokenManagerName := range *tokenManagerData { commentData := map[string]string{ - "OAuth Access Token Manager Resource ID": tokenManagerId, - "OAuth Access Token Manager Resource Name": tokenManagerName, - "Resource Type": r.ResourceType(), + "OAuth Access Token Manager ID": tokenManagerId, + "OAuth Access Token Manager Name": tokenManagerName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ @@ -70,8 +70,8 @@ func (r *PingFederateOAuthAccessTokenManagerResource) getTokenManagerData() (*ma return nil, common.DataNilError(r.ResourceType(), response) } - tokenManagersItems, ok := tokenManagers.GetItemsOk() - if !ok { + tokenManagersItems, tokenManagersItemsOk := tokenManagers.GetItemsOk() + if !tokenManagersItemsOk { return nil, common.DataNilError(r.ResourceType(), response) } diff --git a/internal/connector/pingfederate/resources/pingfederate_oauth_access_token_mapping.go b/internal/connector/pingfederate/resources/pingfederate_oauth_access_token_mapping.go index ea512d0c..efe6b9a7 100644 --- a/internal/connector/pingfederate/resources/pingfederate_oauth_access_token_mapping.go +++ b/internal/connector/pingfederate/resources/pingfederate_oauth_access_token_mapping.go @@ -41,9 +41,9 @@ func (r *PingFederateOAuthAccessTokenMappingResource) ExportAll() (*[]connector. for mappingId, mappingContextType := range *mappingData { commentData := map[string]string{ - "OAuth Access Token Mapping Resource ID": mappingId, + "OAuth Access Token Mapping ID": mappingId, "OAuth Access Token Mapping Context Type": mappingContextType, - "Resource Type": r.ResourceType(), + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ diff --git a/internal/connector/pingfederate/resources/pingfederate_oauth_client.go b/internal/connector/pingfederate/resources/pingfederate_oauth_client.go index 7ebf4847..93390a07 100644 --- a/internal/connector/pingfederate/resources/pingfederate_oauth_client.go +++ b/internal/connector/pingfederate/resources/pingfederate_oauth_client.go @@ -39,9 +39,9 @@ func (r *PingFederateOAuthClientResource) ExportAll() (*[]connector.ImportBlock, for oauthClientId, oauthClientName := range *oauthClientData { commentData := map[string]string{ - "OAuth Client Resource ID": oauthClientId, - "OAuth Client Resource Name": oauthClientName, - "Resource Type": r.ResourceType(), + "OAuth Client ID": oauthClientId, + "OAuth Client Name": oauthClientName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ @@ -70,8 +70,8 @@ func (r *PingFederateOAuthClientResource) getOAuthClientData() (*map[string]stri return nil, common.DataNilError(r.ResourceType(), response) } - clientsItems, ok := clients.GetItemsOk() - if !ok { + clientsItems, clientsItemsOk := clients.GetItemsOk() + if !clientsItemsOk { return nil, common.DataNilError(r.ResourceType(), response) } diff --git a/internal/connector/pingfederate/resources/pingfederate_oauth_issuer.go b/internal/connector/pingfederate/resources/pingfederate_oauth_issuer.go index 97127cca..5b99c889 100644 --- a/internal/connector/pingfederate/resources/pingfederate_oauth_issuer.go +++ b/internal/connector/pingfederate/resources/pingfederate_oauth_issuer.go @@ -39,9 +39,9 @@ func (r *PingFederateOAuthIssuerResource) ExportAll() (*[]connector.ImportBlock, for oauthIssuerId, oauthIssuerName := range *oauthIssuerData { commentData := map[string]string{ - "OAuth Issuer Resource ID": oauthIssuerId, - "OAuth Issuer Resource Name": oauthIssuerName, - "Resource Type": r.ResourceType(), + "OAuth Issuer ID": oauthIssuerId, + "OAuth Issuer Name": oauthIssuerName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ diff --git a/internal/connector/pingfederate/resources/pingfederate_openid_connect_policy.go b/internal/connector/pingfederate/resources/pingfederate_openid_connect_policy.go index d6a3231d..b4855664 100644 --- a/internal/connector/pingfederate/resources/pingfederate_openid_connect_policy.go +++ b/internal/connector/pingfederate/resources/pingfederate_openid_connect_policy.go @@ -39,9 +39,9 @@ func (r *PingFederateOpenIDConnectPolicyResource) ExportAll() (*[]connector.Impo for oidcPolicyId, oidcPolicyName := range *oidcPolicyData { commentData := map[string]string{ - "OpenID Connect Policy Resource ID": oidcPolicyId, - "OpenID Connect Policy Resource Name": oidcPolicyName, - "Resource Type": r.ResourceType(), + "OpenID Connect Policy ID": oidcPolicyId, + "OpenID Connect Policy Name": oidcPolicyName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ @@ -70,8 +70,8 @@ func (r *PingFederateOpenIDConnectPolicyResource) getOIDCPolicyData() (*map[stri return nil, common.DataNilError(r.ResourceType(), response) } - oidcPoliciesItems, ok := oidcPolicies.GetItemsOk() - if !ok { + oidcPoliciesItems, oidcPoliciesItemsOk := oidcPolicies.GetItemsOk() + if !oidcPoliciesItemsOk { return nil, common.DataNilError(r.ResourceType(), response) } diff --git a/internal/connector/pingfederate/resources/pingfederate_password_credential_validator.go b/internal/connector/pingfederate/resources/pingfederate_password_credential_validator.go index 7f557bcb..806841e1 100644 --- a/internal/connector/pingfederate/resources/pingfederate_password_credential_validator.go +++ b/internal/connector/pingfederate/resources/pingfederate_password_credential_validator.go @@ -39,9 +39,9 @@ func (r *PingFederatePasswordCredentialValidatorResource) ExportAll() (*[]connec for passwordCredentialValidatorId, passwordCredentialValidatorName := range *passwordCredentialValidatorData { commentData := map[string]string{ - "Password Credential Validator Resource ID": passwordCredentialValidatorId, - "Password Credential Validator Resource Name": passwordCredentialValidatorName, - "Resource Type": r.ResourceType(), + "Password Credential Validator ID": passwordCredentialValidatorId, + "Password Credential Validator Name": passwordCredentialValidatorName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ @@ -70,8 +70,8 @@ func (r *PingFederatePasswordCredentialValidatorResource) getPasswordCredentialV return nil, common.DataNilError(r.ResourceType(), response) } - passwordCredentialValidatorsItems, ok := passwordCredentialValidators.GetItemsOk() - if !ok { + passwordCredentialValidatorsItems, passwordCredentialValidatorsItemsOk := passwordCredentialValidators.GetItemsOk() + if !passwordCredentialValidatorsItemsOk { return nil, common.DataNilError(r.ResourceType(), response) } diff --git a/internal/connector/pingfederate/resources/pingfederate_pingone_connection.go b/internal/connector/pingfederate/resources/pingfederate_pingone_connection.go index 9e10e444..b88888eb 100644 --- a/internal/connector/pingfederate/resources/pingfederate_pingone_connection.go +++ b/internal/connector/pingfederate/resources/pingfederate_pingone_connection.go @@ -39,9 +39,9 @@ func (r *PingFederatePingOneConnectionResource) ExportAll() (*[]connector.Import for pingoneConnectionId, pingoneConnectionName := range *pingoneConnectionData { commentData := map[string]string{ - "PingOne Connection Resource ID": pingoneConnectionId, - "PingOne Connection Resource Name": pingoneConnectionName, - "Resource Type": r.ResourceType(), + "PingOne Connection ID": pingoneConnectionId, + "PingOne Connection Name": pingoneConnectionName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ diff --git a/internal/connector/pingfederate/resources/pingfederate_sp_authentication_policy_contract_mapping.go b/internal/connector/pingfederate/resources/pingfederate_sp_authentication_policy_contract_mapping.go index 8a090def..dc39afce 100644 --- a/internal/connector/pingfederate/resources/pingfederate_sp_authentication_policy_contract_mapping.go +++ b/internal/connector/pingfederate/resources/pingfederate_sp_authentication_policy_contract_mapping.go @@ -45,9 +45,9 @@ func (r *PingFederateSPAuthenticationPolicyContractMappingResource) ExportAll() commentData := map[string]string{ "Resource Type": r.ResourceType(), - "Source Authentication Policy Contract Resource ID": apcToSpAdapterMappingSourceID, - "SP Authentication Policy Contract Mapping Resource ID": apcToSpAdapterMappingId, - "Target SP Adapter Resource ID": apcToSpAdapterMappingTargetID, + "Source Authentication Policy Contract ID": apcToSpAdapterMappingSourceID, + "SP Authentication Policy Contract Mapping ID": apcToSpAdapterMappingId, + "Target SP Adapter ID": apcToSpAdapterMappingTargetID, } importBlock := connector.ImportBlock{ diff --git a/server-profiles/12.1/data.json.subst b/server-profiles/12.1/data.json.subst index 42ac9b15..992f5242 100644 --- a/server-profiles/12.1/data.json.subst +++ b/server-profiles/12.1/data.json.subst @@ -3916,6 +3916,60 @@ } } ] + }, + { + "resourceType": "/identityStoreProvisioners", + "operationType": "SAVE", + "items": [ + { + "id": "ISPTestID", + "name": "ISP TestName", + "pluginDescriptorRef": { + "id": "com.pingidentity.identitystoreprovisioners.sample.SampleIdentityStoreProvisioner", + "location": "https://localhost:9999/pf-admin-api/v1/identityStoreProvisioners/descriptors/com.pingidentity.identitystoreprovisioners.sample.SampleIdentityStoreProvisioner" + }, + "configuration": { + "tables": [], + "fields": [ + { + "name": "Delete user behavior", + "value": "Disable User" + } + ] + }, + "lastModified": "2024-12-13T18:45:28.156Z", + "attributeContract": { + "coreAttributes": [ + { + "name": "username" + } + ], + "inherited": false + }, + "groupAttributeContract": { + "coreAttributes": [ + { + "name": "groupname" + } + ], + "inherited": false + } + } + ] + }, + { + "resourceType": "/idp/stsRequestParametersContracts", + "operationType": "SAVE", + "items": [ + { + "id": "STSTestID", + "name": "STS TestName", + "parameters": [ + "test" + ], + "lastModified": "2024-12-13T18:59:29.324Z" + } + ] } ] } diff --git a/server-profiles/shared-profile/instance/server/default/deploy/pf-sdk-example-identity-store-provisioner-12.2.0.4-SNAPSHOT.jar b/server-profiles/shared-profile/instance/server/default/deploy/pf-sdk-example-identity-store-provisioner-12.2.0.4-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..39a8638660c3e9322195ac0de367f261ad0bda02 GIT binary patch literal 13333 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 literal 0 HcmV?d00001