Skip to content

Commit

Permalink
feat(authFlow): added optional priority
Browse files Browse the repository at this point in the history
* Added optional priority to AuthenticationExecution
* Added optional priority to AuthenticationSubFLow

Closes: #296

Signed-off-by: Dennis Kniep <kniepdennis@gmail.com>
  • Loading branch information
denniskniep committed Dec 22, 2024
1 parent b35eb9f commit cb1d4e2
Show file tree
Hide file tree
Showing 9 changed files with 596 additions and 30 deletions.
9 changes: 4 additions & 5 deletions docs/resources/authentication_execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
~> Following limitation affects Keycloak < 25: 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.

## Example Usage

Expand All @@ -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
}
# second execution
Expand All @@ -38,10 +39,7 @@ resource "keycloak_authentication_execution" "execution_two" {
parent_flow_alias = "${keycloak_authentication_flow.flow.alias}"
authenticator = "identity-provider-redirector"
requirement = "ALTERNATIVE"
depends_on = [
keycloak_authentication_execution.execution_one
]
priority = 20
}
```

Expand All @@ -51,6 +49,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. Lower values will be executed prior higher values (Only supported by Keycloak >= 25).

## Import

Expand Down
2 changes: 2 additions & 0 deletions docs/resources/authentication_subflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
```

Expand All @@ -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 authenticator priority. Lower values will be executed prior higher values (Only supported by Keycloak >= 25).

## Import

Expand Down
18 changes: 6 additions & 12 deletions example/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -1020,53 +1020,47 @@ 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" {
realm_id = keycloak_realm.test.id
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" {
realm_id = keycloak_realm.test.id
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" {
realm_id = keycloak_realm.test.id
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" {
realm_id = keycloak_realm.test.id
parent_flow_alias = keycloak_authentication_subflow.browser-copy-flow-forms.alias
authenticator = "auth-username-password-form"
requirement = "REQUIRED"
priority = 50
}

resource "keycloak_authentication_execution" "browser-copy-otp" {
realm_id = keycloak_realm.test.id
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 = 60
}

resource "keycloak_authentication_execution_config" "config" {
Expand Down
3 changes: 3 additions & 0 deletions keycloak/authentication_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,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
Expand Down Expand Up @@ -48,6 +49,7 @@ type AuthenticationExecutionInfo struct {
Level int `json:"level"`
ProviderId string `json:"providerId"`
Requirement string `json:"requirement"`
Priority int `json:"priority"`
}

type AuthenticationExecutionList []*AuthenticationExecutionInfo
Expand Down Expand Up @@ -154,6 +156,7 @@ func (keycloakClient *KeycloakClient) UpdateAuthenticationExecution(ctx context.
ParentFlowAlias: execution.ParentFlowAlias,
Id: execution.Id,
Requirement: execution.Requirement,
Priority: execution.Priority,
}
return keycloakClient.UpdateAuthenticationExecutionRequirement(ctx, authenticationExecutionUpdateRequirement)
}
Expand Down
8 changes: 3 additions & 5 deletions keycloak/authentication_subflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,7 @@ func (keycloakClient *KeycloakClient) NewAuthenticationSubFlow(ctx context.Conte
}
authenticationSubFlow.Id = getIdFromLocationHeader(location)

if authenticationSubFlow.Requirement != "DISABLED" {
return keycloakClient.UpdateAuthenticationSubFlow(ctx, authenticationSubFlow)
}
return nil
return keycloakClient.UpdateAuthenticationSubFlow(ctx, authenticationSubFlow)
}

func (keycloakClient *KeycloakClient) GetAuthenticationSubFlow(ctx context.Context, realmId, parentFlowAlias, id string) (*AuthenticationSubFlow, error) {
Expand All @@ -71,7 +68,7 @@ func (keycloakClient *KeycloakClient) GetAuthenticationSubFlow(ctx context.Conte
}
authenticationSubFlow.Authenticator = subFlowExecution.Authenticator
authenticationSubFlow.Requirement = subFlowExecution.Requirement

authenticationSubFlow.Priority = subFlowExecution.Priority
return &authenticationSubFlow, nil
}

Expand Down Expand Up @@ -110,6 +107,7 @@ func (keycloakClient *KeycloakClient) UpdateAuthenticationSubFlow(ctx context.Co
ParentFlowAlias: authenticationSubFlow.ParentFlowAlias,
Id: executionId,
Requirement: authenticationSubFlow.Requirement,
Priority: authenticationSubFlow.Priority,
}
return keycloakClient.UpdateAuthenticationExecutionRequirement(ctx, authenticationExecutionUpdateRequirement)

Expand Down
34 changes: 30 additions & 4 deletions provider/resource_keycloak_authentication_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
},
}
}
Expand All @@ -54,18 +58,31 @@ 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(ctx context.Context, keycloakClient *keycloak.KeycloakClient, data *schema.ResourceData, authenticationExecution *keycloak.AuthenticationExecution) error {

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)

versionOk, err := keycloakClient.VersionIsGreaterThanOrEqualTo(ctx, keycloak.Version_25)
if err != nil {
return err
}

if versionOk {
data.Set("priority", authenticationExecution.Priority)
}

return nil
}

func mapFromAuthenticationExecutionInfoToData(data *schema.ResourceData, authenticationExecutionInfo *keycloak.AuthenticationExecutionInfo) {
Expand All @@ -85,7 +102,10 @@ func resourceKeycloakAuthenticationExecutionCreate(ctx context.Context, data *sc
return diag.FromErr(err)
}

mapFromAuthenticationExecutionToData(data, authenticationExecution)
err = mapFromAuthenticationExecutionToData(ctx, keycloakClient, data, authenticationExecution)
if err != nil {
return diag.FromErr(err)
}

return resourceKeycloakAuthenticationExecutionRead(ctx, data, meta)
}
Expand All @@ -102,7 +122,10 @@ func resourceKeycloakAuthenticationExecutionRead(ctx context.Context, data *sche
return handleNotFoundError(ctx, err, data)
}

mapFromAuthenticationExecutionToData(data, authenticationExecution)
err = mapFromAuthenticationExecutionToData(ctx, keycloakClient, data, authenticationExecution)
if err != nil {
return diag.FromErr(err)
}

return nil
}
Expand All @@ -117,7 +140,10 @@ func resourceKeycloakAuthenticationExecutionUpdate(ctx context.Context, data *sc
return diag.FromErr(err)
}

mapFromAuthenticationExecutionToData(data, authenticationExecution)
err = mapFromAuthenticationExecutionToData(ctx, keycloakClient, data, authenticationExecution)
if err != nil {
return diag.FromErr(err)
}

return nil
}
Expand Down
Loading

0 comments on commit cb1d4e2

Please sign in to comment.