diff --git a/docs/resources/authentication_execution.md b/docs/resources/authentication_execution.md index 15f5b1bd3..637edb494 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 e22d189e8..29ebb91e6 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/keycloak/authentication_execution.go b/keycloak/authentication_execution.go index 971448eea..790150a18 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 593facb94..2576abf22 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 9fcc5d809..0f0cfcbd8 100644 --- a/keycloak/version.go +++ b/keycloak/version.go @@ -22,6 +22,12 @@ const ( Version_17 Version = "17.0.0" Version_18 Version = "18.0.0" Version_19 Version = "19.0.0" + Version_20 Version = "20.0.0" + Version_21 Version = "21.0.0" + Version_22 Version = "22.0.0" + Version_23 Version = "23.0.0" + Version_24 Version = "24.0.0" + Version_25 Version = "25.0.0" ) func (keycloakClient *KeycloakClient) VersionIsGreaterThanOrEqualTo(ctx context.Context, versionString Version) (bool, error) { diff --git a/provider/data_source_keycloak_authentication_execution.go b/provider/data_source_keycloak_authentication_execution.go index b0db979cf..a8de26d54 100644 --- a/provider/data_source_keycloak_authentication_execution.go +++ b/provider/data_source_keycloak_authentication_execution.go @@ -39,7 +39,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/resource_keycloak_authentication_execution.go b/provider/resource_keycloak_authentication_execution.go index 59a3d21ba..acd640bf6 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_subflow.go b/provider/resource_keycloak_authentication_subflow.go index d9e5728e6..543e9385b 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 }