diff --git a/internal/connector/pingone/platform/pingone_platform_connector.go b/internal/connector/pingone/platform/pingone_platform_connector.go index 0852c42b..f9ab1a78 100644 --- a/internal/connector/pingone/platform/pingone_platform_connector.go +++ b/internal/connector/pingone/platform/pingone_platform_connector.go @@ -55,6 +55,7 @@ func (c *PingOnePlatformConnector) Export(format, outputDir string, overwriteExp resources.BrandingThemeDefault(&c.clientInfo), resources.Certificate(&c.clientInfo), resources.CustomDomain(&c.clientInfo), + resources.CustomRole(&c.clientInfo), resources.Environment(&c.clientInfo), resources.Form(&c.clientInfo), resources.FormsRecaptchaV2(&c.clientInfo), diff --git a/internal/connector/pingone/platform/pingone_platform_connector_test.go b/internal/connector/pingone/platform/pingone_platform_connector_test.go index 29903b8e..00c2ce68 100644 --- a/internal/connector/pingone/platform/pingone_platform_connector_test.go +++ b/internal/connector/pingone/platform/pingone_platform_connector_test.go @@ -77,6 +77,11 @@ func TestPlatformTerraformPlan(t *testing.T) { testableResource: pingone_platform_testable_resources.CustomDomain(t, clientInfo), ignoredErrors: nil, }, + { + name: "CustomRole", + testableResource: pingone_platform_testable_resources.CustomRole(t, clientInfo), + ignoredErrors: nil, + }, { name: "Environment", testableResource: pingone_platform_testable_resources.Environment(t, clientInfo), diff --git a/internal/connector/pingone/platform/resources/custom_role.go b/internal/connector/pingone/platform/resources/custom_role.go new file mode 100644 index 00000000..cf2aaedd --- /dev/null +++ b/internal/connector/pingone/platform/resources/custom_role.go @@ -0,0 +1,89 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-cli-generator + +package resources + +import ( + "fmt" + + "github.com/patrickcping/pingone-go-sdk-v2/management" + "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 = &PingOneCustomRoleResource{} +) + +type PingOneCustomRoleResource struct { + clientInfo *connector.ClientInfo +} + +// Utility method for creating a PingOneCustomRoleResource +func CustomRole(clientInfo *connector.ClientInfo) *PingOneCustomRoleResource { + return &PingOneCustomRoleResource{ + clientInfo: clientInfo, + } +} + +func (r *PingOneCustomRoleResource) ResourceType() string { + return "pingone_custom_role" +} + +func (r *PingOneCustomRoleResource) ExportAll() (*[]connector.ImportBlock, error) { + l := logger.Get() + l.Debug().Msgf("Exporting all '%s' Resources...", r.ResourceType()) + + importBlocks := []connector.ImportBlock{} + + customRoleData, err := r.getCustomRoleData() + if err != nil { + return nil, err + } + + for customRoleId, customRoleName := range customRoleData { + commentData := map[string]string{ + "Custom Role ID": customRoleId, + "Custom Role Name": customRoleName, + "Export Environment ID": r.clientInfo.PingOneExportEnvironmentID, + "Resource Type": r.ResourceType(), + } + + importBlock := connector.ImportBlock{ + ResourceType: r.ResourceType(), + ResourceName: customRoleName, + ResourceID: fmt.Sprintf("%s/%s", r.clientInfo.PingOneExportEnvironmentID, customRoleId), + CommentInformation: common.GenerateCommentInformation(commentData), + } + + importBlocks = append(importBlocks, importBlock) + } + + return &importBlocks, nil +} + +func (r *PingOneCustomRoleResource) getCustomRoleData() (map[string]string, error) { + customRoleData := make(map[string]string) + + iter := r.clientInfo.PingOneApiClient.ManagementAPIClient.CustomAdminRolesApi.ReadAllCustomAdminRoles(r.clientInfo.PingOneContext, r.clientInfo.PingOneExportEnvironmentID).Execute() + apiObjs, err := pingone.GetManagementAPIObjectsFromIterator[management.EntityArrayEmbeddedRolesInner](iter, "ReadAllCustomAdminRoles", "GetRoles", r.ResourceType()) + if err != nil { + return nil, err + } + + for _, innerObj := range apiObjs { + if innerObj.CustomAdminRole != nil { + customRoleId, customRoleIdOk := innerObj.CustomAdminRole.GetIdOk() + customRoleName, customRoleNameOk := innerObj.CustomAdminRole.GetNameOk() + + if customRoleIdOk && customRoleNameOk { + customRoleData[*customRoleId] = *customRoleName + } + } + } + + return customRoleData, nil +} diff --git a/internal/connector/pingone/platform/resources/custom_role_test.go b/internal/connector/pingone/platform/resources/custom_role_test.go new file mode 100644 index 00000000..c2b75823 --- /dev/null +++ b/internal/connector/pingone/platform/resources/custom_role_test.go @@ -0,0 +1,33 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-cli-generator + +package resources_test + +import ( + "fmt" + "testing" + + "github.com/pingidentity/pingcli/internal/connector" + "github.com/pingidentity/pingcli/internal/testing/testutils" + "github.com/pingidentity/pingcli/internal/testing/testutils_resource" + "github.com/pingidentity/pingcli/internal/testing/testutils_resource/pingone_platform_testable_resources" +) + +func Test_CustomRole(t *testing.T) { + clientInfo := testutils.GetClientInfo(t) + + tr := pingone_platform_testable_resources.CustomRole(t, clientInfo) + + tr.CreateResource(t) + defer tr.DeleteResource(t) + + expectedImportBlocks := []connector.ImportBlock{ + { + ResourceType: tr.ExportableResource.ResourceType(), + ResourceName: tr.ResourceInfo.CreationInfo[testutils_resource.ENUM_NAME], + ResourceID: fmt.Sprintf("%s/%s", clientInfo.PingOneExportEnvironmentID, tr.ResourceInfo.CreationInfo[testutils_resource.ENUM_ID]), + }, + } + + testutils.ValidateImportBlocks(t, tr.ExportableResource, &expectedImportBlocks) +} diff --git a/internal/testing/testutils_resource/pingone_platform_testable_resources/custom_role.go b/internal/testing/testutils_resource/pingone_platform_testable_resources/custom_role.go new file mode 100644 index 00000000..1ce56052 --- /dev/null +++ b/internal/testing/testutils_resource/pingone_platform_testable_resources/custom_role.go @@ -0,0 +1,131 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-cli-generator + +package pingone_platform_testable_resources + +import ( + "testing" + + "github.com/patrickcping/pingone-go-sdk-v2/management" + "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/connector/pingone/platform/resources" + "github.com/pingidentity/pingcli/internal/testing/testutils_resource" +) + +func CustomRole(t *testing.T, clientInfo *connector.ClientInfo) *testutils_resource.TestableResource { + t.Helper() + + return &testutils_resource.TestableResource{ + ClientInfo: clientInfo, + CreateFunc: createCustomRole, + DeleteFunc: deleteCustomRole, + Dependencies: nil, + ExportableResource: resources.CustomRole(clientInfo), + } +} + +func createCustomRole(t *testing.T, clientInfo *connector.ClientInfo, resourceType string, strArgs ...string) testutils_resource.ResourceInfo { + t.Helper() + + if len(strArgs) != 0 { + t.Errorf("Unexpected number of arguments provided to createCustomRole(): %v", strArgs) + + return testutils_resource.ResourceInfo{} + } + + iter := clientInfo.PingOneApiClient.ManagementAPIClient.RolesApi.ReadAllRoles(clientInfo.PingOneContext).Execute() + apiObjs, err := pingone.GetManagementAPIObjectsFromIterator[management.EntityArrayEmbeddedRolesInner](iter, "ReadAllRoles", "GetRoles", resourceType) + if err != nil { + t.Errorf("Failed to execute PingOne client function\nError: %v", err) + + return testutils_resource.ResourceInfo{} + } + if len(apiObjs) == 0 { + t.Fatal("Failed to execute PingOne client function\n No built-in roles returned from ReadAllRoles()") + } + + var ( + roleId string + ) + + for _, role := range apiObjs { + if role.Role != nil { + if role.Role.Name != nil && *role.Role.Name == management.ENUMROLENAME_APPLICATION_OWNER { + roleId = *role.Role.Id + + break + } + } + } + + request := clientInfo.PingOneApiClient.ManagementAPIClient.CustomAdminRolesApi.CreateCustomAdminRole(clientInfo.PingOneContext, clientInfo.PingOneExportEnvironmentID) + clientStruct := management.CustomAdminRole{ + Name: "Custom Role", + ApplicableTo: []management.EnumCustomAdminRoleApplicableTo{ + management.ENUMCUSTOMADMINROLEAPPLICABLETO_ENVIRONMENT, + management.ENUMCUSTOMADMINROLEAPPLICABLETO_POPULATION, + }, + CanBeAssignedBy: []management.CustomAdminRoleCanAssignInner{ + { + Id: roleId, + }, + }, + Permissions: []management.CustomAdminRolePermissionsInner{ + { + Id: "licensing:read:license", + }, + }, + } + + request = request.CustomAdminRole(clientStruct) + + resource, response, err := request.Execute() + ok, err := common.HandleClientResponse(response, err, "CreateCustomAdminRole", resourceType) + if err != nil { + t.Errorf("Failed to execute PingOne client function\nResponse Status: %s\nResponse Body: %s\nError: %v", response.Status, response.Body, err) + + return testutils_resource.ResourceInfo{} + } + if !ok { + t.Errorf("Failed to execute PingOne client function\nResponse Status: %s\nResponse Body: %s", response.Status, response.Body) + + return testutils_resource.ResourceInfo{} + } + + return testutils_resource.ResourceInfo{ + DeletionIds: []string{ + *resource.Id, + }, + CreationInfo: map[testutils_resource.ResourceCreationInfoType]string{ + testutils_resource.ENUM_ID: *resource.Id, + testutils_resource.ENUM_NAME: resource.Name, + }, + } +} + +func deleteCustomRole(t *testing.T, clientInfo *connector.ClientInfo, resourceType string, ids ...string) { + t.Helper() + + if len(ids) != 1 { + t.Errorf("Unexpected number of arguments provided to deleteCustomRole(): %v", ids) + + return + } + + request := clientInfo.PingOneApiClient.ManagementAPIClient.CustomAdminRolesApi.DeleteCustomAdminRole(clientInfo.PingOneContext, clientInfo.PingOneExportEnvironmentID, ids[0]) + + response, err := request.Execute() + ok, err := common.HandleClientResponse(response, err, "DeleteCustomAdminRole", resourceType) + if err != nil { + t.Errorf("Failed to execute PingOne client function\nResponse Status: %s\nResponse Body: %s\nError: %v", response.Status, response.Body, err) + + return + } + if !ok { + t.Errorf("Failed to execute PingOne client function\nResponse Status: %s\nResponse Body: %s", response.Status, response.Body) + + return + } +}