Skip to content

Commit dc95a36

Browse files
committed
Add support to Roles
1 parent b68358a commit dc95a36

File tree

13 files changed

+514
-8
lines changed

13 files changed

+514
-8
lines changed

iamctl/cmd/cli/exportAll.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
claims "github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/claims"
2626
identityproviders "github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/identityProviders"
2727
oidcScopes "github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/oidcScopes"
28+
roles "github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/roles"
2829
userstores "github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/userStores"
2930
"github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/utils"
3031
)
@@ -49,6 +50,7 @@ var exportAllCmd = &cobra.Command{
4950
utils.APPLICATIONS: applications.ExportAll,
5051
utils.USERSTORES: userstores.ExportAll,
5152
utils.OIDC_SCOPES: oidcScopes.ExportAll,
53+
utils.ROLES: roles.ExportAll,
5254
}
5355

5456
for _, resourceType := range utils.ResourceOrder {

iamctl/cmd/cli/importAll.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
claims "github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/claims"
2626
identityproviders "github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/identityProviders"
2727
oidcScopes "github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/oidcScopes"
28+
roles "github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/roles"
2829
userstores "github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/userStores"
2930
"github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/utils"
3031
)
@@ -48,6 +49,7 @@ var importAllCmd = &cobra.Command{
4849
utils.APPLICATIONS: applications.ImportAll,
4950
utils.USERSTORES: userstores.ImportAll,
5051
utils.OIDC_SCOPES: oidcScopes.ImportAll,
52+
utils.ROLES: roles.ImportAll,
5153
}
5254

5355
for _, resourceType := range utils.ResourceOrder {

iamctl/pkg/roles/export.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/**
2+
* Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
package roles
20+
21+
import (
22+
"fmt"
23+
"log"
24+
"os"
25+
"path/filepath"
26+
27+
"github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/utils"
28+
)
29+
30+
func ExportAll(exportFilePath string, format string) {
31+
32+
log.Println("Exporting roles...")
33+
exportFilePath = filepath.Join(exportFilePath, utils.ROLES.String())
34+
35+
if utils.IsResourceTypeExcluded(utils.ROLES) {
36+
return
37+
}
38+
roles, err := getRoleList()
39+
if err != nil {
40+
log.Println("Error: when exporting roles.", err)
41+
return
42+
}
43+
44+
if _, err := os.Stat(exportFilePath); os.IsNotExist(err) {
45+
os.MkdirAll(exportFilePath, 0700)
46+
} else {
47+
if utils.TOOL_CONFIGS.AllowDelete {
48+
deployedRoleNames := getDeployedRoleLocalFileNames(roles)
49+
utils.RemoveDeletedLocalResources(exportFilePath, deployedRoleNames)
50+
}
51+
}
52+
53+
for _, r := range roles {
54+
if !utils.IsResourceExcluded(r.DisplayName, utils.TOOL_CONFIGS.RoleConfigs) {
55+
log.Println("Exporting role:", r.DisplayName)
56+
57+
err := exportRole(r, exportFilePath, format)
58+
if err != nil {
59+
utils.UpdateFailureSummary(utils.ROLES, r.DisplayName)
60+
log.Printf("Error while exporting role: %s. %s", r.DisplayName, err)
61+
} else {
62+
utils.UpdateSuccessSummary(utils.ROLES, utils.EXPORT)
63+
log.Println("Role exported successfully:", r.DisplayName)
64+
}
65+
}
66+
}
67+
68+
}
69+
70+
func exportRole(r role, outputDirPath string, formatString string) error {
71+
72+
roleData, err := utils.SendGetRequest(utils.ROLES, r.Id)
73+
if err != nil {
74+
return fmt.Errorf("error while getting role: %w", err)
75+
}
76+
roleData, err = utils.RemoveResponseFields(roleData, "id")
77+
if err != nil {
78+
return fmt.Errorf("error while processing response fields: %w", err)
79+
}
80+
81+
format := utils.FormatFromString(formatString)
82+
exportedFileName := utils.GetExportedFilePath(outputDirPath, escapeRoleName(r.DisplayName), format)
83+
84+
roleKeywordMapping := getRoleKeywordMapping(r.DisplayName)
85+
modifiedRole, err := utils.ProcessExportedData(roleData, exportedFileName, format, roleKeywordMapping, utils.ROLES)
86+
if err != nil {
87+
return fmt.Errorf("error while processing exported content: %w", err)
88+
}
89+
90+
modifiedFile, err := utils.Serialize(modifiedRole, format, utils.ROLES)
91+
if err != nil {
92+
return fmt.Errorf("error while serializing role: %w", err)
93+
}
94+
95+
err = os.WriteFile(exportedFileName, modifiedFile, 0644)
96+
if err != nil {
97+
return fmt.Errorf("error when writing exported content to file: %w", err)
98+
}
99+
100+
return nil
101+
}

iamctl/pkg/roles/import.go

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/**
2+
* Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
package roles
20+
21+
import (
22+
"fmt"
23+
"log"
24+
"os"
25+
"path/filepath"
26+
27+
"github.com/wso2-extensions/identity-tools-cli/iamctl/pkg/utils"
28+
)
29+
30+
func ImportAll(inputDirPath string) {
31+
32+
log.Println("Importing roles...")
33+
importFilePath := filepath.Join(inputDirPath, utils.ROLES.String())
34+
35+
if utils.IsResourceTypeExcluded(utils.ROLES) {
36+
return
37+
}
38+
if _, err := os.Stat(importFilePath); os.IsNotExist(err) {
39+
log.Println("No roles to import.")
40+
return
41+
}
42+
43+
existingRoleList, err := getRoleList()
44+
if err != nil {
45+
log.Println("Error retrieving the deployed role list:", err)
46+
return
47+
}
48+
49+
files, err := os.ReadDir(importFilePath)
50+
if err != nil {
51+
log.Println("Error importing roles:", err)
52+
return
53+
}
54+
55+
if utils.TOOL_CONFIGS.AllowDelete {
56+
removeDeletedDeployedRoles(files, existingRoleList)
57+
}
58+
59+
for _, file := range files {
60+
roleFilePath := filepath.Join(importFilePath, file.Name())
61+
fileInfo := utils.GetFileInfo(roleFilePath)
62+
displayName := unescapeName(fileInfo.ResourceName)
63+
64+
if !utils.IsResourceExcluded(displayName, utils.TOOL_CONFIGS.RoleConfigs) {
65+
roleExists := isRoleExists(displayName, existingRoleList)
66+
err := importRole(displayName, roleExists, roleFilePath, existingRoleList)
67+
if err != nil {
68+
log.Println("Error importing role:", err)
69+
utils.UpdateFailureSummary(utils.ROLES, displayName)
70+
}
71+
}
72+
}
73+
}
74+
75+
func importRole(displayName string, roleExists bool, importFilePath string, existingRoles []role) error {
76+
77+
format, err := utils.FormatFromExtension(filepath.Ext(importFilePath))
78+
if err != nil {
79+
return fmt.Errorf("unsupported file format for role: %w", err)
80+
}
81+
82+
fileBytes, err := os.ReadFile(importFilePath)
83+
if err != nil {
84+
return fmt.Errorf("error when reading the file for role: %w", err)
85+
}
86+
87+
roleKeywordMapping := getRoleKeywordMapping(displayName)
88+
modifiedFileData := utils.ReplaceKeywords(string(fileBytes), roleKeywordMapping)
89+
90+
if !roleExists {
91+
return createRole([]byte(modifiedFileData), format, displayName)
92+
}
93+
94+
roleId := getRoleIdByDisplayName(displayName, existingRoles)
95+
return updateRole(roleId, []byte(modifiedFileData), format, displayName)
96+
}
97+
98+
func createRole(requestBody []byte, format utils.Format, displayName string) error {
99+
100+
log.Println("Creating new role:", displayName)
101+
102+
jsonBody, err := utils.PrepareJSONRequestBody(requestBody, format, utils.ROLES)
103+
if err != nil {
104+
return err
105+
}
106+
107+
resp, err := utils.SendPostRequest(utils.ROLES, jsonBody)
108+
if err != nil {
109+
return fmt.Errorf("error when creating role: %w", err)
110+
}
111+
defer resp.Body.Close()
112+
113+
utils.UpdateSuccessSummary(utils.ROLES, utils.IMPORT)
114+
log.Println("Role created successfully:", displayName)
115+
return nil
116+
}
117+
118+
func updateRole(roleId string, requestBody []byte, format utils.Format, displayName string) error {
119+
120+
log.Println("Updating role:", displayName)
121+
122+
patchBody, err := buildRolePermissionsPatchBody(requestBody, format)
123+
if err != nil {
124+
return fmt.Errorf("error building patch body for role: %w", err)
125+
}
126+
127+
resp, err := utils.SendPatchRequest(utils.ROLES, roleId, patchBody)
128+
if err != nil {
129+
return fmt.Errorf("error when updating role: %w", err)
130+
}
131+
defer resp.Body.Close()
132+
133+
utils.UpdateSuccessSummary(utils.ROLES, utils.UPDATE)
134+
log.Println("Role updated successfully:", displayName)
135+
return nil
136+
}
137+
138+
func removeDeletedDeployedRoles(localFiles []os.DirEntry, deployedRoles []role) {
139+
140+
if len(deployedRoles) == 0 {
141+
return
142+
}
143+
144+
localResourceNames := make(map[string]struct{})
145+
for _, file := range localFiles {
146+
resourceName := utils.GetFileInfo(file.Name()).ResourceName
147+
localResourceNames[resourceName] = struct{}{}
148+
}
149+
150+
for _, r := range deployedRoles {
151+
fileName := escapeRoleName(r.DisplayName)
152+
if _, existsLocally := localResourceNames[fileName]; existsLocally {
153+
continue
154+
}
155+
if utils.IsResourceExcluded(r.DisplayName, utils.TOOL_CONFIGS.RoleConfigs) {
156+
log.Println("Role is excluded from deletion:", r.DisplayName)
157+
continue
158+
}
159+
160+
log.Printf("Role: %s not found locally. Deleting role.\n", r.DisplayName)
161+
if err := utils.SendDeleteRequest(r.Id, utils.ROLES); err != nil {
162+
utils.UpdateFailureSummary(utils.ROLES, r.DisplayName)
163+
log.Println("Error deleting role:", r.DisplayName, err)
164+
} else {
165+
utils.UpdateSuccessSummary(utils.ROLES, utils.DELETE)
166+
}
167+
}
168+
}

0 commit comments

Comments
 (0)