Skip to content

Commit

Permalink
add extra_config for openid clients to handle custom attributes (keyc…
Browse files Browse the repository at this point in the history
  • Loading branch information
Olivier BOUDET committed Aug 19, 2021
1 parent 7f49369 commit 4e326f6
Show file tree
Hide file tree
Showing 43 changed files with 954 additions and 52 deletions.
9 changes: 8 additions & 1 deletion docs/resources/openid_client.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ resource "keycloak_openid_client" "openid_client" {
]
login_theme = "keycloak"
extra_config = {
"key1" = "value1"
"key2" = "value2"
}
}
```

Expand Down Expand Up @@ -76,8 +81,10 @@ is set to `true`.
- `authorization` - (Optional) When this block is present, fine-grained authorization will be enabled for this client. The client's `access_type` must be `CONFIDENTIAL`, and `service_accounts_enabled` must be `true`. This block has the following arguments:
- `policy_enforcement_mode` - (Required) Dictates how policies are enforced when evaluating authorization requests. Can be one of `ENFORCING`, `PERMISSIVE`, or `DISABLED`.
- `decision_strategy` - (Optional) Dictates how the policies associated with a given permission are evaluated and how a final decision is obtained. Could be one of `AFFIRMATIVE`, `CONSENSUS`, or `UNANIMOUS`. Applies to permissions.
- `allow_remote_resource_management` - (Optional) When `true`, resources can be managed remotely by the resource server. Defaults to `false`.
- `allow_remote_resource_management` - (Optional) When `true`, resources can be managed remotely by the resource server. Defaults to
`false`.
- `keep_defaults` - (Optional) When `true`, defaults set by Keycloak will be respected. Defaults to `false`.
- `extra_config` - (Optional) A map of key/value pairs to add extra configuration attributes to this client. This can be used for custom attributes, or to add configuration attributes that is not yet supported by this Terraform provider. Use this attribute at your own risk, as s may conflict with top-level configuration attributes in future provider updates.

## Attributes Reference

Expand Down
4 changes: 4 additions & 0 deletions example/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ resource "keycloak_openid_client" "test_client" {
pkce_code_challenge_method = "plain"

login_theme = "keycloak"

extra_config = {
customAttribute = "a test custom value"
}
}

resource "keycloak_openid_client_scope" "test_default_client_scope" {
Expand Down
77 changes: 68 additions & 9 deletions keycloak/openid_client.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package keycloak

import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
)

type OpenidClientRole struct {
Expand Down Expand Up @@ -55,15 +59,16 @@ type OpenidClient struct {
}

type OpenidClientAttributes struct {
PkceCodeChallengeMethod string `json:"pkce.code.challenge.method"`
ExcludeSessionStateFromAuthResponse KeycloakBoolQuoted `json:"exclude.session.state.from.auth.response"`
AccessTokenLifespan string `json:"access.token.lifespan"`
LoginTheme string `json:"login_theme"`
ClientOfflineSessionIdleTimeout string `json:"client.offline.session.idle.timeout,omitempty"`
ClientOfflineSessionMaxLifespan string `json:"client.offline.session.max.lifespan,omitempty"`
ClientSessionIdleTimeout string `json:"client.session.idle.timeout,omitempty"`
ClientSessionMaxLifespan string `json:"client.session.max.lifespan,omitempty"`
UseRefreshTokens KeycloakBoolQuoted `json:"use.refresh.tokens"`
PkceCodeChallengeMethod string `json:"pkce.code.challenge.method"`
ExcludeSessionStateFromAuthResponse KeycloakBoolQuoted `json:"exclude.session.state.from.auth.response"`
AccessTokenLifespan string `json:"access.token.lifespan"`
LoginTheme string `json:"login_theme"`
ClientOfflineSessionIdleTimeout string `json:"client.offline.session.idle.timeout,omitempty"`
ClientOfflineSessionMaxLifespan string `json:"client.offline.session.max.lifespan,omitempty"`
ClientSessionIdleTimeout string `json:"client.session.idle.timeout,omitempty"`
ClientSessionMaxLifespan string `json:"client.session.max.lifespan,omitempty"`
UseRefreshTokens KeycloakBoolQuoted `json:"use.refresh.tokens"`
ExtraConfig map[string]interface{} `json:"-"`
}

type OpenidAuthenticationFlowBindingOverrides struct {
Expand Down Expand Up @@ -344,3 +349,57 @@ func (keycloakClient *KeycloakClient) DetachOpenidClientDefaultScopes(realmId, c
func (keycloakClient *KeycloakClient) DetachOpenidClientOptionalScopes(realmId, clientId string, scopeNames []string) error {
return keycloakClient.detachOpenidClientScopes(realmId, clientId, "optional", scopeNames)
}

func (f *OpenidClientAttributes) UnmarshalJSON(data []byte) error {
f.ExtraConfig = map[string]interface{}{}
err := json.Unmarshal(data, &f.ExtraConfig)
if err != nil {
return err
}
v := reflect.ValueOf(f).Elem()
for i := 0; i < v.NumField(); i++ {
structField := v.Type().Field(i)
jsonKey := strings.Split(structField.Tag.Get("json"), ",")[0]
if jsonKey != "-" {
value, ok := f.ExtraConfig[jsonKey]
if ok {
field := v.FieldByName(structField.Name)
if field.IsValid() && field.CanSet() {
if field.Kind() == reflect.String {
field.SetString(value.(string))
} else if field.Kind() == reflect.Bool {
boolVal, err := strconv.ParseBool(value.(string))
if err == nil {
field.Set(reflect.ValueOf(KeycloakBoolQuoted(boolVal)))
}
}
delete(f.ExtraConfig, jsonKey)
}
}
}
}
return nil
}

func (f *OpenidClientAttributes) MarshalJSON() ([]byte, error) {
out := map[string]interface{}{}

for k, v := range f.ExtraConfig {
out[k] = v
}
v := reflect.ValueOf(f).Elem()
for i := 0; i < v.NumField(); i++ {
jsonKey := strings.Split(v.Type().Field(i).Tag.Get("json"), ",")[0]
if jsonKey != "-" {
field := v.Field(i)
if field.IsValid() && field.CanSet() {
if field.Kind() == reflect.String {
out[jsonKey] = field.String()
} else if field.Kind() == reflect.Bool {
out[jsonKey] = KeycloakBoolQuoted(field.Bool())
}
}
}
}
return json.Marshal(out)
}
5 changes: 5 additions & 0 deletions provider/data_source_keycloak_openid_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ func dataSourceKeycloakOpenidClient() *schema.Resource {
Optional: true,
Default: true,
},
"extra_config": {
Type: schema.TypeMap,
Optional: true,
Computed: true,
},
},
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package provider

import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"regexp"
"testing"

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

func TestAccKeycloakDataSourceOpenidClientAuthorizationPolicy_basic(t *testing.T) {
Expand Down Expand Up @@ -53,6 +54,10 @@ resource "keycloak_openid_client" "test" {
authorization {
policy_enforcement_mode = "ENFORCING"
}
extra_config = {
"backchannel.logout.revoke.offline.tokens" = "false"
"backchannel.logout.session.required" = "true"
}
}
data "keycloak_openid_client_authorization_policy" "test" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package provider

import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"regexp"
"testing"

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

func TestAccKeycloakDataSourceOpenidClientServiceAccountUser_basic(t *testing.T) {
Expand Down Expand Up @@ -53,6 +54,10 @@ resource "keycloak_openid_client" "test" {
web_origins = [
"http://localhost"
]
extra_config = {
"backchannel.logout.revoke.offline.tokens" = "false"
"backchannel.logout.session.required" = "true"
}
}
data keycloak_openid_client_service_account_user test {
Expand Down
57 changes: 56 additions & 1 deletion provider/data_source_keycloak_openid_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package provider

import (
"fmt"
"testing"

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

func TestAccKeycloakDataSourceOpenidClient_basic(t *testing.T) {
Expand Down Expand Up @@ -64,6 +65,10 @@ resource "keycloak_openid_client" "test" {
"http://localhost"
]
full_scope_allowed = false
extra_config = {
"backchannel.logout.revoke.offline.tokens" = "false"
"backchannel.logout.session.required" = "true"
}
}
data "keycloak_openid_client" "test" {
Expand All @@ -76,3 +81,53 @@ data "keycloak_openid_client" "test" {
}
`, testAccRealm.Realm, clientId, clientId)
}

