-
Notifications
You must be signed in to change notification settings - Fork 330
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add keycloak_role_permissions for admin_fine_grained_authhz
- Loading branch information
1 parent
898094d
commit 8967ec2
Showing
5 changed files
with
568 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
--- | ||
page_title: "keycloak_role_permissions Resource" | ||
--- | ||
|
||
# keycloak_role_permissions | ||
|
||
Allows you to manage all role Scope Based Permissions https://www.keycloak.org/docs/latest/server_admin/#role. | ||
|
||
This is part of a preview keycloak feature. You need to enable this feature to be able to use this resource. | ||
More information about enabling the preview feature can be found here: https://www.keycloak.org/docs/latest/server_installation/#profiles | ||
|
||
When enabling Users Permissions, Keycloak does several things automatically: | ||
1. Enable Authorization on build-in realm-management client (if not already enabled) | ||
1. Create a resource representing the role permissions | ||
1. Create scopes "map-role", "map-role-client-scope", "map-role-composite" | ||
1. Create all scope based permission for the scopes and role resource | ||
|
||
If the realm-management Authorization is not enable, you have to ceate a dependency (`depends_on`) with the policy and the role. | ||
|
||
### Example Usage | ||
|
||
```hcl | ||
resource "keycloak_realm" "realm" { | ||
realm = "realm" | ||
} | ||
data keycloak_openid_client "realm_management" { | ||
realm_id = keycloak_realm.realm.id | ||
client_id = "realm-management" | ||
} | ||
resource keycloak_openid_client_permissions "realm-management_permission" { | ||
realm_id = keycloak_realm.realm.id | ||
client_id = data.keycloak_openid_client.realm_management.id | ||
enabled = true | ||
} | ||
resource keycloak_user test { | ||
realm_id = keycloak_realm.realm.id | ||
username = "test-user" | ||
email = "test-user@fakedomain.com" | ||
first_name = "Testy" | ||
last_name = "Tester" | ||
} | ||
resource keycloak_openid_client_user_policy test { | ||
resource_server_id = "${data.keycloak_openid_client.realm_management.id}" | ||
realm_id = keycloak_realm.realm.id | ||
name = "client_user_policy_test" | ||
users = ["${keycloak_user.test.id}"] | ||
logic = "POSITIVE" | ||
decision_strategy = "UNANIMOUS" | ||
depends_on = [ | ||
keycloak_openid_client_permissions.realm-management_permission, | ||
] | ||
} | ||
resource "keycloak_role" "role" { | ||
name = "%s" | ||
realm_id = "${keycloak_realm.realm.id}" | ||
} | ||
resource "keycloak_role_permissions" "my_permission" { | ||
realm_id = keycloak_realm.realm.id | ||
role_id = keycloak_role.role.id | ||
map_role_scope_policy_id = keycloak_openid_client_user_policy.test.id | ||
map_role_client_scope_scope_policy_id = keycloak_openid_client_user_policy.test.id | ||
map_role_composite_scope_policy_id = keycloak_openid_client_user_policy.test.id | ||
} | ||
``` | ||
|
||
### Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
- `realm_id` - (Required) The realm this group exists in. | ||
- `role_id` - (Required) The id of the role. | ||
- `map_role_scope_policy_id` - (Optional) Policy id that will be set on the scope based map-role permission automatically created by enabling permissions on the reference role. | ||
- `map_role_client_scope_scope_policy_id` - (Optional) Policy id that will be set on the scope based map-role-client-scope permission automatically created by enabling permissions on the reference role. | ||
- `map_role_composite_scope_policy_id` - (Optional) Policy id that will be set on the scope based map-role-composite permission automatically created by enabling permissions on the reference role. | ||
|
||
|
||
### Attributes Reference | ||
|
||
In addition to the arguments listed above, the following computed attributes are exported: | ||
|
||
- `enabled` - User permissions are Enabled (true) | ||
- `authorization_resource_server_id` - Resource server id representing the realm management client on which this permission is managed. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package keycloak | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
type RolePermissionsInput struct { | ||
Enabled bool `json:"enabled"` | ||
} | ||
|
||
type RolePermissions struct { | ||
RealmId string `json:"-"` | ||
RoleId string `json:"-"` | ||
Enabled bool `json:"enabled"` | ||
Resource string `json:"resource"` | ||
ScopePermissions map[string]interface{} `json:"scopePermissions"` | ||
} | ||
|
||
func (keycloakClient *KeycloakClient) EnableRolePermissions(realmId, clientId string) error { | ||
return keycloakClient.put(fmt.Sprintf("/realms/%s/roles-by-id/%s/management/permissions", realmId, clientId), RolePermissionsInput{Enabled: true}) | ||
} | ||
|
||
func (keycloakClient *KeycloakClient) DisableRolePermissions(realmId, clientId string) error { | ||
return keycloakClient.put(fmt.Sprintf("/realms/%s/roles-by-id/%s/management/permissions", realmId, clientId), RolePermissionsInput{Enabled: false}) | ||
} | ||
|
||
func (keycloakClient *KeycloakClient) GetRolePermissions(realmId, roleId string) (*RolePermissions, error) { | ||
var rolePermissions RolePermissions | ||
rolePermissions.RealmId = realmId | ||
rolePermissions.RoleId = roleId | ||
|
||
err := keycloakClient.get(fmt.Sprintf("/realms/%s/roles-by-id/%s/management/permissions", realmId, roleId), &rolePermissions, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &rolePermissions, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
package provider | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/mrparkers/terraform-provider-keycloak/keycloak" | ||
) | ||
|
||
func resourceKeycloakRolePermissions() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceKeycloakRolePermissionsCreate, | ||
Read: resourceKeycloakRolePermissionsRead, | ||
Delete: resourceKeycloakRolePermissionsDelete, | ||
Update: resourceKeycloakRolePermissionsUpdate, | ||
Importer: &schema.ResourceImporter{ | ||
State: resourceKeycloakRolePermissionsImport, | ||
}, | ||
Schema: map[string]*schema.Schema{ | ||
"realm_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"role_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"enabled": { | ||
Type: schema.TypeBool, | ||
Computed: true, | ||
}, | ||
"authorization_resource_server_id": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
Description: "Resource server id representing the realm management client on which this permission is managed", | ||
}, | ||
"map_role_scope_policy_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"map_role_client_scope_scope_policy_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"map_role_composite_scope_policy_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}}, | ||
} | ||
} | ||
|
||
func rolePermissionsId(realmId, roleId string) string { | ||
return fmt.Sprintf("%s/%s", realmId, roleId) | ||
} | ||
|
||
func setRoleScopePermissionPolicy(keycloakClient *keycloak.KeycloakClient, realmId, roleId string, scopeName string, policyId string) error { | ||
rolePermissions, err := keycloakClient.GetRolePermissions(realmId, roleId) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
realmManagementClient, err := keycloakClient.GetOpenidClientByClientId(realmId, "realm-management") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
permission, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, rolePermissions.ScopePermissions[scopeName].(string)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
permission.Policies = []string{policyId} | ||
|
||
return keycloakClient.UpdateOpenidClientAuthorizationPermission(permission) | ||
} | ||
|
||
func unsetRoleScopePermissionPolicy(keycloakClient *keycloak.KeycloakClient, realmId, roleId, scopeName string) error { | ||
rolePermissions, err := keycloakClient.GetRolePermissions(realmId, roleId) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
realmManagementClient, err := keycloakClient.GetOpenidClientByClientId(realmId, "realm-management") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
permission, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, rolePermissions.ScopePermissions[scopeName].(string)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
permission.Policies = []string{} | ||
err = keycloakClient.UpdateOpenidClientAuthorizationPermission(permission) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceKeycloakRolePermissionsCreate(data *schema.ResourceData, meta interface{}) error { | ||
return resourceKeycloakRolePermissionsUpdate(data, meta) | ||
} | ||
|
||
func resourceKeycloakRolePermissionsUpdate(data *schema.ResourceData, meta interface{}) error { | ||
keycloakClient := meta.(*keycloak.KeycloakClient) | ||
|
||
realmId := data.Get("realm_id").(string) | ||
roleId := data.Get("role_id").(string) | ||
|
||
err := keycloakClient.EnableRolePermissions(realmId, roleId) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
mapRolesScopePolicyId, ok := data.GetOk("map_role_scope_policy_id") | ||
if ok && mapRolesScopePolicyId.(string) != "" { | ||
err := setRoleScopePermissionPolicy(keycloakClient, realmId, roleId, "map-role", mapRolesScopePolicyId.(string)) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
mapRolesClientsScopePolicyId, ok := data.GetOk("map_role_client_scope_scope_policy_id") | ||
if ok && mapRolesClientsScopePolicyId.(string) != "" { | ||
err := setRoleScopePermissionPolicy(keycloakClient, realmId, roleId, "map-role-client-scope", mapRolesClientsScopePolicyId.(string)) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
mapRolesCompositeScopePolicyId, ok := data.GetOk("map_role_composite_scope_policy_id") | ||
if ok && mapRolesCompositeScopePolicyId.(string) != "" { | ||
err := setRoleScopePermissionPolicy(keycloakClient, realmId, roleId, "map-role-composite", mapRolesCompositeScopePolicyId.(string)) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return resourceKeycloakRolePermissionsRead(data, meta) | ||
} | ||
|
||
func resourceKeycloakRolePermissionsRead(data *schema.ResourceData, meta interface{}) error { | ||
keycloakClient := meta.(*keycloak.KeycloakClient) | ||
realmId := data.Get("realm_id").(string) | ||
roleId := data.Get("role_id").(string) | ||
|
||
rolePermissions, err := keycloakClient.GetRolePermissions(realmId, roleId) | ||
if err != nil { | ||
return handleNotFoundError(err, data) | ||
} | ||
|
||
data.SetId(rolePermissionsId(rolePermissions.RealmId, rolePermissions.RoleId)) | ||
data.Set("realm_id", rolePermissions.RealmId) | ||
data.Set("role_id", rolePermissions.RoleId) | ||
|
||
data.Set("enabled", rolePermissions.Enabled) | ||
|
||
realmManagementClient, err := keycloakClient.GetOpenidClientByClientId(realmId, "realm-management") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
permissionMapRoles, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, rolePermissions.ScopePermissions["map-role"].(string)) | ||
if err != nil { | ||
return err | ||
} | ||
if permissionMapRoles != nil && len(permissionMapRoles.Policies) > 0 { | ||
data.Set("map_role_scope_policy_id", permissionMapRoles.Policies[0]) | ||
} | ||
permissionMapRolesClientScope, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, rolePermissions.ScopePermissions["map-role-client-scope"].(string)) | ||
if err != nil { | ||
return err | ||
} | ||
if permissionMapRolesClientScope != nil && len(permissionMapRolesClientScope.Policies) > 0 { | ||
data.Set("map_role_client_scope_scope_policy_id", permissionMapRolesClientScope.Policies[0]) | ||
} | ||
permissionMapRolesComposite, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, rolePermissions.ScopePermissions["map-role-composite"].(string)) | ||
if err != nil { | ||
return err | ||
} | ||
if permissionMapRolesComposite != nil && len(permissionMapRolesComposite.Policies) > 0 { | ||
data.Set("map_role_composite_scope_policy_id", permissionMapRolesComposite.Policies[0]) | ||
} | ||
|
||
data.Set("authorization_resource_server_id", realmManagementClient.Id) | ||
|
||
return nil | ||
} | ||
|
||
func resourceKeycloakRolePermissionsDelete(data *schema.ResourceData, meta interface{}) error { | ||
|
||
keycloakClient := meta.(*keycloak.KeycloakClient) | ||
|
||
realmId := data.Get("realm_id").(string) | ||
roleId := data.Get("role_id").(string) | ||
|
||
rolePermissions, err := keycloakClient.GetRolePermissions(realmId, roleId) | ||
if err == nil && rolePermissions.Enabled { | ||
_ = unsetRoleScopePermissionPolicy(keycloakClient, realmId, roleId, "map-role") | ||
} | ||
return keycloakClient.DisableRolePermissions(realmId, roleId) | ||
} | ||
|
||
func resourceKeycloakRolePermissionsImport(d *schema.ResourceData, _ interface{}) ([]*schema.ResourceData, error) { | ||
parts := strings.Split(d.Id(), "/") | ||
if len(parts) != 2 { | ||
return nil, fmt.Errorf("Invalid import. Supported import formats: {{realmId}}/{{roleId}}") | ||
} | ||
d.Set("realm_id", parts[0]) | ||
d.Set("role_id", parts[1]) | ||
|
||
d.SetId(rolePermissionsId(parts[0], parts[1])) | ||
|
||
return []*schema.ResourceData{d}, nil | ||
} |
Oops, something went wrong.