Skip to content

Commit eea648e

Browse files
committed
recording tests
1 parent e5d929a commit eea648e

5 files changed

Lines changed: 628 additions & 24 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
resource "okta_admin_role_custom" "test" {
2+
label = "testAcc_replace_with_uuid"
3+
description = "workflow permission drift check"
4+
permissions = ["okta.workflows.invoke"]
5+
}

okta/services/idaas/resource_okta_admin_role_custom.go

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,13 @@ func resourceAdminRoleCustomRead(ctx context.Context, d *schema.ResourceData, me
135135
if err != nil {
136136
return diag.Errorf("failed to list permissions for custom admin role: %v", err)
137137
}
138-
_ = d.Set("permissions", flattenPermissions(perms.Permissions))
138+
// The Okta API can return both the legacy workflow permission labels
139+
// (okta.workflows.invoke / okta.workflows.read) and their newer aliases
140+
// (okta.workflows.flows.invoke / okta.workflows.flows.read). Keep whichever
141+
// form is already recorded in state so we don't introduce perpetual drift.
142+
statePermissions := utils.ConvertInterfaceToStringSetNullable(d.Get("permissions"))
143+
apiPermissions := reconcileWorkflowPermissions(statePermissions, perms.Permissions)
144+
_ = d.Set("permissions", flattenPermissions(apiPermissions))
139145
return nil
140146
}
141147

@@ -199,40 +205,52 @@ func flattenPermissions(permissions []*sdk.Permission) interface{} {
199205
for i := range permissions {
200206
arr[i] = permissions[i].Label
201207
}
202-
// The Okta API auto-expands some permissions, returning additional labels
203-
// alongside the ones that were configured. Normalize them back to the
204-
// user's intended configuration to avoid perpetual state drift.
205-
normalized := normalizePermissions(utils.ConvertInterfaceArrToStringArr(arr))
206-
arr = make([]interface{}, len(normalized))
207-
for i, label := range normalized {
208-
arr[i] = label
209-
}
210208
return schema.NewSet(schema.HashString, arr)
211209
}
212210

213-
func normalizePermissions(apiPermissions []string) []string {
214-
present := make(map[string]bool, len(apiPermissions))
215-
for _, perm := range apiPermissions {
216-
present[perm] = true
211+
// reconcileWorkflowPermissions removes a redundant workflow permission alias
212+
// from the API response when state already tracks the other form. The Okta API
213+
// exposes the same workflow permission under a legacy label and a newer alias:
214+
//
215+
// okta.workflows.invoke <-> okta.workflows.flows.invoke
216+
// okta.workflows.read <-> okta.workflows.flows.read
217+
//
218+
// Whichever form the user already has in state wins, so the opposite form is
219+
// discarded from the API response before it is written back to state.
220+
func reconcileWorkflowPermissions(statePermissions []string, apiPermissions []*sdk.Permission) []*sdk.Permission {
221+
inState := make(map[string]bool, len(statePermissions))
222+
for _, p := range statePermissions {
223+
inState[p] = true
224+
}
225+
226+
existingToNew := map[string]string{
227+
"okta.workflows.invoke": "okta.workflows.flows.invoke",
228+
"okta.workflows.read": "okta.workflows.flows.read",
217229
}
218230

219-
// Suppress an expanded permission only when its original is also present.
220-
suppressed := make(map[string]bool)
221-
if present["okta.workflows.read"] {
222-
suppressed["okta.workflows.flows.read"] = true
231+
discard := make(map[string]bool)
232+
for legacy, modern := range existingToNew {
233+
switch {
234+
case inState[legacy]:
235+
// state uses the legacy label, discard the newer alias from the API response
236+
discard[modern] = true
237+
case inState[modern]:
238+
// state uses the newer alias, discard the legacy label from the API response
239+
discard[legacy] = true
240+
}
223241
}
224-
if present["okta.workflows.invoke"] {
225-
suppressed["okta.workflows.flows.invoke"] = true
242+
if len(discard) == 0 {
243+
return apiPermissions
226244
}
227245

228-
result := make([]string, 0, len(apiPermissions))
229-
for _, perm := range apiPermissions {
230-
if suppressed[perm] {
246+
filtered := make([]*sdk.Permission, 0, len(apiPermissions))
247+
for _, p := range apiPermissions {
248+
if p != nil && discard[p.Label] {
231249
continue
232250
}
233-
result = append(result, perm)
251+
filtered = append(filtered, p)
234252
}
235-
return result
253+
return filtered
236254
}
237255

238256
func addCustomRolePermissions(ctx context.Context, client *sdk.APISupplement, roleIdOrLabel string, permissions []string) error {

okta/services/idaas/resource_okta_admin_role_custom_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,35 @@ func TestAccResourceOktaAdminRoleCustom_crud(t *testing.T) {
4343
})
4444
}
4545

46+
func TestAccResourceOktaAdminRoleCustom_workflowPermissionNoDrift(t *testing.T) {
47+
mgr := newFixtureManager("resources", resources.OktaIDaaSAdminRoleCustom, t.Name())
48+
config := mgr.GetFixtures("workflow_permissions.tf", t)
49+
resourceName := fmt.Sprintf("%s.test", resources.OktaIDaaSAdminRoleCustom)
50+
acctest.OktaResourceTest(
51+
t, resource.TestCase{
52+
PreCheck: acctest.AccPreCheck(t),
53+
ErrorCheck: testAccErrorChecks(t),
54+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactoriesForTestAcc(t),
55+
CheckDestroy: checkResourceDestroy(resources.OktaIDaaSAdminRoleCustom, doesAdminRoleCustomExist),
56+
Steps: []resource.TestStep{
57+
{
58+
Config: config,
59+
Check: resource.ComposeTestCheckFunc(
60+
resource.TestCheckResourceAttr(resourceName, "label", acctest.BuildResourceName(mgr.Seed)),
61+
resource.TestCheckResourceAttr(resourceName, "description", "workflow permission drift check"),
62+
resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"),
63+
resource.TestCheckTypeSetElemAttr(resourceName, "permissions.*", "okta.workflows.invoke"),
64+
),
65+
},
66+
{
67+
Config: config,
68+
PlanOnly: true,
69+
ExpectNonEmptyPlan: false,
70+
},
71+
},
72+
})
73+
}
74+
4675
func doesAdminRoleCustomExist(id string) (bool, error) {
4776
client := iDaaSAPIClientForTestUtil.OktaSDKSupplementClient()
4877
_, response, err := client.GetCustomRole(context.Background(), id)

0 commit comments

Comments
 (0)