func TestAccKeycloakDataSourceOpenidClient_extraConfig(t *testing.T) {
t.Parallel()
clientId := acctest.RandomWithPrefix("tf-acc-test-extra-config")
dataSourceName := "data.keycloak_openid_client.test-extra-config"
resourceName := "keycloak_openid_client.test-extra-config"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: testAccProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccKeycloakOpenidClientConfig_extraConfig(clientId),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrPair(dataSourceName, "key1", resourceName, "value1"),
),
},
},
})
}

func testAccKeycloakOpenidClientConfig_extraConfig(clientId string) string {
return fmt.Sprintf(`
data "keycloak_realm" "realm" {
realm = "%s"
}
resource "keycloak_openid_client" "test-extra-config" {
name = "%s"
client_id = "%s"
realm_id = data.keycloak_realm.realm.id
description = "a test openid client with extra_conf"
access_type = "CONFIDENTIAL"
extra_config = {
"backchannel.logout.revoke.offline.tokens" = "false"
"backchannel.logout.session.required" = "true"
"key1" = "value1"
}
}
data "keycloak_openid_client" "test-extra-config" {
realm_id = data.keycloak_realm.realm.id
client_id = keycloak_openid_client.test-extra-config.client_id
depends_on = [
keycloak_openid_client.test-extra-config,
]
}
`, testAccRealm.Realm, clientId, clientId)
}
7 changes: 6 additions & 1 deletion provider/data_source_keycloak_role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package provider

import (
"fmt"
"testing"

"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"
"testing"
)

func TestAccKeycloakDataSourceRole_basic(t *testing.T) {
Expand Down Expand Up @@ -81,6 +82,10 @@ resource "keycloak_openid_client" "client" {
client_id = "%s"
realm_id = data.keycloak_realm.realm.id
access_type = "CONFIDENTIAL"
extra_config = {
"backchannel.logout.revoke.offline.tokens" = "false"
"backchannel.logout.session.required" = "true"
}
}
resource "keycloak_role" "realm_role" {
Expand Down
Loading

0 comments on commit 4e326f6

Please sign in to comment.