diff --git a/docs/resources/authentication_execution.md b/docs/resources/authentication_execution.md index 15f5b1bd..637edb49 100644 --- a/docs/resources/authentication_execution.md +++ b/docs/resources/authentication_execution.md @@ -9,7 +9,7 @@ Allows for creating and managing an authentication execution within Keycloak. An authentication execution is an action that the user or service may or may not take when authenticating through an authentication flow. -~> Due to limitations in the Keycloak API, the ordering of authentication executions within a flow must be specified using `depends_on`. Authentication executions that are created first will appear first within the flow. +~> Due to limitations in the Keycloak API, the ordering of authentication executions within a flow must be specified using `depends_on` in versions prior to Keycloak 25. Authentication executions that are created first will appear first within the flow. ## Example Usage @@ -30,6 +30,7 @@ resource "keycloak_authentication_execution" "execution_one" { parent_flow_alias = "${keycloak_authentication_flow.flow.alias}" authenticator = "auth-cookie" requirement = "ALTERNATIVE" + priority = 10 # Starting from Keycloak 25 } # second execution @@ -38,7 +39,9 @@ resource "keycloak_authentication_execution" "execution_two" { parent_flow_alias = "${keycloak_authentication_flow.flow.alias}" authenticator = "identity-provider-redirector" requirement = "ALTERNATIVE" + priority = 20 # Starting from Keycloak 25 + # Workaround for older Keycloak versions (Keycloak 24 and older) depends_on = [ keycloak_authentication_execution.execution_one ] @@ -51,6 +54,7 @@ resource "keycloak_authentication_execution" "execution_two" { - `parent_flow_alias` - (Required) The alias of the flow this execution is attached to. - `authenticator` - (Required) The name of the authenticator. This can be found by experimenting with the GUI and looking at HTTP requests within the network tab of your browser's development tools. - `requirement`- (Optional) The requirement setting, which can be one of `REQUIRED`, `ALTERNATIVE`, `OPTIONAL`, `CONDITIONAL`, or `DISABLED`. Defaults to `DISABLED`. +- `priority`- (Optional) The authenticator priority, the lower the value the higher it will be placed in the parent flow. This option is supported only by Keycloak 25 and onwards. ## Import diff --git a/docs/resources/authentication_subflow.md b/docs/resources/authentication_subflow.md index e22d189e..29ebb91e 100644 --- a/docs/resources/authentication_subflow.md +++ b/docs/resources/authentication_subflow.md @@ -28,6 +28,7 @@ resource "keycloak_authentication_subflow" "subflow" { parent_flow_alias = keycloak_authentication_flow.flow.alias provider_id = "basic-flow" requirement = "ALTERNATIVE" + priority = 10 # Starting from Keycloak 25 } ``` @@ -43,6 +44,7 @@ and `client-flow`. Defaults to `basic-flow`. authenticators. In general this will remain empty. - `requirement`- (Optional) The requirement setting, which can be one of `REQUIRED`, `ALTERNATIVE`, `OPTIONAL`, `CONDITIONAL`, or `DISABLED`. Defaults to `DISABLED`. +- `priority`- (Optional) The subflow priority, the lower the value the higher it will be placed in the parent flow. This option is supported only by Keycloak 25 and onwards. ## Import diff --git a/example/main.tf b/example/main.tf index 3aa2ae8a..43890dff 100644 --- a/example/main.tf +++ b/example/main.tf @@ -1020,9 +1020,7 @@ resource "keycloak_authentication_execution" "browser-copy-cookie" { parent_flow_alias = keycloak_authentication_flow.browser-copy-flow.alias authenticator = "auth-cookie" requirement = "ALTERNATIVE" - depends_on = [ - keycloak_authentication_execution.browser-copy-kerberos - ] + priority = 20 } resource "keycloak_authentication_execution" "browser-copy-kerberos" { @@ -1030,6 +1028,7 @@ resource "keycloak_authentication_execution" "browser-copy-kerberos" { parent_flow_alias = keycloak_authentication_flow.browser-copy-flow.alias authenticator = "auth-spnego" requirement = "DISABLED" + priority = 10 } resource "keycloak_authentication_execution" "browser-copy-idp-redirect" { @@ -1037,9 +1036,7 @@ resource "keycloak_authentication_execution" "browser-copy-idp-redirect" { parent_flow_alias = keycloak_authentication_flow.browser-copy-flow.alias authenticator = "identity-provider-redirector" requirement = "ALTERNATIVE" - depends_on = [ - keycloak_authentication_execution.browser-copy-cookie - ] + priority = 30 } resource "keycloak_authentication_subflow" "browser-copy-flow-forms" { @@ -1047,9 +1044,7 @@ resource "keycloak_authentication_subflow" "browser-copy-flow-forms" { parent_flow_alias = keycloak_authentication_flow.browser-copy-flow.alias alias = "browser-copy-flow-forms" requirement = "ALTERNATIVE" - depends_on = [ - keycloak_authentication_execution.browser-copy-idp-redirect - ] + priority = 40 } resource "keycloak_authentication_execution" "browser-copy-auth-username-password-form" { @@ -1057,6 +1052,7 @@ resource "keycloak_authentication_execution" "browser-copy-auth-username-passwor parent_flow_alias = keycloak_authentication_subflow.browser-copy-flow-forms.alias authenticator = "auth-username-password-form" requirement = "REQUIRED" + priority = 10 } resource "keycloak_authentication_execution" "browser-copy-otp" { @@ -1064,9 +1060,7 @@ resource "keycloak_authentication_execution" "browser-copy-otp" { parent_flow_alias = keycloak_authentication_subflow.browser-copy-flow-forms.alias authenticator = "auth-otp-form" requirement = "REQUIRED" - depends_on = [ - keycloak_authentication_execution.browser-copy-auth-username-password-form - ] + priority = 20 } resource "keycloak_authentication_execution_config" "config" { diff --git a/keycloak/authentication_execution.go b/keycloak/authentication_execution.go index 971448ee..790150a1 100644 --- a/keycloak/authentication_execution.go +++ b/keycloak/authentication_execution.go @@ -11,6 +11,7 @@ import ( // POST /realms/${realmId}/authentication/flows/${flowAlias}/executions/execution type authenticationExecutionCreate struct { Provider string `json:"provider"` //authenticator of the execution + Priority int `json:"priority,omitempty"` } type authenticationExecutionRequirementUpdate struct { @@ -18,6 +19,7 @@ type authenticationExecutionRequirementUpdate struct { ParentFlowAlias string `json:"-"` Id string `json:"id"` Requirement string `json:"requirement"` + Priority int `json:"priority,omitempty"` } // this type is returned by GET /realms/${realmId}/authentication/flows/${flowAlias}/executions @@ -30,7 +32,7 @@ type AuthenticationExecution struct { AuthenticationFlow bool `json:"authenticationFlow"` FlowId string `json:"flowId"` ParentFlowId string `json:"parentFlow"` - Priority int `json:"priority"` + Priority int `json:"priority,omitempty"` Requirement string `json:"requirement"` } @@ -47,6 +49,7 @@ type AuthenticationExecutionInfo struct { Index int `json:"index"` Level int `json:"level"` ProviderId string `json:"providerId"` + Priority int `json:"priority,omitempty"` Requirement string `json:"requirement"` } @@ -119,7 +122,14 @@ func (keycloakClient *KeycloakClient) GetAuthenticationExecutionInfoFromProvider } func (keycloakClient *KeycloakClient) NewAuthenticationExecution(ctx context.Context, execution *AuthenticationExecution) error { - _, location, err := keycloakClient.post(ctx, fmt.Sprintf("/realms/%s/authentication/flows/%s/executions/execution", execution.RealmId, execution.ParentFlowAlias), &authenticationExecutionCreate{Provider: execution.Authenticator}) + executionCreate := &authenticationExecutionCreate{ + Provider: execution.Authenticator, + } + if prioritySupported, _ := keycloakClient.VersionIsGreaterThanOrEqualTo(ctx, Version_25); prioritySupported { + executionCreate.Priority = execution.Priority + } + _, location, err := keycloakClient.post(ctx, fmt.Sprintf("/realms/%s/authentication/flows/%s/executions/execution", execution.RealmId, execution.ParentFlowAlias), executionCreate) + if err != nil { return err } @@ -155,6 +165,9 @@ func (keycloakClient *KeycloakClient) UpdateAuthenticationExecution(ctx context. Id: execution.Id, Requirement: execution.Requirement, } + if prioritySupported, _ := keycloakClient.VersionIsGreaterThanOrEqualTo(ctx, Version_25); prioritySupported { + authenticationExecutionUpdateRequirement.Priority = execution.Priority + } return keycloakClient.UpdateAuthenticationExecutionRequirement(ctx, authenticationExecutionUpdateRequirement) } @@ -171,19 +184,3 @@ func (keycloakClient *KeycloakClient) DeleteAuthenticationExecution(ctx context. return nil } - -func (keycloakClient *KeycloakClient) RaiseAuthenticationExecutionPriority(ctx context.Context, realmId, id string) error { - _, _, err := keycloakClient.post(ctx, fmt.Sprintf("/realms/%s/authentication/executions/%s/raise-priority", realmId, id), nil) - if err != nil { - return err - } - return nil -} - -func (keycloakClient *KeycloakClient) LowerAuthenticationExecutionPriority(ctx context.Context, realmId, id string) error { - _, _, err := keycloakClient.post(ctx, fmt.Sprintf("/realms/%s/authentication/executions/%s/lower-priority", realmId, id), nil) - if err != nil { - return err - } - return nil -} diff --git a/keycloak/authentication_subflow.go b/keycloak/authentication_subflow.go index 593facb9..2576abf2 100644 --- a/keycloak/authentication_subflow.go +++ b/keycloak/authentication_subflow.go @@ -71,6 +71,9 @@ func (keycloakClient *KeycloakClient) GetAuthenticationSubFlow(ctx context.Conte } authenticationSubFlow.Authenticator = subFlowExecution.Authenticator authenticationSubFlow.Requirement = subFlowExecution.Requirement + if prioritySupported, _ := keycloakClient.VersionIsGreaterThanOrEqualTo(ctx, Version_25); prioritySupported { + authenticationSubFlow.Priority = subFlowExecution.Priority + } return &authenticationSubFlow, nil } @@ -111,6 +114,9 @@ func (keycloakClient *KeycloakClient) UpdateAuthenticationSubFlow(ctx context.Co Id: executionId, Requirement: authenticationSubFlow.Requirement, } + if prioritySupported, _ := keycloakClient.VersionIsGreaterThanOrEqualTo(ctx, Version_25); prioritySupported { + authenticationExecutionUpdateRequirement.Priority = authenticationSubFlow.Priority + } return keycloakClient.UpdateAuthenticationExecutionRequirement(ctx, authenticationExecutionUpdateRequirement) } @@ -128,31 +134,3 @@ func (keycloakClient *KeycloakClient) DeleteAuthenticationSubFlow(ctx context.Co return keycloakClient.DeleteAuthenticationExecution(ctx, authenticationSubFlow.RealmId, executionId) } - -func (keycloakClient *KeycloakClient) RaiseAuthenticationSubFlowPriority(ctx context.Context, realmId, parentFlowAlias, id string) error { - authenticationSubFlow := AuthenticationSubFlow{ - Id: id, - ParentFlowAlias: parentFlowAlias, - RealmId: realmId, - } - executionId, err := keycloakClient.getExecutionId(ctx, &authenticationSubFlow) - if err != nil { - return err - } - - return keycloakClient.RaiseAuthenticationExecutionPriority(ctx, authenticationSubFlow.RealmId, executionId) -} - -func (keycloakClient *KeycloakClient) LowerAuthenticationSubFlowPriority(ctx context.Context, realmId, parentFlowAlias, id string) error { - authenticationSubFlow := AuthenticationSubFlow{ - Id: id, - ParentFlowAlias: parentFlowAlias, - RealmId: realmId, - } - executionId, err := keycloakClient.getExecutionId(ctx, &authenticationSubFlow) - if err != nil { - return err - } - - return keycloakClient.LowerAuthenticationExecutionPriority(ctx, authenticationSubFlow.RealmId, executionId) -} diff --git a/keycloak/version.go b/keycloak/version.go index b59d0b2a..8bb14b0d 100644 --- a/keycloak/version.go +++ b/keycloak/version.go @@ -74,3 +74,19 @@ func (keycloakClient *KeycloakClient) VersionIsLessThanOrEqualTo(ctx context.Con return keycloakClient.version.LessThanOrEqual(v), nil } + +func (keycloakClient *KeycloakClient) VersionIsLessThan(ctx context.Context, versionString Version) (bool, error) { + if keycloakClient.version == nil { + err := keycloakClient.login(ctx) + if err != nil { + return false, err + } + } + + v, err := version.NewVersion(string(versionString)) + if err != nil { + return false, nil + } + + return keycloakClient.version.LessThan(v), nil +} diff --git a/provider/data_source_keycloak_authentication_execution.go b/provider/data_source_keycloak_authentication_execution.go index df2fd2b8..adca8114 100644 --- a/provider/data_source_keycloak_authentication_execution.go +++ b/provider/data_source_keycloak_authentication_execution.go @@ -23,6 +23,10 @@ func dataSourceKeycloakAuthenticationExecution() *schema.Resource { Type: schema.TypeString, Required: true, }, + "priority": { + Type: schema.TypeInt, + Optional: true, + }, }, } } @@ -39,7 +43,7 @@ func dataSourceKeycloakAuthenticationExecutionRead(ctx context.Context, data *sc return diag.FromErr(err) } - mapFromAuthenticationExecutionInfoToData(data, authenticationExecutionInfo) + mapFromAuthenticationExecutionInfoToData(keycloakClient, ctx, data, authenticationExecutionInfo) return nil } diff --git a/provider/data_source_keycloak_authentication_execution_test.go b/provider/data_source_keycloak_authentication_execution_test.go index ad788631..8f8bc2ef 100644 --- a/provider/data_source_keycloak_authentication_execution_test.go +++ b/provider/data_source_keycloak_authentication_execution_test.go @@ -8,9 +8,11 @@ import ( "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/terraform" + "github.com/keycloak/terraform-provider-keycloak/keycloak" ) func TestAccKeycloakDataSourceAuthenticationExecution_basic(t *testing.T) { + skipIfVersionIsGreaterThanOrEqualTo(testCtx, t, keycloakClient, keycloak.Version_25) t.Parallel() parentFlowAlias := acctest.RandomWithPrefix("tf-acc") @@ -21,7 +23,7 @@ func TestAccKeycloakDataSourceAuthenticationExecution_basic(t *testing.T) { CheckDestroy: testAccCheckKeycloakAuthenticationExecutionConfigDestroy, Steps: []resource.TestStep{ { - Config: testDataSourceKeycloakAuthenticationExecution_basic(parentFlowAlias), + Config: testDataSourceKeycloakAuthenticationExecution_basic(parentFlowAlias, 10), Check: resource.ComposeTestCheckFunc( testAccCheckKeycloakAuthenticationExecutionExists("keycloak_authentication_execution.execution"), resource.TestCheckResourceAttrPair("keycloak_authentication_execution.execution", "id", "data.keycloak_authentication_execution.execution", "id"), @@ -35,6 +37,33 @@ func TestAccKeycloakDataSourceAuthenticationExecution_basic(t *testing.T) { }) } +func TestAccKeycloakDataSourceAuthenticationExecution_basicWithPriority(t *testing.T) { + skipIfVersionIsLessThan(testCtx, t, keycloakClient, keycloak.Version_25) + t.Parallel() + + parentFlowAlias := acctest.RandomWithPrefix("tf-acc") + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviderFactories, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakAuthenticationExecutionConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testDataSourceKeycloakAuthenticationExecution_basic(parentFlowAlias, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckKeycloakAuthenticationExecutionExists("keycloak_authentication_execution.execution"), + resource.TestCheckResourceAttrPair("keycloak_authentication_execution.execution", "id", "data.keycloak_authentication_execution.execution", "id"), + resource.TestCheckResourceAttrPair("keycloak_authentication_execution.execution", "realm_id", "data.keycloak_authentication_execution.execution", "realm_id"), + resource.TestCheckResourceAttrPair("keycloak_authentication_execution.execution", "priority", "data.keycloak_authentication_execution.execution", "priority"), + resource.TestCheckResourceAttrPair("keycloak_authentication_execution.execution", "parent_flow_alias", "data.keycloak_authentication_execution.execution", "parent_flow_alias"), + resource.TestCheckResourceAttrPair("keycloak_authentication_execution.execution", "authenticator", "data.keycloak_authentication_execution.execution", "provider_id"), + testAccCheckDataKeycloakAuthenticationExecution("data.keycloak_authentication_execution.execution"), + ), + }, + }, + }) +} + func TestAccKeycloakDataSourceAuthenticationExecution_errorNoExecutions(t *testing.T) { t.Parallel() parentFlowAlias := acctest.RandomWithPrefix("tf-acc") @@ -45,7 +74,7 @@ func TestAccKeycloakDataSourceAuthenticationExecution_errorNoExecutions(t *testi CheckDestroy: testAccCheckKeycloakAuthenticationExecutionConfigDestroy, Steps: []resource.TestStep{ { - Config: testDataSourceKeycloakAuthenticationExecution_errorNoExecutions(parentFlowAlias), + Config: testDataSourceKeycloakAuthenticationExecution_errorNoExecutions(parentFlowAlias, 10), ExpectError: regexp.MustCompile("no authentication executions found for parent flow alias .*"), }, }, @@ -62,7 +91,7 @@ func TestAccKeycloakDataSourceAuthenticationExecution_errorWrongProviderId(t *te CheckDestroy: testAccCheckKeycloakAuthenticationExecutionConfigDestroy, Steps: []resource.TestStep{ { - Config: testDataSourceKeycloakAuthenticationExecution_errorWrongProviderId(parentFlowAlias, acctest.RandString(10)), + Config: testDataSourceKeycloakAuthenticationExecution_errorWrongProviderId(parentFlowAlias, acctest.RandString(10), 10), ExpectError: regexp.MustCompile("no authentication execution under parent flow alias .* with provider id .* found"), }, }, @@ -94,7 +123,7 @@ func testAccCheckDataKeycloakAuthenticationExecution(resourceName string) resour } } -func testDataSourceKeycloakAuthenticationExecution_basic(parentFlowAlias string) string { +func testDataSourceKeycloakAuthenticationExecution_basic(parentFlowAlias string, priority int) string { return fmt.Sprintf(` data "keycloak_realm" "realm" { realm = "%s" @@ -110,6 +139,7 @@ resource "keycloak_authentication_execution" "execution" { parent_flow_alias = keycloak_authentication_flow.flow.alias authenticator = "identity-provider-redirector" requirement = "REQUIRED" + priority = %d } data "keycloak_authentication_execution" "execution" { @@ -121,10 +151,10 @@ data "keycloak_authentication_execution" "execution" { keycloak_authentication_execution.execution, ] } - `, testAccRealm.Realm, parentFlowAlias) + `, testAccRealm.Realm, parentFlowAlias, priority) } -func testDataSourceKeycloakAuthenticationExecution_errorNoExecutions(parentFlowAlias string) string { +func testDataSourceKeycloakAuthenticationExecution_errorNoExecutions(parentFlowAlias string, priority int) string { return fmt.Sprintf(` data "keycloak_realm" "realm" { realm = "%s" @@ -139,15 +169,16 @@ data "keycloak_authentication_execution" "execution" { realm_id = data.keycloak_realm.realm.id parent_flow_alias = keycloak_authentication_flow.flow.alias provider_id = "foo" + priority = %d depends_on = [ keycloak_authentication_flow.flow, ] } - `, testAccRealm.Realm, parentFlowAlias) + `, testAccRealm.Realm, parentFlowAlias, priority) } -func testDataSourceKeycloakAuthenticationExecution_errorWrongProviderId(parentFlowAlias, providerId string) string { +func testDataSourceKeycloakAuthenticationExecution_errorWrongProviderId(parentFlowAlias, providerId string, priority int) string { return fmt.Sprintf(` data "keycloak_realm" "realm" { realm = "%s" @@ -163,6 +194,7 @@ resource "keycloak_authentication_execution" "execution" { parent_flow_alias = keycloak_authentication_flow.flow.alias authenticator = "identity-provider-redirector" requirement = "REQUIRED" + priority = %d } data "keycloak_authentication_execution" "execution" { @@ -174,5 +206,5 @@ data "keycloak_authentication_execution" "execution" { keycloak_authentication_execution.execution, ] } - `, testAccRealm.Id, parentFlowAlias, providerId) + `, testAccRealm.Id, parentFlowAlias, priority, providerId) } diff --git a/provider/resource_keycloak_authentication_execution.go b/provider/resource_keycloak_authentication_execution.go index 1ab2d8a6..04f8f893 100644 --- a/provider/resource_keycloak_authentication_execution.go +++ b/provider/resource_keycloak_authentication_execution.go @@ -43,6 +43,10 @@ func resourceKeycloakAuthenticationExecution() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{"REQUIRED", "ALTERNATIVE", "OPTIONAL", "CONDITIONAL", "DISABLED"}, false), //OPTIONAL is removed from 8.0.0 onwards Default: "DISABLED", }, + "priority": { + Type: schema.TypeInt, + Optional: true, + }, }, } } @@ -54,25 +58,32 @@ func mapFromDataToAuthenticationExecution(data *schema.ResourceData) *keycloak.A ParentFlowAlias: data.Get("parent_flow_alias").(string), Authenticator: data.Get("authenticator").(string), Requirement: data.Get("requirement").(string), + Priority: data.Get("priority").(int), } return authenticationExecution } -func mapFromAuthenticationExecutionToData(data *schema.ResourceData, authenticationExecution *keycloak.AuthenticationExecution) { +func mapFromAuthenticationExecutionToData(keycloakClient *keycloak.KeycloakClient, ctx context.Context, data *schema.ResourceData, authenticationExecution *keycloak.AuthenticationExecution) { data.SetId(authenticationExecution.Id) data.Set("realm_id", authenticationExecution.RealmId) data.Set("parent_flow_alias", authenticationExecution.ParentFlowAlias) data.Set("authenticator", authenticationExecution.Authenticator) data.Set("requirement", authenticationExecution.Requirement) + if prioritySupported, _ := keycloakClient.VersionIsGreaterThanOrEqualTo(ctx, keycloak.Version_25); prioritySupported { + data.Set("priority", authenticationExecution.Priority) + } } -func mapFromAuthenticationExecutionInfoToData(data *schema.ResourceData, authenticationExecutionInfo *keycloak.AuthenticationExecutionInfo) { +func mapFromAuthenticationExecutionInfoToData(keycloakClient *keycloak.KeycloakClient, ctx context.Context, data *schema.ResourceData, authenticationExecutionInfo *keycloak.AuthenticationExecutionInfo) { data.SetId(authenticationExecutionInfo.Id) data.Set("realm_id", authenticationExecutionInfo.RealmId) data.Set("parent_flow_alias", authenticationExecutionInfo.ParentFlowAlias) + if prioritySupported, _ := keycloakClient.VersionIsGreaterThanOrEqualTo(ctx, keycloak.Version_25); prioritySupported { + data.Set("priority", authenticationExecutionInfo.Priority) + } } func resourceKeycloakAuthenticationExecutionCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -85,7 +96,7 @@ func resourceKeycloakAuthenticationExecutionCreate(ctx context.Context, data *sc return diag.FromErr(err) } - mapFromAuthenticationExecutionToData(data, authenticationExecution) + mapFromAuthenticationExecutionToData(keycloakClient, ctx, data, authenticationExecution) return resourceKeycloakAuthenticationExecutionRead(ctx, data, meta) } @@ -102,7 +113,7 @@ func resourceKeycloakAuthenticationExecutionRead(ctx context.Context, data *sche return handleNotFoundError(ctx, err, data) } - mapFromAuthenticationExecutionToData(data, authenticationExecution) + mapFromAuthenticationExecutionToData(keycloakClient, ctx, data, authenticationExecution) return nil } @@ -117,7 +128,7 @@ func resourceKeycloakAuthenticationExecutionUpdate(ctx context.Context, data *sc return diag.FromErr(err) } - mapFromAuthenticationExecutionToData(data, authenticationExecution) + mapFromAuthenticationExecutionToData(keycloakClient, ctx, data, authenticationExecution) return nil } diff --git a/provider/resource_keycloak_authentication_execution_config_test.go b/provider/resource_keycloak_authentication_execution_config_test.go index e8a323b6..9716eb68 100644 --- a/provider/resource_keycloak_authentication_execution_config_test.go +++ b/provider/resource_keycloak_authentication_execution_config_test.go @@ -199,6 +199,7 @@ resource "keycloak_authentication_execution" "execution" { realm_id = data.keycloak_realm.realm.id parent_flow_alias = keycloak_authentication_flow.flow.alias authenticator = "identity-provider-redirector" + priority = 10 } resource "keycloak_authentication_execution_config" "config" { diff --git a/provider/resource_keycloak_authentication_execution_test.go b/provider/resource_keycloak_authentication_execution_test.go index d892a260..510cddf2 100644 --- a/provider/resource_keycloak_authentication_execution_test.go +++ b/provider/resource_keycloak_authentication_execution_test.go @@ -99,6 +99,41 @@ func TestAccKeycloakAuthenticationExecution_updateAuthenticationExecutionRequire }) } +func TestAccKeycloakAuthenticationExecution_updateAuthenticationExecutionPriority(t *testing.T) { + skipIfVersionIsLessThan(testCtx, t, keycloakClient, keycloak.Version_25) + t.Parallel() + authParentFlowAlias := acctest.RandomWithPrefix("tf-acc") + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviderFactories, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakAuthenticationSubFlowDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakAuthenticationExecution_basicWithPriority(authParentFlowAlias, 50), + Check: resource.ComposeTestCheckFunc( + testAccCheckKeycloakAuthenticationExecutionExists("keycloak_authentication_execution.execution"), + resource.TestCheckResourceAttr("keycloak_authentication_execution.execution", "priority", "50"), + ), + }, + { + Config: testKeycloakAuthenticationExecution_basicWithPriority(authParentFlowAlias, 60), + Check: resource.ComposeTestCheckFunc( + testAccCheckKeycloakAuthenticationExecutionExists("keycloak_authentication_execution.execution"), + resource.TestCheckResourceAttr("keycloak_authentication_execution.execution", "priority", "60"), + ), + }, + { + Config: testKeycloakAuthenticationExecution_basicWithPriority(authParentFlowAlias, 70), + Check: resource.ComposeTestCheckFunc( + testAccCheckKeycloakAuthenticationExecutionExists("keycloak_authentication_execution.execution"), + resource.TestCheckResourceAttr("keycloak_authentication_execution.execution", "priority", "70"), + ), + }, + }, + }) +} + func testAccCheckKeycloakAuthenticationExecutionExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { _, err := getAuthenticationExecutionFromState(s, resourceName) @@ -199,7 +234,27 @@ resource "keycloak_authentication_execution" "execution" { `, testAccRealm.Realm, parentAlias) } -func testKeycloakAuthenticationExecution_basicWithRequirement(parentAlias, requirement string) string { +func testKeycloakAuthenticationExecution_basicWithPriority(parentAlias string, priority int) string { + return fmt.Sprintf(` +data "keycloak_realm" "realm" { + realm = "%s" +} + +resource "keycloak_authentication_flow" "flow" { + realm_id = data.keycloak_realm.realm.id + alias = "%s" +} + +resource "keycloak_authentication_execution" "execution" { + realm_id = data.keycloak_realm.realm.id + parent_flow_alias = keycloak_authentication_flow.flow.alias + authenticator = "auth-cookie" + priority = %d +} + `, testAccRealm.Realm, parentAlias, priority) +} + +func testKeycloakAuthenticationExecution_basicWithRequirement(parentAlias string, requirement string) string { return fmt.Sprintf(` data "keycloak_realm" "realm" { realm = "%s" diff --git a/provider/resource_keycloak_authentication_subflow.go b/provider/resource_keycloak_authentication_subflow.go index b3725cf4..f984a842 100644 --- a/provider/resource_keycloak_authentication_subflow.go +++ b/provider/resource_keycloak_authentication_subflow.go @@ -59,6 +59,10 @@ func resourceKeycloakAuthenticationSubFlow() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{"REQUIRED", "ALTERNATIVE", "OPTIONAL", "CONDITIONAL", "DISABLED"}, false), //OPTIONAL is removed from 8.0.0 onwards Default: "DISABLED", }, + "priority": { + Type: schema.TypeInt, + Optional: true, + }, }, } } @@ -73,12 +77,13 @@ func mapFromDataToAuthenticationSubFlow(data *schema.ResourceData) *keycloak.Aut Description: data.Get("description").(string), Authenticator: data.Get("authenticator").(string), Requirement: data.Get("requirement").(string), + Priority: data.Get("priority").(int), } return authenticationSubFlow } -func mapFromAuthenticationSubFlowToData(data *schema.ResourceData, authenticationSubFlow *keycloak.AuthenticationSubFlow) { +func mapFromAuthenticationSubFlowToData(keycloakClient *keycloak.KeycloakClient, ctx context.Context, data *schema.ResourceData, authenticationSubFlow *keycloak.AuthenticationSubFlow) { data.SetId(authenticationSubFlow.Id) data.Set("realm_id", authenticationSubFlow.RealmId) data.Set("parent_flow_alias", authenticationSubFlow.ParentFlowAlias) @@ -87,6 +92,9 @@ func mapFromAuthenticationSubFlowToData(data *schema.ResourceData, authenticatio data.Set("description", authenticationSubFlow.Description) data.Set("authenticator", authenticationSubFlow.Authenticator) data.Set("requirement", authenticationSubFlow.Requirement) + if prioritySupported, _ := keycloakClient.VersionIsGreaterThanOrEqualTo(ctx, keycloak.Version_25); prioritySupported { + data.Set("priority", authenticationSubFlow.Priority) + } } func resourceKeycloakAuthenticationSubFlowCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -98,7 +106,7 @@ func resourceKeycloakAuthenticationSubFlowCreate(ctx context.Context, data *sche if err != nil { return diag.FromErr(err) } - mapFromAuthenticationSubFlowToData(data, authenticationFlow) + mapFromAuthenticationSubFlowToData(keycloakClient, ctx, data, authenticationFlow) return resourceKeycloakAuthenticationSubFlowRead(ctx, data, meta) } @@ -113,7 +121,7 @@ func resourceKeycloakAuthenticationSubFlowRead(ctx context.Context, data *schema if err != nil { return handleNotFoundError(ctx, err, data) } - mapFromAuthenticationSubFlowToData(data, authenticationFlow) + mapFromAuthenticationSubFlowToData(keycloakClient, ctx, data, authenticationFlow) return nil } @@ -126,7 +134,7 @@ func resourceKeycloakAuthenticationSubFlowUpdate(ctx context.Context, data *sche if err != nil { return diag.FromErr(err) } - mapFromAuthenticationSubFlowToData(data, authenticationFlow) + mapFromAuthenticationSubFlowToData(keycloakClient, ctx, data, authenticationFlow) return nil } diff --git a/provider/test_utils.go b/provider/test_utils.go index cc93939b..ee023e3b 100644 --- a/provider/test_utils.go +++ b/provider/test_utils.go @@ -79,6 +79,17 @@ func skipIfVersionIsLessThanOrEqualTo(ctx context.Context, t *testing.T, keycloa } } +func skipIfVersionIsLessThan(ctx context.Context, t *testing.T, keycloakClient *keycloak.KeycloakClient, version keycloak.Version) { + ok, err := keycloakClient.VersionIsLessThan(ctx, version) + if err != nil { + t.Errorf("error checking keycloak version: %v", err) + } + + if ok { + t.Skipf("keycloak server version is less than %s, skipping...", version) + } +} + func skipIfVersionIsGreaterThanOrEqualTo(ctx context.Context, t *testing.T, keycloakClient *keycloak.KeycloakClient, version keycloak.Version) { ok, err := keycloakClient.VersionIsGreaterThanOrEqualTo(ctx, version) if err != nil {