Skip to content

Commit

Permalink
support multivalue attributes for users, groups and roles (#499)
Browse files Browse the repository at this point in the history
  • Loading branch information
StatueFungus authored Mar 25, 2021
1 parent f9756ce commit bfe1bd2
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 30 deletions.
6 changes: 3 additions & 3 deletions docs/resources/group.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ resource "keycloak_group" "child_group_with_optional_attributes" {
parent_id = keycloak_group.parent_group.id
name = "child-group-with-optional-attributes"
attributes = {
"key1" = "value1"
"key2" = "value2"
"foo" = "bar"
"multivalue" = "value1##value2"
}
}
```
Expand All @@ -49,7 +49,7 @@ resource "keycloak_group" "child_group_with_optional_attributes" {
- `realm_id` - (Required) The realm this group exists in.
- `parent_id` - (Optional) The ID of this group's parent. If omitted, this group will be defined at the root level.
- `name` - (Required) The name of the group.
- `attributes` - (Optional) A map of key/value pairs to set as custom attributes for the group.
- `attributes` - (Optional) A map representing attributes for the group. In order to add multivalue attributes, use `##` to seperate the values. Max length for each value is 255 chars

## Attributes Reference

Expand Down
3 changes: 2 additions & 1 deletion docs/resources/role.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ resource "keycloak_role" "realm_role" {
description = "My Realm Role"
attributes = {
key = "value"
multivalue = "value1##value2"
}
}
```
Expand Down Expand Up @@ -150,7 +151,7 @@ resource "keycloak_role" "admin_role" {
- `client_id` - (Optional) When specified, this role will be created as a client role attached to the client with the provided ID
- `description` - (Optional) The description of the role
- `composite_roles` - (Optional) When specified, this role will be a composite role, composed of all roles that have an ID present within this list.
- `attributes` - (Optional) Attribute key/value pairs
- `attributes` - (Optional) A map representing attributes for the role. In order to add multivalue attributes, use `##` to seperate the values. Max length for each value is 255 chars


## Import
Expand Down
3 changes: 2 additions & 1 deletion docs/resources/user.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ resource "keycloak_user" "user_with_initial_password" {
attributes = {
foo = "bar"
multivalue = "value1##value2"
}
initial_password {
Expand All @@ -60,7 +61,7 @@ resource "keycloak_user" "user_with_initial_password" {
- `email_verified` - (Optional) Whether the email address was validated or not. Default to `false`.
- `first_name` - (Optional) The user's first name.
- `last_name` - (Optional) The user's last name.
- `attributes` - (Optional) A map representing attributes for the user
- `attributes` - (Optional) A map representing attributes for the user. In order to add multivalue attributes, use `##` to seperate the values. Max length for each value is 255 chars
- `federated_identity` - (Optional) When specified, the user will be linked to a federated identity provider. Refer to the [federated user example](https://github.com/mrparkers/terraform-provider-keycloak/blob/master/example/federated_user_example.tf) for more details.
- `identity_provider` - (Required) The name of the identity provider
- `user_id` - (Required) The ID of the user defined in the identity provider
Expand Down
23 changes: 23 additions & 0 deletions example/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,16 @@ resource "keycloak_openid_user_attribute_protocol_mapper" "map_user_attributes_c
claim_name = "description"
}

resource "keycloak_openid_user_attribute_protocol_mapper" "map_user_permissions_attributes_client" {
name = "tf-test-open-id-user-multivalue-attribute-protocol-mapper-client"
realm_id = keycloak_realm.test.id
client_id = keycloak_openid_client.test_client.id
user_attribute = "permissions"
claim_name = "permissions"
multivalued = true
}


resource "keycloak_openid_user_attribute_protocol_mapper" "map_user_attributes_client_scope" {
name = "tf-test-open-id-user-attribute-protocol-mapper-client-scope"
realm_id = keycloak_realm.test.id
Expand Down Expand Up @@ -806,6 +816,19 @@ resource "keycloak_openid_client_authorization_scope" "resource" {
realm_id = keycloak_realm.test.id
}

resource "keycloak_user" "user_with_multivalueattributes" {
realm_id = keycloak_realm.test.id
username = "user-with-mutivalueattributes"

attributes = {
"permissions" = "permission1##permission2"
}
initial_password {
value = "My password"
temporary = false
}
}

resource "keycloak_user" "resource" {
realm_id = keycloak_realm.test.id
username = "test"
Expand Down
4 changes: 2 additions & 2 deletions provider/resource_keycloak_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func mapFromDataToGroup(data *schema.ResourceData) *keycloak.Group {
attributes := map[string][]string{}
if v, ok := data.GetOk("attributes"); ok {
for key, value := range v.(map[string]interface{}) {
attributes[key] = splitLen(value.(string), MAX_ATTRIBUTE_VALUE_LEN)
attributes[key] = strings.Split(value.(string), MULTIVALUE_ATTRIBUTE_SEPARATOR)
}
}

Expand All @@ -67,7 +67,7 @@ func mapFromDataToGroup(data *schema.ResourceData) *keycloak.Group {
func mapFromGroupToData(data *schema.ResourceData, group *keycloak.Group) {
attributes := map[string]string{}
for k, v := range group.Attributes {
attributes[k] = strings.Join(v, "")
attributes[k] = strings.Join(v, MULTIVALUE_ATTRIBUTE_SEPARATOR)
}
data.SetId(group.Id)
data.Set("realm_id", group.RealmId)
Expand Down
4 changes: 2 additions & 2 deletions provider/resource_keycloak_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func mapFromDataToRole(data *schema.ResourceData) *keycloak.Role {
attributes := map[string][]string{}
if v, ok := data.GetOk("attributes"); ok {
for key, value := range v.(map[string]interface{}) {
attributes[key] = splitLen(value.(string), MAX_ATTRIBUTE_VALUE_LEN)
attributes[key] = strings.Split(value.(string), MULTIVALUE_ATTRIBUTE_SEPARATOR)
}
}

Expand All @@ -76,7 +76,7 @@ func mapFromDataToRole(data *schema.ResourceData) *keycloak.Role {
func mapFromRoleToData(data *schema.ResourceData, role *keycloak.Role) {
attributes := map[string]string{}
for k, v := range role.Attributes {
attributes[k] = strings.Join(v, "")
attributes[k] = strings.Join(v, MULTIVALUE_ATTRIBUTE_SEPARATOR)
}
data.SetId(role.Id)

Expand Down
6 changes: 3 additions & 3 deletions provider/resource_keycloak_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"strings"
)

const MAX_ATTRIBUTE_VALUE_LEN = 255
const MULTIVALUE_ATTRIBUTE_SEPARATOR = "##"

func resourceKeycloakUser() *schema.Resource {
return &schema.Resource{
Expand Down Expand Up @@ -117,7 +117,7 @@ func mapFromDataToUser(data *schema.ResourceData) *keycloak.User {
attributes := map[string][]string{}
if v, ok := data.GetOk("attributes"); ok {
for key, value := range v.(map[string]interface{}) {
attributes[key] = splitLen(value.(string), MAX_ATTRIBUTE_VALUE_LEN)
attributes[key] = strings.Split(value.(string), MULTIVALUE_ATTRIBUTE_SEPARATOR)
}
}

Expand Down Expand Up @@ -167,7 +167,7 @@ func mapFromUserToData(data *schema.ResourceData, user *keycloak.User) {
}
attributes := map[string]string{}
for k, v := range user.Attributes {
attributes[k] = strings.Join(v, "")
attributes[k] = strings.Join(v, MULTIVALUE_ATTRIBUTE_SEPARATOR)
}
data.SetId(user.Id)
data.Set("realm_id", user.RealmId)
Expand Down
18 changes: 0 additions & 18 deletions provider/utils.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,13 @@
package provider

import (
"bytes"
"log"
"time"

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

func splitLen(s string, n int) []string {
sub := ""
subs := []string{}
runes := bytes.Runes([]byte(s))
l := len(runes)
for i, r := range runes {
sub = sub + string(r)
if (i+1)%n == 0 {
subs = append(subs, sub)
sub = ""
} else if (i + 1) == l {
subs = append(subs, sub)
}
}
return subs
}

func keys(data map[string]string) []string {
var result = []string{}
for k := range data {
Expand Down

0 comments on commit bfe1bd2

Please sign in to comment.