Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix user & userprofile tests #1042

Merged
merged 3 commits into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ KEYCLOAK_CLIENT_ID=terraform \
KEYCLOAK_CLIENT_SECRET=884e0f95-0f42-4a63-9b1f-94274655669e \
KEYCLOAK_CLIENT_TIMEOUT=5 \
KEYCLOAK_REALM=master \
KEYCLOAK_TEST_PASSWORD_GRANT=true \
KEYCLOAK_URL="http://localhost:8080" \
make testacc
```
Expand Down
11 changes: 3 additions & 8 deletions docs/resources/realm_user_profile.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@ page_title: "keycloak_realm_user_profile Resource"
Allows for managing Realm User Profiles within Keycloak.

A user profile defines a schema for representing user attributes and how they are managed within a realm.
This is a preview feature, hence not fully supported and disabled by default.
To enable it, start the server with one of the following flags:
- WildFly distribution: `-Dkeycloak.profile.feature.declarative_user_profile=enabled`
- Quarkus distribution: `--features=preview` or `--features=declarative-user-profile`

Information for Keycloak versions < 24:
The realm linked to the `keycloak_realm_user_profile` resource must have the user profile feature enabled.
It can be done via the administration UI, or by setting the `userProfileEnabled` realm attribute to `true`.

Expand All @@ -20,14 +17,11 @@ It can be done via the administration UI, or by setting the `userProfileEnabled`
```hcl
resource "keycloak_realm" "realm" {
realm = "my-realm"

attributes = {
userProfileEnabled = true
}
}

