Skip to content

Commit

Permalink
Merge pull request #1159 from okta/exitcode0_data_okta_user
Browse files Browse the repository at this point in the history
DS okta_user and okta_users bug fixes and improvements
  • Loading branch information
monde authored Jun 9, 2022
2 parents 82febe3 + ba654ff commit 8380385
Show file tree
Hide file tree
Showing 8 changed files with 465 additions and 132 deletions.
4 changes: 4 additions & 0 deletions examples/okta_user/datasource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ data "okta_user" "compound_search" {
value = okta_user.other.last_name
}

delay_read_seconds = 2

depends_on = [
okta_user.test,
okta_user.other
Expand All @@ -112,6 +114,8 @@ data "okta_user" "expression_search" {
expression = "profile.array123 eq \"feature\" and (created gt \"2021-01-01T00:00:00.000Z\")"
}

delay_read_seconds = 2

depends_on = [
okta_user.test,
okta_user.other
Expand Down
41 changes: 27 additions & 14 deletions okta/data_source_okta_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package okta
import (
"context"
"fmt"
"strconv"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -77,11 +79,26 @@ func dataSourceUser() *schema.Resource {
ValidateDiagFunc: elemInSlice([]string{"and", "or"}),
Description: "Search operator used when joining mulitple search clauses",
},
"delay_read_seconds": {
Type: schema.TypeString,
Optional: true,
Description: "Force delay of the user read by N seconds. Useful when eventual consistency of user information needs to be allowed for.",
},
}),
}
}

func dataSourceUserRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
if n, ok := d.GetOk("delay_read_seconds"); ok {
delay, err := strconv.Atoi(n.(string))
if err == nil {
logger(m).Info("delaying user read by ", delay, " seconds")
time.Sleep(time.Duration(delay) * time.Second)
} else {
logger(m).Warn("user read delay value ", n, " is not an integer")
}
}

client := getOktaClientFromMetadata(m)
var user *okta.User
var err error
Expand Down Expand Up @@ -112,25 +129,21 @@ func dataSourceUserRead(ctx context.Context, d *schema.ResourceData, m interface
return diag.Errorf("failed to set user's properties: %v", err)
}

skip := false
if val := d.Get("skip_roles"); val != nil {
skip = val.(bool)
}
if !skip {
err = setAdminRoles(ctx, d, m)
if err != nil {
return diag.Errorf("failed to set user's admin roles: %v", err)
if skip, ok := val.(bool); ok && !skip {
err = setAdminRoles(ctx, d, m)
if err != nil {
return diag.Errorf("failed to set user's admin roles: %v", err)
}
}
}

skip = false
if val := d.Get("skip_groups"); val != nil {
skip = val.(bool)
}
if !skip {
err = setAllGroups(ctx, d, client)
if err != nil {
return diag.Errorf("failed to set user's groups: %v", err)
if skip, ok := val.(bool); ok && !skip {
err = setAllGroups(ctx, d, client)
if err != nil {
return diag.Errorf("failed to set user's groups: %v", err)
}
}
}

Expand Down
147 changes: 143 additions & 4 deletions okta/data_source_okta_user_test.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
package okta

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccOktaDataSourceUser_read(t *testing.T) {
func TestAccDataSourceOktaUser_read(t *testing.T) {
ri := acctest.RandInt()
mgr := newFixtureManager(user)
baseConfig := mgr.GetFixtures("datasource.tf", ri, t)
createUserConfig := mgr.GetFixtures("datasource_create_user.tf", ri, t)

// NOTE: The ACC tests on the datasource.tf can flap as sometimes these
// tests can run faster than the Okta org becoming eventually consistent.
//
// NOTE: eliminated previous flapping issues when delay_read_seconds was added to okta_user
// TF_ACC=1 go test -tags unit -mod=readonly -test.v -run ^TestAccOktaDataSourceUser_read$
resource.Test(t, resource.TestCase{
PreCheck: func() {
Expand Down Expand Up @@ -62,3 +62,142 @@ func TestAccOktaDataSourceUser_read(t *testing.T) {
},
})
}

// TestAccDataSourceOktaUser_SkipAdminRoles pertains to https://github.com/okta/terraform-provider-okta/pull/1137 and https://github.com/okta/terraform-provider-okta/issues/1014
func TestAccDataSourceOktaUser_SkipAdminRoles(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
ProviderFactories: testAccProvidersFactories,
Steps: []resource.TestStep{
{
Config: testOktaUserRolesGroupsConfig(false, true),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.okta_user.test", "admin_roles.#", "0"), // skipped
resource.TestCheckResourceAttr("data.okta_user.test", "group_memberships.#", "2"), // Everyone, A Group
),
},
},
})
}

// TestAccDataSourceOktaUser_SkipGroups pertains to https://github.com/okta/terraform-provider-okta/pull/1137 and https://github.com/okta/terraform-provider-okta/issues/1014
func TestAccDataSourceOktaUser_SkipGroups(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
ProviderFactories: testAccProvidersFactories,
Steps: []resource.TestStep{
{
Config: testOktaUserRolesGroupsConfig(true, false),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.okta_user.test", "admin_roles.#", "2"), // SUPER_ADMIN, APP_ADMIN
resource.TestCheckResourceAttr("data.okta_user.test", "group_memberships.#", "0"), // skipped
),
},
},
})
}

