Skip to content
This repository has been archived by the owner on Jun 14, 2021. It is now read-only.

Fix app user/group logic #127

Merged
merged 6 commits into from
Apr 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ This provider plugin is maintained by the Terraform team at [Articulate](https:/
- [Terraform](https://www.terraform.io/downloads.html) 0.11.x
- [Go](https://golang.org/doc/install) 1.11 (to build the provider plugin)

## Common Errors

* App User Error
```
The API returned an error: Deactivate application for user forbidden.. Causes: errorSummary: The application cannot be unassigned from the user while their group memberships grant them access, The API returned an error: Deactivate application for user forbidden.. Causes: errorSummary: The application cannot be unassigned from the user while their group memberships grant them access.
```

This requires manual intervention. A user's access must be "converted" via the UI to group access. Okta does not expose an endpoint for this.

## Disclaimer

There are particular resources and settings that are not exposed on Okta's public API. Please submit an issue if you find one not listed here.
Expand Down
32 changes: 27 additions & 5 deletions examples/okta_saml_app/saml_app_with_groups_and_users.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ resource "okta_group" "group" {
name = "testAcc_%[1]d"
}

resource "okta_group" "group1" {
name = "testAcc_%[1]d_1"
}

resource "okta_group" "group2" {
name = "testAcc_%[1]d_2"
}

resource "okta_user" "user" {
admin_roles = ["APP_ADMIN", "USER_ADMIN"]
first_name = "TestAcc"
Expand All @@ -11,16 +19,30 @@ resource "okta_user" "user" {
status = "ACTIVE"
}

resource "okta_user" "user1" {
first_name = "TestAcc1"
last_name = "blah"
login = "test-acc-1-%[1]d@testing.com"
email = "test-acc-1-%[1]d@testing.com"
status = "ACTIVE"
}

resource "okta_saml_app" "test" {
preconfigured_app = "amazon_aws"
label = "testAcc_%[1]d"

users = [{
id = "${okta_user.user.id}"
username = "${okta_user.user.email}"
}]
users = [
{
id = "${okta_user.user.id}"
username = "${okta_user.user.email}"
},
{
id = "${okta_user.user1.id}"
username = "${okta_user.user1.email}"
},
]

groups = ["${okta_group.group.id}"]
groups = ["${okta_group.group.id}", "${okta_group.group1.id}", "${okta_group.group2.id}"]

key = {
years_valid = 3
Expand Down
28 changes: 28 additions & 0 deletions examples/okta_saml_app/saml_app_with_groups_and_users_updated.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@ resource "okta_group" "group" {
name = "testAcc_%[1]d"
}

resource "okta_group" "group1" {
name = "testAcc_%[1]d_1"
}

resource "okta_group" "group2" {
name = "testAcc_%[1]d_2"
}

data "okta_group" "all" {
name = "Everyone"
}

resource "okta_user" "user" {
admin_roles = ["APP_ADMIN", "USER_ADMIN"]
first_name = "TestAcc"
Expand All @@ -11,7 +23,23 @@ resource "okta_user" "user" {
status = "ACTIVE"
}

resource "okta_user" "user1" {
first_name = "TestAcc1"
last_name = "blah"
login = "test-acc-1-%[1]d@testing.com"
email = "test-acc-1-%[1]d@testing.com"
status = "ACTIVE"
}

resource "okta_saml_app" "test" {
preconfigured_app = "amazon_aws"
label = "testAcc_%[1]d"
groups = ["${data.okta_group.all.id}"]

users = [
{
id = "${okta_user.user1.id}"
username = "${okta_user.user1.email}"
},
]
}
86 changes: 38 additions & 48 deletions okta/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,51 +245,56 @@ func handleAppGroups(id string, d *schema.ResourceData, client *okta.Client) []f
existingGroup, _, _ := client.Application.ListApplicationGroupAssignments(id, &query.Params{})
var (
asyncActionList []func() error
rawArr []interface{}
groupIdList []string
)

if arr, ok := d.GetOk("groups"); ok {
rawArr = arr.(*schema.Set).List()
for _, thing := range rawArr {
g := thing.(string)
contains := false

for _, eGroup := range existingGroup {
if eGroup.Id == g {
contains = true
break
}
}
rawArr := arr.(*schema.Set).List()
groupIdList = make([]string, len(rawArr))

for i, gID := range rawArr {
groupID := gID.(string)
groupIdList[i] = groupID

if !contains {
if !containsGroup(existingGroup, groupID) {
asyncActionList = append(asyncActionList, func() error {
_, _, err := client.Application.CreateApplicationGroupAssignment(id, g, okta.ApplicationGroupAssignment{})
_, _, err := client.Application.CreateApplicationGroupAssignment(id, groupID, okta.ApplicationGroupAssignment{})
return err
})
}
}
}

for _, group := range existingGroup {
contains := false
for _, thing := range rawArr {
g := thing.(string)
if g == group.Id {
contains = true
break
}
}

if !contains {
if !contains(groupIdList, group.Id) {
groupID := group.Id
asyncActionList = append(asyncActionList, func() error {
return suppressErrorOn404(client.Application.DeleteApplicationGroupAssignment(id, group.Id))
return suppressErrorOn404(client.Application.DeleteApplicationGroupAssignment(id, groupID))
})
}
}

return asyncActionList
}

func containsGroup(groupList []*okta.ApplicationGroupAssignment, id string) bool {
for _, group := range groupList {
if group.Id == id {
return true
}
}
return false
}

func containsUser(userList []*okta.AppUser, id string) bool {
for _, user := range userList {
if user.Id == id && user.Scope == "USER" {
return true
}
}
return false
}

// Handles the assigning of groups and users to Applications. Does so asynchronously.
func handleAppGroupsAndUsers(id string, d *schema.ResourceData, m interface{}) error {
var wg sync.WaitGroup
Expand Down Expand Up @@ -319,22 +324,14 @@ func handleAppUsers(id string, d *schema.ResourceData, client *okta.Client) []fu
userIDList = make([]string, len(users))

for i, user := range users {
u := user.(map[string]interface{})
uID := u["id"].(string)
userProfile := user.(map[string]interface{})
uID := userProfile["id"].(string)
userIDList[i] = uID
contains := false

for _, u := range existingUsers {
if u.Id == uID && u.Scope == "USER" {
contains = true
break
}
}

if !contains {
username := u["username"].(string)
if !containsUser(existingUsers, uID) {
username := userProfile["username"].(string)
// Not required
password, _ := u["password"].(string)
password, _ := userProfile["password"].(string)

asyncActionList = append(asyncActionList, func() error {
_, _, err := client.Application.AssignUserToApplication(id, okta.AppUser{
Expand All @@ -356,17 +353,10 @@ func handleAppUsers(id string, d *schema.ResourceData, client *okta.Client) []fu

for _, user := range existingUsers {
if user.Scope == "USER" {
contains := false
for _, uID := range userIDList {
if uID == user.Id {
contains = true
break
}
}

if !contains {
if !contains(userIDList, user.Id) {
userID := user.Id
asyncActionList = append(asyncActionList, func() error {
return suppressErrorOn404(client.Application.DeleteApplicationUser(id, user.Id))
return suppressErrorOn404(client.Application.DeleteApplicationUser(id, userID))
})
}
}
Expand Down
6 changes: 4 additions & 2 deletions okta/resource_saml_app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ func TestAccOktaSamlApplicationUserGroups(t *testing.T) {
ensureResourceExists(resourceName, createDoesAppExist(okta.NewSamlApplication())),
resource.TestCheckResourceAttr(resourceName, "label", buildResourceName(ri)),
resource.TestCheckResourceAttr(resourceName, "status", "ACTIVE"),
resource.TestCheckResourceAttr(resourceName, "users.#", "1"),
resource.TestCheckResourceAttr(resourceName, "groups.#", "1"),
resource.TestCheckResourceAttr(resourceName, "users.#", "2"),
resource.TestCheckResourceAttr(resourceName, "groups.#", "3"),
resource.TestCheckResourceAttr(resourceName, "key.years_valid", "3"),
),
},
Expand All @@ -198,6 +198,8 @@ func TestAccOktaSamlApplicationUserGroups(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "label", buildResourceName(ri)),
resource.TestCheckResourceAttr(resourceName, "status", "ACTIVE"),
resource.TestCheckNoResourceAttr(resourceName, "key.id"),
resource.TestCheckResourceAttr(resourceName, "groups.#", "1"),
resource.TestCheckResourceAttr(resourceName, "users.#", "1"),
),
},
},
Expand Down