Skip to content

Commit

Permalink
Feature/keycloak_required_action config values (#996)
Browse files Browse the repository at this point in the history
* Enable configs in required actions

Signed-off-by: Laureat Grepi <[email protected]>

* registry json file

Signed-off-by: Laureat Grepi <[email protected]>

* newline

Signed-off-by: Laureat Grepi <[email protected]>

* Enable configuring required action from terraform

Signed-off-by: Laureat Grepi <[email protected]>

* Allow required action config values to be passed from terraform

Signed-off-by: Laureat Grepi <[email protected]>

* update examples

Signed-off-by: Laureat Grepi <[email protected]>

* Add multivalued config in user profile

Signed-off-by: Laureat Grepi <[email protected]>

* fmt

Signed-off-by: Laureat Grepi <[email protected]>

* remove default value

Signed-off-by: Laureat Grepi <[email protected]>

* format

Signed-off-by: Laureat Grepi <[email protected]>

* remove multivalued

Signed-off-by: Laureat Grepi <[email protected]>

* format

Signed-off-by: Laureat Grepi <[email protected]>

---------

Signed-off-by: Laureat Grepi <[email protected]>
Co-authored-by: Laureat Grepi <[email protected]>
  • Loading branch information
laureat-natzka and Laureat Grepi authored Feb 11, 2025
1 parent 9411368 commit 0cda82d
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 21 deletions.
8 changes: 6 additions & 2 deletions docs/resources/required_action.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ resource "keycloak_realm" "realm" {
resource "keycloak_required_action" "required_action" {
realm_id = keycloak_realm.realm.realm
alias = "webauthn-register"
alias = "UPDATE_PASSWORD"
enabled = true
name = "Webauthn Register"
name = "Update Password"
config = {
max_auth_age = "600"
}
}
```

Expand All @@ -33,6 +36,7 @@ resource "keycloak_required_action" "required_action" {
- `enabled` - (Optional) When `false`, the required action is not enabled for new users. Defaults to `false`.
- `default_action` - (Optional) When `true`, the required action is set as the default action for new users. Defaults to `false`.
- `priority`- (Optional) The priority of the required action.
- `config`- (Optional) The configuration. Keys are specific to each configurable required action and not checked when applying.

## Import

Expand Down
24 changes: 18 additions & 6 deletions example/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ resource "keycloak_required_action" "custom-terms-and-conditions" {
name = "Custom Terms and Conditions"
}

resource "keycloak_required_action" "update-password" {
realm_id = keycloak_realm.test.realm
alias = "UPDATE_PASSWORD"
default_action = true
enabled = true
name = "Update Password"

config {
max_auth_age = "600"
}
}

resource "keycloak_required_action" "custom-configured_totp" {
realm_id = keycloak_realm.test.realm
alias = "CONFIGURE_TOTP"
Expand Down Expand Up @@ -1020,47 +1032,47 @@ resource "keycloak_authentication_execution" "browser-copy-cookie" {
parent_flow_alias = keycloak_authentication_flow.browser-copy-flow.alias
authenticator = "auth-cookie"
requirement = "ALTERNATIVE"
priority = 20
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
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"
priority = 30
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"
priority = 40
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
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"
priority = 60
priority = 60
}

resource "keycloak_authentication_execution_config" "config" {
Expand Down
23 changes: 11 additions & 12 deletions keycloak/required_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,30 @@ import (
)

type RequiredAction struct {
Id string `json:"-"`
RealmId string `json:"-"`
Alias string `json:"alias"`
Name string `json:"name"`
ProviderId string `json:"providerId"`
Enabled bool `json:"enabled"`
DefaultAction bool `json:"defaultAction"`
Priority int `json:"priority"`
Config map[string][]string `json:"config"`
Id string `json:"-"`
RealmId string `json:"-"`
Alias string `json:"alias"`
Name string `json:"name"`
ProviderId string `json:"providerId"`
Enabled bool `json:"enabled"`
DefaultAction bool `json:"defaultAction"`
Priority int `json:"priority"`
Config map[string]string `json:"config"`
}

func (requiredActions *RequiredAction) getConfig(val string) string {
if len(requiredActions.Config[val]) == 0 {
return ""
}
return requiredActions.Config[val][0]
return requiredActions.Config[val]
}

func (requiredActions *RequiredAction) getConfigOk(val string) (string, bool) {
if v, ok := requiredActions.Config[val]; ok {
return v[0], true
return v, true
}
return "", false
}

func (keycloakClient *KeycloakClient) GetRequiredActions(ctx context.Context, realmId string) ([]*RequiredAction, error) {
var requiredActions []*RequiredAction

Expand Down
13 changes: 12 additions & 1 deletion provider/resource_keycloak_required_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,21 @@ func resourceKeycloakRequiredAction() *schema.Resource {
Optional: true,
Computed: true,
},
"config": {
Type: schema.TypeMap,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
},
},
}
}

func getRequiredActionFromData(data *schema.ResourceData) (*keycloak.RequiredAction, error) {
config := make(map[string]string)
for key, value := range data.Get("config").(map[string]interface{}) {
config[key] = value.(string)
}

action := &keycloak.RequiredAction{
Id: fmt.Sprintf("%s/%s", data.Get("realm_id").(string), data.Get("alias").(string)),
RealmId: data.Get("realm_id").(string),
Expand All @@ -60,7 +70,7 @@ func getRequiredActionFromData(data *schema.ResourceData) (*keycloak.RequiredAct
Enabled: data.Get("enabled").(bool),
DefaultAction: data.Get("default_action").(bool),
Priority: data.Get("priority").(int),
Config: make(map[string][]string),
Config: config,
}

return action, nil
Expand All @@ -74,6 +84,7 @@ func setRequiredActionData(data *schema.ResourceData, action *keycloak.RequiredA
data.Set("enabled", action.Enabled)
data.Set("default_action", action.DefaultAction)
data.Set("priority", action.Priority)
data.Set("config", action.Config)
}

func resourceKeycloakRequiredActionsCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
Expand Down
41 changes: 41 additions & 0 deletions provider/resource_keycloak_required_action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,28 @@ func TestAccKeycloakRequiredAction_basic(t *testing.T) {
})
}

func TestAccKeycloakRequiredAction_withConfig(t *testing.T) {
realmName := acctest.RandomWithPrefix("tf-acc")
requiredActionAlias := "UPDATE_PASSWORD"
maxAuthAgeConfig := "3600"

resource.Test(t, resource.TestCase{
ProviderFactories: testAccProviderFactories,
PreCheck: func() { testAccPreCheck(t) },
Steps: []resource.TestStep{
{
Config: testKeycloakRequiredAction_withConfig(realmName, requiredActionAlias, 37, maxAuthAgeConfig),
Check: resource.ComposeTestCheckFunc(
testAccCheckKeycloakRequiresActionExists(realmName, requiredActionAlias),
resource.TestCheckResourceAttr("keycloak_required_action.required_action", "realm_id", realmName),
resource.TestCheckResourceAttr("keycloak_required_action.required_action", "config.%", "1"),
resource.TestCheckResourceAttr("keycloak_required_action.required_action", "config.max_auth_age", maxAuthAgeConfig),
),
},
},
})
}

func TestAccKeycloakRequiredAction_unregisteredAction(t *testing.T) {
realmName := acctest.RandomWithPrefix("tf-acc")
requiredActionAlias := "webauthn-register"
Expand Down Expand Up @@ -128,6 +150,25 @@ resource "keycloak_required_action" "required_action" {
`, realm, requiredActionAlias, priority)
}

func testKeycloakRequiredAction_withConfig(realm, requiredActionAlias string, priority int, maxAuthAgeConfig string) string {
return fmt.Sprintf(`
resource "keycloak_realm" "realm" {
realm = "%s"
}
resource "keycloak_required_action" "required_action" {
realm_id = "${keycloak_realm.realm.realm}"
alias = "%s"
default_action = true
enabled = true
name = "My required Action"
priority = %d
config = {
max_auth_age = "%s"
}
}
`, realm, requiredActionAlias, priority, maxAuthAgeConfig)
}

func testKeycloakRequiredAction_import(realm, requiredActionAlias string) string {
return fmt.Sprintf(`
resource "keycloak_realm" "realm" {
Expand Down

0 comments on commit 0cda82d

Please sign in to comment.