Repository: ansible-collections/community.general

Issue:
keycloak_authz_permission incorrectly sends a one element array as string
### Summary

When running:

```yaml
- name: Allow all users to list sessions
  community.general.keycloak_authz_permission:
    validate_certs: false
    name: allow-users-to-list-sessions
    state: present
    description: Allow users to list sessions
    permission_type: scope
    decision_strategy: AFFIRMATIVE
    scopes:
      - urn:ebo:CS:scopes:list
    resources:
      - Session
    policies:
      - all-users
    client_id: charging-stations
    realm: "{{ keycloak_realm }}"
    auth_keycloak_url: https://{{ ansible_default_ipv4.address }}:{{ keycloak_https_port }}/auth
    auth_username: admin
    auth_password: "{{ secret_keycloak_admin_password }}"
    auth_realm: master
```
It returns a 500, where java/keycloak prints:

```text
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `java.util.HashSet` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('34a556e1-95c0-482c-91df-4bccc865d356')
```

Which means it expects an array, but it got a string.

Looking at the python code [https://github.com/ansible-collections/community.general/blob/7721420388aa1d9cf7751fa250754d3419f3a2b1/plugins/modules/keycloak_authz_permission.py#L333](https://github.com/ansible-collections/community.general/blob/7721420388aa1d9cf7751fa250754d3419f3a2b1/plugins/modules/keycloak_authz_permission.py#L333):
```python
                payload['resources'] = r['_id']
```
I think this should be wrapped in an array.

    

### Issue Type

Bug Report

### Component Name

community.general.keycloak_authz_permission

### Ansible Version

```console (paste below)
$ ansible --version
ansible [core 2.15.3]
  config file = None
  configured module search path = ['/home/rgoussey/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/rgoussey/.local/lib/python3.10/site-packages/ansible
  ansible collection location = /home/rgoussey/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/rgoussey/.local/bin/ansible
  python version = 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] (/usr/bin/python3)
  jinja version = 3.1.2
  libyaml = True

```


### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general
Collection        Version
----------------- -------
community.general 7.3.0  

```


### Configuration

```console (paste below)

```


### OS / Environment

Ubuntu

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->

Tweak variables, and run against a running keycloak.

```yaml (paste below)
- name: Allow all users to list sessions
  community.general.keycloak_authz_permission:
    validate_certs: false
    name: allow-users-to-list-sessions
    state: present
    description: Allow users to list sessions
    permission_type: scope
    decision_strategy: AFFIRMATIVE
    scopes:
      - urn:ebo:CS:scopes:list
    resources:
      - Session
    policies:
      - all-users
    client_id: charging-stations
    realm: "{{ keycloak_realm }}"
    auth_keycloak_url: https://{{ ansible_default_ipv4.address }}:{{ keycloak_https_port }}/auth
    auth_username: admin
    auth_password: "{{ secret_keycloak_admin_password }}"
    auth_realm: master
```


### Expected Results

The task should use the correct serialization format.

### Actual Results

```console (paste below)

```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/modules/keycloak_authz_permission.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/keycloak_authz_permission.py)

If these files are incorrect, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @eikef @mattock @ndclt
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
@RobinGoussey

The *keycloak_authz_permission* uses API endpoints and JSON payloads that are not officially documented. I basically created the only API documentation that exists by capturing the REST API calls made from the Admin Console:

* https://www.puppeteers.net/blog/keycloak-authorization-services-rest-api-paths-and-payload/

Given the unofficial status of this API (endpoint and payloads) I would not be surprised if Keycloak project might change them without warning. 

Now, what Keycloak version are you using? I developed the code against Keycloak 15.0.2 and could not spot any issues despite extensive integration tests.


@mattock I'm testing against keycloak 22.0.0
@RobinGoussey I've been integration testing this module against 20.0.2 and have not noticed any problems. Maybe something broke in 21 or 22. Are you able to run the integration tests against 22.0.0 and if yes, do they pass? You need to run Keycloak in a container like this:

```
podman rm mykeycloak
podman run --name mykeycloak -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=password -e KC_HTTP_RELATIVE_PATH=/auth quay.io/keycloak/keycloak:20.0.2 start-dev
```
You should be able to replace "20.0.2" with "22.0.0".

Then run the tests:

```
cd tests/integration/targets
ansible-test integration --allow-unsupported -v keycloak_authz_permission
```

If there is an issue with 22.0.0 it should manifest itself in these tests.
I'm also experiencing this issue with KC 22.0.1. I believe the problem isn't with KC, but the module incorrectly sending the "resources" element as a string instead of list. If you look at the module debug output, you can see it *thinks* its creating a list in the module args, even when setting the "resources" parameter as a string.:
```
...
 "resources": [
                "group.resource.1fb95ef0-a822-4b37-a837-6f09ca91e95a"
            ],
...
```
However, looking at the json payload its sending to KC in Wireshark, it looks like this:
```
{"name": "view.members.permission.group.1fb95ef0-a822-4b37-a837-6f09ca91e95a", "description": null, "type": "scope", "decisionStrategy": "AFFIRMATIVE", "logic": "POSITIVE", "scopes": ["225b674e-ef19-4599-8a0b-00fc656a73b6"], "resources": "14193fd0-ae34-4a70-82d3-bebe23ac79bf", "policies": ["d92b8837-9b2d-4014-bad2-38992862c4bb"], "id": "a557d676-004d-4299-bd44-b166bbf01ef6"}
```
FWIW, manually using the REST api with the "resources" parameter as a list works.
```
PUT /auth/admin/realms/testrealm/clients/19a12ac7-dfac-440b-818c-5ad2994499f8/authz/resource-server/permission/scope/a557d676-004d-4299-bd44-b166bbf01ef6
```
json payload
```
{"name": "view.members.permission.group.1fb95ef0-a822-4b37-a837-6f09ca91e95a", "description": null, "type": "scope", "decisionStrategy": "AFFIRMATIVE", "logic": "POSITIVE", "scopes": ["225b674e-ef19-4599-8a0b-00fc656a73b6"], "resources": ["14193fd0-ae34-4a70-82d3-bebe23ac79bf"], "policies": ["d92b8837-9b2d-4014-bad2-38992862c4bb"], "id": "a557d676-004d-4299-bd44-b166bbf01ef6"}
```

Finally, looking at your integration test, there is no "resources" parameter for the "Create scope permission" test. I believe that resources are optional in the KC API, so its probably a valid test, just not in this case where you are trying to validate assigning a single resource to a permission (for which there is no test case).
I can confirm this error exists with version 15.0.2.
```
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `java.util.HashSet` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('e0d9706f-fa20-4e25-8eab-f8e920e15436')
 at [Source: (String)"{"name": "view.members.permission.group.f90a06ec-3d04-4e2e-8e43-e4faec6dc4a8", "description": null, "type": "scope", "decisionStrategy": "AFFIRMATIVE", "logic": "POSITIVE", "scopes": ["28c9998c-a59a-443f-8af4-e6b1a29f9d26"], "resources": "e0d9706f-fa20-4e25-8eab-f8e920e15436", "policies": ["43fe2363-8ab9-4e39-a417-5765494110d7"], "id": "b04b1ad1-d30f-4a06-ad17-a8b8d7eb0e88"}"; line: 1, column: 239] (through reference chain: org.keycloak.representations.idm.authorization.ScopePermissionRepresentation["resources"])
```

Resolving patch:
diff --git a/plugins/modules/keycloak_authz_permission.py b/plugins/modules/keycloak_authz_permission.py
--- a/plugins/modules/keycloak_authz_permission.py
+++ b/plugins/modules/keycloak_authz_permission.py
@@ -330,7 +330,7 @@ def main():
             if not r:
                 module.fail_json(msg='Unable to find authorization resource with name %s for client %s in realm %s' % (resources[0], cid, realm))
             else:
-                payload['resources'] = r['_id']
+                payload['resources'].append(r['_id'])
 
             for rs in r['scopes']:
                 resource_scopes.append(rs['id'])


Test patch: