diff --git a/.gitignore b/.gitignore index 69533ff0..ac27c815 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ go.work go.work.sum export vendor -env*.sh \ No newline at end of file +env*.sh +.env diff --git a/go.mod b/go.mod index d76aef6d..d6231954 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/hashicorp/go-uuid v1.0.3 github.com/manifoldco/promptui v0.9.0 github.com/patrickcping/pingone-go-sdk-v2 v0.12.9 + github.com/patrickcping/pingone-go-sdk-v2/authorize v0.8.0 github.com/patrickcping/pingone-go-sdk-v2/management v0.49.0 github.com/patrickcping/pingone-go-sdk-v2/mfa v0.23.0 github.com/patrickcping/pingone-go-sdk-v2/risk v0.19.0 @@ -137,7 +138,6 @@ require ( github.com/nishanths/predeclared v0.2.2 // indirect github.com/nunnatsa/ginkgolinter v0.19.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/patrickcping/pingone-go-sdk-v2/authorize v0.8.0 // indirect github.com/patrickcping/pingone-go-sdk-v2/credentials v0.11.0 // indirect github.com/patrickcping/pingone-go-sdk-v2/verify v0.9.0 // indirect github.com/pavius/impi v0.0.3 // indirect diff --git a/internal/commands/platform/export_internal.go b/internal/commands/platform/export_internal.go index d1d692c3..c047bfa7 100644 --- a/internal/commands/platform/export_internal.go +++ b/internal/commands/platform/export_internal.go @@ -17,6 +17,7 @@ import ( "github.com/pingidentity/pingcli/internal/connector" "github.com/pingidentity/pingcli/internal/connector/common" "github.com/pingidentity/pingcli/internal/connector/pingfederate" + "github.com/pingidentity/pingcli/internal/connector/pingone/authorize" "github.com/pingidentity/pingcli/internal/connector/pingone/mfa" "github.com/pingidentity/pingcli/internal/connector/pingone/platform" "github.com/pingidentity/pingcli/internal/connector/pingone/protect" @@ -464,6 +465,8 @@ func getExportableConnectors(exportServices *customtypes.ExportServices) (export switch service { case customtypes.ENUM_EXPORT_SERVICE_PINGONE_PLATFORM: connectors = append(connectors, platform.PlatformConnector(pingoneContext, pingoneApiClient, &pingoneApiClientId, pingoneExportEnvID)) + case customtypes.ENUM_EXPORT_SERVICE_PINGONE_AUTHORIZE: + connectors = append(connectors, authorize.AuthorizeConnector(pingoneContext, pingoneApiClient, &pingoneApiClientId, pingoneExportEnvID)) case customtypes.ENUM_EXPORT_SERVICE_PINGONE_SSO: connectors = append(connectors, sso.SSOConnector(pingoneContext, pingoneApiClient, &pingoneApiClientId, pingoneExportEnvID)) case customtypes.ENUM_EXPORT_SERVICE_PINGONE_MFA: diff --git a/internal/connector/pingone/authorize/pingone_authorize_connector.go b/internal/connector/pingone/authorize/pingone_authorize_connector.go new file mode 100644 index 00000000..af2d312b --- /dev/null +++ b/internal/connector/pingone/authorize/pingone_authorize_connector.go @@ -0,0 +1,68 @@ +package authorize + +import ( + "context" + + pingoneGoClient "github.com/patrickcping/pingone-go-sdk-v2/pingone" + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/common" + "github.com/pingidentity/pingcli/internal/connector/pingone/authorize/resources" + "github.com/pingidentity/pingcli/internal/logger" +) + +const ( + serviceName = "pingone-authorize" +) + +// Verify that the connector satisfies the expected interfaces +var ( + _ connector.Exportable = &PingoneAuthorizeConnector{} + _ connector.Authenticatable = &PingoneAuthorizeConnector{} +) + +type PingoneAuthorizeConnector struct { + clientInfo connector.PingOneClientInfo +} + +// Utility method for creating a PingoneAuthorizeConnector +func AuthorizeConnector(ctx context.Context, apiClient *pingoneGoClient.Client, apiClientId *string, exportEnvironmentID string) *PingoneAuthorizeConnector { + return &PingoneAuthorizeConnector{ + clientInfo: connector.PingOneClientInfo{ + Context: ctx, + ApiClient: apiClient, + ApiClientId: apiClientId, + ExportEnvironmentID: exportEnvironmentID, + }, + } +} + +func (c *PingoneAuthorizeConnector) Export(format, outputDir string, overwriteExport bool) error { + l := logger.Get() + + l.Debug().Msgf("Exporting all PingOne Authorize Resources...") + + exportableResources := []connector.ExportableResource{ + resources.AuthorizeAPIService(&c.clientInfo), + resources.AuthorizeAPIServiceDeployment(&c.clientInfo), + resources.AuthorizeAPIServiceOperation(&c.clientInfo), + resources.ApplicationResource(&c.clientInfo), + resources.AuthorizeApplicationResourcePermission(&c.clientInfo), + resources.AuthorizeApplicationRole(&c.clientInfo), + resources.AuthorizeApplicationRolePermission(&c.clientInfo), + resources.AuthorizeDecisionEndpoint(&c.clientInfo), + } + + return common.WriteFiles(exportableResources, format, outputDir, overwriteExport) +} + +func (c *PingoneAuthorizeConnector) ConnectorServiceName() string { + return serviceName +} + +func (c *PingoneAuthorizeConnector) Login() error { + return nil +} + +func (c *PingoneAuthorizeConnector) Logout() error { + return nil +} diff --git a/internal/connector/pingone/authorize/pingone_authorize_connector_test.go b/internal/connector/pingone/authorize/pingone_authorize_connector_test.go new file mode 100644 index 00000000..0cc8923b --- /dev/null +++ b/internal/connector/pingone/authorize/pingone_authorize_connector_test.go @@ -0,0 +1,59 @@ +package authorize_test + +import ( + "testing" + + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/pingone/authorize/resources" + "github.com/pingidentity/pingcli/internal/testing/testutils" + "github.com/pingidentity/pingcli/internal/testing/testutils_terraform" +) + +func TestAuthorizeTerraformPlan(t *testing.T) { + PingOneClientInfo := testutils.GetPingOneClientInfo(t) + + testutils_terraform.InitPingOneTerraform(t) + + testCases := []struct { + name string + resource connector.ExportableResource + ignoredErrors []string + }{ + { + name: "AuthorizeAPIService", + resource: resources.AuthorizeAPIService(PingOneClientInfo), + ignoredErrors: nil, + }, + { + name: "AuthorizeAPIServiceDeployment", + resource: resources.AuthorizeAPIServiceDeployment(PingOneClientInfo), + ignoredErrors: nil, + }, + { + name: "AuthorizeAPIServiceOperation", + resource: resources.AuthorizeAPIServiceOperation(PingOneClientInfo), + ignoredErrors: nil, + }, + { + name: "AuthorizeApplicationRole", + resource: resources.AuthorizeApplicationRole(PingOneClientInfo), + ignoredErrors: nil, + }, + { + name: "AuthorizeApplicationRolePermission", + resource: resources.AuthorizeApplicationRolePermission(PingOneClientInfo), + ignoredErrors: nil, + }, + { + name: "AuthorizeDecisionEndpoint", + resource: resources.AuthorizeDecisionEndpoint(PingOneClientInfo), + ignoredErrors: nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + testutils_terraform.ValidateTerraformPlan(t, tc.resource, tc.ignoredErrors) + }) + } +} diff --git a/internal/connector/pingone/authorize/resources/pingone_application_resource.go b/internal/connector/pingone/authorize/resources/pingone_application_resource.go new file mode 100644 index 00000000..e35f7530 --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_application_resource.go @@ -0,0 +1,108 @@ +package resources + +import ( + "fmt" + + "github.com/patrickcping/pingone-go-sdk-v2/authorize" + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/common" + "github.com/pingidentity/pingcli/internal/connector/pingone" + "github.com/pingidentity/pingcli/internal/logger" +) + +// Verify that the resource satisfies the exportable resource interface +var ( + _ connector.ExportableResource = &PingOneApplicationResourceResource{} +) + +type PingOneApplicationResourceResource struct { + clientInfo *connector.PingOneClientInfo +} + +// Utility method for creating a PingOneApplicationResourceResource +func ApplicationResource(clientInfo *connector.PingOneClientInfo) *PingOneApplicationResourceResource { + return &PingOneApplicationResourceResource{ + clientInfo: clientInfo, + } +} + +func (r *PingOneApplicationResourceResource) ResourceType() string { + return "pingone_application_resource" +} + +type applicationResourceObj struct { + applicationResourceName string + resourceId string + resourceName string +} + +func (r *PingOneApplicationResourceResource) ExportAll() (*[]connector.ImportBlock, error) { + l := logger.Get() + l.Debug().Msgf("Exporting all '%s' Resources...", r.ResourceType()) + + importBlocks := []connector.ImportBlock{} + + applicationResourceData, err := r.getApplicationResourceData() + if err != nil { + return nil, err + } + + for applicationResourceId, applicationResourceObj := range applicationResourceData { + commentData := map[string]string{ + "PingOne Resource ID": applicationResourceObj.resourceId, + "PingOne Resource Name": applicationResourceObj.resourceName, + "Application Resource ID": applicationResourceId, + "Application Resource Name": applicationResourceObj.applicationResourceName, + "Export Environment ID": r.clientInfo.ExportEnvironmentID, + "Resource Type": r.ResourceType(), + } + + importBlock := connector.ImportBlock{ + ResourceType: r.ResourceType(), + ResourceName: fmt.Sprintf("%s_%s", applicationResourceObj.resourceName, applicationResourceObj.applicationResourceName), + ResourceID: fmt.Sprintf("%s/%s/%s", r.clientInfo.ExportEnvironmentID, applicationResourceObj.resourceId, applicationResourceId), + CommentInformation: common.GenerateCommentInformation(commentData), + } + + importBlocks = append(importBlocks, importBlock) + } + + return &importBlocks, nil +} + +func (r *PingOneApplicationResourceResource) getApplicationResourceData() (map[string]applicationResourceObj, error) { + applicationResourceData := make(map[string]applicationResourceObj) + + iter := r.clientInfo.ApiClient.AuthorizeAPIClient.ApplicationResourcesApi.ReadApplicationResources(r.clientInfo.Context, r.clientInfo.ExportEnvironmentID).Execute() + applicationResources, err := pingone.GetAuthorizeAPIObjectsFromIterator[authorize.ApplicationResource](iter, "ReadApplicationResources", "GetResources", r.ResourceType()) + if err != nil { + return nil, err + } + + for _, applicationResource := range applicationResources { + applicationResourceId, applicationResourceIdOk := applicationResource.GetIdOk() + applicationResourceName, applicationResourceNameOk := applicationResource.GetNameOk() + resourceId, resourceIdOk := applicationResource.Parent.GetIdOk() + + if applicationResourceIdOk && applicationResourceNameOk && resourceIdOk { + + resourceObj, httpResponse, err := r.clientInfo.ApiClient.ManagementAPIClient.ResourcesApi.ReadOneResource(r.clientInfo.Context, r.clientInfo.ExportEnvironmentID, *resourceId).Execute() + ok, err := common.HandleClientResponse(httpResponse, err, "ReadOneResource", r.ResourceType()) + if err != nil { + return nil, err + } + // A warning was given when handling the client response. Return nil apiObjects to skip export of resource + if !ok { + return nil, nil + } + + applicationResourceData[*applicationResourceId] = applicationResourceObj{ + applicationResourceName: *applicationResourceName, + resourceId: *resourceId, + resourceName: resourceObj.GetName(), + } + } + } + + return applicationResourceData, nil +} diff --git a/internal/connector/pingone/authorize/resources/pingone_application_resource_permission.go b/internal/connector/pingone/authorize/resources/pingone_application_resource_permission.go new file mode 100644 index 00000000..875184e4 --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_application_resource_permission.go @@ -0,0 +1,133 @@ +package resources + +import ( + "fmt" + + "github.com/patrickcping/pingone-go-sdk-v2/authorize" + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/common" + "github.com/pingidentity/pingcli/internal/connector/pingone" + "github.com/pingidentity/pingcli/internal/logger" +) + +// Verify that the resource satisfies the exportable resource interface +var ( + _ connector.ExportableResource = &PingoneAuthorizeApplicationResourcePermissionResource{} +) + +type PingoneAuthorizeApplicationResourcePermissionResource struct { + clientInfo *connector.PingOneClientInfo +} + +// Utility method for creating a PingoneAuthorizeApplicationResourcePermissionResource +func AuthorizeApplicationResourcePermission(clientInfo *connector.PingOneClientInfo) *PingoneAuthorizeApplicationResourcePermissionResource { + return &PingoneAuthorizeApplicationResourcePermissionResource{ + clientInfo: clientInfo, + } +} + +func (r *PingoneAuthorizeApplicationResourcePermissionResource) ExportAll() (*[]connector.ImportBlock, error) { + l := logger.Get() + l.Debug().Msgf("Exporting all '%s' Resources...", r.ResourceType()) + + importBlocks := []connector.ImportBlock{} + + applicationResourceData, err := r.getApplicationResourceData() + if err != nil { + return nil, err + } + + for appResourceId, appResourceName := range applicationResourceData { + appResourcePermissionData, err := r.getApplicationResourcePermissionData(appResourceId) + if err != nil { + return nil, err + } + + for appResourcePermissionId, appResourcePermissionKey := range appResourcePermissionData { + commentData := map[string]string{ + "Application Resource ID": appResourceId, + "Application Resource Name": appResourceName, + "Application Resource Permission ID": appResourcePermissionId, + "Application Resource Permission Key": appResourcePermissionKey, + "Export Environment ID": r.clientInfo.ExportEnvironmentID, + "Resource Type": r.ResourceType(), + } + + importBlock := connector.ImportBlock{ + ResourceType: r.ResourceType(), + ResourceName: appResourcePermissionKey, + ResourceID: fmt.Sprintf("%s/%s/%s", r.clientInfo.ExportEnvironmentID, appResourceId, appResourcePermissionId), + CommentInformation: common.GenerateCommentInformation(commentData), + } + + importBlocks = append(importBlocks, importBlock) + } + } + + return &importBlocks, nil +} + +func (r *PingoneAuthorizeApplicationResourcePermissionResource) getApplicationResourceData() (map[string]string, error) { + applicationResourceData := make(map[string]string) + + iter := r.clientInfo.ApiClient.AuthorizeAPIClient.ApplicationResourcesApi.ReadApplicationResources(r.clientInfo.Context, r.clientInfo.ExportEnvironmentID).Execute() + applicationResources, err := pingone.GetAuthorizeAPIObjectsFromIterator[authorize.ApplicationResource](iter, "ReadApplicationResources", "GetResources", r.ResourceType()) + if err != nil { + return nil, err + } + + for _, applicationResource := range applicationResources { + applicationResourceId, applicationResourceIdOk := applicationResource.GetIdOk() + applicationResourceName, applicationResourceNameOk := applicationResource.GetNameOk() + + if applicationResourceIdOk && applicationResourceNameOk { + applicationResourceData[*applicationResourceId] = *applicationResourceName + } + } + + return applicationResourceData, nil +} + +func (r *PingoneAuthorizeApplicationResourcePermissionResource) getApplicationResourcePermissionData(appResourceId string) (map[string]string, error) { + applicationResourcePermissionData := make(map[string]string) + + iter := r.clientInfo.ApiClient.AuthorizeAPIClient.ApplicationResourcePermissionsApi.ReadApplicationPermissions(r.clientInfo.Context, r.clientInfo.ExportEnvironmentID, appResourceId).Execute() + applicationResourcePermissions, err := pingone.GetAuthorizeAPIObjectsFromIterator[authorize.EntityArrayEmbeddedPermissionsInner](iter, "ReadApplicationPermissions", "GetPermissions", r.ResourceType()) + if err != nil { + return nil, err + } + + for _, applicationResourcePermission := range applicationResourcePermissions { + + var ( + applicationResourcePermissionId *string + applicationResourcePermissionIdOk bool + applicationResourcePermissionKey *string + applicationResourcePermissionKeyOk bool + ) + + switch t := applicationResourcePermission.GetActualInstance().(type) { + case *authorize.ApplicationResourcePermission: + applicationResourcePermissionId, applicationResourcePermissionIdOk = t.GetIdOk() + case *authorize.ApplicationRolePermission: + applicationResourcePermissionId, applicationResourcePermissionIdOk = t.GetIdOk() + applicationResourcePermissionKey, applicationResourcePermissionKeyOk = t.GetKeyOk() + default: + continue + } + + if applicationResourcePermissionIdOk && applicationResourcePermissionKeyOk { + applicationResourcePermissionData[*applicationResourcePermissionId] = *applicationResourcePermissionKey + } + + if applicationResourcePermissionIdOk && !applicationResourcePermissionKeyOk { + applicationResourcePermissionData[*applicationResourcePermissionId] = *applicationResourcePermissionId + } + } + + return applicationResourcePermissionData, nil +} + +func (r *PingoneAuthorizeApplicationResourcePermissionResource) ResourceType() string { + return "pingone_application_resource_permission" +} diff --git a/internal/connector/pingone/authorize/resources/pingone_application_resource_permission_test.go b/internal/connector/pingone/authorize/resources/pingone_application_resource_permission_test.go new file mode 100644 index 00000000..e0e055ed --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_application_resource_permission_test.go @@ -0,0 +1,32 @@ +package resources_test + +import ( + "fmt" + "testing" + + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/pingone/authorize/resources" + "github.com/pingidentity/pingcli/internal/testing/testutils" +) + +func TestAuthorizeApplicationResourcePermissionExport(t *testing.T) { + // Get initialized apiClient and resource + PingOneClientInfo := testutils.GetPingOneClientInfo(t) + resource := resources.AuthorizeApplicationResourcePermission(PingOneClientInfo) + + // Defined the expected ImportBlocks for the resource + expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingone_application_resource_permission", + ResourceName: "test-permission1:action1", + ResourceID: fmt.Sprintf("%s/62b8a221-a530-44f4-ad02-cdb0d3b1395f/080dd732-99ea-4730-a8a6-8da88a232131", testutils.GetEnvironmentID()), + }, + { + ResourceType: "pingone_application_resource_permission", + ResourceName: "test-permission1:action2", + ResourceID: fmt.Sprintf("%s/62b8a221-a530-44f4-ad02-cdb0d3b1395f/05717cf9-3ce4-443a-8154-1986fe984780", testutils.GetEnvironmentID()), + }, + } + + testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks) +} diff --git a/internal/connector/pingone/authorize/resources/pingone_application_resource_test.go b/internal/connector/pingone/authorize/resources/pingone_application_resource_test.go new file mode 100644 index 00000000..f0bd44fe --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_application_resource_test.go @@ -0,0 +1,27 @@ +package resources_test + +import ( + "fmt" + "testing" + + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/pingone/authorize/resources" + "github.com/pingidentity/pingcli/internal/testing/testutils" +) + +func TestApplicationResourceExport(t *testing.T) { + // Get initialized apiClient and resource + PingOneClientInfo := testutils.GetPingOneClientInfo(t) + resource := resources.ApplicationResource(PingOneClientInfo) + + // Defined the expected ImportBlocks for the resource + expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingone_application_resource", + ResourceName: "authorize-api-service_test-permission1", + ResourceID: fmt.Sprintf("%s/3c6001a0-6110-4934-9d34-fa8c4a2894c2/62b8a221-a530-44f4-ad02-cdb0d3b1395f", testutils.GetEnvironmentID()), + }, + } + + testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks) +} diff --git a/internal/connector/pingone/authorize/resources/pingone_authorize_api_service.go b/internal/connector/pingone/authorize/resources/pingone_authorize_api_service.go new file mode 100644 index 00000000..3af7aab0 --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_authorize_api_service.go @@ -0,0 +1,84 @@ +package resources + +import ( + "fmt" + + "github.com/patrickcping/pingone-go-sdk-v2/authorize" + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/common" + "github.com/pingidentity/pingcli/internal/connector/pingone" + "github.com/pingidentity/pingcli/internal/logger" +) + +// Verify that the resource satisfies the exportable resource interface +var ( + _ connector.ExportableResource = &PingoneAuthorizeAPIServiceResource{} +) + +type PingoneAuthorizeAPIServiceResource struct { + clientInfo *connector.PingOneClientInfo +} + +// Utility method for creating a PingoneAuthorizeAPIServiceResource +func AuthorizeAPIService(clientInfo *connector.PingOneClientInfo) *PingoneAuthorizeAPIServiceResource { + return &PingoneAuthorizeAPIServiceResource{ + clientInfo: clientInfo, + } +} + +func (r *PingoneAuthorizeAPIServiceResource) ExportAll() (*[]connector.ImportBlock, error) { + l := logger.Get() + l.Debug().Msgf("Exporting all '%s' Resources...", r.ResourceType()) + + importBlocks := []connector.ImportBlock{} + + APIServerData, err := r.getAPIServerData() + if err != nil { + return nil, err + } + + for apiServerId, apiServerName := range APIServerData { + commentData := map[string]string{ + "Export Environment ID": r.clientInfo.ExportEnvironmentID, + "API Server ID": apiServerId, + "API Server Name": apiServerName, + "Resource Type": r.ResourceType(), + } + + importBlock := connector.ImportBlock{ + ResourceType: r.ResourceType(), + ResourceName: apiServerName, + ResourceID: fmt.Sprintf("%s/%s", r.clientInfo.ExportEnvironmentID, apiServerId), + CommentInformation: common.GenerateCommentInformation(commentData), + } + + importBlocks = append(importBlocks, importBlock) + } + + return &importBlocks, nil +} + +func (r *PingoneAuthorizeAPIServiceResource) getAPIServerData() (map[string]string, error) { + apiServerData := make(map[string]string) + + iter := r.clientInfo.ApiClient.AuthorizeAPIClient.APIServersApi.ReadAllAPIServers(r.clientInfo.Context, r.clientInfo.ExportEnvironmentID).Execute() + apiServers, err := pingone.GetAuthorizeAPIObjectsFromIterator[authorize.APIServer](iter, "ReadAllAPIServers", "GetApiServers", r.ResourceType()) + if err != nil { + return nil, err + } + + for _, apiServer := range apiServers { + apiServerId, apiServerIdOk := apiServer.GetIdOk() + apiServerName, apiServerNameOk := apiServer.GetNameOk() + + if apiServerIdOk && apiServerNameOk { + apiServerData[*apiServerId] = *apiServerName + } + } + + return apiServerData, nil +} + +func (r *PingoneAuthorizeAPIServiceResource) ResourceType() string { + return "pingone_authorize_api_service" +} diff --git a/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_deployment.go b/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_deployment.go new file mode 100644 index 00000000..5e5ddab2 --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_deployment.go @@ -0,0 +1,112 @@ +package resources + +import ( + "fmt" + + "github.com/patrickcping/pingone-go-sdk-v2/authorize" + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/common" + "github.com/pingidentity/pingcli/internal/connector/pingone" + "github.com/pingidentity/pingcli/internal/logger" +) + +// Verify that the resource satisfies the exportable resource interface +var ( + _ connector.ExportableResource = &PingoneAuthorizeAPIServiceDeploymentResource{} +) + +type PingoneAuthorizeAPIServiceDeploymentResource struct { + clientInfo *connector.PingOneClientInfo +} + +// Utility method for creating a PingoneAuthorizeAPIServiceDeploymentResource +func AuthorizeAPIServiceDeployment(clientInfo *connector.PingOneClientInfo) *PingoneAuthorizeAPIServiceDeploymentResource { + return &PingoneAuthorizeAPIServiceDeploymentResource{ + clientInfo: clientInfo, + } +} + +func (r *PingoneAuthorizeAPIServiceDeploymentResource) ExportAll() (*[]connector.ImportBlock, error) { + l := logger.Get() + l.Debug().Msgf("Exporting all '%s' Resources...", r.ResourceType()) + + importBlocks := []connector.ImportBlock{} + + apiServiceData, err := r.getAPIServiceData() + if err != nil { + return nil, err + } + + for apiServiceId, apiServiceName := range apiServiceData { + apiServiceDeployed, err := r.getAPIServiceDeployed(apiServiceId) + if err != nil { + return nil, err + } + + if apiServiceDeployed { + commentData := map[string]string{ + "API Service ID": apiServiceId, + "API Service Name": apiServiceName, + "Export Environment ID": r.clientInfo.ExportEnvironmentID, + "Resource Type": r.ResourceType(), + } + + importBlock := connector.ImportBlock{ + ResourceType: r.ResourceType(), + ResourceName: apiServiceName, + ResourceID: fmt.Sprintf("%s/%s", r.clientInfo.ExportEnvironmentID, apiServiceId), + CommentInformation: common.GenerateCommentInformation(commentData), + } + + importBlocks = append(importBlocks, importBlock) + } + } + + return &importBlocks, nil +} + +func (r *PingoneAuthorizeAPIServiceDeploymentResource) getAPIServiceData() (map[string]string, error) { + apiServiceData := make(map[string]string) + + iter := r.clientInfo.ApiClient.AuthorizeAPIClient.APIServersApi.ReadAllAPIServers(r.clientInfo.Context, r.clientInfo.ExportEnvironmentID).Execute() + apiServices, err := pingone.GetAuthorizeAPIObjectsFromIterator[authorize.APIServer](iter, "ReadAllAPIServers", "GetApiServers", r.ResourceType()) + if err != nil { + return nil, err + } + + for _, apiService := range apiServices { + apiServiceId, apiServiceIdOk := apiService.GetIdOk() + apiServiceName, apiServiceNameOk := apiService.GetNameOk() + + if apiServiceIdOk && apiServiceNameOk { + apiServiceData[*apiServiceId] = *apiServiceName + } + } + + return apiServiceData, nil +} + +func (r *PingoneAuthorizeAPIServiceDeploymentResource) getAPIServiceDeployed(apiServiceId string) (bool, error) { + + apiServerDeployment, httpResponse, err := r.clientInfo.ApiClient.AuthorizeAPIClient.APIServerDeploymentApi.ReadDeploymentStatus(r.clientInfo.Context, r.clientInfo.ExportEnvironmentID, apiServiceId).Execute() + ok, err := common.HandleClientResponse(httpResponse, err, "ReadDeploymentStatus", r.ResourceType()) + if err != nil { + return false, err + } + // A warning was given when handling the client response. Return nil apiObjects to skip export of resource + if !ok { + return false, nil + } + + if status, ok := apiServerDeployment.GetStatusOk(); ok { + if statusCode, ok := status.GetCodeOk(); ok && statusCode != nil && *statusCode != "DEPLOYMENT_UNINITIALIZED" { + return true, nil + } + } + + return false, nil +} + +func (r *PingoneAuthorizeAPIServiceDeploymentResource) ResourceType() string { + return "pingone_authorize_api_service_deployment" +} diff --git a/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_deployment_test.go b/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_deployment_test.go new file mode 100644 index 00000000..151e701b --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_deployment_test.go @@ -0,0 +1,27 @@ +package resources_test + +import ( + "fmt" + "testing" + + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/pingone/authorize/resources" + "github.com/pingidentity/pingcli/internal/testing/testutils" +) + +func TestAuthorizeAPIServiceDeploymentExport(t *testing.T) { + // Get initialized apiClient and resource + PingOneClientInfo := testutils.GetPingOneClientInfo(t) + resource := resources.AuthorizeAPIServiceDeployment(PingOneClientInfo) + + // Defined the expected ImportBlocks for the resource + expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingone_authorize_api_service_deployment", + ResourceName: "Test API Service", + ResourceID: fmt.Sprintf("%s/cee5d5a9-49aa-478d-816e-ec47a2b5aede", testutils.GetEnvironmentID()), + }, + } + + testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks) +} diff --git a/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_operation.go b/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_operation.go new file mode 100644 index 00000000..242ceb16 --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_operation.go @@ -0,0 +1,114 @@ +package resources + +import ( + "fmt" + + "github.com/patrickcping/pingone-go-sdk-v2/authorize" + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/common" + "github.com/pingidentity/pingcli/internal/connector/pingone" + "github.com/pingidentity/pingcli/internal/logger" +) + +// Verify that the resource satisfies the exportable resource interface +var ( + _ connector.ExportableResource = &PingoneAuthorizeAPIServiceOperationResource{} +) + +type PingoneAuthorizeAPIServiceOperationResource struct { + clientInfo *connector.PingOneClientInfo +} + +// Utility method for creating a PingoneAuthorizeAPIServiceOperationResource +func AuthorizeAPIServiceOperation(clientInfo *connector.PingOneClientInfo) *PingoneAuthorizeAPIServiceOperationResource { + return &PingoneAuthorizeAPIServiceOperationResource{ + clientInfo: clientInfo, + } +} + +func (r *PingoneAuthorizeAPIServiceOperationResource) ExportAll() (*[]connector.ImportBlock, error) { + l := logger.Get() + l.Debug().Msgf("Exporting all '%s' Resources...", r.ResourceType()) + + importBlocks := []connector.ImportBlock{} + + apiServiceData, err := r.getAPIServiceData() + if err != nil { + return nil, err + } + + for apiServiceId, apiServiceName := range apiServiceData { + apiServiceOperationData, err := r.getAPIServiceOperationData(apiServiceId) + if err != nil { + return nil, err + } + + for apiServiceOperationId, apiServiceOperationName := range apiServiceOperationData { + commentData := map[string]string{ + "API Service ID": apiServiceId, + "API Service Name": apiServiceName, + "API Service Operation ID": apiServiceOperationId, + "API Service Operation Name": apiServiceOperationName, + "Export Environment ID": r.clientInfo.ExportEnvironmentID, + "Resource Type": r.ResourceType(), + } + + importBlock := connector.ImportBlock{ + ResourceType: r.ResourceType(), + ResourceName: fmt.Sprintf("%s_%s", apiServiceName, apiServiceOperationName), + ResourceID: fmt.Sprintf("%s/%s/%s", r.clientInfo.ExportEnvironmentID, apiServiceId, apiServiceOperationId), + CommentInformation: common.GenerateCommentInformation(commentData), + } + + importBlocks = append(importBlocks, importBlock) + } + } + + return &importBlocks, nil +} + +func (r *PingoneAuthorizeAPIServiceOperationResource) getAPIServiceData() (map[string]string, error) { + apiServiceData := make(map[string]string) + + iter := r.clientInfo.ApiClient.AuthorizeAPIClient.APIServersApi.ReadAllAPIServers(r.clientInfo.Context, r.clientInfo.ExportEnvironmentID).Execute() + apiServices, err := pingone.GetAuthorizeAPIObjectsFromIterator[authorize.APIServer](iter, "ReadAllAPIServers", "GetApiServers", r.ResourceType()) + if err != nil { + return nil, err + } + + for _, apiService := range apiServices { + apiServiceId, apiServiceIdOk := apiService.GetIdOk() + apiServiceName, apiServiceNameOk := apiService.GetNameOk() + + if apiServiceIdOk && apiServiceNameOk { + apiServiceData[*apiServiceId] = *apiServiceName + } + } + + return apiServiceData, nil +} + +func (r *PingoneAuthorizeAPIServiceOperationResource) getAPIServiceOperationData(apiServiceId string) (map[string]string, error) { + apiServiceOperationData := make(map[string]string) + + iter := r.clientInfo.ApiClient.AuthorizeAPIClient.APIServerOperationsApi.ReadAllAPIServerOperations(r.clientInfo.Context, r.clientInfo.ExportEnvironmentID, apiServiceId).Execute() + apiServiceOperations, err := pingone.GetAuthorizeAPIObjectsFromIterator[authorize.APIServerOperation](iter, "ReadAllAPIServerOperations", "GetOperations", r.ResourceType()) + if err != nil { + return nil, err + } + + for _, apiServiceOperation := range apiServiceOperations { + apiServiceOperationId, apiServiceOperationIdOk := apiServiceOperation.GetIdOk() + apiServiceOperationName, apiServiceOperationNameOk := apiServiceOperation.GetNameOk() + + if apiServiceOperationIdOk && apiServiceOperationNameOk { + apiServiceOperationData[*apiServiceOperationId] = *apiServiceOperationName + } + } + + return apiServiceOperationData, nil +} + +func (r *PingoneAuthorizeAPIServiceOperationResource) ResourceType() string { + return "pingone_authorize_api_service_operation" +} diff --git a/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_operation_test.go b/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_operation_test.go new file mode 100644 index 00000000..0b09b7e0 --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_operation_test.go @@ -0,0 +1,27 @@ +package resources_test + +import ( + "fmt" + "testing" + + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/pingone/authorize/resources" + "github.com/pingidentity/pingcli/internal/testing/testutils" +) + +func TestAuthorizeAPIServiceOperationExport(t *testing.T) { + // Get initialized apiClient and resource + PingOneClientInfo := testutils.GetPingOneClientInfo(t) + resource := resources.AuthorizeAPIServiceOperation(PingOneClientInfo) + + // Defined the expected ImportBlocks for the resource + expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingone_authorize_api_service_operation", + ResourceName: "Test API Service_My Path", + ResourceID: fmt.Sprintf("%s/cee5d5a9-49aa-478d-816e-ec47a2b5aede/07fc42c1-d998-40bd-bb64-143911924608", testutils.GetEnvironmentID()), + }, + } + + testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks) +} diff --git a/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_test.go b/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_test.go new file mode 100644 index 00000000..acf234bb --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_authorize_api_service_test.go @@ -0,0 +1,32 @@ +package resources_test + +import ( + "fmt" + "testing" + + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/pingone/authorize/resources" + "github.com/pingidentity/pingcli/internal/testing/testutils" +) + +func TestAuthorizeAPIServiceExport(t *testing.T) { + // Get initialized apiClient and resource + PingOneClientInfo := testutils.GetPingOneClientInfo(t) + resource := resources.AuthorizeAPIService(PingOneClientInfo) + + // Defined the expected ImportBlocks for the resource + expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingone_authorize_api_service", + ResourceName: "Test API Service", + ResourceID: fmt.Sprintf("%s/cee5d5a9-49aa-478d-816e-ec47a2b5aede", testutils.GetEnvironmentID()), + }, + { + ResourceType: "pingone_authorize_api_service", + ResourceName: "Undeployed Test API Service", + ResourceID: fmt.Sprintf("%s/5558f5ab-46b2-40ef-ac78-9a32a07e31c3", testutils.GetEnvironmentID()), + }, + } + + testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks) +} diff --git a/internal/connector/pingone/authorize/resources/pingone_authorize_application_role.go b/internal/connector/pingone/authorize/resources/pingone_authorize_application_role.go new file mode 100644 index 00000000..bec9eaf8 --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_authorize_application_role.go @@ -0,0 +1,84 @@ +package resources + +import ( + "fmt" + + "github.com/patrickcping/pingone-go-sdk-v2/authorize" + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/common" + "github.com/pingidentity/pingcli/internal/connector/pingone" + "github.com/pingidentity/pingcli/internal/logger" +) + +// Verify that the resource satisfies the exportable resource interface +var ( + _ connector.ExportableResource = &PingoneAuthorizeApplicationRoleResource{} +) + +type PingoneAuthorizeApplicationRoleResource struct { + clientInfo *connector.PingOneClientInfo +} + +// Utility method for creating a PingoneAuthorizeApplicationRoleResource +func AuthorizeApplicationRole(clientInfo *connector.PingOneClientInfo) *PingoneAuthorizeApplicationRoleResource { + return &PingoneAuthorizeApplicationRoleResource{ + clientInfo: clientInfo, + } +} + +func (r *PingoneAuthorizeApplicationRoleResource) ExportAll() (*[]connector.ImportBlock, error) { + l := logger.Get() + l.Debug().Msgf("Exporting all '%s' Resources...", r.ResourceType()) + + importBlocks := []connector.ImportBlock{} + + ApplicationRoleData, err := r.getApplicationRoleData() + if err != nil { + return nil, err + } + + for applicationRoleId, applicationRoleName := range ApplicationRoleData { + commentData := map[string]string{ + "Export Environment ID": r.clientInfo.ExportEnvironmentID, + "Application Role ID": applicationRoleId, + "Application Role Name": applicationRoleName, + "Resource Type": r.ResourceType(), + } + + importBlock := connector.ImportBlock{ + ResourceType: r.ResourceType(), + ResourceName: applicationRoleName, + ResourceID: fmt.Sprintf("%s/%s", r.clientInfo.ExportEnvironmentID, applicationRoleId), + CommentInformation: common.GenerateCommentInformation(commentData), + } + + importBlocks = append(importBlocks, importBlock) + } + + return &importBlocks, nil +} + +func (r *PingoneAuthorizeApplicationRoleResource) getApplicationRoleData() (map[string]string, error) { + applicationRoleData := make(map[string]string) + + iter := r.clientInfo.ApiClient.AuthorizeAPIClient.ApplicationRolesApi.ReadApplicationRoles(r.clientInfo.Context, r.clientInfo.ExportEnvironmentID).Execute() + applicationRoles, err := pingone.GetAuthorizeAPIObjectsFromIterator[authorize.ApplicationRole](iter, "ReadApplicationRoles", "GetRoles", r.ResourceType()) + if err != nil { + return nil, err + } + + for _, applicationRole := range applicationRoles { + applicationRoleId, applicationRoleIdOk := applicationRole.GetIdOk() + applicationRoleName, applicationRoleNameOk := applicationRole.GetNameOk() + + if applicationRoleIdOk && applicationRoleNameOk { + applicationRoleData[*applicationRoleId] = *applicationRoleName + } + } + + return applicationRoleData, nil +} + +func (r *PingoneAuthorizeApplicationRoleResource) ResourceType() string { + return "pingone_authorize_application_role" +} diff --git a/internal/connector/pingone/authorize/resources/pingone_authorize_application_role_permission.go b/internal/connector/pingone/authorize/resources/pingone_authorize_application_role_permission.go new file mode 100644 index 00000000..05acdbe1 --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_authorize_application_role_permission.go @@ -0,0 +1,127 @@ +package resources + +import ( + "fmt" + + "github.com/patrickcping/pingone-go-sdk-v2/authorize" + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/common" + "github.com/pingidentity/pingcli/internal/connector/pingone" + "github.com/pingidentity/pingcli/internal/logger" +) + +// Verify that the resource satisfies the exportable resource interface +var ( + _ connector.ExportableResource = &PingoneAuthorizeApplicationRolePermissionResource{} +) + +type PingoneAuthorizeApplicationRolePermissionResource struct { + clientInfo *connector.PingOneClientInfo +} + +// Utility method for creating a PingoneAuthorizeApplicationRolePermissionResource +func AuthorizeApplicationRolePermission(clientInfo *connector.PingOneClientInfo) *PingoneAuthorizeApplicationRolePermissionResource { + return &PingoneAuthorizeApplicationRolePermissionResource{ + clientInfo: clientInfo, + } +} + +func (r *PingoneAuthorizeApplicationRolePermissionResource) ExportAll() (*[]connector.ImportBlock, error) { + l := logger.Get() + l.Debug().Msgf("Exporting all '%s' Resources...", r.ResourceType()) + + importBlocks := []connector.ImportBlock{} + + applicationRoleData, err := r.getApplicationRoleData() + if err != nil { + return nil, err + } + + for appRoleId, appRoleName := range applicationRoleData { + appRolePermissionData, err := r.getApplicationRolePermissionData(appRoleId) + if err != nil { + return nil, err + } + + for appRolePermissionId, appRolePermissionKey := range appRolePermissionData { + commentData := map[string]string{ + "Application Role ID": appRoleId, + "Application Role Name": appRoleName, + "Application Role Permission ID": appRolePermissionId, + "Application Role Permission Key": appRolePermissionKey, + "Export Environment ID": r.clientInfo.ExportEnvironmentID, + "Resource Type": r.ResourceType(), + } + + importBlock := connector.ImportBlock{ + ResourceType: r.ResourceType(), + ResourceName: fmt.Sprintf("%s_%s", appRoleName, appRolePermissionKey), + ResourceID: fmt.Sprintf("%s/%s/%s", r.clientInfo.ExportEnvironmentID, appRoleId, appRolePermissionId), + CommentInformation: common.GenerateCommentInformation(commentData), + } + + importBlocks = append(importBlocks, importBlock) + } + } + + return &importBlocks, nil +} + +func (r *PingoneAuthorizeApplicationRolePermissionResource) getApplicationRoleData() (map[string]string, error) { + applicationRoleData := make(map[string]string) + + iter := r.clientInfo.ApiClient.AuthorizeAPIClient.ApplicationRolesApi.ReadApplicationRoles(r.clientInfo.Context, r.clientInfo.ExportEnvironmentID).Execute() + applicationRoles, err := pingone.GetAuthorizeAPIObjectsFromIterator[authorize.ApplicationRole](iter, "ReadApplicationRoles", "GetRoles", r.ResourceType()) + if err != nil { + return nil, err + } + + for _, applicationRole := range applicationRoles { + applicationRoleId, applicationRoleIdOk := applicationRole.GetIdOk() + applicationRoleName, applicationRoleNameOk := applicationRole.GetNameOk() + + if applicationRoleIdOk && applicationRoleNameOk { + applicationRoleData[*applicationRoleId] = *applicationRoleName + } + } + + return applicationRoleData, nil +} + +func (r *PingoneAuthorizeApplicationRolePermissionResource) getApplicationRolePermissionData(appRoleId string) (map[string]string, error) { + applicationRolePermissionData := make(map[string]string) + + iter := r.clientInfo.ApiClient.AuthorizeAPIClient.ApplicationRolePermissionsApi.ReadApplicationRolePermissions(r.clientInfo.Context, r.clientInfo.ExportEnvironmentID, appRoleId).Execute() + applicationRolePermissions, err := pingone.GetAuthorizeAPIObjectsFromIterator[authorize.EntityArrayEmbeddedPermissionsInner](iter, "ReadApplicationRolePermissions", "GetPermissions", r.ResourceType()) + if err != nil { + return nil, err + } + + for _, applicationRolePermission := range applicationRolePermissions { + + var ( + applicationRolePermissionId *string + applicationRolePermissionIdOk bool + applicationRolePermissionKey *string + applicationRolePermissionKeyOk bool + ) + + switch t := applicationRolePermission.GetActualInstance().(type) { + case *authorize.ApplicationRolePermission: + applicationRolePermissionId, applicationRolePermissionIdOk = t.GetIdOk() + applicationRolePermissionKey, applicationRolePermissionKeyOk = t.GetKeyOk() + default: + continue + } + + if applicationRolePermissionIdOk && applicationRolePermissionKeyOk { + applicationRolePermissionData[*applicationRolePermissionId] = *applicationRolePermissionKey + } + } + + return applicationRolePermissionData, nil +} + +func (r *PingoneAuthorizeApplicationRolePermissionResource) ResourceType() string { + return "pingone_authorize_application_role_permission" +} diff --git a/internal/connector/pingone/authorize/resources/pingone_authorize_application_role_permission_test.go b/internal/connector/pingone/authorize/resources/pingone_authorize_application_role_permission_test.go new file mode 100644 index 00000000..544517c1 --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_authorize_application_role_permission_test.go @@ -0,0 +1,32 @@ +package resources_test + +import ( + "fmt" + "testing" + + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/pingone/authorize/resources" + "github.com/pingidentity/pingcli/internal/testing/testutils" +) + +func TestAuthorizeApplicationRolePermissionExport(t *testing.T) { + // Get initialized apiClient and resource + PingOneClientInfo := testutils.GetPingOneClientInfo(t) + resource := resources.AuthorizeApplicationRolePermission(PingOneClientInfo) + + // Defined the expected ImportBlocks for the resource + expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingone_authorize_application_role_permission", + ResourceName: "test-role_test-permission1:action1", + ResourceID: fmt.Sprintf("%s/f45cbcc7-2406-470b-93bc-ff477da0b8f7/080dd732-99ea-4730-a8a6-8da88a232131", testutils.GetEnvironmentID()), + }, + { + ResourceType: "pingone_authorize_application_role_permission", + ResourceName: "test-role_test-permission1:action2", + ResourceID: fmt.Sprintf("%s/f45cbcc7-2406-470b-93bc-ff477da0b8f7/05717cf9-3ce4-443a-8154-1986fe984780", testutils.GetEnvironmentID()), + }, + } + + testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks) +} diff --git a/internal/connector/pingone/authorize/resources/pingone_authorize_application_role_test.go b/internal/connector/pingone/authorize/resources/pingone_authorize_application_role_test.go new file mode 100644 index 00000000..19f43f4d --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_authorize_application_role_test.go @@ -0,0 +1,27 @@ +package resources_test + +import ( + "fmt" + "testing" + + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/pingone/authorize/resources" + "github.com/pingidentity/pingcli/internal/testing/testutils" +) + +func TestAuthorizeApplicationRoleExport(t *testing.T) { + // Get initialized apiClient and resource + PingOneClientInfo := testutils.GetPingOneClientInfo(t) + resource := resources.AuthorizeApplicationRole(PingOneClientInfo) + + // Defined the expected ImportBlocks for the resource + expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingone_authorize_application_role", + ResourceName: "test-role", + ResourceID: fmt.Sprintf("%s/f45cbcc7-2406-470b-93bc-ff477da0b8f7", testutils.GetEnvironmentID()), + }, + } + + testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks) +} diff --git a/internal/connector/pingone/authorize/resources/pingone_authorize_decision_endpoint.go b/internal/connector/pingone/authorize/resources/pingone_authorize_decision_endpoint.go new file mode 100644 index 00000000..4902a636 --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_authorize_decision_endpoint.go @@ -0,0 +1,84 @@ +package resources + +import ( + "fmt" + + "github.com/patrickcping/pingone-go-sdk-v2/authorize" + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/common" + "github.com/pingidentity/pingcli/internal/connector/pingone" + "github.com/pingidentity/pingcli/internal/logger" +) + +// Verify that the resource satisfies the exportable resource interface +var ( + _ connector.ExportableResource = &PingoneAuthorizeDecisionEndpointResource{} +) + +type PingoneAuthorizeDecisionEndpointResource struct { + clientInfo *connector.PingOneClientInfo +} + +// Utility method for creating a PingoneAuthorizeDecisionEndpointResource +func AuthorizeDecisionEndpoint(clientInfo *connector.PingOneClientInfo) *PingoneAuthorizeDecisionEndpointResource { + return &PingoneAuthorizeDecisionEndpointResource{ + clientInfo: clientInfo, + } +} + +func (r *PingoneAuthorizeDecisionEndpointResource) ExportAll() (*[]connector.ImportBlock, error) { + l := logger.Get() + l.Debug().Msgf("Exporting all '%s' Resources...", r.ResourceType()) + + importBlocks := []connector.ImportBlock{} + + DecisionEndpointData, err := r.getDecisionEndpointData() + if err != nil { + return nil, err + } + + for decisionEndpointId, decisionEndpointName := range DecisionEndpointData { + commentData := map[string]string{ + "Export Environment ID": r.clientInfo.ExportEnvironmentID, + "Decision Endpoint ID": decisionEndpointId, + "Decision Endpoint Name": decisionEndpointName, + "Resource Type": r.ResourceType(), + } + + importBlock := connector.ImportBlock{ + ResourceType: r.ResourceType(), + ResourceName: decisionEndpointName, + ResourceID: fmt.Sprintf("%s/%s", r.clientInfo.ExportEnvironmentID, decisionEndpointId), + CommentInformation: common.GenerateCommentInformation(commentData), + } + + importBlocks = append(importBlocks, importBlock) + } + + return &importBlocks, nil +} + +func (r *PingoneAuthorizeDecisionEndpointResource) getDecisionEndpointData() (map[string]string, error) { + decisionEndpointData := make(map[string]string) + + iter := r.clientInfo.ApiClient.AuthorizeAPIClient.PolicyDecisionManagementApi.ReadAllDecisionEndpoints(r.clientInfo.Context, r.clientInfo.ExportEnvironmentID).Execute() + decisionEndpoints, err := pingone.GetAuthorizeAPIObjectsFromIterator[authorize.DecisionEndpoint](iter, "ReadAllDecisionEndpoints", "GetDecisionEndpoints", r.ResourceType()) + if err != nil { + return nil, err + } + + for _, decisionEndpoint := range decisionEndpoints { + decisionEndpointId, decisionEndpointIdOk := decisionEndpoint.GetIdOk() + decisionEndpointName, decisionEndpointNameOk := decisionEndpoint.GetNameOk() + + if decisionEndpointIdOk && decisionEndpointNameOk { + decisionEndpointData[*decisionEndpointId] = *decisionEndpointName + } + } + + return decisionEndpointData, nil +} + +func (r *PingoneAuthorizeDecisionEndpointResource) ResourceType() string { + return "pingone_authorize_decision_endpoint" +} diff --git a/internal/connector/pingone/authorize/resources/pingone_authorize_decision_endpoint_test.go b/internal/connector/pingone/authorize/resources/pingone_authorize_decision_endpoint_test.go new file mode 100644 index 00000000..2f61b8f2 --- /dev/null +++ b/internal/connector/pingone/authorize/resources/pingone_authorize_decision_endpoint_test.go @@ -0,0 +1,47 @@ +package resources_test + +import ( + "fmt" + "testing" + + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/connector/pingone/authorize/resources" + "github.com/pingidentity/pingcli/internal/testing/testutils" +) + +func TestAuthorizeDecisionEndpointExport(t *testing.T) { + // Get initialized apiClient and resource + PingOneClientInfo := testutils.GetPingOneClientInfo(t) + resource := resources.AuthorizeDecisionEndpoint(PingOneClientInfo) + + // Defined the expected ImportBlocks for the resource + expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingone_authorize_decision_endpoint", + ResourceName: "DEV", + ResourceID: fmt.Sprintf("%s/f8660b46-b96e-457c-8d8f-8ee455e4baa3", testutils.GetEnvironmentID()), + }, + { + ResourceType: "pingone_authorize_decision_endpoint", + ResourceName: "PROD", + ResourceID: fmt.Sprintf("%s/07a4f450-d99f-439f-834a-46b8332a3e31", testutils.GetEnvironmentID()), + }, + { + ResourceType: "pingone_authorize_decision_endpoint", + ResourceName: "TEST", + ResourceID: fmt.Sprintf("%s/3368886d-7d57-4aa8-a8f6-7d24dffa4b3c", testutils.GetEnvironmentID()), + }, + { + ResourceType: "pingone_authorize_decision_endpoint", + ResourceName: "CLI", + ResourceID: fmt.Sprintf("%s/6f4cf36d-fdc1-445c-a1df-37c8e3305eaf", testutils.GetEnvironmentID()), + }, + { + ResourceType: "pingone_authorize_decision_endpoint", + ResourceName: "Test API Service", + ResourceID: fmt.Sprintf("%s/20c01743-084f-4129-b304-42ce6a5edf4f", testutils.GetEnvironmentID()), + }, + } + + testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks) +} diff --git a/internal/connector/pingone/common.go b/internal/connector/pingone/common.go index 122a7e63..1456cd08 100644 --- a/internal/connector/pingone/common.go +++ b/internal/connector/pingone/common.go @@ -5,6 +5,7 @@ import ( "net/http" "reflect" + "github.com/patrickcping/pingone-go-sdk-v2/authorize" "github.com/patrickcping/pingone-go-sdk-v2/management" "github.com/patrickcping/pingone-go-sdk-v2/mfa" "github.com/patrickcping/pingone-go-sdk-v2/risk" @@ -34,6 +35,41 @@ func CheckSingletonResource(response *http.Response, err error, apiFuncName, res return true, nil } +func GetAuthorizeAPIObjectsFromIterator[T any](iter authorize.EntityArrayPagedIterator, clientFuncName, extractionFuncName, resourceType string) ([]T, error) { + apiObjects := []T{} + + for cursor, err := range iter { + ok, err := common.HandleClientResponse(cursor.HTTPResponse, err, clientFuncName, resourceType) + if err != nil { + return nil, err + } + // A warning was given when handling the client response. Return nil apiObjects to skip export of resource + if !ok { + return nil, nil + } + + nilErr := common.DataNilError(resourceType, cursor.HTTPResponse) + + if cursor.EntityArray == nil { + return nil, nilErr + } + + embedded, embeddedOk := cursor.EntityArray.GetEmbeddedOk() + if !embeddedOk { + return nil, nilErr + } + + apiObject, err := getAPIObjectFromEmbedded[T](reflect.ValueOf(embedded), extractionFuncName, resourceType) + if err != nil { + output.SystemError(err.Error(), nil) + } + + apiObjects = append(apiObjects, apiObject...) + } + + return apiObjects, nil +} + func GetManagementAPIObjectsFromIterator[T any](iter management.EntityArrayPagedIterator, clientFuncName, extractionFuncName, resourceType string) ([]T, error) { apiObjects := []T{} diff --git a/internal/connector/pingone/sso/resources/pingone_resource.go b/internal/connector/pingone/sso/resources/pingone_resource.go index ccd51362..1c1e26cd 100644 --- a/internal/connector/pingone/sso/resources/pingone_resource.go +++ b/internal/connector/pingone/sso/resources/pingone_resource.go @@ -44,8 +44,8 @@ func (r *PingOneResourceResource) ExportAll() (*[]connector.ImportBlock, error) for resourceId, resourceName := range resourceData { commentData := map[string]string{ "Export Environment ID": r.clientInfo.ExportEnvironmentID, - "Resource ID": resourceId, - "Resource Name": resourceName, + "PingOne Resource ID": resourceId, + "PingOne Resource Name": resourceName, "Resource Type": r.ResourceType(), } diff --git a/internal/connector/pingone/sso/resources/pingone_resource_attribute.go b/internal/connector/pingone/sso/resources/pingone_resource_attribute.go index 39dba1b2..a86852ae 100644 --- a/internal/connector/pingone/sso/resources/pingone_resource_attribute.go +++ b/internal/connector/pingone/sso/resources/pingone_resource_attribute.go @@ -52,12 +52,12 @@ func (r *PingOneResourceAttributeResource) ExportAll() (*[]connector.ImportBlock for resourceAttributeId, resourceAttributeName := range resourceAttributeData { commentData := map[string]string{ - "Export Environment ID": r.clientInfo.ExportEnvironmentID, - "Resource Attribute ID": resourceAttributeId, - "Resource Attribute Name": resourceAttributeName, - "Resource ID": resourceId, - "Resource Name": resourceName, - "Resource Type": r.ResourceType(), + "Export Environment ID": r.clientInfo.ExportEnvironmentID, + "PingOne Resource Attribute ID": resourceAttributeId, + "PingOne Resource Attribute Name": resourceAttributeName, + "PingOne Resource ID": resourceId, + "PingOne Resource Name": resourceName, + "Resource Type": r.ResourceType(), } importBlock := connector.ImportBlock{ diff --git a/internal/connector/pingone/sso/resources/pingone_resource_attribute_test.go b/internal/connector/pingone/sso/resources/pingone_resource_attribute_test.go index 8c8eb4a5..490649e7 100644 --- a/internal/connector/pingone/sso/resources/pingone_resource_attribute_test.go +++ b/internal/connector/pingone/sso/resources/pingone_resource_attribute_test.go @@ -16,6 +16,11 @@ func TestResourceAttributeExport(t *testing.T) { // Defined the expected ImportBlocks for the resource expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingone_resource_attribute", + ResourceName: "authorize-api-service_sub", + ResourceID: fmt.Sprintf("%s/3c6001a0-6110-4934-9d34-fa8c4a2894c2/3f79ad4f-588a-4652-bad8-d64d405bef8a", testutils.GetEnvironmentID()), + }, { ResourceType: "pingone_resource_attribute", ResourceName: "test_sub", @@ -151,6 +156,11 @@ func TestResourceAttributeExport(t *testing.T) { ResourceName: "openid_middle_name", ResourceID: fmt.Sprintf("%s/8c428665-3e68-4f3c-997d-16a97f8cbe80/fd6180af-b339-47bb-a9e3-6e02b69fb7ad", testutils.GetEnvironmentID()), }, + { + ResourceType: "pingone_resource_attribute", + ResourceName: "Undeployed Test API Service_sub", + ResourceID: fmt.Sprintf("%s/a35fe5ea-084c-4245-80f1-85f9eaf4f063/be2a2418-127a-4d60-9c04-88a5a568e25c", testutils.GetEnvironmentID()), + }, } testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks) diff --git a/internal/connector/pingone/sso/resources/pingone_resource_scope_test.go b/internal/connector/pingone/sso/resources/pingone_resource_scope_test.go index 74bd1bd0..11c1d1ac 100644 --- a/internal/connector/pingone/sso/resources/pingone_resource_scope_test.go +++ b/internal/connector/pingone/sso/resources/pingone_resource_scope_test.go @@ -16,6 +16,16 @@ func TestResourceScopeExport(t *testing.T) { // Defined the expected ImportBlocks for the resource expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingone_resource_scope", + ResourceName: "authorize-api-service_apiscope", + ResourceID: fmt.Sprintf("%s/3c6001a0-6110-4934-9d34-fa8c4a2894c2/97b9c81c-56a3-4727-8626-9c55826f98c0", testutils.GetEnvironmentID()), + }, + { + ResourceType: "pingone_resource_scope", + ResourceName: "authorize-api-service_testing", + ResourceID: fmt.Sprintf("%s/3c6001a0-6110-4934-9d34-fa8c4a2894c2/6aa03c9d-7003-4ddb-9395-b176d4bde6d6", testutils.GetEnvironmentID()), + }, { ResourceType: "pingone_resource_scope", ResourceName: "test_testing", diff --git a/internal/connector/pingone/sso/resources/pingone_resource_test.go b/internal/connector/pingone/sso/resources/pingone_resource_test.go index 49ad7154..08bcc047 100644 --- a/internal/connector/pingone/sso/resources/pingone_resource_test.go +++ b/internal/connector/pingone/sso/resources/pingone_resource_test.go @@ -16,6 +16,11 @@ func TestResourceExport(t *testing.T) { // Defined the expected ImportBlocks for the resource expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: "pingone_resource", + ResourceName: "authorize-api-service", + ResourceID: fmt.Sprintf("%s/3c6001a0-6110-4934-9d34-fa8c4a2894c2", testutils.GetEnvironmentID()), + }, { ResourceType: "pingone_resource", ResourceName: "test", @@ -36,6 +41,11 @@ func TestResourceExport(t *testing.T) { ResourceName: "openid", ResourceID: fmt.Sprintf("%s/8c428665-3e68-4f3c-997d-16a97f8cbe80", testutils.GetEnvironmentID()), }, + { + ResourceType: "pingone_resource", + ResourceName: "Undeployed Test API Service", + ResourceID: fmt.Sprintf("%s/a35fe5ea-084c-4245-80f1-85f9eaf4f063", testutils.GetEnvironmentID()), + }, } testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks) diff --git a/internal/customtypes/export_services.go b/internal/customtypes/export_services.go index b5f1badc..41be1159 100644 --- a/internal/customtypes/export_services.go +++ b/internal/customtypes/export_services.go @@ -9,11 +9,12 @@ import ( ) const ( - ENUM_EXPORT_SERVICE_PINGONE_PLATFORM string = "pingone-platform" - ENUM_EXPORT_SERVICE_PINGONE_SSO string = "pingone-sso" - ENUM_EXPORT_SERVICE_PINGONE_MFA string = "pingone-mfa" - ENUM_EXPORT_SERVICE_PINGONE_PROTECT string = "pingone-protect" - ENUM_EXPORT_SERVICE_PINGFEDERATE string = "pingfederate" + ENUM_EXPORT_SERVICE_PINGONE_PLATFORM string = "pingone-platform" + ENUM_EXPORT_SERVICE_PINGONE_AUTHORIZE string = "pingone-authorize" + ENUM_EXPORT_SERVICE_PINGONE_SSO string = "pingone-sso" + ENUM_EXPORT_SERVICE_PINGONE_MFA string = "pingone-mfa" + ENUM_EXPORT_SERVICE_PINGONE_PROTECT string = "pingone-protect" + ENUM_EXPORT_SERVICE_PINGFEDERATE string = "pingfederate" ) type ExportServices []string @@ -64,6 +65,7 @@ func (es ExportServices) ContainsPingOneService() bool { pingoneServices := []string{ ENUM_EXPORT_SERVICE_PINGONE_PLATFORM, + ENUM_EXPORT_SERVICE_PINGONE_AUTHORIZE, ENUM_EXPORT_SERVICE_PINGONE_SSO, ENUM_EXPORT_SERVICE_PINGONE_MFA, ENUM_EXPORT_SERVICE_PINGONE_PROTECT, @@ -100,6 +102,7 @@ func ExportServicesValidValues() []string { allServices := []string{ ENUM_EXPORT_SERVICE_PINGFEDERATE, ENUM_EXPORT_SERVICE_PINGONE_PLATFORM, + ENUM_EXPORT_SERVICE_PINGONE_AUTHORIZE, ENUM_EXPORT_SERVICE_PINGONE_SSO, ENUM_EXPORT_SERVICE_PINGONE_MFA, ENUM_EXPORT_SERVICE_PINGONE_PROTECT,