Repository: mrparkers/terraform-provider-keycloak
Pull request #364 (2020-08-19 15:12:32)

Add Keycloak_openid_client_permission for admin_fine_grained_authz
Add on `keycloak_openid_client` the optional option `permissions_enabled` to enable Fine grained permissions on the client.

Add resource `keycloak_openid_client_permission` to manage the policies to apply to each scopes.

mrparkers: @tomrutsaert could you take a look at this when you have a chance?  I'm very unfamiliar with the permissions / policy features within Keycloak.
jermarchand: @tomrutsaert could you take a look at my updates ?

tomrutsaert: Looks good to me

Patch:
diff --git a/docs/resources/openid_client_permission.md b/docs/resources/openid_client_permission.md
new file mode 100644
index 0000000..de7f25a
--- /dev/null
+++ b/docs/resources/openid_client_permission.md
@@ -0,0 +1,93 @@
+# keycloak_openid_client_permissions
+
+Allows you to manage all openid client Scope Based Permissions.
+
+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/securing_apps/index.html#_token-exchange
+
+When enabling Openid Client Permissions, Keycloak does several things automatically:
+1. Enable Authorization on build-in realm-management client
+1. Create scopes "view", "manage", "configure", "map-roles", "map-roles-client-scope", "map-roles-composite", "token-exchange"
+1. Create a resource representing the openid client
+1. Create all scope based permission for the scopes and openid client resource
+
+If the realm-management Authorization is not enable, you have to ceate a dependency (`depends_on`) with the policy and the openid client.
+
+### Example Usage
+
+```hcl
+resource "keycloak_realm" "realm" {
+	realm  = "realm"
+}
+
+resource "keycloak_openid_client" "my_openid_client" {
+  realm_id              = keycloak_realm.realm.id
+  name                  = "my_openid_client"
+  client_id             = "my_openid_client"
+  client_secret         = "secret"
+  access_type           = "CONFIDENTIAL"
+  standard_flow_enabled = true
+  valid_redirect_uris   = [
+    "http://localhost:8080/*",
+  ]
+}
+
+data "keycloak_openid_client" "realm_management" {
+	realm_id  = keycloak_realm.realm.id
+	client_id = "realm-management"  
+  }
+
+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.my_openid_client
+	]
+}
+
+resource "keycloak_openid_client_permissions" "my_permission" {
+	realm_id                               = keycloak_realm.realm.id
+	client_id                              = keycloak_openid_client.my_openid_client.id
+	view_scope_policy_id                   = keycloak_openid_client_user_policy.test.id
+	manage_scope_policy_id                 = keycloak_openid_client_user_policy.test.id
+	configure_scope_policy_id              = keycloak_openid_client_user_policy.test.id
+	map_roles_scope_policy_id              = keycloak_openid_client_user_policy.test.id
+	map_roles_client_scope_scope_policy_id = keycloak_openid_client_user_policy.test.id
+	map_roles_composite_scope_policy_id    = keycloak_openid_client_user_policy.test.id
+	token_exchange_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.
+- `client_id` - (Required) The id of the client that provides the role.
+- `view_scope_policy_id` - (Optional) Policy id that will be set on the scope based view permission automatically created by enabling permissions on the reference openid client.
+- `manage_scope_policy_id` - (Optional) Policy id that will be set on the scope based manage permission automatically created by enabling permissions on the reference openid client.
+- `configure_scope_policy_id` - (Optional) Policy id that will be set on the scope based configure permission automatically created by enabling permissions on the reference openid client.
+- `map_roles_scope_policy_id` - (Optional) Policy id that will be set on the scope based map-roles permission automatically created by enabling permissions on the reference openid client.
+- `map_roles_client_scope_scope_policy_id` - (Optional) Policy id that will be set on the scope based map-roles-client-scope permission automatically created by enabling permissions on the reference openid client.
+- `map_roles_composite_scope_policy_id` - (Optional) Policy id that will be set on the scope based map-roles-composite permission automatically created by enabling permissions on the reference openid client.
+- `token_exchange_scope_policy_id` - (Optional) Policy id that will be set on the scope based token-exchange permission automatically created by enabling permissions on the reference openid client.
+
+### Attributes Reference
+
+In addition to the arguments listed above, the following computed attributes are exported:
+
+- `authorization_resource_server_id` - Resource server id representing the realm management client on which this permission is managed.
+
diff --git a/keycloak/openid_client_permissions.go b/keycloak/openid_client_permissions.go
new file mode 100644
index 0000000..c50fa83
--- /dev/null
+++ b/keycloak/openid_client_permissions.go
@@ -0,0 +1,38 @@
+package keycloak
+
+import (
+	"fmt"
+)
+
+type OpenidClientPermissionsInput struct {
+	Enabled bool `json:"enabled"`
+}
+
+type OpenidClientPermissions struct {
+	RealmId          string                 `json:"-"`
+	ClientId         string                 `json:"-"`
+	Enabled          bool                   `json:"enabled"`
+	Resource         string                 `json:"resource"`
+	ScopePermissions map[string]interface{} `json:"scopePermissions"`
+}
+
+func (keycloakClient *KeycloakClient) EnableOpenidClientPermissions(realmId, clientId string) error {
+	return keycloakClient.put(fmt.Sprintf("/realms/%s/clients/%s/management/permissions", realmId, clientId), OpenidClientPermissionsInput{Enabled: true})
+}
+
+func (keycloakClient *KeycloakClient) DisableOpenidClientPermissions(realmId, clientId string) error {
+	return keycloakClient.put(fmt.Sprintf("/realms/%s/clients/%s/management/permissions", realmId, clientId), OpenidClientPermissionsInput{Enabled: false})
+}
+
+func (keycloakClient *KeycloakClient) GetOpenidClientPermissions(realmId, clientId string) (*OpenidClientPermissions, error) {
+	var openidClientPermissions OpenidClientPermissions
+	openidClientPermissions.RealmId = realmId
+	openidClientPermissions.ClientId = clientId
+
+	err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/management/permissions", realmId, clientId), &openidClientPermissions, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return &openidClientPermissions, nil
+}
diff --git a/provider/provider.go b/provider/provider.go
index d8ca5a7..36bce2e 100644
--- a/provider/provider.go
+++ b/provider/provider.go
@@ -91,6 +91,7 @@ func KeycloakProvider() *schema.Provider {
 			"keycloak_authentication_execution":                          resourceKeycloakAuthenticationExecution(),
 			"keycloak_authentication_execution_config":                   resourceKeycloakAuthenticationExecutionConfig(),
 			"keycloak_identity_provider_token_exchange_scope_permission": resourceKeycloakIdentityProviderTokenExchangeScopePermission(),
+			"keycloak_openid_client_permissions":                         resourceKeycloakOpenidClientPermissions(),
 		},
 		Schema: map[string]*schema.Schema{
 			"client_id": {
diff --git a/provider/resource_keycloak_openid_client.go b/provider/resource_keycloak_openid_client.go
index 36a085c..6296fc2 100644
--- a/provider/resource_keycloak_openid_client.go
+++ b/provider/resource_keycloak_openid_client.go
@@ -3,10 +3,11 @@ package provider
 import (
 	"errors"
 	"fmt"
+	"strings"
+
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
 	"github.com/mrparkers/terraform-provider-keycloak/keycloak"
-	"strings"
 )
 
 var (
diff --git a/provider/resource_keycloak_openid_client_permissions.go b/provider/resource_keycloak_openid_client_permissions.go
new file mode 100644
index 0000000..b264364
--- /dev/null
+++ b/provider/resource_keycloak_openid_client_permissions.go
@@ -0,0 +1,312 @@
+package provider
+
+import (
+	"fmt"
+	"log"
+	"strings"
+
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+	"github.com/mrparkers/terraform-provider-keycloak/keycloak"
+)
+
+func resourceKeycloakOpenidClientPermissions() *schema.Resource {
+	return &schema.Resource{
+		Create: resourceKeycloakOpenidClientPermissionsCreate,
+		Read:   resourceKeycloakOpenidClientPermissionsRead,
+		Delete: resourceKeycloakOpenidClientPermissionsDelete,
+		Update: resourceKeycloakOpenidClientPermissionsUpdate,
+		// This resource can be imported using {{realm}}/{{client_id}}. The Client ID is displayed in the GUI
+		Importer: &schema.ResourceImporter{
+			State: resourceKeycloakOpenidClientPermissionsImport,
+		},
+		Schema: map[string]*schema.Schema{
+			"realm_id": {
+				Type:     schema.TypeString,
+				Required: true,
+				ForceNew: true,
+			},
+			"client_id": {
+				Type:     schema.TypeString,
+				Required: true,
+				ForceNew: true,
+			},
+			"enabled": {
+				Type:     schema.TypeBool,
+				Required: true,
+				ForceNew: 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",
+			},
+			"view_scope_policy_id": {
+				Type:     schema.TypeString,
+				Optional: true,
+			},
+			"manage_scope_policy_id": {
+				Type:     schema.TypeString,
+				Optional: true,
+			},
+			"configure_scope_policy_id": {
+				Type:     schema.TypeString,
+				Optional: true,
+			},
+			"map_roles_scope_policy_id": {
+				Type:     schema.TypeString,
+				Optional: true,
+			},
+			"map_roles_client_scope_scope_policy_id": {
+				Type:     schema.TypeString,
+				Optional: true,
+			},
+			"map_roles_composite_scope_policy_id": {
+				Type:     schema.TypeString,
+				Optional: true,
+			},
+			"token_exchange_scope_policy_id": {
+				Type:     schema.TypeString,
+				Optional: true,
+			},
+		},
+	}
+}
+
+func clientPermissionsId(realmId, clientId string) string {
+	return fmt.Sprintf("%s/%s", realmId, clientId)
+}
+
+func setOpenidClientScopePermissionPolicy(keycloakClient *keycloak.KeycloakClient, realmId, clientId string, scopeName string, policyId string) error {
+	openidClientPermissions, err := keycloakClient.GetOpenidClientPermissions(realmId, clientId)
+	if err != nil {
+		return err
+	}
+
+	realmManagementClient, err := keycloakClient.GetOpenidClientByClientId(realmId, "realm-management")
+	if err != nil {
+		return err
+	}
+
+	permission, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, openidClientPermissions.ScopePermissions[scopeName].(string))
+	if err != nil {
+		return err
+	}
+
+	permission.Policies = []string{policyId}
+
+	return keycloakClient.UpdateOpenidClientAuthorizationPermission(permission)
+}
+
+func unsetOpenidClientScopePermissionPolicy(keycloakClient *keycloak.KeycloakClient, realmId, clientId, scopeName string) error {
+	openidClientPermissions, err := keycloakClient.GetOpenidClientPermissions(realmId, clientId)
+	if err != nil {
+		return err
+	}
+
+	realmManagementClient, err := keycloakClient.GetOpenidClientByClientId(realmId, "realm-management")
+	if err != nil {
+		return err
+	}
+
+	permission, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, openidClientPermissions.ScopePermissions[scopeName].(string))
+	if err != nil {
+		return err
+	}
+
+	permission.Policies = []string{}
+	err = keycloakClient.UpdateOpenidClientAuthorizationPermission(permission)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func resourceKeycloakOpenidClientPermissionsCreate(data *schema.ResourceData, meta interface{}) error {
+	return resourceKeycloakOpenidClientPermissionsUpdate(data, meta)
+}
+
+func resourceKeycloakOpenidClientPermissionsUpdate(data *schema.ResourceData, meta interface{}) error {
+	keycloakClient := meta.(*keycloak.KeycloakClient)
+
+	realmId := data.Get("realm_id").(string)
+	clientId := data.Get("client_id").(string)
+
+	if data.Get("enabled").(bool) {
+		err := keycloakClient.EnableOpenidClientPermissions(realmId, clientId)
+		if err != nil {
+			return err
+		}
+	} else {
+		err := keycloakClient.DisableOpenidClientPermissions(realmId, clientId)
+		if err != nil {
+			return err
+		}
+	}
+
+	viewScopePolicyId, ok := data.GetOkExists("view_scope_policy_id")
+	if ok && viewScopePolicyId != nil {
+		err := setOpenidClientScopePermissionPolicy(keycloakClient, realmId, clientId, "view", viewScopePolicyId.(string))
+		if err != nil {
+			return err
+		}
+	}
+	manageScopePolicyId, ok := data.GetOkExists("manage_scope_policy_id")
+	if ok && manageScopePolicyId != "" {
+		err := setOpenidClientScopePermissionPolicy(keycloakClient, realmId, clientId, "manage", manageScopePolicyId.(string))
+		if err != nil {
+			return err
+		}
+	}
+	configureScopePolicyId, ok := data.GetOkExists("configure_scope_policy_id")
+	if ok && configureScopePolicyId != "" {
+		err := setOpenidClientScopePermissionPolicy(keycloakClient, realmId, clientId, "configure", configureScopePolicyId.(string))
+		if err != nil {
+			return err
+		}
+	}
+	mapRolesScopePolicyId, ok := data.GetOkExists("map_roles_scope_policy_id")
+	if ok && mapRolesScopePolicyId != "" {
+		err := setOpenidClientScopePermissionPolicy(keycloakClient, realmId, clientId, "map-roles", mapRolesScopePolicyId.(string))
+		if err != nil {
+			return err
+		}
+	}
+	mapRolesClientsScopePolicyId, ok := data.GetOkExists("map_roles_client_scope_scope_policy_id")
+	if ok && mapRolesClientsScopePolicyId != "" {
+		err := setOpenidClientScopePermissionPolicy(keycloakClient, realmId, clientId, "map-roles-client-scope", mapRolesClientsScopePolicyId.(string))
+		if err != nil {
+			return err
+		}
+	}
+	mapRolesCompositeScopePolicyId, ok := data.GetOkExists("map_roles_composite_scope_policy_id")
+	if ok && mapRolesCompositeScopePolicyId != "" {
+		err := setOpenidClientScopePermissionPolicy(keycloakClient, realmId, clientId, "map-roles-composite", mapRolesCompositeScopePolicyId.(string))
+		if err != nil {
+			return err
+		}
+	}
+	tokenExchangeScopePolicyId, ok := data.GetOkExists("token_exchange_scope_policy_id")
+	if ok && tokenExchangeScopePolicyId != "" {
+		err := setOpenidClientScopePermissionPolicy(keycloakClient, realmId, clientId, "token-exchange", tokenExchangeScopePolicyId.(string))
+		if err != nil {
+			return err
+		}
+	}
+
+	return resourceKeycloakOpenidClientPermissionsRead(data, meta)
+}
+
+func resourceKeycloakOpenidClientPermissionsRead(data *schema.ResourceData, meta interface{}) error {
+	keycloakClient := meta.(*keycloak.KeycloakClient)
+	realmId := data.Get("realm_id").(string)
+	clientId := data.Get("client_id").(string)
+
+	openidClientPermissions, err := keycloakClient.GetOpenidClientPermissions(realmId, clientId)
+	if err != nil {
+		return handleNotFoundError(err, data)
+	}
+
+	data.SetId(clientPermissionsId(openidClientPermissions.RealmId, openidClientPermissions.ClientId))
+	data.Set("realm_id", openidClientPermissions.RealmId)
+	data.Set("client_id", openidClientPermissions.ClientId)
+
+	data.Set("enabled", openidClientPermissions.Enabled)
+
+	if !openidClientPermissions.Enabled {
+		log.Printf("[WARN] Removing resource with id %s from state as it no longer enabled", data.Id())
+		return nil
+	}
+
+	data.Set("view_scope_policy_id", nil)
+	data.Set("manage_scope_policy_id", nil)
+	data.Set("configure_scope_policy_id", nil)
+	data.Set("map_roles_scope_policy_id", nil)
+	data.Set("map_roles_client_scope_scope_policy_id", nil)
+	data.Set("map_roles_composite_scope_policy_id", nil)
+	data.Set("token_exchange_scope_policy_id", nil)
+
+	realmManagementClient, err := keycloakClient.GetOpenidClientByClientId(realmId, "realm-management")
+	if err != nil {
+		return err
+	}
+	permissionView, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, openidClientPermissions.ScopePermissions["view"].(string))
+	if err != nil {
+		return err
+	}
+	if permissionView != nil && len(permissionView.Policies) > 0 {
+		data.Set("view_scope_policy_id", permissionView.Policies[0])
+	}
+	permissionManage, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, openidClientPermissions.ScopePermissions["manage"].(string))
+	if err != nil {
+		return err
+	}
+	if permissionManage != nil && len(permissionManage.Policies) > 0 {
+		data.Set("manage_scope_policy_id", permissionManage.Policies[0])
+	}
+	permissionConfigure, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, openidClientPermissions.ScopePermissions["configure"].(string))
+	if err != nil {
+		return err
+	}
+	if permissionConfigure != nil && len(permissionConfigure.Policies) > 0 {
+		data.Set("configure_scope_policy_id", permissionConfigure.Policies[0])
+	}
+	permissionMapRoles, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, openidClientPermissions.ScopePermissions["map-roles"].(string))
+	if err != nil {
+		return err
+	}
+	if permissionMapRoles != nil && len(permissionMapRoles.Policies) > 0 {
+		data.Set("map_roles_scope_policy_id", permissionMapRoles.Policies[0])
+	}
+	permissionMapRolesClientScope, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, openidClientPermissions.ScopePermissions["map-roles-client-scope"].(string))
+	if err != nil {
+		return err
+	}
+	if permissionMapRolesClientScope != nil && len(permissionMapRolesClientScope.Policies) > 0 {
+		data.Set("map_roles_client_scope_scope_policy_id", permissionMapRolesClientScope.Policies[0])
+	}
+	permissionMapRolesComposite, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, openidClientPermissions.ScopePermissions["map-roles-composite"].(string))
+	if err != nil {
+		return err
+	}
+	if permissionMapRolesComposite != nil && len(permissionMapRolesComposite.Policies) > 0 {
+		data.Set("map_roles_composite_scope_policy_id", permissionMapRolesComposite.Policies[0])
+	}
+	permissionTokenExchange, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, realmManagementClient.Id, openidClientPermissions.ScopePermissions["token-exchange"].(string))
+	if err != nil {
+		return err
+	}
+	if permissionTokenExchange != nil && len(permissionTokenExchange.Policies) > 0 {
+		data.Set("token_exchange_scope_policy_id", permissionTokenExchange.Policies[0])
+	}
+	data.Set("authorization_resource_server_id", realmManagementClient.Id)
+
+	return nil
+}
+
+func resourceKeycloakOpenidClientPermissionsDelete(data *schema.ResourceData, meta interface{}) error {
+
+	keycloakClient := meta.(*keycloak.KeycloakClient)
+
+	realmId := data.Get("realm_id").(string)
+	clientId := data.Get("client_id").(string)
+
+	openidClientPermissions, err := keycloakClient.GetOpenidClientPermissions(realmId, clientId)
+	if err == nil && openidClientPermissions.Enabled {
+		_ = unsetOpenidClientScopePermissionPolicy(keycloakClient, realmId, clientId, "view")
+	}
+	return keycloakClient.DisableOpenidClientPermissions(realmId, clientId)
+}
+
+func resourceKeycloakOpenidClientPermissionsImport(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}}/{{openidClientId}}")
+	}
+	d.Set("realm_id", parts[0])
+	d.Set("client_id", parts[1])
+
+	d.SetId(clientPermissionsId(parts[0], parts[1]))
+
+	return []*schema.ResourceData{d}, nil
+}


Test patch:
diff --git a/provider/resource_keycloak_openid_client_permissions_test.go b/provider/resource_keycloak_openid_client_permissions_test.go
new file mode 100644
index 0000000..21afd70
--- /dev/null
+++ b/provider/resource_keycloak_openid_client_permissions_test.go
@@ -0,0 +1,243 @@
+package provider
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+	"github.com/mrparkers/terraform-provider-keycloak/keycloak"
+)
+
+func TestAccKeycloakOpenidClientPermission_basic(t *testing.T) {
+	realmName := "tf_view-" + acctest.RandString(10)
+	clientId := "tf-" + acctest.RandString(10)
+
+	resource.Test(t, resource.TestCase{
+		ProviderFactories: testAccProviderFactories,
+		PreCheck:          func() { testAccPreCheck(t) },
+		Steps: []resource.TestStep{
+			{
+				Config: testKeycloakOpenidClientPermission_basic(realmName, clientId),
+				Check:  testAccCheckKeycloakOpenidClientPermissionExists("keycloak_openid_client_permissions.my_permission"),
+			}, {
+				Config: testKeycloakOpenidClientPermissionDelete_basic(realmName, clientId),
+				Check:  testAccCheckKeycloakOpenidClientPermissionDoentExists("keycloak_openid_client_permissions.my_permission"),
+			},
+		},
+	})
+}
+
+func testAccCheckKeycloakOpenidClientPermissionExists(resourceName string) resource.TestCheckFunc {
+	return func(s *terraform.State) error {
+		permissions, err := getOpenidClientPermissionsFromState(s, resourceName)
+		if err != nil {
+			return err
+		}
+
+		rs, ok := s.RootModule().Resources[resourceName]
+		if !ok {
+			return fmt.Errorf("resource not found: %s", resourceName)
+		}
+		authorizationResourceServerId := rs.Primary.Attributes["authorization_resource_server_id"]
+		viewScopePolicyId := rs.Primary.Attributes["view_scope_policy_id"]
+
+		var realmManagementId string
+		clients, _ := keycloakClient.GetOpenidClients(permissions.RealmId, false)
+		for _, client := range clients {
+			if client.ClientId == "realm-management" {
+				realmManagementId = client.Id
+				break
+			}
+		}
+
+		if authorizationResourceServerId != realmManagementId {
+			return fmt.Errorf("computed authorizationResourceServerId %s was not equal to %s (the id of the realm-management client)", authorizationResourceServerId, realmManagementId)
+		}
+
+		authzClient, err := keycloakClient.GetOpenidClientAuthorizationPermission(permissions.RealmId, realmManagementId, permissions.ScopePermissions["view"].(string))
+		if err != nil {
+			return err
+		}
+
+		policyId := authzClient.Policies[0]
+		if viewScopePolicyId != policyId {
+			return fmt.Errorf("computed ViewScopePolicyId %s was not equal to policyId %s", viewScopePolicyId, policyId)
+		}
+
+		return nil
+	}
+}
+
+func testAccCheckKeycloakOpenidClientPermissionDoentExists(resourceName string) resource.TestCheckFunc {
+	return func(s *terraform.State) error {
+		permissions, err := getOpenidClientPermissionsFromState(s, resourceName)
+		if err != nil {
+			return err
+		}
+
+		rs, ok := s.RootModule().Resources[resourceName]
+		if !ok {
+			return fmt.Errorf("resource not found: %s", resourceName)
+		}
+
+		if permissions.Enabled != false {
+			return fmt.Errorf("Client Permission in Keycloak is not disabled")
+		}
+		if rs.Primary.Attributes["enabled"] != "false" {
+			return fmt.Errorf("Client Permission State is not disabled")
+		}
+
+		return nil
+	}
+}
+
+func getOpenidClientPermissionsFromState(s *terraform.State, resourceName string) (*keycloak.OpenidClientPermissions, error) {
+	keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient)
+
+	rs, ok := s.RootModule().Resources[resourceName]
+	if !ok {
+		return nil, fmt.Errorf("resource not found: %s", resourceName)
+	}
+
+	realmId := rs.Primary.Attributes["realm_id"]
+	clientId := rs.Primary.Attributes["client_id"]
+
+	permissions, err := keycloakClient.GetOpenidClientPermissions(realmId, clientId)
+	if err != nil {
+		return nil, fmt.Errorf("error getting openid_client permissions with realm id %s and client id %s: %s", realmId, clientId, err)
+
+	}
+	return permissions, nil
+}
+
+func testKeycloakOpenidClientPermission_basic(realmId, clientId string) string {
+	return fmt.Sprintf(`
+resource "keycloak_realm" "realm" {
+  realm = "%s"
+}
+
+resource keycloak_openid_client "my_openid_client" {
+  realm_id              = keycloak_realm.realm.id
+  name                  = "my_openid_client"
+  client_id             = "%s"
+  client_secret         = "secret"
+  access_type           = "CONFIDENTIAL"
+  standard_flow_enabled = true
+  valid_redirect_uris   = [
+    "http://localhost:8080/*",
+  ]
+}
+
+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_openid_client_permissions" "my_permission" {
+	realm_id                               = keycloak_realm.realm.id
+	client_id                              = keycloak_openid_client.my_openid_client.id
+
+	enabled = true
+
+	view_scope_policy_id                   = keycloak_openid_client_user_policy.test.id
+	manage_scope_policy_id                 = keycloak_openid_client_user_policy.test.id
+	configure_scope_policy_id              = keycloak_openid_client_user_policy.test.id
+	map_roles_scope_policy_id              = keycloak_openid_client_user_policy.test.id
+	map_roles_client_scope_scope_policy_id = keycloak_openid_client_user_policy.test.id
+	map_roles_composite_scope_policy_id    = keycloak_openid_client_user_policy.test.id
+	token_exchange_scope_policy_id         = keycloak_openid_client_user_policy.test.id
+}
+
+	`, realmId, clientId)
+}
+
+func testKeycloakOpenidClientPermissionDelete_basic(realmId, clientId string) string {
+	return fmt.Sprintf(`
+resource "keycloak_realm" "realm" {
+  realm = "%s"
+}
+
+resource keycloak_openid_client "my_openid_client" {
+  realm_id              = keycloak_realm.realm.id
+  name                  = "my_openid_client"
+  client_id             = "%s"
+  client_secret         = "secret"
+  access_type           = "CONFIDENTIAL"
+  standard_flow_enabled = true
+  valid_redirect_uris   = [
+    "http://localhost:8080/*",
+  ]
+}
+
+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_openid_client_permissions" "my_permission" {
+	realm_id                               = keycloak_realm.realm.id
+	client_id                              = keycloak_openid_client.my_openid_client.id
+
+	enabled = false
+}
+
+	`, realmId, clientId)
+}
diff --git a/provider/resource_keycloak_openid_client_test.go b/provider/resource_keycloak_openid_client_test.go
index 0be2d34..82a9566 100644
--- a/provider/resource_keycloak_openid_client_test.go
+++ b/provider/resource_keycloak_openid_client_test.go
@@ -2,12 +2,13 @@ package provider
 
 import (
 	"fmt"
+	"regexp"
+	"testing"
+
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
 	"github.com/mrparkers/terraform-provider-keycloak/keycloak"
-	"regexp"
-	"testing"
 )
 
 func TestAccKeycloakOpenidClient_basic(t *testing.T) {