Skip to content

Commit

Permalink
1.50.0 Updates (#275)
Browse files Browse the repository at this point in the history
* Add refresh_token_expiration_policy to jwt_configuration

* Add refresh_token_expiration_policy to tenant

* Allow key rotations without destroying SAMLv2 IDP resources

* Update resource_fusionauth_application_helpers.go

Add some additional configuration options for JWT configuration of application, note that it seems that currently there is no way to set `RefreshTokenUsagePolicy` on application using API (I guess this is a bug and the API should be extended or docs should be updated to allow for that, it is possible to be changed only from UI)

* Added the consent themed page for the 1.50 release (#274)

* upgraded to 1.50 version of client lib

* added note on how to run tests

* added consent themed page

* Fix missing confirmation_required field in test

* Add username claim to samlv2 resource (#267)

* add username claim to samlv2 resource

* update docs to match api

* Add helpers

* Update github actions

* Update linter
Refactor out deprecated methods

* Use consistent naming

* Update application refresh token configuration fields

* Docs, cleanup

* Remove duplicate check

* Adds unique_id_claim to the idp_samlv2 resource (#246)

* Fix formatting

* Update password length example and docs to reflect maximum length when using bcrypt.

---------

Co-authored-by: Hamza Baig <[email protected]>
Co-authored-by: Stuart Auld <[email protected]>
Co-authored-by: zaalbarxx <[email protected]>
Co-authored-by: Dan Moore <[email protected]>
Co-authored-by: Chris Lingwood <[email protected]>
Co-authored-by: David Bebb <[email protected]>
  • Loading branch information
7 people authored May 24, 2024
1 parent 9855df5 commit 3e8258c
Show file tree
Hide file tree
Showing 20 changed files with 394 additions and 353 deletions.
9 changes: 6 additions & 3 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ jobs:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.20'
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v6
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: latest
version: v1.58
# Optional: working directory, useful for monorepos
# working-directory: somedir
# Optional: golangci-lint command line arguments.
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Unshallow
run: git fetch --prune --unshallow
- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@v5
with:
go-version: 1.18
go-version: 1.20
- name: Import GPG key
id: import_gpg
uses: paultyng/ghaction-import-gpg@v2.1.0
uses: crazy-max/ghaction-import-gpg@v6
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
PASSPHRASE: ${{ secrets.PASSPHRASE }}
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --rm-dist
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ Please continue to use and provide feedback on this provider as you have in the
* webhook
* tenants

## Testing

Please add tests to the relevant files.

To run tests:

```
cd fusionauth
go test
```

## Known issues

If you do not specify permissions when adding an API key, you will get a key created that has no permissions. See the following issues for more details.
Expand Down
3 changes: 3 additions & 0 deletions docs/resources/application.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ resource "fusionauth_application" "Forum" {
- `access_token_id` - (Optional) The Id of the signing key used to sign the access token.
- `enabled` - (Optional) Indicates if this application is using the JWT configuration defined here or the global JWT configuration defined by the System Configuration. If this is false the signing algorithm configured in the System Configuration will be used. If true the signing algorithm defined in this application will be used.
- `id_token_key_id` - (Optional) The Id of the signing key used to sign the Id token.
- `refresh_token_sliding_window_maximum_time_to_live_in_minutes` - (Optional) The maximum lifetime of a refresh token when using a refresh token expiration policy of `SlidingWindowWithMaximumLifetime`. Value must be greater than 0.
- `refresh_token_ttl_minutes` - (Optional) The length of time in minutes the JWT refresh token will live before it is expired and is not able to be exchanged for a JWT.
- `refresh_token_expiration_policy` - (Optional) The Refresh Token expiration policy. The possible values are: Fixed - the expiration is calculated from the time the token is issued. SlidingWindow - the expiration is calculated from the last time the token was used. SlidingWindowWithMaximumLifetime - the expiration is calculated from the last time the token was used, or until `refresh_token_sliding_window_maximum_time_to_live_in_minutes` is reached.
- `refresh_token_usage_policy` - (Optional) The refresh token usage policy. The following are valid values: Reusable - the token does not change after it was issued. OneTimeUse - the token value will be changed each time the token is used to refresh a JWT. The client must store the new value after each usage. Defaults to Reusable.
- `ttl_seconds` - (Optional) The length of time in seconds the JWT will live before it is expired and no longer valid.
* `lambda_configuration` - (Optional)
- `access_token_populate_id` - (Optional) The Id of the Lambda that will be invoked when an access token is generated for this application. This will be utilized during OAuth2 and OpenID Connect authentication requests as well as when an access token is generated for the Login API.
Expand Down
1 change: 1 addition & 0 deletions docs/resources/idp_saml_v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ resource "fusionauth_idp_saml_v2" "Saml" {
* `debug` - (Optional) Determines if debug is enabled for this provider. When enabled, each time this provider is invoked to reconcile a login an Event Log will be created.
* `domains` - (Optional) This is an optional list of domains that this OpenID Connect provider should be used for. This converts the FusionAuth login form to a domain-based login form. This type of form first asks the user for their email. FusionAuth then uses their email to determine if an OpenID Connect identity provider should be used. If an OpenID Connect provider should be used, the browser is redirected to the authorization endpoint of that identity provider. Otherwise, the password field is revealed on the form so that the user can login using FusionAuth.
* `email_claim` - (Optional) The name of the email claim (Attribute in the Assertion element) in the SAML response that FusionAuth uses to uniquely identity the user. If this is not set, the `use_name_for_email` flag must be true.
* `username_claim` - (Optional) The name of the claim in the SAML response that FusionAuth uses to identify the username. If this is not set, the NameId value will be used to link a user. This property is required when linkingStrategy is set to LinkByUsername or LinkByUsernameForExistingUser.
* `enabled` - (Optional) Determines if this provider is enabled. If it is false then it will be disabled globally.
* `idp_endpoint` - (Optional) The SAML v2 login page of the identity provider.
* `key_id` - (Required) The id of the key stored in Key Master that is used to verify the SAML response sent back to FusionAuth from the identity provider. This key must be a verification only key or certificate (meaning that it only has a public key component).
Expand Down
2 changes: 1 addition & 1 deletion docs/resources/tenant.md
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ resource "fusionauth_tenant" "example" {
- `match_mode` - (Optional) The level of severity where Reactor will consider a breach.
- `notify_user_email_template_id` - (Optional) The Id of the email template to use when notifying user of breached password. Required if tenant.passwordValidationRules.breachDetection.onLogin is set to NotifyUser.
- `on_login` - (Optional) The behavior when detecting breaches at time of user login
- `max_length` - (Optional) The maximum length of a password when a new user is created or a user requests a password change.
- `max_length` - (Optional) The maximum length of a password when a new user is created or a user requests a password change. This value must be greater than 0 and less than or equal to 256. When `passwordEncryptionConfiguration.encryptionScheme` is equal to `bcrypt`, the maximum will be limited to 50.
- `min_length` - (Optional) The minimum length of a password when a new user is created or a user requests a password change.
- `remember_previous_passwords` - (Optional)
- `count` - (Optional) The number of previous passwords to remember. Value must be greater than 0.
Expand Down
2 changes: 2 additions & 0 deletions docs/resources/theme.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ resource "fusionauth_theme" "mytheme" {
oauth2_child_registration_not_allowed = "[#ftl/]"
oauth2_child_registration_not_allowed_complete = "[#ftl/]"
oauth2_complete_registration = "[#ftl/]"
oauth2_consent = "[#ftl/]"
oauth2_device = "[#ftl/]"
oauth2_device_complete = "[#ftl/]"
oauth2_error = "[#ftl/]"
Expand Down Expand Up @@ -93,6 +94,7 @@ resource "fusionauth_theme" "mytheme" {
* `oauth2_child_registration_not_allowed` - (Optional) A FreeMarker template that is rendered when the user requests the /oauth2/child-registration-not-allowed path. This page contains a form where a child must provide their parent’s email address to ask their parent to create an account for them in a Consent workflow.
* `oauth2_child_registration_not_allowed_complete` - (Optional) A FreeMarker template that is rendered when the user requests the /oauth2/child-registration-not-allowed-complete path. This page is rendered is rendered after a child provides their parent’s email address for parental consent in a Consent workflow.
* `oauth2_complete_registration` - (Optional) A FreeMarker template that is rendered when the user requests the /oauth2/complete-registration path. This page contains a form that is used for users that have accounts but might be missing required fields.
* `oauth2_consent` - (Optional) A FreeMarker template that is rendered when a third party application requests scopes from the user.
* `oauth2_device` - (Optional) A FreeMarker template that is rendered when the user requests the /oauth2/device path. This page contains a form for accepting an end user’s short code for the interactive portion of the OAuth Device Authorization Grant workflow.
* `oauth2_device_complete` - (Optional) A FreeMarker template that is rendered when the user requests the /oauth2/device-complete path. This page contains a complete message indicating the device authentication has completed.
* `oauth2_error` - (Optional) This page is used if the user starts or is in the middle of the OAuth workflow and any type of error occurs. This could be caused by the user messing with the URL or internally some type of information wasn’t passed between the OAuth endpoints correctly. For example, if you are federating login to an external IdP and that IdP does not properly echo the state parameter, FusionAuth’s OAuth workflow will break and this page will be displayed.
Expand Down
5 changes: 3 additions & 2 deletions fusionauth/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/FusionAuth/go-client/pkg/fusionauth"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)
Expand Down Expand Up @@ -110,9 +111,9 @@ func checkFusionauthErrors(faErrs *fusionauth.Errors, err error) error {

// checkFusionauthRetryErrors wraps checking for fusionauth or low-level client
// errors and returns a non-retryable error on failure.
func checkFusionauthRetryErrors(faErrs *fusionauth.Errors, err error) *resource.RetryError {
func checkFusionauthRetryErrors(faErrs *fusionauth.Errors, err error) *retry.RetryError {
if anErr := checkFusionauthErrors(faErrs, err); anErr != nil {
return resource.NonRetryableError(anErr)
return retry.NonRetryableError(anErr)
}

return nil
Expand Down
27 changes: 27 additions & 0 deletions fusionauth/resource_fusionauth_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,33 @@ func newJWTConfiguration() *schema.Resource {
Default: 43200,
Description: "The length of time in minutes the JWT refresh token will live before it is expired and is not able to be exchanged for a JWT.",
},
"refresh_token_expiration_policy": {
Type: schema.TypeString,
Optional: true,
Default: fusionauth.RefreshTokenExpirationPolicy_Fixed.String(),
Description: "The Refresh Token expiration policy. The possible values are: Fixed - the expiration is calculated from the time the token is issued. SlidingWindow - the expiration is calculated from the last time the token was used.",
ValidateFunc: validation.StringInSlice([]string{
fusionauth.RefreshTokenExpirationPolicy_SlidingWindow.String(),
fusionauth.RefreshTokenExpirationPolicy_Fixed.String(),
fusionauth.RefreshTokenExpirationPolicy_SlidingWindowWithMaximumLifetime.String(),
}, false),
},
"refresh_token_sliding_window_maximum_time_to_live_in_minutes": {
Type: schema.TypeInt,
Optional: true,
Description: "The maximum lifetime of a refresh token when using a refresh token expiration policy of SlidingWindowWithMaximumLifetime. Value must be greater than 0.",
ValidateFunc: validation.IntAtLeast(1),
},
"refresh_token_usage_policy": {
Type: schema.TypeString,
Optional: true,
Default: fusionauth.RefreshTokenUsagePolicy_Reusable.String(),
Description: "The refresh token usage policy. The following are valid values: Reusable - the token does not change after it was issued. OneTimeUse - the token value will be changed each time the token is used to refresh a JWT. The client must store the new value after each usage.",
ValidateFunc: validation.StringInSlice([]string{
fusionauth.RefreshTokenUsagePolicy_Reusable.String(),
fusionauth.RefreshTokenUsagePolicy_OneTimeUse.String(),
}, false),
},
"ttl_seconds": {
Type: schema.TypeInt,
Optional: true,
Expand Down
18 changes: 13 additions & 5 deletions fusionauth/resource_fusionauth_application_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ func buildApplication(data *schema.ResourceData) fusionauth.Application {
IdTokenKeyId: data.Get("jwt_configuration.0.id_token_key_id").(string),
RefreshTokenTimeToLiveInMinutes: data.Get("jwt_configuration.0.refresh_token_ttl_minutes").(int),
TimeToLiveInSeconds: data.Get("jwt_configuration.0.ttl_seconds").(int),
RefreshTokenExpirationPolicy: fusionauth.RefreshTokenExpirationPolicy(data.Get("jwt_configuration.0.refresh_token_expiration_policy").(string)),
RefreshTokenUsagePolicy: fusionauth.RefreshTokenUsagePolicy(data.Get("jwt_configuration.0.refresh_token_usage_policy").(string)),
RefreshTokenSlidingWindowConfiguration: fusionauth.RefreshTokenSlidingWindowConfiguration{
MaximumTimeToLiveInMinutes: data.Get("jwt_configuration.0.refresh_token_sliding_window_maximum_time_to_live_in_minutes").(int),
},
},
LambdaConfiguration: fusionauth.LambdaConfiguration{
AccessTokenPopulateId: data.Get("lambda_configuration.0.access_token_populate_id").(string),
Expand Down Expand Up @@ -225,11 +230,14 @@ func buildResourceDataFromApplication(a fusionauth.Application, data *schema.Res

err = data.Set("jwt_configuration", []map[string]interface{}{
{
"enabled": a.JwtConfiguration.Enabled,
"access_token_id": a.JwtConfiguration.AccessTokenKeyId,
"id_token_key_id": a.JwtConfiguration.IdTokenKeyId,
"refresh_token_ttl_minutes": a.JwtConfiguration.RefreshTokenTimeToLiveInMinutes,
"ttl_seconds": a.JwtConfiguration.TimeToLiveInSeconds,
"enabled": a.JwtConfiguration.Enabled,
"access_token_id": a.JwtConfiguration.AccessTokenKeyId,
"id_token_key_id": a.JwtConfiguration.IdTokenKeyId,
"refresh_token_expiration_policy": a.JwtConfiguration.RefreshTokenExpirationPolicy,
"refresh_token_sliding_window_maximum_time_to_live_in_minutes": a.JwtConfiguration.RefreshTokenSlidingWindowConfiguration.MaximumTimeToLiveInMinutes,
"refresh_token_ttl_minutes": a.JwtConfiguration.RefreshTokenTimeToLiveInMinutes,
"refresh_token_usage_policy": a.JwtConfiguration.RefreshTokenUsagePolicy,
"ttl_seconds": a.JwtConfiguration.TimeToLiveInSeconds,
},
})
if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions fusionauth/resource_fusionauth_generic_connector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

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

Expand Down Expand Up @@ -135,7 +136,7 @@ func testAccCheckGenericConnectorDestroy(s *terraform.State) error {
}

// Ensure we retry for eventual consistency in HA setups.
err := resource.RetryContext(context.Background(), retryTimeout, func() *resource.RetryError {
err := retry.RetryContext(context.Background(), retryTimeout, func() *retry.RetryError {
connector, faErrs, err := RetrieveConnector(context.Background(), fusionauthClient(), rs.Primary.ID)
if errs := checkFusionauthRetryErrors(faErrs, err); errs != nil {
return errs
Expand All @@ -146,7 +147,7 @@ func testAccCheckGenericConnectorDestroy(s *terraform.State) error {
return nil
}

return resource.RetryableError(fmt.Errorf("fusionauth resource still exists: %s", rs.Primary.ID))
return retry.RetryableError(fmt.Errorf("fusionauth resource still exists: %s", rs.Primary.ID))
})

if err != nil {
Expand Down
19 changes: 18 additions & 1 deletion fusionauth/resource_fusionauth_idp_samlv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ func resourceIDPSAMLv2() *schema.Resource {
Optional: true,
Description: "The name of the email claim (Attribute in the Assertion element) in the SAML response that FusionAuth uses to uniquely identity the user. If this is not set, the `use_name_for_email` flag must be true.",
},
"unique_id_claim": {
Type: schema.TypeString,
Optional: true,
Description: "The name of the unique claim in the SAML response that FusionAuth uses to uniquely link the user. If this is not set, the emailClaim will be used when linking user.",
},
"username_claim": {
Type: schema.TypeString,
Optional: true,
Description: "The name of the claim in the SAML response that FusionAuth uses to identify the username. If this is not set, the NameId value will be used to link a user. This property is required when linkingStrategy is set to LinkByUsername or LinkByUsernameForExistingUser.",
},
"enabled": {
Type: schema.TypeBool,
Optional: true,
Expand All @@ -114,7 +124,6 @@ func resourceIDPSAMLv2() *schema.Resource {
Required: true,
ValidateFunc: validation.IsUUID,
Description: "The id of the key stored in Key Master that is used to verify the SAML response sent back to FusionAuth from the identity provider. This key must be a verification only key or certificate (meaning that it only has a public key component).",
ForceNew: true,
},
"lambda_reconcile_id": {
Type: schema.TypeString,
Expand Down Expand Up @@ -294,7 +303,9 @@ func buildIDPSAMLv2(data *schema.ResourceData) SAMLIdentityProviderBody {
Type: fusionauth.IdentityProviderType_SAMLv2,
LinkingStrategy: fusionauth.IdentityProviderLinkingStrategy(data.Get("linking_strategy").(string)),
},
UniqueIdClaim: data.Get("unique_id_claim").(string),
EmailClaim: data.Get("email_claim").(string),
UsernameClaim: data.Get("username_claim").(string),
KeyId: data.Get("key_id").(string),
UseNameIdForEmail: data.Get("use_name_for_email").(bool),
},
Expand Down Expand Up @@ -329,6 +340,12 @@ func buildResourceDataFromIDPSAMLv2(data *schema.ResourceData, res fusionauth.SA
if err := data.Set("email_claim", res.EmailClaim); err != nil {
return diag.Errorf("idpSAMLv2.email_claim: %s", err.Error())
}
if err := data.Set("unique_id_claim", res.UniqueIdClaim); err != nil {
return diag.Errorf("idpSAMLv2.unique_id_claim: %s", err.Error())
}
if err := data.Set("username_claim", res.UsernameClaim); err != nil {
return diag.Errorf("idpSAMLv2.username_claim: %s", err.Error())
}
if err := data.Set("enabled", res.Enabled); err != nil {
return diag.Errorf("idpSAMLv2.enabled: %s", err.Error())
}
Expand Down
5 changes: 3 additions & 2 deletions fusionauth/resource_fusionauth_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/FusionAuth/go-client/pkg/fusionauth"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

Expand Down Expand Up @@ -165,7 +166,7 @@ func testAccCheckFusionauthKeyDestroy(s *terraform.State) error {
}

// Ensure we retry for eventual consistency in HA setups.
err := resource.RetryContext(context.Background(), retryTimeout, func() *resource.RetryError {
err := retry.RetryContext(context.Background(), retryTimeout, func() *retry.RetryError {
key, faErrs, err := faClient.RetrieveKey(rs.Primary.ID)
if errs := checkFusionauthRetryErrors(faErrs, err); errs != nil {
return errs
Expand All @@ -176,7 +177,7 @@ func testAccCheckFusionauthKeyDestroy(s *terraform.State) error {
return nil
}

return resource.RetryableError(fmt.Errorf("fusionauth resource still exists: %s", rs.Primary.ID))
return retry.RetryableError(fmt.Errorf("fusionauth resource still exists: %s", rs.Primary.ID))
})

if err != nil {
Expand Down
Loading

0 comments on commit 3e8258c

Please sign in to comment.