Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add okta_app_oauth_post_logout_redirect_uri resource #931

Merged
merged 2 commits into from
Feb 10, 2022

Conversation

jmaness
Copy link
Contributor

@jmaness jmaness commented Jan 29, 2022

…x to prevent multiple redirect URI resources from modifying the Okta app concurrently which is rejected by Okta.

Copy MutexKV to internal package

Updated tests and docs with okta_app_oauth_post_logout_redirect_uri resource
@monde
Copy link
Collaborator

monde commented Feb 2, 2022

Thanks for the PR @jmaness . We are about to release v3.20.4. I'll review the PR with @bogdanprodan-okta next week. Since it is a new resource it rates being in a minor point release. If this gets in it would be in a v3.21.0 release.

func resourceAppOAuthPostLogoutRedirectURIDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
appID := d.Get("app_id").(string)

oktaMutexKV.Lock(appID)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmaness is your PR complete? I'm not seeing were oktaMutexKV is initialized with a call to the kv mutex constructor func NewMutexKV()

Copy link
Contributor Author

@jmaness jmaness Feb 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right! I always forget to look and see if the GH diff has collapsed the view of a file on big changes. I see it now.

app := okta.NewOpenIdConnectApplication()
err := fetchAppByID(ctx, appID, m, app)
if err != nil {
return diag.Errorf("failed to get application: %v", err)
}
if app.Id == "" || contains(app.Settings.OauthClient.RedirectUris, d.Id()) {
if app.Id == "" {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmaness also, a kv mutex is being brought into the implementation just to fix bug you pointed out:

if app.Id == "" || contains(app.Settings.OauthClient.RedirectUris, d.Id()) {

Can just fixing the bug and not adding the mutex be enough of a fix?

Copy link
Contributor Author

@jmaness jmaness Feb 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll test again without the mutex. If I recall, the issue requiring the mutex was a separate issue from the logic bug.

Issue 1

The concurrency issue requiring the mutex was caused by two terraform operations running in parallel (e.g. adding multiple redirect URI resources) where each operation performed the following:

  1. F - Fetch Okta app
  2. A - Add redirect URI to Okta app JSON object
  3. U - Update Okta app.

Since the critical section (the 3 steps above) isn't protected by a lock, it's possible for terraform to attempt create two redirect URI resources and interleave the operations like this:

  1. F1
  2. F2
  3. A1
  4. A2
  5. U2
  6. U1 (rejected by Okta, maybe by comparing E-Tags or lastUpdateDate)

Issue 2

The logic issue is that if the app contained a redirect URI, it would fail to be deleted and the error "application with id %s does not exist" would be returned. It seems like the logic should be that if the Okta app does not have the redirect URI, then the Okta app does not need to be updated, but otherwise, remove the redirect URI from the Okta app.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmaness thanks for explaining the concurrency issue and you actually did experience an issue with the critical path?

I'm asking for my own reference. I am familiar with the kv mutex you referenced from the AWS provider as I used it as reference for a mutex that is used for our rate limit accounting. Different code paths in the provider can make API calls and so having a mutex on a global data structure that is used for accounting makes sense.

I wasn't sure if the threading model in the terraform runtime would actually interrupt an executing function like resourceAppOAuthPostLogoutRedirectURIDelete and thus the need for a mutex there. Let me know what you think and have seen.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tested without using a mutex and reproduced the error. Here is an example where one of the redirect URI resources was successfully created, but the other two failed.

Example main.tf

terraform {
  required_providers {
    okta = {
      source = "okta/okta"
      version = "~> 3.20"
    }
  }
}

provider "okta" {
  org_name  = var.okta_org_name
  base_url  = var.okta_base_url
  api_token = var.okta_api_token
}

resource "okta_app_oauth_redirect_uri" "redirect_uri_1" {
  app_id = var.okta_app_id
  uri    = "https://www.example1.com"
}

resource "okta_app_oauth_redirect_uri" "redirect_uri_2" {
  app_id = var.okta_app_id
  uri    = "https://www.example2.com"
}

resource "okta_app_oauth_redirect_uri" "redirect_uri_3" {
  app_id = var.okta_app_id
  uri    = "https://www.example3.com"
}

terraform apply output

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # okta_app_oauth_redirect_uri.redirect_uri_1 will be created
  + resource "okta_app_oauth_redirect_uri" "redirect_uri_1" {
      + app_id = "0oajksrf3koG0PdWt0h7"
      + id     = (known after apply)
      + uri    = "https://www.example1.com"
    }

  # okta_app_oauth_redirect_uri.redirect_uri_2 will be created
  + resource "okta_app_oauth_redirect_uri" "redirect_uri_2" {
      + app_id = "0oajksrf3koG0PdWt0h7"
      + id     = (known after apply)
      + uri    = "https://www.example2.com"
    }

  # okta_app_oauth_redirect_uri.redirect_uri_3 will be created
  + resource "okta_app_oauth_redirect_uri" "redirect_uri_3" {
      + app_id = "0oajksrf3koG0PdWt0h7"
      + id     = (known after apply)
      + uri    = "https://www.example3.com"
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

okta_app_oauth_redirect_uri.redirect_uri_3: Creating...
okta_app_oauth_redirect_uri.redirect_uri_2: Creating...
okta_app_oauth_redirect_uri.redirect_uri_1: Creating...
okta_app_oauth_redirect_uri.redirect_uri_3: Creation complete after 1s [id=https://www.example3.com]
╷
│ Error: failed to create redirect URI: the API returned an error: Operation on application settings failed.. Causes: errorSummary: The requested action cannot be supported for this application., Status: 403 Forbidden
│
│   with okta_app_oauth_redirect_uri.redirect_uri_1,
│   on main.tf line 16, in resource "okta_app_oauth_redirect_uri" "redirect_uri_1":
│   16: resource "okta_app_oauth_redirect_uri" "redirect_uri_1" {
│
╵
╷
│ Error: failed to create redirect URI: the API returned an error: Operation on application settings failed.. Causes: errorSummary: The requested action cannot be supported for this application., Status: 403 Forbidden
│
│   with okta_app_oauth_redirect_uri.redirect_uri_2,
│   on main.tf line 21, in resource "okta_app_oauth_redirect_uri" "redirect_uri_2":
│   21: resource "okta_app_oauth_redirect_uri" "redirect_uri_2" {
│
╵

Verbose logging output including the Okta API response (with some data redacted)

2022-02-08T19:04:58.996-0500 [INFO]  provider.terraform-provider-okta: 2022/02/08 19:04:58 [DEBUG] Okta API Response Details:
---[ RESPONSE ]--------------------------------------
HTTP/2.0 403 Forbidden
...
{
 "errorCode": "E0000039",
 "errorSummary": "Operation on application settings failed.",
 "errorLink": "E0000039",
 "errorId": "oaeVKnbk1sMSomEQYlVuLEsOQ",
 "errorCauses": [
  {
   "errorSummary": "The requested action cannot be supported for this application."
  }
 ]
}

With a mutex, the output is:

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # okta_app_oauth_redirect_uri.redirect_uri_1 will be created
  + resource "okta_app_oauth_redirect_uri" "redirect_uri_1" {
      + app_id = "0oajksrf3koG0PdWt0h7"
      + id     = (known after apply)
      + uri    = "https://www.example1.com"
    }

  # okta_app_oauth_redirect_uri.redirect_uri_2 will be created
  + resource "okta_app_oauth_redirect_uri" "redirect_uri_2" {
      + app_id = "0oajksrf3koG0PdWt0h7"
      + id     = (known after apply)
      + uri    = "https://www.example2.com"
    }

  # okta_app_oauth_redirect_uri.redirect_uri_3 will be created
  + resource "okta_app_oauth_redirect_uri" "redirect_uri_3" {
      + app_id = "0oajksrf3koG0PdWt0h7"
      + id     = (known after apply)
      + uri    = "https://www.example3.com"
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

okta_app_oauth_redirect_uri.redirect_uri_1: Creating...
okta_app_oauth_redirect_uri.redirect_uri_3: Creating...
okta_app_oauth_redirect_uri.redirect_uri_2: Creating...
okta_app_oauth_redirect_uri.redirect_uri_1: Creation complete after 0s [id=https://www.example1.com]
okta_app_oauth_redirect_uri.redirect_uri_3: Creation complete after 1s [id=https://www.example3.com]
okta_app_oauth_redirect_uri.redirect_uri_2: Creation complete after 2s [id=https://www.example2.com]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also tested destroy of multiple redirect URI resources with and without a mutex and reproduced the error when not using a mutex.

Without a mutex

okta_app_oauth_redirect_uri.redirect_uri_1: Refreshing state... [id=https://www.example1.com]
okta_app_oauth_redirect_uri.redirect_uri_3: Refreshing state... [id=https://www.example3.com]
okta_app_oauth_redirect_uri.redirect_uri_2: Refreshing state... [id=https://www.example2.com]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # okta_app_oauth_redirect_uri.redirect_uri_1 will be destroyed
  - resource "okta_app_oauth_redirect_uri" "redirect_uri_1" {
      - app_id = "0oajksrf3koG0PdWt0h7" -> null
      - id     = "https://www.example1.com" -> null
      - uri    = "https://www.example1.com" -> null
    }

  # okta_app_oauth_redirect_uri.redirect_uri_2 will be destroyed
  - resource "okta_app_oauth_redirect_uri" "redirect_uri_2" {
      - app_id = "0oajksrf3koG0PdWt0h7" -> null
      - id     = "https://www.example2.com" -> null
      - uri    = "https://www.example2.com" -> null
    }

  # okta_app_oauth_redirect_uri.redirect_uri_3 will be destroyed
  - resource "okta_app_oauth_redirect_uri" "redirect_uri_3" {
      - app_id = "0oajksrf3koG0PdWt0h7" -> null
      - id     = "https://www.example3.com" -> null
      - uri    = "https://www.example3.com" -> null
    }

Plan: 0 to add, 0 to change, 3 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

okta_app_oauth_redirect_uri.redirect_uri_2: Destroying... [id=https://www.example2.com]
okta_app_oauth_redirect_uri.redirect_uri_3: Destroying... [id=https://www.example3.com]
okta_app_oauth_redirect_uri.redirect_uri_1: Destroying... [id=https://www.example1.com]
okta_app_oauth_redirect_uri.redirect_uri_3: Destruction complete after 1s
╷
│ Error: failed to delete redirect URI: the API returned an error: Operation on application settings failed.. Causes: errorSummary: The requested action cannot be supported for this application., Status: 403 Forbidden
│
│
╵
╷
│ Error: failed to delete redirect URI: the API returned an error: Operation on application settings failed.. Causes: errorSummary: The requested action cannot be supported for this application., Status: 403 Forbidden
│
│
╵

With a mutex

okta_app_oauth_redirect_uri.redirect_uri_2: Refreshing state... [id=https://www.example2.com]
okta_app_oauth_redirect_uri.redirect_uri_1: Refreshing state... [id=https://www.example1.com]
okta_app_oauth_redirect_uri.redirect_uri_3: Refreshing state... [id=https://www.example3.com]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # okta_app_oauth_redirect_uri.redirect_uri_1 will be destroyed
  - resource "okta_app_oauth_redirect_uri" "redirect_uri_1" {
      - app_id = "0oajksrf3koG0PdWt0h7" -> null
      - id     = "https://www.example1.com" -> null
      - uri    = "https://www.example1.com" -> null
    }

  # okta_app_oauth_redirect_uri.redirect_uri_2 will be destroyed
  - resource "okta_app_oauth_redirect_uri" "redirect_uri_2" {
      - app_id = "0oajksrf3koG0PdWt0h7" -> null
      - id     = "https://www.example2.com" -> null
      - uri    = "https://www.example2.com" -> null
    }

  # okta_app_oauth_redirect_uri.redirect_uri_3 will be destroyed
  - resource "okta_app_oauth_redirect_uri" "redirect_uri_3" {
      - app_id = "0oajksrf3koG0PdWt0h7" -> null
      - id     = "https://www.example3.com" -> null
      - uri    = "https://www.example3.com" -> null
    }

Plan: 0 to add, 0 to change, 3 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

okta_app_oauth_redirect_uri.redirect_uri_1: Destroying... [id=https://www.example1.com]
okta_app_oauth_redirect_uri.redirect_uri_2: Destroying... [id=https://www.example2.com]
okta_app_oauth_redirect_uri.redirect_uri_3: Destroying... [id=https://www.example3.com]
okta_app_oauth_redirect_uri.redirect_uri_3: Destruction complete after 1s
okta_app_oauth_redirect_uri.redirect_uri_1: Destruction complete after 1s
okta_app_oauth_redirect_uri.redirect_uri_2: Destruction complete after 2s

Destroy complete! Resources: 3 destroyed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without any of the code changes in this PR, here's the output that demonstrates both the concurrency issue and the logic issue (in the destroy case):

$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # okta_app_oauth_redirect_uri.redirect_uri_1 will be created
  + resource "okta_app_oauth_redirect_uri" "redirect_uri_1" {
      + app_id = "0oajksrf3koG0PdWt0h7"
      + id     = (known after apply)
      + uri    = "https://www.example1.com"
    }

  # okta_app_oauth_redirect_uri.redirect_uri_2 will be created
  + resource "okta_app_oauth_redirect_uri" "redirect_uri_2" {
      + app_id = "0oajksrf3koG0PdWt0h7"
      + id     = (known after apply)
      + uri    = "https://www.example2.com"
    }

  # okta_app_oauth_redirect_uri.redirect_uri_3 will be created
  + resource "okta_app_oauth_redirect_uri" "redirect_uri_3" {
      + app_id = "0oajksrf3koG0PdWt0h7"
      + id     = (known after apply)
      + uri    = "https://www.example3.com"
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

okta_app_oauth_redirect_uri.redirect_uri_2: Creating...
okta_app_oauth_redirect_uri.redirect_uri_1: Creating...
okta_app_oauth_redirect_uri.redirect_uri_3: Creating...
okta_app_oauth_redirect_uri.redirect_uri_2: Creation complete after 1s [id=https://www.example2.com]
╷
│ Error: failed to create redirect URI: the API returned an error: Operation on application settings failed.. Causes: errorSummary: The requested action cannot be supported for this application., Status: 403 Forbidden
│
│   with okta_app_oauth_redirect_uri.redirect_uri_1,
│   on main.tf line 16, in resource "okta_app_oauth_redirect_uri" "redirect_uri_1":
│   16: resource "okta_app_oauth_redirect_uri" "redirect_uri_1" {
│
╵
╷
│ Error: failed to create redirect URI: the API returned an error: Operation on application settings failed.. Causes: errorSummary: The requested action cannot be supported for this application., Status: 403 Forbidden
│
│   with okta_app_oauth_redirect_uri.redirect_uri_3,
│   on main.tf line 26, in resource "okta_app_oauth_redirect_uri" "redirect_uri_3":
│   26: resource "okta_app_oauth_redirect_uri" "redirect_uri_3" {
│
╵
$ terraform destroy
okta_app_oauth_redirect_uri.redirect_uri_2: Refreshing state... [id=https://www.example2.com]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # okta_app_oauth_redirect_uri.redirect_uri_2 will be destroyed
  - resource "okta_app_oauth_redirect_uri" "redirect_uri_2" {
      - app_id = "0oajksrf3koG0PdWt0h7" -> null
      - id     = "https://www.example2.com" -> null
      - uri    = "https://www.example2.com" -> null
    }

Plan: 0 to add, 0 to change, 1 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

okta_app_oauth_redirect_uri.redirect_uri_2: Destroying... [id=https://www.example2.com]
╷
│ Error: application with id 0oajksrf3koG0PdWt0h7 does not exist
│
│
╵

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the follow up details @jmaness , I'll put some time into this PR tomorrow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started looking around at other places, and likely, the same mutex might need to be added in resource_okta_app_saml_app_settings.go, resource_okta_app_saml.go, and a number of others (probably around a dozen or so). Basically, any place that fetches or updates the Okta OAuth app. It might make sense to do that in other PRs though.

@monde monde merged commit 2d06025 into okta:master Feb 10, 2022
@monde
Copy link
Collaborator

monde commented Feb 10, 2022

will be in v3.21.0 today

@monde monde mentioned this pull request Feb 10, 2022
@jmaness jmaness deleted the feature/add_post_logout_redirect_uris branch February 10, 2022 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants