diff --git a/docs/resources/group.md b/docs/resources/group.md index 09344d90..629140e9 100644 --- a/docs/resources/group.md +++ b/docs/resources/group.md @@ -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" } } ``` @@ -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 diff --git a/docs/resources/role.md b/docs/resources/role.md index 1303c208..e64e2791 100644 --- a/docs/resources/role.md +++ b/docs/resources/role.md @@ -22,6 +22,7 @@ resource "keycloak_role" "realm_role" { description = "My Realm Role" attributes = { key = "value" + multivalue = "value1##value2" } } ``` @@ -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 diff --git a/docs/resources/user.md b/docs/resources/user.md index 3afe6f14..de5da2a5 100644 --- a/docs/resources/user.md +++ b/docs/resources/user.md @@ -39,6 +39,7 @@ resource "keycloak_user" "user_with_initial_password" { attributes = { foo = "bar" + multivalue = "value1##value2" } initial_password { @@ -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 diff --git a/example/main.tf b/example/main.tf index 5b880c6b..3cf6efc7 100644 --- a/example/main.tf +++ b/example/main.tf @@ -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 @@ -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" diff --git a/provider/resource_keycloak_group.go b/provider/resource_keycloak_group.go index 21d7ecf7..8229ffd1 100644 --- a/provider/resource_keycloak_group.go +++ b/provider/resource_keycloak_group.go @@ -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) } } @@ -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) diff --git a/provider/resource_keycloak_role.go b/provider/resource_keycloak_role.go index c9e7061c..52e463e4 100644 --- a/provider/resource_keycloak_role.go +++ b/provider/resource_keycloak_role.go @@ -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) } } @@ -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) diff --git a/provider/resource_keycloak_user.go b/provider/resource_keycloak_user.go index 07d18b8f..9f49f9c2 100644 --- a/provider/resource_keycloak_user.go +++ b/provider/resource_keycloak_user.go @@ -7,7 +7,7 @@ import ( "strings" ) -const MAX_ATTRIBUTE_VALUE_LEN = 255 +const MULTIVALUE_ATTRIBUTE_SEPARATOR = "##" func resourceKeycloakUser() *schema.Resource { return &schema.Resource{ @@ -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) } } @@ -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) diff --git a/provider/utils.go b/provider/utils.go index 62aa078e..1a884ffc 100644 --- a/provider/utils.go +++ b/provider/utils.go @@ -1,7 +1,6 @@ package provider import ( - "bytes" "log" "time" @@ -9,23 +8,6 @@ import ( "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 {