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

feat:(roles): Importable role by rolename #1043

Merged
merged 1 commit 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 docs/resources/role.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ resource "keycloak_role" "admin_role" {
- `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) 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` - (Optional) When `true`, the role with the specified `name` is assumed to already exist, and it will be imported into state instead of being created. This attribute is useful when dealing with roles that Keycloak creates automatically during realm creation, such as the client roles `create-client`, `view-realm`, ... for the client `realm-management` created per realm. Note, that the role will not be removed during destruction if `import` is `true`.


## Import
Expand Down
94 changes: 77 additions & 17 deletions provider/resource_keycloak_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/imdario/mergo"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -39,18 +40,26 @@ func resourceKeycloakRole() *schema.Resource {
"description": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"composite_roles": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
MinItems: 1,
Set: schema.HashString,
Optional: true,
Computed: true,
},
// misc attributes
"attributes": {
Type: schema.TypeMap,
Optional: true,
Computed: true,
},
"import": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ForceNew: true,
},
},
}
Expand Down Expand Up @@ -95,41 +104,87 @@ func resourceKeycloakRoleCreate(ctx context.Context, data *schema.ResourceData,

role := mapFromDataToRole(data)

var compositeRoles []*keycloak.Role
if v, ok := data.GetOk("composite_roles"); ok {
compositeRolesTf := v.(*schema.Set).List()
if data.Get("import").(bool) {
realmId := data.Get("realm_id").(string)
name := data.Get("name").(string)
clientId := data.Get("client_id").(string)
compositeRolesValue, compositeRolesSpecified := data.GetOk("composite_roles")

for _, compositeRoleId := range compositeRolesTf {
compositeRoleToAdd, err := keycloakClient.GetRole(ctx, role.RealmId, compositeRoleId.(string))
if err != nil {
compositeRoles, err := mapCompositeRoleIdsToRoleObjects(ctx, keycloakClient, compositeRolesValue.(*schema.Set).List(), role.RealmId)
if err != nil {
return diag.FromErr(err)
}

if len(compositeRoles) != 0 {
role.Composite = true
}

existingRole, err := keycloakClient.GetRoleByName(ctx, realmId, clientId, name)
if err != nil {
return diag.FromErr(err)
}

if err = mergo.Merge(role, existingRole); err != nil {
return diag.FromErr(err)
}
if err = keycloakClient.UpdateRole(ctx, role); err != nil {
return diag.FromErr(err)
}

existingCompositeRoles, err := keycloakClient.GetRoleComposites(ctx, role)
if err != nil {
return diag.FromErr(err)
}

if role.Composite && compositeRolesSpecified {
if err = keycloakClient.RemoveCompositesFromRole(ctx, role, existingCompositeRoles); err != nil {
return diag.FromErr(err)
}
if err = keycloakClient.AddCompositesToRole(ctx, role, compositeRoles); err != nil {
return diag.FromErr(err)
}
}

compositeRoles = append(compositeRoles, compositeRoleToAdd)
} else {
compositeRoles, err := mapCompositeRoleIdsToRoleObjects(ctx, keycloakClient, data.Get("composite_roles").(*schema.Set).List(), role.RealmId)
if err != nil {
return diag.FromErr(err)
}

if len(compositeRoles) != 0 { // technically you can still specify composite_roles = [] in HCL
role.Composite = true
}
}

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

if role.Composite {
err = keycloakClient.AddCompositesToRole(ctx, role, compositeRoles)
if err != nil {
if err = keycloakClient.CreateRole(ctx, role); err != nil {
return diag.FromErr(err)
}

if role.Composite {
if err = keycloakClient.AddCompositesToRole(ctx, role, compositeRoles); err != nil {
return diag.FromErr(err)
}
}
}

mapFromRoleToData(data, role)

return resourceKeycloakRoleRead(ctx, data, meta)
}

func mapCompositeRoleIdsToRoleObjects(ctx context.Context, keycloakClient *keycloak.KeycloakClient, compositeRoleIds []interface{}, realmId string) ([]*keycloak.Role, error) {
var compositeRoles []*keycloak.Role

for _, compositeRoleId := range compositeRoleIds {
compositeRoleToAdd, err := keycloakClient.GetRole(ctx, realmId, compositeRoleId.(string))
if err != nil {
return nil, err
}

compositeRoles = append(compositeRoles, compositeRoleToAdd)
}
return compositeRoles, nil
}

func resourceKeycloakRoleRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
keycloakClient := meta.(*keycloak.KeycloakClient)

Expand Down Expand Up @@ -232,6 +287,10 @@ func resourceKeycloakRoleUpdate(ctx context.Context, data *schema.ResourceData,
}

func resourceKeycloakRoleDelete(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
if data.Get("import").(bool) {
return nil
}

keycloakClient := meta.(*keycloak.KeycloakClient)

realmId := data.Get("realm_id").(string)
Expand All @@ -254,6 +313,7 @@ func resourceKeycloakRoleImport(ctx context.Context, d *schema.ResourceData, met
}

d.Set("realm_id", parts[0])
d.Set("import", false)
d.SetId(parts[1])

diagnostics := resourceKeycloakRoleRead(ctx, d, meta)
Expand Down
Loading
Loading