Repository: Nerzal/gocloak (go)

Issue:
Fetch authoriazation policy (resources, scope, associated policies) using keycloak policy APIs
**Is your feature request related to a problem? Please describe.**
As a developer i want to get the related resources, scopes, and associated policies related to a policy so that i can get the related resources of this policy

**Describe the solution you'd like**
Consume Keycloak Policy API's
1. auth/admin/realms/{realmName}/clients/{clientId}/authz/resource-server/policy/{policyId}/resources
2. auth/admin/realms/{realmName}/clients/{clientId}/authz/resource-server/policy/{policyId}/scopes
3. auth/admin/realms/{realmName}/clients/{clientId}/authz/resource-server/policy/{policyId}/associatedPolicies

**Describe alternatives you've considered**
using permission API's that provide the details of the a permission.

**Additional context**
![image](https://user-images.githubusercontent.com/25984976/130353545-a28f10d9-3204-4157-818d-5ea7e7befc23.png)


Pull request description:
feat: adding APIs handler to fetch policy resources
1. handle fetch policy resources
2. handle fetch policy scopes
3. handle fetch policy associated policies

Thanks for your contribution!
Hi, if there is an issue, that your PR adresses, please link it! 


Fetch authoriazation policy (resources, scope, associated policies) using keycloak policy APIs Fixes #300


Patch:
diff --git a/client.go b/client.go
index 4b10f26..1e19e9f 100644
--- a/client.go
+++ b/client.go
@@ -2913,6 +2913,54 @@ func (client *gocloak) DeletePolicy(ctx context.Context, token, realm, idOfClien
 	return checkForError(resp, err, errMessage)
 }
 
+// GetAuthorizationPolicyAssociatedPolicies returns a client's associated policies of specific policy with the given policy id, using access token from admin
+func (client *gocloak) GetAuthorizationPolicyAssociatedPolicies(ctx context.Context, token, realm, idOfClient, policyID string) ([]*PolicyRepresentation, error) {
+	const errMessage = "could not get policy associated policies"
+
+	var result []*PolicyRepresentation
+	resp, err := client.getRequestWithBearerAuth(ctx, token).
+		SetResult(&result).
+		Get(client.getAdminRealmURL(realm, "clients", idOfClient, "authz", "resource-server", "policy", policyID, "associatedPolicies"))
+
+	if err := checkForError(resp, err, errMessage); err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
+// GetAuthorizationPolicyResources returns a client's resources of specific policy with the given policy id, using access token from admin
+func (client *gocloak) GetAuthorizationPolicyResources(ctx context.Context, token, realm, idOfClient, policyID string) ([]*PolicyResourceRepresentation, error) {
+	const errMessage = "could not get policy resources"
+
+	var result []*PolicyResourceRepresentation
+	resp, err := client.getRequestWithBearerAuth(ctx, token).
+		SetResult(&result).
+		Get(client.getAdminRealmURL(realm, "clients", idOfClient, "authz", "resource-server", "policy", policyID, "resources"))
+
+	if err := checkForError(resp, err, errMessage); err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
+// GetAuthorizationPolicyScopes returns a client's scopes of specific policy with the given policy id, using access token from admin
+func (client *gocloak) GetAuthorizationPolicyScopes(ctx context.Context, token, realm, idOfClient, policyID string) ([]*PolicyScopeRepresentation, error) {
+	const errMessage = "could not get policy scopes"
+
+	var result []*PolicyScopeRepresentation
+	resp, err := client.getRequestWithBearerAuth(ctx, token).
+		SetResult(&result).
+		Get(client.getAdminRealmURL(realm, "clients", idOfClient, "authz", "resource-server", "policy", policyID, "scopes"))
+
+	if err := checkForError(resp, err, errMessage); err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
 // GetResourcePolicy updates a permission for a specifc resource, using token obtained by Resource Owner Password Credentials Grant or Token exchange
 func (client *gocloak) GetResourcePolicy(ctx context.Context, token, realm, permissionID string) (*ResourcePolicyRepresentation, error) {
 	const errMessage = "could not get resource policy"
diff --git a/gocloak.go b/gocloak.go
index 2ad9f20..182177f 100644
--- a/gocloak.go
+++ b/gocloak.go
@@ -424,6 +424,12 @@ type GoCloak interface {
 	UpdatePolicy(ctx context.Context, token, realm, idOfClient string, policy PolicyRepresentation) error
 	// DeletePolicy deletes a policy associated with the client, using access token from admin
 	DeletePolicy(ctx context.Context, token, realm, idOfClient, policyID string) error
+	// GetPolicyAssociatedPolicies returns a client's policy associated policies with the given policy id, using access token from admin
+	GetAuthorizationPolicyAssociatedPolicies(ctx context.Context, token, realm, idOfClient, policyID string) ([]*PolicyRepresentation, error)
+	// GetPolicyResources returns a client's resources of specific policy with the given policy id, using access token from admin
+	GetAuthorizationPolicyResources(ctx context.Context, token, realm, idOfClient, policyID string) ([]*PolicyResourceRepresentation, error)
+	// GetPolicyScopes returns a client's scopes of specific policy with the given policy id, using access token from admin
+	GetAuthorizationPolicyScopes(ctx context.Context, token, realm, idOfClient, policyID string) ([]*PolicyScopeRepresentation, error)
 
 	// GetResourcePolicy updates a permission for a specifc resource, using token obtained by Resource Owner Password Credentials Grant or Token exchange
 	GetResourcePolicy(ctx context.Context, token, realm, permissionID string) (*ResourcePolicyRepresentation, error)
diff --git a/models.go b/models.go
index 20140b5..4cedd2a 100644
--- a/models.go
+++ b/models.go
@@ -1090,6 +1090,18 @@ type ResourcePolicyRepresentation struct {
 	Type             *string           `json:"type,omitempty"`
 }
 
+// PolicyScopeRepresentation is a representation of a scopes of specific policy
+type PolicyScopeRepresentation struct {
+	ID   *string `json:"id,omitempty"`
+	Name *string `json:"name,omitempty"`
+}
+
+// PolicyResourceRepresentation is a representation of a resource of specific policy
+type PolicyResourceRepresentation struct {
+	ID   *string `json:"_id,omitempty"`
+	Name *string `json:"name,omitempty"`
+}
+
 // GetResourcePoliciesParams is a representation of the query params for getting policies
 type GetResourcePoliciesParams struct {
 	ResourceID *string `json:"resource_id,omitempty"`


Test patch:
diff --git a/client_test.go b/client_test.go
index 5579355..f47c704 100644
--- a/client_test.go
+++ b/client_test.go
@@ -4704,6 +4704,312 @@ func TestGocloak_CreateListGetUpdateDeletePolicy(t *testing.T) {
 	require.Equal(t, *(createdPolicy.Name), *(updatedPolicy.Name))
 }
 
+func TestGocloak_ErrorsGetAuthorizationPolicyAssociatedPolicies(t *testing.T) {
+	cfg := GetConfig(t)
+	client := NewClientWithDebug(t)
+	token := GetAdminToken(t, client)
+
+	// Create Policy
+	policy, parentPolicyID := CreatePolicy(t, client, gocloakClientID, gocloak.PolicyRepresentation{
+		Name:        GetRandomNameP("PolicyName"),
+		Description: gocloak.StringP("Policy Description"),
+		Type:        gocloak.StringP("client"),
+		Logic:       gocloak.POSITIVE,
+		ClientPolicyRepresentation: gocloak.ClientPolicyRepresentation{
+			Clients: &[]string{
+				gocloakClientID,
+			},
+		},
+	})
+
+	// Create Resource
+	resource, resourceID := CreateResource(t, client, gocloakClientID)
+
+	// Create Permission
+	permission, permissionID := CreatePermission(t, client, gocloakClientID, gocloak.PermissionRepresentation{
+		Name:        GetRandomNameP("PermissionName"),
+		Description: gocloak.StringP("Permission Description"),
+		Resources: &[]string{
+			resourceID,
+		},
+		Policies: &[]string{
+			parentPolicyID,
+		},
+		Type: gocloak.StringP("resource"),
+	})
+
+	func() {
+		permission()
+		resource()
+		policy()
+	}()
+
+	// List Polices
+	_, err := client.GetAuthorizationPolicyAssociatedPolicies(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		gocloakClientID,
+		permissionID,
+	)
+	require.Error(t, err, "GetAuthorizationPolicyAssociatedPolicies no error")
+}
+
+func TestGocloak_GetAuthorizationPolicyAssociatedPolicies(t *testing.T) {
+	cfg := GetConfig(t)
+	client := NewClientWithDebug(t)
+	token := GetAdminToken(t, client)
+
+	// Create Policy
+	policyName := "parentPolicy"
+	parentPolicy, parentPolicyID := CreatePolicy(t, client, gocloakClientID, gocloak.PolicyRepresentation{
+		Name:        gocloak.StringP(policyName),
+		Description: gocloak.StringP("Policy Description"),
+		Type:        gocloak.StringP("client"),
+		Logic:       gocloak.POSITIVE,
+		ClientPolicyRepresentation: gocloak.ClientPolicyRepresentation{
+			Clients: &[]string{
+				gocloakClientID,
+			},
+		},
+	})
+
+	// Create Resource
+	resource, resourceID := CreateResource(t, client, gocloakClientID)
+
+	// Create Permission
+	permission, permissionID := CreatePermission(t, client, gocloakClientID, gocloak.PermissionRepresentation{
+		Name:        GetRandomNameP("PermissionName"),
+		Description: gocloak.StringP("Permission Description"),
+		Resources: &[]string{
+			resourceID,
+		},
+		Policies: &[]string{
+			parentPolicyID,
+		},
+		Type: gocloak.StringP("resource"),
+	})
+
+	// List Polices
+	policies, err := client.GetAuthorizationPolicyAssociatedPolicies(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		gocloakClientID,
+		permissionID,
+	)
+	require.NoError(t, err, "GetAuthorizationPolicyAssociatedPolicies failed")
+	require.Equal(t, *policies[0].Name, policyName)
+
+	// Delete
+	defer func() {
+		permission()
+		resource()
+		parentPolicy()
+	}()
+}
+
+func TestGocloak_ErrorsGetAuthorizationPolicyResources(t *testing.T) {
+	cfg := GetConfig(t)
+	client := NewClientWithDebug(t)
+	token := GetAdminToken(t, client)
+
+	// Create Policy
+	policy, policyID := CreatePolicy(t, client, gocloakClientID, gocloak.PolicyRepresentation{
+		Name:        GetRandomNameP("PolicyName"),
+		Description: gocloak.StringP("Policy Description"),
+		Type:        gocloak.StringP("client"),
+		Logic:       gocloak.POSITIVE,
+		ClientPolicyRepresentation: gocloak.ClientPolicyRepresentation{
+			Clients: &[]string{
+				gocloakClientID,
+			},
+		},
+	})
+
+	// Create Resource
+	resource, resourceID := CreateResource(t, client, gocloakClientID)
+
+	// Create Permission
+	_, permissionID := CreatePermission(t, client, gocloakClientID, gocloak.PermissionRepresentation{
+		Name:        GetRandomNameP("PermissionName"),
+		Description: gocloak.StringP("Permission Description"),
+		Resources: &[]string{
+			resourceID,
+		},
+		Policies: &[]string{
+			policyID,
+		},
+		Type: gocloak.StringP("resource"),
+	})
+
+	func() {
+		resource()
+		policy()
+	}()
+
+	// List Polices
+	_, err := client.GetAuthorizationPolicyResources(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		gocloakClientID,
+		permissionID,
+	)
+	require.Error(t, err, "GetAuthorizationPolicyResources no error")
+}
+
+func TestGocloak_GetAuthorizationPolicyResources(t *testing.T) {
+	cfg := GetConfig(t)
+	client := NewClientWithDebug(t)
+	token := GetAdminToken(t, client)
+
+	// Create Policy
+	policy, policyID := CreatePolicy(t, client, gocloakClientID, gocloak.PolicyRepresentation{
+		Name:        GetRandomNameP("PolicyName"),
+		Description: gocloak.StringP("Policy Description"),
+		Type:        gocloak.StringP("client"),
+		Logic:       gocloak.POSITIVE,
+		ClientPolicyRepresentation: gocloak.ClientPolicyRepresentation{
+			Clients: &[]string{
+				gocloakClientID,
+			},
+		},
+	})
+
+	// Create Resource
+	resource, resourceID := CreateResource(t, client, gocloakClientID)
+
+	// Create Permission
+	_, permissionID := CreatePermission(t, client, gocloakClientID, gocloak.PermissionRepresentation{
+		Name:        GetRandomNameP("PermissionName"),
+		Description: gocloak.StringP("Permission Description"),
+		Resources: &[]string{
+			resourceID,
+		},
+		Policies: &[]string{
+			policyID,
+		},
+		Type: gocloak.StringP("resource"),
+	})
+
+	// List Polices
+	resources, err := client.GetAuthorizationPolicyResources(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		gocloakClientID,
+		permissionID,
+	)
+	require.NoError(t, err, "GetAuthorizationPolicyResources failed")
+	require.Equal(t, *resources[0].ID, resourceID)
+
+	defer func() {
+		resource()
+		policy()
+	}()
+}
+
+func TestGocloak_ErrorsGetAuthorizationPolicyScopes(t *testing.T) {
+	cfg := GetConfig(t)
+	client := NewClientWithDebug(t)
+	token := GetAdminToken(t, client)
+
+	// Create Policy
+	policy, policyID := CreatePolicy(t, client, gocloakClientID, gocloak.PolicyRepresentation{
+		Name:        GetRandomNameP("PolicyName"),
+		Description: gocloak.StringP("Policy Description"),
+		Type:        gocloak.StringP("client"),
+		Logic:       gocloak.POSITIVE,
+		ClientPolicyRepresentation: gocloak.ClientPolicyRepresentation{
+			Clients: &[]string{
+				gocloakClientID,
+			},
+		},
+	})
+
+	// Create SCOPE
+	scope, scopeID := CreateScope(t, client, gocloakClientID)
+
+	// Create Permission
+	_, permissionID := CreatePermission(t, client, gocloakClientID, gocloak.PermissionRepresentation{
+		Name:        GetRandomNameP("PermissionName"),
+		Description: gocloak.StringP("Permission Description"),
+		Resources: &[]string{
+			scopeID,
+		},
+		Policies: &[]string{
+			policyID,
+		},
+		Type: gocloak.StringP("resource"),
+	})
+
+	func() {
+		scope()
+		policy()
+	}()
+
+	// List Polices
+	_, err := client.GetAuthorizationPolicyScopes(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		gocloakClientID,
+		permissionID,
+	)
+	require.Error(t, err, "GetAuthorizationPolicyScopes no error")
+}
+
+func TestGocloak_GetAuthorizationPolicyScopes(t *testing.T) {
+	cfg := GetConfig(t)
+	client := NewClientWithDebug(t)
+	token := GetAdminToken(t, client)
+
+	// Create Policy
+	policy, policyID := CreatePolicy(t, client, gocloakClientID, gocloak.PolicyRepresentation{
+		Name:        GetRandomNameP("PolicyName"),
+		Description: gocloak.StringP("Policy Description"),
+		Type:        gocloak.StringP("client"),
+		Logic:       gocloak.POSITIVE,
+		ClientPolicyRepresentation: gocloak.ClientPolicyRepresentation{
+			Clients: &[]string{
+				gocloakClientID,
+			},
+		},
+	})
+
+	// Create Resource
+	scope, scopeID := CreateScope(t, client, gocloakClientID)
+
+	// Create Permission
+	_, permissionID := CreatePermission(t, client, gocloakClientID, gocloak.PermissionRepresentation{
+		Name:        GetRandomNameP("PermissionName"),
+		Description: gocloak.StringP("Permission Description"),
+		Scopes: &[]string{
+			scopeID,
+		},
+		Policies: &[]string{
+			policyID,
+		},
+		Type: gocloak.StringP("resource"),
+	})
+	// List Polices
+	scopes, err := client.GetAuthorizationPolicyScopes(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		gocloakClientID,
+		permissionID,
+	)
+	require.NoError(t, err, "GetAuthorizationPolicyScopes failed")
+	require.Equal(t, *scopes[0].ID, scopeID)
+
+	defer func() {
+		scope()
+		policy()
+	}()
+}
+
 func TestGocloak_CreateGetUpdateDeleteResourcePolicy(t *testing.T) {
 	// parallel is causing intermittent conflict with role-based test GetClientScopeMappingsClientRolesAvailable
 	cfg := GetConfig(t)