resource "keycloak_realm_user_profile" "userprofile" {
realm_id = keycloak_realm.my_realm.id
unmanaged_attribute_policy = "ENABLED"

attribute {
name = "field1"
Expand Down Expand Up @@ -98,6 +92,7 @@ resource "keycloak_realm_user_profile" "userprofile" {
- `realm_id` - (Required) The ID of the realm the user profile applies to.
- `attribute` - (Optional) An ordered list of [attributes](#attribute-arguments).
- `group` - (Optional) A list of [groups](#group-arguments).
- `unmanaged_attribute_policy` - (Optional) Unmanaged attributes are user attributes not explicitly defined in the user profile configuration. By default, unmanaged attributes are not enabled. Value could be one of `DISABLED`, `ENABLED`, `ADMIN_EDIT` or `ADMIN_VIEW`. If value is not specified it means `DISABLED`

### Attribute Arguments

Expand Down
5 changes: 3 additions & 2 deletions keycloak/realm_user_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ type RealmUserProfileGroup struct {
}

type RealmUserProfile struct {
Attributes []*RealmUserProfileAttribute `json:"attributes"`
Groups []*RealmUserProfileGroup `json:"groups,omitempty"`
Attributes []*RealmUserProfileAttribute `json:"attributes"`
Groups []*RealmUserProfileGroup `json:"groups,omitempty"`
UnmanagedAttributePolicy *string `json:"unmanagedAttributePolicy,omitempty"`
}

func (keycloakClient *KeycloakClient) UpdateRealmUserProfile(ctx context.Context, realmId string, realmUserProfile *RealmUserProfile) error {
Expand Down
16 changes: 16 additions & 0 deletions keycloak/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,19 @@ func (keycloakClient *KeycloakClient) VersionIsLessThanOrEqualTo(ctx context.Con

return keycloakClient.version.LessThanOrEqual(v), nil
}

func (keycloakClient *KeycloakClient) VersionIsLessThan(ctx context.Context, versionString Version) (bool, error) {
if keycloakClient.version == nil {
err := keycloakClient.login(ctx)
if err != nil {
return false, err
}
}

v, err := version.NewVersion(string(versionString))
if err != nil {
return false, nil
}

return keycloakClient.version.LessThan(v), nil
}
1 change: 0 additions & 1 deletion provider/data_source_keycloak_user_realm_roles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
)

func TestAccKeycloakDataSourceUserRoles(t *testing.T) {
t.Parallel()
username := acctest.RandomWithPrefix("tf-acc")
email := acctest.RandomWithPrefix("tf-acc") + "@fakedomain.com"
realmRoleName := acctest.RandomWithPrefix("tf-acc")
Expand Down
2 changes: 0 additions & 2 deletions provider/data_source_keycloak_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (
)

func TestAccKeycloakDataSourceUser(t *testing.T) {
t.Parallel()

username := acctest.RandomWithPrefix("tf-acc")
email := acctest.RandomWithPrefix("tf-acc") + "@fakedomain.com"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
)

func TestAccKeycloakOpenidClientAuthorizationUserPolicy(t *testing.T) {
t.Parallel()
clientId := acctest.RandomWithPrefix("tf-acc")
username := acctest.RandomWithPrefix("tf-acc")
email := acctest.RandomWithPrefix("tf-acc") + "@fakedomain.com"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
)

func TestAccKeycloakOpenidClientPermission_basic(t *testing.T) {
t.Parallel()
clientId := acctest.RandomWithPrefix("tf-acc")
username := acctest.RandomWithPrefix("tf-acc")
email := acctest.RandomWithPrefix("tf-acc") + "@fakedomain.com"
Expand Down
122 changes: 110 additions & 12 deletions provider/resource_keycloak_realm_user_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,24 @@ package provider
import (
"context"
"encoding/json"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/keycloak/terraform-provider-keycloak/keycloak"
)

const (
DISABLED string = "DISABLED"
ENABLED = "ENABLED"
ADMIN_VIEW = "ADMIN_VIEW"
ADMIN_EDIT = "ADMIN_EDIT"
)

const USER_PROFILE_ENABLED string = "userProfileEnabled"

func resourceKeycloakRealmUserProfile() *schema.Resource {
return &schema.Resource{
CreateContext: resourceKeycloakRealmUserProfileCreate,
Expand Down Expand Up @@ -125,6 +136,12 @@ func resourceKeycloakRealmUserProfile() *schema.Resource {
},
},
},
"unmanaged_attribute_policy": {
Type: schema.TypeString,
Default: DISABLED,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{DISABLED, ENABLED, ADMIN_VIEW, ADMIN_EDIT}, false),
},
},
}
}
Expand Down Expand Up @@ -287,13 +304,23 @@ func getRealmUserProfileGroupsFromData(lst []interface{}) []*keycloak.RealmUserP
return groups
}

func getRealmUserProfileFromData(data *schema.ResourceData) *keycloak.RealmUserProfile {
func getRealmUserProfileFromData(ctx context.Context, keycloakClient *keycloak.KeycloakClient, data *schema.ResourceData) (*keycloak.RealmUserProfile, error) {
realmUserProfile := &keycloak.RealmUserProfile{}

realmUserProfile.Attributes = getRealmUserProfileAttributesFromData(data.Get("attribute").([]interface{}))
realmUserProfile.Groups = getRealmUserProfileGroupsFromData(data.Get("group").(*schema.Set).List())

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

unmanagedAttr, unmanagedAttrOk := data.Get("unmanaged_attribute_policy").(string)
if versionOk && unmanagedAttrOk && unmanagedAttr != DISABLED {
realmUserProfile.UnmanagedAttributePolicy = &unmanagedAttr
}

return realmUserProfile, nil
}

func getRealmUserProfileAttributeData(attr *keycloak.RealmUserProfileAttribute) map[string]interface{} {
Expand Down Expand Up @@ -388,7 +415,7 @@ func getRealmUserProfileGroupData(group *keycloak.RealmUserProfileGroup) map[str
return groupData
}

func setRealmUserProfileData(data *schema.ResourceData, realmUserProfile *keycloak.RealmUserProfile) {
func setRealmUserProfileData(ctx context.Context, keycloakClient *keycloak.KeycloakClient, data *schema.ResourceData, realmUserProfile *keycloak.RealmUserProfile) error {
attributes := make([]interface{}, 0)
for _, attr := range realmUserProfile.Attributes {
attributes = append(attributes, getRealmUserProfileAttributeData(attr))
Expand All @@ -400,16 +427,39 @@ func setRealmUserProfileData(data *schema.ResourceData, realmUserProfile *keyclo
groups = append(groups, getRealmUserProfileGroupData(group))
}
data.Set("group", groups)

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

if versionOk {
if realmUserProfile.UnmanagedAttributePolicy != nil {
data.Set("unmanaged_attribute_policy", *realmUserProfile.UnmanagedAttributePolicy)
} else {
data.Set("unmanaged_attribute_policy", DISABLED)
}
}
return nil
}

func resourceKeycloakRealmUserProfileCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
keycloakClient := meta.(*keycloak.KeycloakClient)
realmId := data.Get("realm_id").(string)

err := checkUserProfileEnabled(ctx, keycloakClient, realmId)
if err != nil {
return diag.FromErr(err)
}

data.SetId(realmId)

realmUserProfile := getRealmUserProfileFromData(data)
realmUserProfile, err := getRealmUserProfileFromData(ctx, keycloakClient, data)
if err != nil {
return diag.FromErr(err)
}

err := keycloakClient.UpdateRealmUserProfile(ctx, realmId, realmUserProfile)
err = keycloakClient.UpdateRealmUserProfile(ctx, realmId, realmUserProfile)
if err != nil {
return diag.FromErr(err)
}
Expand All @@ -427,7 +477,10 @@ func resourceKeycloakRealmUserProfileRead(ctx context.Context, data *schema.Reso
return handleNotFoundError(ctx, err, data)
}

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

return nil
}
Expand All @@ -436,10 +489,16 @@ func resourceKeycloakRealmUserProfileDelete(ctx context.Context, data *schema.Re
keycloakClient := meta.(*keycloak.KeycloakClient)
realmId := data.Get("realm_id").(string)

err := checkUserProfileEnabled(ctx, keycloakClient, realmId)
if err != nil {
return diag.FromErr(err)
}

// The realm user profile cannot be deleted, so instead we set it back to its "zero" values.
realmUserProfile := &keycloak.RealmUserProfile{
Attributes: []*keycloak.RealmUserProfileAttribute{},
Groups: []*keycloak.RealmUserProfileGroup{},
Attributes: []*keycloak.RealmUserProfileAttribute{},
Groups: []*keycloak.RealmUserProfileGroup{},
UnmanagedAttributePolicy: nil,
}

if ok, _ := keycloakClient.VersionIsGreaterThanOrEqualTo(ctx, keycloak.Version_23); ok {
Expand All @@ -450,7 +509,7 @@ func resourceKeycloakRealmUserProfileDelete(ctx context.Context, data *schema.Re
}
}

err := keycloakClient.UpdateRealmUserProfile(ctx, realmId, realmUserProfile)
err = keycloakClient.UpdateRealmUserProfile(ctx, realmId, realmUserProfile)
if err != nil {
return diag.FromErr(err)
}
Expand All @@ -462,14 +521,53 @@ func resourceKeycloakRealmUserProfileUpdate(ctx context.Context, data *schema.Re
keycloakClient := meta.(*keycloak.KeycloakClient)

realmId := data.Get("realm_id").(string)
realmUserProfile := getRealmUserProfileFromData(data)
err := checkUserProfileEnabled(ctx, keycloakClient, realmId)
if err != nil {
return diag.FromErr(err)
}

err := keycloakClient.UpdateRealmUserProfile(ctx, realmId, realmUserProfile)
realmUserProfile, err := getRealmUserProfileFromData(ctx, keycloakClient, data)
if err != nil {
return diag.FromErr(err)
}

setRealmUserProfileData(data, realmUserProfile)
err = keycloakClient.UpdateRealmUserProfile(ctx, realmId, realmUserProfile)
if err != nil {
return diag.FromErr(err)
}

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

return nil
}

func checkUserProfileEnabled(ctx context.Context, keycloakClient *keycloak.KeycloakClient, realmId string) error {
versionOk, err := keycloakClient.VersionIsGreaterThanOrEqualTo(ctx, keycloak.Version_24)
if err != nil {
return err
}

if versionOk {
return nil
}

realm, err := keycloakClient.GetRealm(ctx, realmId)
if err != nil {
return err
}

userProfileEnabled := realm.Attributes[USER_PROFILE_ENABLED]
if userProfileEnabled != nil {
if value, ok := userProfileEnabled.(bool); ok && value {
return nil
}

if value, ok := userProfileEnabled.(string); ok && strings.ToLower(value) == "true" {
return nil
}
}
return fmt.Errorf("User Profile is disabled for the %s realm", realmId)
}
Loading
Loading