// TestAccDataSourceOktaUser_SkipGroupsSkipRoles pertains to https://github.com/okta/terraform-provider-okta/pull/1137 and https://github.com/okta/terraform-provider-okta/issues/1014
func TestAccDataSourceOktaUser_SkipGroupsSkipRoles(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
ProviderFactories: testAccProvidersFactories,
Steps: []resource.TestStep{
{
Config: testOktaUserRolesGroupsConfig(true, true),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.okta_user.test", "admin_roles.#", "0"), // skipped
resource.TestCheckResourceAttr("data.okta_user.test", "group_memberships.#", "0"), // skipped
),
},
},
})
}

// TestAccDataSourceOktaUser_NoSkips pertains to https://github.com/okta/terraform-provider-okta/pull/1137 and https://github.com/okta/terraform-provider-okta/issues/1014
func TestAccDataSourceOktaUser_NoSkips(t *testing.T) {
allAdminRolesRegexp, _ := regexp.Compile("APP_ADMIN, SUPER_ADMIN")
allGroupMembershipsRegexp, _ := regexp.Compile("00g[a-z,A-Z,0-9]{17}, 00g[a-z,A-Z,0-9]{17}")
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
ProviderFactories: testAccProvidersFactories,
Steps: []resource.TestStep{
{
Config: testOktaUserRolesGroupsConfig(false, false),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.okta_user.test", "admin_roles.#", "2"), // SUPER_ADMIN, APP_ADMIN
resource.TestCheckResourceAttr("data.okta_user.test", "group_memberships.#", "2"), // Everyone, A Group
resource.TestMatchOutput("output_admin_roles", allAdminRolesRegexp),
resource.TestMatchOutput("output_group_memberships", allGroupMembershipsRegexp),
),
},
},
})
}

func testOktaUserRolesGroupsConfig(skipGroups, skipRoles bool) string {
prepend := `
resource "okta_group" "test" {
name = "Example"
description = "A Group"
}
resource "okta_user" "test" {
first_name = "TestAcc"
last_name = "Smith"
login = "testAcc-replace_with_uuid@example.com"
email = "testAcc-replace_with_uuid@example.com"
lifecycle {
ignore_changes = [admin_roles]
}
}
resource "okta_user_admin_roles" "test" {
user_id = okta_user.test.id
admin_roles = [
"SUPER_ADMIN",
"APP_ADMIN",
]
}
resource "okta_user_group_memberships" "test" {
user_id = okta_user.test.id
groups = [
okta_group.test.id,
]
}
data "okta_user" "test" {
user_id = okta_user.test.id
`

var clause string
if skipGroups {
clause = " skip_groups = true"
}
if skipRoles {
clause = fmt.Sprintf("%s\n skip_roles = true\n", clause)
}

append := `
depends_on = [
okta_user_admin_roles.test,
okta_user_admin_roles.test,
okta_user_group_memberships.test,
]
}
output "output_admin_roles" {
value = join(", ", data.okta_user.test.admin_roles)
}
output "output_group_memberships" {
value = join(", ", data.okta_user.test.group_memberships)
}
`

return fmt.Sprintf("%s%s%s", prepend, clause, append)
}
36 changes: 34 additions & 2 deletions okta/data_source_okta_users.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"fmt"
"hash/crc32"
"strconv"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand All @@ -27,6 +29,12 @@ func dataSourceUsers() *schema.Resource {
Default: false,
Description: "Fetch group memberships for each user",
},
"include_roles": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Fetch user roles for each user",
},
"search": {
Type: schema.TypeSet,
Optional: true,
Expand Down Expand Up @@ -56,11 +64,26 @@ func dataSourceUsers() *schema.Resource {
ValidateDiagFunc: elemInSlice([]string{"and", "or"}),
Description: "Search operator used when joining mulitple search clauses",
},
"delay_read_seconds": {
Type: schema.TypeString,
Optional: true,
Description: "Force delay of the users read by N seconds. Useful when eventual consistency of users information needs to be allowed for.",
},
},
}
}

func dataSourceUsersRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
if n, ok := d.GetOk("delay_read_seconds"); ok {
delay, err := strconv.Atoi(n.(string))
if err == nil {
logger(m).Info("delaying users read by ", delay, " seconds")
time.Sleep(time.Duration(delay) * time.Second)
} else {
logger(m).Warn("users read delay value ", n, " is not an integer")
}
}

var (
users []*okta.User
id string
Expand All @@ -84,20 +107,29 @@ func dataSourceUsersRead(ctx context.Context, d *schema.ResourceData, m interfac
return diag.Errorf("failed to list users: %v", err)
}
d.SetId(id)
shouldGetGroups := d.Get("include_groups").(bool)
includeGroups := d.Get("include_groups").(bool)
includeRoles := d.Get("include_roles").(bool)
arr := make([]map[string]interface{}, len(users))
for i, user := range users {
rawMap := flattenUser(user)
rawMap["id"] = user.Id
if shouldGetGroups {
if includeGroups {
groups, err := getGroupsForUser(ctx, user.Id, client)
if err != nil {
return diag.Errorf("failed to list users: %v", err)
}
rawMap["group_memberships"] = groups
}
if includeRoles {
roles, err := getAdminRoles(ctx, user.Id, client)
if err != nil {
return diag.Errorf("failed to set user's admin roles: %v", err)
}
rawMap["admin_roles"] = roles
}
arr[i] = rawMap
}

_ = d.Set("users", arr)
return nil
}
Expand Down
Loading

0 comments on commit 8380385

Please sign in to comment.