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

Add exhaustive argument to group_roles #501

Merged
merged 3 commits into from
Apr 9, 2021
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
67 changes: 63 additions & 4 deletions docs/resources/group_roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ page_title: "keycloak_group_roles Resource"

Allows you to manage roles assigned to a Keycloak group.

Note that this resource attempts to be an **authoritative** source over group roles. When this resource takes control over
a group's roles, roles that are manually added to the group will be removed, and roles that are manually removed from the
If `exhaustive` is true, this resource attempts to be an **authoritative** source over group roles: roles that are manually added to the group will be removed, and roles that are manually removed from the
group will be added upon the next run of `terraform apply`.
If `exhaustive` is false, this resource is a partial assignation of roles to a group. As a result, you can get multiple `keycloak_group_roles` for the same `group_id`.

Note that when assigning composite roles to a group, you may see a non-empty plan following a `terraform apply` if you
assign a role and a composite that includes that role to the same group.

## Example Usage
## Example Usage (exhaustive roles)


```hcl
resource "keycloak_realm" "realm" {
Expand Down Expand Up @@ -60,11 +61,69 @@ resource "keycloak_group_roles" "group_roles" {
}
```

## Example Usage (non exhaustive roles)

```hcl
resource "keycloak_realm" "realm" {
realm = "my-realm"
enabled = true
}

resource "keycloak_role" "realm_role" {
realm_id = keycloak_realm.realm.id
name = "my-realm-role"
description = "My Realm Role"
}

resource "keycloak_openid_client" "client" {
realm_id = keycloak_realm.realm.id
client_id = "client"
name = "client"

enabled = true

access_type = "BEARER-ONLY"
}

resource "keycloak_role" "client_role" {
realm_id = keycloak_realm.realm.id
client_id = keycloak_client.client.id
name = "my-client-role"
description = "My Client Role"
}

resource "keycloak_group" "group" {
realm_id = keycloak_realm.realm.id
name = "my-group"
}

resource "keycloak_group_roles" "group_role_association1" {
realm_id = keycloak_realm.realm.id
group_id = keycloak_group.group.id
exhaustive = false

role_ids = [
keycloak_role.realm_role.id,
]
}

resource "keycloak_group_roles" "group_role_association2" {
realm_id = keycloak_realm.realm.id
group_id = keycloak_group.group.id
exhaustive = false

role_ids = [
keycloak_role.client_role.id,
]
}

```
## Argument Reference

- `realm_id` - (Required) The realm this group exists in.
- `group_id` - (Required) The ID of the group this resource should manage roles for.
- `role_ids` - (Required) A list of role IDs to map to the group
- `role_ids` - (Required) A list of role IDs to map to the group.
- `exhaustive` - (Optional) Indicate if the list of roles is exhaustive. In this case, roles that are manually added to the group will be removed. Default `true`.

## Import

Expand Down
55 changes: 47 additions & 8 deletions provider/resource_keycloak_group_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ func resourceKeycloakGroupRoles() *schema.Resource {
Set: schema.HashString,
Required: true,
},
"exhaustive": {
Type: schema.TypeBool,
Optional: true,
Default: true,
},
},
}
}
Expand Down Expand Up @@ -87,13 +92,30 @@ func resourceKeycloakGroupRolesReconcile(data *schema.ResourceData, meta interfa

realmId := data.Get("realm_id").(string)
groupId := data.Get("group_id").(string)
roleIds := interfaceSliceToStringSlice(data.Get("role_ids").(*schema.Set).List())
exhaustive := data.Get("exhaustive").(bool)

group, err := keycloakClient.GetGroup(realmId, groupId)
if err != nil {
return err
}

roleIds := interfaceSliceToStringSlice(data.Get("role_ids").(*schema.Set).List())
if data.HasChange("role_ids") {
o, n := data.GetChange("role_ids")
os := o.(*schema.Set)
ns := n.(*schema.Set)
remove := interfaceSliceToStringSlice(os.Difference(ns).List())

tfRolesToRemove, err := getExtendedRoleMapping(keycloakClient, realmId, remove)
if err != nil {
return err
}

if err = removeRolesFromGroup(keycloakClient, tfRolesToRemove.clientRoles, tfRolesToRemove.realmRoles, group); err != nil {
return err
}
}

tfRoles, err := getExtendedRoleMapping(keycloakClient, realmId, roleIds)
if err != nil {
return err
Expand All @@ -111,21 +133,34 @@ func resourceKeycloakGroupRolesReconcile(data *schema.ResourceData, meta interfa
return err
}

// remove roles
err = removeRolesFromGroup(keycloakClient, updates.clientRolesToRemove, updates.realmRolesToRemove, group)
if err != nil {
return err
if exhaustive {
// remove roles
err = removeRolesFromGroup(keycloakClient, updates.clientRolesToRemove, updates.realmRolesToRemove, group)
if err != nil {
return err
}
}

data.SetId(groupRolesId(realmId, groupId))
return resourceKeycloakGroupRolesRead(data, meta)
}

// Helper function
func containsAString(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}

func resourceKeycloakGroupRolesRead(data *schema.ResourceData, meta interface{}) error {
keycloakClient := meta.(*keycloak.KeycloakClient)

realmId := data.Get("realm_id").(string)
groupId := data.Get("group_id").(string)
sortedRoleIds := interfaceSliceToStringSlice(data.Get("role_ids").(*schema.Set).List())
exhaustive := data.Get("exhaustive").(bool)

// check if group exists, remove from state if not found
if _, err := keycloakClient.GetGroup(realmId, groupId); err != nil {
Expand All @@ -140,12 +175,16 @@ func resourceKeycloakGroupRolesRead(data *schema.ResourceData, meta interface{})
var roleIds []string

for _, realmRole := range roles.RealmMappings {
roleIds = append(roleIds, realmRole.Id)
if exhaustive || containsAString(sortedRoleIds, realmRole.Id) {
roleIds = append(roleIds, realmRole.Id)
}
}

for _, clientRoleMapping := range roles.ClientMappings {
for _, clientRole := range clientRoleMapping.Mappings {
roleIds = append(roleIds, clientRole.Id)
if exhaustive || containsAString(sortedRoleIds, clientRole.Id) {
roleIds = append(roleIds, clientRole.Id)
}
}
}

Expand Down
Loading