Skip to content

Commit

Permalink
add client_scope_id attribute to keycloak_generic_client_role_mapper …
Browse files Browse the repository at this point in the history
…resource (#253)
  • Loading branch information
dmeyerholt authored Mar 23, 2020
1 parent 5313139 commit d05298a
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 23 deletions.
61 changes: 60 additions & 1 deletion example/roles.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ resource "keycloak_openid_client" "pet_api" {
access_type = "BEARER-ONLY"
}

// Optional client scope for mapping additional client role
resource "keycloak_openid_client_scope" "extended_pet_details" {
realm_id = "${keycloak_realm.roles_example.id}"
name = "extended-pet-details"
description = "Optional scope offering additional information when getting pets"
}

resource "keycloak_role" "pet_api_create_pet" {
name = "create-pet"
realm_id = "${keycloak_realm.roles_example.id}"
Expand Down Expand Up @@ -43,6 +50,20 @@ resource "keycloak_role" "pet_api_delete_pet" {
description = "Ability to delete a pet"
}

resource "keycloak_role" "pet_api_read_pet_details" {
name = "read-pet-with-details"
realm_id = "${keycloak_realm.roles_example.id}"
client_id = "${keycloak_openid_client.pet_api.id}"
description = "Ability to read / list pets with further details"
}

// Map a role from the "pet_api" api client to the "extended_pet_details" client scope
resource "keycloak_generic_client_role_mapper" "pet_api_read_pet_details_role_mapping" {
realm_id = "${keycloak_realm.roles_example.id}"
client_scope_id = "${keycloak_openid_client_scope.extended_pet_details.id}"
role_id = "${keycloak_role.pet_api_read_pet_details.id}"
}

resource "keycloak_role" "pet_api_admin" {
name = "admin"
realm_id = "${keycloak_realm.roles_example.id}"
Expand Down Expand Up @@ -76,6 +97,18 @@ resource "keycloak_openid_client" "pet_app" {
valid_redirect_uris = [
"http://localhost:5555/openid-callback",
]

// disable full scope, roles are assigned via keycloak_generic_client_role_mapper
full_scope_allowed = false
}

resource "keycloak_openid_client_optional_scopes" "pet_app_optional_scopes" {
realm_id = "${keycloak_realm.roles_example.id}"
client_id = "${keycloak_openid_client.pet_app.id}"

optional_scopes = [
"${keycloak_openid_client_scope.extended_pet_details.name}"
]
}

// The app will always need access to the API, so this audience should be used regardless of auth type
Expand All @@ -96,13 +129,37 @@ resource "keycloak_openid_hardcoded_role_protocol_mapper" "pet_app_pet_api_read_
role_id = "${keycloak_role.pet_api_read_pet.id}"
}

// Map a role from the "pet_api" api client to the "pet_app" consumer client
// Map all roles from the "pet_api" api client to the "pet_app" consumer client, read_pet_details comes via client scope
resource "keycloak_generic_client_role_mapper" "pet_app_pet_api_read_role_mapping" {
realm_id = "${keycloak_realm.roles_example.id}"
client_id = "${keycloak_openid_client.pet_app.id}"
role_id = "${keycloak_role.pet_api_read_pet.id}"
}

resource "keycloak_generic_client_role_mapper" "pet_app_pet_api_delete_role_mapping" {
realm_id = "${keycloak_realm.roles_example.id}"
client_id = "${keycloak_openid_client.pet_app.id}"
role_id = "${keycloak_role.pet_api_delete_pet.id}"
}

resource "keycloak_generic_client_role_mapper" "pet_app_pet_api_create_role_mapping" {
realm_id = "${keycloak_realm.roles_example.id}"
client_id = "${keycloak_openid_client.pet_app.id}"
role_id = "${keycloak_role.pet_api_create_pet.id}"
}

resource "keycloak_generic_client_role_mapper" "pet_app_pet_api_update_role_mapping" {
realm_id = "${keycloak_realm.roles_example.id}"
client_id = "${keycloak_openid_client.pet_app.id}"
role_id = "${keycloak_role.pet_api_update_pet.id}"
}

resource "keycloak_generic_client_role_mapper" "pet_app_pet_api_admin_role_mapping" {
realm_id = "${keycloak_realm.roles_example.id}"
client_id = "${keycloak_openid_client.pet_app.id}"
role_id = "${keycloak_role.pet_api_admin.id}"
}

// Users and groups

resource "keycloak_group" "pet_api_base" {
Expand Down Expand Up @@ -133,6 +190,7 @@ resource "keycloak_group_roles" "admin_roles" {

role_ids = [
"${keycloak_role.pet_api_read_pet.id}",
"${keycloak_role.pet_api_read_pet_details.id}",
"${keycloak_role.pet_api_delete_pet.id}",
"${keycloak_role.pet_api_create_pet.id}",
"${data.keycloak_role.realm_offline_access.id}",
Expand All @@ -145,6 +203,7 @@ resource "keycloak_group_roles" "front_desk_roles" {

role_ids = [
"${keycloak_role.pet_api_read_pet.id}",
"${keycloak_role.pet_api_read_pet_details.id}",
"${keycloak_role.pet_api_create_pet.id}",
"${data.keycloak_role.realm_offline_access.id}",
]
Expand Down
20 changes: 12 additions & 8 deletions keycloak/role_scope_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import (
"fmt"
)

func roleScopeMappingUrl(realmId, clientId string, role *Role) string {
return fmt.Sprintf("/realms/%s/clients/%s/scope-mappings/clients/%s", realmId, clientId, role.ClientId)
func roleScopeMappingUrl(realmId, clientId string, clientScopeId string, role *Role) string {
if clientId != "" {
return fmt.Sprintf("/realms/%s/clients/%s/scope-mappings/clients/%s", realmId, clientId, role.ClientId)
} else {
return fmt.Sprintf("/realms/%s/client-scopes/%s/scope-mappings/clients/%s", realmId, clientScopeId, role.ClientId)
}
}

func (keycloakClient *KeycloakClient) CreateRoleScopeMapping(realmId string, clientId string, role *Role) error {
roleUrl := roleScopeMappingUrl(realmId, clientId, role)
func (keycloakClient *KeycloakClient) CreateRoleScopeMapping(realmId string, clientId string, clientScopeId string, role *Role) error {
roleUrl := roleScopeMappingUrl(realmId, clientId, clientScopeId, role)

_, _, err := keycloakClient.post(roleUrl, []Role{*role})
if err != nil {
Expand All @@ -19,8 +23,8 @@ func (keycloakClient *KeycloakClient) CreateRoleScopeMapping(realmId string, cli
return nil
}

func (keycloakClient *KeycloakClient) GetRoleScopeMapping(realmId string, clientId string, role *Role) (*Role, error) {
roleUrl := roleScopeMappingUrl(realmId, clientId, role)
func (keycloakClient *KeycloakClient) GetRoleScopeMapping(realmId string, clientId string, clientScopeId string, role *Role) (*Role, error) {
roleUrl := roleScopeMappingUrl(realmId, clientId, clientScopeId, role)
var roles []Role

err := keycloakClient.get(roleUrl, &roles, nil)
Expand All @@ -37,7 +41,7 @@ func (keycloakClient *KeycloakClient) GetRoleScopeMapping(realmId string, client
return nil, nil
}

func (keycloakClient *KeycloakClient) DeleteRoleScopeMapping(realmId string, clientId string, role *Role) error {
roleUrl := roleScopeMappingUrl(realmId, clientId, role)
func (keycloakClient *KeycloakClient) DeleteRoleScopeMapping(realmId string, clientId string, clientScopeId string, role *Role) error {
roleUrl := roleScopeMappingUrl(realmId, clientId, clientScopeId, role)
return keycloakClient.delete(roleUrl, nil)
}
44 changes: 31 additions & 13 deletions provider/resource_keycloak_generic_client_role_mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,30 @@ func resourceKeycloakGenericClientRoleMapper() *schema.Resource {

Schema: map[string]*schema.Schema{
"realm_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The realm id where the associated client or client scope exists.",
},
"client_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: "The destination client of the client role. Cannot be used at the same time as client_scope_id.",
ConflictsWith: []string{"client_scope_id"},
},
"client_scope_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: "The destination client scope of the client role. Cannot be used at the same time as client_id.",
ConflictsWith: []string{"client_id"},
},
"role_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Id of the role to assign",
},
},
}
Expand All @@ -38,19 +49,24 @@ func resourceKeycloakGenericClientRoleMapperCreate(data *schema.ResourceData, me

realmId := data.Get("realm_id").(string)
clientId := data.Get("client_id").(string)
clientScopeId := data.Get("client_scope_id").(string)
roleId := data.Get("role_id").(string)

role, err := keycloakClient.GetRole(realmId, roleId)
if err != nil {
return err
}

err = keycloakClient.CreateRoleScopeMapping(realmId, clientId, role)
err = keycloakClient.CreateRoleScopeMapping(realmId, clientId, clientScopeId, role)
if err != nil {
return err
}

data.SetId(fmt.Sprintf("%s/client/%s/scope-mappings/%s/%s", realmId, clientId, role.ClientId, role.Id))
if clientId != "" {
data.SetId(fmt.Sprintf("%s/client/%s/scope-mappings/%s/%s", realmId, clientId, role.ClientId, role.Id))
} else {
data.SetId(fmt.Sprintf("%s/client-scope/%s/scope-mappings/%s/%s", realmId, clientScopeId, role.ClientId, role.Id))
}

return resourceKeycloakGenericClientRoleMapperRead(data, meta)
}
Expand All @@ -60,14 +76,15 @@ func resourceKeycloakGenericClientRoleMapperRead(data *schema.ResourceData, meta

realmId := data.Get("realm_id").(string)
clientId := data.Get("client_id").(string)
clientScopeId := data.Get("client_scope_id").(string)
roleId := data.Get("role_id").(string)

role, err := keycloakClient.GetRole(realmId, roleId)
if err != nil {
return err
}

mappedRole, err := keycloakClient.GetRoleScopeMapping(realmId, clientId, role)
mappedRole, err := keycloakClient.GetRoleScopeMapping(realmId, clientId, clientScopeId, role)

if mappedRole == nil {
data.SetId("")
Expand All @@ -81,12 +98,13 @@ func resourceKeycloakGenericClientRoleMapperDelete(data *schema.ResourceData, me

realmId := data.Get("realm_id").(string)
clientId := data.Get("client_id").(string)
clientScopeId := data.Get("client_scope_id").(string)
roleId := data.Get("role_id").(string)

role, err := keycloakClient.GetRole(realmId, roleId)
if err != nil {
return err
}

return keycloakClient.DeleteRoleScopeMapping(realmId, clientId, role)
return keycloakClient.DeleteRoleScopeMapping(realmId, clientId, clientScopeId, role)
}
Loading

0 comments on commit d05298a

Please sign in to comment.