From b5042b013d9ec5f4dc90818e4c753656e1498556 Mon Sep 17 00:00:00 2001 From: Tibo Beijen Date: Mon, 15 Jan 2024 10:28:00 +0100 Subject: [PATCH 1/4] POC. Succesfully provisions realm & default_roles in one go. Exposes RefreshAuth on Realm which can be improved. --- keycloak/realm.go | 9 ++++++++- provider/resource_keycloak_default_roles.go | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/keycloak/realm.go b/keycloak/realm.go index 6afdf82ee..4db6d446e 100644 --- a/keycloak/realm.go +++ b/keycloak/realm.go @@ -3,8 +3,9 @@ package keycloak import ( "context" "fmt" - "github.com/mrparkers/terraform-provider-keycloak/keycloak/types" "strings" + + "github.com/mrparkers/terraform-provider-keycloak/keycloak/types" ) type Key struct { @@ -168,6 +169,12 @@ type SmtpServer struct { Password string `json:"password,omitempty"` } +func (keycloakClient *KeycloakClient) RefreshAuth(ctx context.Context) error { + err := keycloakClient.login(ctx) + + return err +} + func (keycloakClient *KeycloakClient) NewRealm(ctx context.Context, realm *Realm) error { _, _, err := keycloakClient.post(ctx, "/realms", realm) diff --git a/provider/resource_keycloak_default_roles.go b/provider/resource_keycloak_default_roles.go index 30f8e0fa6..df36f4e25 100644 --- a/provider/resource_keycloak_default_roles.go +++ b/provider/resource_keycloak_default_roles.go @@ -4,9 +4,10 @@ import ( "context" "errors" "fmt" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "strings" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/mrparkers/terraform-provider-keycloak/keycloak" ) @@ -98,11 +99,20 @@ func resourceKeycloakDefaultRolesReconcile(ctx context.Context, data *schema.Res defaultRoles := mapFromDataToDefaultRoles(data) - realm, err := keycloakClient.GetRealm(ctx, defaultRoles.RealmId) + var realm *keycloak.Realm + var err error + // realm, err := keycloakClient.GetRealm(ctx, defaultRoles.RealmId) + realm, err = keycloakClient.GetRealm(ctx, defaultRoles.RealmId) if err != nil { return diag.FromErr(err) } + if realm.DefaultRole == nil { + tflog.Warn(ctx, "Realm does not contain DefaultRole property. Renewing access token to bypass any caching in Keycloak.") + keycloakClient.RefreshAuth(ctx) + realm, err = keycloakClient.GetRealm(ctx, defaultRoles.RealmId) + } + data.SetId(realm.DefaultRole.Id) composites, err := keycloakClient.GetDefaultRoles(ctx, defaultRoles.RealmId, realm.DefaultRole.Id) From 2cc362cbdcab689071bb7674c6fa436138730a65 Mon Sep 17 00:00:00 2001 From: Tibo Beijen Date: Mon, 15 Jan 2024 18:09:09 +0100 Subject: [PATCH 2/4] Implemented retry within GetRealm --- keycloak/realm.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/keycloak/realm.go b/keycloak/realm.go index 4db6d446e..5321a101d 100644 --- a/keycloak/realm.go +++ b/keycloak/realm.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/mrparkers/terraform-provider-keycloak/keycloak/types" ) @@ -169,12 +170,6 @@ type SmtpServer struct { Password string `json:"password,omitempty"` } -func (keycloakClient *KeycloakClient) RefreshAuth(ctx context.Context) error { - err := keycloakClient.login(ctx) - - return err -} - func (keycloakClient *KeycloakClient) NewRealm(ctx context.Context, realm *Realm) error { _, _, err := keycloakClient.post(ctx, "/realms", realm) @@ -183,11 +178,21 @@ func (keycloakClient *KeycloakClient) NewRealm(ctx context.Context, realm *Realm func (keycloakClient *KeycloakClient) GetRealm(ctx context.Context, name string) (*Realm, error) { var realm Realm + var err error + + err = keycloakClient.get(ctx, fmt.Sprintf("/realms/%s", name), &realm, nil) + if err == nil && realm.DefaultRole == nil { + tflog.Warn(ctx, "Realm does not contain expected properties. Refreshing access token to bypass any caching in Keycloak and fetching realm again.") + err = keycloakClient.refresh(ctx) + if err == nil { + err = keycloakClient.get(ctx, fmt.Sprintf("/realms/%s", name), &realm, nil) + } + } - err := keycloakClient.get(ctx, fmt.Sprintf("/realms/%s", name), &realm, nil) if err != nil { return nil, err } + return &realm, nil } From 1cc4c2692e82bb36744b97fa977ddff6f5dbb81f Mon Sep 17 00:00:00 2001 From: Tibo Beijen Date: Mon, 15 Jan 2024 18:24:10 +0100 Subject: [PATCH 3/4] Reverted changes in defaultRoles resource --- provider/resource_keycloak_default_roles.go | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/provider/resource_keycloak_default_roles.go b/provider/resource_keycloak_default_roles.go index df36f4e25..30f8e0fa6 100644 --- a/provider/resource_keycloak_default_roles.go +++ b/provider/resource_keycloak_default_roles.go @@ -4,10 +4,9 @@ import ( "context" "errors" "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "strings" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/mrparkers/terraform-provider-keycloak/keycloak" ) @@ -99,20 +98,11 @@ func resourceKeycloakDefaultRolesReconcile(ctx context.Context, data *schema.Res defaultRoles := mapFromDataToDefaultRoles(data) - var realm *keycloak.Realm - var err error - // realm, err := keycloakClient.GetRealm(ctx, defaultRoles.RealmId) - realm, err = keycloakClient.GetRealm(ctx, defaultRoles.RealmId) + realm, err := keycloakClient.GetRealm(ctx, defaultRoles.RealmId) if err != nil { return diag.FromErr(err) } - if realm.DefaultRole == nil { - tflog.Warn(ctx, "Realm does not contain DefaultRole property. Renewing access token to bypass any caching in Keycloak.") - keycloakClient.RefreshAuth(ctx) - realm, err = keycloakClient.GetRealm(ctx, defaultRoles.RealmId) - } - data.SetId(realm.DefaultRole.Id) composites, err := keycloakClient.GetDefaultRoles(ctx, defaultRoles.RealmId, realm.DefaultRole.Id) From 641514155e0e7c8491cd97a593fb3098f1fec3ef Mon Sep 17 00:00:00 2001 From: Tibo Beijen Date: Fri, 19 Jan 2024 07:08:06 +0100 Subject: [PATCH 4/4] Simplified fix. Now always refreshing token after create realm. --- keycloak/realm.go | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/keycloak/realm.go b/keycloak/realm.go index 5321a101d..e2c105423 100644 --- a/keycloak/realm.go +++ b/keycloak/realm.go @@ -5,7 +5,6 @@ import ( "fmt" "strings" - "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/mrparkers/terraform-provider-keycloak/keycloak/types" ) @@ -173,26 +172,25 @@ type SmtpServer struct { func (keycloakClient *KeycloakClient) NewRealm(ctx context.Context, realm *Realm) error { _, _, err := keycloakClient.post(ctx, "/realms", realm) + if err != nil { + return err + } + + // Always refresh token after creating realm to include any admin roles for the new realm. + // This works around keycloak issue 26301 where, since v22, newly created realms no longer return 403, + // bypassing the refresh mechanism in KeycloakClient.sendRequest + err = keycloakClient.refresh(ctx) + return err } func (keycloakClient *KeycloakClient) GetRealm(ctx context.Context, name string) (*Realm, error) { var realm Realm - var err error - - err = keycloakClient.get(ctx, fmt.Sprintf("/realms/%s", name), &realm, nil) - if err == nil && realm.DefaultRole == nil { - tflog.Warn(ctx, "Realm does not contain expected properties. Refreshing access token to bypass any caching in Keycloak and fetching realm again.") - err = keycloakClient.refresh(ctx) - if err == nil { - err = keycloakClient.get(ctx, fmt.Sprintf("/realms/%s", name), &realm, nil) - } - } + err := keycloakClient.get(ctx, fmt.Sprintf("/realms/%s", name), &realm, nil) if err != nil { return nil, err } - return &realm, nil }