From 9e6a6f607612bf4585eccf022ff9f3f7559caa8d Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Wed, 1 May 2024 14:44:15 +0000 Subject: [PATCH 001/135] Bump version to 2.2.1 --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index 02838e3..d121f3c 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "2.2.0" +version: "2.2.1" readme: README.md authors: - Romain Pelisse From 1a73c39a9140c7e0132e0211e3726191b61075d9 Mon Sep 17 00:00:00 2001 From: Deven Phillips Date: Thu, 2 May 2024 12:09:36 -0400 Subject: [PATCH 002/135] Fix logic in when clause --- roles/keycloak_quarkus/tasks/jdbc_driver.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/roles/keycloak_quarkus/tasks/jdbc_driver.yml b/roles/keycloak_quarkus/tasks/jdbc_driver.yml index 11fa385..10731cf 100644 --- a/roles/keycloak_quarkus/tasks/jdbc_driver.yml +++ b/roles/keycloak_quarkus/tasks/jdbc_driver.yml @@ -4,8 +4,7 @@ msg: >- When JDBC driver download credentials are set, both the username and the password MUST be set when: - - keycloak_jdbc_download_user is undefined and keycloak_jdbc_download_pass is not undefined - - keycloak_jdbc_download_pass is undefined and keycloak_jdbc_download_user is not undefined + - (keycloak_jdbc_download_user is undefined and keycloak_jdbc_download_pass is not undefined) or (keycloak_jdbc_download_pass is undefined and keycloak_jdbc_download_user is not undefined) - name: "Retrieve JDBC Driver from {{ keycloak_jdbc_download_user | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url) }}" ansible.builtin.get_url: url: "{{ keycloak_jdbc_download_url | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url) }}" From 978494524fd850f396245c4302a2f5eb5eb24f67 Mon Sep 17 00:00:00 2001 From: Deven Phillips Date: Thu, 2 May 2024 12:31:16 -0400 Subject: [PATCH 003/135] Fix errors introduced --- roles/keycloak_quarkus/tasks/jdbc_driver.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/keycloak_quarkus/tasks/jdbc_driver.yml b/roles/keycloak_quarkus/tasks/jdbc_driver.yml index 10731cf..dc84880 100644 --- a/roles/keycloak_quarkus/tasks/jdbc_driver.yml +++ b/roles/keycloak_quarkus/tasks/jdbc_driver.yml @@ -3,8 +3,8 @@ ansible.builtin.fail: msg: >- When JDBC driver download credentials are set, both the username and the password MUST be set - when: - - (keycloak_jdbc_download_user is undefined and keycloak_jdbc_download_pass is not undefined) or (keycloak_jdbc_download_pass is undefined and keycloak_jdbc_download_user is not undefined) + when: + - (keycloak_jdbc_download_user is undefined and keycloak_jdbc_download_pass is not undefined) or (keycloak_jdbc_download_pass is undefined and keycloak_jdbc_download_user is not undefined) - name: "Retrieve JDBC Driver from {{ keycloak_jdbc_download_user | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url) }}" ansible.builtin.get_url: url: "{{ keycloak_jdbc_download_url | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url) }}" From 1ab3ebc2a4dfdf54cc4fc19f091a58d31af1fd89 Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Thu, 2 May 2024 16:59:47 +0000 Subject: [PATCH 004/135] Update changelog for release 2.2.1 Signed-off-by: ansible-middleware-core --- CHANGELOG.rst | 13 +++++++++++++ changelogs/changelog.yaml | 11 +++++++++++ 2 files changed, 24 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4366622..145a090 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,19 @@ middleware\_automation.keycloak Release Notes This changelog describes changes after version 0.2.6. +v2.2.1 +====== + +Release Summary +--------------- + +Internal release, documentation or test changes only. + +Bugfixes +-------- + +- JDBC provider: fix clause in argument validation `#204 `_ + v2.2.0 ====== diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index a58a5d6..400d633 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -503,3 +503,14 @@ releases: - 201.yaml - 202.yaml release_date: '2024-05-01' + 2.2.1: + changes: + bugfixes: + - 'JDBC provider: fix clause in argument validation `#204 `_ + + ' + release_summary: Internal release, documentation or test changes only. + fragments: + - 204.yaml + - v2.2.1-devel_summary.yaml + release_date: '2024-05-02' From 1d6a6eb7ee2449f41528d30d2ff4eed0c7ae98d2 Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Thu, 2 May 2024 17:00:01 +0000 Subject: [PATCH 005/135] Bump version to 2.2.2 --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index d121f3c..ae08f9b 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "2.2.1" +version: "2.2.2" readme: README.md authors: - Romain Pelisse From b14d75dfab246ed38f97158c98aeb014f691a6e3 Mon Sep 17 00:00:00 2001 From: Deven Phillips Date: Thu, 2 May 2024 14:33:36 -0400 Subject: [PATCH 006/135] jdbc_download and validate_certs params update - Added jdbc_download customization to both keycloak releases - Added option to allow invalid certificates to download JDBC drivers --- github.json | 0 roles/keycloak/meta/argument_specs.yml | 13 +++++++++++++ roles/keycloak/tasks/jdbc_driver.yml | 9 +++++++++ roles/keycloak_quarkus/meta/argument_specs.yml | 4 ++++ roles/keycloak_quarkus/tasks/jdbc_driver.yml | 9 +++++---- 5 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 github.json diff --git a/github.json b/github.json new file mode 100644 index 0000000..e69de29 diff --git a/roles/keycloak/meta/argument_specs.yml b/roles/keycloak/meta/argument_specs.yml index 7ba6509..eab89cf 100644 --- a/roles/keycloak/meta/argument_specs.yml +++ b/roles/keycloak/meta/argument_specs.yml @@ -316,6 +316,19 @@ argument_specs: default: '/var/log/keycloak' type: "str" description: "Set the destination of the keycloak log folder link" + keycloak_jdbc_download_url: + description: "Override the default Maven Central download URL for the JDBC driver" + type: "str" + keycloak_jdbc_download_user: + description: "Set a username with which to authenticate when downloading JDBC drivers from an alternative location" + type: "str" + keycloak_jdbc_download_pass: + description: "Set a password with which to authenticate when downloading JDBC drivers from an alternative location (requires keycloak_jdbc_download_user)" + type: "str" + keycloak_jdbc_download_validate_certs: + default: true + description: "Allow the option to ignore invalid certificates when downloading JDBC drivers from a custom URL" + type: "bool" downstream: options: sso_version: diff --git a/roles/keycloak/tasks/jdbc_driver.yml b/roles/keycloak/tasks/jdbc_driver.yml index 1b0a1ec..a7cd8ab 100644 --- a/roles/keycloak/tasks/jdbc_driver.yml +++ b/roles/keycloak/tasks/jdbc_driver.yml @@ -16,6 +16,12 @@ become: true when: - not dest_path.stat.exists +- name: "Verify valid parameters for download credentials when specified" + ansible.builtin.fail: + msg: >- + When JDBC driver download credentials are set, both the username and the password MUST be set + when: + - (keycloak_jdbc_download_user is undefined and keycloak_jdbc_download_pass is not undefined) or (keycloak_jdbc_download_pass is undefined and keycloak_jdbc_download_user is not undefined) - name: "Retrieve JDBC Driver from {{ keycloak_jdbc[keycloak_jdbc_engine].driver_jar_url }}" ansible.builtin.get_url: @@ -23,6 +29,9 @@ dest: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_dir }}/{{ keycloak_jdbc[keycloak_jdbc_engine].driver_jar_filename }}" group: "{{ keycloak_service_group }}" owner: "{{ keycloak_service_user }}" + url_username: "{{ keycloak_jdbc_download_user | default(omit) }}" + url_password: "{{ keycloak_jdbc_download_pass | default(omit) }}" + validate_certs: "{{ keycloak_jdbc_download_validate_certs | default(omit) }}" mode: 0640 become: true diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 0669dc5..538e0ab 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -385,6 +385,10 @@ argument_specs: keycloak_quarkus_jdbc_download_pass: description: "Set a password with which to authenticate when downloading JDBC drivers from an alternative location (requires keycloak_quarkus_jdbc_download_user)" type: "str" + keycloak_quarkus_jdbc_download_validate_certs: + default: true + description: "Allow the option to ignore invalid certificates when downloading JDBC drivers from a custom URL" + type: "bool" downstream: options: rhbk_version: diff --git a/roles/keycloak_quarkus/tasks/jdbc_driver.yml b/roles/keycloak_quarkus/tasks/jdbc_driver.yml index dc84880..52298aa 100644 --- a/roles/keycloak_quarkus/tasks/jdbc_driver.yml +++ b/roles/keycloak_quarkus/tasks/jdbc_driver.yml @@ -4,15 +4,16 @@ msg: >- When JDBC driver download credentials are set, both the username and the password MUST be set when: - - (keycloak_jdbc_download_user is undefined and keycloak_jdbc_download_pass is not undefined) or (keycloak_jdbc_download_pass is undefined and keycloak_jdbc_download_user is not undefined) + - (keycloak_quarkus_jdbc_download_user is undefined and keycloak_quarkus_jdbc_download_pass is not undefined) or (keycloak_quarkus_jdbc_download_pass is undefined and keycloak_quarkus_jdbc_download_user is not undefined) - name: "Retrieve JDBC Driver from {{ keycloak_jdbc_download_user | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url) }}" ansible.builtin.get_url: - url: "{{ keycloak_jdbc_download_url | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url) }}" + url: "{{ keycloak_quarkus_jdbc_download_url | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url) }}" dest: "{{ keycloak.home }}/providers" owner: "{{ keycloak.service_user }}" group: "{{ keycloak.service_group }}" - url_username: "{{ keycloak_jdbc_download_user | default(omit) }}" - url_password: "{{ keycloak_jdbc_download_pass | default(omit) }}" + url_username: "{{ keycloak_quarkus_jdbc_download_user | default(omit) }}" + url_password: "{{ keycloak_quarkus_jdbc_download_pass | default(omit) }}" + validate_certs: "{{ keycloak_quarkus_jdbc_download_validate_certs | default(omit) }}" mode: '0640' become: true notify: From feec4d9f8b866150f759ae7eb9c9ace87df2ab4a Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Fri, 3 May 2024 13:03:18 +0200 Subject: [PATCH 007/135] controller priv escalation --- roles/keycloak_quarkus/tasks/install.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index d95887f..7155c3a 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -57,6 +57,7 @@ path: "{{ lookup('env', 'PWD') }}" register: local_path delegate_to: localhost + run_once: true become: false - name: Download keycloak archive @@ -108,15 +109,20 @@ client_secret: "{{ rhn_password }}" product_id: "{{ (rhn_filtered_products | first).id }}" dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" + mode: '0640' + owner: "{{ lookup('env', 'USER') | default(omit) }}" no_log: "{{ omit_rhn_output | default(true) }}" delegate_to: localhost run_once: true + become: false - name: Check downloaded archive ansible.builtin.stat: path: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" register: local_archive_path delegate_to: localhost + become: false + run_once: true ## copy and unpack - name: Copy archive to target nodes From 9bc1ae69e9a8af0c720b910197a818f365c2e84f Mon Sep 17 00:00:00 2001 From: Footur <3769085+Footur@users.noreply.github.com> Date: Fri, 3 May 2024 14:34:57 +0000 Subject: [PATCH 008/135] Enable copying of key material This commit updates the configuration to use the standard Red Hat Enterprise Linux (RHEL) default path for TLS certificates, which is /etc/pki/tls. Also, it copies the private key and certificate to the target host. --- roles/keycloak_quarkus/README.md | 8 ++++-- roles/keycloak_quarkus/defaults/main.yml | 8 ++++-- .../keycloak_quarkus/meta/argument_specs.yml | 20 ++++++++++++-- roles/keycloak_quarkus/tasks/install.yml | 26 +++++++++++++++++++ 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index ed44e21..17f2bc8 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -44,8 +44,12 @@ Role Defaults |`keycloak_quarkus_http_relative_path` | Set the path relative to / for serving resources. The path must start with a / | `/` | |`keycloak_quarkus_http_enabled`| Enable listener on HTTP port | `True` | |`keycloak_quarkus_https_key_file_enabled`| Enable listener on HTTPS port | `False` | -|`keycloak_quarkus_key_file`| The file path to a private key in PEM format | `{{ keycloak.home }}/conf/server.key.pem` | -|`keycloak_quarkus_cert_file`| The file path to a server certificate or certificate chain in PEM format | `{{ keycloak.home }}/conf/server.crt.pem` | +|`keycloak_quarkus_key_file_copy_enabled`| Enable copy of key file to target host | `False` | +|`keycloak_quarkus_key_file_src`| Set the source file path | `""` | +|`keycloak_quarkus_key_file`| The file path to a private key in PEM format | `/etc/pki/tls/private/server.key.pem` | +|`keycloak_quarkus_cert_file_copy_enabled`| Enable copy of cert file to target host | `False`| +|`keycloak_quarkus_cert_file_src`| Set the source file path | `""` | +|`keycloak_quarkus_cert_file`| The file path to a server certificate or certificate chain in PEM format | `/etc/pki/tls/certs/server.crt.pem` | |`keycloak_quarkus_https_key_store_enabled`| Enable configuration of HTTPS via a key store | `False` | |`keycloak_quarkus_key_store_file`| Deprecated, use `keycloak_quarkus_https_key_store_file` instead. || |`keycloak_quarkus_key_store_password`| Deprecated, use `keycloak_quarkus_https_key_store_password` instead.|| diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 771dc85..fcd02a5 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -47,8 +47,12 @@ keycloak_quarkus_java_opts: "{{ keycloak_quarkus_java_heap_opts + ' ' + keycloak ### TLS/HTTPS configuration keycloak_quarkus_https_key_file_enabled: false -keycloak_quarkus_key_file: "{{ keycloak.home }}/conf/server.key.pem" -keycloak_quarkus_cert_file: "{{ keycloak.home }}/conf/server.crt.pem" +keycloak_quarkus_key_file_copy_enabled: false +keycloak_quarkus_key_file_src: "" +keycloak_quarkus_key_file: "/etc/pki/tls/private/server.key.pem" +keycloak_quarkus_cert_file_copy_enabled: false +keycloak_quarkus_cert_file_src: "" +keycloak_quarkus_cert_file: "/etc/pki/tls/certs/server.crt.pem" #### key store configuration keycloak_quarkus_https_key_store_enabled: false keycloak_quarkus_https_key_store_file: "{{ keycloak.home }}/conf/key_store.p12" diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 538e0ab..768b3e9 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -108,12 +108,28 @@ argument_specs: default: false description: "Enable configuration of HTTPS via files in PEM format" type: "bool" + keycloak_quarkus_key_file_copy_enabled: + default: false + description: "Enable copy of key file to target host" + type: "bool" + keycloak_quarkus_key_file_src: + default: "" + description: "Set the source file path" + type: "str" keycloak_quarkus_key_file: - default: "{{ keycloak.home }}/conf/server.key.pem" + default: "/etc/pki/tls/private/server.key.pem" description: "The file path to a private key in PEM format" type: "str" + keycloak_quarkus_cert_file_copy_enabled: + default: false + description: "Enable copy of cert file to target host" + type: "bool" + keycloak_quarkus_cert_file_src: + default: "" + description: "Set the source file path" + type: "str" keycloak_quarkus_cert_file: - default: "{{ keycloak.home }}/conf/server.crt.pem" + default: "/etc/pki/tls/certs/server.crt.pem" description: "The file path to a server certificate or certificate chain in PEM format" type: "str" keycloak_quarkus_https_key_store_enabled: diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index d95887f..b4b566a 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -159,6 +159,32 @@ when: - (not new_version_downloaded.changed) and path_to_workdir.stat.exists +- name: "Copy private key to target" + ansible.builtin.copy: + src: "{{ keycloak_quarkus_key_file_src }}" + dest: "{{ keycloak_quarkus_key_file }}" + owner: "{{ keycloak.service_user }}" + group: "{{ keycloak.service_group }}" + mode: 0640 + become: true + when: + - keycloak_quarkus_https_key_file_enabled is defined and keycloak_quarkus_https_key_file_enabled + - keycloak_quarkus_key_file_copy_enabled is defined and keycloak_quarkus_key_file_copy_enabled + - keycloak_quarkus_key_file_src | length > 0 + +- name: "Copy certificate to target" + ansible.builtin.copy: + src: "{{ keycloak_quarkus_cert_file_src }}" + dest: "{{ keycloak_quarkus_cert_file }}" + owner: "{{ keycloak.service_user }}" + group: "{{ keycloak.service_group }}" + mode: 0644 + become: true + when: + - keycloak_quarkus_https_key_file_enabled is defined and keycloak_quarkus_https_key_file_enabled + - keycloak_quarkus_cert_file_copy_enabled is defined and keycloak_quarkus_cert_file_copy_enabled + - keycloak_quarkus_cert_file_src | length > 0 + - name: "Install {{ keycloak_quarkus_jdbc_engine }} JDBC driver" ansible.builtin.include_tasks: jdbc_driver.yml when: From 7141e1c9b2d53469ddfebb5cf24ef6a740bca64c Mon Sep 17 00:00:00 2001 From: Footur <3769085+Footur@users.noreply.github.com> Date: Sun, 5 May 2024 12:08:14 +0200 Subject: [PATCH 009/135] Test: Installation of key material via Ansible role --- molecule/quarkus/converge.yml | 8 +++++--- molecule/quarkus/prepare.yml | 12 ++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index 0480f9a..5971a93 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -9,11 +9,13 @@ keycloak_quarkus_log: file keycloak_quarkus_log_level: debug keycloak_quarkus_https_key_file_enabled: true - keycloak_quarkus_key_file: "/opt/keycloak/certs/key.pem" - keycloak_quarkus_cert_file: "/opt/keycloak/certs/cert.pem" + keycloak_quarkus_key_file_copy_enabled: true + keycloak_quarkus_key_file_src: key.pem + keycloak_quarkus_cert_file_copy_enabled: true + keycloak_quarkus_cert_file_src: cert.pem keycloak_quarkus_log_target: /tmp/keycloak keycloak_quarkus_ks_vault_enabled: true - keycloak_quarkus_ks_vault_file: "/opt/keycloak/certs/keystore.p12" + keycloak_quarkus_ks_vault_file: "/opt/keycloak/vault/keystore.p12" keycloak_quarkus_ks_vault_pass: keystorepassword keycloak_quarkus_systemd_wait_for_port: true keycloak_quarkus_systemd_wait_for_timeout: 20 diff --git a/molecule/quarkus/prepare.yml b/molecule/quarkus/prepare.yml index 1efdb15..459bafa 100644 --- a/molecule/quarkus/prepare.yml +++ b/molecule/quarkus/prepare.yml @@ -14,11 +14,11 @@ delegate_to: localhost changed_when: False - - name: Create conf directory # risky-file-permissions in test user account does not exist yet + - name: Create vault directory become: true ansible.builtin.file: state: directory - path: "/opt/keycloak/certs/" + path: "/opt/keycloak/vault" mode: 0755 - name: Make sure a jre is available (for keytool to prepare keystore) @@ -39,10 +39,6 @@ - name: Copy certificates and vault become: true ansible.builtin.copy: - src: "{{ item }}" - dest: "/opt/keycloak/certs/{{ item }}" + src: keystore.p12 + dest: /opt/keycloak/vault/keystore.p12 mode: 0444 - loop: - - cert.pem - - key.pem - - keystore.p12 From 320a5f0d9a171aeb9dbe4bf6914dcedd42596d07 Mon Sep 17 00:00:00 2001 From: Footur <3769085+Footur@users.noreply.github.com> Date: Sun, 5 May 2024 11:58:19 +0000 Subject: [PATCH 010/135] Copy the TLS private key from memory This change should avoid storing plain private keys on disk due to security risks. It also makes it easier to encrypt the data with SOPS. --- molecule/quarkus/converge.yml | 2 +- roles/keycloak_quarkus/README.md | 2 +- roles/keycloak_quarkus/defaults/main.yml | 2 +- roles/keycloak_quarkus/meta/argument_specs.yml | 4 ++-- roles/keycloak_quarkus/tasks/install.yml | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index 5971a93..9e74aa6 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -10,7 +10,7 @@ keycloak_quarkus_log_level: debug keycloak_quarkus_https_key_file_enabled: true keycloak_quarkus_key_file_copy_enabled: true - keycloak_quarkus_key_file_src: key.pem + keycloak_quarkus_key_content: "{{ lookup('file', 'key.pem') }}" keycloak_quarkus_cert_file_copy_enabled: true keycloak_quarkus_cert_file_src: cert.pem keycloak_quarkus_log_target: /tmp/keycloak diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 17f2bc8..ccb9e75 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -45,7 +45,7 @@ Role Defaults |`keycloak_quarkus_http_enabled`| Enable listener on HTTP port | `True` | |`keycloak_quarkus_https_key_file_enabled`| Enable listener on HTTPS port | `False` | |`keycloak_quarkus_key_file_copy_enabled`| Enable copy of key file to target host | `False` | -|`keycloak_quarkus_key_file_src`| Set the source file path | `""` | +|`keycloak_quarkus_key_content`| Content of the TLS private key. Use `"{{ lookup('file', 'server.key.pem') }}"` to lookup a file. | `""` | |`keycloak_quarkus_key_file`| The file path to a private key in PEM format | `/etc/pki/tls/private/server.key.pem` | |`keycloak_quarkus_cert_file_copy_enabled`| Enable copy of cert file to target host | `False`| |`keycloak_quarkus_cert_file_src`| Set the source file path | `""` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index fcd02a5..a54e6c7 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -48,7 +48,7 @@ keycloak_quarkus_java_opts: "{{ keycloak_quarkus_java_heap_opts + ' ' + keycloak ### TLS/HTTPS configuration keycloak_quarkus_https_key_file_enabled: false keycloak_quarkus_key_file_copy_enabled: false -keycloak_quarkus_key_file_src: "" +keycloak_quarkus_key_content: "" keycloak_quarkus_key_file: "/etc/pki/tls/private/server.key.pem" keycloak_quarkus_cert_file_copy_enabled: false keycloak_quarkus_cert_file_src: "" diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 768b3e9..094a46b 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -112,9 +112,9 @@ argument_specs: default: false description: "Enable copy of key file to target host" type: "bool" - keycloak_quarkus_key_file_src: + keycloak_quarkus_key_content: default: "" - description: "Set the source file path" + description: "Content of the TLS private key" type: "str" keycloak_quarkus_key_file: default: "/etc/pki/tls/private/server.key.pem" diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index b4b566a..5a385e2 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -161,7 +161,7 @@ - name: "Copy private key to target" ansible.builtin.copy: - src: "{{ keycloak_quarkus_key_file_src }}" + content: "{{ keycloak_quarkus_key_content }}" dest: "{{ keycloak_quarkus_key_file }}" owner: "{{ keycloak.service_user }}" group: "{{ keycloak.service_group }}" @@ -170,7 +170,7 @@ when: - keycloak_quarkus_https_key_file_enabled is defined and keycloak_quarkus_https_key_file_enabled - keycloak_quarkus_key_file_copy_enabled is defined and keycloak_quarkus_key_file_copy_enabled - - keycloak_quarkus_key_file_src | length > 0 + - keycloak_quarkus_key_content | length > 0 - name: "Copy certificate to target" ansible.builtin.copy: From b427cb8a24624bf83d95c1a417bb20691f7df7c9 Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Mon, 6 May 2024 08:11:11 +0000 Subject: [PATCH 011/135] Update changelog for release 2.2.2 Signed-off-by: ansible-middleware-core --- CHANGELOG.rst | 14 ++++++++++++++ changelogs/changelog.yaml | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 145a090..3d0f005 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,20 @@ middleware\_automation.keycloak Release Notes This changelog describes changes after version 0.2.6. +v2.2.2 +====== + +Minor Changes +------------- + +- Copying of key material for TLS configuration `#210 `_ +- Validate certs parameter for JDBC driver downloads `#207 `_ + +Bugfixes +-------- + +- Turn off controller privilege escalation `#209 `_ + v2.2.1 ====== diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 400d633..fe5c499 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -514,3 +514,21 @@ releases: - 204.yaml - v2.2.1-devel_summary.yaml release_date: '2024-05-02' + 2.2.2: + changes: + bugfixes: + - 'Turn off controller privilege escalation `#209 `_ + + ' + minor_changes: + - 'Copying of key material for TLS configuration `#210 `_ + + ' + - 'Validate certs parameter for JDBC driver downloads `#207 `_ + + ' + fragments: + - 207.yaml + - 209.yaml + - 210.yaml + release_date: '2024-05-06' From 4da0e83ae92ff21c95c5094158c469f9d23b3f2f Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Mon, 6 May 2024 08:11:28 +0000 Subject: [PATCH 012/135] Bump version to 2.2.3 --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index ae08f9b..1e6fd20 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "2.2.2" +version: "2.2.3" readme: README.md authors: - Romain Pelisse From 2a7395c444df8fcd6af8aa1edf851de5e18f50b1 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Mon, 6 May 2024 11:20:00 +0200 Subject: [PATCH 013/135] downstream: update default to rhbk 24.0.3 --- galaxy.yml | 2 +- roles/keycloak_quarkus/meta/argument_specs.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/galaxy.yml b/galaxy.yml index 1e6fd20..ae08f9b 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "2.2.3" +version: "2.2.2" readme: README.md authors: - Romain Pelisse diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 094a46b..5fccedc 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -408,7 +408,7 @@ argument_specs: downstream: options: rhbk_version: - default: "22.0.10" + default: "24.0.3" description: "Red Hat Build of Keycloak version" type: "str" rhbk_archive: From 70834ccf133e4c73375daf1ed6c39cf1fbf116d0 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Mon, 6 May 2024 12:03:44 +0200 Subject: [PATCH 014/135] downstream: remove problematic owner of downloaded zipfile --- roles/keycloak_quarkus/tasks/install.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index bbface7..732e5b1 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -110,7 +110,6 @@ product_id: "{{ (rhn_filtered_products | first).id }}" dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" mode: '0640' - owner: "{{ lookup('env', 'USER') | default(omit) }}" no_log: "{{ omit_rhn_output | default(true) }}" delegate_to: localhost run_once: true From a45b18dc85ecf57802d450de9b4be83dfdc84319 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Mon, 6 May 2024 13:02:51 +0200 Subject: [PATCH 015/135] kc.sh build uses configured jdk --- roles/keycloak_quarkus/tasks/rebuild_config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/roles/keycloak_quarkus/tasks/rebuild_config.yml b/roles/keycloak_quarkus/tasks/rebuild_config.yml index 5a715c6..5d2247d 100644 --- a/roles/keycloak_quarkus/tasks/rebuild_config.yml +++ b/roles/keycloak_quarkus/tasks/rebuild_config.yml @@ -3,5 +3,8 @@ - name: "Rebuild {{ keycloak.service_name }} config" ansible.builtin.shell: | {{ keycloak.home }}/bin/kc.sh build + environment: + PATH: "{{ keycloak_quarkus_java_home | default(keycloak_quarkus_pkg_java_home, true) }}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + JAVA_HOME: "{{ keycloak_quarkus_java_home | default(keycloak_quarkus_pkg_java_home, true) }}" become: true changed_when: true From b497e946cce42b49311d33fa458def1c63642e1f Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 7 May 2024 09:47:12 +0200 Subject: [PATCH 016/135] Bump tp 2.2.3 --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index ae08f9b..1e6fd20 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "2.2.2" +version: "2.2.3" readme: README.md authors: - Romain Pelisse From 1115ee409a706e2453d5c9b321630ba08208e8e7 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 7 May 2024 10:18:43 +0200 Subject: [PATCH 017/135] Linter warnings fix pass --- .ansible-lint | 2 +- changelogs/config.yaml | 32 +++++++++---------- roles/keycloak/meta/argument_specs.yml | 10 ++++-- roles/keycloak/tasks/fastpackages.yml | 7 ++-- roles/keycloak/tasks/install.yml | 24 +++++++------- roles/keycloak/tasks/jdbc_driver.yml | 11 ++++--- roles/keycloak/tasks/prereqs.yml | 9 ++++-- roles/keycloak/tasks/restart_keycloak.yml | 2 +- roles/keycloak/tasks/rhsso_cli.yml | 2 +- roles/keycloak/tasks/rhsso_patch.yml | 12 +++---- roles/keycloak/tasks/systemd.yml | 6 ++-- roles/keycloak/vars/debian.yml | 5 +-- roles/keycloak/vars/main.yml | 14 +++++--- roles/keycloak_quarkus/handlers/main.yml | 2 +- .../keycloak_quarkus/meta/argument_specs.yml | 8 +++-- roles/keycloak_quarkus/tasks/install.yml | 4 +-- roles/keycloak_quarkus/tasks/jdbc_driver.yml | 8 +++-- 17 files changed, 90 insertions(+), 68 deletions(-) diff --git a/.ansible-lint b/.ansible-lint index 92e5eaf..9c2702e 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -28,7 +28,6 @@ warn_list: - name[casing] - fqcn[action] - schema[meta] - - var-naming[no-role-prefix] - key-order[task] - blocked_modules @@ -36,6 +35,7 @@ skip_list: - vars_should_not_be_used - file_is_small_enough - name[template] + - var-naming[no-role-prefix] use_default_rules: true parseable: true diff --git a/changelogs/config.yaml b/changelogs/config.yaml index 374ae65..3c7fb7e 100644 --- a/changelogs/config.yaml +++ b/changelogs/config.yaml @@ -11,22 +11,22 @@ notesdir: fragments prelude_section_name: release_summary prelude_section_title: Release Summary sections: -- - major_changes - - Major Changes -- - minor_changes - - Minor Changes -- - breaking_changes - - Breaking Changes / Porting Guide -- - deprecated_features - - Deprecated Features -- - removed_features - - Removed Features -- - security_fixes - - Security Fixes -- - bugfixes - - Bugfixes -- - known_issues - - Known Issues + - - major_changes + - Major Changes + - - minor_changes + - Minor Changes + - - breaking_changes + - Breaking Changes / Porting Guide + - - deprecated_features + - Deprecated Features + - - removed_features + - Removed Features + - - security_fixes + - Security Fixes + - - bugfixes + - Bugfixes + - - known_issues + - Known Issues title: middleware_automation.keycloak trivial_section_name: trivial use_fqcn: true diff --git a/roles/keycloak/meta/argument_specs.yml b/roles/keycloak/meta/argument_specs.yml index eab89cf..5f6052d 100644 --- a/roles/keycloak/meta/argument_specs.yml +++ b/roles/keycloak/meta/argument_specs.yml @@ -86,7 +86,9 @@ argument_specs: type: "str" keycloak_features: default: "[]" - description: "List of `name`/`status` pairs of features (also known as profiles on RH-SSO) to `enable` or `disable`, example: `[ { name: 'docker', status: 'enabled' } ]`" + description: > + List of `name`/`status` pairs of features (also known as profiles on RH-SSO) to `enable` or `disable`, + example: `[ { name: 'docker', status: 'enabled' } ]` type: "list" keycloak_bind_address: default: "0.0.0.0" @@ -310,7 +312,8 @@ argument_specs: type: "str" keycloak_jgroups_subnet: required: false - description: "Override the subnet match for jgroups cluster formation; if not defined, it will be inferred from local machine route configuration" + description: > + Override the subnet match for jgroups cluster formation; if not defined, it will be inferred from local machine route configuration type: "str" keycloak_log_target: default: '/var/log/keycloak' @@ -323,7 +326,8 @@ argument_specs: description: "Set a username with which to authenticate when downloading JDBC drivers from an alternative location" type: "str" keycloak_jdbc_download_pass: - description: "Set a password with which to authenticate when downloading JDBC drivers from an alternative location (requires keycloak_jdbc_download_user)" + description: > + Set a password with which to authenticate when downloading JDBC drivers from an alternative location (requires keycloak_jdbc_download_user) type: "str" keycloak_jdbc_download_validate_certs: default: true diff --git a/roles/keycloak/tasks/fastpackages.yml b/roles/keycloak/tasks/fastpackages.yml index 3b557ef..be34c72 100644 --- a/roles/keycloak/tasks/fastpackages.yml +++ b/roles/keycloak/tasks/fastpackages.yml @@ -8,7 +8,8 @@ - name: "Add missing packages to the yum install list" ansible.builtin.set_fact: - packages_to_install: "{{ packages_to_install | default([]) + rpm_info.stdout_lines | map('regex_findall', 'package (.+) is not installed$') | default([]) | flatten }}" + packages_to_install: "{{ packages_to_install | default([]) + rpm_info.stdout_lines | \ + map('regex_findall', 'package (.+) is not installed$') | default([]) | flatten }}" when: ansible_facts.os_family == "RedHat" - name: "Install packages: {{ packages_to_install }}" @@ -17,8 +18,8 @@ name: "{{ packages_to_install }}" state: present when: - - packages_to_install | default([]) | length > 0 - - ansible_facts.os_family == "RedHat" + - packages_to_install | default([]) | length > 0 + - ansible_facts.os_family == "RedHat" - name: "Install packages: {{ packages_list }}" become: true diff --git a/roles/keycloak/tasks/install.yml b/roles/keycloak/tasks/install.yml index 67b98cd..b620b03 100644 --- a/roles/keycloak/tasks/install.yml +++ b/roles/keycloak/tasks/install.yml @@ -41,8 +41,8 @@ ansible.builtin.user: name: "{{ keycloak_service_user }}" home: /opt/keycloak - system: yes - create_home: no + system: true + create_home: false - name: "Create install location for {{ keycloak.service_name }}" become: true @@ -51,7 +51,7 @@ state: directory owner: "{{ keycloak_service_user }}" group: "{{ keycloak_service_group }}" - mode: 0750 + mode: '0750' - name: Create pidfile folder become: true @@ -60,7 +60,7 @@ state: directory owner: "{{ keycloak_service_user if keycloak_service_runas else omit }}" group: "{{ keycloak_service_group if keycloak_service_runas else omit }}" - mode: 0750 + mode: '0750' ## check remote archive - name: Set download archive path @@ -84,7 +84,7 @@ ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user url: "{{ keycloak_download_url }}" dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" - mode: 0644 + mode: '0644' delegate_to: localhost run_once: true when: @@ -136,7 +136,7 @@ ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user url: "{{ keycloak_rhsso_download_url }}" dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" - mode: 0644 + mode: '0644' delegate_to: localhost run_once: true when: @@ -160,7 +160,7 @@ dest: "{{ archive }}" owner: "{{ keycloak_service_user }}" group: "{{ keycloak_service_group }}" - mode: 0640 + mode: '0640' register: new_version_downloaded when: - not archive_path.stat.exists @@ -221,7 +221,7 @@ dest: "{{ keycloak_config_path_to_standalone_xml }}" owner: "{{ keycloak_service_user }}" group: "{{ keycloak_service_group }}" - mode: 0640 + mode: '0640' notify: - restart keycloak when: keycloak_config_override_template | length > 0 @@ -233,7 +233,7 @@ dest: "{{ keycloak_config_path_to_standalone_xml }}" owner: "{{ keycloak_service_user }}" group: "{{ keycloak_service_group }}" - mode: 0640 + mode: '0640' notify: - restart keycloak when: @@ -261,7 +261,7 @@ dest: "{{ keycloak_config_path_to_standalone_xml }}" owner: "{{ keycloak_service_user }}" group: "{{ keycloak_service_group }}" - mode: 0640 + mode: '0640' notify: - restart keycloak when: @@ -276,7 +276,7 @@ dest: "{{ keycloak_config_path_to_standalone_xml }}" owner: "{{ keycloak_service_user }}" group: "{{ keycloak_service_group }}" - mode: 0640 + mode: '0640' notify: - restart keycloak when: @@ -291,7 +291,7 @@ dest: "{{ keycloak_config_path_to_properties }}" owner: "{{ keycloak_service_user }}" group: "{{ keycloak_service_group }}" - mode: 0640 + mode: '0640' notify: - restart keycloak when: keycloak_features | length > 0 diff --git a/roles/keycloak/tasks/jdbc_driver.yml b/roles/keycloak/tasks/jdbc_driver.yml index a7cd8ab..bec80e3 100644 --- a/roles/keycloak/tasks/jdbc_driver.yml +++ b/roles/keycloak/tasks/jdbc_driver.yml @@ -12,7 +12,7 @@ recurse: true owner: "{{ keycloak_service_user }}" group: "{{ keycloak_service_group }}" - mode: 0750 + mode: '0750' become: true when: - not dest_path.stat.exists @@ -20,8 +20,9 @@ ansible.builtin.fail: msg: >- When JDBC driver download credentials are set, both the username and the password MUST be set - when: - - (keycloak_jdbc_download_user is undefined and keycloak_jdbc_download_pass is not undefined) or (keycloak_jdbc_download_pass is undefined and keycloak_jdbc_download_user is not undefined) + when: > + (keycloak_jdbc_download_user is undefined and keycloak_jdbc_download_pass is not undefined) or + (keycloak_jdbc_download_pass is undefined and keycloak_jdbc_download_user is not undefined) - name: "Retrieve JDBC Driver from {{ keycloak_jdbc[keycloak_jdbc_engine].driver_jar_url }}" ansible.builtin.get_url: @@ -32,7 +33,7 @@ url_username: "{{ keycloak_jdbc_download_user | default(omit) }}" url_password: "{{ keycloak_jdbc_download_pass | default(omit) }}" validate_certs: "{{ keycloak_jdbc_download_validate_certs | default(omit) }}" - mode: 0640 + mode: '0640' become: true - name: "Deploy module.xml for JDBC Driver" @@ -41,5 +42,5 @@ dest: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_dir }}/module.xml" group: "{{ keycloak_service_group }}" owner: "{{ keycloak_service_user }}" - mode: 0640 + mode: '0640' become: true diff --git a/roles/keycloak/tasks/prereqs.yml b/roles/keycloak/tasks/prereqs.yml index c92bb1c..d97390c 100644 --- a/roles/keycloak/tasks/prereqs.yml +++ b/roles/keycloak/tasks/prereqs.yml @@ -4,13 +4,16 @@ that: - keycloak_admin_password | length > 12 quiet: true - fail_msg: "The console administrator password is empty or invalid. Please set the keycloak_admin_password variable to a 12+ char long string" + fail_msg: > + The console administrator password is empty or invalid. Please set the keycloak_admin_password variable to a 12+ char long string success_msg: "{{ 'Console administrator password OK' }}" - name: Validate configuration ansible.builtin.assert: - that: - - (keycloak_ha_enabled and keycloak_db_enabled) or (not keycloak_ha_enabled and keycloak_db_enabled) or (not keycloak_ha_enabled and not keycloak_db_enabled) + that: > + (keycloak_ha_enabled and keycloak_db_enabled) or + (not keycloak_ha_enabled and keycloak_db_enabled) or + (not keycloak_ha_enabled and not keycloak_db_enabled) quiet: true fail_msg: "Cannot install HA setup without a backend database service. Check keycloak_ha_enabled and keycloak_db_enabled" success_msg: "{{ 'Configuring HA' if keycloak_ha_enabled else 'Configuring standalone' }}" diff --git a/roles/keycloak/tasks/restart_keycloak.yml b/roles/keycloak/tasks/restart_keycloak.yml index bae91cd..7284bd0 100644 --- a/roles/keycloak/tasks/restart_keycloak.yml +++ b/roles/keycloak/tasks/restart_keycloak.yml @@ -22,7 +22,7 @@ - name: "Restart and enable {{ keycloak.service_name }} service" ansible.builtin.systemd: name: keycloak - enabled: yes + enabled: true state: restarted become: true when: inventory_hostname != ansible_play_hosts | first diff --git a/roles/keycloak/tasks/rhsso_cli.yml b/roles/keycloak/tasks/rhsso_cli.yml index c51cdc7..fd41dd6 100644 --- a/roles/keycloak/tasks/rhsso_cli.yml +++ b/roles/keycloak/tasks/rhsso_cli.yml @@ -10,4 +10,4 @@ ansible.builtin.command: > {{ keycloak.cli_path }} --connect --command='{{ query }}' --controller={{ keycloak_host }}:{{ keycloak_management_http_port }} changed_when: false - register: cli_result \ No newline at end of file + register: cli_result diff --git a/roles/keycloak/tasks/rhsso_patch.yml b/roles/keycloak/tasks/rhsso_patch.yml index 191a3e0..e7ac3f0 100644 --- a/roles/keycloak/tasks/rhsso_patch.yml +++ b/roles/keycloak/tasks/rhsso_patch.yml @@ -45,7 +45,7 @@ - name: Determine latest version ansible.builtin.set_fact: - sso_latest_version: "{{ filtered_versions | middleware_automation.common.version_sort | last }}" + sso_latest_version: "{{ filtered_versions | middleware_automation.common.version_sort | last }}" when: sso_patch_version is not defined or sso_patch_version | length == 0 delegate_to: localhost run_once: true @@ -95,7 +95,7 @@ dest: "{{ patch_archive }}" owner: "{{ keycloak_service_user }}" group: "{{ keycloak_service_group }}" - mode: 0640 + mode: '0640' register: new_version_downloaded when: - not patch_archive_path.stat.exists @@ -135,8 +135,8 @@ - cli_result.rc == 0 args: apply: - become: true - become_user: "{{ keycloak_service_user }}" + become: true + become_user: "{{ keycloak_service_user }}" - name: "Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}" ansible.builtin.uri: @@ -152,8 +152,8 @@ query: "patch info" args: apply: - become: true - become_user: "{{ keycloak_service_user }}" + become: true + become_user: "{{ keycloak_service_user }}" - name: "Verify installed patch version" ansible.builtin.assert: diff --git a/roles/keycloak/tasks/systemd.yml b/roles/keycloak/tasks/systemd.yml index 797eb7b..1653406 100644 --- a/roles/keycloak/tasks/systemd.yml +++ b/roles/keycloak/tasks/systemd.yml @@ -6,7 +6,7 @@ dest: "{{ keycloak_dest }}/keycloak-service.sh" owner: root group: root - mode: 0755 + mode: '0755' notify: - restart keycloak @@ -17,7 +17,7 @@ dest: "{{ keycloak_sysconf_file }}" owner: root group: root - mode: 0644 + mode: '0644' notify: - restart keycloak @@ -27,7 +27,7 @@ dest: /etc/systemd/system/keycloak.service owner: root group: root - mode: 0644 + mode: '0644' become: true register: systemdunit notify: diff --git a/roles/keycloak/vars/debian.yml b/roles/keycloak/vars/debian.yml index 60cdfa8..b005b0a 100644 --- a/roles/keycloak/vars/debian.yml +++ b/roles/keycloak/vars/debian.yml @@ -6,6 +6,7 @@ keycloak_prereq_package_list: - procps - apt - tzdata -keycloak_configure_iptables: True +keycloak_configure_iptables: true keycloak_sysconf_file: /etc/default/keycloak -keycloak_pkg_java_home: "/usr/lib/jvm/java-{{ keycloak_varjvm_package | regex_search('(?!:openjdk-)[0-9.]+') }}-openjdk-{{ 'arm64' if ansible_architecture == 'aarch64' else 'amd64' }}" +keycloak_pkg_java_home: "/usr/lib/jvm/java-{{ keycloak_varjvm_package | \ + regex_search('(?!:openjdk-)[0-9.]+') }}-openjdk-{{ 'arm64' if ansible_architecture == 'aarch64' else 'amd64' }}" diff --git a/roles/keycloak/vars/main.yml b/roles/keycloak/vars/main.yml index b03a1a5..7f7dfd1 100644 --- a/roles/keycloak/vars/main.yml +++ b/roles/keycloak/vars/main.yml @@ -13,7 +13,8 @@ keycloak: service_name: "{{ keycloak_service_name }}" health_url: "{{ keycloak_management_url }}/health" cli_path: "{{ keycloak_jboss_home }}/bin/jboss-cli.sh" - config_template_source: "{{ keycloak_config_override_template if keycloak_config_override_template | length > 0 else 'standalone-ha.xml.j2' if keycloak_remote_cache_enabled else 'standalone.xml.j2' }}" + config_template_source: "{{ keycloak_config_override_template if keycloak_config_override_template | length > 0 \ + else 'standalone-ha.xml.j2' if keycloak_remote_cache_enabled else 'standalone.xml.j2' }}" features: "{{ keycloak_features }}" # database @@ -26,7 +27,8 @@ keycloak_jdbc: driver_module_dir: "{{ keycloak_jboss_home }}/modules/org/postgresql/main" driver_version: "{{ keycloak_jdbc_driver_version }}" driver_jar_filename: "postgresql-{{ keycloak_jdbc_driver_version }}.jar" - driver_jar_url: "https://repo.maven.apache.org/maven2/org/postgresql/postgresql/{{ keycloak_jdbc_driver_version }}/postgresql-{{ keycloak_jdbc_driver_version }}.jar" + driver_jar_url: > + {{ keycloak_maven_central }}org/postgresql/postgresql/{{ keycloak_jdbc_driver_version }}/postgresql-{{ keycloak_jdbc_driver_version }}.jar connection_url: "{{ keycloak_jdbc_url }}" db_user: "{{ keycloak_db_user }}" db_password: "{{ keycloak_db_pass }}" @@ -46,7 +48,8 @@ keycloak_jdbc: driver_module_dir: "{{ keycloak_jboss_home }}/modules/org/mariadb/main" driver_version: "{{ keycloak_jdbc_driver_version }}" driver_jar_filename: "mariadb-java-client-{{ keycloak_jdbc_driver_version }}.jar" - driver_jar_url: "https://repo1.maven.org/maven2/org/mariadb/jdbc/mariadb-java-client/{{ keycloak_jdbc_driver_version }}/mariadb-java-client-{{ keycloak_jdbc_driver_version }}.jar" + driver_jar_url: > + {{ keycloak_maven_central }}org/mariadb/jdbc/mariadb-java-client/{{ keycloak_jdbc_driver_version }}/mariadb-java-client-{{ keycloak_jdbc_driver_version }}.jar connection_url: "{{ keycloak_jdbc_url }}" db_user: "{{ keycloak_db_user }}" db_password: "{{ keycloak_db_pass }}" @@ -67,7 +70,8 @@ keycloak_jdbc: driver_module_dir: "{{ keycloak_jboss_home }}/modules/com/microsoft/sqlserver/main" driver_version: "{{ keycloak_jdbc_driver_version }}" driver_jar_filename: "mssql-java-client-{{ keycloak_jdbc_driver_version }}.jar" - driver_jar_url: "https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/{{ keycloak_jdbc_driver_version }}.jre11/mssql-jdbc-{{ keycloak_jdbc_driver_version }}.jre11.jar" # e.g., https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/12.2.0.jre11/mssql-jdbc-12.2.0.jre11.jar + driver_jar_url: > + {{ keycloak_maven_central }}com/microsoft/sqlserver/mssql-jdbc/{{ keycloak_jdbc_driver_version }}.jre11/mssql-jdbc-{{ keycloak_jdbc_driver_version }}.jre11.jar connection_url: "{{ keycloak_jdbc_url }}" db_user: "{{ keycloak_db_user }}" db_password: "{{ keycloak_db_pass }}" @@ -102,3 +106,5 @@ keycloak_remotecache: use_ssl: "{{ keycloak_infinispan_use_ssl }}" trust_store_path: "{{ keycloak_infinispan_trust_store_path }}" trust_store_password: "{{ keycloak_infinispan_trust_store_password }}" + +keycloak_maven_central: https://repo1.maven.org/maven2/ diff --git a/roles/keycloak_quarkus/handlers/main.yml b/roles/keycloak_quarkus/handlers/main.yml index f60e747..b95d5c3 100644 --- a/roles/keycloak_quarkus/handlers/main.yml +++ b/roles/keycloak_quarkus/handlers/main.yml @@ -12,6 +12,6 @@ - name: "Print deprecation warning" ansible.builtin.fail: msg: "Deprecation warning: you are using the deprecated variable '{{ deprecated_variable | d('NotSet') }}', check docs on how to upgrade." - ignore_errors: true failed_when: false + changed_when: true listen: "print deprecation warning" diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 5fccedc..a8b1a05 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -170,7 +170,9 @@ argument_specs: type: "str" keycloak_quarkus_config_key_store_password: default: "" - description: "Password of the configuration key store; if non-empty, `keycloak_quarkus_db_pass` will be saved to the key store at `keycloak_quarkus_config_key_store_file` (instead of being written to the configuration file in clear text" + description: > + Password of the configuration key store; if non-empty, `keycloak_quarkus_db_pass` will be saved to the key store + at `keycloak_quarkus_config_key_store_file` (instead of being written to the configuration file in clear text) type: "str" keycloak_quarkus_https_port: default: 8443 @@ -399,7 +401,9 @@ argument_specs: description: "Set a username with which to authenticate when downloading JDBC drivers from an alternative location" type: "str" keycloak_quarkus_jdbc_download_pass: - description: "Set a password with which to authenticate when downloading JDBC drivers from an alternative location (requires keycloak_quarkus_jdbc_download_user)" + description: > + Set a password with which to authenticate when downloading JDBC drivers from an alternative location + (requires `keycloak_quarkus_jdbc_download_user``) type: "str" keycloak_quarkus_jdbc_download_validate_certs: default: true diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index 732e5b1..65fd391 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -170,7 +170,7 @@ dest: "{{ keycloak_quarkus_key_file }}" owner: "{{ keycloak.service_user }}" group: "{{ keycloak.service_group }}" - mode: 0640 + mode: '0640' become: true when: - keycloak_quarkus_https_key_file_enabled is defined and keycloak_quarkus_https_key_file_enabled @@ -183,7 +183,7 @@ dest: "{{ keycloak_quarkus_cert_file }}" owner: "{{ keycloak.service_user }}" group: "{{ keycloak.service_group }}" - mode: 0644 + mode: '0644' become: true when: - keycloak_quarkus_https_key_file_enabled is defined and keycloak_quarkus_https_key_file_enabled diff --git a/roles/keycloak_quarkus/tasks/jdbc_driver.yml b/roles/keycloak_quarkus/tasks/jdbc_driver.yml index 52298aa..880a915 100644 --- a/roles/keycloak_quarkus/tasks/jdbc_driver.yml +++ b/roles/keycloak_quarkus/tasks/jdbc_driver.yml @@ -3,9 +3,11 @@ ansible.builtin.fail: msg: >- When JDBC driver download credentials are set, both the username and the password MUST be set - when: - - (keycloak_quarkus_jdbc_download_user is undefined and keycloak_quarkus_jdbc_download_pass is not undefined) or (keycloak_quarkus_jdbc_download_pass is undefined and keycloak_quarkus_jdbc_download_user is not undefined) -- name: "Retrieve JDBC Driver from {{ keycloak_jdbc_download_user | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url) }}" + when: > + (keycloak_quarkus_jdbc_download_user is undefined and keycloak_quarkus_jdbc_download_pass is not undefined) or + (keycloak_quarkus_jdbc_download_pass is undefined and keycloak_quarkus_jdbc_download_user is not undefined) + +- name: "Retrieve JDBC Driver from {{ keycloak_jdbc_download_url | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url) }}" ansible.builtin.get_url: url: "{{ keycloak_quarkus_jdbc_download_url | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url) }}" dest: "{{ keycloak.home }}/providers" From a0198238715563bfd0fe13e1b72c7fc6659a5424 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Wed, 8 May 2024 17:15:50 +0200 Subject: [PATCH 018/135] Close #214: RHBK 24.*: Update sqlserver JDBC version --- roles/keycloak_quarkus/defaults/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index a54e6c7..5a11f08 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -130,9 +130,9 @@ keycloak_quarkus_default_jdbc: version: 2.7.4 mssql: url: 'jdbc:sqlserver://localhost:1433;databaseName=keycloak;' - version: 12.2.0 - driver_jar_url: "https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/12.2.0.jre11/mssql-jdbc-12.2.0.jre11.jar" - # cf. https://access.redhat.com/documentation/en-us/red_hat_build_of_keycloak/22.0/html/server_guide/db-#db-installing-the-microsoft-sql-server-driver + version: 12.4.2 + driver_jar_url: "https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/12.4.2.jre11/mssql-jdbc-12.4.2.jre11.jar" + # cf. https://access.redhat.com/documentation/en-us/red_hat_build_of_keycloak/24.0/html/server_guide/db-#db-installing-the-microsoft-sql-server-driver ### logging configuration keycloak_quarkus_log: file keycloak_quarkus_log_level: info From 3c224176743f71af3d50377d97ca3889f7f7ddf7 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 8 May 2024 19:03:30 +0200 Subject: [PATCH 019/135] Port downstream upgrade --- molecule/quarkus_upgrade/converge.yml | 9 +++++ molecule/quarkus_upgrade/molecule.yml | 43 ++++++++++++++++++++++ molecule/quarkus_upgrade/prepare.yml | 52 +++++++++++++++++++++++++++ molecule/quarkus_upgrade/vars.yml | 14 ++++++++ molecule/quarkus_upgrade/verify.yml | 32 +++++++++++++++++ 5 files changed, 150 insertions(+) create mode 100644 molecule/quarkus_upgrade/converge.yml create mode 100644 molecule/quarkus_upgrade/molecule.yml create mode 100644 molecule/quarkus_upgrade/prepare.yml create mode 100644 molecule/quarkus_upgrade/vars.yml create mode 100644 molecule/quarkus_upgrade/verify.yml diff --git a/molecule/quarkus_upgrade/converge.yml b/molecule/quarkus_upgrade/converge.yml new file mode 100644 index 0000000..eb84589 --- /dev/null +++ b/molecule/quarkus_upgrade/converge.yml @@ -0,0 +1,9 @@ +--- +- name: Converge + hosts: all + vars_files: + - vars.yml + vars: + keycloak_quarkus_version: 24.0.3 + roles: + - role: keycloak_quarkus diff --git a/molecule/quarkus_upgrade/molecule.yml b/molecule/quarkus_upgrade/molecule.yml new file mode 100644 index 0000000..77f687f --- /dev/null +++ b/molecule/quarkus_upgrade/molecule.yml @@ -0,0 +1,43 @@ +--- +dependency: + name: galaxy + options: + requirements-file: molecule/requirements.yml +driver: + name: docker +platforms: + - name: instance + image: registry.access.redhat.com/ubi9/ubi-init:latest + command: "/usr/sbin/init" + pre_build_image: true + privileged: true + port_bindings: + - 8080:8080 + published_ports: + - 0.0.0.0:8080:8080/TCP +provisioner: + name: ansible + playbooks: + prepare: prepare.yml + converge: converge.yml + verify: verify.yml + inventory: + host_vars: + localhost: + ansible_python_interpreter: "{{ ansible_playbook_python }}" +verifier: + name: ansible +scenario: + test_sequence: + - dependency + - cleanup + - destroy + - syntax + - create + - prepare + - converge + - idempotence + - side_effect + - verify + - cleanup + - destroy diff --git a/molecule/quarkus_upgrade/prepare.yml b/molecule/quarkus_upgrade/prepare.yml new file mode 100644 index 0000000..1a0e708 --- /dev/null +++ b/molecule/quarkus_upgrade/prepare.yml @@ -0,0 +1,52 @@ +--- +- name: Prepare + hosts: all + vars_files: + - vars.yml + vars: + sudo_pkg_name: sudo + keycloak_quarkus_version: 22.0.10 + pre_tasks: + - name: Install sudo + ansible.builtin.apt: + name: + - sudo + - openjdk-17-jdk-headless + state: present + when: + - ansible_facts.os_family == 'Debian' + + - name: "Ensure common prepare phase are set." + ansible.builtin.include_tasks: ../prepare.yml + + - name: Display Ansible version + ansible.builtin.debug: + msg: "Ansible version is {{ ansible_version.full }}" + + - name: "Ensure {{ sudo_pkg_name }} is installed (if user is root)." + ansible.builtin.dnf: + name: "{{ sudo_pkg_name }}" + when: + - ansible_user_id == 'root' + + - name: Gather the package facts + ansible.builtin.package_facts: + manager: auto + + - name: "Check if {{ sudo_pkg_name }} is installed." + ansible.builtin.assert: + that: + - sudo_pkg_name in ansible_facts.packages + + - name: Create certificate request + ansible.builtin.command: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=instance' + delegate_to: localhost + changed_when: false + roles: + - role: keycloak_quarkus + post_tasks: + - name: "Delete custom fact" + ansible.builtin.file: + path: /etc/ansible/facts.d/keycloak.fact + state: absent + become: true diff --git a/molecule/quarkus_upgrade/vars.yml b/molecule/quarkus_upgrade/vars.yml new file mode 100644 index 0000000..81d56b5 --- /dev/null +++ b/molecule/quarkus_upgrade/vars.yml @@ -0,0 +1,14 @@ +--- +keycloak_quarkus_offline_install: false +keycloak_quarkus_admin_password: "remembertochangeme" +keycloak_quarkus_admin_pass: "remembertochangeme" +keycloak_quarkus_realm: TestRealm +keycloak_quarkus_host: instance +keycloak_quarkus_log: file +keycloak_quarkus_https_key_file_enabled: true +keycloak_quarkus_log_target: /tmp/keycloak +keycloak_quarkus_hostname_strict: false +keycloak_quarkus_cert_file_copy_enabled: true +keycloak_quarkus_key_file_copy_enabled: true +keycloak_quarkus_key_content: "{{ lookup('file', 'key.pem') }}" +keycloak_quarkus_cert_file_src: cert.pem diff --git a/molecule/quarkus_upgrade/verify.yml b/molecule/quarkus_upgrade/verify.yml new file mode 100644 index 0000000..def2d5d --- /dev/null +++ b/molecule/quarkus_upgrade/verify.yml @@ -0,0 +1,32 @@ +--- +- name: Verify + hosts: instance + vars: + keycloak_quarkus_admin_password: "remembertochangeme" + keycloak_quarkus_port: http://localhost:8080 + tasks: + - name: Populate service facts + ansible.builtin.service_facts: + + - name: Check if keycloak service started + ansible.builtin.assert: + that: + - ansible_facts.services["rhbk.service"]["state"] == "running" + - ansible_facts.services["rhbk.service"]["status"] == "enabled" + + - name: Verify we are running on requested jvm + ansible.builtin.shell: | + set -eo pipefail + ps -ef | grep 'etc/alternatives/.*17' | grep -v grep + changed_when: false + + - name: Verify token api call + ansible.builtin.uri: + url: "{{ keycloak_quarkus_port }}/realms/master/protocol/openid-connect/token" + method: POST + body: "client_id=admin-cli&username=admin&password={{ keycloak_quarkus_admin_password }}&grant_type=password" + validate_certs: no + register: keycloak_auth_response + until: keycloak_auth_response.status == 200 + retries: 2 + delay: 2 From 22f5ad902f1372b701ab81c43cccf6a48eb6080c Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 8 May 2024 19:05:24 +0200 Subject: [PATCH 020/135] add test to github actions --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 509bebb..d5cac41 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,4 +15,4 @@ jobs: with: fqcn: 'middleware_automation/keycloak' molecule_tests: >- - [ "default", "overridexml", "https_revproxy", "quarkus", "quarkus-devmode", "debian" ] + [ "default", "overridexml", "https_revproxy", "quarkus", "quarkus-devmode", "quarkus_upgrade", "debian" ] From 4c96cbe7f6d1aca3279f54179dd0c11b2b450e4c Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 8 May 2024 19:09:59 +0200 Subject: [PATCH 021/135] use sane version to be upgraded --- molecule/quarkus_upgrade/prepare.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/molecule/quarkus_upgrade/prepare.yml b/molecule/quarkus_upgrade/prepare.yml index 1a0e708..bebfc68 100644 --- a/molecule/quarkus_upgrade/prepare.yml +++ b/molecule/quarkus_upgrade/prepare.yml @@ -5,7 +5,7 @@ - vars.yml vars: sudo_pkg_name: sudo - keycloak_quarkus_version: 22.0.10 + keycloak_quarkus_version: 23.0.7 pre_tasks: - name: Install sudo ansible.builtin.apt: From 4bbc8e02566732f4550ff8718d753d8152ffd644 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 8 May 2024 19:14:04 +0200 Subject: [PATCH 022/135] update systemd service name in verify --- molecule/quarkus_upgrade/verify.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/molecule/quarkus_upgrade/verify.yml b/molecule/quarkus_upgrade/verify.yml index def2d5d..f5edfd0 100644 --- a/molecule/quarkus_upgrade/verify.yml +++ b/molecule/quarkus_upgrade/verify.yml @@ -11,8 +11,8 @@ - name: Check if keycloak service started ansible.builtin.assert: that: - - ansible_facts.services["rhbk.service"]["state"] == "running" - - ansible_facts.services["rhbk.service"]["status"] == "enabled" + - ansible_facts.services["keycloak.service"]["state"] == "running" + - ansible_facts.services["keycloak.service"]["status"] == "enabled" - name: Verify we are running on requested jvm ansible.builtin.shell: | From fcf629d05e22eedffb279f645382347ae3ef3a06 Mon Sep 17 00:00:00 2001 From: Footur <3769085+Footur@users.noreply.github.com> Date: Thu, 9 May 2024 07:24:47 +0000 Subject: [PATCH 023/135] Update Keycloak to version 24.0.4 --- roles/keycloak_quarkus/README.md | 2 +- roles/keycloak_quarkus/defaults/main.yml | 2 +- roles/keycloak_quarkus/meta/argument_specs.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index ccb9e75..a70deae 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -11,7 +11,7 @@ Role Defaults | Variable | Description | Default | |:---------|:------------|:--------| -|`keycloak_quarkus_version`| keycloak.org package version | `24.0.3` | +|`keycloak_quarkus_version`| keycloak.org package version | `24.0.4` | |`keycloak_quarkus_offline_install` | Perform an offline install | `False`| |`keycloak_quarkus_dest`| Installation root path | `/opt/keycloak` | |`keycloak_quarkus_download_url` | Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 5a11f08..242482d 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -1,6 +1,6 @@ --- ### Configuration specific to keycloak -keycloak_quarkus_version: 24.0.3 +keycloak_quarkus_version: 24.0.4 keycloak_quarkus_archive: "keycloak-{{ keycloak_quarkus_version }}.zip" keycloak_quarkus_download_url: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}" keycloak_quarkus_installdir: "{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_quarkus_version }}" diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index a8b1a05..56a99ce 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -2,7 +2,7 @@ argument_specs: main: options: keycloak_quarkus_version: - default: "24.0.3" + default: "24.0.4" description: "keycloak.org package version" type: "str" keycloak_quarkus_archive: @@ -412,7 +412,7 @@ argument_specs: downstream: options: rhbk_version: - default: "24.0.3" + default: "24.0.4" description: "Red Hat Build of Keycloak version" type: "str" rhbk_archive: From 8f14be37d7ad512ea393a81ecca6db819eb79d20 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Fri, 10 May 2024 10:17:37 +0200 Subject: [PATCH 024/135] add functionality --- roles/keycloak_quarkus/README.md | 10 ++++++++++ roles/keycloak_quarkus/tasks/install.yml | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index a70deae..f7f1be3 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -185,6 +185,16 @@ Role Variables |`keycloak_quarkus_frontend_url`| Base URL for frontend URLs, including scheme, host, port and path | `no` | |`keycloak_quarkus_admin_url`| Base URL for accessing the administration console, including scheme, host, port and path | `no` | |`keycloak_quarkus_ks_vault_pass`| The password for accessing the keystore vault SPI | `no` | +|`keycloak_quarkus_alternate_download_url`| Alternate location with optional authentication for downloading RHBK | `no` | +|`keycloak_quarkus_download_user`| Optional username for http authentication | `no*` | +|`keycloak_quarkus_download_pass`| Optional password for http authentication | `no*` | +|`keycloak_quarkus_download_validate_certs`| Whether to validate certs for URL `keycloak_quarkus_alternate_download_url` | `no` | +|`keycloak_quarkus_jdbc_download_user`| Optional username for http authentication | `no*` | +|`keycloak_quarkus_jdbc_download_pass`| Optional password for http authentication | `no*` | +|`keycloak_quarkus_jdbc_download_validate_certs`| Whether to validate certs for URL `keycloak_quarkus_download_validate_certs` | `no` | + +`*` username/password authentication credentials must be both declared or both undefined + Role custom facts ----------------- diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index 65fd391..480ccbc 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -115,6 +115,25 @@ run_once: true become: false +- name: Perform download of RHBK from alternate download location + delegate_to: localhost + run_once: true + become: false + when: + - archive_path is defined + - archive_path.stat is defined + - not archive_path.stat.exists + - rhbk_enable is defined and rhbk_enable + - not keycloak.offline_install + - keycloak_quarkus_alternate_download_url is defined + ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user + url: "{{ keycloak_quarkus_alternate_download_url }}" + dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" + mode: '0640' + url_username: "{{ keycloak_quarkus_download_user | default(omit) }}" + url_password: "{{ keycloak_quarkus_download_pass | default(omit) }}" + validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}" + - name: Check downloaded archive ansible.builtin.stat: path: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" From 9f4623b05a776ec555e3a1581798a530fddf4cbd Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 14 May 2024 08:36:55 +0200 Subject: [PATCH 025/135] #224: keycloak_quarkus: Add support for policy files --- molecule/quarkus/converge.yml | 6 ++++++ roles/keycloak_quarkus/README.md | 16 ++++++++++++++++ roles/keycloak_quarkus/defaults/main.yml | 2 ++ roles/keycloak_quarkus/tasks/install.yml | 22 ++++++++++++++++++++++ roles/keycloak_quarkus/tasks/prereqs.yml | 10 ++++++++++ 5 files changed, 56 insertions(+) diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index 9e74aa6..2982250 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -31,6 +31,12 @@ value: 10 - id: spid-saml url: https://github.com/italia/spid-keycloak-provider/releases/download/24.0.2/spid-provider.jar + keycloak_quarkus_policies: + - name: "xato-net-10-million-passwords.txt" + url: "https://github.com/danielmiessler/SecLists/raw/master/Passwords/xato-net-10-million-passwords.txt" + - name: "xato-net-10-million-passwords-10.txt" + url: "https://github.com/danielmiessler/SecLists/raw/master/Passwords/xato-net-10-million-passwords-10.txt" + type: password-blacklists roles: - role: keycloak_quarkus - role: keycloak_realm diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index f7f1be3..47d5a21 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -176,6 +176,22 @@ bin/kc.sh build --spi-connections-provider=http-client --spi-connections-http-cl ``` +#### Configuring policies + +| Variable | Description | Default | +|:---------|:------------|:--------| +|`keycloak_quarkus_policies`| List of policy definitions; see below | `[]` | + +Provider definition: + +```yaml +keycloak_quarkus_policies: + - name: xato-net-10-million-passwords.txt # required, resulting file name + url: https://github.com/danielmiessler/SecLists/raw/master/Passwords/xato-net-10-million-passwords.txt # required, url for download + type: password-blacklists # optional, defaults to `password-blacklists`; supported values: [`password-blacklists`] +``` + + Role Variables -------------- diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 242482d..8a4f9f1 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -150,3 +150,5 @@ keycloak_quarkus_ks_vault_type: PKCS12 keycloak_quarkus_ks_vault_pass: keycloak_quarkus_providers: [] +keycloak_quarkus_policies: [] +keycloak_quarkus_supported_policy_types: ['password-blacklists'] diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index 480ccbc..ab7c396 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -226,3 +226,25 @@ loop: "{{ keycloak_quarkus_providers }}" when: item.url is defined and item.url | length > 0 notify: "{{ ['rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or not item.restart else [] }}" + +- name: Ensure required folder structure for policies exits + ansible.builtin.file: + path: "{{ keycloak.home }}/data/{{ item | lower }}" + state: directory + owner: "{{ keycloak.service_user }}" + group: "{{ keycloak.service_group }}" + mode: '0750' + become: true + loop: "{{ keycloak_quarkus_supported_policy_types }}" + +- name: "Install custom policies" + ansible.builtin.get_url: + url: "{{ item.url }}" + dest: "{{ keycloak.home }}/data/{{ item.type|default(keycloak_quarkus_supported_policy_types | first) | lower }}/{{ item.name }}" + owner: "{{ keycloak.service_user }}" + group: "{{ keycloak.service_group }}" + mode: '0640' + become: true + loop: "{{ keycloak_quarkus_policies }}" + when: item.url is defined and item.url | length > 0 + notify: "restart keycloak" diff --git a/roles/keycloak_quarkus/tasks/prereqs.yml b/roles/keycloak_quarkus/tasks/prereqs.yml index e0a76d5..1e422a7 100644 --- a/roles/keycloak_quarkus/tasks/prereqs.yml +++ b/roles/keycloak_quarkus/tasks/prereqs.yml @@ -65,3 +65,13 @@ quiet: true fail_msg: "Providers definition is incorrect; `id` and one of `spi` or `url` are mandatory. `key` and `value` are mandatory for each property" loop: "{{ keycloak_quarkus_providers }}" + +- name: "Validate policies" + ansible.builtin.assert: + that: + - item.name is defined and item.name | length > 0 + - item.url is defined and item.url | length > 0 + - item.type is not defined or item.type | lower in keycloak_quarkus_supported_policy_types + quiet: true + fail_msg: "Policy definition is incorrect: `name` and one of `url` are mandatory, `type` needs to be left empty or one of {{ keycloak_quarkus_supported_policy_types }}." + loop: "{{ keycloak_quarkus_policies }}" From 6682853a2d105aa701ac9f30a33b47ebf1e0f028 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 14 May 2024 08:58:57 +0200 Subject: [PATCH 026/135] #224: Add missing argument specs --- roles/keycloak_quarkus/meta/argument_specs.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 56a99ce..c5f5138 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -394,6 +394,14 @@ argument_specs: description: "List of provider definition dicts: { 'id': str, 'spi': str, 'url': str, 'default': bool, 'properties': list of key/value }" default: [] type: "list" + keycloak_quarkus_supported_policy_types: + description: "List of str of supported policy types" + default: ['password-blacklists'] + type: "list" + keycloak_quarkus_policies: + description: "List of policy definition dicts: { 'name': str, 'url': str, 'type': str }" + default: [] + type: "list" keycloak_quarkus_jdbc_download_url: description: "Override the default Maven Central download URL for the JDBC driver" type: "str" From 4b902adc8d4598dc1ce3ca98f3f0ae3670aaa435 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Mon, 13 May 2024 17:20:21 +0200 Subject: [PATCH 027/135] #222: Add support for maven providers --- requirements.txt | 5 ++-- requirements.yml | 1 + roles/keycloak_quarkus/README.md | 34 ++++++++++++++++++++++-- roles/keycloak_quarkus/tasks/install.yml | 33 +++++++++++++++++++++-- roles/keycloak_quarkus/tasks/prereqs.yml | 4 +-- 5 files changed, 69 insertions(+), 8 deletions(-) diff --git a/requirements.txt b/requirements.txt index b2366a5..a91d12a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ ################################################# -# python dependencies required to be installed +# python dependencies required to be installed # on the controller host with: # pip install -r requirements.txt # -netaddr \ No newline at end of file +netaddr +lxml # for community.general.maven_artifact \ No newline at end of file diff --git a/requirements.yml b/requirements.yml index 3f6feef..10150ad 100644 --- a/requirements.yml +++ b/requirements.yml @@ -2,3 +2,4 @@ collections: - name: middleware_automation.common - name: ansible.posix + - name: community.general # for `maven_artifact` diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 47d5a21..028097c 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -4,6 +4,29 @@ keycloak_quarkus Install [keycloak](https://keycloak.org/) >= 20.0.0 (quarkus) server configurations. +Requirements +------------ + +This role requires the `python3-netaddr` and `lxml` library installed on the controller node. + +* to install via yum/dnf: `dnf install python3-netaddr python3-lxml` +* to install via apt: `apt install python3-netaddr python3-lxml` +* or via the collection: `pip install -r requirements.txt` + + +Dependencies +------------ + +The roles depends on: + +* [middleware_automation.common](https://github.com/ansible-middleware/common) +* [ansible-posix](https://docs.ansible.com/ansible/latest/collections/ansible/posix/index.html) +* [community.general](https://docs.ansible.com/ansible/latest/collections/community/general/index.html) + +To install all the dependencies via galaxy: + + ansible-galaxy collection install -r requirements.yml + Role Defaults ------------- @@ -160,10 +183,17 @@ Provider definition: ```yaml keycloak_quarkus_providers: - id: http-client # required - spi: connections # required if url is not specified + spi: connections # required if neither url nor maven are specified default: true # optional, whether to set default for spi, default false restart: true # optional, whether to restart, default true - url: https://.../.../custom_spi.jar # optional, url for download + url: https://.../.../custom_spi.jar # optional, url for download via http + maven: # optional, for download using maven + repository_url: https://maven.pkg.github.com/OWNER/REPOSITORY # optional, maven repo url + group_id: my.group # optional, maven group id + artifact_id: artifact # optional, maven artifact id + version: 24.0.4 # optional, defaults to latest + username: user # optional, cf. https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry#authenticating-to-github-packages + password: pat # optional, provide a PAT for accessing Github's Apache Maven registry properties: # optional, list of key-values - key: default-connection-pool-size value: 10 diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index ab7c396..8d604e6 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -215,7 +215,7 @@ - rhbk_enable is defined and rhbk_enable - keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url is defined -- name: "Download custom providers" +- name: "Download custom providers via http" ansible.builtin.get_url: url: "{{ item.url }}" dest: "{{ keycloak.home }}/providers/{{ item.id }}.jar" @@ -227,7 +227,36 @@ when: item.url is defined and item.url | length > 0 notify: "{{ ['rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or not item.restart else [] }}" -- name: Ensure required folder structure for policies exits +# this requires the `lxml` package to be installed; we redirect this step to localhost such that we do need to install it on the remote hosts +- name: "Download custom providers to localhost using maven" + community.general.maven_artifact: + repository_url: "{{ item.maven.repository_url }}" + group_id: "{{ item.maven.group_id }}" + artifact_id: "{{ item.maven.artifact_id }}" + version: "{{ item.maven.version | default(omit) }}" + username: "{{ item.maven.username | default(omit) }}" + password: "{{ item.maven.password | default(omit) }}" + dest: "{{ local_path.stat.path }}/{{ item.id }}.jar" + delegate_to: "localhost" + run_once: true + loop: "{{ keycloak_quarkus_providers }}" + when: item.maven is defined + no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" + notify: "{{ ['rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or not item.restart else [] }}" + +- name: "Upload local maven SPIs" + ansible.builtin.copy: + src: "{{ local_path.stat.path }}/{{ item.id }}.jar" + dest: "{{ keycloak.home }}/providers/{{ item.id }}.jar" + owner: "{{ keycloak.service_user }}" + group: "{{ keycloak.service_group }}" + mode: '0640' + become: true + loop: "{{ keycloak_quarkus_providers }}" + when: item.maven is defined + no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" + +- name: Ensure required folder structure for policies exists ansible.builtin.file: path: "{{ keycloak.home }}/data/{{ item | lower }}" state: directory diff --git a/roles/keycloak_quarkus/tasks/prereqs.yml b/roles/keycloak_quarkus/tasks/prereqs.yml index 1e422a7..064cc10 100644 --- a/roles/keycloak_quarkus/tasks/prereqs.yml +++ b/roles/keycloak_quarkus/tasks/prereqs.yml @@ -61,9 +61,9 @@ ansible.builtin.assert: that: - item.id is defined and item.id | length > 0 - - (item.spi is defined and item.spi | length > 0) or (item.url is defined and item.url | length > 0) + - (item.spi is defined and item.spi | length > 0) or (item.url is defined and item.url | length > 0) or (item.maven is defined and item.maven.repository_url is defined and item.maven.repository_url | length > 0 and item.maven.group_id is defined and item.maven.group_id | length > 0 and item.maven.artifact_id is defined and item.maven.artifact_id | length > 0) quiet: true - fail_msg: "Providers definition is incorrect; `id` and one of `spi` or `url` are mandatory. `key` and `value` are mandatory for each property" + fail_msg: "Providers definition is incorrect; `id` and one of `spi`, `url`, or `maven` are mandatory. `key` and `value` are mandatory for each property" loop: "{{ keycloak_quarkus_providers }}" - name: "Validate policies" From d8e9620a8a26381eb272c3125c9b5adb0183fcc0 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Mon, 13 May 2024 17:20:32 +0200 Subject: [PATCH 028/135] #222: Molecule tests --- molecule/quarkus/converge.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index 2982250..6186af4 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -31,6 +31,14 @@ value: 10 - id: spid-saml url: https://github.com/italia/spid-keycloak-provider/releases/download/24.0.2/spid-provider.jar + - id: keycloak-kerberos-federation + maven: + repository_url: https://repo1.maven.org/maven2/ # https://mvnrepository.com/artifact/org.keycloak/keycloak-kerberos-federation/24.0.4 + group_id: org.keycloak + artifact_id: keycloak-kerberos-federation + version: 24.0.4 # optional + # username: myUser # optional + # password: myPAT # optional keycloak_quarkus_policies: - name: "xato-net-10-million-passwords.txt" url: "https://github.com/danielmiessler/SecLists/raw/master/Passwords/xato-net-10-million-passwords.txt" From d87c8ca8acba5695253f91dbd39794632cc75a0c Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 14 May 2024 10:12:58 +0200 Subject: [PATCH 029/135] wip --- roles/keycloak_quarkus/meta/argument_specs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index c5f5138..b630d6a 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -391,7 +391,7 @@ argument_specs: default: 10 type: 'int' keycloak_quarkus_providers: - description: "List of provider definition dicts: { 'id': str, 'spi': str, 'url': str, 'default': bool, 'properties': list of key/value }" + description: "List of provider definition dicts: { 'id': str, 'spi': str, 'url': str, 'default': bool, 'properties': list of key/value TODO:add maven}" default: [] type: "list" keycloak_quarkus_supported_policy_types: From 6d01ffbb7796da35d91713913e44f6f6dd411e22 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 14 May 2024 11:10:38 +0200 Subject: [PATCH 030/135] Close #228: add support for custom env vars in sysconfig file --- roles/keycloak_quarkus/README.md | 3 ++- roles/keycloak_quarkus/defaults/main.yml | 1 + roles/keycloak_quarkus/meta/argument_specs.yml | 4 ++++ roles/keycloak_quarkus/tasks/prereqs.yml | 10 ++++++++++ roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 | 5 +++++ 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 47d5a21..f8f9899 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -38,7 +38,8 @@ Role Defaults |`keycloak_quarkus_java_home`| JAVA_HOME of installed JRE, leave empty for using specified keycloak_quarkus_jvm_package RPM path | `None` | |`keycloak_quarkus_java_heap_opts`| Heap memory JVM setting | `-Xms1024m -Xmx2048m` | |`keycloak_quarkus_java_jvm_opts`| Other JVM settings | same as keycloak | -|`keycloak_quarkus_java_opts`| JVM arguments; if overriden, it takes precedence over `keycloak_quarkus_java_*` | `{{ keycloak_quarkus_java_heap_opts + ' ' + keycloak_quarkus_java_jvm_opts }}` | +|`keycloak_quarkus_java_opts`| JVM arguments; if overridden, it takes precedence over `keycloak_quarkus_java_*` | `{{ keycloak_quarkus_java_heap_opts + ' ' + keycloak_quarkus_java_jvm_opts }}` | +|`keycloak_quarkus_additional_env_vars` | List of additional env variables of { key: str, value: str} to be put in sysconfig file | `[]` | |`keycloak_quarkus_frontend_url`| Set the base URL for frontend URLs, including scheme, host, port and path | | |`keycloak_quarkus_admin_url`| Set the base URL for accessing the administration console, including scheme, host, port and path | | |`keycloak_quarkus_http_relative_path` | Set the path relative to / for serving resources. The path must start with a / | `/` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 8a4f9f1..d61cfff 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -44,6 +44,7 @@ keycloak_quarkus_java_jvm_opts: "-XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.security.egd=file:/dev/urandom -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:FlightRecorderOptions=stackdepth=512" keycloak_quarkus_java_opts: "{{ keycloak_quarkus_java_heap_opts + ' ' + keycloak_quarkus_java_jvm_opts }}" +keycloak_quarkus_additional_env_vars: [] ### TLS/HTTPS configuration keycloak_quarkus_https_key_file_enabled: false diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index c5f5138..c8cece8 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -201,6 +201,10 @@ argument_specs: default: "{{ keycloak_quarkus_java_heap_opts + ' ' + keycloak_quarkus_java_jvm_opts }}" description: "JVM arguments, by default heap_opts + jvm_opts, if overriden it takes precedence over them" type: "str" + keycloak_quarkus_additional_env_vars: + default: "[]" + description: "List of additional env variables of { key: str, value: str} to be put in sysconfig file" + type: "list" keycloak_quarkus_ha_enabled: default: false description: "Enable auto configuration for database backend, clustering and remote caches on infinispan" diff --git a/roles/keycloak_quarkus/tasks/prereqs.yml b/roles/keycloak_quarkus/tasks/prereqs.yml index 1e422a7..0229036 100644 --- a/roles/keycloak_quarkus/tasks/prereqs.yml +++ b/roles/keycloak_quarkus/tasks/prereqs.yml @@ -75,3 +75,13 @@ quiet: true fail_msg: "Policy definition is incorrect: `name` and one of `url` are mandatory, `type` needs to be left empty or one of {{ keycloak_quarkus_supported_policy_types }}." loop: "{{ keycloak_quarkus_policies }}" + +- name: "Validate additional env variables" + ansible.builtin.assert: + that: + - item.key is defined and item.key | length > 0 + - item.value is defined and item.value | length > 0 + quiet: true + fail_msg: "Additional env variable definition is incorrect: `key` and `value` are mandatory." + no_log: true + loop: "{{ keycloak_quarkus_additional_env_vars }}" diff --git a/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 b/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 index ef27d27..2c260a3 100644 --- a/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 +++ b/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 @@ -8,3 +8,8 @@ KEYCLOAK_ADMIN_PASSWORD='{{ keycloak_quarkus_admin_pass }}' PATH={{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin JAVA_HOME={{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }} JAVA_OPTS={{ keycloak_quarkus_java_opts }} + +# Custom ENV variables +{% for env in keycloak_quarkus_additional_env_vars %} +{{ env.key }}={{ env.value }} +{% endfor %} From 26316ddc506872b794b48f1c3edf882a94490b1d Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 14 May 2024 11:54:45 +0200 Subject: [PATCH 031/135] #222: add support for local providers to be uploaded --- molecule/quarkus/converge.yml | 2 ++ roles/keycloak_quarkus/README.md | 11 +++++++++-- roles/keycloak_quarkus/meta/argument_specs.yml | 2 +- roles/keycloak_quarkus/tasks/install.yml | 11 +++++++++++ roles/keycloak_quarkus/tasks/prereqs.yml | 4 ++-- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index 6186af4..2fa1ceb 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -39,6 +39,8 @@ version: 24.0.4 # optional # username: myUser # optional # password: myPAT # optional + # - id: my-static-theme + # local_path: /tmp/my-static-theme.jar keycloak_quarkus_policies: - name: "xato-net-10-million-passwords.txt" url: "https://github.com/danielmiessler/SecLists/raw/master/Passwords/xato-net-10-million-passwords.txt" diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 028097c..28f81aa 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -178,15 +178,22 @@ Role Defaults |:---------|:------------|:--------| |`keycloak_quarkus_providers`| List of provider definitions; see below | `[]` | +Providers support different sources: + +* `url`: http download for SPIs not requiring authentication +* `maven`: maven download for SPIs hosted publicly on Apache Maven Central or private Maven repositories like Github Maven requiring authentication +* `local_path`: static SPIs to be uploaded + Provider definition: ```yaml keycloak_quarkus_providers: - - id: http-client # required - spi: connections # required if neither url nor maven are specified + - id: http-client # required; "{{ id }}.jar" identifies the file name on RHBK + spi: connections # required if neither url, local_path nor maven are specified; required for setting properties default: true # optional, whether to set default for spi, default false restart: true # optional, whether to restart, default true url: https://.../.../custom_spi.jar # optional, url for download via http + local_path: my_theme_spi.jar # optional, path on local controller for SPI to be uploaded maven: # optional, for download using maven repository_url: https://maven.pkg.github.com/OWNER/REPOSITORY # optional, maven repo url group_id: my.group # optional, maven group id diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index b630d6a..f4c9f51 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -391,7 +391,7 @@ argument_specs: default: 10 type: 'int' keycloak_quarkus_providers: - description: "List of provider definition dicts: { 'id': str, 'spi': str, 'url': str, 'default': bool, 'properties': list of key/value TODO:add maven}" + description: "List of provider definition dicts: { 'id': str, 'spi': str, 'url': str, 'local_path': str, 'maven': { 'repository_url': str, 'group_id': str, 'artifact_id': str, 'version': str, 'username': str, optional, 'password': str, optional }, 'default': bool, 'properties': list of key/value }" default: [] type: "list" keycloak_quarkus_supported_policy_types: diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index 8d604e6..6c8b31d 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -256,6 +256,17 @@ when: item.maven is defined no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" +- name: "Upload local SPIs" + ansible.builtin.copy: + src: "{{ item.local_path}}" + dest: "{{ keycloak.home }}/providers/{{ item.id }}.jar" + owner: "{{ keycloak.service_user }}" + group: "{{ keycloak.service_group }}" + mode: '0640' + become: true + loop: "{{ keycloak_quarkus_providers }}" + when: item.local_path is defined + - name: Ensure required folder structure for policies exists ansible.builtin.file: path: "{{ keycloak.home }}/data/{{ item | lower }}" diff --git a/roles/keycloak_quarkus/tasks/prereqs.yml b/roles/keycloak_quarkus/tasks/prereqs.yml index 064cc10..12f9b23 100644 --- a/roles/keycloak_quarkus/tasks/prereqs.yml +++ b/roles/keycloak_quarkus/tasks/prereqs.yml @@ -61,9 +61,9 @@ ansible.builtin.assert: that: - item.id is defined and item.id | length > 0 - - (item.spi is defined and item.spi | length > 0) or (item.url is defined and item.url | length > 0) or (item.maven is defined and item.maven.repository_url is defined and item.maven.repository_url | length > 0 and item.maven.group_id is defined and item.maven.group_id | length > 0 and item.maven.artifact_id is defined and item.maven.artifact_id | length > 0) + - (item.spi is defined and item.spi | length > 0) or (item.url is defined and item.url | length > 0) or (item.maven is defined and item.maven.repository_url is defined and item.maven.repository_url | length > 0 and item.maven.group_id is defined and item.maven.group_id | length > 0 and item.maven.artifact_id is defined and item.maven.artifact_id | length > 0) or (item.local_path is defined and item.local_path | length > 0) quiet: true - fail_msg: "Providers definition is incorrect; `id` and one of `spi`, `url`, or `maven` are mandatory. `key` and `value` are mandatory for each property" + fail_msg: "Providers definition is incorrect; `id` and one of `spi`, `url`, `local_path`, or `maven` are mandatory. `key` and `value` are mandatory for each property" loop: "{{ keycloak_quarkus_providers }}" - name: "Validate policies" From d2ece93c12237459901928e163a0127f82b94235 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 14 May 2024 20:28:14 +0200 Subject: [PATCH 032/135] #222 Migrate to middleware_automation.common.maven_artifact --- requirements.txt | 2 +- requirements.yml | 2 +- roles/keycloak_quarkus/README.md | 7 +++---- roles/keycloak_quarkus/tasks/install.yml | 6 +++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/requirements.txt b/requirements.txt index a91d12a..5de7845 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,4 @@ # pip install -r requirements.txt # netaddr -lxml # for community.general.maven_artifact \ No newline at end of file +lxml # for middleware_automation.common.maven_artifact \ No newline at end of file diff --git a/requirements.yml b/requirements.yml index 10150ad..06e5714 100644 --- a/requirements.yml +++ b/requirements.yml @@ -1,5 +1,5 @@ --- collections: - name: middleware_automation.common + version: ">=1.2.1" - name: ansible.posix - - name: community.general # for `maven_artifact` diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 28f81aa..3e4ce6d 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -21,7 +21,6 @@ The roles depends on: * [middleware_automation.common](https://github.com/ansible-middleware/common) * [ansible-posix](https://docs.ansible.com/ansible/latest/collections/ansible/posix/index.html) -* [community.general](https://docs.ansible.com/ansible/latest/collections/community/general/index.html) To install all the dependencies via galaxy: @@ -180,9 +179,9 @@ Role Defaults Providers support different sources: -* `url`: http download for SPIs not requiring authentication -* `maven`: maven download for SPIs hosted publicly on Apache Maven Central or private Maven repositories like Github Maven requiring authentication -* `local_path`: static SPIs to be uploaded +* `url`: http download for providers not requiring authentication +* `maven`: maven download for providers hosted publicly on Apache Maven Central or private Maven repositories like Github Maven requiring authentication +* `local_path`: static providers to be uploaded Provider definition: diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index 6c8b31d..ced4191 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -229,7 +229,7 @@ # this requires the `lxml` package to be installed; we redirect this step to localhost such that we do need to install it on the remote hosts - name: "Download custom providers to localhost using maven" - community.general.maven_artifact: + middleware_automation.common.maven_artifact: repository_url: "{{ item.maven.repository_url }}" group_id: "{{ item.maven.group_id }}" artifact_id: "{{ item.maven.artifact_id }}" @@ -244,7 +244,7 @@ no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" notify: "{{ ['rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or not item.restart else [] }}" -- name: "Upload local maven SPIs" +- name: "Upload local maven providers" ansible.builtin.copy: src: "{{ local_path.stat.path }}/{{ item.id }}.jar" dest: "{{ keycloak.home }}/providers/{{ item.id }}.jar" @@ -256,7 +256,7 @@ when: item.maven is defined no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" -- name: "Upload local SPIs" +- name: "Upload local providers" ansible.builtin.copy: src: "{{ item.local_path}}" dest: "{{ keycloak.home }}/providers/{{ item.id }}.jar" From 5adb28dcd83c217e059f5775fa80ac9a9cc12a9d Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 15 May 2024 09:22:45 +0200 Subject: [PATCH 033/135] Bump to 2.3.0 --- galaxy.yml | 4 ++-- github.json | 0 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 github.json diff --git a/galaxy.yml b/galaxy.yml index 1e6fd20..da8794c 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "2.2.3" +version: "2.3.0" readme: README.md authors: - Romain Pelisse @@ -26,7 +26,7 @@ tags: - middleware - a4mw dependencies: - "middleware_automation.common": ">=1.1.0" + "middleware_automation.common": ">=1.2.1" "ansible.posix": ">=1.4.0" repository: https://github.com/ansible-middleware/keycloak documentation: https://ansible-middleware.github.io/keycloak diff --git a/github.json b/github.json deleted file mode 100644 index e69de29..0000000 From d57be1f188bec5bdf886605dad8581dfc158db94 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 14 May 2024 13:59:51 +0200 Subject: [PATCH 034/135] Close #182, #221: improve restart handler logic --- roles/keycloak_quarkus/tasks/restart.yml | 43 ++++++++++++++++++++---- roles/keycloak_quarkus/vars/main.yml | 2 +- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/roles/keycloak_quarkus/tasks/restart.yml b/roles/keycloak_quarkus/tasks/restart.yml index 77e1099..4b43ac8 100644 --- a/roles/keycloak_quarkus/tasks/restart.yml +++ b/roles/keycloak_quarkus/tasks/restart.yml @@ -1,9 +1,38 @@ --- -- name: "Restart and enable {{ keycloak.service_name }} service" +- name: Ensure only one service at a time gets rebooted, to ensure replication of distributed ispn caches throttle: 1 - ansible.builtin.systemd: - name: keycloak - enabled: true - state: restarted - daemon_reload: true - become: true + block: + - name: "Restart and enable {{ keycloak.service_name }} service on first host" + ansible.builtin.systemd: + name: "{{ keycloak.service_name }}" + enabled: true + state: restarted + daemon_reload: true + become: true + delegate_to: "{{ ansible_play_hosts | first }}" + run_once: true + + - name: "Wait until {{ keycloak.service_name }} service becomes active {{ keycloak.health_url }}" + ansible.builtin.uri: + url: "{{ keycloak.health_url }}" + register: keycloak_status + until: keycloak_status.status == 200 + retries: 25 + delay: 10 + delegate_to: "{{ ansible_play_hosts | first }}" + run_once: true + + - name: Pause to give distributed ispn caches time to (re-)replicate back onto first host + ansible.builtin.pause: + seconds: 15 + when: + - rhbk_ha_enabled + + - name: "Restart and enable {{ keycloak.service_name }} service on all other hosts" + ansible.builtin.systemd: + name: "{{ keycloak.service_name }}" + enabled: true + state: restarted + daemon_reload: true + become: true + when: inventory_hostname != ansible_play_hosts | first diff --git a/roles/keycloak_quarkus/vars/main.yml b/roles/keycloak_quarkus/vars/main.yml index fcf82f0..a5c4aaa 100644 --- a/roles/keycloak_quarkus/vars/main.yml +++ b/roles/keycloak_quarkus/vars/main.yml @@ -4,7 +4,7 @@ keycloak: # noqa var-naming this is an internal dict of interpolated values config_dir: "{{ keycloak_quarkus_config_dir }}" bundle: "{{ keycloak_quarkus_archive }}" service_name: "keycloak" - health_url: "http://{{ keycloak_quarkus_host }}:{{ keycloak_quarkus_http_port }}{{ keycloak_quarkus_http_relative_path }}{{ '/' \ + health_url: "{{ 'https' if keycloak_quarkus_http_enabled == False else 'http' }}://{{ keycloak_quarkus_host }}:{{ keycloak_quarkus_https_port if keycloak_quarkus_http_enabled == False else keycloak_quarkus_http_port }}{{ keycloak_quarkus_http_relative_path }}{{ '/' \ if keycloak_quarkus_http_relative_path | length > 1 else '' }}realms/master/.well-known/openid-configuration" cli_path: "{{ keycloak_quarkus_home }}/bin/kcadm.sh" service_user: "{{ keycloak_quarkus_service_user }}" From db831fa339c6d2c8e31aba7a2c2146f080aa9234 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Wed, 15 May 2024 10:17:32 +0200 Subject: [PATCH 035/135] #182 - CR changes --- roles/keycloak_quarkus/tasks/restart.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/keycloak_quarkus/tasks/restart.yml b/roles/keycloak_quarkus/tasks/restart.yml index 4b43ac8..4d55189 100644 --- a/roles/keycloak_quarkus/tasks/restart.yml +++ b/roles/keycloak_quarkus/tasks/restart.yml @@ -26,7 +26,7 @@ ansible.builtin.pause: seconds: 15 when: - - rhbk_ha_enabled + - keycloak_quarkus_ha_enabled - name: "Restart and enable {{ keycloak.service_name }} service on all other hosts" ansible.builtin.systemd: From 1e9a669deab77ef65e749f9926211fe35eaea40c Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Wed, 15 May 2024 10:28:46 +0200 Subject: [PATCH 036/135] #221 - add keycloak_quarkus_health_check_url_path config option --- roles/keycloak_quarkus/README.md | 1 + roles/keycloak_quarkus/meta/argument_specs.yml | 4 ++++ roles/keycloak_quarkus/vars/main.yml | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index a011f29..2ba3c99 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -66,6 +66,7 @@ Role Defaults |`keycloak_quarkus_admin_url`| Set the base URL for accessing the administration console, including scheme, host, port and path | | |`keycloak_quarkus_http_relative_path` | Set the path relative to / for serving resources. The path must start with a / | `/` | |`keycloak_quarkus_http_enabled`| Enable listener on HTTP port | `True` | +|`keycloak_quarkus_health_check_url_path`| Path to the health check endpoint; scheme, host and keycloak_quarkus_http_relative_path will be prepended automatically | `realms/master/.well-known/openid-configuration` | |`keycloak_quarkus_https_key_file_enabled`| Enable listener on HTTPS port | `False` | |`keycloak_quarkus_key_file_copy_enabled`| Enable copy of key file to target host | `False` | |`keycloak_quarkus_key_content`| Content of the TLS private key. Use `"{{ lookup('file', 'server.key.pem') }}"` to lookup a file. | `""` | diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 11d928f..6936fa2 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -104,6 +104,10 @@ argument_specs: default: 8080 description: "HTTP port" type: "int" + keycloak_quarkus_health_check_url_path: + default: "realms/master/.well-known/openid-configuration" + description: "Path to the health check endpoint; scheme, host and keycloak_quarkus_http_relative_path will be prepended automatically" + type: "str" keycloak_quarkus_https_key_file_enabled: default: false description: "Enable configuration of HTTPS via files in PEM format" diff --git a/roles/keycloak_quarkus/vars/main.yml b/roles/keycloak_quarkus/vars/main.yml index a5c4aaa..e59af55 100644 --- a/roles/keycloak_quarkus/vars/main.yml +++ b/roles/keycloak_quarkus/vars/main.yml @@ -5,7 +5,7 @@ keycloak: # noqa var-naming this is an internal dict of interpolated values bundle: "{{ keycloak_quarkus_archive }}" service_name: "keycloak" health_url: "{{ 'https' if keycloak_quarkus_http_enabled == False else 'http' }}://{{ keycloak_quarkus_host }}:{{ keycloak_quarkus_https_port if keycloak_quarkus_http_enabled == False else keycloak_quarkus_http_port }}{{ keycloak_quarkus_http_relative_path }}{{ '/' \ - if keycloak_quarkus_http_relative_path | length > 1 else '' }}realms/master/.well-known/openid-configuration" + if keycloak_quarkus_http_relative_path | length > 1 else '' }}{{ keycloak_quarkus_health_check_url_path | default('realms/master/.well-known/openid-configuration') }}" cli_path: "{{ keycloak_quarkus_home }}/bin/kcadm.sh" service_user: "{{ keycloak_quarkus_service_user }}" service_group: "{{ keycloak_quarkus_service_group }}" From 2d573c2b626e348130b921ad827ed3a222daee75 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 15 May 2024 13:34:29 +0200 Subject: [PATCH 037/135] Add restart strategies, and allow custom task include Co-authored-by: Helmut Wolf Co-authored-by: Guido Grazioli --- roles/keycloak_quarkus/defaults/main.yml | 5 ++ roles/keycloak_quarkus/handlers/main.yml | 3 +- .../keycloak_quarkus/meta/argument_specs.yml | 22 +++++++- roles/keycloak_quarkus/tasks/install.yml | 4 +- roles/keycloak_quarkus/tasks/prereqs.yml | 18 +++++-- roles/keycloak_quarkus/tasks/restart.yml | 51 ++++++------------- roles/keycloak_quarkus/tasks/restart/none.yml | 4 ++ .../keycloak_quarkus/tasks/restart/serial.yml | 8 +++ .../tasks/restart/verify_first.yml | 34 +++++++++++++ 9 files changed, 104 insertions(+), 45 deletions(-) create mode 100644 roles/keycloak_quarkus/tasks/restart/none.yml create mode 100644 roles/keycloak_quarkus/tasks/restart/serial.yml create mode 100644 roles/keycloak_quarkus/tasks/restart/verify_first.yml diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index d61cfff..0dde230 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -153,3 +153,8 @@ keycloak_quarkus_ks_vault_pass: keycloak_quarkus_providers: [] keycloak_quarkus_policies: [] keycloak_quarkus_supported_policy_types: ['password-blacklists'] + +# files in restart directory (one of [ 'serial', 'none', 'verify_first' ]), or path to file when providing custom strategy +keycloak_quarkus_restart_strategy: restart/serial.yml +keycloak_quarkus_restart_health_check: "{{ keycloak_quarkus_ha_enabled }}" +keycloak_quarkus_restart_pause: 15 diff --git a/roles/keycloak_quarkus/handlers/main.yml b/roles/keycloak_quarkus/handlers/main.yml index b95d5c3..5851a71 100644 --- a/roles/keycloak_quarkus/handlers/main.yml +++ b/roles/keycloak_quarkus/handlers/main.yml @@ -7,7 +7,8 @@ ansible.builtin.include_tasks: bootstrapped.yml listen: bootstrapped - name: "Restart {{ keycloak.service_name }}" - ansible.builtin.include_tasks: restart.yml + ansible.builtin.include_tasks: + file: "{{ keycloak_quarkus_restart_strategy if keycloak_quarkus_ha_enabled else 'restart.yml' }}" listen: "restart keycloak" - name: "Print deprecation warning" ansible.builtin.fail: diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 6936fa2..01dbfb9 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -399,7 +399,13 @@ argument_specs: default: 10 type: 'int' keycloak_quarkus_providers: - description: "List of provider definition dicts: { 'id': str, 'spi': str, 'url': str, 'local_path': str, 'maven': { 'repository_url': str, 'group_id': str, 'artifact_id': str, 'version': str, 'username': str, optional, 'password': str, optional }, 'default': bool, 'properties': list of key/value }" + description: > + List of provider definition dicts: { 'id': str, 'spi': str, 'url': str, 'local_path': str, + 'maven': { + 'repository_url': str, 'group_id': str, 'artifact_id': str, 'version': str, 'username': str, optional, 'password': str, optional + }, + 'default': bool, + 'properties': list of key/value } default: [] type: "list" keycloak_quarkus_supported_policy_types: @@ -425,6 +431,20 @@ argument_specs: default: true description: "Allow the option to ignore invalid certificates when downloading JDBC drivers from a custom URL" type: "bool" + keycloak_quarkus_restart_health_check: + default: "{{ keycloak_quarkus_ha_enabled }}" + description: "Whether to wait on successful health check after restart" + type: "bool" + keycloak_quarkus_restart_strategy: + description: > + Strategy task file for restarting in HA, one of [ 'serial', 'none', 'verify_first' ] below, or path to + file when providing custom strategy + default: "restart/serial.yml" + type: "str" + keycloak_quarkus_restart_pause: + description: "Seconds to wait between restarts in HA strategy" + default: 15 + type: int downstream: options: rhbk_version: diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index ced4191..7745031 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -258,7 +258,7 @@ - name: "Upload local providers" ansible.builtin.copy: - src: "{{ item.local_path}}" + src: "{{ item.local_path }}" dest: "{{ keycloak.home }}/providers/{{ item.id }}.jar" owner: "{{ keycloak.service_user }}" group: "{{ keycloak.service_group }}" @@ -280,7 +280,7 @@ - name: "Install custom policies" ansible.builtin.get_url: url: "{{ item.url }}" - dest: "{{ keycloak.home }}/data/{{ item.type|default(keycloak_quarkus_supported_policy_types | first) | lower }}/{{ item.name }}" + dest: "{{ keycloak.home }}/data/{{ item.type | default(keycloak_quarkus_supported_policy_types | first) | lower }}/{{ item.name }}" owner: "{{ keycloak.service_user }}" group: "{{ keycloak.service_group }}" mode: '0640' diff --git a/roles/keycloak_quarkus/tasks/prereqs.yml b/roles/keycloak_quarkus/tasks/prereqs.yml index d901c71..220f65b 100644 --- a/roles/keycloak_quarkus/tasks/prereqs.yml +++ b/roles/keycloak_quarkus/tasks/prereqs.yml @@ -59,11 +59,18 @@ - name: "Validate providers" ansible.builtin.assert: - that: - - item.id is defined and item.id | length > 0 - - (item.spi is defined and item.spi | length > 0) or (item.url is defined and item.url | length > 0) or (item.maven is defined and item.maven.repository_url is defined and item.maven.repository_url | length > 0 and item.maven.group_id is defined and item.maven.group_id | length > 0 and item.maven.artifact_id is defined and item.maven.artifact_id | length > 0) or (item.local_path is defined and item.local_path | length > 0) + that: > + item.id is defined and item.id | length > 0 and + ( (item.spi is defined and item.spi | length > 0) or + (item.url is defined and item.url | length > 0) or + ( item.maven is defined and item.maven.repository_url is defined and item.maven.repository_url | length > 0 and + item.maven.group_id is defined and item.maven.group_id | length > 0 and + item.maven.artifact_id is defined and item.maven.artifact_id | length > 0) or + (item.local_path is defined and item.local_path | length > 0) + ) quiet: true - fail_msg: "Providers definition is incorrect; `id` and one of `spi`, `url`, `local_path`, or `maven` are mandatory. `key` and `value` are mandatory for each property" + fail_msg: > + Providers definition incorrect; `id` and one of `spi`, `url`, `local_path`, or `maven` are mandatory. `key` and `value` are mandatory for each property loop: "{{ keycloak_quarkus_providers }}" - name: "Validate policies" @@ -73,7 +80,8 @@ - item.url is defined and item.url | length > 0 - item.type is not defined or item.type | lower in keycloak_quarkus_supported_policy_types quiet: true - fail_msg: "Policy definition is incorrect: `name` and one of `url` are mandatory, `type` needs to be left empty or one of {{ keycloak_quarkus_supported_policy_types }}." + fail_msg: > + Policy definition is incorrect: `name` and one of `url` are mandatory, `type` needs to be left empty or one of {{ keycloak_quarkus_supported_policy_types }}. loop: "{{ keycloak_quarkus_policies }}" - name: "Validate additional env variables" diff --git a/roles/keycloak_quarkus/tasks/restart.yml b/roles/keycloak_quarkus/tasks/restart.yml index 4d55189..9255114 100644 --- a/roles/keycloak_quarkus/tasks/restart.yml +++ b/roles/keycloak_quarkus/tasks/restart.yml @@ -1,38 +1,17 @@ --- -- name: Ensure only one service at a time gets rebooted, to ensure replication of distributed ispn caches - throttle: 1 - block: - - name: "Restart and enable {{ keycloak.service_name }} service on first host" - ansible.builtin.systemd: - name: "{{ keycloak.service_name }}" - enabled: true - state: restarted - daemon_reload: true - become: true - delegate_to: "{{ ansible_play_hosts | first }}" - run_once: true +- name: "Restart and enable {{ keycloak.service_name }} service" + ansible.builtin.systemd: + name: "{{ keycloak.service_name }}" + enabled: true + state: restarted + daemon_reload: true + become: true - - name: "Wait until {{ keycloak.service_name }} service becomes active {{ keycloak.health_url }}" - ansible.builtin.uri: - url: "{{ keycloak.health_url }}" - register: keycloak_status - until: keycloak_status.status == 200 - retries: 25 - delay: 10 - delegate_to: "{{ ansible_play_hosts | first }}" - run_once: true - - - name: Pause to give distributed ispn caches time to (re-)replicate back onto first host - ansible.builtin.pause: - seconds: 15 - when: - - keycloak_quarkus_ha_enabled - - - name: "Restart and enable {{ keycloak.service_name }} service on all other hosts" - ansible.builtin.systemd: - name: "{{ keycloak.service_name }}" - enabled: true - state: restarted - daemon_reload: true - become: true - when: inventory_hostname != ansible_play_hosts | first +- name: "Wait until {{ keycloak.service_name }} service becomes active {{ keycloak.health_url }}" + ansible.builtin.uri: + url: "{{ keycloak.health_url }}" + register: keycloak_status + until: keycloak_status.status == 200 + retries: 25 + delay: 10 + when: keycloak_quarkus_restart_health_check diff --git a/roles/keycloak_quarkus/tasks/restart/none.yml b/roles/keycloak_quarkus/tasks/restart/none.yml new file mode 100644 index 0000000..d048959 --- /dev/null +++ b/roles/keycloak_quarkus/tasks/restart/none.yml @@ -0,0 +1,4 @@ +--- +- name: "Display message" + ansible.builtin.debug: + msg: "keycloak_quarkus_restart_strategy is none, skipping restart" diff --git a/roles/keycloak_quarkus/tasks/restart/serial.yml b/roles/keycloak_quarkus/tasks/restart/serial.yml new file mode 100644 index 0000000..74e8e3b --- /dev/null +++ b/roles/keycloak_quarkus/tasks/restart/serial.yml @@ -0,0 +1,8 @@ +--- +- name: "Restart services in serial, with optional healtch check (keycloak_quarkus_restart_health_check)" + throttle: 1 + loop: "{{ ansible_play_hosts }}" + block: + - name: "Restart and enable {{ keycloak.service_name }} service on first host" + ansible.builtin.include_tasks: ../restart.yml + delegate_to: "{{ item }}" diff --git a/roles/keycloak_quarkus/tasks/restart/verify_first.yml b/roles/keycloak_quarkus/tasks/restart/verify_first.yml new file mode 100644 index 0000000..64e2f53 --- /dev/null +++ b/roles/keycloak_quarkus/tasks/restart/verify_first.yml @@ -0,0 +1,34 @@ +--- +- name: Verify first restarted service with health URL, then rest in parallel + block: + - name: "Restart and enable {{ keycloak.service_name }} service on first host" + ansible.builtin.systemd: + name: "{{ keycloak.service_name }}" + enabled: true + state: restarted + daemon_reload: true + become: true + delegate_to: "{{ ansible_play_hosts | first }}" + run_once: true + + - name: "Wait until {{ keycloak.service_name }} service becomes active {{ keycloak.health_url }}" + ansible.builtin.uri: + url: "{{ keycloak.health_url }}" + register: keycloak_status + until: keycloak_status.status == 200 + retries: 25 + delay: 10 + delegate_to: "{{ ansible_play_hosts | first }}" + run_once: true + + - name: Pause to give distributed ispn caches time to (re-)replicate back onto first host + ansible.builtin.pause: + seconds: "{{ keycloak_quarkus_restart_pause }}" + when: + - keycloak_quarkus_ha_enabled + + - name: "Restart and enable {{ keycloak.service_name }} service on other hosts" + ansible.builtin.include_tasks: ../restart.yml + delegate_to: "{{ item }}" + loop: "{{ ansible_play_hosts }}" + when: inventory_hostname != ansible_play_hosts | first From c22389c86fabce46d98bab90f8761586b97e000a Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 15 May 2024 15:58:21 +0200 Subject: [PATCH 038/135] address review reqs --- roles/keycloak_quarkus/README.md | 3 +++ roles/keycloak_quarkus/defaults/main.yml | 2 +- roles/keycloak_quarkus/meta/argument_specs.yml | 6 +++--- roles/keycloak_quarkus/tasks/restart.yml | 6 ++++++ roles/keycloak_quarkus/tasks/restart/serial.yml | 9 ++++++--- .../{verify_first.yml => serial_then_parallel.yml} | 11 +++++++---- 6 files changed, 26 insertions(+), 11 deletions(-) rename roles/keycloak_quarkus/tasks/restart/{verify_first.yml => serial_then_parallel.yml} (85%) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 2ba3c99..a9dbea0 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -101,6 +101,9 @@ Role Defaults |`keycloak_quarkus_systemd_wait_for_log` | Whether systemd unit should wait for service to be up in logs | `false` | |`keycloak_quarkus_systemd_wait_for_timeout`| How long to wait for service to be alive (seconds) | `60` | |`keycloak_quarkus_systemd_wait_for_delay`| Activation delay for service systemd unit (seconds) | `10` | +|`keycloak_quarkus_restart_strategy`| Strategy task file for restarting in HA (one of provided restart/['serial.yml','none.yml','serial_then_parallel.yml']) or path to file when providing custom strategy | `restart/serial.yml` | +|`keycloak_quarkus_restart_health_check`| Whether to wait for successful health check after restart | `{{ keycloak_quarkus_ha_enabled }}` | +|`keycloak_quarkus_restart_pause`| Seconds to wait between restarts in HA strategy | `15` | #### Hostname configuration diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 0dde230..c152a20 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -154,7 +154,7 @@ keycloak_quarkus_providers: [] keycloak_quarkus_policies: [] keycloak_quarkus_supported_policy_types: ['password-blacklists'] -# files in restart directory (one of [ 'serial', 'none', 'verify_first' ]), or path to file when providing custom strategy +# files in restart directory (one of [ 'serial', 'none', 'serial_then_parallel' ]), or path to file when providing custom strategy keycloak_quarkus_restart_strategy: restart/serial.yml keycloak_quarkus_restart_health_check: "{{ keycloak_quarkus_ha_enabled }}" keycloak_quarkus_restart_pause: 15 diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 01dbfb9..3e06525 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -433,12 +433,12 @@ argument_specs: type: "bool" keycloak_quarkus_restart_health_check: default: "{{ keycloak_quarkus_ha_enabled }}" - description: "Whether to wait on successful health check after restart" + description: "Whether to wait for successful health check after restart" type: "bool" keycloak_quarkus_restart_strategy: description: > - Strategy task file for restarting in HA, one of [ 'serial', 'none', 'verify_first' ] below, or path to - file when providing custom strategy + Strategy task file for restarting in HA, one of restart/[ 'serial', 'none', 'serial_then_parallel' ].yml, or path to + file when providing custom strategy; when keycloak_quarkus_ha_enabled and keycloak_quarkus_restart_health_check == true default: "restart/serial.yml" type: "str" keycloak_quarkus_restart_pause: diff --git a/roles/keycloak_quarkus/tasks/restart.yml b/roles/keycloak_quarkus/tasks/restart.yml index 9255114..bcb8c1d 100644 --- a/roles/keycloak_quarkus/tasks/restart.yml +++ b/roles/keycloak_quarkus/tasks/restart.yml @@ -15,3 +15,9 @@ retries: 25 delay: 10 when: keycloak_quarkus_restart_health_check + +- name: Pause to give distributed ispn caches time to (re-)replicate back onto first host + ansible.builtin.pause: + seconds: "{{ keycloak_quarkus_restart_pause }}" + when: + - keycloak_quarkus_ha_enabled diff --git a/roles/keycloak_quarkus/tasks/restart/serial.yml b/roles/keycloak_quarkus/tasks/restart/serial.yml index 74e8e3b..d98dbf9 100644 --- a/roles/keycloak_quarkus/tasks/restart/serial.yml +++ b/roles/keycloak_quarkus/tasks/restart/serial.yml @@ -3,6 +3,9 @@ throttle: 1 loop: "{{ ansible_play_hosts }}" block: - - name: "Restart and enable {{ keycloak.service_name }} service on first host" - ansible.builtin.include_tasks: ../restart.yml - delegate_to: "{{ item }}" + - name: "Restart and enable {{ keycloak.service_name }} service on {{ item }}" + ansible.builtin.include_tasks: + file: ../restart.yml + apply: + delegate_to: "{{ item }}" + run_once: true diff --git a/roles/keycloak_quarkus/tasks/restart/verify_first.yml b/roles/keycloak_quarkus/tasks/restart/serial_then_parallel.yml similarity index 85% rename from roles/keycloak_quarkus/tasks/restart/verify_first.yml rename to roles/keycloak_quarkus/tasks/restart/serial_then_parallel.yml index 64e2f53..372a302 100644 --- a/roles/keycloak_quarkus/tasks/restart/verify_first.yml +++ b/roles/keycloak_quarkus/tasks/restart/serial_then_parallel.yml @@ -1,5 +1,5 @@ --- -- name: Verify first restarted service with health URL, then rest in parallel +- name: Verify first restarted service with health URL, then rest restart in parallel block: - name: "Restart and enable {{ keycloak.service_name }} service on first host" ansible.builtin.systemd: @@ -28,7 +28,10 @@ - keycloak_quarkus_ha_enabled - name: "Restart and enable {{ keycloak.service_name }} service on other hosts" - ansible.builtin.include_tasks: ../restart.yml - delegate_to: "{{ item }}" - loop: "{{ ansible_play_hosts }}" + ansible.builtin.systemd: + name: "{{ keycloak.service_name }}" + enabled: true + state: restarted + daemon_reload: true + become: true when: inventory_hostname != ansible_play_hosts | first From fdcf1b2ed2d45f696f20263a267cc637a21a5bf8 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 15 May 2024 19:53:33 +0200 Subject: [PATCH 039/135] Add molecule scenario for HA restart --- .github/workflows/ci.yml | 2 +- .gitignore | 1 + molecule/quarkus/converge.yml | 2 +- molecule/quarkus_ha/converge.yml | 29 + molecule/quarkus_ha/molecule.yml | 79 ++ .../quarkus_ha/postgresql/postgresql.conf | 750 ++++++++++++++++++ molecule/quarkus_ha/prepare.yml | 44 + molecule/quarkus_ha/roles | 1 + molecule/quarkus_ha/verify.yml | 86 ++ .../keycloak_quarkus/tasks/restart/serial.yml | 4 +- .../templates/quarkus.properties.j2 | 2 +- 11 files changed, 995 insertions(+), 5 deletions(-) create mode 100644 molecule/quarkus_ha/converge.yml create mode 100644 molecule/quarkus_ha/molecule.yml create mode 100644 molecule/quarkus_ha/postgresql/postgresql.conf create mode 100644 molecule/quarkus_ha/prepare.yml create mode 120000 molecule/quarkus_ha/roles create mode 100644 molecule/quarkus_ha/verify.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5cac41..0927320 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,4 +15,4 @@ jobs: with: fqcn: 'middleware_automation/keycloak' molecule_tests: >- - [ "default", "overridexml", "https_revproxy", "quarkus", "quarkus-devmode", "quarkus_upgrade", "debian" ] + [ "default", "overridexml", "https_revproxy", "quarkus", "quarkus-devmode", "quarkus_upgrade", "debian", "quarkus_ha" ] diff --git a/.gitignore b/.gitignore index e1daa92..f2ffea4 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ docs/_build/ *.retry changelogs/.plugin-cache.yaml *.pem +*.key diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index 2fa1ceb..da0f3dc 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -7,7 +7,7 @@ keycloak_realm: TestRealm keycloak_quarkus_host: instance keycloak_quarkus_log: file - keycloak_quarkus_log_level: debug + keycloak_quarkus_log_level: info keycloak_quarkus_https_key_file_enabled: true keycloak_quarkus_key_file_copy_enabled: true keycloak_quarkus_key_content: "{{ lookup('file', 'key.pem') }}" diff --git a/molecule/quarkus_ha/converge.yml b/molecule/quarkus_ha/converge.yml new file mode 100644 index 0000000..2434e65 --- /dev/null +++ b/molecule/quarkus_ha/converge.yml @@ -0,0 +1,29 @@ +--- +- name: Converge + hosts: keycloak + vars: + keycloak_quarkus_admin_pass: "remembertochangeme" + keycloak_admin_password: "remembertochangeme" + keycloak_realm: TestRealm + keycloak_quarkus_host: "{{ inventory_hostname }}" + keycloak_quarkus_log: file + keycloak_quarkus_log_level: info + keycloak_quarkus_https_key_file_enabled: true + keycloak_quarkus_key_file_copy_enabled: true + keycloak_quarkus_key_content: "{{ lookup('file', inventory_hostname + '.key') }}" + keycloak_quarkus_cert_file_copy_enabled: true + keycloak_quarkus_cert_file_src: "{{ inventory_hostname }}.pem" + keycloak_quarkus_ks_vault_enabled: true + keycloak_quarkus_ks_vault_file: "/opt/keycloak/vault/keystore.p12" + keycloak_quarkus_ks_vault_pass: keystorepassword + keycloak_quarkus_systemd_wait_for_port: true + keycloak_quarkus_systemd_wait_for_timeout: 20 + keycloak_quarkus_systemd_wait_for_delay: 2 + keycloak_quarkus_systemd_wait_for_log: true + keycloak_quarkus_ha_enabled: true + keycloak_quarkus_restart_strategy: restart/serial.yml + keycloak_quarkus_db_user: keycloak + keycloak_quarkus_db_pass: mysecretpass + keycloak_quarkus_jdbc_url: jdbc:postgresql://postgres:5432/keycloak + roles: + - role: keycloak_quarkus diff --git a/molecule/quarkus_ha/molecule.yml b/molecule/quarkus_ha/molecule.yml new file mode 100644 index 0000000..8e07e0f --- /dev/null +++ b/molecule/quarkus_ha/molecule.yml @@ -0,0 +1,79 @@ +--- +driver: + name: docker +platforms: + - name: instance1 + image: registry.access.redhat.com/ubi9/ubi-init:latest + pre_build_image: true + privileged: true + command: "/usr/sbin/init" + groups: + - keycloak + networks: + - name: rhbk + port_bindings: + - "8080/tcp" + - "8443/tcp" + - name: instance2 + image: registry.access.redhat.com/ubi9/ubi-init:latest + pre_build_image: true + privileged: true + command: "/usr/sbin/init" + groups: + - keycloak + networks: + - name: rhbk + port_bindings: + - "8080/tcp" + - "8443/tcp" + - name: postgres + image: ubuntu/postgres:14-22.04_beta + pre_build_image: true + privileged: true + command: postgres + groups: + - database + networks: + - name: rhbk + port_bindings: + - "5432/tcp" + mounts: + - type: bind + target: /etc/postgresql/postgresql.conf + source: ${PWD}/molecule/quarkus_ha/postgresql/postgresql.conf + env: + POSTGRES_USER: keycloak + POSTGRES_PASSWORD: mysecretpass + POSTGRES_DB: keycloak + POSTGRES_HOST_AUTH_METHOD: trust +provisioner: + name: ansible + config_options: + defaults: + interpreter_python: auto_silent + ssh_connection: + pipelining: false + playbooks: + prepare: prepare.yml + converge: converge.yml + verify: verify.yml + inventory: + host_vars: + localhost: + ansible_python_interpreter: "{{ ansible_playbook_python }}" + env: + ANSIBLE_FORCE_COLOR: "true" +verifier: + name: ansible +scenario: + test_sequence: + - cleanup + - destroy + - create + - prepare + - converge + - idempotence + - side_effect + - verify + - cleanup + - destroy diff --git a/molecule/quarkus_ha/postgresql/postgresql.conf b/molecule/quarkus_ha/postgresql/postgresql.conf new file mode 100644 index 0000000..d702576 --- /dev/null +++ b/molecule/quarkus_ha/postgresql/postgresql.conf @@ -0,0 +1,750 @@ +# ----------------------------- +# PostgreSQL configuration file +# ----------------------------- +# +# This file consists of lines of the form: +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are introduced with +# "#" anywhere on a line. The complete list of parameter names and allowed +# values can be found in the PostgreSQL documentation. +# +# The commented-out settings shown in this file represent the default values. +# Re-commenting a setting is NOT sufficient to revert it to the default value; +# you need to reload the server. +# +# This file is read on server startup and when the server receives a SIGHUP +# signal. If you edit the file on a running system, you have to SIGHUP the +# server for the changes to take effect, run "pg_ctl reload", or execute +# "SELECT pg_reload_conf()". Some parameters, which are marked below, +# require a server shutdown and restart to take effect. +# +# Any parameter can also be given as a command-line option to the server, e.g., +# "postgres -c log_connections=on". Some parameters can be changed at run time +# with the "SET" SQL command. +# +# Memory units: kB = kilobytes Time units: ms = milliseconds +# MB = megabytes s = seconds +# GB = gigabytes min = minutes +# TB = terabytes h = hours +# d = days + + +#------------------------------------------------------------------------------ +# FILE LOCATIONS +#------------------------------------------------------------------------------ + +# The default values of these variables are driven from the -D command-line +# option or PGDATA environment variable, represented here as ConfigDir. + +#data_directory = 'ConfigDir' # use data in another directory + # (change requires restart) +#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file + # (change requires restart) +#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file + # (change requires restart) + +# If external_pid_file is not explicitly set, no extra PID file is written. +#external_pid_file = '' # write an extra PID file + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONNECTIONS AND AUTHENTICATION +#------------------------------------------------------------------------------ + +# - Connection Settings - + +listen_addresses = '*' # what IP address(es) to listen on; + # comma-separated list of addresses; + # defaults to 'localhost'; use '*' for all + # (change requires restart) +#port = 5432 # (change requires restart) +#max_connections = 100 # (change requires restart) +#superuser_reserved_connections = 3 # (change requires restart) +#unix_socket_directories = '/tmp' # comma-separated list of directories + # (change requires restart) +#unix_socket_group = '' # (change requires restart) +#unix_socket_permissions = 0777 # begin with 0 to use octal notation + # (change requires restart) +#bonjour = off # advertise server via Bonjour + # (change requires restart) +#bonjour_name = '' # defaults to the computer name + # (change requires restart) + +# - TCP settings - +# see "man 7 tcp" for details + +#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; + # 0 selects the system default +#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; + # 0 selects the system default +#tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default +#tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds; + # 0 selects the system default + +# - Authentication - + +#authentication_timeout = 1min # 1s-600s +#password_encryption = md5 # md5 or scram-sha-256 +#db_user_namespace = off + +# GSSAPI using Kerberos +#krb_server_keyfile = '' +#krb_caseins_users = off + +# - SSL - + +#ssl = off +#ssl_ca_file = '' +#ssl_cert_file = 'server.crt' +#ssl_crl_file = '' +#ssl_key_file = 'server.key' +#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers +#ssl_prefer_server_ciphers = on +#ssl_ecdh_curve = 'prime256v1' +#ssl_min_protocol_version = 'TLSv1' +#ssl_max_protocol_version = '' +#ssl_dh_params_file = '' +#ssl_passphrase_command = '' +#ssl_passphrase_command_supports_reload = off + + +#------------------------------------------------------------------------------ +# RESOURCE USAGE (except WAL) +#------------------------------------------------------------------------------ + +# - Memory - + +#shared_buffers = 32MB # min 128kB + # (change requires restart) +#huge_pages = try # on, off, or try + # (change requires restart) +#temp_buffers = 8MB # min 800kB +#max_prepared_transactions = 0 # zero disables the feature + # (change requires restart) +# Caution: it is not advisable to set max_prepared_transactions nonzero unless +# you actively intend to use prepared transactions. +#work_mem = 4MB # min 64kB +#maintenance_work_mem = 64MB # min 1MB +#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem +#max_stack_depth = 2MB # min 100kB +#shared_memory_type = mmap # the default is the first option + # supported by the operating system: + # mmap + # sysv + # windows + # (change requires restart) +#dynamic_shared_memory_type = posix # the default is the first option + # supported by the operating system: + # posix + # sysv + # windows + # mmap + # (change requires restart) + +# - Disk - + +#temp_file_limit = -1 # limits per-process temp file space + # in kB, or -1 for no limit + +# - Kernel Resources - + +#max_files_per_process = 1000 # min 25 + # (change requires restart) + +# - Cost-Based Vacuum Delay - + +#vacuum_cost_delay = 0 # 0-100 milliseconds (0 disables) +#vacuum_cost_page_hit = 1 # 0-10000 credits +#vacuum_cost_page_miss = 10 # 0-10000 credits +#vacuum_cost_page_dirty = 20 # 0-10000 credits +#vacuum_cost_limit = 200 # 1-10000 credits + +# - Background Writer - + +#bgwriter_delay = 200ms # 10-10000ms between rounds +#bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables +#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round +#bgwriter_flush_after = 0 # measured in pages, 0 disables + +# - Asynchronous Behavior - + +#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching +#max_worker_processes = 8 # (change requires restart) +#max_parallel_maintenance_workers = 2 # taken from max_parallel_workers +#max_parallel_workers_per_gather = 2 # taken from max_parallel_workers +#parallel_leader_participation = on +#max_parallel_workers = 8 # maximum number of max_worker_processes that + # can be used in parallel operations +#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate + # (change requires restart) +#backend_flush_after = 0 # measured in pages, 0 disables + + +#------------------------------------------------------------------------------ +# WRITE-AHEAD LOG +#------------------------------------------------------------------------------ + +# - Settings - + +#wal_level = replica # minimal, replica, or logical + # (change requires restart) +#fsync = on # flush data to disk for crash safety + # (turning this off can cause + # unrecoverable data corruption) +#synchronous_commit = on # synchronization level; + # off, local, remote_write, remote_apply, or on +#wal_sync_method = fsync # the default is the first option + # supported by the operating system: + # open_datasync + # fdatasync (default on Linux) + # fsync + # fsync_writethrough + # open_sync +#full_page_writes = on # recover from partial page writes +#wal_compression = off # enable compression of full-page writes +#wal_log_hints = off # also do full page writes of non-critical updates + # (change requires restart) +#wal_init_zero = on # zero-fill new WAL files +#wal_recycle = on # recycle WAL files +#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers + # (change requires restart) +#wal_writer_delay = 200ms # 1-10000 milliseconds +#wal_writer_flush_after = 1MB # measured in pages, 0 disables + +#commit_delay = 0 # range 0-100000, in microseconds +#commit_siblings = 5 # range 1-1000 + +# - Checkpoints - + +#checkpoint_timeout = 5min # range 30s-1d +#max_wal_size = 1GB +#min_wal_size = 80MB +#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0 +#checkpoint_flush_after = 0 # measured in pages, 0 disables +#checkpoint_warning = 30s # 0 disables + +# - Archiving - + +#archive_mode = off # enables archiving; off, on, or always + # (change requires restart) +#archive_command = '' # command to use to archive a logfile segment + # placeholders: %p = path of file to archive + # %f = file name only + # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' +#archive_timeout = 0 # force a logfile segment switch after this + # number of seconds; 0 disables + +# - Archive Recovery - + +# These are only used in recovery mode. + +#restore_command = '' # command to use to restore an archived logfile segment + # placeholders: %p = path of file to restore + # %f = file name only + # e.g. 'cp /mnt/server/archivedir/%f %p' + # (change requires restart) +#archive_cleanup_command = '' # command to execute at every restartpoint +#recovery_end_command = '' # command to execute at completion of recovery + +# - Recovery Target - + +# Set these only when performing a targeted recovery. + +#recovery_target = '' # 'immediate' to end recovery as soon as a + # consistent state is reached + # (change requires restart) +#recovery_target_name = '' # the named restore point to which recovery will proceed + # (change requires restart) +#recovery_target_time = '' # the time stamp up to which recovery will proceed + # (change requires restart) +#recovery_target_xid = '' # the transaction ID up to which recovery will proceed + # (change requires restart) +#recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed + # (change requires restart) +#recovery_target_inclusive = on # Specifies whether to stop: + # just after the specified recovery target (on) + # just before the recovery target (off) + # (change requires restart) +#recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID + # (change requires restart) +#recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown' + # (change requires restart) + + +#------------------------------------------------------------------------------ +# REPLICATION +#------------------------------------------------------------------------------ + +# - Sending Servers - + +# Set these on the master and on any standby that will send replication data. + +#max_wal_senders = 10 # max number of walsender processes + # (change requires restart) +#wal_keep_segments = 0 # in logfile segments; 0 disables +#wal_sender_timeout = 60s # in milliseconds; 0 disables + +#max_replication_slots = 10 # max number of replication slots + # (change requires restart) +#track_commit_timestamp = off # collect timestamp of transaction commit + # (change requires restart) + +# - Master Server - + +# These settings are ignored on a standby server. + +#synchronous_standby_names = '' # standby servers that provide sync rep + # method to choose sync standbys, number of sync standbys, + # and comma-separated list of application_name + # from standby(s); '*' = all +#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed + +# - Standby Servers - + +# These settings are ignored on a master server. + +#primary_conninfo = '' # connection string to sending server + # (change requires restart) +#primary_slot_name = '' # replication slot on sending server + # (change requires restart) +#promote_trigger_file = '' # file name whose presence ends recovery +#hot_standby = on # "off" disallows queries during recovery + # (change requires restart) +#max_standby_archive_delay = 30s # max delay before canceling queries + # when reading WAL from archive; + # -1 allows indefinite delay +#max_standby_streaming_delay = 30s # max delay before canceling queries + # when reading streaming WAL; + # -1 allows indefinite delay +#wal_receiver_status_interval = 10s # send replies at least this often + # 0 disables +#hot_standby_feedback = off # send info from standby to prevent + # query conflicts +#wal_receiver_timeout = 60s # time that receiver waits for + # communication from master + # in milliseconds; 0 disables +#wal_retrieve_retry_interval = 5s # time to wait before retrying to + # retrieve WAL after a failed attempt +#recovery_min_apply_delay = 0 # minimum delay for applying changes during recovery + +# - Subscribers - + +# These settings are ignored on a publisher. + +#max_logical_replication_workers = 4 # taken from max_worker_processes + # (change requires restart) +#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers + + +#------------------------------------------------------------------------------ +# QUERY TUNING +#------------------------------------------------------------------------------ + +# - Planner Method Configuration - + +#enable_bitmapscan = on +#enable_hashagg = on +#enable_hashjoin = on +#enable_indexscan = on +#enable_indexonlyscan = on +#enable_material = on +#enable_mergejoin = on +#enable_nestloop = on +#enable_parallel_append = on +#enable_seqscan = on +#enable_sort = on +#enable_tidscan = on +#enable_partitionwise_join = off +#enable_partitionwise_aggregate = off +#enable_parallel_hash = on +#enable_partition_pruning = on + +# - Planner Cost Constants - + +#seq_page_cost = 1.0 # measured on an arbitrary scale +#random_page_cost = 4.0 # same scale as above +#cpu_tuple_cost = 0.01 # same scale as above +#cpu_index_tuple_cost = 0.005 # same scale as above +#cpu_operator_cost = 0.0025 # same scale as above +#parallel_tuple_cost = 0.1 # same scale as above +#parallel_setup_cost = 1000.0 # same scale as above + +#jit_above_cost = 100000 # perform JIT compilation if available + # and query more expensive than this; + # -1 disables +#jit_inline_above_cost = 500000 # inline small functions if query is + # more expensive than this; -1 disables +#jit_optimize_above_cost = 500000 # use expensive JIT optimizations if + # query is more expensive than this; + # -1 disables + +#min_parallel_table_scan_size = 8MB +#min_parallel_index_scan_size = 512kB +#effective_cache_size = 4GB + +# - Genetic Query Optimizer - + +#geqo = on +#geqo_threshold = 12 +#geqo_effort = 5 # range 1-10 +#geqo_pool_size = 0 # selects default based on effort +#geqo_generations = 0 # selects default based on effort +#geqo_selection_bias = 2.0 # range 1.5-2.0 +#geqo_seed = 0.0 # range 0.0-1.0 + +# - Other Planner Options - + +#default_statistics_target = 100 # range 1-10000 +#constraint_exclusion = partition # on, off, or partition +#cursor_tuple_fraction = 0.1 # range 0.0-1.0 +#from_collapse_limit = 8 +#join_collapse_limit = 8 # 1 disables collapsing of explicit + # JOIN clauses +#force_parallel_mode = off +#jit = on # allow JIT compilation +#plan_cache_mode = auto # auto, force_generic_plan or + # force_custom_plan + + +#------------------------------------------------------------------------------ +# REPORTING AND LOGGING +#------------------------------------------------------------------------------ + +# - Where to Log - + +#log_destination = 'stderr' # Valid values are combinations of + # stderr, csvlog, syslog, and eventlog, + # depending on platform. csvlog + # requires logging_collector to be on. + +# This is used when logging to stderr: +#logging_collector = off # Enable capturing of stderr and csvlog + # into log files. Required to be on for + # csvlogs. + # (change requires restart) + +# These are only used if logging_collector is on: +#log_directory = 'log' # directory where log files are written, + # can be absolute or relative to PGDATA +#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, + # can include strftime() escapes +#log_file_mode = 0600 # creation mode for log files, + # begin with 0 to use octal notation +#log_truncate_on_rotation = off # If on, an existing log file with the + # same name as the new log file will be + # truncated rather than appended to. + # But such truncation only occurs on + # time-driven rotation, not on restarts + # or size-driven rotation. Default is + # off, meaning append to existing files + # in all cases. +#log_rotation_age = 1d # Automatic rotation of logfiles will + # happen after that time. 0 disables. +#log_rotation_size = 10MB # Automatic rotation of logfiles will + # happen after that much log output. + # 0 disables. + +# These are relevant when logging to syslog: +#syslog_facility = 'LOCAL0' +#syslog_ident = 'postgres' +#syslog_sequence_numbers = on +#syslog_split_messages = on + +# This is only relevant when logging to eventlog (win32): +# (change requires restart) +#event_source = 'PostgreSQL' + +# - When to Log - + +#log_min_messages = warning # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic + +#log_min_error_statement = error # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic (effectively off) + +#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements + # and their durations, > 0 logs only + # statements running at least this number + # of milliseconds + +#log_transaction_sample_rate = 0.0 # Fraction of transactions whose statements + # are logged regardless of their duration. 1.0 logs all + # statements from all transactions, 0.0 never logs. + +# - What to Log - + +#debug_print_parse = off +#debug_print_rewritten = off +#debug_print_plan = off +#debug_pretty_print = on +#log_checkpoints = off +#log_connections = off +#log_disconnections = off +#log_duration = off +#log_error_verbosity = default # terse, default, or verbose messages +#log_hostname = off +#log_line_prefix = '%m [%p] ' # special values: + # %a = application name + # %u = user name + # %d = database name + # %r = remote host and port + # %h = remote host + # %p = process ID + # %t = timestamp without milliseconds + # %m = timestamp with milliseconds + # %n = timestamp with milliseconds (as a Unix epoch) + # %i = command tag + # %e = SQL state + # %c = session ID + # %l = session line number + # %s = session start timestamp + # %v = virtual transaction ID + # %x = transaction ID (0 if none) + # %q = stop here in non-session + # processes + # %% = '%' + # e.g. '<%u%%%d> ' +#log_lock_waits = off # log lock waits >= deadlock_timeout +#log_statement = 'none' # none, ddl, mod, all +#log_replication_commands = off +#log_temp_files = -1 # log temporary files equal or larger + # than the specified size in kilobytes; + # -1 disables, 0 logs all temp files +#log_timezone = 'GMT' + +#------------------------------------------------------------------------------ +# PROCESS TITLE +#------------------------------------------------------------------------------ + +#cluster_name = '' # added to process titles if nonempty + # (change requires restart) +#update_process_title = on + + +#------------------------------------------------------------------------------ +# STATISTICS +#------------------------------------------------------------------------------ + +# - Query and Index Statistics Collector - + +#track_activities = on +#track_counts = on +#track_io_timing = off +#track_functions = none # none, pl, all +#track_activity_query_size = 1024 # (change requires restart) +#stats_temp_directory = 'pg_stat_tmp' + + +# - Monitoring - + +#log_parser_stats = off +#log_planner_stats = off +#log_executor_stats = off +#log_statement_stats = off + + +#------------------------------------------------------------------------------ +# AUTOVACUUM +#------------------------------------------------------------------------------ + +#autovacuum = on # Enable autovacuum subprocess? 'on' + # requires track_counts to also be on. +#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and + # their durations, > 0 logs only + # actions running at least this number + # of milliseconds. +#autovacuum_max_workers = 3 # max number of autovacuum subprocesses + # (change requires restart) +#autovacuum_naptime = 1min # time between autovacuum runs +#autovacuum_vacuum_threshold = 50 # min number of row updates before + # vacuum +#autovacuum_analyze_threshold = 50 # min number of row updates before + # analyze +#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum +#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze +#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum + # (change requires restart) +#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age + # before forced vacuum + # (change requires restart) +#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for + # autovacuum, in milliseconds; + # -1 means use vacuum_cost_delay +#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for + # autovacuum, -1 means use + # vacuum_cost_limit + + +#------------------------------------------------------------------------------ +# CLIENT CONNECTION DEFAULTS +#------------------------------------------------------------------------------ + +# - Statement Behavior - + +#client_min_messages = notice # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # log + # notice + # warning + # error +#search_path = '"$user", public' # schema names +#row_security = on +#default_tablespace = '' # a tablespace name, '' uses the default +#temp_tablespaces = '' # a list of tablespace names, '' uses + # only default tablespace +#default_table_access_method = 'heap' +#check_function_bodies = on +#default_transaction_isolation = 'read committed' +#default_transaction_read_only = off +#default_transaction_deferrable = off +#session_replication_role = 'origin' +#statement_timeout = 0 # in milliseconds, 0 is disabled +#lock_timeout = 0 # in milliseconds, 0 is disabled +#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled +#vacuum_freeze_min_age = 50000000 +#vacuum_freeze_table_age = 150000000 +#vacuum_multixact_freeze_min_age = 5000000 +#vacuum_multixact_freeze_table_age = 150000000 +#vacuum_cleanup_index_scale_factor = 0.1 # fraction of total number of tuples + # before index cleanup, 0 always performs + # index cleanup +#bytea_output = 'hex' # hex, escape +#xmlbinary = 'base64' +#xmloption = 'content' +#gin_fuzzy_search_limit = 0 +#gin_pending_list_limit = 4MB + +# - Locale and Formatting - + +#datestyle = 'iso, mdy' +#intervalstyle = 'postgres' +#timezone = 'GMT' +#timezone_abbreviations = 'Default' # Select the set of available time zone + # abbreviations. Currently, there are + # Default + # Australia (historical usage) + # India + # You can create your own file in + # share/timezonesets/. +#extra_float_digits = 1 # min -15, max 3; any value >0 actually + # selects precise output mode +#client_encoding = sql_ascii # actually, defaults to database + # encoding + +# These settings are initialized by initdb, but they can be changed. +#lc_messages = 'C' # locale for system error message + # strings +#lc_monetary = 'C' # locale for monetary formatting +#lc_numeric = 'C' # locale for number formatting +#lc_time = 'C' # locale for time formatting + +# default configuration for text search +#default_text_search_config = 'pg_catalog.simple' + +# - Shared Library Preloading - + +#shared_preload_libraries = '' # (change requires restart) +#local_preload_libraries = '' +#session_preload_libraries = '' +#jit_provider = 'llvmjit' # JIT library to use + +# - Other Defaults - + +#dynamic_library_path = '$libdir' + + +#------------------------------------------------------------------------------ +# LOCK MANAGEMENT +#------------------------------------------------------------------------------ + +#deadlock_timeout = 1s +#max_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_relation = -2 # negative values mean + # (max_pred_locks_per_transaction + # / -max_pred_locks_per_relation) - 1 +#max_pred_locks_per_page = 2 # min 0 + + +#------------------------------------------------------------------------------ +# VERSION AND PLATFORM COMPATIBILITY +#------------------------------------------------------------------------------ + +# - Previous PostgreSQL Versions - + +#array_nulls = on +#backslash_quote = safe_encoding # on, off, or safe_encoding +#escape_string_warning = on +#lo_compat_privileges = off +#operator_precedence_warning = off +#quote_all_identifiers = off +#standard_conforming_strings = on +#synchronize_seqscans = on + +# - Other Platforms and Clients - + +#transform_null_equals = off + + +#------------------------------------------------------------------------------ +# ERROR HANDLING +#------------------------------------------------------------------------------ + +#exit_on_error = off # terminate session on any error? +#restart_after_crash = on # reinitialize after backend crash? +#data_sync_retry = off # retry or panic on failure to fsync + # data? + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONFIG FILE INCLUDES +#------------------------------------------------------------------------------ + +# These options allow settings to be loaded from files other than the +# default postgresql.conf. Note that these are directives, not variable +# assignments, so they can usefully be given more than once. + +#include_dir = '...' # include files ending in '.conf' from + # a directory, e.g., 'conf.d' +#include_if_exists = '...' # include file only if it exists +#include = '...' # include file + + +#------------------------------------------------------------------------------ +# CUSTOMIZED OPTIONS +#------------------------------------------------------------------------------ + +# Add settings for extensions here diff --git a/molecule/quarkus_ha/prepare.yml b/molecule/quarkus_ha/prepare.yml new file mode 100644 index 0000000..dff1821 --- /dev/null +++ b/molecule/quarkus_ha/prepare.yml @@ -0,0 +1,44 @@ +--- +- name: Prepare + hosts: keycloak + tasks: + - name: "Display hera_home if defined." + ansible.builtin.set_fact: + hera_home: "{{ lookup('env', 'HERA_HOME') }}" + + - name: "Ensure common prepare phase are set." + ansible.builtin.include_tasks: ../prepare.yml + + - name: Create certificate request + ansible.builtin.command: "openssl req -x509 -newkey rsa:4096 -keyout {{ inventory_hostname }}.key -out {{ inventory_hostname }}.pem -sha256 -days 365 -nodes -subj '/CN={{ inventory_hostname }}'" + delegate_to: localhost + changed_when: False + + - name: Create vault directory + become: true + ansible.builtin.file: + state: directory + path: "/opt/keycloak/vault" + mode: 0755 + + - name: Make sure a jre is available (for keytool to prepare keystore) + delegate_to: localhost + ansible.builtin.package: + name: "{{ 'java-17-openjdk-headless' if hera_home | length > 0 else 'openjdk-17-jdk-headless' }}" + state: present + become: true + failed_when: false + + - name: Create vault keystore + ansible.builtin.command: keytool -importpass -alias TestRealm_testalias -keystore keystore.p12 -storepass keystorepassword + delegate_to: localhost + register: keytool_cmd + changed_when: False + failed_when: not 'already exists' in keytool_cmd.stdout and keytool_cmd.rc != 0 + + - name: Copy certificates and vault + become: true + ansible.builtin.copy: + src: keystore.p12 + dest: /opt/keycloak/vault/keystore.p12 + mode: 0444 diff --git a/molecule/quarkus_ha/roles b/molecule/quarkus_ha/roles new file mode 120000 index 0000000..b741aa3 --- /dev/null +++ b/molecule/quarkus_ha/roles @@ -0,0 +1 @@ +../../roles \ No newline at end of file diff --git a/molecule/quarkus_ha/verify.yml b/molecule/quarkus_ha/verify.yml new file mode 100644 index 0000000..dd8490f --- /dev/null +++ b/molecule/quarkus_ha/verify.yml @@ -0,0 +1,86 @@ +--- +- name: Verify + hosts: all + tasks: + - name: Populate service facts + ansible.builtin.service_facts: + + - name: Check if keycloak service started + ansible.builtin.assert: + that: + - ansible_facts.services["keycloak.service"]["state"] == "running" + - ansible_facts.services["keycloak.service"]["status"] == "enabled" + fail_msg: "Service not running" + + - name: Set internal envvar + ansible.builtin.set_fact: + hera_home: "{{ lookup('env', 'HERA_HOME') }}" + + - name: Verify openid config + when: + - hera_home is defined + - hera_home | length == 0 + block: + - name: Fetch openID config # noqa blocked_modules command-instead-of-module + ansible.builtin.shell: | + set -o pipefail + curl -H 'Host: instance' https://localhost:8443/realms/master/.well-known/openid-configuration -k | jq . + args: + executable: /bin/bash + delegate_to: localhost + register: openid_config + changed_when: False + - name: Verify endpoint URLs + ansible.builtin.assert: + that: + - (openid_config.stdout | from_json)["backchannel_authentication_endpoint"] == 'https://instance/realms/master/protocol/openid-connect/ext/ciba/auth' + - (openid_config.stdout | from_json)['issuer'] == 'https://instance/realms/master' + - (openid_config.stdout | from_json)['authorization_endpoint'] == 'https://instance/realms/master/protocol/openid-connect/auth' + - (openid_config.stdout | from_json)['token_endpoint'] == 'https://instance/realms/master/protocol/openid-connect/token' + delegate_to: localhost + + - name: Check log folder + ansible.builtin.stat: + path: /tmp/keycloak + register: keycloak_log_folder + + - name: Check that keycloak log folder exists and is a link + ansible.builtin.assert: + that: + - keycloak_log_folder.stat.exists + - not keycloak_log_folder.stat.isdir + - keycloak_log_folder.stat.islnk + fail_msg: "Service log symlink not correctly created" + + - name: Check log file + become: true + ansible.builtin.stat: + path: /tmp/keycloak/keycloak.log + register: keycloak_log_file + + - name: Check if keycloak file exists + ansible.builtin.assert: + that: + - keycloak_log_file.stat.exists + - not keycloak_log_file.stat.isdir + + - name: Check default log folder + become: yes + ansible.builtin.stat: + path: /var/log/keycloak + register: keycloak_default_log_folder + failed_when: false + + - name: Check that default keycloak log folder doesn't exist + ansible.builtin.assert: + that: + - not keycloak_default_log_folder.stat.exists + + - name: Verify vault SPI in logfile + become: true + ansible.builtin.shell: | + set -o pipefail + zgrep 'Configured KeystoreVaultProviderFactory with the keystore file' /opt/keycloak/keycloak-*/data/log/keycloak.log*zip + changed_when: false + failed_when: slurped_log.rc != 0 + register: slurped_log diff --git a/roles/keycloak_quarkus/tasks/restart/serial.yml b/roles/keycloak_quarkus/tasks/restart/serial.yml index d98dbf9..26397d3 100644 --- a/roles/keycloak_quarkus/tasks/restart/serial.yml +++ b/roles/keycloak_quarkus/tasks/restart/serial.yml @@ -1,11 +1,11 @@ --- - name: "Restart services in serial, with optional healtch check (keycloak_quarkus_restart_health_check)" throttle: 1 - loop: "{{ ansible_play_hosts }}" block: - name: "Restart and enable {{ keycloak.service_name }} service on {{ item }}" ansible.builtin.include_tasks: - file: ../restart.yml + file: restart.yml apply: delegate_to: "{{ item }}" run_once: true + loop: "{{ ansible_play_hosts }}" diff --git a/roles/keycloak_quarkus/templates/quarkus.properties.j2 b/roles/keycloak_quarkus/templates/quarkus.properties.j2 index 5689bfc..93152e8 100644 --- a/roles/keycloak_quarkus/templates/quarkus.properties.j2 +++ b/roles/keycloak_quarkus/templates/quarkus.properties.j2 @@ -1,6 +1,6 @@ {{ ansible_managed | comment }} {% if keycloak_quarkus_ha_enabled %} -{% if not rhbk_enable or keycloak_quarkus_version.split('.')[0]|int < 22 %} +{% if keycloak_quarkus_version.split('.')[0] | int < 22 %} quarkus.infinispan-client.server-list={{ keycloak_quarkus_ispn_hosts }} quarkus.infinispan-client.auth-username={{ keycloak_quarkus_ispn_user }} quarkus.infinispan-client.auth-password={{ keycloak_quarkus_ispn_pass }} From f63b20b9d40a22bafdad0ec996789e7e951b672d Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 15 May 2024 20:01:58 +0200 Subject: [PATCH 040/135] Update verify steps --- molecule/quarkus/converge.yml | 2 +- molecule/quarkus_ha/verify.yml | 61 ++-------------------------------- 2 files changed, 3 insertions(+), 60 deletions(-) diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index da0f3dc..b7430a1 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -7,7 +7,7 @@ keycloak_realm: TestRealm keycloak_quarkus_host: instance keycloak_quarkus_log: file - keycloak_quarkus_log_level: info + keycloak_quarkus_log_level: debug # needed for the verify step keycloak_quarkus_https_key_file_enabled: true keycloak_quarkus_key_file_copy_enabled: true keycloak_quarkus_key_content: "{{ lookup('file', 'key.pem') }}" diff --git a/molecule/quarkus_ha/verify.yml b/molecule/quarkus_ha/verify.yml index dd8490f..c1a2fb9 100644 --- a/molecule/quarkus_ha/verify.yml +++ b/molecule/quarkus_ha/verify.yml @@ -1,6 +1,6 @@ --- - name: Verify - hosts: all + hosts: keycloak tasks: - name: Populate service facts ansible.builtin.service_facts: @@ -16,46 +16,10 @@ ansible.builtin.set_fact: hera_home: "{{ lookup('env', 'HERA_HOME') }}" - - name: Verify openid config - when: - - hera_home is defined - - hera_home | length == 0 - block: - - name: Fetch openID config # noqa blocked_modules command-instead-of-module - ansible.builtin.shell: | - set -o pipefail - curl -H 'Host: instance' https://localhost:8443/realms/master/.well-known/openid-configuration -k | jq . - args: - executable: /bin/bash - delegate_to: localhost - register: openid_config - changed_when: False - - name: Verify endpoint URLs - ansible.builtin.assert: - that: - - (openid_config.stdout | from_json)["backchannel_authentication_endpoint"] == 'https://instance/realms/master/protocol/openid-connect/ext/ciba/auth' - - (openid_config.stdout | from_json)['issuer'] == 'https://instance/realms/master' - - (openid_config.stdout | from_json)['authorization_endpoint'] == 'https://instance/realms/master/protocol/openid-connect/auth' - - (openid_config.stdout | from_json)['token_endpoint'] == 'https://instance/realms/master/protocol/openid-connect/token' - delegate_to: localhost - - - name: Check log folder - ansible.builtin.stat: - path: /tmp/keycloak - register: keycloak_log_folder - - - name: Check that keycloak log folder exists and is a link - ansible.builtin.assert: - that: - - keycloak_log_folder.stat.exists - - not keycloak_log_folder.stat.isdir - - keycloak_log_folder.stat.islnk - fail_msg: "Service log symlink not correctly created" - - name: Check log file become: true ansible.builtin.stat: - path: /tmp/keycloak/keycloak.log + path: /var/log/keycloak/keycloak.log register: keycloak_log_file - name: Check if keycloak file exists @@ -63,24 +27,3 @@ that: - keycloak_log_file.stat.exists - not keycloak_log_file.stat.isdir - - - name: Check default log folder - become: yes - ansible.builtin.stat: - path: /var/log/keycloak - register: keycloak_default_log_folder - failed_when: false - - - name: Check that default keycloak log folder doesn't exist - ansible.builtin.assert: - that: - - not keycloak_default_log_folder.stat.exists - - - name: Verify vault SPI in logfile - become: true - ansible.builtin.shell: | - set -o pipefail - zgrep 'Configured KeystoreVaultProviderFactory with the keystore file' /opt/keycloak/keycloak-*/data/log/keycloak.log*zip - changed_when: false - failed_when: slurped_log.rc != 0 - register: slurped_log From 4b21569f3635526f6e5330e7203187d520b93e17 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Thu, 16 May 2024 11:16:20 +0200 Subject: [PATCH 041/135] parameterize health check; refactor serial_then_parallel --- roles/keycloak_quarkus/README.md | 2 ++ roles/keycloak_quarkus/defaults/main.yml | 2 ++ .../keycloak_quarkus/meta/argument_specs.yml | 8 +++++ roles/keycloak_quarkus/tasks/restart.yml | 6 ++-- .../tasks/restart/serial_then_parallel.yml | 33 +++++-------------- 5 files changed, 23 insertions(+), 28 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index a9dbea0..ce94392 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -103,6 +103,8 @@ Role Defaults |`keycloak_quarkus_systemd_wait_for_delay`| Activation delay for service systemd unit (seconds) | `10` | |`keycloak_quarkus_restart_strategy`| Strategy task file for restarting in HA (one of provided restart/['serial.yml','none.yml','serial_then_parallel.yml']) or path to file when providing custom strategy | `restart/serial.yml` | |`keycloak_quarkus_restart_health_check`| Whether to wait for successful health check after restart | `{{ keycloak_quarkus_ha_enabled }}` | +|`keycloak_quarkus_restart_health_check_delay`| Seconds to let pass before starting healch checks | `10` | +|`keycloak_quarkus_restart_health_check_reries`| Number of attempts for successful health check before failing | `25` | |`keycloak_quarkus_restart_pause`| Seconds to wait between restarts in HA strategy | `15` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index c152a20..46aca81 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -157,4 +157,6 @@ keycloak_quarkus_supported_policy_types: ['password-blacklists'] # files in restart directory (one of [ 'serial', 'none', 'serial_then_parallel' ]), or path to file when providing custom strategy keycloak_quarkus_restart_strategy: restart/serial.yml keycloak_quarkus_restart_health_check: "{{ keycloak_quarkus_ha_enabled }}" +keycloak_quarkus_restart_health_check_delay: 10 +keycloak_quarkus_restart_health_check_reries: 25 keycloak_quarkus_restart_pause: 15 diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 3e06525..57eea53 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -445,6 +445,14 @@ argument_specs: description: "Seconds to wait between restarts in HA strategy" default: 15 type: int + keycloak_quarkus_restart_health_check_delay: + description: "Seconds to let pass before starting healch checks" + default: 10 + type: 'int' + keycloak_quarkus_restart_health_check_reries: + description: "Number of attempts for successful health check before failing" + default: 25 + type: 'int' downstream: options: rhbk_version: diff --git a/roles/keycloak_quarkus/tasks/restart.yml b/roles/keycloak_quarkus/tasks/restart.yml index bcb8c1d..2a1fabd 100644 --- a/roles/keycloak_quarkus/tasks/restart.yml +++ b/roles/keycloak_quarkus/tasks/restart.yml @@ -12,9 +12,9 @@ url: "{{ keycloak.health_url }}" register: keycloak_status until: keycloak_status.status == 200 - retries: 25 - delay: 10 - when: keycloak_quarkus_restart_health_check + retries: "{{ keycloak_quarkus_restart_health_check_reries }}" + delay: "{{ keycloak_quarkus_restart_health_check_delay }}" + when: internal_force_health_check | default(keycloak_quarkus_restart_health_check) - name: Pause to give distributed ispn caches time to (re-)replicate back onto first host ansible.builtin.pause: diff --git a/roles/keycloak_quarkus/tasks/restart/serial_then_parallel.yml b/roles/keycloak_quarkus/tasks/restart/serial_then_parallel.yml index 372a302..d883ff1 100644 --- a/roles/keycloak_quarkus/tasks/restart/serial_then_parallel.yml +++ b/roles/keycloak_quarkus/tasks/restart/serial_then_parallel.yml @@ -1,31 +1,14 @@ --- - name: Verify first restarted service with health URL, then rest restart in parallel block: - - name: "Restart and enable {{ keycloak.service_name }} service on first host" - ansible.builtin.systemd: - name: "{{ keycloak.service_name }}" - enabled: true - state: restarted - daemon_reload: true - become: true - delegate_to: "{{ ansible_play_hosts | first }}" - run_once: true - - - name: "Wait until {{ keycloak.service_name }} service becomes active {{ keycloak.health_url }}" - ansible.builtin.uri: - url: "{{ keycloak.health_url }}" - register: keycloak_status - until: keycloak_status.status == 200 - retries: 25 - delay: 10 - delegate_to: "{{ ansible_play_hosts | first }}" - run_once: true - - - name: Pause to give distributed ispn caches time to (re-)replicate back onto first host - ansible.builtin.pause: - seconds: "{{ keycloak_quarkus_restart_pause }}" - when: - - keycloak_quarkus_ha_enabled + - name: "Restart and enable {{ keycloak.service_name }} service on initial host" + ansible.builtin.include_tasks: + file: restart.yml + apply: + delegate_to: "{{ ansible_play_hosts | first }}" + run_once: true + vars: + internal_force_health_check: true - name: "Restart and enable {{ keycloak.service_name }} service on other hosts" ansible.builtin.systemd: From 6f2ed4d53b9a20f5e613556bbb2136314cea6ebe Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 14 May 2024 10:41:46 +0200 Subject: [PATCH 042/135] Fix #226 - minor proxy-header enhancement --- roles/keycloak_quarkus/templates/keycloak.conf.j2 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/roles/keycloak_quarkus/templates/keycloak.conf.j2 b/roles/keycloak_quarkus/templates/keycloak.conf.j2 index f55ee80..6291b38 100644 --- a/roles/keycloak_quarkus/templates/keycloak.conf.j2 +++ b/roles/keycloak_quarkus/templates/keycloak.conf.j2 @@ -69,13 +69,12 @@ cache-config-file=cache-ispn.xml {% endif %} {% endif %} -{% if keycloak_quarkus_proxy_mode is defined and keycloak_quarkus_proxy_mode != "none" %} -# Deprecated Proxy configuration -proxy={{ keycloak_quarkus_proxy_mode }} -{% endif %} {% if keycloak_quarkus_proxy_headers is defined and keycloak_quarkus_proxy_headers != "none" %} # Proxy proxy-headers={{ keycloak_quarkus_proxy_headers }} +{% elif keycloak_quarkus_proxy_mode is defined and keycloak_quarkus_proxy_mode != "none" %} +# Deprecated Proxy configuration +proxy={{ keycloak_quarkus_proxy_mode }} {% endif %} spi-sticky-session-encoder-infinispan-should-attach-route={{ keycloak_quarkus_spi_sticky_session_encoder_infinispan_should_attach_route | d(true) | lower }} From 0fd8eb52d2920e16a3db9c229b32b358b775020b Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 14 May 2024 20:05:14 +0200 Subject: [PATCH 043/135] #226: CR changes --- roles/keycloak_quarkus/tasks/prereqs.yml | 7 +++++++ roles/keycloak_quarkus/templates/keycloak.conf.j2 | 5 ++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/roles/keycloak_quarkus/tasks/prereqs.yml b/roles/keycloak_quarkus/tasks/prereqs.yml index 220f65b..503b308 100644 --- a/roles/keycloak_quarkus/tasks/prereqs.yml +++ b/roles/keycloak_quarkus/tasks/prereqs.yml @@ -93,3 +93,10 @@ fail_msg: "Additional env variable definition is incorrect: `key` and `value` are mandatory." no_log: true loop: "{{ keycloak_quarkus_additional_env_vars }}" + +- name: "Validate proxy-headers" + ansible.builtin.assert: + that: + - keycloak_quarkus_proxy_headers | lower in ['', 'forwarded', 'xforwarded'] + quiet: true + fail_msg: "keycloak_quarkus_proxy_headers must be either '', 'forwarded' or 'xforwarded'" diff --git a/roles/keycloak_quarkus/templates/keycloak.conf.j2 b/roles/keycloak_quarkus/templates/keycloak.conf.j2 index 6291b38..ab4024b 100644 --- a/roles/keycloak_quarkus/templates/keycloak.conf.j2 +++ b/roles/keycloak_quarkus/templates/keycloak.conf.j2 @@ -69,9 +69,8 @@ cache-config-file=cache-ispn.xml {% endif %} {% endif %} -{% if keycloak_quarkus_proxy_headers is defined and keycloak_quarkus_proxy_headers != "none" %} -# Proxy -proxy-headers={{ keycloak_quarkus_proxy_headers }} +{% if keycloak_quarkus_proxy_headers | length > 0 %} +proxy-headers={{ keycloak_quarkus_proxy_headers | lower }} {% elif keycloak_quarkus_proxy_mode is defined and keycloak_quarkus_proxy_mode != "none" %} # Deprecated Proxy configuration proxy={{ keycloak_quarkus_proxy_mode }} From 4d31117c16555c3ac6f46f0911d6ad7c85dfdec0 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 14 May 2024 20:15:51 +0200 Subject: [PATCH 044/135] Fix RHBK version --- roles/keycloak_quarkus/meta/argument_specs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 57eea53..66cae79 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -456,7 +456,7 @@ argument_specs: downstream: options: rhbk_version: - default: "24.0.4" + default: "24.0.3" description: "Red Hat Build of Keycloak version" type: "str" rhbk_archive: From cc012767a4416a5348cea2038661dc4bb42feb38 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 14 May 2024 20:16:00 +0200 Subject: [PATCH 045/135] #226 - add deprecation warning --- roles/keycloak_quarkus/tasks/deprecations.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/roles/keycloak_quarkus/tasks/deprecations.yml b/roles/keycloak_quarkus/tasks/deprecations.yml index a81c808..556879a 100644 --- a/roles/keycloak_quarkus/tasks/deprecations.yml +++ b/roles/keycloak_quarkus/tasks/deprecations.yml @@ -34,3 +34,20 @@ - name: Flush handlers ansible.builtin.meta: flush_handlers + +# https://access.redhat.com/documentation/en-us/red_hat_build_of_keycloak/24.0/html-single/upgrading_guide/index#deprecated_literal_proxy_literal_option +- name: Check deprecation of keycloak_quarkus_proxy_mode + delegate_to: localhost + run_once: true + when: + - keycloak_quarkus_proxy_mode is defined + - keycloak_quarkus_proxy_headers is defined and keycloak_quarkus_proxy_headers | length == 0 + - keycloak_quarkus_version.split('.') | first | int >= 24 + changed_when: true + ansible.builtin.set_fact: + deprecated_variable: "keycloak_quarkus_proxy_mode" # read in deprecation handler + notify: + - print deprecation warning + +- name: Flush handlers + ansible.builtin.meta: flush_handlers From 92c24e49e7f1a1ad7e1683bb1ea3e8ac1741dbf5 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Wed, 15 May 2024 11:09:58 +0200 Subject: [PATCH 046/135] #226: add proper default value for proxy-headers --- roles/keycloak_quarkus/defaults/main.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 46aca81..a0f6fa4 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -91,7 +91,10 @@ keycloak_quarkus_hostname_strict: true # If all applications use the public URL this option should be enabled. keycloak_quarkus_hostname_strict_backchannel: false -# proxy address forwarding mode if the server is behind a reverse proxy. [none, edge, reencrypt, passthrough] +# The proxy headers that should be accepted by the server. ['', 'forwarded', 'xforwarded'] +keycloak_quarkus_proxy_headers: "" + +# deprecated: proxy address forwarding mode if the server is behind a reverse proxy. [none, edge, reencrypt, passthrough] keycloak_quarkus_proxy_mode: edge # disable xa transactions From 62cbaa35966f7edfeeeb4f76497a4b87de840f7e Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Thu, 16 May 2024 16:24:25 +0200 Subject: [PATCH 047/135] Introduce keycloak_quarkus_show_deprecation_warnings, disabled in molecule tests --- molecule/debian/converge.yml | 1 + molecule/https_revproxy/converge.yml | 3 ++- molecule/quarkus-devmode/converge.yml | 3 ++- molecule/quarkus/converge.yml | 1 + molecule/quarkus_ha/converge.yml | 1 + molecule/quarkus_upgrade/converge.yml | 1 + roles/keycloak_quarkus/README.md | 1 + roles/keycloak_quarkus/defaults/main.yml | 2 ++ roles/keycloak_quarkus/meta/argument_specs.yml | 4 ++++ roles/keycloak_quarkus/tasks/deprecations.yml | 10 +++++----- 10 files changed, 20 insertions(+), 7 deletions(-) diff --git a/molecule/debian/converge.yml b/molecule/debian/converge.yml index 17517b8..e6319b7 100644 --- a/molecule/debian/converge.yml +++ b/molecule/debian/converge.yml @@ -2,6 +2,7 @@ - name: Converge hosts: all vars: + keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_admin_pass: "remembertochangeme" keycloak_realm: TestRealm keycloak_quarkus_log: file diff --git a/molecule/https_revproxy/converge.yml b/molecule/https_revproxy/converge.yml index b1eb7bc..b490721 100644 --- a/molecule/https_revproxy/converge.yml +++ b/molecule/https_revproxy/converge.yml @@ -1,7 +1,8 @@ --- - name: Converge hosts: all - vars: + vars: + keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_admin_pass: "remembertochangeme" keycloak_admin_password: "remembertochangeme" keycloak_realm: TestRealm diff --git a/molecule/quarkus-devmode/converge.yml b/molecule/quarkus-devmode/converge.yml index 2a45189..2e5d351 100644 --- a/molecule/quarkus-devmode/converge.yml +++ b/molecule/quarkus-devmode/converge.yml @@ -1,7 +1,8 @@ --- - name: Converge hosts: all - vars: + vars: + keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_admin_pass: "remembertochangeme" keycloak_admin_password: "remembertochangeme" keycloak_realm: TestRealm diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index b7430a1..7c86756 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -2,6 +2,7 @@ - name: Converge hosts: all vars: + keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_admin_pass: "remembertochangeme" keycloak_admin_password: "remembertochangeme" keycloak_realm: TestRealm diff --git a/molecule/quarkus_ha/converge.yml b/molecule/quarkus_ha/converge.yml index 2434e65..00246b8 100644 --- a/molecule/quarkus_ha/converge.yml +++ b/molecule/quarkus_ha/converge.yml @@ -2,6 +2,7 @@ - name: Converge hosts: keycloak vars: + keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_admin_pass: "remembertochangeme" keycloak_admin_password: "remembertochangeme" keycloak_realm: TestRealm diff --git a/molecule/quarkus_upgrade/converge.yml b/molecule/quarkus_upgrade/converge.yml index eb84589..6025b7c 100644 --- a/molecule/quarkus_upgrade/converge.yml +++ b/molecule/quarkus_upgrade/converge.yml @@ -4,6 +4,7 @@ vars_files: - vars.yml vars: + keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_version: 24.0.3 roles: - role: keycloak_quarkus diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index ce94392..a20c760 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -167,6 +167,7 @@ Role Defaults |`keycloak_quarkus_start_dev`| Whether to start the service in development mode (start-dev) | `False` | |`keycloak_quarkus_transaction_xa_enabled`| Whether to use XA transactions | `True` | |`keycloak_quarkus_spi_sticky_session_encoder_infinispan_should_attach_route`| If the route should be attached to cookies to reflect the node that owns a particular session. If false, route is not attached to cookies and we rely on the session affinity capabilities from reverse proxy | `True` | +|`keycloak_quarkus_show_deprecation_warnings`| Whether deprecation warnings should be shown | `True` | #### Vault SPI diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index a0f6fa4..b3752c5 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -8,6 +8,8 @@ keycloak_quarkus_installdir: "{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_q # whether to install from local archive keycloak_quarkus_offline_install: false +keycloak_quarkus_show_deprecation_warnings: true + ### Install location and service settings keycloak_quarkus_java_home: keycloak_quarkus_dest: /opt/keycloak diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 66cae79..659e7f6 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -483,6 +483,10 @@ argument_specs: default: false description: "Perform an offline install" type: "bool" + keycloak_quarkus_show_deprecation_warnings: + default: true + description: "Whether deprecation warnings should be shown" + type: "bool" rhbk_service_name: default: "rhbk" description: "systemd service name for Red Hat Build of Keycloak" diff --git a/roles/keycloak_quarkus/tasks/deprecations.yml b/roles/keycloak_quarkus/tasks/deprecations.yml index 556879a..27ea6e3 100644 --- a/roles/keycloak_quarkus/tasks/deprecations.yml +++ b/roles/keycloak_quarkus/tasks/deprecations.yml @@ -10,7 +10,7 @@ - keycloak_quarkus_key_store_file is defined - keycloak_quarkus_key_store_file != '' - keycloak_quarkus_https_key_store_file == keycloak.home + "/conf/key_store.p12" # default value - changed_when: true + changed_when: keycloak_quarkus_show_deprecation_warnings ansible.builtin.set_fact: keycloak_quarkus_https_key_store_file: "{{ keycloak_quarkus_key_store_file }}" deprecated_variable: "keycloak_quarkus_key_store_file" # read in deprecation handler @@ -25,7 +25,7 @@ - keycloak_quarkus_key_store_password is defined - keycloak_quarkus_key_store_password != '' - keycloak_quarkus_https_key_store_password == "" # default value - changed_when: true + changed_when: keycloak_quarkus_show_deprecation_warnings ansible.builtin.set_fact: keycloak_quarkus_https_key_store_password: "{{ keycloak_quarkus_key_store_password }}" deprecated_variable: "keycloak_quarkus_key_store_password" # read in deprecation handler @@ -37,13 +37,13 @@ # https://access.redhat.com/documentation/en-us/red_hat_build_of_keycloak/24.0/html-single/upgrading_guide/index#deprecated_literal_proxy_literal_option - name: Check deprecation of keycloak_quarkus_proxy_mode - delegate_to: localhost - run_once: true when: - keycloak_quarkus_proxy_mode is defined - keycloak_quarkus_proxy_headers is defined and keycloak_quarkus_proxy_headers | length == 0 - keycloak_quarkus_version.split('.') | first | int >= 24 - changed_when: true + delegate_to: localhost + run_once: true + changed_when: keycloak_quarkus_show_deprecation_warnings ansible.builtin.set_fact: deprecated_variable: "keycloak_quarkus_proxy_mode" # read in deprecation handler notify: From df1939e387f6028b88faee6227be6a87d27c8ce6 Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Mon, 20 May 2024 10:21:55 +0000 Subject: [PATCH 048/135] Update changelog for release 2.3.0 Signed-off-by: ansible-middleware-core --- CHANGELOG.rst | 23 +++++++++++++++++++++++ changelogs/changelog.yaml | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3d0f005..f139898 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,29 @@ middleware\_automation.keycloak Release Notes This changelog describes changes after version 0.2.6. +v2.3.0 +====== + +Major Changes +------------- + +- Allow for custom providers hosted on maven repositories `#223 `_ +- Restart handler strategy behaviour `#231 `_ + +Minor Changes +------------- + +- Add support for policy files `#225 `_ +- Allow to add extra custom env vars in sysconfig file `#229 `_ +- Download from alternate URL with optional http authentication `#220 `_ +- Update Keycloak to version 24.0.4 `#218 `_ +- ``proxy-header`` enhancement `#227 `_ + +Bugfixes +-------- + +- ``kc.sh build`` uses configured jdk `#211 `_ + v2.2.2 ====== diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index fe5c499..773f77d 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -532,3 +532,42 @@ releases: - 209.yaml - 210.yaml release_date: '2024-05-06' + 2.3.0: + changes: + bugfixes: + - '``kc.sh build`` uses configured jdk `#211 `_ + + ' + major_changes: + - 'Allow for custom providers hosted on maven repositories `#223 `_ + + ' + - 'Restart handler strategy behaviour `#231 `_ + + ' + minor_changes: + - 'Add support for policy files `#225 `_ + + ' + - 'Allow to add extra custom env vars in sysconfig file `#229 `_ + + ' + - 'Download from alternate URL with optional http authentication `#220 `_ + + ' + - 'Update Keycloak to version 24.0.4 `#218 `_ + + ' + - '``proxy-header`` enhancement `#227 `_ + + ' + fragments: + - 211.yaml + - 218.yaml + - 220.yaml + - 223.yaml + - 225.yaml + - 227.yaml + - 229.yaml + - 231.yaml + release_date: '2024-05-20' From 8ca73364e98e283e5b798d4381d75967386d77c4 Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Mon, 20 May 2024 10:22:09 +0000 Subject: [PATCH 049/135] Bump version to 2.3.1 --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index da8794c..1a4d35a 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "2.3.0" +version: "2.3.1" readme: README.md authors: - Romain Pelisse From 2092c2d23a2c028149d314205735a8970b713fd8 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 21 May 2024 12:27:45 +0200 Subject: [PATCH 050/135] Update minimum ansible-core version > 2.15 --- README.md | 2 +- meta/runtime.yml | 2 +- roles/keycloak/meta/main.yml | 2 +- roles/keycloak_quarkus/meta/main.yml | 2 +- roles/keycloak_realm/meta/main.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0cd3ba0..e71a8f6 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Collection to install and configure [Keycloak](https://www.keycloak.org/) or [Re ## Ansible version compatibility -This collection has been tested against following Ansible versions: **>=2.14.0**. +This collection has been tested against following Ansible versions: **>=2.15.0**. Plugins and modules within a collection may be tested with only specific Ansible versions. A collection may contain metadata that identifies these versions. diff --git a/meta/runtime.yml b/meta/runtime.yml index ce6befd..1e85b01 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -1,2 +1,2 @@ --- -requires_ansible: ">=2.14.0" +requires_ansible: ">=2.15.0" diff --git a/roles/keycloak/meta/main.yml b/roles/keycloak/meta/main.yml index a70e9f1..a27afb8 100644 --- a/roles/keycloak/meta/main.yml +++ b/roles/keycloak/meta/main.yml @@ -12,7 +12,7 @@ galaxy_info: license: Apache License 2.0 - min_ansible_version: "2.14" + min_ansible_version: "2.15" platforms: - name: EL diff --git a/roles/keycloak_quarkus/meta/main.yml b/roles/keycloak_quarkus/meta/main.yml index 0f82003..40a487f 100644 --- a/roles/keycloak_quarkus/meta/main.yml +++ b/roles/keycloak_quarkus/meta/main.yml @@ -8,7 +8,7 @@ galaxy_info: license: Apache License 2.0 - min_ansible_version: "2.14" + min_ansible_version: "2.15" platforms: - name: EL diff --git a/roles/keycloak_realm/meta/main.yml b/roles/keycloak_realm/meta/main.yml index 915f62c..ab861ed 100644 --- a/roles/keycloak_realm/meta/main.yml +++ b/roles/keycloak_realm/meta/main.yml @@ -8,7 +8,7 @@ galaxy_info: license: Apache License 2.0 - min_ansible_version: "2.14" + min_ansible_version: "2.15" platforms: - name: EL From ef53ca545af031cb662cb143caf9e288e7fafbb7 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 21 May 2024 12:31:40 +0200 Subject: [PATCH 051/135] update yamllint --- .yamllint | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.yamllint b/.yamllint index fa1f1fc..10e554e 100644 --- a/.yamllint +++ b/.yamllint @@ -15,7 +15,8 @@ rules: commas: max-spaces-after: -1 level: error - comments: disable + comments: + min-spaces-from-content: 1 comments-indentation: disable document-start: disable empty-lines: @@ -30,4 +31,8 @@ rules: new-lines: type: unix trailing-spaces: disable - truthy: disable \ No newline at end of file + truthy: disable + octal-values: + forbid-implicit-octal: true + forbid-explicit-octal: true + From adfee5f6e1e4767fe6fbf70d3431322409491798 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 21 May 2024 12:34:11 +0200 Subject: [PATCH 052/135] ci --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0927320..3d71b46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,7 @@ on: branches: - main pull_request: + workflow_dispatch: schedule: - cron: '15 6 * * *' From bf1871182b3ce806991278a1efbc99d1e72b387a Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 21 May 2024 12:35:33 +0200 Subject: [PATCH 053/135] linter --- roles/keycloak/tasks/fastpackages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/keycloak/tasks/fastpackages.yml b/roles/keycloak/tasks/fastpackages.yml index be34c72..a89f7f6 100644 --- a/roles/keycloak/tasks/fastpackages.yml +++ b/roles/keycloak/tasks/fastpackages.yml @@ -14,7 +14,7 @@ - name: "Install packages: {{ packages_to_install }}" become: true - ansible.builtin.yum: + ansible.builtin.dnf: name: "{{ packages_to_install }}" state: present when: From e69e5b7ba4086d5ad0dda3529e2f239669e76392 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 21 May 2024 12:41:31 +0200 Subject: [PATCH 054/135] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e71a8f6..c5ee5cf 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ ansible-playbook -i -e @rhn-creds.yml playbooks/keycloak.yml -e localhost ansible_connection=local ``` -Note: when deploying clustered configurations, all hosts belonging to the cluster must be present in ansible_play_batch; ie. they must be targeted by the same ansible-playbook execution. +Note: when deploying clustered configurations, all hosts belonging to the cluster must be present in `ansible_play_batch`; ie. they must be targeted by the same ansible-playbook execution. ## Configuration From 4fb44091d6504a669592ce22ea23ebbedeeefa10 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Thu, 30 May 2024 08:44:04 +0200 Subject: [PATCH 055/135] ci: fix missing symlink --- molecule/quarkus_upgrade/roles | 1 + 1 file changed, 1 insertion(+) create mode 120000 molecule/quarkus_upgrade/roles diff --git a/molecule/quarkus_upgrade/roles b/molecule/quarkus_upgrade/roles new file mode 120000 index 0000000..b741aa3 --- /dev/null +++ b/molecule/quarkus_upgrade/roles @@ -0,0 +1 @@ +../../roles \ No newline at end of file From a4deaa005a374fc599eae658740fd4ff4120c3fe Mon Sep 17 00:00:00 2001 From: Giovanni Toraldo Date: Tue, 4 Jun 2024 17:00:11 +0200 Subject: [PATCH 056/135] Enable by default health check on restart --- roles/keycloak_quarkus/README.md | 2 +- roles/keycloak_quarkus/defaults/main.yml | 2 +- roles/keycloak_quarkus/meta/argument_specs.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index a20c760..a166725 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -102,7 +102,7 @@ Role Defaults |`keycloak_quarkus_systemd_wait_for_timeout`| How long to wait for service to be alive (seconds) | `60` | |`keycloak_quarkus_systemd_wait_for_delay`| Activation delay for service systemd unit (seconds) | `10` | |`keycloak_quarkus_restart_strategy`| Strategy task file for restarting in HA (one of provided restart/['serial.yml','none.yml','serial_then_parallel.yml']) or path to file when providing custom strategy | `restart/serial.yml` | -|`keycloak_quarkus_restart_health_check`| Whether to wait for successful health check after restart | `{{ keycloak_quarkus_ha_enabled }}` | +|`keycloak_quarkus_restart_health_check`| Whether to wait for successful health check after restart | `true` | |`keycloak_quarkus_restart_health_check_delay`| Seconds to let pass before starting healch checks | `10` | |`keycloak_quarkus_restart_health_check_reries`| Number of attempts for successful health check before failing | `25` | |`keycloak_quarkus_restart_pause`| Seconds to wait between restarts in HA strategy | `15` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index b3752c5..4f6deb3 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -161,7 +161,7 @@ keycloak_quarkus_supported_policy_types: ['password-blacklists'] # files in restart directory (one of [ 'serial', 'none', 'serial_then_parallel' ]), or path to file when providing custom strategy keycloak_quarkus_restart_strategy: restart/serial.yml -keycloak_quarkus_restart_health_check: "{{ keycloak_quarkus_ha_enabled }}" +keycloak_quarkus_restart_health_check: true keycloak_quarkus_restart_health_check_delay: 10 keycloak_quarkus_restart_health_check_reries: 25 keycloak_quarkus_restart_pause: 15 diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 659e7f6..978ca7e 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -432,7 +432,7 @@ argument_specs: description: "Allow the option to ignore invalid certificates when downloading JDBC drivers from a custom URL" type: "bool" keycloak_quarkus_restart_health_check: - default: "{{ keycloak_quarkus_ha_enabled }}" + default: true description: "Whether to wait for successful health check after restart" type: "bool" keycloak_quarkus_restart_strategy: From a82bdfbbb6a079e96b013479955b8b5cf612de19 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 4 Jun 2024 17:36:20 +0200 Subject: [PATCH 057/135] Bump to 2.4.0 --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index 1a4d35a..c64908f 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "2.3.1" +version: "2.4.0" readme: README.md authors: - Romain Pelisse From e927ddbb6caa3ddd0fba30b9fdd68b6510c5f710 Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Tue, 4 Jun 2024 15:44:20 +0000 Subject: [PATCH 058/135] Update changelog for release 2.4.0 Signed-off-by: ansible-middleware-core --- CHANGELOG.rst | 9 +++++++++ changelogs/changelog.yaml | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f139898..d8fbfdd 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,15 @@ middleware\_automation.keycloak Release Notes This changelog describes changes after version 0.2.6. +v2.4.0 +====== + +Major Changes +------------- + +- Enable by default health check on restart `#234 `_ +- Update minimum ansible-core version > 2.15 `#232 `_ + v2.3.0 ====== diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 773f77d..e5de562 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -571,3 +571,16 @@ releases: - 229.yaml - 231.yaml release_date: '2024-05-20' + 2.4.0: + changes: + major_changes: + - 'Enable by default health check on restart `#234 `_ + + ' + - 'Update minimum ansible-core version > 2.15 `#232 `_ + + ' + fragments: + - 232.yaml + - 234.yaml + release_date: '2024-06-04' From 5f059e8d63b8e5e04d957951a4820a4ff90cb901 Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Tue, 4 Jun 2024 15:44:35 +0000 Subject: [PATCH 059/135] Bump version to 2.4.1 --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index c64908f..7ff57c6 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "2.4.0" +version: "2.4.1" readme: README.md authors: - Romain Pelisse From c6fac7bb70932f6ea852483f86deea8a3f9b3d36 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Thu, 27 Jun 2024 11:00:29 +0200 Subject: [PATCH 060/135] ci: add traffic wf --- .github/workflows/traffic.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/traffic.yml diff --git a/.github/workflows/traffic.yml b/.github/workflows/traffic.yml new file mode 100644 index 0000000..6b0dde7 --- /dev/null +++ b/.github/workflows/traffic.yml @@ -0,0 +1,24 @@ +on: + schedule: + - cron: "51 23 * * 0" + +jobs: + traffic: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + ref: "traffic" + + - name: GitHub traffic + uses: sangonzal/repository-traffic-action@v.0.1.6 + env: + TRAFFIC_ACTION_TOKEN: ${{ secrets.TRIGGERING_PAT }} + + - name: Commit changes + uses: EndBug/add-and-commit@v4 + with: + author_name: Ansible Middleware + message: "GitHub traffic" + add: "./traffic/*" + ref: "traffic" From 64e2a95685459155f1b2fcb3a9314ba1c9bf245b Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Thu, 27 Jun 2024 11:01:38 +0200 Subject: [PATCH 061/135] ci: add traffic wf --- .github/workflows/traffic.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/traffic.yml b/.github/workflows/traffic.yml index 6b0dde7..1d85eac 100644 --- a/.github/workflows/traffic.yml +++ b/.github/workflows/traffic.yml @@ -1,6 +1,8 @@ +name: Collect traffic stats on: schedule: - cron: "51 23 * * 0" + workflow_dispatch: jobs: traffic: From e40f5549367e124103863db08d18f8edb389854d Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Thu, 27 Jun 2024 11:02:32 +0200 Subject: [PATCH 062/135] ci: add traffic wf --- .github/workflows/traffic.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/traffic.yml b/.github/workflows/traffic.yml index 1d85eac..b4f245e 100644 --- a/.github/workflows/traffic.yml +++ b/.github/workflows/traffic.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v2 with: - ref: "traffic" + ref: "gh-pages" - name: GitHub traffic uses: sangonzal/repository-traffic-action@v.0.1.6 @@ -23,4 +23,4 @@ jobs: author_name: Ansible Middleware message: "GitHub traffic" add: "./traffic/*" - ref: "traffic" + ref: "gh-pages" From 94f1b8b35589edd75667188115afec8a35b2c787 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 2 Jul 2024 15:46:05 +0200 Subject: [PATCH 063/135] ci: update README --- README.md | 4 +++- roles/keycloak_quarkus/README.md | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c5ee5cf..3827b90 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,9 @@ > **_NOTE:_ If you are Red Hat customer, install `redhat.sso` (for Red Hat Single Sign-On) or `redhat.rhbk` (for Red Hat Build of Keycloak) from [Automation Hub](https://console.redhat.com/ansible/ansible-dashboard) as the certified version of this collection.** + Collection to install and configure [Keycloak](https://www.keycloak.org/) or [Red Hat Single Sign-On](https://access.redhat.com/products/red-hat-single-sign-on) / [Red Hat Build of Keycloak](https://access.redhat.com/products/red-hat-build-of-keycloak). - + ## Ansible version compatibility @@ -39,6 +40,7 @@ collections: The keycloak collection also depends on the following python packages to be present on the controller host: * netaddr +* lxml A requirement file is provided to install: diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index a166725..1fb0cd1 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -1,8 +1,8 @@ keycloak_quarkus ================ - + Install [keycloak](https://keycloak.org/) >= 20.0.0 (quarkus) server configurations. - + Requirements ------------ From 35b3b090f61ff54687138496080f98260f98c548 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 2 Jul 2024 15:59:16 +0200 Subject: [PATCH 064/135] ci: update READMEs --- roles/keycloak_realm/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/roles/keycloak_realm/README.md b/roles/keycloak_realm/README.md index 73d823f..d6ce30c 100644 --- a/roles/keycloak_realm/README.md +++ b/roles/keycloak_realm/README.md @@ -1,8 +1,9 @@ keycloak_realm ============== + Create realms and clients in [keycloak](https://keycloak.org/) or [Red Hat Single Sign-On](https://access.redhat.com/products/red-hat-single-sign-on) services. - + Role Defaults ------------- @@ -136,4 +137,4 @@ Author Information ------------------ * [Guido Grazioli](https://github.com/guidograzioli) -* [Romain Pelisse](https://github.com/rpelisse) \ No newline at end of file +* [Romain Pelisse](https://github.com/rpelisse) From 7c520dcdd2f4a424a52a5d8bc6522f17f707b41b Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Tue, 2 Jul 2024 14:23:37 +0000 Subject: [PATCH 065/135] Update changelog for release 2.4.1 Signed-off-by: ansible-middleware-core --- CHANGELOG.rst | 8 ++++++++ changelogs/changelog.yaml | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d8fbfdd..1aed396 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,14 @@ middleware\_automation.keycloak Release Notes This changelog describes changes after version 0.2.6. +v2.4.1 +====== + +Release Summary +--------------- + +Internal release, documentation or test changes only. + v2.4.0 ====== diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index e5de562..c841535 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -584,3 +584,9 @@ releases: - 232.yaml - 234.yaml release_date: '2024-06-04' + 2.4.1: + changes: + release_summary: Internal release, documentation or test changes only. + fragments: + - v2.4.1-devel_summary.yaml + release_date: '2024-07-02' From fa2319d5da0922a5161d8475b17b0c2c0d0457a8 Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Tue, 2 Jul 2024 14:23:53 +0000 Subject: [PATCH 066/135] Bump version to 2.4.2 --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index 7ff57c6..2091f28 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "2.4.1" +version: "2.4.2" readme: README.md authors: - Romain Pelisse From 11aab0f5e2bc68f7b895de3f10e4a39e67639ed1 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Thu, 18 Jul 2024 12:53:49 +0200 Subject: [PATCH 067/135] add verify steps for quarkus/keycloak_realm --- molecule/quarkus/verify.yml | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/molecule/quarkus/verify.yml b/molecule/quarkus/verify.yml index dd8490f..24c27aa 100644 --- a/molecule/quarkus/verify.yml +++ b/molecule/quarkus/verify.yml @@ -1,6 +1,8 @@ --- - name: Verify hosts: all + vars: + keycloak_admin_password: "remembertochangeme" tasks: - name: Populate service facts ansible.builtin.service_facts: @@ -84,3 +86,42 @@ changed_when: false failed_when: slurped_log.rc != 0 register: slurped_log + + - name: Verify token api call + ansible.builtin.uri: + url: "https://localhost:8443/realms/master/protocol/openid-connect/token" + method: POST + body: "client_id=admin-cli&username=admin&password={{ keycloak_admin_password }}&grant_type=password" + validate_certs: no + register: keycloak_auth_response + until: keycloak_auth_response.status == 200 + retries: 2 + delay: 2 + + - name: "Get Clients" + ansible.builtin.uri: + url: "https://localhost:8443/admin/realms/TestRealm/clients" + headers: + validate_certs: false + Authorization: "Bearer {{ keycloak_auth_response.json.access_token }}" + register: keycloak_clients + + - name: Get client uuid + ansible.builtin.set_fact: + keycloak_client_uuid: "{{ ((keycloak_clients.json | selectattr('clientId', '==', 'TestClient')) | first).id }}" + + - name: "Get Client {{ keycloak_client_uuid }}" + ansible.builtin.uri: + url: "https://localhost:8443/admin/realms/TestRealm/clients/{{ keycloak_client_uuid }}" + headers: + validate_certs: false + Authorization: "Bearer {{ keycloak_auth_response.json.access_token }}" + register: keycloak_test_client + + - name: "Get Client roles" + ansible.builtin.uri: + url: "https://localhost:8443/admin/realms/TestRealm/clients/{{ keycloak_client_uuid }}/roles" + headers: + validate_certs: false + Authorization: "Bearer {{ keycloak_auth_response.json.access_token }}" + register: keycloak_test_client_roles \ No newline at end of file From a35c963a65c0a61ff7a2c65b13ee4e23baa894e4 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Thu, 18 Jul 2024 13:01:01 +0200 Subject: [PATCH 068/135] add verify steps for quarkus/keycloak_realm --- molecule/quarkus/verify.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/molecule/quarkus/verify.yml b/molecule/quarkus/verify.yml index 24c27aa..63769dc 100644 --- a/molecule/quarkus/verify.yml +++ b/molecule/quarkus/verify.yml @@ -89,7 +89,7 @@ - name: Verify token api call ansible.builtin.uri: - url: "https://localhost:8443/realms/master/protocol/openid-connect/token" + url: "https://instance:8443/realms/master/protocol/openid-connect/token" method: POST body: "client_id=admin-cli&username=admin&password={{ keycloak_admin_password }}&grant_type=password" validate_certs: no @@ -100,7 +100,7 @@ - name: "Get Clients" ansible.builtin.uri: - url: "https://localhost:8443/admin/realms/TestRealm/clients" + url: "https://instance:8443/admin/realms/TestRealm/clients" headers: validate_certs: false Authorization: "Bearer {{ keycloak_auth_response.json.access_token }}" @@ -112,7 +112,7 @@ - name: "Get Client {{ keycloak_client_uuid }}" ansible.builtin.uri: - url: "https://localhost:8443/admin/realms/TestRealm/clients/{{ keycloak_client_uuid }}" + url: "https://instance:8443/admin/realms/TestRealm/clients/{{ keycloak_client_uuid }}" headers: validate_certs: false Authorization: "Bearer {{ keycloak_auth_response.json.access_token }}" @@ -120,7 +120,7 @@ - name: "Get Client roles" ansible.builtin.uri: - url: "https://localhost:8443/admin/realms/TestRealm/clients/{{ keycloak_client_uuid }}/roles" + url: "https://instance:8443/admin/realms/TestRealm/clients/{{ keycloak_client_uuid }}/roles" headers: validate_certs: false Authorization: "Bearer {{ keycloak_auth_response.json.access_token }}" From 34caf6a490e3762f2d6864c00683594ae89f9ecc Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 31 Jul 2024 17:18:30 +0200 Subject: [PATCH 069/135] add wait_for_port number parameter --- roles/keycloak_quarkus/README.md | 1 + roles/keycloak_quarkus/defaults/main.yml | 1 + roles/keycloak_quarkus/meta/argument_specs.yml | 4 ++++ roles/keycloak_quarkus/templates/keycloak.service.j2 | 2 +- 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 1fb0cd1..01163cd 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -98,6 +98,7 @@ Role Defaults |`keycloak_quarkus_db_enabled`| Enable auto configuration for database backend | `True` if `keycloak_quarkus_ha_enabled` is True, else `False` | |`keycloak_quarkus_jgroups_port`| jgroups cluster tcp port | `7800` | |`keycloak_quarkus_systemd_wait_for_port` | Whether systemd unit should wait for keycloak port before returning | `{{ keycloak_quarkus_ha_enabled }}` | +|`keycloak_quarkus_systemd_wait_for_port_number`| Which port the systemd unit should wait for | `{{ keycloak_quarkus_https_port }}` | |`keycloak_quarkus_systemd_wait_for_log` | Whether systemd unit should wait for service to be up in logs | `false` | |`keycloak_quarkus_systemd_wait_for_timeout`| How long to wait for service to be alive (seconds) | `60` | |`keycloak_quarkus_systemd_wait_for_delay`| Activation delay for service systemd unit (seconds) | `10` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 4f6deb3..f99e9ff 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -74,6 +74,7 @@ keycloak_quarkus_ha_discovery: "TCPPING" ### Enable database configuration, must be enabled when HA is configured keycloak_quarkus_db_enabled: "{{ keycloak_quarkus_ha_enabled }}" keycloak_quarkus_systemd_wait_for_port: "{{ keycloak_quarkus_ha_enabled }}" +keycloak_quarkus_systemd_wait_for_port_number: "{{ keycloak_quarkus_https_port }}" keycloak_quarkus_systemd_wait_for_log: false keycloak_quarkus_systemd_wait_for_timeout: 60 keycloak_quarkus_systemd_wait_for_delay: 10 diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 978ca7e..2c68460 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -386,6 +386,10 @@ argument_specs: description: 'Whether systemd unit should wait for keycloak port before returning' default: "{{ keycloak_quarkus_ha_enabled }}" type: "bool" + keycloak_quarkus_systemd_wait_for_port_number: + default: "{{ keycloak_quarkus_https_port }}" + description: "The port the systemd unit should wait for, by default the https port" + type: "int" keycloak_quarkus_systemd_wait_for_log: description: 'Whether systemd unit should wait for service to be up in logs' default: false diff --git a/roles/keycloak_quarkus/templates/keycloak.service.j2 b/roles/keycloak_quarkus/templates/keycloak.service.j2 index 9cabb69..96207ed 100644 --- a/roles/keycloak_quarkus/templates/keycloak.service.j2 +++ b/roles/keycloak_quarkus/templates/keycloak.service.j2 @@ -23,7 +23,7 @@ RestartSec={{ keycloak_quarkus_service_restartsec }} AmbientCapabilities=CAP_NET_BIND_SERVICE {% endif %} {% if keycloak_quarkus_systemd_wait_for_port %} -ExecStartPost=/usr/bin/timeout {{ keycloak_quarkus_systemd_wait_for_timeout }} sh -c 'while ! ss -H -t -l -n sport = :{{ keycloak_quarkus_https_port }} | grep -q "^LISTEN.*:{{ keycloak_quarkus_https_port }}"; do sleep 1; done && /bin/sleep {{ keycloak_quarkus_systemd_wait_for_delay }}' +ExecStartPost=/usr/bin/timeout {{ keycloak_quarkus_systemd_wait_for_timeout }} sh -c 'while ! ss -H -t -l -n sport = :{{ keycloak_quarkus_systemd_wait_for_port_number }} | grep -q "^LISTEN.*:{{ keycloak_quarkus_systemd_wait_for_port_number }}"; do sleep 1; done && /bin/sleep {{ keycloak_quarkus_systemd_wait_for_delay }}' {% endif %} {% if keycloak_quarkus_systemd_wait_for_log %} ExecStartPost=/usr/bin/timeout {{ keycloak_quarkus_systemd_wait_for_timeout }} sh -c 'cat {{ keycloak.log.file }} | sed "/Profile.*activated/ q" && /bin/sleep {{ keycloak_quarkus_systemd_wait_for_delay }}' From f1702572059585bb97537deaa6a1096230314055 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 24 Sep 2024 09:21:10 +0200 Subject: [PATCH 070/135] Add local download path --- .ansible-lint | 1 + roles/keycloak_quarkus/README.md | 1 + roles/keycloak_quarkus/defaults/main.yml | 1 + roles/keycloak_quarkus/handlers/main.yml | 2 +- .../keycloak_quarkus/meta/argument_specs.yml | 18 ++++---- roles/keycloak_quarkus/tasks/bootstrapped.yml | 4 +- roles/keycloak_quarkus/tasks/config_store.yml | 6 +-- roles/keycloak_quarkus/tasks/install.yml | 13 ++---- roles/keycloak_quarkus/tasks/main.yml | 2 +- roles/keycloak_quarkus/tasks/prereqs.yml | 42 ++++++++++++++++++- .../keycloak_quarkus/tasks/rebuild_config.yml | 2 +- roles/keycloak_quarkus/tasks/restart.yml | 2 +- 12 files changed, 67 insertions(+), 27 deletions(-) diff --git a/.ansible-lint b/.ansible-lint index 9c2702e..e6607a4 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -34,6 +34,7 @@ warn_list: skip_list: - vars_should_not_be_used - file_is_small_enough + - file_has_valid_name - name[template] - var-naming[no-role-prefix] diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 01163cd..00c785f 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -37,6 +37,7 @@ Role Defaults |`keycloak_quarkus_offline_install` | Perform an offline install | `False`| |`keycloak_quarkus_dest`| Installation root path | `/opt/keycloak` | |`keycloak_quarkus_download_url` | Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}` | +|`keycloak_quarkus_download_path`| Path local to controller for offline/download of install archives | `{{ lookup('env', 'PWD') }}` | #### Service configuration diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index f99e9ff..b029fee 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -15,6 +15,7 @@ keycloak_quarkus_java_home: keycloak_quarkus_dest: /opt/keycloak keycloak_quarkus_home: "{{ keycloak_quarkus_installdir }}" keycloak_quarkus_config_dir: "{{ keycloak_quarkus_home }}/conf" +keycloak_quarkus_download_path: "{{ lookup('env', 'PWD') }}" keycloak_quarkus_start_dev: false keycloak_quarkus_service_user: keycloak keycloak_quarkus_service_group: keycloak diff --git a/roles/keycloak_quarkus/handlers/main.yml b/roles/keycloak_quarkus/handlers/main.yml index 5851a71..37d65c5 100644 --- a/roles/keycloak_quarkus/handlers/main.yml +++ b/roles/keycloak_quarkus/handlers/main.yml @@ -10,7 +10,7 @@ ansible.builtin.include_tasks: file: "{{ keycloak_quarkus_restart_strategy if keycloak_quarkus_ha_enabled else 'restart.yml' }}" listen: "restart keycloak" -- name: "Print deprecation warning" +- name: "Display deprecation warning" ansible.builtin.fail: msg: "Deprecation warning: you are using the deprecated variable '{{ deprecated_variable | d('NotSet') }}', check docs on how to upgrade." failed_when: false diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 2c68460..34ec365 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -56,15 +56,15 @@ argument_specs: default: false description: "Ensure firewalld is running and configure keycloak ports" type: "bool" - keycloak_service_restart_always: + keycloak_quarkus_service_restart_always: default: false description: "systemd restart always behavior of service; takes precedence over keycloak_service_restart_on_failure if true" type: "bool" - keycloak_service_restart_on_failure: + keycloak_quarkus_service_restart_on_failure: default: false description: "systemd restart on-failure behavior of service" type: "bool" - keycloak_service_restartsec: + keycloak_quarkus_service_restartsec: default: "10s" description: "systemd RestartSec for service" type: "str" @@ -457,6 +457,14 @@ argument_specs: description: "Number of attempts for successful health check before failing" default: 25 type: 'int' + keycloak_quarkus_show_deprecation_warnings: + default: true + description: "Whether or not deprecation warnings should be shown" + type: "bool" + keycloak_quarkus_download_path: + description: "Path local to controller for offline/download of install archives" + default: "{{ lookup('env', 'PWD') }}" + type: "str" downstream: options: rhbk_version: @@ -487,10 +495,6 @@ argument_specs: default: false description: "Perform an offline install" type: "bool" - keycloak_quarkus_show_deprecation_warnings: - default: true - description: "Whether deprecation warnings should be shown" - type: "bool" rhbk_service_name: default: "rhbk" description: "systemd service name for Red Hat Build of Keycloak" diff --git a/roles/keycloak_quarkus/tasks/bootstrapped.yml b/roles/keycloak_quarkus/tasks/bootstrapped.yml index 46278ab..3cbc5c4 100644 --- a/roles/keycloak_quarkus/tasks/bootstrapped.yml +++ b/roles/keycloak_quarkus/tasks/bootstrapped.yml @@ -1,5 +1,5 @@ --- -- name: Write ansible custom facts +- name: Save ansible custom facts become: true ansible.builtin.template: src: keycloak.fact.j2 @@ -8,7 +8,7 @@ vars: bootstrapped: true -- name: Re-read custom facts +- name: Refresh custom facts ansible.builtin.setup: filter: ansible_local diff --git a/roles/keycloak_quarkus/tasks/config_store.yml b/roles/keycloak_quarkus/tasks/config_store.yml index 40acc65..2d8b39e 100644 --- a/roles/keycloak_quarkus/tasks/config_store.yml +++ b/roles/keycloak_quarkus/tasks/config_store.yml @@ -8,7 +8,7 @@ - name: "Initialize empty configuration key store" become: true # keytool doesn't allow creating an empty key store, so this is a hacky way around it - ansible.builtin.shell: | + ansible.builtin.shell: | # noqa blocked_modules shell is necessary here set -o nounset # abort on unbound variable set -o pipefail # do not hide errors within pipes set -o errexit # abort on nonzero exit status @@ -19,7 +19,7 @@ creates: "{{ keycloak_quarkus_config_key_store_file }}" - name: "Set configuration key store using keytool" - ansible.builtin.shell: | + ansible.builtin.shell: | # noqa blocked_modules shell is necessary here set -o nounset # abort on unbound variable set -o pipefail # do not hide errors within pipes @@ -36,7 +36,7 @@ fi echo {{ item.value | quote }} | keytool -noprompt -importpass -alias {{ item.key | quote }} -keystore {{ keycloak_quarkus_config_key_store_file | quote }} -storepass {{ keycloak_quarkus_config_key_store_password | quote }} -storetype PKCS12 - with_items: "{{ store_items }}" + loop: "{{ store_items }}" no_log: true become: true changed_when: true diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index 7745031..809e07e 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -8,6 +8,7 @@ - keycloak_quarkus_archive is defined - keycloak_quarkus_download_url is defined - keycloak_quarkus_version is defined + - local_path is defined quiet: true - name: Check for an existing deployment @@ -52,14 +53,6 @@ register: archive_path ## download to controller -- name: Check local download archive path - ansible.builtin.stat: - path: "{{ lookup('env', 'PWD') }}" - register: local_path - delegate_to: localhost - run_once: true - become: false - - name: Download keycloak archive ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user url: "{{ keycloak_quarkus_download_url }}" @@ -244,7 +237,7 @@ no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" notify: "{{ ['rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or not item.restart else [] }}" -- name: "Upload local maven providers" +- name: "Copy maven providers" ansible.builtin.copy: src: "{{ local_path.stat.path }}/{{ item.id }}.jar" dest: "{{ keycloak.home }}/providers/{{ item.id }}.jar" @@ -256,7 +249,7 @@ when: item.maven is defined no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" -- name: "Upload local providers" +- name: "Copy providers" ansible.builtin.copy: src: "{{ item.local_path }}" dest: "{{ keycloak.home }}/providers/{{ item.id }}.jar" diff --git a/roles/keycloak_quarkus/tasks/main.yml b/roles/keycloak_quarkus/tasks/main.yml index 2dadf61..bb86b2c 100644 --- a/roles/keycloak_quarkus/tasks/main.yml +++ b/roles/keycloak_quarkus/tasks/main.yml @@ -91,7 +91,7 @@ register: keycloak_service_status changed_when: false -- name: "Trigger bootstrapped notification: remove `keycloak_quarkus_admin_user[_pass]` env vars" +- name: "Notify to remove `keycloak_quarkus_admin_user[_pass]` env vars" when: - not ansible_local.keycloak.general.bootstrapped | default(false) | bool # it was not bootstrapped prior to the current role's execution - keycloak_service_status.status.ActiveState == "active" # but it is now diff --git a/roles/keycloak_quarkus/tasks/prereqs.yml b/roles/keycloak_quarkus/tasks/prereqs.yml index 503b308..ae2dacf 100644 --- a/roles/keycloak_quarkus/tasks/prereqs.yml +++ b/roles/keycloak_quarkus/tasks/prereqs.yml @@ -43,10 +43,50 @@ vars: packages_list: "{{ keycloak_quarkus_prereq_package_list }}" +- name: Check local download archive path + ansible.builtin.stat: + path: "{{ keycloak_quarkus_download_path }}" + register: local_path + delegate_to: localhost + run_once: true + become: false + +- name: Validate local download path + ansible.builtin.assert: + that: + - local_path.stat.exists + - local_path.stat.readable + - keycloak_quarkus_offline_install or local_path.stat.writeable + quiet: true + fail_msg: "Defined controller path for downloading resource is incorrect: {{ keycloak_quarkus_download_path }}" + success_msg: "Will download resource to controller path: {{ local_path.stat.path }}" + delegate_to: localhost + run_once: true + +- name: Check downloaded archive if offline + ansible.builtin.stat: + path: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" + when: keycloak_quarkus_offline_install + register: local_archive_path_check + delegate_to: localhost + run_once: true + +- name: Validate local downloaded archive if offline + ansible.builtin.assert: + that: + - local_archive_path_check.stat.exists + - local_archive_path_check.stat.readable + quiet: true + fail_msg: "Configured for offline install but install archive not found at: {{ local_archive_path_check.stat.path }}" + success_msg: "Will install offline with expected archive: {{ local_archive_path_check.stat.path }}" + when: keycloak_quarkus_offline_install + delegate_to: localhost + run_once: true + - name: "Validate keytool" when: keycloak_quarkus_config_key_store_password | length > 0 block: - - name: "Attempt to run keytool" + - name: "Check run keytool" changed_when: false ansible.builtin.command: keytool -help register: keytool_check diff --git a/roles/keycloak_quarkus/tasks/rebuild_config.yml b/roles/keycloak_quarkus/tasks/rebuild_config.yml index 5d2247d..ac78504 100644 --- a/roles/keycloak_quarkus/tasks/rebuild_config.yml +++ b/roles/keycloak_quarkus/tasks/rebuild_config.yml @@ -1,7 +1,7 @@ --- # cf. https://www.keycloak.org/server/configuration#_optimize_the_keycloak_startup - name: "Rebuild {{ keycloak.service_name }} config" - ansible.builtin.shell: | + ansible.builtin.shell: | # noqa blocked_modules shell is necessary here {{ keycloak.home }}/bin/kc.sh build environment: PATH: "{{ keycloak_quarkus_java_home | default(keycloak_quarkus_pkg_java_home, true) }}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" diff --git a/roles/keycloak_quarkus/tasks/restart.yml b/roles/keycloak_quarkus/tasks/restart.yml index 2a1fabd..61356d5 100644 --- a/roles/keycloak_quarkus/tasks/restart.yml +++ b/roles/keycloak_quarkus/tasks/restart.yml @@ -16,7 +16,7 @@ delay: "{{ keycloak_quarkus_restart_health_check_delay }}" when: internal_force_health_check | default(keycloak_quarkus_restart_health_check) -- name: Pause to give distributed ispn caches time to (re-)replicate back onto first host +- name: Wait to give distributed ispn caches time to (re-)replicate back onto first host ansible.builtin.pause: seconds: "{{ keycloak_quarkus_restart_pause }}" when: From eb66d4a412528bfb4cb560a7b3ae730e97f8225f Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Thu, 26 Sep 2024 10:22:28 +0200 Subject: [PATCH 071/135] update prereqs validation --- .ansible-lint | 1 + .gitignore | 1 + roles/keycloak_quarkus/tasks/prereqs.yml | 8 ++++---- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.ansible-lint b/.ansible-lint index e6607a4..8e4b5ca 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -30,6 +30,7 @@ warn_list: - schema[meta] - key-order[task] - blocked_modules + - run-once[task] skip_list: - vars_should_not_be_used diff --git a/.gitignore b/.gitignore index f2ffea4..ce41aef 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ docs/_build/ changelogs/.plugin-cache.yaml *.pem *.key +*.p12 diff --git a/roles/keycloak_quarkus/tasks/prereqs.yml b/roles/keycloak_quarkus/tasks/prereqs.yml index ae2dacf..0835aab 100644 --- a/roles/keycloak_quarkus/tasks/prereqs.yml +++ b/roles/keycloak_quarkus/tasks/prereqs.yml @@ -58,8 +58,8 @@ - local_path.stat.readable - keycloak_quarkus_offline_install or local_path.stat.writeable quiet: true - fail_msg: "Defined controller path for downloading resource is incorrect: {{ keycloak_quarkus_download_path }}" - success_msg: "Will download resource to controller path: {{ local_path.stat.path }}" + fail_msg: "Defined controller path for downloading resources is incorrect or unreadable: {{ keycloak_quarkus_download_path }}" + success_msg: "Will download resource to controller path: {{ keycloak_quarkus_download_path }}" delegate_to: localhost run_once: true @@ -77,8 +77,8 @@ - local_archive_path_check.stat.exists - local_archive_path_check.stat.readable quiet: true - fail_msg: "Configured for offline install but install archive not found at: {{ local_archive_path_check.stat.path }}" - success_msg: "Will install offline with expected archive: {{ local_archive_path_check.stat.path }}" + fail_msg: "Configured for offline install but install archive not found at: {{ local_path.stat.path }}/{{ keycloak.bundle }}" + success_msg: "Will install offline with expected archive: {{ local_path.stat.path }}/{{ keycloak.bundle }}" when: keycloak_quarkus_offline_install delegate_to: localhost run_once: true From fc0ee5a89654324ead9a8a66809fa0418d28d122 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Thu, 26 Sep 2024 10:22:42 +0200 Subject: [PATCH 072/135] refactor default test for keycloak-quarkus offline --- molecule/default/converge.yml | 97 +++++++++++++++-------------------- molecule/default/prepare.yml | 28 +++++----- molecule/default/verify.yml | 69 +------------------------ 3 files changed, 57 insertions(+), 137 deletions(-) diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index 07cd724..bb8c552 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -2,61 +2,46 @@ - name: Converge hosts: all vars: + keycloak_quarkus_show_deprecation_warnings: false + keycloak_quarkus_admin_pass: "remembertochangeme" keycloak_admin_password: "remembertochangeme" - keycloak_jvm_package: java-11-openjdk-headless - keycloak_modcluster_enabled: True - keycloak_modcluster_urls: - - host: myhost1 - port: 16667 - - host: myhost2 - port: 16668 - keycloak_jboss_port_offset: 10 - keycloak_log_target: /tmp/keycloak + keycloak_quarkus_host: instance + keycloak_quarkus_log: file + keycloak_quarkus_log_level: debug + keycloak_quarkus_log_target: /tmp/keycloak + keycloak_quarkus_start_dev: True + keycloak_quarkus_proxy_mode: none + keycloak_quarkus_offline_install: true + keycloak_quarkus_download_path: /tmp/keycloak/ roles: - - role: keycloak - tasks: - - name: Keycloak Realm Role - ansible.builtin.include_role: - name: keycloak_realm - vars: - keycloak_client_default_roles: - - TestRoleAdmin - - TestRoleUser - keycloak_client_users: - - username: TestUser - password: password - client_roles: - - client: TestClient - role: TestRoleUser - realm: "{{ keycloak_realm }}" - - username: TestAdmin - password: password - client_roles: - - client: TestClient - role: TestRoleUser - realm: "{{ keycloak_realm }}" - - client: TestClient - role: TestRoleAdmin - realm: "{{ keycloak_realm }}" - keycloak_realm: TestRealm - keycloak_clients: - - name: TestClient - roles: "{{ keycloak_client_default_roles }}" - realm: "{{ keycloak_realm }}" - public_client: "{{ keycloak_client_public }}" - web_origins: "{{ keycloak_client_web_origins }}" - users: "{{ keycloak_client_users }}" - client_id: TestClient - attributes: - post.logout.redirect.uris: '/public/logout' - pre_tasks: - - name: "Retrieve assets server from env" - ansible.builtin.set_fact: - assets_server: "{{ lookup('env', 'MIDDLEWARE_DOWNLOAD_RELEASE_SERVER_URL') }}" - - - name: "Set offline when assets server from env is defined" - ansible.builtin.set_fact: - sso_offline_install: True - when: - - assets_server is defined - - assets_server | length > 0 + - role: keycloak_quarkus + - role: keycloak_realm + keycloak_context: '' + keycloak_client_default_roles: + - TestRoleAdmin + - TestRoleUser + keycloak_client_users: + - username: TestUser + password: password + client_roles: + - client: TestClient + role: TestRoleUser + realm: "{{ keycloak_realm }}" + - username: TestAdmin + password: password + client_roles: + - client: TestClient + role: TestRoleUser + realm: "{{ keycloak_realm }}" + - client: TestClient + role: TestRoleAdmin + realm: "{{ keycloak_realm }}" + keycloak_realm: TestRealm + keycloak_clients: + - name: TestClient + roles: "{{ keycloak_client_default_roles }}" + realm: "{{ keycloak_realm }}" + public_client: "{{ keycloak_client_public }}" + web_origins: "{{ keycloak_client_web_origins }}" + users: "{{ keycloak_client_users }}" + client_id: TestClient diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml index b707f6c..a50dfa4 100644 --- a/molecule/default/prepare.yml +++ b/molecule/default/prepare.yml @@ -12,18 +12,18 @@ - "{{ assets_server }}/sso/7.6.0/rh-sso-7.6.0-server-dist.zip" - "{{ assets_server }}/sso/7.6.1/rh-sso-7.6.1-patch.zip" - - name: Install JDK8 - become: yes - ansible.builtin.yum: - name: - - java-1.8.0-openjdk - state: present - when: ansible_facts['os_family'] == "RedHat" + - name: Create controller directory for downloads + ansible.builtin.file: # noqa risky-file-permissions delegated, uses controller host user + path: /tmp/keycloak + state: directory + mode: '0750' + delegate_to: localhost + run_once: true - - name: Install JDK8 - become: yes - ansible.builtin.apt: - name: - - openjdk-8-jdk - state: present - when: ansible_facts['os_family'] == "Debian" + - name: Download keycloak archive to controller directory + ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user + url: https://github.com/keycloak/keycloak/releases/download/24.0.4/keycloak-24.0.4.zip + dest: /tmp/keycloak + mode: '0640' + delegate_to: localhost + run_once: true diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml index 39e94c5..b880105 100644 --- a/molecule/default/verify.yml +++ b/molecule/default/verify.yml @@ -3,10 +3,7 @@ hosts: all vars: keycloak_admin_password: "remembertochangeme" - keycloak_jvm_package: java-11-openjdk-headless - keycloak_uri: "http://localhost:{{ 8080 + ( keycloak_jboss_port_offset | default(0) ) }}" - keycloak_management_port: "http://localhost:{{ 9990 + ( keycloak_jboss_port_offset | default(0) ) }}" - keycloak_jboss_port_offset: 10 + keycloak_uri: "http://localhost:8080" tasks: - name: Populate service facts ansible.builtin.service_facts: @@ -15,16 +12,9 @@ that: - ansible_facts.services["keycloak.service"]["state"] == "running" - ansible_facts.services["keycloak.service"]["status"] == "enabled" - - name: Verify we are running on requested jvm # noqa blocked_modules command-instead-of-module - ansible.builtin.shell: | - set -o pipefail - ps -ef | grep '/etc/alternatives/jre_11/' | grep -v grep - args: - executable: /bin/bash - changed_when: no - name: Verify token api call ansible.builtin.uri: - url: "{{ keycloak_uri }}/auth/realms/master/protocol/openid-connect/token" + url: "{{ keycloak_uri }}/realms/master/protocol/openid-connect/token" method: POST body: "client_id=admin-cli&username=admin&password={{ keycloak_admin_password }}&grant_type=password" validate_certs: no @@ -32,58 +22,3 @@ until: keycloak_auth_response.status == 200 retries: 2 delay: 2 - - name: Fetch openid-connect config - ansible.builtin.uri: - url: "{{ keycloak_uri }}/auth/realms/TestRealm/.well-known/openid-configuration" - method: GET - validate_certs: no - status_code: 200 - register: keycloak_openid_config - - name: Verify expected config - ansible.builtin.assert: - that: - - keycloak_openid_config.json.registration_endpoint == 'http://localhost:8080/auth/realms/TestRealm/clients-registrations/openid-connect' - - name: Get test realm clients - ansible.builtin.uri: - url: "{{ keycloak_uri }}/auth/admin/realms/TestRealm/clients" - method: GET - validate_certs: no - status_code: 200 - headers: - Authorization: "Bearer {{ keycloak_auth_response.json.access_token }}" - register: keycloak_query_clients - - name: Verify expected config - ansible.builtin.assert: - that: - - (keycloak_query_clients.json | selectattr('clientId','equalto','TestClient') | first)["attributes"]["post.logout.redirect.uris"] == '/public/logout' - - name: "Privilege escalation as some files/folders may requires it" - become: yes - block: - - name: Check log folder - ansible.builtin.stat: - path: "/tmp/keycloak" - register: keycloak_log_folder - - name: Check that keycloak log folder exists and is a link - ansible.builtin.assert: - that: - - keycloak_log_folder.stat.exists - - not keycloak_log_folder.stat.isdir - - keycloak_log_folder.stat.islnk - - name: Check log file - ansible.builtin.stat: - path: "/tmp/keycloak/server.log" - register: keycloak_log_file - - name: Check if keycloak file exists - ansible.builtin.assert: - that: - - keycloak_log_file.stat.exists - - not keycloak_log_file.stat.isdir - - name: Check default log folder - ansible.builtin.stat: - path: "/var/log/keycloak" - register: keycloak_default_log_folder - failed_when: false - - name: Check that default keycloak log folder doesn't exist - ansible.builtin.assert: - that: - - not keycloak_default_log_folder.stat.exists From b2edea87771fb59d46f2bb2ecc81fc91fd02c8ed Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Thu, 26 Sep 2024 10:22:54 +0200 Subject: [PATCH 073/135] linter --- molecule/quarkus/prepare.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/molecule/quarkus/prepare.yml b/molecule/quarkus/prepare.yml index 459bafa..21a0f30 100644 --- a/molecule/quarkus/prepare.yml +++ b/molecule/quarkus/prepare.yml @@ -12,14 +12,14 @@ - name: Create certificate request ansible.builtin.command: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=instance' delegate_to: localhost - changed_when: False + changed_when: false - name: Create vault directory become: true ansible.builtin.file: state: directory path: "/opt/keycloak/vault" - mode: 0755 + mode: '0755' - name: Make sure a jre is available (for keytool to prepare keystore) delegate_to: localhost @@ -41,4 +41,4 @@ ansible.builtin.copy: src: keystore.p12 dest: /opt/keycloak/vault/keystore.p12 - mode: 0444 + mode: '0444' From c8021f3102b156f077054a714a36648d01e03deb Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Thu, 26 Sep 2024 08:52:04 +0000 Subject: [PATCH 074/135] Update changelog for release 2.4.2 Signed-off-by: ansible-middleware-core --- CHANGELOG.rst | 13 +++++++++++++ changelogs/changelog.yaml | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1aed396..7283c76 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,19 @@ middleware\_automation.keycloak Release Notes This changelog describes changes after version 0.2.6. +v2.4.2 +====== + +Minor Changes +------------- + +- New parameter ``keycloak_quarkus_download_path`` `#239 `_ + +Bugfixes +-------- + +- Add wait_for_port number parameter `#237 `_ + v2.4.1 ====== diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index c841535..41a4076 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -590,3 +590,17 @@ releases: fragments: - v2.4.1-devel_summary.yaml release_date: '2024-07-02' + 2.4.2: + changes: + bugfixes: + - 'Add wait_for_port number parameter `#237 `_ + + ' + minor_changes: + - 'New parameter ``keycloak_quarkus_download_path`` `#239 `_ + + ' + fragments: + - 237.yaml + - 239.yaml + release_date: '2024-09-26' From ac4511bea9e39263fec9eec12dadb19feb356db3 Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Thu, 26 Sep 2024 08:52:17 +0000 Subject: [PATCH 075/135] Bump version to 2.4.3 --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index 2091f28..a782353 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "2.4.2" +version: "2.4.3" readme: README.md authors: - Romain Pelisse From c6bb815979a875c6b0bdb1384c3d9c9c81ab25c9 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Mon, 14 Oct 2024 10:07:44 +0200 Subject: [PATCH 076/135] update keycloak modules --- .../identity/keycloak/keycloak.py | 1251 +++++++++++++++-- plugins/modules/keycloak_client.py | 289 +++- plugins/modules/keycloak_role.py | 97 +- plugins/modules/keycloak_user_federation.py | 257 ++-- 4 files changed, 1605 insertions(+), 289 deletions(-) diff --git a/plugins/module_utils/identity/keycloak/keycloak.py b/plugins/module_utils/identity/keycloak/keycloak.py index 15b6657..128b0fe 100644 --- a/plugins/module_utils/identity/keycloak/keycloak.py +++ b/plugins/module_utils/identity/keycloak/keycloak.py @@ -9,6 +9,7 @@ __metaclass__ = type import json import traceback +import copy from ansible.module_utils.urls import open_url from ansible.module_utils.six.moves.urllib.parse import urlencode, quote @@ -18,6 +19,7 @@ from ansible.module_utils.common.text.converters import to_native, to_text URL_REALM_INFO = "{url}/realms/{realm}" URL_REALMS = "{url}/admin/realms" URL_REALM = "{url}/admin/realms/{realm}" +URL_REALM_KEYS_METADATA = "{url}/admin/realms/{realm}/keys" URL_TOKEN = "{url}/realms/{realm}/protocol/openid-connect/token" URL_CLIENT = "{url}/admin/realms/{realm}/clients/{id}" @@ -27,6 +29,9 @@ URL_CLIENT_ROLES = "{url}/admin/realms/{realm}/clients/{id}/roles" URL_CLIENT_ROLE = "{url}/admin/realms/{realm}/clients/{id}/roles/{name}" URL_CLIENT_ROLE_COMPOSITES = "{url}/admin/realms/{realm}/clients/{id}/roles/{name}/composites" +URL_CLIENT_ROLE_SCOPE_CLIENTS = "{url}/admin/realms/{realm}/clients/{id}/scope-mappings/clients/{scopeid}" +URL_CLIENT_ROLE_SCOPE_REALM = "{url}/admin/realms/{realm}/clients/{id}/scope-mappings/realm" + URL_REALM_ROLES = "{url}/admin/realms/{realm}/roles" URL_REALM_ROLE = "{url}/admin/realms/{realm}/roles/{name}" URL_REALM_ROLEMAPPINGS = "{url}/admin/realms/{realm}/users/{id}/role-mappings/realm" @@ -49,16 +54,36 @@ URL_CLIENTSCOPE = "{url}/admin/realms/{realm}/client-scopes/{id}" URL_CLIENTSCOPE_PROTOCOLMAPPERS = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models" URL_CLIENTSCOPE_PROTOCOLMAPPER = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models/{mapper_id}" +URL_DEFAULT_CLIENTSCOPES = "{url}/admin/realms/{realm}/default-default-client-scopes" +URL_DEFAULT_CLIENTSCOPE = "{url}/admin/realms/{realm}/default-default-client-scopes/{id}" +URL_OPTIONAL_CLIENTSCOPES = "{url}/admin/realms/{realm}/default-optional-client-scopes" +URL_OPTIONAL_CLIENTSCOPE = "{url}/admin/realms/{realm}/default-optional-client-scopes/{id}" + +URL_CLIENT_DEFAULT_CLIENTSCOPES = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes" +URL_CLIENT_DEFAULT_CLIENTSCOPE = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes/{id}" +URL_CLIENT_OPTIONAL_CLIENTSCOPES = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes" +URL_CLIENT_OPTIONAL_CLIENTSCOPE = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes/{id}" + URL_CLIENT_GROUP_ROLEMAPPINGS = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}" URL_CLIENT_GROUP_ROLEMAPPINGS_AVAILABLE = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}/available" URL_CLIENT_GROUP_ROLEMAPPINGS_COMPOSITE = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}/composite" URL_USERS = "{url}/admin/realms/{realm}/users" +URL_USER = "{url}/admin/realms/{realm}/users/{id}" +URL_USER_ROLE_MAPPINGS = "{url}/admin/realms/{realm}/users/{id}/role-mappings" +URL_USER_REALM_ROLE_MAPPINGS = "{url}/admin/realms/{realm}/users/{id}/role-mappings/realm" +URL_USER_CLIENTS_ROLE_MAPPINGS = "{url}/admin/realms/{realm}/users/{id}/role-mappings/clients" +URL_USER_CLIENT_ROLE_MAPPINGS = "{url}/admin/realms/{realm}/users/{id}/role-mappings/clients/{client_id}" +URL_USER_GROUPS = "{url}/admin/realms/{realm}/users/{id}/groups" +URL_USER_GROUP = "{url}/admin/realms/{realm}/users/{id}/groups/{group_id}" + URL_CLIENT_SERVICE_ACCOUNT_USER = "{url}/admin/realms/{realm}/clients/{id}/service-account-user" URL_CLIENT_USER_ROLEMAPPINGS = "{url}/admin/realms/{realm}/users/{id}/role-mappings/clients/{client}" URL_CLIENT_USER_ROLEMAPPINGS_AVAILABLE = "{url}/admin/realms/{realm}/users/{id}/role-mappings/clients/{client}/available" URL_CLIENT_USER_ROLEMAPPINGS_COMPOSITE = "{url}/admin/realms/{realm}/users/{id}/role-mappings/clients/{client}/composite" +URL_REALM_GROUP_ROLEMAPPINGS = "{url}/admin/realms/{realm}/groups/{group}/role-mappings/realm" + URL_CLIENTSECRET = "{url}/admin/realms/{realm}/clients/{id}/client-secret" URL_AUTHENTICATION_FLOWS = "{url}/admin/realms/{realm}/authentication/flows" @@ -71,6 +96,9 @@ URL_AUTHENTICATION_EXECUTION_CONFIG = "{url}/admin/realms/{realm}/authentication URL_AUTHENTICATION_EXECUTION_RAISE_PRIORITY = "{url}/admin/realms/{realm}/authentication/executions/{id}/raise-priority" URL_AUTHENTICATION_EXECUTION_LOWER_PRIORITY = "{url}/admin/realms/{realm}/authentication/executions/{id}/lower-priority" URL_AUTHENTICATION_CONFIG = "{url}/admin/realms/{realm}/authentication/config/{id}" +URL_AUTHENTICATION_REGISTER_REQUIRED_ACTION = "{url}/admin/realms/{realm}/authentication/register-required-action" +URL_AUTHENTICATION_REQUIRED_ACTIONS = "{url}/admin/realms/{realm}/authentication/required-actions" +URL_AUTHENTICATION_REQUIRED_ACTIONS_ALIAS = "{url}/admin/realms/{realm}/authentication/required-actions/{alias}" URL_IDENTITY_PROVIDERS = "{url}/admin/realms/{realm}/identity-provider/instances" URL_IDENTITY_PROVIDER = "{url}/admin/realms/{realm}/identity-provider/instances/{alias}" @@ -80,6 +108,23 @@ URL_IDENTITY_PROVIDER_MAPPER = "{url}/admin/realms/{realm}/identity-provider/ins URL_COMPONENTS = "{url}/admin/realms/{realm}/components" URL_COMPONENT = "{url}/admin/realms/{realm}/components/{id}" +URL_AUTHZ_AUTHORIZATION_SCOPE = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/scope/{id}" +URL_AUTHZ_AUTHORIZATION_SCOPES = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/scope" + +# This URL is used for: +# - Querying client authorization permissions +# - Removing client authorization permissions +URL_AUTHZ_POLICIES = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/policy" +URL_AUTHZ_POLICY = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/policy/{id}" + +URL_AUTHZ_PERMISSION = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/permission/{permission_type}/{id}" +URL_AUTHZ_PERMISSIONS = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/permission/{permission_type}" + +URL_AUTHZ_RESOURCES = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/resource" + +URL_AUTHZ_CUSTOM_POLICY = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/policy/{policy_type}" +URL_AUTHZ_CUSTOM_POLICIES = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/policy" + def keycloak_argument_spec(): """ @@ -140,8 +185,7 @@ def get_token(module_params): 'password': auth_password, } # Remove empty items, for instance missing client_secret - payload = dict( - (k, v) for k, v in temp_payload.items() if v is not None) + payload = {k: v for k, v in temp_payload.items() if v is not None} try: r = json.loads(to_native(open_url(auth_url, method='POST', validate_certs=validate_certs, http_agent=http_agent, timeout=connection_timeout, @@ -194,24 +238,30 @@ def is_struct_included(struct1, struct2, exclude=None): Return True if all element of dict 1 are present in dict 2, return false otherwise. """ if isinstance(struct1, list) and isinstance(struct2, list): + if not struct1 and not struct2: + return True for item1 in struct1: if isinstance(item1, (list, dict)): for item2 in struct2: - if not is_struct_included(item1, item2, exclude): - return False + if is_struct_included(item1, item2, exclude): + break + else: + return False else: if item1 not in struct2: return False return True elif isinstance(struct1, dict) and isinstance(struct2, dict): + if not struct1 and not struct2: + return True try: for key in struct1: if not (exclude and key in exclude): if not is_struct_included(struct1[key], struct2[key], exclude): return False - return True except KeyError: return False + return True elif isinstance(struct1, bool) and isinstance(struct2, bool): return struct1 == struct2 else: @@ -247,8 +297,39 @@ class KeycloakAPI(object): if e.code == 404: return None else: - self.module.fail_json(msg='Could not obtain realm %s: %s' % (realm, str(e)), - exception=traceback.format_exc()) + self.fail_open_url(e, msg='Could not obtain realm %s: %s' % (realm, str(e)), + exception=traceback.format_exc()) + except ValueError as e: + self.module.fail_json(msg='API returned incorrect JSON when trying to obtain realm %s: %s' % (realm, str(e)), + exception=traceback.format_exc()) + except Exception as e: + self.module.fail_json(msg='Could not obtain realm %s: %s' % (realm, str(e)), + exception=traceback.format_exc()) + + def get_realm_keys_metadata_by_id(self, realm='master'): + """Obtain realm public info by id + + :param realm: realm id + + :return: None, or a 'KeysMetadataRepresentation' + (https://www.keycloak.org/docs-api/latest/rest-api/index.html#KeysMetadataRepresentation) + -- a dict containing the keys 'active' and 'keys', the former containing a mapping + from algorithms to key-ids, the latter containing a list of dicts with key + information. + """ + realm_keys_metadata_url = URL_REALM_KEYS_METADATA.format(url=self.baseurl, realm=realm) + + try: + return json.loads(to_native(open_url(realm_keys_metadata_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs).read())) + + except HTTPError as e: + if e.code == 404: + return None + else: + self.fail_open_url(e, msg='Could not obtain realm %s: %s' % (realm, str(e)), + exception=traceback.format_exc()) except ValueError as e: self.module.fail_json(msg='API returned incorrect JSON when trying to obtain realm %s: %s' % (realm, str(e)), exception=traceback.format_exc()) @@ -272,8 +353,8 @@ class KeycloakAPI(object): if e.code == 404: return None else: - self.module.fail_json(msg='Could not obtain realm %s: %s' % (realm, str(e)), - exception=traceback.format_exc()) + self.fail_open_url(e, msg='Could not obtain realm %s: %s' % (realm, str(e)), + exception=traceback.format_exc()) except ValueError as e: self.module.fail_json(msg='API returned incorrect JSON when trying to obtain realm %s: %s' % (realm, str(e)), exception=traceback.format_exc()) @@ -293,8 +374,8 @@ class KeycloakAPI(object): return open_url(realm_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(realmrep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not update realm %s: %s' % (realm, str(e)), - exception=traceback.format_exc()) + self.fail_open_url(e, msg='Could not update realm %s: %s' % (realm, str(e)), + exception=traceback.format_exc()) def create_realm(self, realmrep): """ Create a realm in keycloak @@ -307,8 +388,8 @@ class KeycloakAPI(object): return open_url(realm_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(realmrep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not create realm %s: %s' % (realmrep['id'], str(e)), - exception=traceback.format_exc()) + self.fail_open_url(e, msg='Could not create realm %s: %s' % (realmrep['id'], str(e)), + exception=traceback.format_exc()) def delete_realm(self, realm="master"): """ Delete a realm from Keycloak @@ -322,8 +403,8 @@ class KeycloakAPI(object): return open_url(realm_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not delete realm %s: %s' % (realm, str(e)), - exception=traceback.format_exc()) + self.fail_open_url(e, msg='Could not delete realm %s: %s' % (realm, str(e)), + exception=traceback.format_exc()) def get_clients(self, realm='master', filter=None): """ Obtains client representations for clients in a realm @@ -344,7 +425,7 @@ class KeycloakAPI(object): self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of clients for realm %s: %s' % (realm, str(e))) except Exception as e: - self.module.fail_json(msg='Could not obtain list of clients for realm %s: %s' + self.fail_open_url(e, msg='Could not obtain list of clients for realm %s: %s' % (realm, str(e))) def get_client_by_clientid(self, client_id, realm='master'): @@ -377,7 +458,7 @@ class KeycloakAPI(object): if e.code == 404: return None else: - self.module.fail_json(msg='Could not obtain client %s for realm %s: %s' + self.fail_open_url(e, msg='Could not obtain client %s for realm %s: %s' % (id, realm, str(e))) except ValueError as e: self.module.fail_json(msg='API returned incorrect JSON when trying to obtain client %s for realm %s: %s' @@ -412,7 +493,7 @@ class KeycloakAPI(object): return open_url(client_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(clientrep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not update client %s in realm %s: %s' + self.fail_open_url(e, msg='Could not update client %s in realm %s: %s' % (id, realm, str(e))) def create_client(self, clientrep, realm="master"): @@ -427,7 +508,7 @@ class KeycloakAPI(object): return open_url(client_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(clientrep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not create client %s in realm %s: %s' + self.fail_open_url(e, msg='Could not create client %s in realm %s: %s' % (clientrep['clientId'], realm, str(e))) def delete_client(self, id, realm="master"): @@ -443,7 +524,7 @@ class KeycloakAPI(object): return open_url(client_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not delete client %s in realm %s: %s' + self.fail_open_url(e, msg='Could not delete client %s in realm %s: %s' % (id, realm, str(e))) def get_client_roles_by_id(self, cid, realm="master"): @@ -459,7 +540,7 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except Exception as e: - self.module.fail_json(msg="Could not fetch rolemappings for client %s in realm %s: %s" + self.fail_open_url(e, msg="Could not fetch rolemappings for client %s in realm %s: %s" % (cid, realm, str(e))) def get_client_role_id_by_name(self, cid, name, realm="master"): @@ -494,12 +575,12 @@ class KeycloakAPI(object): if rid == role['id']: return role except Exception as e: - self.module.fail_json(msg="Could not fetch rolemappings for client %s in group %s, realm %s: %s" + self.fail_open_url(e, msg="Could not fetch rolemappings for client %s in group %s, realm %s: %s" % (cid, gid, realm, str(e))) return None def get_client_group_available_rolemappings(self, gid, cid, realm="master"): - """ Fetch the available role of a client in a specified goup on the Keycloak server. + """ Fetch the available role of a client in a specified group on the Keycloak server. :param gid: ID of the group from which to obtain the rolemappings. :param cid: ID of the client from which to obtain the rolemappings. @@ -512,7 +593,7 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except Exception as e: - self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s" + self.fail_open_url(e, msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s" % (cid, gid, realm, str(e))) def get_client_group_composite_rolemappings(self, gid, cid, realm="master"): @@ -529,7 +610,7 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except Exception as e: - self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s" + self.fail_open_url(e, msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s" % (cid, gid, realm, str(e))) def get_role_by_id(self, rid, realm="master"): @@ -545,7 +626,7 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except Exception as e: - self.module.fail_json(msg="Could not fetch role for id %s in realm %s: %s" + self.fail_open_url(e, msg="Could not fetch role for id %s in realm %s: %s" % (rid, realm, str(e))) def get_client_roles_by_id_composite_rolemappings(self, rid, cid, realm="master"): @@ -562,7 +643,7 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except Exception as e: - self.module.fail_json(msg="Could not fetch role for id %s and cid %s in realm %s: %s" + self.fail_open_url(e, msg="Could not fetch role for id %s and cid %s in realm %s: %s" % (rid, cid, realm, str(e))) def add_client_roles_by_id_composite_rolemapping(self, rid, roles_rep, realm="master"): @@ -578,11 +659,43 @@ class KeycloakAPI(object): open_url(available_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(roles_rep), validate_certs=self.validate_certs, timeout=self.connection_timeout) except Exception as e: - self.module.fail_json(msg="Could not assign roles to composite role %s and realm %s: %s" + self.fail_open_url(e, msg="Could not assign roles to composite role %s and realm %s: %s" % (rid, realm, str(e))) + def add_group_realm_rolemapping(self, gid, role_rep, realm="master"): + """ Add the specified realm role to specified group on the Keycloak server. + + :param gid: ID of the group to add the role mapping. + :param role_rep: Representation of the role to assign. + :param realm: Realm from which to obtain the rolemappings. + :return: None. + """ + url = URL_REALM_GROUP_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, group=gid) + try: + open_url(url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep), + validate_certs=self.validate_certs, timeout=self.connection_timeout) + except Exception as e: + self.fail_open_url(e, msg="Could add realm role mappings for group %s, realm %s: %s" + % (gid, realm, str(e))) + + def delete_group_realm_rolemapping(self, gid, role_rep, realm="master"): + """ Delete the specified realm role from the specified group on the Keycloak server. + + :param gid: ID of the group from which to obtain the rolemappings. + :param role_rep: Representation of the role to assign. + :param realm: Realm from which to obtain the rolemappings. + :return: None. + """ + url = URL_REALM_GROUP_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, group=gid) + try: + open_url(url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep), + validate_certs=self.validate_certs, timeout=self.connection_timeout) + except Exception as e: + self.fail_open_url(e, msg="Could not delete realm role mappings for group %s, realm %s: %s" + % (gid, realm, str(e))) + def add_group_rolemapping(self, gid, cid, role_rep, realm="master"): - """ Fetch the composite role of a client in a specified goup on the Keycloak server. + """ Fetch the composite role of a client in a specified group on the Keycloak server. :param gid: ID of the group from which to obtain the rolemappings. :param cid: ID of the client from which to obtain the rolemappings. @@ -595,7 +708,7 @@ class KeycloakAPI(object): open_url(available_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep), validate_certs=self.validate_certs, timeout=self.connection_timeout) except Exception as e: - self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s" + self.fail_open_url(e, msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s" % (cid, gid, realm, str(e))) def delete_group_rolemapping(self, gid, cid, role_rep, realm="master"): @@ -612,7 +725,7 @@ class KeycloakAPI(object): open_url(available_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep), validate_certs=self.validate_certs, timeout=self.connection_timeout) except Exception as e: - self.module.fail_json(msg="Could not delete available rolemappings for client %s in group %s, realm %s: %s" + self.fail_open_url(e, msg="Could not delete available rolemappings for client %s in group %s, realm %s: %s" % (cid, gid, realm, str(e))) def get_client_user_rolemapping_by_id(self, uid, cid, rid, realm='master'): @@ -633,7 +746,7 @@ class KeycloakAPI(object): if rid == role['id']: return role except Exception as e: - self.module.fail_json(msg="Could not fetch rolemappings for client %s and user %s, realm %s: %s" + self.fail_open_url(e, msg="Could not fetch rolemappings for client %s and user %s, realm %s: %s" % (cid, uid, realm, str(e))) return None @@ -651,7 +764,7 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except Exception as e: - self.module.fail_json(msg="Could not fetch effective rolemappings for client %s and user %s, realm %s: %s" + self.fail_open_url(e, msg="Could not fetch effective rolemappings for client %s and user %s, realm %s: %s" % (cid, uid, realm, str(e))) def get_client_user_composite_rolemappings(self, uid, cid, realm="master"): @@ -668,7 +781,7 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except Exception as e: - self.module.fail_json(msg="Could not fetch available rolemappings for user %s of realm %s: %s" + self.fail_open_url(e, msg="Could not fetch available rolemappings for user %s of realm %s: %s" % (uid, realm, str(e))) def get_realm_user_rolemapping_by_id(self, uid, rid, realm='master'): @@ -688,7 +801,7 @@ class KeycloakAPI(object): if rid == role['id']: return role except Exception as e: - self.module.fail_json(msg="Could not fetch rolemappings for user %s, realm %s: %s" + self.fail_open_url(e, msg="Could not fetch rolemappings for user %s, realm %s: %s" % (uid, realm, str(e))) return None @@ -705,7 +818,7 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except Exception as e: - self.module.fail_json(msg="Could not fetch available rolemappings for user %s of realm %s: %s" + self.fail_open_url(e, msg="Could not fetch available rolemappings for user %s of realm %s: %s" % (uid, realm, str(e))) def get_realm_user_composite_rolemappings(self, uid, realm="master"): @@ -721,7 +834,7 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except Exception as e: - self.module.fail_json(msg="Could not fetch effective rolemappings for user %s, realm %s: %s" + self.fail_open_url(e, msg="Could not fetch effective rolemappings for user %s, realm %s: %s" % (uid, realm, str(e))) def get_user_by_username(self, username, realm="master"): @@ -734,13 +847,21 @@ class KeycloakAPI(object): users_url = URL_USERS.format(url=self.baseurl, realm=realm) users_url += '?username=%s&exact=true' % username try: - return json.loads(to_native(open_url(users_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout, - validate_certs=self.validate_certs).read())) + userrep = None + users = json.loads(to_native(open_url(users_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs).read())) + for user in users: + if user['username'] == username: + userrep = user + break + return userrep + except ValueError as e: self.module.fail_json(msg='API returned incorrect JSON when trying to obtain the user for realm %s and username %s: %s' % (realm, username, str(e))) except Exception as e: - self.module.fail_json(msg='Could not obtain the user for realm %s and username %s: %s' + self.fail_open_url(e, msg='Could not obtain the user for realm %s and username %s: %s' % (realm, username, str(e))) def get_service_account_user_by_client_id(self, client_id, realm="master"): @@ -754,13 +875,14 @@ class KeycloakAPI(object): service_account_user_url = URL_CLIENT_SERVICE_ACCOUNT_USER.format(url=self.baseurl, realm=realm, id=cid) try: - return json.loads(to_native(open_url(service_account_user_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout, + return json.loads(to_native(open_url(service_account_user_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except ValueError as e: self.module.fail_json(msg='API returned incorrect JSON when trying to obtain the service-account-user for realm %s and client_id %s: %s' % (realm, client_id, str(e))) except Exception as e: - self.module.fail_json(msg='Could not obtain the service-account-user for realm %s and client_id %s: %s' + self.fail_open_url(e, msg='Could not obtain the service-account-user for realm %s and client_id %s: %s' % (realm, client_id, str(e))) def add_user_rolemapping(self, uid, cid, role_rep, realm="master"): @@ -778,7 +900,7 @@ class KeycloakAPI(object): open_url(user_realm_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep), validate_certs=self.validate_certs, timeout=self.connection_timeout) except Exception as e: - self.module.fail_json(msg="Could not map roles to userId %s for realm %s and roles %s: %s" + self.fail_open_url(e, msg="Could not map roles to userId %s for realm %s and roles %s: %s" % (uid, realm, json.dumps(role_rep), str(e))) else: user_client_rolemappings_url = URL_CLIENT_USER_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=uid, client=cid) @@ -786,7 +908,7 @@ class KeycloakAPI(object): open_url(user_client_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep), validate_certs=self.validate_certs, timeout=self.connection_timeout) except Exception as e: - self.module.fail_json(msg="Could not map roles to userId %s for client %s, realm %s and roles %s: %s" + self.fail_open_url(e, msg="Could not map roles to userId %s for client %s, realm %s and roles %s: %s" % (cid, uid, realm, json.dumps(role_rep), str(e))) def delete_user_rolemapping(self, uid, cid, role_rep, realm="master"): @@ -804,7 +926,7 @@ class KeycloakAPI(object): open_url(user_realm_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep), validate_certs=self.validate_certs, timeout=self.connection_timeout) except Exception as e: - self.module.fail_json(msg="Could not remove roles %s from userId %s, realm %s: %s" + self.fail_open_url(e, msg="Could not remove roles %s from userId %s, realm %s: %s" % (json.dumps(role_rep), uid, realm, str(e))) else: user_client_rolemappings_url = URL_CLIENT_USER_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=uid, client=cid) @@ -812,7 +934,7 @@ class KeycloakAPI(object): open_url(user_client_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep), validate_certs=self.validate_certs, timeout=self.connection_timeout) except Exception as e: - self.module.fail_json(msg="Could not remove roles %s for client %s from userId %s, realm %s: %s" + self.fail_open_url(e, msg="Could not remove roles %s for client %s from userId %s, realm %s: %s" % (json.dumps(role_rep), cid, uid, realm, str(e))) def get_client_templates(self, realm='master'): @@ -830,7 +952,7 @@ class KeycloakAPI(object): self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of client templates for realm %s: %s' % (realm, str(e))) except Exception as e: - self.module.fail_json(msg='Could not obtain list of client templates for realm %s: %s' + self.fail_open_url(e, msg='Could not obtain list of client templates for realm %s: %s' % (realm, str(e))) def get_client_template_by_id(self, id, realm='master'): @@ -849,7 +971,7 @@ class KeycloakAPI(object): self.module.fail_json(msg='API returned incorrect JSON when trying to obtain client templates %s for realm %s: %s' % (id, realm, str(e))) except Exception as e: - self.module.fail_json(msg='Could not obtain client template %s for realm %s: %s' + self.fail_open_url(e, msg='Could not obtain client template %s for realm %s: %s' % (id, realm, str(e))) def get_client_template_by_name(self, name, realm='master'): @@ -892,7 +1014,7 @@ class KeycloakAPI(object): return open_url(url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(clienttrep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not update client template %s in realm %s: %s' + self.fail_open_url(e, msg='Could not update client template %s in realm %s: %s' % (id, realm, str(e))) def create_client_template(self, clienttrep, realm="master"): @@ -907,7 +1029,7 @@ class KeycloakAPI(object): return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(clienttrep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not create client template %s in realm %s: %s' + self.fail_open_url(e, msg='Could not create client template %s in realm %s: %s' % (clienttrep['clientId'], realm, str(e))) def delete_client_template(self, id, realm="master"): @@ -923,7 +1045,7 @@ class KeycloakAPI(object): return open_url(url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not delete client template %s in realm %s: %s' + self.fail_open_url(e, msg='Could not delete client template %s in realm %s: %s' % (id, realm, str(e))) def get_clientscopes(self, realm="master"): @@ -941,7 +1063,7 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except Exception as e: - self.module.fail_json(msg="Could not fetch list of clientscopes in realm %s: %s" + self.fail_open_url(e, msg="Could not fetch list of clientscopes in realm %s: %s" % (realm, str(e))) def get_clientscope_by_clientscopeid(self, cid, realm="master"): @@ -963,7 +1085,7 @@ class KeycloakAPI(object): if e.code == 404: return None else: - self.module.fail_json(msg="Could not fetch clientscope %s in realm %s: %s" + self.fail_open_url(e, msg="Could not fetch clientscope %s in realm %s: %s" % (cid, realm, str(e))) except Exception as e: self.module.fail_json(msg="Could not clientscope group %s in realm %s: %s" @@ -1004,7 +1126,7 @@ class KeycloakAPI(object): return open_url(clientscopes_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(clientscoperep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg="Could not create clientscope %s in realm %s: %s" + self.fail_open_url(e, msg="Could not create clientscope %s in realm %s: %s" % (clientscoperep['name'], realm, str(e))) def update_clientscope(self, clientscoperep, realm="master"): @@ -1020,7 +1142,7 @@ class KeycloakAPI(object): data=json.dumps(clientscoperep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not update clientscope %s in realm %s: %s' + self.fail_open_url(e, msg='Could not update clientscope %s in realm %s: %s' % (clientscoperep['name'], realm, str(e))) def delete_clientscope(self, name=None, cid=None, realm="master"): @@ -1058,7 +1180,7 @@ class KeycloakAPI(object): validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg="Unable to delete clientscope %s: %s" % (cid, str(e))) + self.fail_open_url(e, msg="Unable to delete clientscope %s: %s" % (cid, str(e))) def get_clientscope_protocolmappers(self, cid, realm="master"): """ Fetch the name and ID of all clientscopes on the Keycloak server. @@ -1076,7 +1198,7 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except Exception as e: - self.module.fail_json(msg="Could not fetch list of protocolmappers in realm %s: %s" + self.fail_open_url(e, msg="Could not fetch list of protocolmappers in realm %s: %s" % (realm, str(e))) def get_clientscope_protocolmapper_by_protocolmapperid(self, pid, cid, realm="master"): @@ -1100,7 +1222,7 @@ class KeycloakAPI(object): if e.code == 404: return None else: - self.module.fail_json(msg="Could not fetch protocolmapper %s in realm %s: %s" + self.fail_open_url(e, msg="Could not fetch protocolmapper %s in realm %s: %s" % (pid, realm, str(e))) except Exception as e: self.module.fail_json(msg="Could not fetch protocolmapper %s in realm %s: %s" @@ -1143,7 +1265,7 @@ class KeycloakAPI(object): return open_url(protocolmappers_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(mapper_rep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg="Could not create protocolmapper %s in realm %s: %s" + self.fail_open_url(e, msg="Could not create protocolmapper %s in realm %s: %s" % (mapper_rep['name'], realm, str(e))) def update_clientscope_protocolmappers(self, cid, mapper_rep, realm="master"): @@ -1160,9 +1282,134 @@ class KeycloakAPI(object): data=json.dumps(mapper_rep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not update protocolmappers for clientscope %s in realm %s: %s' + self.fail_open_url(e, msg='Could not update protocolmappers for clientscope %s in realm %s: %s' % (mapper_rep, realm, str(e))) + def get_default_clientscopes(self, realm, client_id=None): + """Fetch the name and ID of all clientscopes on the Keycloak server. + + To fetch the full data of the client scope, make a subsequent call to + get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return. + + :param realm: Realm in which the clientscope resides. + :param client_id: The client in which the clientscope resides. + :return The default clientscopes of this realm or client + """ + url = URL_DEFAULT_CLIENTSCOPES if client_id is None else URL_CLIENT_DEFAULT_CLIENTSCOPES + return self._get_clientscopes_of_type(realm, url, 'default', client_id) + + def get_optional_clientscopes(self, realm, client_id=None): + """Fetch the name and ID of all clientscopes on the Keycloak server. + + To fetch the full data of the client scope, make a subsequent call to + get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return. + + :param realm: Realm in which the clientscope resides. + :param client_id: The client in which the clientscope resides. + :return The optional clientscopes of this realm or client + """ + url = URL_OPTIONAL_CLIENTSCOPES if client_id is None else URL_CLIENT_OPTIONAL_CLIENTSCOPES + return self._get_clientscopes_of_type(realm, url, 'optional', client_id) + + def _get_clientscopes_of_type(self, realm, url_template, scope_type, client_id=None): + """Fetch the name and ID of all clientscopes on the Keycloak server. + + To fetch the full data of the client scope, make a subsequent call to + get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return. + + :param realm: Realm in which the clientscope resides. + :param url_template the template for the right type + :param scope_type this can be either optional or default + :param client_id: The client in which the clientscope resides. + :return The clientscopes of the specified type of this realm + """ + if client_id is None: + clientscopes_url = url_template.format(url=self.baseurl, realm=realm) + try: + return json.loads(to_native(open_url(clientscopes_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) + except Exception as e: + self.fail_open_url(e, msg="Could not fetch list of %s clientscopes in realm %s: %s" % (scope_type, realm, str(e))) + else: + cid = self.get_client_id(client_id=client_id, realm=realm) + clientscopes_url = url_template.format(url=self.baseurl, realm=realm, cid=cid) + try: + return json.loads(to_native(open_url(clientscopes_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) + except Exception as e: + self.fail_open_url(e, msg="Could not fetch list of %s clientscopes in client %s: %s" % (scope_type, client_id, clientscopes_url)) + + def _decide_url_type_clientscope(self, client_id=None, scope_type="default"): + """Decides which url to use. + :param scope_type this can be either optional or default + :param client_id: The client in which the clientscope resides. + """ + if client_id is None: + if scope_type == "default": + return URL_DEFAULT_CLIENTSCOPE + if scope_type == "optional": + return URL_OPTIONAL_CLIENTSCOPE + else: + if scope_type == "default": + return URL_CLIENT_DEFAULT_CLIENTSCOPE + if scope_type == "optional": + return URL_CLIENT_OPTIONAL_CLIENTSCOPE + + def add_default_clientscope(self, id, realm="master", client_id=None): + """Add a client scope as default either on realm or client level. + + :param id: Client scope Id. + :param realm: Realm in which the clientscope resides. + :param client_id: The client in which the clientscope resides. + """ + self._action_type_clientscope(id, client_id, "default", realm, 'add') + + def add_optional_clientscope(self, id, realm="master", client_id=None): + """Add a client scope as optional either on realm or client level. + + :param id: Client scope Id. + :param realm: Realm in which the clientscope resides. + :param client_id: The client in which the clientscope resides. + """ + self._action_type_clientscope(id, client_id, "optional", realm, 'add') + + def delete_default_clientscope(self, id, realm="master", client_id=None): + """Remove a client scope as default either on realm or client level. + + :param id: Client scope Id. + :param realm: Realm in which the clientscope resides. + :param client_id: The client in which the clientscope resides. + """ + self._action_type_clientscope(id, client_id, "default", realm, 'delete') + + def delete_optional_clientscope(self, id, realm="master", client_id=None): + """Remove a client scope as optional either on realm or client level. + + :param id: Client scope Id. + :param realm: Realm in which the clientscope resides. + :param client_id: The client in which the clientscope resides. + """ + self._action_type_clientscope(id, client_id, "optional", realm, 'delete') + + def _action_type_clientscope(self, id=None, client_id=None, scope_type="default", realm="master", action='add'): + """ Delete or add a clientscope of type. + :param name: The name of the clientscope. A lookup will be performed to retrieve the clientscope ID. + :param client_id: The ID of the clientscope (preferred to name). + :param scope_type 'default' or 'optional' + :param realm: The realm in which this group resides, default "master". + """ + cid = None if client_id is None else self.get_client_id(client_id=client_id, realm=realm) + # should have a good cid by here. + clientscope_type_url = self._decide_url_type_clientscope(client_id, scope_type).format(realm=realm, id=id, cid=cid, url=self.baseurl) + try: + method = 'PUT' if action == "add" else 'DELETE' + return open_url(clientscope_type_url, method=method, http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + validate_certs=self.validate_certs) + + except Exception as e: + place = 'realm' if client_id is None else 'client ' + client_id + self.fail_open_url(e, msg="Unable to %s %s clientscope %s @ %s : %s" % (action, scope_type, id, place, str(e))) + def create_clientsecret(self, id, realm="master"): """ Generate a new client secret by id @@ -1173,14 +1420,15 @@ class KeycloakAPI(object): clientsecret_url = URL_CLIENTSECRET.format(url=self.baseurl, realm=realm, id=id) try: - return json.loads(to_native(open_url(clientsecret_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout, + return json.loads(to_native(open_url(clientsecret_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except HTTPError as e: if e.code == 404: return None else: - self.module.fail_json(msg='Could not obtain clientsecret of client %s for realm %s: %s' + self.fail_open_url(e, msg='Could not obtain clientsecret of client %s for realm %s: %s' % (id, realm, str(e))) except Exception as e: self.module.fail_json(msg='Could not obtain clientsecret of client %s for realm %s: %s' @@ -1196,14 +1444,15 @@ class KeycloakAPI(object): clientsecret_url = URL_CLIENTSECRET.format(url=self.baseurl, realm=realm, id=id) try: - return json.loads(to_native(open_url(clientsecret_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout, + return json.loads(to_native(open_url(clientsecret_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except HTTPError as e: if e.code == 404: return None else: - self.module.fail_json(msg='Could not obtain clientsecret of client %s for realm %s: %s' + self.fail_open_url(e, msg='Could not obtain clientsecret of client %s for realm %s: %s' % (id, realm, str(e))) except Exception as e: self.module.fail_json(msg='Could not obtain clientsecret of client %s for realm %s: %s' @@ -1223,7 +1472,7 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except Exception as e: - self.module.fail_json(msg="Could not fetch list of groups in realm %s: %s" + self.fail_open_url(e, msg="Could not fetch list of groups in realm %s: %s" % (realm, str(e))) def get_group_by_groupid(self, gid, realm="master"): @@ -1244,7 +1493,7 @@ class KeycloakAPI(object): if e.code == 404: return None else: - self.module.fail_json(msg="Could not fetch group %s in realm %s: %s" + self.fail_open_url(e, msg="Could not fetch group %s in realm %s: %s" % (gid, realm, str(e))) except Exception as e: self.module.fail_json(msg="Could not fetch group %s in realm %s: %s" @@ -1339,7 +1588,7 @@ class KeycloakAPI(object): def get_subgroup_direct_parent(self, parents, realm="master", children_to_resolve=None): """ Get keycloak direct parent group API object for a given chain of parents. - To succesfully work the API for subgroups we actually dont need + To successfully work the API for subgroups we actually don't need to "walk the whole tree" for nested groups but only need to know the ID for the direct predecessor of current subgroup. This method will guarantee us this information getting there with @@ -1391,7 +1640,7 @@ class KeycloakAPI(object): return open_url(groups_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(grouprep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg="Could not create group %s in realm %s: %s" + self.fail_open_url(e, msg="Could not create group %s in realm %s: %s" % (grouprep['name'], realm, str(e))) def create_subgroup(self, parents, grouprep, realm="master"): @@ -1419,7 +1668,7 @@ class KeycloakAPI(object): return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(grouprep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg="Could not create subgroup %s for parent group %s in realm %s: %s" + self.fail_open_url(e, msg="Could not create subgroup %s for parent group %s in realm %s: %s" % (grouprep['name'], parent_id, realm, str(e))) def update_group(self, grouprep, realm="master"): @@ -1434,7 +1683,7 @@ class KeycloakAPI(object): return open_url(group_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(grouprep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not update group %s in realm %s: %s' + self.fail_open_url(e, msg='Could not update group %s in realm %s: %s' % (grouprep['name'], realm, str(e))) def delete_group(self, name=None, groupid=None, realm="master"): @@ -1471,7 +1720,7 @@ class KeycloakAPI(object): return open_url(group_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg="Unable to delete group %s: %s" % (groupid, str(e))) + self.fail_open_url(e, msg="Unable to delete group %s: %s" % (groupid, str(e))) def get_realm_roles(self, realm='master'): """ Obtains role representations for roles in a realm @@ -1488,7 +1737,7 @@ class KeycloakAPI(object): self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of roles for realm %s: %s' % (realm, str(e))) except Exception as e: - self.module.fail_json(msg='Could not obtain list of roles for realm %s: %s' + self.fail_open_url(e, msg='Could not obtain list of roles for realm %s: %s' % (realm, str(e))) def get_realm_role(self, name, realm='master'): @@ -1498,7 +1747,7 @@ class KeycloakAPI(object): :param name: Name of the role to fetch. :param realm: Realm in which the role resides; default 'master'. """ - role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=quote(name)) + role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=quote(name, safe='')) try: return json.loads(to_native(open_url(role_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) @@ -1506,7 +1755,7 @@ class KeycloakAPI(object): if e.code == 404: return None else: - self.module.fail_json(msg='Could not fetch role %s in realm %s: %s' + self.fail_open_url(e, msg='Could not fetch role %s in realm %s: %s' % (name, realm, str(e))) except Exception as e: self.module.fail_json(msg='Could not fetch role %s in realm %s: %s' @@ -1520,10 +1769,13 @@ class KeycloakAPI(object): """ roles_url = URL_REALM_ROLES.format(url=self.baseurl, realm=realm) try: + if "composites" in rolerep: + keycloak_compatible_composites = self.convert_role_composites(rolerep["composites"]) + rolerep["composites"] = keycloak_compatible_composites return open_url(roles_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(rolerep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not create role %s in realm %s: %s' + self.fail_open_url(e, msg='Could not create role %s in realm %s: %s' % (rolerep['name'], realm, str(e))) def update_realm_role(self, rolerep, realm='master'): @@ -1532,26 +1784,138 @@ class KeycloakAPI(object): :param rolerep: A RoleRepresentation of the updated role. :return HTTPResponse object on success """ - role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=quote(rolerep['name'])) + role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=quote(rolerep['name']), safe='') try: - return open_url(role_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, - data=json.dumps(rolerep), validate_certs=self.validate_certs) + composites = None + if "composites" in rolerep: + composites = copy.deepcopy(rolerep["composites"]) + del rolerep["composites"] + role_response = open_url(role_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(rolerep), validate_certs=self.validate_certs) + if composites is not None: + self.update_role_composites(rolerep=rolerep, composites=composites, realm=realm) + return role_response except Exception as e: - self.module.fail_json(msg='Could not update role %s in realm %s: %s' + self.fail_open_url(e, msg='Could not update role %s in realm %s: %s' % (rolerep['name'], realm, str(e))) + def get_role_composites(self, rolerep, clientid=None, realm='master'): + composite_url = '' + try: + if clientid is not None: + client = self.get_client_by_clientid(client_id=clientid, realm=realm) + cid = client['id'] + composite_url = URL_CLIENT_ROLE_COMPOSITES.format(url=self.baseurl, realm=realm, id=cid, name=quote(rolerep["name"], safe='')) + else: + composite_url = URL_REALM_ROLE_COMPOSITES.format(url=self.baseurl, realm=realm, name=quote(rolerep["name"], safe='')) + # Get existing composites + return json.loads(to_native(open_url( + composite_url, + method='GET', + http_agent=self.http_agent, + headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs).read())) + except Exception as e: + self.fail_open_url(e, msg='Could not get role %s composites in realm %s: %s' + % (rolerep['name'], realm, str(e))) + + def create_role_composites(self, rolerep, composites, clientid=None, realm='master'): + composite_url = '' + try: + if clientid is not None: + client = self.get_client_by_clientid(client_id=clientid, realm=realm) + cid = client['id'] + composite_url = URL_CLIENT_ROLE_COMPOSITES.format(url=self.baseurl, realm=realm, id=cid, name=quote(rolerep["name"], safe='')) + else: + composite_url = URL_REALM_ROLE_COMPOSITES.format(url=self.baseurl, realm=realm, name=quote(rolerep["name"], safe='')) + # Get existing composites + # create new composites + return open_url(composite_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(composites), validate_certs=self.validate_certs) + except Exception as e: + self.fail_open_url(e, msg='Could not create role %s composites in realm %s: %s' + % (rolerep['name'], realm, str(e))) + + def delete_role_composites(self, rolerep, composites, clientid=None, realm='master'): + composite_url = '' + try: + if clientid is not None: + client = self.get_client_by_clientid(client_id=clientid, realm=realm) + cid = client['id'] + composite_url = URL_CLIENT_ROLE_COMPOSITES.format(url=self.baseurl, realm=realm, id=cid, name=quote(rolerep["name"], safe='')) + else: + composite_url = URL_REALM_ROLE_COMPOSITES.format(url=self.baseurl, realm=realm, name=quote(rolerep["name"], safe='')) + # Get existing composites + # create new composites + return open_url(composite_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(composites), validate_certs=self.validate_certs) + except Exception as e: + self.fail_open_url(e, msg='Could not create role %s composites in realm %s: %s' + % (rolerep['name'], realm, str(e))) + + def update_role_composites(self, rolerep, composites, clientid=None, realm='master'): + # Get existing composites + existing_composites = self.get_role_composites(rolerep=rolerep, clientid=clientid, realm=realm) + composites_to_be_created = [] + composites_to_be_deleted = [] + for composite in composites: + composite_found = False + existing_composite_client = None + for existing_composite in existing_composites: + if existing_composite["clientRole"]: + existing_composite_client = self.get_client_by_id(existing_composite["containerId"], realm=realm) + if ("client_id" in composite + and composite['client_id'] is not None + and existing_composite_client["clientId"] == composite["client_id"] + and composite["name"] == existing_composite["name"]): + composite_found = True + break + else: + if (("client_id" not in composite or composite['client_id'] is None) + and composite["name"] == existing_composite["name"]): + composite_found = True + break + if (not composite_found and ('state' not in composite or composite['state'] == 'present')): + if "client_id" in composite and composite['client_id'] is not None: + client_roles = self.get_client_roles(clientid=composite['client_id'], realm=realm) + for client_role in client_roles: + if client_role['name'] == composite['name']: + composites_to_be_created.append(client_role) + break + else: + realm_role = self.get_realm_role(name=composite["name"], realm=realm) + composites_to_be_created.append(realm_role) + elif composite_found and 'state' in composite and composite['state'] == 'absent': + if "client_id" in composite and composite['client_id'] is not None: + client_roles = self.get_client_roles(clientid=composite['client_id'], realm=realm) + for client_role in client_roles: + if client_role['name'] == composite['name']: + composites_to_be_deleted.append(client_role) + break + else: + realm_role = self.get_realm_role(name=composite["name"], realm=realm) + composites_to_be_deleted.append(realm_role) + + if len(composites_to_be_created) > 0: + # create new composites + self.create_role_composites(rolerep=rolerep, composites=composites_to_be_created, clientid=clientid, realm=realm) + if len(composites_to_be_deleted) > 0: + # delete new composites + self.delete_role_composites(rolerep=rolerep, composites=composites_to_be_deleted, clientid=clientid, realm=realm) + def delete_realm_role(self, name, realm='master'): """ Delete a realm role. :param name: The name of the role. :param realm: The realm in which this role resides, default "master". """ - role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=quote(name)) + role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=quote(name, safe='')) try: return open_url(role_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Unable to delete role %s in realm %s: %s' + self.fail_open_url(e, msg='Unable to delete role %s in realm %s: %s' % (name, realm, str(e))) def get_client_roles(self, clientid, realm='master'): @@ -1574,7 +1938,7 @@ class KeycloakAPI(object): self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of roles for client %s in realm %s: %s' % (clientid, realm, str(e))) except Exception as e: - self.module.fail_json(msg='Could not obtain list of roles for client %s in realm %s: %s' + self.fail_open_url(e, msg='Could not obtain list of roles for client %s in realm %s: %s' % (clientid, realm, str(e))) def get_client_role(self, name, clientid, realm='master'): @@ -1590,7 +1954,7 @@ class KeycloakAPI(object): if cid is None: self.module.fail_json(msg='Could not find client %s in realm %s' % (clientid, realm)) - role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=quote(name)) + role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=quote(name, safe='')) try: return json.loads(to_native(open_url(role_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) @@ -1598,7 +1962,7 @@ class KeycloakAPI(object): if e.code == 404: return None else: - self.module.fail_json(msg='Could not fetch role %s in client %s of realm %s: %s' + self.fail_open_url(e, msg='Could not fetch role %s in client %s of realm %s: %s' % (name, clientid, realm, str(e))) except Exception as e: self.module.fail_json(msg='Could not fetch role %s for client %s in realm %s: %s' @@ -1618,12 +1982,30 @@ class KeycloakAPI(object): % (clientid, realm)) roles_url = URL_CLIENT_ROLES.format(url=self.baseurl, realm=realm, id=cid) try: + if "composites" in rolerep: + keycloak_compatible_composites = self.convert_role_composites(rolerep["composites"]) + rolerep["composites"] = keycloak_compatible_composites return open_url(roles_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(rolerep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not create role %s for client %s in realm %s: %s' + self.fail_open_url(e, msg='Could not create role %s for client %s in realm %s: %s' % (rolerep['name'], clientid, realm, str(e))) + def convert_role_composites(self, composites): + keycloak_compatible_composites = { + 'client': {}, + 'realm': [] + } + for composite in composites: + if 'state' not in composite or composite['state'] == 'present': + if "client_id" in composite and composite["client_id"] is not None: + if composite["client_id"] not in keycloak_compatible_composites["client"]: + keycloak_compatible_composites["client"][composite["client_id"]] = [] + keycloak_compatible_composites["client"][composite["client_id"]].append(composite["name"]) + else: + keycloak_compatible_composites["realm"].append(composite["name"]) + return keycloak_compatible_composites + def update_client_role(self, rolerep, clientid, realm="master"): """ Update an existing client role. @@ -1636,12 +2018,19 @@ class KeycloakAPI(object): if cid is None: self.module.fail_json(msg='Could not find client %s in realm %s' % (clientid, realm)) - role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=quote(rolerep['name'])) + role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=quote(rolerep['name'], safe='')) try: - return open_url(role_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, - data=json.dumps(rolerep), validate_certs=self.validate_certs) + composites = None + if "composites" in rolerep: + composites = copy.deepcopy(rolerep["composites"]) + del rolerep['composites'] + update_role_response = open_url(role_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(rolerep), validate_certs=self.validate_certs) + if composites is not None: + self.update_role_composites(rolerep=rolerep, clientid=clientid, composites=composites, realm=realm) + return update_role_response except Exception as e: - self.module.fail_json(msg='Could not update role %s for client %s in realm %s: %s' + self.fail_open_url(e, msg='Could not update role %s for client %s in realm %s: %s' % (rolerep['name'], clientid, realm, str(e))) def delete_client_role(self, name, clientid, realm="master"): @@ -1655,12 +2044,12 @@ class KeycloakAPI(object): if cid is None: self.module.fail_json(msg='Could not find client %s in realm %s' % (clientid, realm)) - role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=quote(name)) + role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=quote(name, safe='')) try: return open_url(role_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Unable to delete role %s for client %s in realm %s: %s' + self.fail_open_url(e, msg='Unable to delete role %s for client %s in realm %s: %s' % (name, clientid, realm, str(e))) def get_authentication_flow_by_alias(self, alias, realm='master'): @@ -1682,7 +2071,7 @@ class KeycloakAPI(object): break return authentication_flow except Exception as e: - self.module.fail_json(msg="Unable get authentication flow %s: %s" % (alias, str(e))) + self.fail_open_url(e, msg="Unable get authentication flow %s: %s" % (alias, str(e))) def delete_authentication_flow_by_id(self, id, realm='master'): """ @@ -1697,8 +2086,8 @@ class KeycloakAPI(object): return open_url(flow_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not delete authentication flow %s in realm %s: %s' - % (id, realm, str(e))) + self.fail_open_url(e, msg='Could not delete authentication flow %s in realm %s: %s' + % (id, realm, str(e))) def copy_auth_flow(self, config, realm='master'): """ @@ -1715,7 +2104,7 @@ class KeycloakAPI(object): URL_AUTHENTICATION_FLOW_COPY.format( url=self.baseurl, realm=realm, - copyfrom=quote(config["copyFrom"])), + copyfrom=quote(config["copyFrom"], safe='')), method='POST', http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(new_name), @@ -1734,8 +2123,8 @@ class KeycloakAPI(object): return flow return None except Exception as e: - self.module.fail_json(msg='Could not copy authentication flow %s in realm %s: %s' - % (config["alias"], realm, str(e))) + self.fail_open_url(e, msg='Could not copy authentication flow %s in realm %s: %s' + % (config["alias"], realm, str(e))) def create_empty_auth_flow(self, config, realm='master'): """ @@ -1774,8 +2163,8 @@ class KeycloakAPI(object): return flow return None except Exception as e: - self.module.fail_json(msg='Could not create empty authentication flow %s in realm %s: %s' - % (config["alias"], realm, str(e))) + self.fail_open_url(e, msg='Could not create empty authentication flow %s in realm %s: %s' + % (config["alias"], realm, str(e))) def update_authentication_executions(self, flowAlias, updatedExec, realm='master'): """ Update authentication executions @@ -1789,12 +2178,15 @@ class KeycloakAPI(object): URL_AUTHENTICATION_FLOW_EXECUTIONS.format( url=self.baseurl, realm=realm, - flowalias=quote(flowAlias)), + flowalias=quote(flowAlias, safe='')), method='PUT', http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(updatedExec), timeout=self.connection_timeout, validate_certs=self.validate_certs) + except HTTPError as e: + self.fail_open_url(e, msg="Unable to update execution '%s': %s: %s %s" + % (flowAlias, repr(e), ";".join([e.url, e.msg, str(e.code), str(e.hdrs)]), str(updatedExec))) except Exception as e: self.module.fail_json(msg="Unable to update executions %s: %s" % (updatedExec, str(e))) @@ -1817,9 +2209,9 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg="Unable to add authenticationConfig %s: %s" % (executionId, str(e))) + self.fail_open_url(e, msg="Unable to add authenticationConfig %s: %s" % (executionId, str(e))) - def create_subflow(self, subflowName, flowAlias, realm='master'): + def create_subflow(self, subflowName, flowAlias, realm='master', flowType='basic-flow'): """ Create new sublow on the flow :param subflowName: name of the subflow to create @@ -1830,19 +2222,19 @@ class KeycloakAPI(object): newSubFlow = {} newSubFlow["alias"] = subflowName newSubFlow["provider"] = "registration-page-form" - newSubFlow["type"] = "basic-flow" + newSubFlow["type"] = flowType open_url( URL_AUTHENTICATION_FLOW_EXECUTIONS_FLOW.format( url=self.baseurl, realm=realm, - flowalias=quote(flowAlias)), + flowalias=quote(flowAlias, safe='')), method='POST', http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(newSubFlow), timeout=self.connection_timeout, validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg="Unable to create new subflow %s: %s" % (subflowName, str(e))) + self.fail_open_url(e, msg="Unable to create new subflow %s: %s" % (subflowName, str(e))) def create_execution(self, execution, flowAlias, realm='master'): """ Create new execution on the flow @@ -1859,14 +2251,17 @@ class KeycloakAPI(object): URL_AUTHENTICATION_FLOW_EXECUTIONS_EXECUTION.format( url=self.baseurl, realm=realm, - flowalias=quote(flowAlias)), + flowalias=quote(flowAlias, safe='')), method='POST', http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(newExec), timeout=self.connection_timeout, validate_certs=self.validate_certs) + except HTTPError as e: + self.fail_open_url(e, msg="Unable to create new execution '%s' %s: %s: %s %s" + % (flowAlias, execution["providerId"], repr(e), ";".join([e.url, e.msg, str(e.code), str(e.hdrs)]), str(newExec))) except Exception as e: - self.module.fail_json(msg="Unable to create new execution %s: %s" % (execution["provider"], str(e))) + self.module.fail_json(msg="Unable to create new execution '%s' %s: %s" % (flowAlias, execution["providerId"], repr(e))) def change_execution_priority(self, executionId, diff, realm='master'): """ Raise or lower execution priority of diff time @@ -1900,7 +2295,7 @@ class KeycloakAPI(object): timeout=self.connection_timeout, validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg="Unable to change execution priority %s: %s" % (executionId, str(e))) + self.fail_open_url(e, msg="Unable to change execution priority %s: %s" % (executionId, str(e))) def get_executions_representation(self, config, realm='master'): """ @@ -1916,7 +2311,7 @@ class KeycloakAPI(object): URL_AUTHENTICATION_FLOW_EXECUTIONS.format( url=self.baseurl, realm=realm, - flowalias=quote(config["alias"])), + flowalias=quote(config["alias"], safe='')), method='GET', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, @@ -1937,8 +2332,121 @@ class KeycloakAPI(object): execution["authenticationConfig"] = execConfig return executions except Exception as e: - self.module.fail_json(msg='Could not get executions for authentication flow %s in realm %s: %s' - % (config["alias"], realm, str(e))) + self.fail_open_url(e, msg='Could not get executions for authentication flow %s in realm %s: %s' + % (config["alias"], realm, str(e))) + + def get_required_actions(self, realm='master'): + """ + Get required actions. + :param realm: Realm name (not id). + :return: List of representations of the required actions. + """ + + try: + required_actions = json.load( + open_url( + URL_AUTHENTICATION_REQUIRED_ACTIONS.format( + url=self.baseurl, + realm=realm + ), + method='GET', + http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs + ) + ) + + return required_actions + except Exception: + return None + + def register_required_action(self, rep, realm='master'): + """ + Register required action. + :param rep: JSON containing 'providerId', and 'name' attributes. + :param realm: Realm name (not id). + :return: Representation of the required action. + """ + + data = { + 'name': rep['name'], + 'providerId': rep['providerId'] + } + + try: + return open_url( + URL_AUTHENTICATION_REGISTER_REQUIRED_ACTION.format( + url=self.baseurl, + realm=realm + ), + method='POST', + http_agent=self.http_agent, headers=self.restheaders, + data=json.dumps(data), + timeout=self.connection_timeout, + validate_certs=self.validate_certs + ) + except Exception as e: + self.fail_open_url( + e, + msg='Unable to register required action %s in realm %s: %s' + % (rep["name"], realm, str(e)) + ) + + def update_required_action(self, alias, rep, realm='master'): + """ + Update required action. + :param alias: Alias of required action. + :param rep: JSON describing new state of required action. + :param realm: Realm name (not id). + :return: HTTPResponse object on success. + """ + + try: + return open_url( + URL_AUTHENTICATION_REQUIRED_ACTIONS_ALIAS.format( + url=self.baseurl, + alias=quote(alias, safe=''), + realm=realm + ), + method='PUT', + http_agent=self.http_agent, headers=self.restheaders, + data=json.dumps(rep), + timeout=self.connection_timeout, + validate_certs=self.validate_certs + ) + except Exception as e: + self.fail_open_url( + e, + msg='Unable to update required action %s in realm %s: %s' + % (alias, realm, str(e)) + ) + + def delete_required_action(self, alias, realm='master'): + """ + Delete required action. + :param alias: Alias of required action. + :param realm: Realm name (not id). + :return: HTTPResponse object on success. + """ + + try: + return open_url( + URL_AUTHENTICATION_REQUIRED_ACTIONS_ALIAS.format( + url=self.baseurl, + alias=quote(alias, safe=''), + realm=realm + ), + method='DELETE', + http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs + ) + except Exception as e: + self.fail_open_url( + e, + msg='Unable to delete required action %s in realm %s: %s' + % (alias, realm, str(e)) + ) def get_identity_providers(self, realm='master'): """ Fetch representations for identity providers in a realm @@ -1953,7 +2461,7 @@ class KeycloakAPI(object): self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of identity providers for realm %s: %s' % (realm, str(e))) except Exception as e: - self.module.fail_json(msg='Could not obtain list of identity providers for realm %s: %s' + self.fail_open_url(e, msg='Could not obtain list of identity providers for realm %s: %s' % (realm, str(e))) def get_identity_provider(self, alias, realm='master'): @@ -1970,7 +2478,7 @@ class KeycloakAPI(object): if e.code == 404: return None else: - self.module.fail_json(msg='Could not fetch identity provider %s in realm %s: %s' + self.fail_open_url(e, msg='Could not fetch identity provider %s in realm %s: %s' % (alias, realm, str(e))) except Exception as e: self.module.fail_json(msg='Could not fetch identity provider %s in realm %s: %s' @@ -1987,7 +2495,7 @@ class KeycloakAPI(object): return open_url(idps_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(idprep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not create identity provider %s in realm %s: %s' + self.fail_open_url(e, msg='Could not create identity provider %s in realm %s: %s' % (idprep['alias'], realm, str(e))) def update_identity_provider(self, idprep, realm='master'): @@ -2001,7 +2509,7 @@ class KeycloakAPI(object): return open_url(idp_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(idprep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not update identity provider %s in realm %s: %s' + self.fail_open_url(e, msg='Could not update identity provider %s in realm %s: %s' % (idprep['alias'], realm, str(e))) def delete_identity_provider(self, alias, realm='master'): @@ -2014,7 +2522,7 @@ class KeycloakAPI(object): return open_url(idp_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Unable to delete identity provider %s in realm %s: %s' + self.fail_open_url(e, msg='Unable to delete identity provider %s in realm %s: %s' % (alias, realm, str(e))) def get_identity_provider_mappers(self, alias, realm='master'): @@ -2032,7 +2540,7 @@ class KeycloakAPI(object): self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of identity provider mappers for idp %s in realm %s: %s' % (alias, realm, str(e))) except Exception as e: - self.module.fail_json(msg='Could not obtain list of identity provider mappers for idp %s in realm %s: %s' + self.fail_open_url(e, msg='Could not obtain list of identity provider mappers for idp %s in realm %s: %s' % (alias, realm, str(e))) def get_identity_provider_mapper(self, mid, alias, realm='master'): @@ -2051,7 +2559,7 @@ class KeycloakAPI(object): if e.code == 404: return None else: - self.module.fail_json(msg='Could not fetch mapper %s for identity provider %s in realm %s: %s' + self.fail_open_url(e, msg='Could not fetch mapper %s for identity provider %s in realm %s: %s' % (mid, alias, realm, str(e))) except Exception as e: self.module.fail_json(msg='Could not fetch mapper %s for identity provider %s in realm %s: %s' @@ -2069,7 +2577,7 @@ class KeycloakAPI(object): return open_url(mappers_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(mapper), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not create identity provider mapper %s for idp %s in realm %s: %s' + self.fail_open_url(e, msg='Could not create identity provider mapper %s for idp %s in realm %s: %s' % (mapper['name'], alias, realm, str(e))) def update_identity_provider_mapper(self, mapper, alias, realm='master'): @@ -2084,7 +2592,7 @@ class KeycloakAPI(object): return open_url(mapper_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(mapper), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not update mapper %s for identity provider %s in realm %s: %s' + self.fail_open_url(e, msg='Could not update mapper %s for identity provider %s in realm %s: %s' % (mapper['id'], alias, realm, str(e))) def delete_identity_provider_mapper(self, mid, alias, realm='master'): @@ -2098,7 +2606,7 @@ class KeycloakAPI(object): return open_url(mapper_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Unable to delete mapper %s for identity provider %s in realm %s: %s' + self.fail_open_url(e, msg='Unable to delete mapper %s for identity provider %s in realm %s: %s' % (mid, alias, realm, str(e))) def get_components(self, filter=None, realm='master'): @@ -2118,7 +2626,7 @@ class KeycloakAPI(object): self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of components for realm %s: %s' % (realm, str(e))) except Exception as e: - self.module.fail_json(msg='Could not obtain list of components for realm %s: %s' + self.fail_open_url(e, msg='Could not obtain list of components for realm %s: %s' % (realm, str(e))) def get_component(self, cid, realm='master'): @@ -2135,7 +2643,7 @@ class KeycloakAPI(object): if e.code == 404: return None else: - self.module.fail_json(msg='Could not fetch component %s in realm %s: %s' + self.fail_open_url(e, msg='Could not fetch component %s in realm %s: %s' % (cid, realm, str(e))) except Exception as e: self.module.fail_json(msg='Could not fetch component %s in realm %s: %s' @@ -2158,7 +2666,7 @@ class KeycloakAPI(object): return json.loads(to_native(open_url(comp_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs).read())) except Exception as e: - self.module.fail_json(msg='Could not create component in realm %s: %s' + self.fail_open_url(e, msg='Could not create component in realm %s: %s' % (realm, str(e))) def update_component(self, comprep, realm='master'): @@ -2175,7 +2683,7 @@ class KeycloakAPI(object): return open_url(comp_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, data=json.dumps(comprep), validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Could not update component %s in realm %s: %s' + self.fail_open_url(e, msg='Could not update component %s in realm %s: %s' % (cid, realm, str(e))) def delete_component(self, cid, realm='master'): @@ -2188,5 +2696,496 @@ class KeycloakAPI(object): return open_url(comp_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs) except Exception as e: - self.module.fail_json(msg='Unable to delete component %s in realm %s: %s' + self.fail_open_url(e, msg='Unable to delete component %s in realm %s: %s' % (cid, realm, str(e))) + + def get_authz_authorization_scope_by_name(self, name, client_id, realm): + url = URL_AUTHZ_AUTHORIZATION_SCOPES.format(url=self.baseurl, client_id=client_id, realm=realm) + search_url = "%s/search?name=%s" % (url, quote(name, safe='')) + + try: + return json.loads(to_native(open_url(search_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs).read())) + except Exception: + return False + + def create_authz_authorization_scope(self, payload, client_id, realm): + """Create an authorization scope for a Keycloak client""" + url = URL_AUTHZ_AUTHORIZATION_SCOPES.format(url=self.baseurl, client_id=client_id, realm=realm) + + try: + return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(payload), validate_certs=self.validate_certs) + except Exception as e: + self.fail_open_url(e, msg='Could not create authorization scope %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e))) + + def update_authz_authorization_scope(self, payload, id, client_id, realm): + """Update an authorization scope for a Keycloak client""" + url = URL_AUTHZ_AUTHORIZATION_SCOPE.format(url=self.baseurl, id=id, client_id=client_id, realm=realm) + + try: + return open_url(url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(payload), validate_certs=self.validate_certs) + except Exception as e: + self.fail_open_url(e, msg='Could not create update scope %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e))) + + def remove_authz_authorization_scope(self, id, client_id, realm): + """Remove an authorization scope from a Keycloak client""" + url = URL_AUTHZ_AUTHORIZATION_SCOPE.format(url=self.baseurl, id=id, client_id=client_id, realm=realm) + + try: + return open_url(url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + validate_certs=self.validate_certs) + except Exception as e: + self.fail_open_url(e, msg='Could not delete scope %s for client %s in realm %s: %s' % (id, client_id, realm, str(e))) + + def get_user_by_id(self, user_id, realm='master'): + """ + Get a User by its ID. + :param user_id: ID of the user. + :param realm: Realm + :return: Representation of the user. + """ + try: + user_url = URL_USER.format( + url=self.baseurl, + realm=realm, + id=user_id) + userrep = json.load( + open_url( + user_url, + method='GET', + http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs)) + return userrep + except Exception as e: + self.fail_open_url(e, msg='Could not get user %s in realm %s: %s' + % (user_id, realm, str(e))) + + def create_user(self, userrep, realm='master'): + """ + Create a new User. + :param userrep: Representation of the user to create + :param realm: Realm + :return: Representation of the user created. + """ + try: + if 'attributes' in userrep and isinstance(userrep['attributes'], list): + attributes = copy.deepcopy(userrep['attributes']) + userrep['attributes'] = self.convert_user_attributes_to_keycloak_dict(attributes=attributes) + users_url = URL_USERS.format( + url=self.baseurl, + realm=realm) + open_url(users_url, + method='POST', + http_agent=self.http_agent, headers=self.restheaders, + data=json.dumps(userrep), + timeout=self.connection_timeout, + validate_certs=self.validate_certs) + created_user = self.get_user_by_username( + username=userrep['username'], + realm=realm) + return created_user + except Exception as e: + self.fail_open_url(e, msg='Could not create user %s in realm %s: %s' + % (userrep['username'], realm, str(e))) + + def convert_user_attributes_to_keycloak_dict(self, attributes): + keycloak_user_attributes_dict = {} + for attribute in attributes: + if ('state' not in attribute or attribute['state'] == 'present') and 'name' in attribute: + keycloak_user_attributes_dict[attribute['name']] = attribute['values'] if 'values' in attribute else [] + return keycloak_user_attributes_dict + + def convert_keycloak_user_attributes_dict_to_module_list(self, attributes): + module_attributes_list = [] + for key in attributes: + attr = {} + attr['name'] = key + attr['values'] = attributes[key] + module_attributes_list.append(attr) + return module_attributes_list + + def update_user(self, userrep, realm='master'): + """ + Update a User. + :param userrep: Representation of the user to update. This representation must include the ID of the user. + :param realm: Realm + :return: Representation of the updated user. + """ + try: + if 'attributes' in userrep and isinstance(userrep['attributes'], list): + attributes = copy.deepcopy(userrep['attributes']) + userrep['attributes'] = self.convert_user_attributes_to_keycloak_dict(attributes=attributes) + user_url = URL_USER.format( + url=self.baseurl, + realm=realm, + id=userrep["id"]) + open_url( + user_url, + method='PUT', + http_agent=self.http_agent, headers=self.restheaders, + data=json.dumps(userrep), + timeout=self.connection_timeout, + validate_certs=self.validate_certs) + updated_user = self.get_user_by_id( + user_id=userrep['id'], + realm=realm) + return updated_user + except Exception as e: + self.fail_open_url(e, msg='Could not update user %s in realm %s: %s' + % (userrep['username'], realm, str(e))) + + def delete_user(self, user_id, realm='master'): + """ + Delete a User. + :param user_id: ID of the user to be deleted + :param realm: Realm + :return: HTTP response. + """ + try: + user_url = URL_USER.format( + url=self.baseurl, + realm=realm, + id=user_id) + return open_url( + user_url, + method='DELETE', + http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs) + except Exception as e: + self.fail_open_url(e, msg='Could not delete user %s in realm %s: %s' + % (user_id, realm, str(e))) + + def get_user_groups(self, user_id, realm='master'): + """ + Get groups for a user. + :param user_id: User ID + :param realm: Realm + :return: Representation of the client groups. + """ + try: + groups = [] + user_groups_url = URL_USER_GROUPS.format( + url=self.baseurl, + realm=realm, + id=user_id) + user_groups = json.load( + open_url( + user_groups_url, + method='GET', + http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs)) + for user_group in user_groups: + groups.append(user_group["name"]) + return groups + except Exception as e: + self.fail_open_url(e, msg='Could not get groups for user %s in realm %s: %s' + % (user_id, realm, str(e))) + + def add_user_in_group(self, user_id, group_id, realm='master'): + """ + Add a user to a group. + :param user_id: User ID + :param group_id: Group Id to add the user to. + :param realm: Realm + :return: HTTP Response + """ + try: + user_group_url = URL_USER_GROUP.format( + url=self.baseurl, + realm=realm, + id=user_id, + group_id=group_id) + return open_url( + user_group_url, + method='PUT', + http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs) + except Exception as e: + self.fail_open_url(e, msg='Could not add user %s in group %s in realm %s: %s' + % (user_id, group_id, realm, str(e))) + + def remove_user_from_group(self, user_id, group_id, realm='master'): + """ + Remove a user from a group for a user. + :param user_id: User ID + :param group_id: Group Id to add the user to. + :param realm: Realm + :return: HTTP response + """ + try: + user_group_url = URL_USER_GROUP.format( + url=self.baseurl, + realm=realm, + id=user_id, + group_id=group_id) + return open_url( + user_group_url, + method='DELETE', + http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs) + except Exception as e: + self.fail_open_url(e, msg='Could not remove user %s from group %s in realm %s: %s' + % (user_id, group_id, realm, str(e))) + + def update_user_groups_membership(self, userrep, groups, realm='master'): + """ + Update user's group membership + :param userrep: Representation of the user. This representation must include the ID. + :param realm: Realm + :return: True if group membership has been changed. False Otherwise. + """ + changed = False + try: + user_existing_groups = self.get_user_groups( + user_id=userrep['id'], + realm=realm) + groups_to_add_and_remove = self.extract_groups_to_add_to_and_remove_from_user(groups) + # If group membership need to be changed + if not is_struct_included(groups_to_add_and_remove['add'], user_existing_groups): + # Get available groups in the realm + realm_groups = self.get_groups(realm=realm) + for realm_group in realm_groups: + if "name" in realm_group and realm_group["name"] in groups_to_add_and_remove['add']: + self.add_user_in_group( + user_id=userrep["id"], + group_id=realm_group["id"], + realm=realm) + changed = True + elif "name" in realm_group and realm_group['name'] in groups_to_add_and_remove['remove']: + self.remove_user_from_group( + user_id=userrep['id'], + group_id=realm_group['id'], + realm=realm) + changed = True + return changed + except Exception as e: + self.module.fail_json(msg='Could not update group membership for user %s in realm %s: %s' + % (userrep['id]'], realm, str(e))) + + def extract_groups_to_add_to_and_remove_from_user(self, groups): + groups_extract = {} + groups_to_add = [] + groups_to_remove = [] + if isinstance(groups, list) and len(groups) > 0: + for group in groups: + group_name = group['name'] if isinstance(group, dict) and 'name' in group else group + if isinstance(group, dict) and ('state' not in group or group['state'] == 'present'): + groups_to_add.append(group_name) + else: + groups_to_remove.append(group_name) + groups_extract['add'] = groups_to_add + groups_extract['remove'] = groups_to_remove + + return groups_extract + + def convert_user_group_list_of_str_to_list_of_dict(self, groups): + list_of_groups = [] + if isinstance(groups, list) and len(groups) > 0: + for group in groups: + if isinstance(group, str): + group_dict = {} + group_dict['name'] = group + list_of_groups.append(group_dict) + return list_of_groups + + def create_authz_custom_policy(self, policy_type, payload, client_id, realm): + """Create a custom policy for a Keycloak client""" + url = URL_AUTHZ_CUSTOM_POLICY.format(url=self.baseurl, policy_type=policy_type, client_id=client_id, realm=realm) + + try: + return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(payload), validate_certs=self.validate_certs) + except Exception as e: + self.fail_open_url(e, msg='Could not create permission %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e))) + + def remove_authz_custom_policy(self, policy_id, client_id, realm): + """Remove a custom policy from a Keycloak client""" + url = URL_AUTHZ_CUSTOM_POLICIES.format(url=self.baseurl, client_id=client_id, realm=realm) + delete_url = "%s/%s" % (url, policy_id) + + try: + return open_url(delete_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + validate_certs=self.validate_certs) + except Exception as e: + self.fail_open_url(e, msg='Could not delete custom policy %s for client %s in realm %s: %s' % (id, client_id, realm, str(e))) + + def get_authz_permission_by_name(self, name, client_id, realm): + """Get authorization permission by name""" + url = URL_AUTHZ_POLICIES.format(url=self.baseurl, client_id=client_id, realm=realm) + search_url = "%s/search?name=%s" % (url, name.replace(' ', '%20')) + + try: + return json.loads(to_native(open_url(search_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs).read())) + except Exception: + return False + + def create_authz_permission(self, payload, permission_type, client_id, realm): + """Create an authorization permission for a Keycloak client""" + url = URL_AUTHZ_PERMISSIONS.format(url=self.baseurl, permission_type=permission_type, client_id=client_id, realm=realm) + + try: + return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(payload), validate_certs=self.validate_certs) + except Exception as e: + self.fail_open_url(e, msg='Could not create permission %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e))) + + def remove_authz_permission(self, id, client_id, realm): + """Create an authorization permission for a Keycloak client""" + url = URL_AUTHZ_POLICY.format(url=self.baseurl, id=id, client_id=client_id, realm=realm) + + try: + return open_url(url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + validate_certs=self.validate_certs) + except Exception as e: + self.fail_open_url(e, msg='Could not delete permission %s for client %s in realm %s: %s' % (id, client_id, realm, str(e))) + + def update_authz_permission(self, payload, permission_type, id, client_id, realm): + """Update a permission for a Keycloak client""" + url = URL_AUTHZ_PERMISSION.format(url=self.baseurl, permission_type=permission_type, id=id, client_id=client_id, realm=realm) + + try: + return open_url(url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(payload), validate_certs=self.validate_certs) + except Exception as e: + self.fail_open_url(e, msg='Could not create update permission %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e))) + + def get_authz_resource_by_name(self, name, client_id, realm): + """Get authorization resource by name""" + url = URL_AUTHZ_RESOURCES.format(url=self.baseurl, client_id=client_id, realm=realm) + search_url = "%s/search?name=%s" % (url, name.replace(' ', '%20')) + + try: + return json.loads(to_native(open_url(search_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs).read())) + except Exception: + return False + + def get_authz_policy_by_name(self, name, client_id, realm): + """Get authorization policy by name""" + url = URL_AUTHZ_POLICIES.format(url=self.baseurl, client_id=client_id, realm=realm) + search_url = "%s/search?name=%s&permission=false" % (url, name.replace(' ', '%20')) + + try: + return json.loads(to_native(open_url(search_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs).read())) + except Exception: + return False + + def get_client_role_scope_from_client(self, clientid, clientscopeid, realm="master"): + """ Fetch the roles associated with the client's scope for a specific client on the Keycloak server. + :param clientid: ID of the client from which to obtain the associated roles. + :param clientscopeid: ID of the client who owns the roles. + :param realm: Realm from which to obtain the scope. + :return: The client scope of roles from specified client. + """ + client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format(url=self.baseurl, realm=realm, id=clientid, scopeid=clientscopeid) + try: + return json.loads(to_native(open_url(client_role_scope_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs).read())) + except Exception as e: + self.fail_open_url(e, msg='Could not fetch roles scope for client %s in realm %s: %s' % (clientid, realm, str(e))) + + def update_client_role_scope_from_client(self, payload, clientid, clientscopeid, realm="master"): + """ Update and fetch the roles associated with the client's scope on the Keycloak server. + :param payload: List of roles to be added to the scope. + :param clientid: ID of the client to update scope. + :param clientscopeid: ID of the client who owns the roles. + :param realm: Realm from which to obtain the clients. + :return: The client scope of roles from specified client. + """ + client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format(url=self.baseurl, realm=realm, id=clientid, scopeid=clientscopeid) + try: + open_url(client_role_scope_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(payload), validate_certs=self.validate_certs) + + except Exception as e: + self.fail_open_url(e, msg='Could not update roles scope for client %s in realm %s: %s' % (clientid, realm, str(e))) + + return self.get_client_role_scope_from_client(clientid, clientscopeid, realm) + + def delete_client_role_scope_from_client(self, payload, clientid, clientscopeid, realm="master"): + """ Delete the roles contains in the payload from the client's scope on the Keycloak server. + :param payload: List of roles to be deleted. + :param clientid: ID of the client to delete roles from scope. + :param clientscopeid: ID of the client who owns the roles. + :param realm: Realm from which to obtain the clients. + :return: The client scope of roles from specified client. + """ + client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format(url=self.baseurl, realm=realm, id=clientid, scopeid=clientscopeid) + try: + open_url(client_role_scope_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(payload), validate_certs=self.validate_certs) + + except Exception as e: + self.fail_open_url(e, msg='Could not delete roles scope for client %s in realm %s: %s' % (clientid, realm, str(e))) + + return self.get_client_role_scope_from_client(clientid, clientscopeid, realm) + + def get_client_role_scope_from_realm(self, clientid, realm="master"): + """ Fetch the realm roles from the client's scope on the Keycloak server. + :param clientid: ID of the client from which to obtain the associated realm roles. + :param realm: Realm from which to obtain the clients. + :return: The client realm roles scope. + """ + client_role_scope_url = URL_CLIENT_ROLE_SCOPE_REALM.format(url=self.baseurl, realm=realm, id=clientid) + try: + return json.loads(to_native(open_url(client_role_scope_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, + timeout=self.connection_timeout, + validate_certs=self.validate_certs).read())) + except Exception as e: + self.fail_open_url(e, msg='Could not fetch roles scope for client %s in realm %s: %s' % (clientid, realm, str(e))) + + def update_client_role_scope_from_realm(self, payload, clientid, realm="master"): + """ Update and fetch the realm roles from the client's scope on the Keycloak server. + :param payload: List of realm roles to add. + :param clientid: ID of the client to update scope. + :param realm: Realm from which to obtain the clients. + :return: The client realm roles scope. + """ + client_role_scope_url = URL_CLIENT_ROLE_SCOPE_REALM.format(url=self.baseurl, realm=realm, id=clientid) + try: + open_url(client_role_scope_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(payload), validate_certs=self.validate_certs) + + except Exception as e: + self.fail_open_url(e, msg='Could not update roles scope for client %s in realm %s: %s' % (clientid, realm, str(e))) + + return self.get_client_role_scope_from_realm(clientid, realm) + + def delete_client_role_scope_from_realm(self, payload, clientid, realm="master"): + """ Delete the realm roles contains in the payload from the client's scope on the Keycloak server. + :param payload: List of realm roles to delete. + :param clientid: ID of the client to delete roles from scope. + :param realm: Realm from which to obtain the clients. + :return: The client realm roles scope. + """ + client_role_scope_url = URL_CLIENT_ROLE_SCOPE_REALM.format(url=self.baseurl, realm=realm, id=clientid) + try: + open_url(client_role_scope_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(payload), validate_certs=self.validate_certs) + + except Exception as e: + self.fail_open_url(e, msg='Could not delete roles scope for client %s in realm %s: %s' % (clientid, realm, str(e))) + + return self.get_client_role_scope_from_realm(clientid, realm) + + def fail_open_url(self, e, msg, **kwargs): + try: + if isinstance(e, HTTPError): + msg = "%s: %s" % (msg, to_native(e.read())) + except Exception as ingore: + pass + self.module.fail_json(msg, **kwargs) diff --git a/plugins/modules/keycloak_client.py b/plugins/modules/keycloak_client.py index dc824ca..0afa52b 100644 --- a/plugins/modules/keycloak_client.py +++ b/plugins/modules/keycloak_client.py @@ -40,8 +40,8 @@ options: state: description: - State of the client - - On C(present), the client will be created (or updated if it exists already). - - On C(absent), the client will be removed if it exists + - On V(present), the client will be created (or updated if it exists already). + - On V(absent), the client will be removed if it exists choices: ['present', 'absent'] default: 'present' type: str @@ -55,7 +55,7 @@ options: client_id: description: - Client id of client to be worked on. This is usually an alphanumeric name chosen by - you. Either this or I(id) is required. If you specify both, I(id) takes precedence. + you. Either this or O(id) is required. If you specify both, O(id) takes precedence. This is 'clientId' in the Keycloak REST API. aliases: - clientId @@ -63,13 +63,13 @@ options: id: description: - - Id of client to be worked on. This is usually an UUID. Either this or I(client_id) + - Id of client to be worked on. This is usually an UUID. Either this or O(client_id) is required. If you specify both, this takes precedence. type: str name: description: - - Name of the client (this is not the same as I(client_id)). + - Name of the client (this is not the same as O(client_id)). type: str description: @@ -108,20 +108,21 @@ options: client_authenticator_type: description: - - How do clients authenticate with the auth server? Either C(client-secret) or - C(client-jwt) can be chosen. When using C(client-secret), the module parameter - I(secret) can set it, while for C(client-jwt), you can use the keys C(use.jwks.url), - C(jwks.url), and C(jwt.credential.certificate) in the I(attributes) module parameter - to configure its behavior. - This is 'clientAuthenticatorType' in the Keycloak REST API. - choices: ['client-secret', 'client-jwt'] + - How do clients authenticate with the auth server? Either V(client-secret), + V(client-jwt), or V(client-x509) can be chosen. When using V(client-secret), the module parameter + O(secret) can set it, for V(client-jwt), you can use the keys C(use.jwks.url), + C(jwks.url), and C(jwt.credential.certificate) in the O(attributes) module parameter + to configure its behavior. For V(client-x509) you can use the keys C(x509.allow.regex.pattern.comparison) + and C(x509.subjectdn) in the O(attributes) module parameter to configure which certificate(s) to accept. + - This is 'clientAuthenticatorType' in the Keycloak REST API. + choices: ['client-secret', 'client-jwt', 'client-x509'] aliases: - clientAuthenticatorType type: str secret: description: - - When using I(client_authenticator_type) C(client-secret) (the default), you can + - When using O(client_authenticator_type=client-secret) (the default), you can specify a secret here (otherwise one will be generated if it does not exit). If changing this secret, the module will not register a change currently (but the changed secret will be saved). @@ -246,9 +247,11 @@ options: protocol: description: - - Type of client (either C(openid-connect) or C(saml). + - Type of client. + - At creation only, default value will be V(openid-connect) if O(protocol) is omitted. + - The V(docker-v2) value was added in community.general 8.6.0. type: str - choices: ['openid-connect', 'saml'] + choices: ['openid-connect', 'saml', 'docker-v2'] full_scope_allowed: description: @@ -286,7 +289,7 @@ options: use_template_config: description: - - Whether or not to use configuration from the I(client_template). + - Whether or not to use configuration from the O(client_template). This is 'useTemplateConfig' in the Keycloak REST API. aliases: - useTemplateConfig @@ -294,7 +297,7 @@ options: use_template_scope: description: - - Whether or not to use scope configuration from the I(client_template). + - Whether or not to use scope configuration from the O(client_template). This is 'useTemplateScope' in the Keycloak REST API. aliases: - useTemplateScope @@ -302,7 +305,7 @@ options: use_template_mappers: description: - - Whether or not to use mapper configuration from the I(client_template). + - Whether or not to use mapper configuration from the O(client_template). This is 'useTemplateMappers' in the Keycloak REST API. aliases: - useTemplateMappers @@ -338,6 +341,42 @@ options: description: - Override realm authentication flow bindings. type: dict + suboptions: + browser: + description: + - Flow ID of the browser authentication flow. + - O(authentication_flow_binding_overrides.browser) + and O(authentication_flow_binding_overrides.browser_name) are mutually exclusive. + type: str + + browser_name: + description: + - Flow name of the browser authentication flow. + - O(authentication_flow_binding_overrides.browser) + and O(authentication_flow_binding_overrides.browser_name) are mutually exclusive. + aliases: + - browserName + type: str + version_added: 9.1.0 + + direct_grant: + description: + - Flow ID of the direct grant authentication flow. + - O(authentication_flow_binding_overrides.direct_grant) + and O(authentication_flow_binding_overrides.direct_grant_name) are mutually exclusive. + aliases: + - directGrant + type: str + + direct_grant_name: + description: + - Flow name of the direct grant authentication flow. + - O(authentication_flow_binding_overrides.direct_grant) + and O(authentication_flow_binding_overrides.direct_grant_name) are mutually exclusive. + aliases: + - directGrantName + type: str + version_added: 9.1.0 aliases: - authenticationFlowBindingOverrides version_added: 3.4.0 @@ -391,38 +430,37 @@ options: protocol: description: - - This is either C(openid-connect) or C(saml), this specifies for which protocol this protocol mapper. - is active. - choices: ['openid-connect', 'saml'] + - This specifies for which protocol this protocol mapper is active. + choices: ['openid-connect', 'saml', 'docker-v2'] type: str protocolMapper: description: - - The Keycloak-internal name of the type of this protocol-mapper. While an exhaustive list is + - "The Keycloak-internal name of the type of this protocol-mapper. While an exhaustive list is impossible to provide since this may be extended through SPIs by the user of Keycloak, - by default Keycloak as of 3.4 ships with at least - - C(docker-v2-allow-all-mapper) - - C(oidc-address-mapper) - - C(oidc-full-name-mapper) - - C(oidc-group-membership-mapper) - - C(oidc-hardcoded-claim-mapper) - - C(oidc-hardcoded-role-mapper) - - C(oidc-role-name-mapper) - - C(oidc-script-based-protocol-mapper) - - C(oidc-sha256-pairwise-sub-mapper) - - C(oidc-usermodel-attribute-mapper) - - C(oidc-usermodel-client-role-mapper) - - C(oidc-usermodel-property-mapper) - - C(oidc-usermodel-realm-role-mapper) - - C(oidc-usersessionmodel-note-mapper) - - C(saml-group-membership-mapper) - - C(saml-hardcode-attribute-mapper) - - C(saml-hardcode-role-mapper) - - C(saml-role-list-mapper) - - C(saml-role-name-mapper) - - C(saml-user-attribute-mapper) - - C(saml-user-property-mapper) - - C(saml-user-session-note-mapper) + by default Keycloak as of 3.4 ships with at least:" + - V(docker-v2-allow-all-mapper) + - V(oidc-address-mapper) + - V(oidc-full-name-mapper) + - V(oidc-group-membership-mapper) + - V(oidc-hardcoded-claim-mapper) + - V(oidc-hardcoded-role-mapper) + - V(oidc-role-name-mapper) + - V(oidc-script-based-protocol-mapper) + - V(oidc-sha256-pairwise-sub-mapper) + - V(oidc-usermodel-attribute-mapper) + - V(oidc-usermodel-client-role-mapper) + - V(oidc-usermodel-property-mapper) + - V(oidc-usermodel-realm-role-mapper) + - V(oidc-usersessionmodel-note-mapper) + - V(saml-group-membership-mapper) + - V(saml-hardcode-attribute-mapper) + - V(saml-hardcode-role-mapper) + - V(saml-role-list-mapper) + - V(saml-role-name-mapper) + - V(saml-user-attribute-mapper) + - V(saml-user-property-mapper) + - V(saml-user-session-note-mapper) - An exhaustive list of available mappers on your installation can be obtained on the admin console by going to Server Info -> Providers and looking under 'protocol-mapper'. @@ -431,10 +469,10 @@ options: config: description: - Dict specifying the configuration options for the protocol mapper; the - contents differ depending on the value of I(protocolMapper) and are not documented + contents differ depending on the value of O(protocol_mappers[].protocolMapper) and are not documented other than by the source of the mappers and its parent class(es). An example is given below. It is easiest to obtain valid config values by dumping an already-existing - protocol mapper configuration through check-mode in the I(existing) field. + protocol mapper configuration through check-mode in the RV(existing) field. type: dict attributes: @@ -478,7 +516,7 @@ options: saml.signature.algorithm: description: - - Signature algorithm used to sign SAML documents. One of C(RSA_SHA256), C(RSA_SHA1), C(RSA_SHA512), or C(DSA_SHA1). + - Signature algorithm used to sign SAML documents. One of V(RSA_SHA256), V(RSA_SHA1), V(RSA_SHA512), or V(DSA_SHA1). saml.signing.certificate: description: @@ -496,22 +534,21 @@ options: description: - SAML Redirect Binding URL for the client's assertion consumer service (login responses). - saml_force_name_id_format: description: - For SAML clients, Boolean specifying whether to ignore requested NameID subject format and using the configured one instead. saml_name_id_format: description: - - For SAML clients, the NameID format to use (one of C(username), C(email), C(transient), or C(persistent)) + - For SAML clients, the NameID format to use (one of V(username), V(email), V(transient), or V(persistent)) saml_signature_canonicalization_method: description: - SAML signature canonicalization method. This is one of four values, namely - C(http://www.w3.org/2001/10/xml-exc-c14n#) for EXCLUSIVE, - C(http://www.w3.org/2001/10/xml-exc-c14n#WithComments) for EXCLUSIVE_WITH_COMMENTS, - C(http://www.w3.org/TR/2001/REC-xml-c14n-20010315) for INCLUSIVE, and - C(http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments) for INCLUSIVE_WITH_COMMENTS. + V(http://www.w3.org/2001/10/xml-exc-c14n#) for EXCLUSIVE, + V(http://www.w3.org/2001/10/xml-exc-c14n#WithComments) for EXCLUSIVE_WITH_COMMENTS, + V(http://www.w3.org/TR/2001/REC-xml-c14n-20010315) for INCLUSIVE, and + V(http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments) for INCLUSIVE_WITH_COMMENTS. saml_single_logout_service_url_post: description: @@ -523,12 +560,12 @@ options: user.info.response.signature.alg: description: - - For OpenID-Connect clients, JWA algorithm for signed UserInfo-endpoint responses. One of C(RS256) or C(unsigned). + - For OpenID-Connect clients, JWA algorithm for signed UserInfo-endpoint responses. One of V(RS256) or V(unsigned). request.object.signature.alg: description: - For OpenID-Connect clients, JWA algorithm which the client needs to use when sending - OIDC request object. One of C(any), C(none), C(RS256). + OIDC request object. One of V(any), V(none), V(RS256). use.jwks.url: description: @@ -544,9 +581,21 @@ options: - For OpenID-Connect clients, client certificate for validating JWT issued by client and signed by its key, base64-encoded. + x509.subjectdn: + description: + - For OpenID-Connect clients, subject which will be used to authenticate the client. + type: str + version_added: 9.5.0 + + x509.allow.regex.pattern.comparison: + description: + - For OpenID-Connect clients, boolean specifying whether to allow C(x509.subjectdn) as regular expression. + type: bool + version_added: 9.5.0 + extends_documentation_fragment: - - middleware_automation.keycloak.keycloak - - middleware_automation.keycloak.attributes + - middleware_automation.keycloak.keycloak + - middleware_automation.keycloak.attributes author: - Eike Frost (@eikef) @@ -587,6 +636,22 @@ EXAMPLES = ''' delegate_to: localhost +- name: Create or update a Keycloak client (minimal example), with x509 authentication + middleware_automation.keycloak.keycloak_client: + auth_client_id: admin-cli + auth_keycloak_url: https://auth.example.com/auth + auth_realm: master + auth_username: USERNAME + auth_password: PASSWORD + realm: master + state: present + client_id: test + client_authenticator_type: client-x509 + attributes: + x509.subjectdn: "CN=client" + x509.allow.regex.pattern.comparison: false + + - name: Create or update a Keycloak client (with all the bells and whistles) middleware_automation.keycloak.keycloak_client: auth_client_id: admin-cli @@ -637,7 +702,7 @@ EXAMPLES = ''' - test01 - test02 authentication_flow_binding_overrides: - browser: 4c90336b-bf1d-4b87-916d-3677ba4e5fbb + browser: 4c90336b-bf1d-4b87-916d-3677ba4e5fbb protocol_mappers: - config: access.token.claim: true @@ -717,11 +782,17 @@ end_state: ''' from ansible_collections.middleware_automation.keycloak.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, camel, \ - keycloak_argument_spec, get_token, KeycloakError + keycloak_argument_spec, get_token, KeycloakError, is_struct_included from ansible.module_utils.basic import AnsibleModule import copy +PROTOCOL_OPENID_CONNECT = 'openid-connect' +PROTOCOL_SAML = 'saml' +PROTOCOL_DOCKER_V2 = 'docker-v2' +CLIENT_META_DATA = ['authorizationServicesEnabled'] + + def normalise_cr(clientrep, remove_ids=False): """ Re-sorts any properties where the order so that diff's is minimised, and adds default values where appropriate so that the the change detection is more effective. @@ -737,6 +808,12 @@ def normalise_cr(clientrep, remove_ids=False): if 'attributes' in clientrep: clientrep['attributes'] = list(sorted(clientrep['attributes'])) + if 'defaultClientScopes' in clientrep: + clientrep['defaultClientScopes'] = list(sorted(clientrep['defaultClientScopes'])) + + if 'optionalClientScopes' in clientrep: + clientrep['optionalClientScopes'] = list(sorted(clientrep['optionalClientScopes'])) + if 'redirectUris' in clientrep: clientrep['redirectUris'] = list(sorted(clientrep['redirectUris'])) @@ -762,11 +839,70 @@ def sanitize_cr(clientrep): if 'secret' in result: result['secret'] = 'no_log' if 'attributes' in result: - if 'saml.signing.private.key' in result['attributes']: - result['attributes']['saml.signing.private.key'] = 'no_log' + attributes = result['attributes'] + if isinstance(attributes, dict) and 'saml.signing.private.key' in attributes: + attributes['saml.signing.private.key'] = 'no_log' return normalise_cr(result) +def get_authentication_flow_id(flow_name, realm, kc): + """ Get the authentication flow ID based on the flow name, realm, and Keycloak client. + + Args: + flow_name (str): The name of the authentication flow. + realm (str): The name of the realm. + kc (KeycloakClient): The Keycloak client instance. + + Returns: + str: The ID of the authentication flow. + + Raises: + KeycloakAPIException: If the authentication flow with the given name is not found in the realm. + """ + flow = kc.get_authentication_flow_by_alias(flow_name, realm) + if flow: + return flow["id"] + kc.module.fail_json(msg='Authentification flow %s not found in realm %s' % (flow_name, realm)) + + +def flow_binding_from_dict_to_model(newClientFlowBinding, realm, kc): + """ Convert a dictionary representing client flow bindings to a model representation. + + Args: + newClientFlowBinding (dict): A dictionary containing client flow bindings. + realm (str): The name of the realm. + kc (KeycloakClient): An instance of the KeycloakClient class. + + Returns: + dict: A dictionary representing the model flow bindings. The dictionary has two keys: + - "browser" (str or None): The ID of the browser authentication flow binding, or None if not provided. + - "direct_grant" (str or None): The ID of the direct grant authentication flow binding, or None if not provided. + + Raises: + KeycloakAPIException: If the authentication flow with the given name is not found in the realm. + + """ + + modelFlow = { + "browser": None, + "direct_grant": None + } + + for k, v in newClientFlowBinding.items(): + if not v: + continue + if k == "browser": + modelFlow["browser"] = v + elif k == "browser_name": + modelFlow["browser"] = get_authentication_flow_id(v, realm, kc) + elif k == "direct_grant": + modelFlow["direct_grant"] = v + elif k == "direct_grant_name": + modelFlow["direct_grant"] = get_authentication_flow_id(v, realm, kc) + + return modelFlow + + def main(): """ Module execution @@ -780,11 +916,18 @@ def main(): consentText=dict(type='str'), id=dict(type='str'), name=dict(type='str'), - protocol=dict(type='str', choices=['openid-connect', 'saml']), + protocol=dict(type='str', choices=[PROTOCOL_OPENID_CONNECT, PROTOCOL_SAML, PROTOCOL_DOCKER_V2]), protocolMapper=dict(type='str'), config=dict(type='dict'), ) + authentication_flow_spec = dict( + browser=dict(type='str'), + browser_name=dict(type='str', aliases=['browserName']), + direct_grant=dict(type='str', aliases=['directGrant']), + direct_grant_name=dict(type='str', aliases=['directGrantName']), + ) + meta_args = dict( state=dict(default='present', choices=['present', 'absent']), realm=dict(type='str', default='master'), @@ -798,7 +941,7 @@ def main(): base_url=dict(type='str', aliases=['baseUrl']), surrogate_auth_required=dict(type='bool', aliases=['surrogateAuthRequired']), enabled=dict(type='bool'), - client_authenticator_type=dict(type='str', choices=['client-secret', 'client-jwt'], aliases=['clientAuthenticatorType']), + client_authenticator_type=dict(type='str', choices=['client-secret', 'client-jwt', 'client-x509'], aliases=['clientAuthenticatorType']), secret=dict(type='str', no_log=True), registration_access_token=dict(type='str', aliases=['registrationAccessToken'], no_log=True), default_roles=dict(type='list', elements='str', aliases=['defaultRoles']), @@ -814,7 +957,7 @@ def main(): authorization_services_enabled=dict(type='bool', aliases=['authorizationServicesEnabled']), public_client=dict(type='bool', aliases=['publicClient']), frontchannel_logout=dict(type='bool', aliases=['frontchannelLogout']), - protocol=dict(type='str', choices=['openid-connect', 'saml']), + protocol=dict(type='str', choices=[PROTOCOL_OPENID_CONNECT, PROTOCOL_SAML, PROTOCOL_DOCKER_V2]), attributes=dict(type='dict'), full_scope_allowed=dict(type='bool', aliases=['fullScopeAllowed']), node_re_registration_timeout=dict(type='int', aliases=['nodeReRegistrationTimeout']), @@ -824,7 +967,13 @@ def main(): use_template_scope=dict(type='bool', aliases=['useTemplateScope']), use_template_mappers=dict(type='bool', aliases=['useTemplateMappers']), always_display_in_console=dict(type='bool', aliases=['alwaysDisplayInConsole']), - authentication_flow_binding_overrides=dict(type='dict', aliases=['authenticationFlowBindingOverrides']), + authentication_flow_binding_overrides=dict( + type='dict', + aliases=['authenticationFlowBindingOverrides'], + options=authentication_flow_spec, + required_one_of=[['browser', 'direct_grant', 'browser_name', 'direct_grant_name']], + mutually_exclusive=[['browser', 'browser_name'], ['direct_grant', 'direct_grant_name']], + ), protocol_mappers=dict(type='list', elements='dict', options=protmapper_spec, aliases=['protocolMappers']), authorization_settings=dict(type='dict', aliases=['authorizationSettings']), default_client_scopes=dict(type='list', elements='str', aliases=['defaultClientScopes']), @@ -885,7 +1034,9 @@ def main(): # Unfortunately, the ansible argument spec checker introduces variables with null values when # they are not specified if client_param == 'protocol_mappers': - new_param_value = [dict((k, v) for k, v in x.items() if x[k] is not None) for x in new_param_value] + new_param_value = [{k: v for k, v in x.items() if v is not None} for x in new_param_value] + elif client_param == 'authentication_flow_binding_overrides': + new_param_value = flow_binding_from_dict_to_model(new_param_value, realm, kc) changeset[camel(client_param)] = new_param_value @@ -912,6 +1063,8 @@ def main(): if 'clientId' not in desired_client: module.fail_json(msg='client_id needs to be specified when creating a new client') + if 'protocol' not in desired_client: + desired_client['protocol'] = PROTOCOL_OPENID_CONNECT if module._diff: result['diff'] = dict(before='', after=sanitize_cr(desired_client)) @@ -940,7 +1093,7 @@ def main(): if module._diff: result['diff'] = dict(before=sanitize_cr(before_norm), after=sanitize_cr(desired_norm)) - result['changed'] = (before_norm != desired_norm) + result['changed'] = not is_struct_included(desired_norm, before_norm, CLIENT_META_DATA) module.exit_json(**result) diff --git a/plugins/modules/keycloak_role.py b/plugins/modules/keycloak_role.py index 558f362..c48e9c9 100644 --- a/plugins/modules/keycloak_role.py +++ b/plugins/modules/keycloak_role.py @@ -40,8 +40,8 @@ options: state: description: - State of the role. - - On C(present), the role will be created if it does not yet exist, or updated with the parameters you provide. - - On C(absent), the role will be removed if it exists. + - On V(present), the role will be created if it does not yet exist, or updated with the parameters you provide. + - On V(absent), the role will be removed if it exists. default: 'present' type: str choices: @@ -77,6 +77,42 @@ options: description: - A dict of key/value pairs to set as custom attributes for the role. - Values may be single values (e.g. a string) or a list of strings. + composite: + description: + - If V(true), the role is a composition of other realm and/or client role. + default: false + type: bool + version_added: 7.1.0 + composites: + description: + - List of roles to include to the composite realm role. + - If the composite role is a client role, the C(clientId) (not ID of the client) must be specified. + default: [] + type: list + elements: dict + version_added: 7.1.0 + suboptions: + name: + description: + - Name of the role. This can be the name of a REALM role or a client role. + type: str + required: true + client_id: + description: + - Client ID if the role is a client role. Do not include this option for a REALM role. + - Use the client ID you can see in the Keycloak console, not the technical ID of the client. + type: str + required: false + aliases: + - clientId + state: + description: + - Create the composite if present, remove it if absent. + type: str + choices: + - present + - absent + default: present extends_documentation_fragment: - middleware_automation.keycloak.keycloak @@ -142,14 +178,14 @@ EXAMPLES = ''' auth_password: PASSWORD name: my-new-role attributes: - attrib1: value1 - attrib2: value2 - attrib3: - - with - - numerous - - individual - - list - - items + attrib1: value1 + attrib2: value2 + attrib3: + - with + - numerous + - individual + - list + - items delegate_to: localhost ''' @@ -198,8 +234,9 @@ end_state: ''' from ansible_collections.middleware_automation.keycloak.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, camel, \ - keycloak_argument_spec, get_token, KeycloakError + keycloak_argument_spec, get_token, KeycloakError, is_struct_included from ansible.module_utils.basic import AnsibleModule +import copy def main(): @@ -210,6 +247,12 @@ def main(): """ argument_spec = keycloak_argument_spec() + composites_spec = dict( + name=dict(type='str', required=True), + client_id=dict(type='str', aliases=['clientId'], required=False), + state=dict(type='str', default='present', choices=['present', 'absent']) + ) + meta_args = dict( state=dict(type='str', default='present', choices=['present', 'absent']), name=dict(type='str', required=True), @@ -217,6 +260,8 @@ def main(): realm=dict(type='str', default='master'), client_id=dict(type='str'), attributes=dict(type='dict'), + composites=dict(type='list', default=[], options=composites_spec, elements='dict'), + composite=dict(type='bool', default=False), ) argument_spec.update(meta_args) @@ -250,7 +295,7 @@ def main(): # Filter and map the parameters names that apply to the role role_params = [x for x in module.params - if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'client_id', 'composites'] and + if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'client_id'] and module.params.get(x) is not None] # See if it already exists in Keycloak @@ -269,10 +314,10 @@ def main(): new_param_value = module.params.get(param) old_value = before_role[param] if param in before_role else None if new_param_value != old_value: - changeset[camel(param)] = new_param_value + changeset[camel(param)] = copy.deepcopy(new_param_value) # Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis) - desired_role = before_role.copy() + desired_role = copy.deepcopy(before_role) desired_role.update(changeset) result['proposed'] = changeset @@ -309,6 +354,9 @@ def main(): kc.create_client_role(desired_role, clientid, realm) after_role = kc.get_client_role(name, clientid, realm) + if after_role['composite']: + after_role['composites'] = kc.get_role_composites(rolerep=after_role, clientid=clientid, realm=realm) + result['end_state'] = after_role result['msg'] = 'Role {name} has been created'.format(name=name) @@ -316,10 +364,25 @@ def main(): else: if state == 'present': + compare_exclude = [] + if 'composites' in desired_role and isinstance(desired_role['composites'], list) and len(desired_role['composites']) > 0: + composites = kc.get_role_composites(rolerep=before_role, clientid=clientid, realm=realm) + before_role['composites'] = [] + for composite in composites: + before_composite = {} + if composite['clientRole']: + composite_client = kc.get_client_by_id(id=composite['containerId'], realm=realm) + before_composite['client_id'] = composite_client['clientId'] + else: + before_composite['client_id'] = None + before_composite['name'] = composite['name'] + before_composite['state'] = 'present' + before_role['composites'].append(before_composite) + else: + compare_exclude.append('composites') # Process an update - # no changes - if desired_role == before_role: + if is_struct_included(desired_role, before_role, exclude=compare_exclude): result['changed'] = False result['end_state'] = desired_role result['msg'] = "No changes required to role {name}.".format(name=name) @@ -341,6 +404,8 @@ def main(): else: kc.update_client_role(desired_role, clientid, realm) after_role = kc.get_client_role(name, clientid, realm) + if after_role['composite']: + after_role['composites'] = kc.get_role_composites(rolerep=after_role, clientid=clientid, realm=realm) result['end_state'] = after_role diff --git a/plugins/modules/keycloak_user_federation.py b/plugins/modules/keycloak_user_federation.py index 36fe440..864cfbc 100644 --- a/plugins/modules/keycloak_user_federation.py +++ b/plugins/modules/keycloak_user_federation.py @@ -36,9 +36,9 @@ options: state: description: - State of the user federation. - - On C(present), the user federation will be created if it does not yet exist, or updated with + - On V(present), the user federation will be created if it does not yet exist, or updated with the parameters you provide. - - On C(absent), the user federation will be removed if it exists. + - On V(absent), the user federation will be removed if it exists. default: 'present' type: str choices: @@ -54,7 +54,7 @@ options: id: description: - The unique ID for this user federation. If left empty, the user federation will be searched - by its I(name). + by its O(name). type: str name: @@ -64,18 +64,15 @@ options: provider_id: description: - - Provider for this user federation. + - Provider for this user federation. Built-in providers are V(ldap), V(kerberos), and V(sssd). + Custom user storage providers can also be used. aliases: - providerId type: str - choices: - - ldap - - kerberos - - sssd provider_type: description: - - Component type for user federation (only supported value is C(org.keycloak.storage.UserStorageProvider)). + - Component type for user federation (only supported value is V(org.keycloak.storage.UserStorageProvider)). aliases: - providerType default: org.keycloak.storage.UserStorageProvider @@ -88,13 +85,37 @@ options: - parentId type: str + remove_unspecified_mappers: + description: + - Remove mappers that are not specified in the configuration for this federation. + - Set to V(false) to keep mappers that are not listed in O(mappers). + type: bool + default: true + + bind_credential_update_mode: + description: + - The value of the config parameter O(config.bindCredential) is redacted in the Keycloak responses. + Comparing the redacted value with the desired value always evaluates to not equal. This means + the before and desired states are never equal if the parameter is set. + - Set to V(always) to include O(config.bindCredential) in the comparison of before and desired state. + Because of the redacted value returned by Keycloak the module will always detect a change + and make an update if a O(config.bindCredential) value is set. + - Set to V(only_indirect) to exclude O(config.bindCredential) when comparing the before state with the + desired state. The value of O(config.bindCredential) will only be updated if there are other changes + to the user federation that require an update. + type: str + default: always + choices: + - always + - only_indirect + config: description: - Dict specifying the configuration options for the provider; the contents differ depending on - the value of I(provider_id). Examples are given below for C(ldap), C(kerberos) and C(sssd). + the value of O(provider_id). Examples are given below for V(ldap), V(kerberos) and V(sssd). It is easiest to obtain valid config values by dumping an already-existing user federation - configuration through check-mode in the I(existing) field. - - The value C(sssd) has been supported since middleware_automation.keycloak 1.0.0. + configuration through check-mode in the RV(existing) field. + - The value V(sssd) has been supported since middleware_automation.keycloak 2.0.0. type: dict suboptions: enabled: @@ -111,15 +132,15 @@ options: importEnabled: description: - - If C(true), LDAP users will be imported into Keycloak DB and synced by the configured + - If V(true), LDAP users will be imported into Keycloak DB and synced by the configured sync policies. default: true type: bool editMode: description: - - C(READ_ONLY) is a read-only LDAP store. C(WRITABLE) means data will be synced back to LDAP - on demand. C(UNSYNCED) means user data will be imported, but not synced back to LDAP. + - V(READ_ONLY) is a read-only LDAP store. V(WRITABLE) means data will be synced back to LDAP + on demand. V(UNSYNCED) means user data will be imported, but not synced back to LDAP. type: str choices: - READ_ONLY @@ -136,13 +157,13 @@ options: vendor: description: - LDAP vendor (provider). - - Use short name. For instance, write C(rhds) for "Red Hat Directory Server". + - Use short name. For instance, write V(rhds) for "Red Hat Directory Server". type: str usernameLDAPAttribute: description: - Name of LDAP attribute, which is mapped as Keycloak username. For many LDAP server - vendors it can be C(uid). For Active directory it can be C(sAMAccountName) or C(cn). + vendors it can be V(uid). For Active directory it can be V(sAMAccountName) or V(cn). The attribute should be filled for all LDAP user records you want to import from LDAP to Keycloak. type: str @@ -151,15 +172,15 @@ options: description: - Name of LDAP attribute, which is used as RDN (top attribute) of typical user DN. Usually it's the same as Username LDAP attribute, however it is not required. For - example for Active directory, it is common to use C(cn) as RDN attribute when - username attribute might be C(sAMAccountName). + example for Active directory, it is common to use V(cn) as RDN attribute when + username attribute might be V(sAMAccountName). type: str uuidLDAPAttribute: description: - Name of LDAP attribute, which is used as unique object identifier (UUID) for objects - in LDAP. For many LDAP server vendors, it is C(entryUUID); however some are different. - For example for Active directory it should be C(objectGUID). If your LDAP server does + in LDAP. For many LDAP server vendors, it is V(entryUUID); however some are different. + For example for Active directory it should be V(objectGUID). If your LDAP server does not support the notion of UUID, you can use any other attribute that is supposed to be unique among LDAP users in tree. type: str @@ -167,7 +188,7 @@ options: userObjectClasses: description: - All values of LDAP objectClass attribute for users in LDAP divided by comma. - For example C(inetOrgPerson, organizationalPerson). Newly created Keycloak users + For example V(inetOrgPerson, organizationalPerson). Newly created Keycloak users will be written to LDAP with all those object classes and existing LDAP user records are found just if they contain all those object classes. type: str @@ -251,8 +272,8 @@ options: useTruststoreSpi: description: - Specifies whether LDAP connection will use the truststore SPI with the truststore - configured in standalone.xml/domain.xml. C(Always) means that it will always use it. - C(Never) means that it will not use it. C(Only for ldaps) means that it will use if + configured in standalone.xml/domain.xml. V(always) means that it will always use it. + V(never) means that it will not use it. V(ldapsOnly) means that it will use if your connection URL use ldaps. Note even if standalone.xml/domain.xml is not configured, the default Java cacerts or certificate specified by C(javax.net.ssl.trustStore) property will be used. @@ -297,7 +318,7 @@ options: connectionPoolingDebug: description: - A string that indicates the level of debug output to produce. Example valid values are - C(fine) (trace connection creation and removal) and C(all) (all debugging information). + V(fine) (trace connection creation and removal) and V(all) (all debugging information). type: str connectionPoolingInitSize: @@ -321,7 +342,7 @@ options: connectionPoolingProtocol: description: - A list of space-separated protocol types of connections that may be pooled. - Valid types are C(plain) and C(ssl). + Valid types are V(plain) and V(ssl). type: str connectionPoolingTimeout: @@ -342,17 +363,26 @@ options: - Name of kerberos realm. type: str + krbPrincipalAttribute: + description: + - Name of the LDAP attribute, which refers to Kerberos principal. + This is used to lookup appropriate LDAP user after successful Kerberos/SPNEGO authentication in Keycloak. + When this is empty, the LDAP user will be looked based on LDAP username corresponding + to the first part of his Kerberos principal. For instance, for principal C(john@KEYCLOAK.ORG), + it will assume that LDAP username is V(john). + type: str + serverPrincipal: description: - Full name of server principal for HTTP service including server and domain name. For - example C(HTTP/host.foo.org@FOO.ORG). Use C(*) to accept any service principal in the + example V(HTTP/host.foo.org@FOO.ORG). Use V(*) to accept any service principal in the KeyTab file. type: str keyTab: description: - Location of Kerberos KeyTab file containing the credentials of server principal. For - example C(/etc/krb5.keytab). + example V(/etc/krb5.keytab). type: str debug: @@ -427,6 +457,16 @@ options: - Max lifespan of cache entry in milliseconds. type: int + referral: + description: + - Specifies if LDAP referrals should be followed or ignored. Please note that enabling + referrals can slow down authentication as it allows the LDAP server to decide which other + LDAP servers to use. This could potentially include untrusted servers. + type: str + choices: + - ignore + - follow + mappers: description: - A list of dicts defining mappers associated with this Identity Provider. @@ -451,7 +491,7 @@ options: providerId: description: - - The mapper type for this mapper (for instance C(user-attribute-ldap-mapper)). + - The mapper type for this mapper (for instance V(user-attribute-ldap-mapper)). type: str providerType: @@ -534,14 +574,14 @@ EXAMPLES = ''' provider_id: kerberos provider_type: org.keycloak.storage.UserStorageProvider config: - priority: 0 - enabled: true - cachePolicy: DEFAULT - kerberosRealm: EXAMPLE.COM - serverPrincipal: HTTP/host.example.com@EXAMPLE.COM - keyTab: keytab - allowPasswordAuthentication: false - updateProfileFirstLogin: false + priority: 0 + enabled: true + cachePolicy: DEFAULT + kerberosRealm: EXAMPLE.COM + serverPrincipal: HTTP/host.example.com@EXAMPLE.COM + keyTab: keytab + allowPasswordAuthentication: false + updateProfileFirstLogin: false - name: Create sssd user federation middleware_automation.keycloak.keycloak_user_federation: @@ -704,16 +744,27 @@ from ansible.module_utils.six.moves.urllib.parse import urlencode from copy import deepcopy +def normalize_kc_comp(comp): + if 'config' in comp: + # kc completely removes the parameter `krbPrincipalAttribute` if it is set to `''`; the unset kc parameter is equivalent to `''`; + # to make change detection and diff more accurate we set it again in the kc responses + if 'krbPrincipalAttribute' not in comp['config']: + comp['config']['krbPrincipalAttribute'] = [''] + + # kc stores a timestamp of the last sync in `lastSync` to time the periodic sync, it is removed to minimize diff/changes + comp['config'].pop('lastSync', None) + + def sanitize(comp): compcopy = deepcopy(comp) if 'config' in compcopy: - compcopy['config'] = dict((k, v[0]) for k, v in compcopy['config'].items()) + compcopy['config'] = {k: v[0] for k, v in compcopy['config'].items()} if 'bindCredential' in compcopy['config']: compcopy['config']['bindCredential'] = '**********' if 'mappers' in compcopy: for mapper in compcopy['mappers']: if 'config' in mapper: - mapper['config'] = dict((k, v[0]) for k, v in mapper['config'].items()) + mapper['config'] = {k: v[0] for k, v in mapper['config'].items()} return compcopy @@ -760,8 +811,10 @@ def main(): priority=dict(type='int', default=0), rdnLDAPAttribute=dict(type='str'), readTimeout=dict(type='int'), + referral=dict(type='str', choices=['ignore', 'follow']), searchScope=dict(type='str', choices=['1', '2'], default='1'), serverPrincipal=dict(type='str'), + krbPrincipalAttribute=dict(type='str'), startTls=dict(type='bool', default=False), syncRegistrations=dict(type='bool', default=False), trustEmail=dict(type='bool', default=False), @@ -792,9 +845,11 @@ def main(): realm=dict(type='str', default='master'), id=dict(type='str'), name=dict(type='str'), - provider_id=dict(type='str', aliases=['providerId'], choices=['ldap', 'kerberos', 'sssd']), + provider_id=dict(type='str', aliases=['providerId']), provider_type=dict(type='str', aliases=['providerType'], default='org.keycloak.storage.UserStorageProvider'), parent_id=dict(type='str', aliases=['parentId']), + remove_unspecified_mappers=dict(type='bool', default=True), + bind_credential_update_mode=dict(type='str', default='always', choices=['always', 'only_indirect']), mappers=dict(type='list', elements='dict', options=mapper_spec), ) @@ -825,19 +880,26 @@ def main(): # Keycloak API expects config parameters to be arrays containing a single string element if config is not None: - module.params['config'] = dict((k, [str(v).lower() if not isinstance(v, str) else v]) - for k, v in config.items() if config[k] is not None) + module.params['config'] = { + k: [str(v).lower() if not isinstance(v, str) else v] + for k, v in config.items() + if config[k] is not None + } if mappers is not None: for mapper in mappers: if mapper.get('config') is not None: - mapper['config'] = dict((k, [str(v).lower() if not isinstance(v, str) else v]) - for k, v in mapper['config'].items() if mapper['config'][k] is not None) + mapper['config'] = { + k: [str(v).lower() if not isinstance(v, str) else v] + for k, v in mapper['config'].items() + if mapper['config'][k] is not None + } # Filter and map the parameters names that apply comp_params = [x for x in module.params - if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'mappers'] and - module.params.get(x) is not None] + if x not in list(keycloak_argument_spec().keys()) + + ['state', 'realm', 'mappers', 'remove_unspecified_mappers', 'bind_credential_update_mode'] + and module.params.get(x) is not None] # See if it already exists in Keycloak if cid is None: @@ -855,7 +917,9 @@ def main(): # if user federation exists, get associated mappers if cid is not None and before_comp: - before_comp['mappers'] = sorted(kc.get_components(urlencode(dict(parent=cid)), realm), key=lambda x: x.get('name')) + before_comp['mappers'] = sorted(kc.get_components(urlencode(dict(parent=cid)), realm), key=lambda x: x.get('name') or '') + + normalize_kc_comp(before_comp) # Build a proposed changeset from parameters given to this module changeset = {} @@ -864,7 +928,7 @@ def main(): new_param_value = module.params.get(param) old_value = before_comp[camel(param)] if camel(param) in before_comp else None if param == 'mappers': - new_param_value = [dict((k, v) for k, v in x.items() if x[k] is not None) for x in new_param_value] + new_param_value = [{k: v for k, v in x.items() if v is not None} for x in new_param_value] if new_param_value != old_value: changeset[camel(param)] = new_param_value @@ -873,17 +937,17 @@ def main(): if module.params['provider_id'] in ['kerberos', 'sssd']: module.fail_json(msg='Cannot configure mappers for {type} provider.'.format(type=module.params['provider_id'])) for change in module.params['mappers']: - change = dict((k, v) for k, v in change.items() if change[k] is not None) + change = {k: v for k, v in change.items() if v is not None} if change.get('id') is None and change.get('name') is None: module.fail_json(msg='Either `name` or `id` has to be specified on each mapper.') if cid is None: old_mapper = {} elif change.get('id') is not None: - old_mapper = kc.get_component(change['id'], realm) + old_mapper = next((before_mapper for before_mapper in before_comp.get('mappers', []) if before_mapper["id"] == change['id']), None) if old_mapper is None: old_mapper = {} else: - found = kc.get_components(urlencode(dict(parent=cid, name=change['name'])), realm) + found = [before_mapper for before_mapper in before_comp.get('mappers', []) if before_mapper['name'] == change['name']] if len(found) > 1: module.fail_json(msg='Found multiple mappers with name `{name}`. Cannot continue.'.format(name=change['name'])) if len(found) == 1: @@ -892,10 +956,16 @@ def main(): old_mapper = {} new_mapper = old_mapper.copy() new_mapper.update(change) - if new_mapper != old_mapper: - if changeset.get('mappers') is None: - changeset['mappers'] = list() - changeset['mappers'].append(new_mapper) + # changeset contains all desired mappers: those existing, to update or to create + if changeset.get('mappers') is None: + changeset['mappers'] = list() + changeset['mappers'].append(new_mapper) + changeset['mappers'] = sorted(changeset['mappers'], key=lambda x: x.get('name') or '') + + # to keep unspecified existing mappers we add them to the desired mappers list, unless they're already present + if not module.params['remove_unspecified_mappers'] and 'mappers' in before_comp: + changeset_mapper_ids = [mapper['id'] for mapper in changeset['mappers'] if 'id' in mapper] + changeset['mappers'].extend([mapper for mapper in before_comp['mappers'] if mapper['id'] not in changeset_mapper_ids]) # Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis) desired_comp = before_comp.copy() @@ -918,50 +988,68 @@ def main(): # Process a creation result['changed'] = True - if module._diff: - result['diff'] = dict(before='', after=sanitize(desired_comp)) - if module.check_mode: + if module._diff: + result['diff'] = dict(before='', after=sanitize(desired_comp)) module.exit_json(**result) # create it - desired_comp = desired_comp.copy() - updated_mappers = desired_comp.pop('mappers', []) + desired_mappers = desired_comp.pop('mappers', []) after_comp = kc.create_component(desired_comp, realm) - cid = after_comp['id'] + updated_mappers = [] + # when creating a user federation, keycloak automatically creates default mappers + default_mappers = kc.get_components(urlencode(dict(parent=cid)), realm) - for mapper in updated_mappers: - found = kc.get_components(urlencode(dict(parent=cid, name=mapper['name'])), realm) + # create new mappers or update existing default mappers + for desired_mapper in desired_mappers: + found = [default_mapper for default_mapper in default_mappers if default_mapper['name'] == desired_mapper['name']] if len(found) > 1: - module.fail_json(msg='Found multiple mappers with name `{name}`. Cannot continue.'.format(name=mapper['name'])) + module.fail_json(msg='Found multiple mappers with name `{name}`. Cannot continue.'.format(name=desired_mapper['name'])) if len(found) == 1: old_mapper = found[0] else: old_mapper = {} new_mapper = old_mapper.copy() - new_mapper.update(mapper) + new_mapper.update(desired_mapper) if new_mapper.get('id') is not None: kc.update_component(new_mapper, realm) + updated_mappers.append(new_mapper) else: if new_mapper.get('parentId') is None: - new_mapper['parentId'] = after_comp['id'] - mapper = kc.create_component(new_mapper, realm) + new_mapper['parentId'] = cid + updated_mappers.append(kc.create_component(new_mapper, realm)) - after_comp['mappers'] = updated_mappers + if module.params['remove_unspecified_mappers']: + # we remove all unwanted default mappers + # we use ids so we dont accidently remove one of the previously updated default mapper + for default_mapper in default_mappers: + if not default_mapper['id'] in [x['id'] for x in updated_mappers]: + kc.delete_component(default_mapper['id'], realm) + + after_comp['mappers'] = kc.get_components(urlencode(dict(parent=cid)), realm) + normalize_kc_comp(after_comp) + if module._diff: + result['diff'] = dict(before='', after=sanitize(after_comp)) result['end_state'] = sanitize(after_comp) - - result['msg'] = "User federation {id} has been created".format(id=after_comp['id']) + result['msg'] = "User federation {id} has been created".format(id=cid) module.exit_json(**result) else: if state == 'present': # Process an update + desired_copy = deepcopy(desired_comp) + before_copy = deepcopy(before_comp) + # exclude bindCredential when checking wether an update is required, therefore + # updating it only if there are other changes + if module.params['bind_credential_update_mode'] == 'only_indirect': + desired_copy.get('config', []).pop('bindCredential', None) + before_copy.get('config', []).pop('bindCredential', None) # no changes - if desired_comp == before_comp: + if desired_copy == before_copy: result['changed'] = False result['end_state'] = sanitize(desired_comp) result['msg'] = "No changes required to user federation {id}.".format(id=cid) @@ -977,22 +1065,33 @@ def main(): module.exit_json(**result) # do the update - desired_comp = desired_comp.copy() - updated_mappers = desired_comp.pop('mappers', []) + desired_mappers = desired_comp.pop('mappers', []) kc.update_component(desired_comp, realm) - after_comp = kc.get_component(cid, realm) - for mapper in updated_mappers: + for before_mapper in before_comp.get('mappers', []): + # remove unwanted existing mappers that will not be updated + if not before_mapper['id'] in [x['id'] for x in desired_mappers if 'id' in x]: + kc.delete_component(before_mapper['id'], realm) + + for mapper in desired_mappers: + if mapper in before_comp.get('mappers', []): + continue if mapper.get('id') is not None: kc.update_component(mapper, realm) else: if mapper.get('parentId') is None: mapper['parentId'] = desired_comp['id'] - mapper = kc.create_component(mapper, realm) - - after_comp['mappers'] = updated_mappers - result['end_state'] = sanitize(after_comp) + kc.create_component(mapper, realm) + after_comp = kc.get_component(cid, realm) + after_comp['mappers'] = sorted(kc.get_components(urlencode(dict(parent=cid)), realm), key=lambda x: x.get('name') or '') + normalize_kc_comp(after_comp) + after_comp_sanitized = sanitize(after_comp) + before_comp_sanitized = sanitize(before_comp) + result['end_state'] = after_comp_sanitized + if module._diff: + result['diff'] = dict(before=before_comp_sanitized, after=after_comp_sanitized) + result['changed'] = before_comp_sanitized != after_comp_sanitized result['msg'] = "User federation {id} has been updated".format(id=cid) module.exit_json(**result) From 5f1b43f37ba6dcf409a38ec4958d497c7a25578b Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Mon, 14 Oct 2024 10:34:20 +0200 Subject: [PATCH 077/135] update docs --- .github/workflows/traffic.yml | 14 ++++----- CHANGELOG.rst | 6 ++-- CONTRIBUTING.md | 34 ++++++++++++++++++++ README.md | 14 ++++----- docs/_gh_include/footer.inc | 4 +-- docs/index.rst | 16 +++------- docs/testing.md | 29 ++--------------- molecule/debian/converge.yml | 59 ++++++++++++++++++----------------- 8 files changed, 91 insertions(+), 85 deletions(-) diff --git a/.github/workflows/traffic.yml b/.github/workflows/traffic.yml index b4f245e..d997f4e 100644 --- a/.github/workflows/traffic.yml +++ b/.github/workflows/traffic.yml @@ -1,9 +1,9 @@ name: Collect traffic stats on: - schedule: + schedule: - cron: "51 23 * * 0" - workflow_dispatch: - + workflow_dispatch: + jobs: traffic: runs-on: ubuntu-latest @@ -11,12 +11,12 @@ jobs: - uses: actions/checkout@v2 with: ref: "gh-pages" - - - name: GitHub traffic + + - name: GitHub traffic uses: sangonzal/repository-traffic-action@v.0.1.6 env: - TRAFFIC_ACTION_TOKEN: ${{ secrets.TRIGGERING_PAT }} - + TRAFFIC_ACTION_TOKEN: ${{ secrets.TRIGGERING_PAT }} + - name: Commit changes uses: EndBug/add-and-commit@v4 with: diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7283c76..aafdac2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,6 @@ -============================================= -middleware\_automation.keycloak Release Notes -============================================= +============= +Release Notes +============= .. contents:: Topics diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aee36a6..21ddc18 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,37 @@ +## Developing + +### Build and install locally + +Clone the repository, checkout the tag you want to build, or pick the main branch for the development version; then: + + ansible-galaxy collection build . + ansible-galaxy collection install middleware_automation-keycloak-*.tar.gz + + +### Development environment + +Make sure your development machine has avilable: + +* python 3.11+ +* virtualenv +* docker (or podman) + +In order to run setup the development environment and run the molecule tests locally, after cloning the repository: + +``` +# create new virtualenv using python 3 +virtualenv $PATH_TO_DEV_VIRTUALENV +# activate the virtual env +source $PATH_TO_DEV_VIRTUALENV/bin/activate +# install ansible and tools onto the virtualenv +pip install yamllint 'molecule>=6.0' 'molecule-plugins[docker]' 'ansible-core>=2.15' ansible-lint +# install collection dependencies +ansible-galaxy collection install -r requirements.yml +# install python dependencies +pip install -r requirements.txt molecule/requirements.txt +# execute the tests (replace --all with -s subdirectory to run a single test) +molecule test --all +``` ## Contributor's Guidelines diff --git a/README.md b/README.md index 3827b90..bca0270 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build Status](https://github.com/ansible-middleware/keycloak/workflows/CI/badge.svg?branch=main)](https://github.com/ansible-middleware/keycloak/actions/workflows/ci.yml) -> **_NOTE:_ If you are Red Hat customer, install `redhat.sso` (for Red Hat Single Sign-On) or `redhat.rhbk` (for Red Hat Build of Keycloak) from [Automation Hub](https://console.redhat.com/ansible/ansible-dashboard) as the certified version of this collection.** +> **_NOTE:_ If you are Red Hat customer, install `redhat.rhbk` (for Red Hat Build of Keycloak) or `redhat.sso` (for Red Hat Single Sign-On) from [Automation Hub](https://console.redhat.com/ansible/ansible-dashboard) as the certified version of this collection.** @@ -49,9 +49,10 @@ A requirement file is provided to install: ### Included roles -* [`keycloak`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak/README.md): role for installing the service (keycloak <= 19.0). -* [`keycloak_realm`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_realm/README.md): role for configuring a realm, user federation(s), clients and users, in an installed service. -* [`keycloak_quarkus`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_quarkus/README.md): role for installing the quarkus variant of keycloak (>= 17.0.0). +* `keycloak_quarkus`: role for installing keycloak (>= 19.0.0, quarkus based). +* `keycloak_realm`: role for configuring a realm, user federation(s), clients and users, in an installed service. +* `keycloak`: role for installing legacy keycloak (<= 19.0, wildfly based). + ## Usage @@ -61,7 +62,7 @@ A requirement file is provided to install: * [`playbooks/keycloak.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak.yml) installs keycloak legacy based on the defined variables (using most defaults). * [`playbooks/keycloak_quarkus.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_quarkus.yml) installs keycloak >= 17 based on the defined variables (using most defaults). - + Both playbooks include the `keycloak` role, with different settings, as described in the following sections. For full service configuration details, refer to the [keycloak role README](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak/README.md). @@ -92,7 +93,7 @@ Execute the following command from the source root directory ``` ansible-playbook -i -e @rhn-creds.yml playbooks/keycloak.yml -e keycloak_admin_password= -``` +``` - `keycloak_admin_password` Password for the administration console user account. - `ansible_hosts` is the inventory, below is an example inventory for deploying to localhost @@ -143,4 +144,3 @@ Apache License v2.0 or later See [LICENSE](LICENSE) to view the full text. - diff --git a/docs/_gh_include/footer.inc b/docs/_gh_include/footer.inc index 73bac34..11c1cfe 100644 --- a/docs/_gh_include/footer.inc +++ b/docs/_gh_include/footer.inc @@ -7,7 +7,7 @@
-

© Copyright 2022, Red Hat, Inc.

+

© Copyright 2024, Red Hat, Inc.

Built with Sphinx using a theme @@ -18,4 +18,4 @@ - \ No newline at end of file + diff --git a/docs/index.rst b/docs/index.rst index 38dec5b..6c46ab1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,31 +10,25 @@ Welcome to Keycloak Collection documentation README plugins/index roles/index + Changelog .. toctree:: :maxdepth: 2 :caption: Developer documentation - testing - developing - releasing - -.. toctree:: - :maxdepth: 2 - :caption: General - - Changelog + Developing + Testing + Releasing .. toctree:: :maxdepth: 2 :caption: Middleware collections - Infinispan / Red Hat Data Grid Keycloak / Red Hat Single Sign-On + Infinispan / Red Hat Data Grid Wildfly / Red Hat JBoss EAP Tomcat / Red Hat JWS ActiveMQ / Red Hat AMQ Broker Kafka / Red Hat AMQ Streams Ansible Middleware utilities - Red Hat CSP Download JCliff diff --git a/docs/testing.md b/docs/testing.md index 1d06d7f..8e773ea 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -4,24 +4,7 @@ The collection is tested with a [molecule](https://github.com/ansible-community/molecule) setup covering the included roles and verifying correct installation and idempotency. In order to run the molecule tests locally with python 3.9 available, after cloning the repository: - -``` -pip install yamllint 'molecule[docker]~=3.5.2' ansible-core flake8 ansible-lint voluptuous -molecule test --all -``` - - -## Integration testing - -Demo repositories which depend on the collection, and aggregate functionality with other middleware_automation collections, are automatically rebuilt -at every collection release to ensure non-breaking changes and consistent behaviour. - -The repository are: - - - [Flange demo](https://github.com/ansible-middleware/flange-demo) - A deployment of Wildfly cluster integrated with keycloak and infinispan. - - [CrossDC keycloak demo](https://github.com/ansible-middleware/cross-dc-rhsso-demo) - A clustered multi-regional installation of keycloak with infinispan remote caches. +The test scenarios are available on the source code repository each on his own subdirectory under [molecule/](https://github.com/ansible-middleware/keycloak/molecule). ## Test playbooks @@ -29,15 +12,7 @@ The repository are: Sample playbooks are provided in the `playbooks/` directory; to run the playbooks locally (requires a rhel system with python 3.9+, ansible, and systemd) the steps are as follows: ``` -# setup environment -pip install ansible-core -# clone the repository -git clone https://github.com/ansible-middleware/keycloak -cd keycloak -# install collection dependencies -ansible-galaxy collection install -r requirements.yml -# install collection python deps -pip install -r requirements.txt +# setup environment as in developing # create inventory for localhost cat << EOF > inventory [keycloak] diff --git a/molecule/debian/converge.yml b/molecule/debian/converge.yml index e6319b7..5b60408 100644 --- a/molecule/debian/converge.yml +++ b/molecule/debian/converge.yml @@ -7,36 +7,39 @@ keycloak_realm: TestRealm keycloak_quarkus_log: file keycloak_quarkus_frontend_url: 'http://localhost:8080/' - keycloak_quarkus_start_dev: True + keycloak_quarkus_start_dev: true keycloak_quarkus_proxy_mode: none - keycloak_client_default_roles: - - TestRoleAdmin - - TestRoleUser - keycloak_client_users: - - username: TestUser - password: password - client_roles: - - client: TestClient - role: TestRoleUser - - username: TestAdmin - password: password - client_roles: - - client: TestClient - role: TestRoleUser - - client: TestClient - role: TestRoleAdmin - keycloak_clients: - - name: TestClient - roles: "{{ keycloak_client_default_roles }}" - public_client: "{{ keycloak_client_public }}" - web_origins: "{{ keycloak_client_web_origins }}" - users: "{{ keycloak_client_users }}" - client_id: TestClient - attributes: - post.logout.redirect.uris: '/public/logout' roles: - role: keycloak_quarkus - role: keycloak_realm - keycloak_realm: TestRealm - keycloak_admin_password: "remembertochangeme" keycloak_context: '' + keycloak_client_default_roles: + - TestRoleAdmin + - TestRoleUser + keycloak_client_users: + - username: TestUser + password: password + client_roles: + - client: TestClient + role: TestRoleUser + realm: "{{ keycloak_realm }}" + - username: TestAdmin + password: password + client_roles: + - client: TestClient + role: TestRoleUser + realm: "{{ keycloak_realm }}" + - client: TestClient + role: TestRoleAdmin + realm: "{{ keycloak_realm }}" + keycloak_realm: TestRealm + keycloak_clients: + - name: TestClient + roles: "{{ keycloak_client_default_roles }}" + realm: "{{ keycloak_realm }}" + public_client: "{{ keycloak_client_public }}" + web_origins: "{{ keycloak_client_web_origins }}" + users: "{{ keycloak_client_users }}" + client_id: TestClient + attributes: + post.logout.redirect.uris: '/public/logout' From be19ec1289805dea625d93e5b36357e1004f0e46 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Mon, 14 Oct 2024 10:41:17 +0200 Subject: [PATCH 078/135] update tests --- README.md | 2 +- molecule/debian/converge.yml | 8 +- molecule/default/converge.yml | 6 +- plugins/modules/keycloak_realm.py | 848 +++++++++++++++++++ roles/keycloak_realm/README.md | 2 +- roles/keycloak_realm/defaults/main.yml | 2 +- roles/keycloak_realm/meta/argument_specs.yml | 2 +- roles/keycloak_realm/tasks/main.yml | 4 +- 8 files changed, 857 insertions(+), 17 deletions(-) create mode 100644 plugins/modules/keycloak_realm.py diff --git a/README.md b/README.md index bca0270..e8c289e 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,8 @@ A requirement file is provided to install: ### Install Playbook -* [`playbooks/keycloak.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak.yml) installs keycloak legacy based on the defined variables (using most defaults). * [`playbooks/keycloak_quarkus.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_quarkus.yml) installs keycloak >= 17 based on the defined variables (using most defaults). +* [`playbooks/keycloak.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak.yml) installs keycloak legacy based on the defined variables (using most defaults). Both playbooks include the `keycloak` role, with different settings, as described in the following sections. diff --git a/molecule/debian/converge.yml b/molecule/debian/converge.yml index 5b60408..1a7d267 100644 --- a/molecule/debian/converge.yml +++ b/molecule/debian/converge.yml @@ -4,18 +4,15 @@ vars: keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_admin_pass: "remembertochangeme" - keycloak_realm: TestRealm + keycloak_admin_password: "remembertochangeme" + keycloak_quarkus_host: instance keycloak_quarkus_log: file - keycloak_quarkus_frontend_url: 'http://localhost:8080/' keycloak_quarkus_start_dev: true keycloak_quarkus_proxy_mode: none roles: - role: keycloak_quarkus - role: keycloak_realm keycloak_context: '' - keycloak_client_default_roles: - - TestRoleAdmin - - TestRoleUser keycloak_client_users: - username: TestUser password: password @@ -35,7 +32,6 @@ keycloak_realm: TestRealm keycloak_clients: - name: TestClient - roles: "{{ keycloak_client_default_roles }}" realm: "{{ keycloak_realm }}" public_client: "{{ keycloak_client_public }}" web_origins: "{{ keycloak_client_web_origins }}" diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index bb8c552..417713f 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -9,7 +9,7 @@ keycloak_quarkus_log: file keycloak_quarkus_log_level: debug keycloak_quarkus_log_target: /tmp/keycloak - keycloak_quarkus_start_dev: True + keycloak_quarkus_start_dev: true keycloak_quarkus_proxy_mode: none keycloak_quarkus_offline_install: true keycloak_quarkus_download_path: /tmp/keycloak/ @@ -17,9 +17,6 @@ - role: keycloak_quarkus - role: keycloak_realm keycloak_context: '' - keycloak_client_default_roles: - - TestRoleAdmin - - TestRoleUser keycloak_client_users: - username: TestUser password: password @@ -39,7 +36,6 @@ keycloak_realm: TestRealm keycloak_clients: - name: TestClient - roles: "{{ keycloak_client_default_roles }}" realm: "{{ keycloak_realm }}" public_client: "{{ keycloak_client_public }}" web_origins: "{{ keycloak_client_web_origins }}" diff --git a/plugins/modules/keycloak_realm.py b/plugins/modules/keycloak_realm.py new file mode 100644 index 0000000..a22795b --- /dev/null +++ b/plugins/modules/keycloak_realm.py @@ -0,0 +1,848 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2017, Eike Frost +# Copyright (c) 2021, Christophe Gilles +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = ''' +--- +module: keycloak_realm + +short_description: Allows administration of Keycloak realm via Keycloak API + +version_added: 3.0.0 + +description: + - This module allows the administration of Keycloak realm via the Keycloak REST API. It + requires access to the REST API via OpenID Connect; the user connecting and the realm being + used must have the requisite access rights. In a default Keycloak installation, admin-cli + and an admin user would work, as would a separate realm definition with the scope tailored + to your needs and a user having the expected roles. + + - The names of module options are snake_cased versions of the camelCase ones found in the + Keycloak API and its documentation at U(https://www.keycloak.org/docs-api/8.0/rest-api/index.html). + Aliases are provided so camelCased versions can be used as well. + + - The Keycloak API does not always sanity check inputs e.g. you can set + SAML-specific settings on an OpenID Connect client for instance and vice versa. Be careful. + If you do not specify a setting, usually a sensible default is chosen. + +attributes: + check_mode: + support: full + diff_mode: + support: full + +options: + state: + description: + - State of the realm. + - On V(present), the realm will be created (or updated if it exists already). + - On V(absent), the realm will be removed if it exists. + choices: ['present', 'absent'] + default: 'present' + type: str + + id: + description: + - The realm to create. + type: str + realm: + description: + - The realm name. + type: str + access_code_lifespan: + description: + - The realm access code lifespan. + aliases: + - accessCodeLifespan + type: int + access_code_lifespan_login: + description: + - The realm access code lifespan login. + aliases: + - accessCodeLifespanLogin + type: int + access_code_lifespan_user_action: + description: + - The realm access code lifespan user action. + aliases: + - accessCodeLifespanUserAction + type: int + access_token_lifespan: + description: + - The realm access token lifespan. + aliases: + - accessTokenLifespan + type: int + access_token_lifespan_for_implicit_flow: + description: + - The realm access token lifespan for implicit flow. + aliases: + - accessTokenLifespanForImplicitFlow + type: int + account_theme: + description: + - The realm account theme. + aliases: + - accountTheme + type: str + action_token_generated_by_admin_lifespan: + description: + - The realm action token generated by admin lifespan. + aliases: + - actionTokenGeneratedByAdminLifespan + type: int + action_token_generated_by_user_lifespan: + description: + - The realm action token generated by user lifespan. + aliases: + - actionTokenGeneratedByUserLifespan + type: int + admin_events_details_enabled: + description: + - The realm admin events details enabled. + aliases: + - adminEventsDetailsEnabled + type: bool + admin_events_enabled: + description: + - The realm admin events enabled. + aliases: + - adminEventsEnabled + type: bool + admin_theme: + description: + - The realm admin theme. + aliases: + - adminTheme + type: str + attributes: + description: + - The realm attributes. + type: dict + browser_flow: + description: + - The realm browser flow. + aliases: + - browserFlow + type: str + browser_security_headers: + description: + - The realm browser security headers. + aliases: + - browserSecurityHeaders + type: dict + brute_force_protected: + description: + - The realm brute force protected. + aliases: + - bruteForceProtected + type: bool + client_authentication_flow: + description: + - The realm client authentication flow. + aliases: + - clientAuthenticationFlow + type: str + client_scope_mappings: + description: + - The realm client scope mappings. + aliases: + - clientScopeMappings + type: dict + default_default_client_scopes: + description: + - The realm default default client scopes. + aliases: + - defaultDefaultClientScopes + type: list + elements: str + default_groups: + description: + - The realm default groups. + aliases: + - defaultGroups + type: list + elements: str + default_locale: + description: + - The realm default locale. + aliases: + - defaultLocale + type: str + default_optional_client_scopes: + description: + - The realm default optional client scopes. + aliases: + - defaultOptionalClientScopes + type: list + elements: str + default_roles: + description: + - The realm default roles. + aliases: + - defaultRoles + type: list + elements: str + default_signature_algorithm: + description: + - The realm default signature algorithm. + aliases: + - defaultSignatureAlgorithm + type: str + direct_grant_flow: + description: + - The realm direct grant flow. + aliases: + - directGrantFlow + type: str + display_name: + description: + - The realm display name. + aliases: + - displayName + type: str + display_name_html: + description: + - The realm display name HTML. + aliases: + - displayNameHtml + type: str + docker_authentication_flow: + description: + - The realm docker authentication flow. + aliases: + - dockerAuthenticationFlow + type: str + duplicate_emails_allowed: + description: + - The realm duplicate emails allowed option. + aliases: + - duplicateEmailsAllowed + type: bool + edit_username_allowed: + description: + - The realm edit username allowed option. + aliases: + - editUsernameAllowed + type: bool + email_theme: + description: + - The realm email theme. + aliases: + - emailTheme + type: str + enabled: + description: + - The realm enabled option. + type: bool + enabled_event_types: + description: + - The realm enabled event types. + aliases: + - enabledEventTypes + type: list + elements: str + events_enabled: + description: + - Enables or disables login events for this realm. + aliases: + - eventsEnabled + type: bool + version_added: 3.6.0 + events_expiration: + description: + - The realm events expiration. + aliases: + - eventsExpiration + type: int + events_listeners: + description: + - The realm events listeners. + aliases: + - eventsListeners + type: list + elements: str + failure_factor: + description: + - The realm failure factor. + aliases: + - failureFactor + type: int + internationalization_enabled: + description: + - The realm internationalization enabled option. + aliases: + - internationalizationEnabled + type: bool + login_theme: + description: + - The realm login theme. + aliases: + - loginTheme + type: str + login_with_email_allowed: + description: + - The realm login with email allowed option. + aliases: + - loginWithEmailAllowed + type: bool + max_delta_time_seconds: + description: + - The realm max delta time in seconds. + aliases: + - maxDeltaTimeSeconds + type: int + max_failure_wait_seconds: + description: + - The realm max failure wait in seconds. + aliases: + - maxFailureWaitSeconds + type: int + minimum_quick_login_wait_seconds: + description: + - The realm minimum quick login wait in seconds. + aliases: + - minimumQuickLoginWaitSeconds + type: int + not_before: + description: + - The realm not before. + aliases: + - notBefore + type: int + offline_session_idle_timeout: + description: + - The realm offline session idle timeout. + aliases: + - offlineSessionIdleTimeout + type: int + offline_session_max_lifespan: + description: + - The realm offline session max lifespan. + aliases: + - offlineSessionMaxLifespan + type: int + offline_session_max_lifespan_enabled: + description: + - The realm offline session max lifespan enabled option. + aliases: + - offlineSessionMaxLifespanEnabled + type: bool + otp_policy_algorithm: + description: + - The realm otp policy algorithm. + aliases: + - otpPolicyAlgorithm + type: str + otp_policy_digits: + description: + - The realm otp policy digits. + aliases: + - otpPolicyDigits + type: int + otp_policy_initial_counter: + description: + - The realm otp policy initial counter. + aliases: + - otpPolicyInitialCounter + type: int + otp_policy_look_ahead_window: + description: + - The realm otp policy look ahead window. + aliases: + - otpPolicyLookAheadWindow + type: int + otp_policy_period: + description: + - The realm otp policy period. + aliases: + - otpPolicyPeriod + type: int + otp_policy_type: + description: + - The realm otp policy type. + aliases: + - otpPolicyType + type: str + otp_supported_applications: + description: + - The realm otp supported applications. + aliases: + - otpSupportedApplications + type: list + elements: str + password_policy: + description: + - The realm password policy. + aliases: + - passwordPolicy + type: str + permanent_lockout: + description: + - The realm permanent lockout. + aliases: + - permanentLockout + type: bool + quick_login_check_milli_seconds: + description: + - The realm quick login check in milliseconds. + aliases: + - quickLoginCheckMilliSeconds + type: int + refresh_token_max_reuse: + description: + - The realm refresh token max reuse. + aliases: + - refreshTokenMaxReuse + type: int + registration_allowed: + description: + - The realm registration allowed option. + aliases: + - registrationAllowed + type: bool + registration_email_as_username: + description: + - The realm registration email as username option. + aliases: + - registrationEmailAsUsername + type: bool + registration_flow: + description: + - The realm registration flow. + aliases: + - registrationFlow + type: str + remember_me: + description: + - The realm remember me option. + aliases: + - rememberMe + type: bool + reset_credentials_flow: + description: + - The realm reset credentials flow. + aliases: + - resetCredentialsFlow + type: str + reset_password_allowed: + description: + - The realm reset password allowed option. + aliases: + - resetPasswordAllowed + type: bool + revoke_refresh_token: + description: + - The realm revoke refresh token option. + aliases: + - revokeRefreshToken + type: bool + smtp_server: + description: + - The realm smtp server. + aliases: + - smtpServer + type: dict + ssl_required: + description: + - The realm ssl required option. + choices: ['all', 'external', 'none'] + aliases: + - sslRequired + type: str + sso_session_idle_timeout: + description: + - The realm sso session idle timeout. + aliases: + - ssoSessionIdleTimeout + type: int + sso_session_idle_timeout_remember_me: + description: + - The realm sso session idle timeout remember me. + aliases: + - ssoSessionIdleTimeoutRememberMe + type: int + sso_session_max_lifespan: + description: + - The realm sso session max lifespan. + aliases: + - ssoSessionMaxLifespan + type: int + sso_session_max_lifespan_remember_me: + description: + - The realm sso session max lifespan remember me. + aliases: + - ssoSessionMaxLifespanRememberMe + type: int + supported_locales: + description: + - The realm supported locales. + aliases: + - supportedLocales + type: list + elements: str + user_managed_access_allowed: + description: + - The realm user managed access allowed option. + aliases: + - userManagedAccessAllowed + type: bool + verify_email: + description: + - The realm verify email option. + aliases: + - verifyEmail + type: bool + wait_increment_seconds: + description: + - The realm wait increment in seconds. + aliases: + - waitIncrementSeconds + type: int + +extends_documentation_fragment: + - middleware_automation.keycloak.keycloak + - middleware_automation.keycloak.attributes + +author: + - Christophe Gilles (@kris2kris) +''' + +EXAMPLES = ''' +- name: Create or update Keycloak realm (minimal example) + middleware_automation.keycloak.keycloak_realm: + auth_client_id: admin-cli + auth_keycloak_url: https://auth.example.com/auth + auth_realm: master + auth_username: USERNAME + auth_password: PASSWORD + id: realm + realm: realm + state: present + +- name: Delete a Keycloak realm + middleware_automation.keycloak.keycloak_realm: + auth_client_id: admin-cli + auth_keycloak_url: https://auth.example.com/auth + auth_realm: master + auth_username: USERNAME + auth_password: PASSWORD + id: test + state: absent +''' + +RETURN = ''' +msg: + description: Message as to what action was taken. + returned: always + type: str + sample: "Realm testrealm has been updated" + +proposed: + description: Representation of proposed realm. + returned: always + type: dict + sample: { + id: "test" + } + +existing: + description: Representation of existing realm (sample is truncated). + returned: always + type: dict + sample: { + "adminUrl": "http://www.example.com/admin_url", + "attributes": { + "request.object.signature.alg": "RS256", + } + } + +end_state: + description: Representation of realm after module execution (sample is truncated). + returned: on success + type: dict + sample: { + "adminUrl": "http://www.example.com/admin_url", + "attributes": { + "request.object.signature.alg": "RS256", + } + } +''' + +from ansible_collections.middleware_automation.keycloak.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, camel, \ + keycloak_argument_spec, get_token, KeycloakError +from ansible.module_utils.basic import AnsibleModule + + +def normalise_cr(realmrep): + """ Re-sorts any properties where the order is important so that diff's is minimised and the change detection is more effective. + + :param realmrep: the realmrep dict to be sanitized + :return: normalised realmrep dict + """ + # Avoid the dict passed in to be modified + realmrep = realmrep.copy() + + if 'enabledEventTypes' in realmrep: + realmrep['enabledEventTypes'] = list(sorted(realmrep['enabledEventTypes'])) + + if 'otpSupportedApplications' in realmrep: + realmrep['otpSupportedApplications'] = list(sorted(realmrep['otpSupportedApplications'])) + + if 'supportedLocales' in realmrep: + realmrep['supportedLocales'] = list(sorted(realmrep['supportedLocales'])) + + return realmrep + + +def sanitize_cr(realmrep): + """ Removes probably sensitive details from a realm representation. + + :param realmrep: the realmrep dict to be sanitized + :return: sanitized realmrep dict + """ + result = realmrep.copy() + if 'secret' in result: + result['secret'] = '********' + if 'attributes' in result: + if 'saml.signing.private.key' in result['attributes']: + result['attributes'] = result['attributes'].copy() + result['attributes']['saml.signing.private.key'] = '********' + return normalise_cr(result) + + +def main(): + """ + Module execution + + :return: + """ + argument_spec = keycloak_argument_spec() + + meta_args = dict( + state=dict(default='present', choices=['present', 'absent']), + + id=dict(type='str'), + realm=dict(type='str'), + access_code_lifespan=dict(type='int', aliases=['accessCodeLifespan']), + access_code_lifespan_login=dict(type='int', aliases=['accessCodeLifespanLogin']), + access_code_lifespan_user_action=dict(type='int', aliases=['accessCodeLifespanUserAction']), + access_token_lifespan=dict(type='int', aliases=['accessTokenLifespan'], no_log=False), + access_token_lifespan_for_implicit_flow=dict(type='int', aliases=['accessTokenLifespanForImplicitFlow'], no_log=False), + account_theme=dict(type='str', aliases=['accountTheme']), + action_token_generated_by_admin_lifespan=dict(type='int', aliases=['actionTokenGeneratedByAdminLifespan'], no_log=False), + action_token_generated_by_user_lifespan=dict(type='int', aliases=['actionTokenGeneratedByUserLifespan'], no_log=False), + admin_events_details_enabled=dict(type='bool', aliases=['adminEventsDetailsEnabled']), + admin_events_enabled=dict(type='bool', aliases=['adminEventsEnabled']), + admin_theme=dict(type='str', aliases=['adminTheme']), + attributes=dict(type='dict'), + browser_flow=dict(type='str', aliases=['browserFlow']), + browser_security_headers=dict(type='dict', aliases=['browserSecurityHeaders']), + brute_force_protected=dict(type='bool', aliases=['bruteForceProtected']), + client_authentication_flow=dict(type='str', aliases=['clientAuthenticationFlow']), + client_scope_mappings=dict(type='dict', aliases=['clientScopeMappings']), + default_default_client_scopes=dict(type='list', elements='str', aliases=['defaultDefaultClientScopes']), + default_groups=dict(type='list', elements='str', aliases=['defaultGroups']), + default_locale=dict(type='str', aliases=['defaultLocale']), + default_optional_client_scopes=dict(type='list', elements='str', aliases=['defaultOptionalClientScopes']), + default_roles=dict(type='list', elements='str', aliases=['defaultRoles']), + default_signature_algorithm=dict(type='str', aliases=['defaultSignatureAlgorithm']), + direct_grant_flow=dict(type='str', aliases=['directGrantFlow']), + display_name=dict(type='str', aliases=['displayName']), + display_name_html=dict(type='str', aliases=['displayNameHtml']), + docker_authentication_flow=dict(type='str', aliases=['dockerAuthenticationFlow']), + duplicate_emails_allowed=dict(type='bool', aliases=['duplicateEmailsAllowed']), + edit_username_allowed=dict(type='bool', aliases=['editUsernameAllowed']), + email_theme=dict(type='str', aliases=['emailTheme']), + enabled=dict(type='bool'), + enabled_event_types=dict(type='list', elements='str', aliases=['enabledEventTypes']), + events_enabled=dict(type='bool', aliases=['eventsEnabled']), + events_expiration=dict(type='int', aliases=['eventsExpiration']), + events_listeners=dict(type='list', elements='str', aliases=['eventsListeners']), + failure_factor=dict(type='int', aliases=['failureFactor']), + internationalization_enabled=dict(type='bool', aliases=['internationalizationEnabled']), + login_theme=dict(type='str', aliases=['loginTheme']), + login_with_email_allowed=dict(type='bool', aliases=['loginWithEmailAllowed']), + max_delta_time_seconds=dict(type='int', aliases=['maxDeltaTimeSeconds']), + max_failure_wait_seconds=dict(type='int', aliases=['maxFailureWaitSeconds']), + minimum_quick_login_wait_seconds=dict(type='int', aliases=['minimumQuickLoginWaitSeconds']), + not_before=dict(type='int', aliases=['notBefore']), + offline_session_idle_timeout=dict(type='int', aliases=['offlineSessionIdleTimeout']), + offline_session_max_lifespan=dict(type='int', aliases=['offlineSessionMaxLifespan']), + offline_session_max_lifespan_enabled=dict(type='bool', aliases=['offlineSessionMaxLifespanEnabled']), + otp_policy_algorithm=dict(type='str', aliases=['otpPolicyAlgorithm']), + otp_policy_digits=dict(type='int', aliases=['otpPolicyDigits']), + otp_policy_initial_counter=dict(type='int', aliases=['otpPolicyInitialCounter']), + otp_policy_look_ahead_window=dict(type='int', aliases=['otpPolicyLookAheadWindow']), + otp_policy_period=dict(type='int', aliases=['otpPolicyPeriod']), + otp_policy_type=dict(type='str', aliases=['otpPolicyType']), + otp_supported_applications=dict(type='list', elements='str', aliases=['otpSupportedApplications']), + password_policy=dict(type='str', aliases=['passwordPolicy'], no_log=False), + permanent_lockout=dict(type='bool', aliases=['permanentLockout']), + quick_login_check_milli_seconds=dict(type='int', aliases=['quickLoginCheckMilliSeconds']), + refresh_token_max_reuse=dict(type='int', aliases=['refreshTokenMaxReuse'], no_log=False), + registration_allowed=dict(type='bool', aliases=['registrationAllowed']), + registration_email_as_username=dict(type='bool', aliases=['registrationEmailAsUsername']), + registration_flow=dict(type='str', aliases=['registrationFlow']), + remember_me=dict(type='bool', aliases=['rememberMe']), + reset_credentials_flow=dict(type='str', aliases=['resetCredentialsFlow']), + reset_password_allowed=dict(type='bool', aliases=['resetPasswordAllowed'], no_log=False), + revoke_refresh_token=dict(type='bool', aliases=['revokeRefreshToken']), + smtp_server=dict(type='dict', aliases=['smtpServer']), + ssl_required=dict(choices=["external", "all", "none"], aliases=['sslRequired']), + sso_session_idle_timeout=dict(type='int', aliases=['ssoSessionIdleTimeout']), + sso_session_idle_timeout_remember_me=dict(type='int', aliases=['ssoSessionIdleTimeoutRememberMe']), + sso_session_max_lifespan=dict(type='int', aliases=['ssoSessionMaxLifespan']), + sso_session_max_lifespan_remember_me=dict(type='int', aliases=['ssoSessionMaxLifespanRememberMe']), + supported_locales=dict(type='list', elements='str', aliases=['supportedLocales']), + user_managed_access_allowed=dict(type='bool', aliases=['userManagedAccessAllowed']), + verify_email=dict(type='bool', aliases=['verifyEmail']), + wait_increment_seconds=dict(type='int', aliases=['waitIncrementSeconds']), + ) + + argument_spec.update(meta_args) + + module = AnsibleModule(argument_spec=argument_spec, + supports_check_mode=True, + required_one_of=([['id', 'realm', 'enabled'], + ['token', 'auth_realm', 'auth_username', 'auth_password']]), + required_together=([['auth_realm', 'auth_username', 'auth_password']])) + + result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={}) + + # Obtain access token, initialize API + try: + connection_header = get_token(module.params) + except KeycloakError as e: + module.fail_json(msg=str(e)) + + kc = KeycloakAPI(module, connection_header) + + realm = module.params.get('realm') + state = module.params.get('state') + + # convert module parameters to realm representation parameters (if they belong in there) + params_to_ignore = list(keycloak_argument_spec().keys()) + ['state'] + + # Filter and map the parameters names that apply to the role + realm_params = [x for x in module.params + if x not in params_to_ignore and + module.params.get(x) is not None] + + # See whether the realm already exists in Keycloak + before_realm = kc.get_realm_by_id(realm=realm) + + if before_realm is None: + before_realm = {} + + # Build a proposed changeset from parameters given to this module + changeset = {} + + for realm_param in realm_params: + new_param_value = module.params.get(realm_param) + changeset[camel(realm_param)] = new_param_value + + # Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis) + desired_realm = before_realm.copy() + desired_realm.update(changeset) + + result['proposed'] = sanitize_cr(changeset) + before_realm_sanitized = sanitize_cr(before_realm) + result['existing'] = before_realm_sanitized + + # Cater for when it doesn't exist (an empty dict) + if not before_realm: + if state == 'absent': + # Do nothing and exit + if module._diff: + result['diff'] = dict(before='', after='') + result['changed'] = False + result['end_state'] = {} + result['msg'] = 'Realm does not exist, doing nothing.' + module.exit_json(**result) + + # Process a creation + result['changed'] = True + + if 'id' not in desired_realm: + module.fail_json(msg='id needs to be specified when creating a new realm') + + if module._diff: + result['diff'] = dict(before='', after=sanitize_cr(desired_realm)) + + if module.check_mode: + module.exit_json(**result) + + # create it + kc.create_realm(desired_realm) + after_realm = kc.get_realm_by_id(desired_realm['id']) + + result['end_state'] = sanitize_cr(after_realm) + + result['msg'] = 'Realm %s has been created.' % desired_realm['id'] + module.exit_json(**result) + + else: + if state == 'present': + # Process an update + + # doing an update + result['changed'] = True + if module.check_mode: + # We can only compare the current realm with the proposed updates we have + before_norm = normalise_cr(before_realm) + desired_norm = normalise_cr(desired_realm) + if module._diff: + result['diff'] = dict(before=sanitize_cr(before_norm), + after=sanitize_cr(desired_norm)) + result['changed'] = (before_norm != desired_norm) + + module.exit_json(**result) + + # do the update + kc.update_realm(desired_realm, realm=realm) + + after_realm = kc.get_realm_by_id(realm=realm) + + if before_realm == after_realm: + result['changed'] = False + + result['end_state'] = sanitize_cr(after_realm) + + if module._diff: + result['diff'] = dict(before=before_realm_sanitized, + after=sanitize_cr(after_realm)) + + result['msg'] = 'Realm %s has been updated.' % desired_realm['id'] + module.exit_json(**result) + + else: + # Process a deletion (because state was not 'present') + result['changed'] = True + + if module._diff: + result['diff'] = dict(before=before_realm_sanitized, after='') + + if module.check_mode: + module.exit_json(**result) + + # delete it + kc.delete_realm(realm=realm) + + result['proposed'] = {} + result['end_state'] = {} + + result['msg'] = 'Realm %s has been deleted.' % before_realm['id'] + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/roles/keycloak_realm/README.md b/roles/keycloak_realm/README.md index d6ce30c..179784e 100644 --- a/roles/keycloak_realm/README.md +++ b/roles/keycloak_realm/README.md @@ -19,7 +19,7 @@ Role Defaults |`keycloak_management_http_port`| Management port | `9990` | |`keycloak_auth_client`| Authentication client for configuration REST calls | `admin-cli` | |`keycloak_client_public`| Configure a public realm client | `True` | -|`keycloak_client_web_origins`| Web origins for realm client | `+` | +|`keycloak_client_web_origins`| Web origins for realm client | `/*` | |`keycloak_url`| URL for configuration rest calls | `http://{{ keycloak_host }}:{{ keycloak_http_port }}` | |`keycloak_management_url`| URL for management console rest calls | `http://{{ keycloak_host }}:{{ keycloak_management_http_port }}` | diff --git a/roles/keycloak_realm/defaults/main.yml b/roles/keycloak_realm/defaults/main.yml index e488207..b9e1e45 100644 --- a/roles/keycloak_realm/defaults/main.yml +++ b/roles/keycloak_realm/defaults/main.yml @@ -43,7 +43,7 @@ keycloak_client_default_roles: [] keycloak_client_public: true # allowed web origins for the client -keycloak_client_web_origins: '+' +keycloak_client_web_origins: '/*' # list of user and role mappings to create in the client # Each user has the form: diff --git a/roles/keycloak_realm/meta/argument_specs.yml b/roles/keycloak_realm/meta/argument_specs.yml index b124be2..7c24a7c 100644 --- a/roles/keycloak_realm/meta/argument_specs.yml +++ b/roles/keycloak_realm/meta/argument_specs.yml @@ -53,7 +53,7 @@ argument_specs: type: "bool" keycloak_client_web_origins: # line 42 of keycloak_realm/defaults/main.yml - default: "+" + default: "/*" description: "Web origins for realm client" type: "str" keycloak_client_users: diff --git a/roles/keycloak_realm/tasks/main.yml b/roles/keycloak_realm/tasks/main.yml index 016ab55..962016c 100644 --- a/roles/keycloak_realm/tasks/main.yml +++ b/roles/keycloak_realm/tasks/main.yml @@ -82,7 +82,7 @@ base_url: "{{ item.base_url | default('') }}" enabled: "{{ item.enabled | default(True) }}" redirect_uris: "{{ item.redirect_uris | default(omit) }}" - web_origins: "{{ item.web_origins | default('+') }}" + web_origins: "{{ item.web_origins | default(omit) }}" bearer_only: "{{ item.bearer_only | default(omit) }}" standard_flow_enabled: "{{ item.standard_flow_enabled | default(omit) }}" implicit_flow_enabled: "{{ item.implicit_flow_enabled | default(omit) }}" @@ -92,7 +92,7 @@ protocol: "{{ item.protocol | default(omit) }}" attributes: "{{ item.attributes | default(omit) }}" state: present - no_log: "{{ keycloak_no_log | default('True') }}" + no_log: "{{ keycloak_no_log | default('false') }}" register: create_client_result loop: "{{ keycloak_clients | flatten }}" when: (item.name is defined and item.client_id is defined) or (item.name is defined and item.id is defined) From 1279937bb0e95f0398ad04494b5f6797bd21afd6 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 16 Oct 2024 08:56:53 +0200 Subject: [PATCH 079/135] Update keycloak to 24.0.5 --- molecule/default/prepare.yml | 2 +- molecule/quarkus/converge.yml | 2 +- roles/keycloak_quarkus/README.md | 4 ++-- roles/keycloak_quarkus/defaults/main.yml | 2 +- roles/keycloak_quarkus/meta/argument_specs.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml index a50dfa4..899dabd 100644 --- a/molecule/default/prepare.yml +++ b/molecule/default/prepare.yml @@ -22,7 +22,7 @@ - name: Download keycloak archive to controller directory ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user - url: https://github.com/keycloak/keycloak/releases/download/24.0.4/keycloak-24.0.4.zip + url: https://github.com/keycloak/keycloak/releases/download/24.0.5/keycloak-24.0.5.zip dest: /tmp/keycloak mode: '0640' delegate_to: localhost diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index 7c86756..0860a42 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -37,7 +37,7 @@ repository_url: https://repo1.maven.org/maven2/ # https://mvnrepository.com/artifact/org.keycloak/keycloak-kerberos-federation/24.0.4 group_id: org.keycloak artifact_id: keycloak-kerberos-federation - version: 24.0.4 # optional + version: 24.0.5 # optional # username: myUser # optional # password: myPAT # optional # - id: my-static-theme diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 00c785f..034c021 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -33,7 +33,7 @@ Role Defaults | Variable | Description | Default | |:---------|:------------|:--------| -|`keycloak_quarkus_version`| keycloak.org package version | `24.0.4` | +|`keycloak_quarkus_version`| keycloak.org package version | `24.0.5` | |`keycloak_quarkus_offline_install` | Perform an offline install | `False`| |`keycloak_quarkus_dest`| Installation root path | `/opt/keycloak` | |`keycloak_quarkus_download_url` | Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}` | @@ -207,7 +207,7 @@ keycloak_quarkus_providers: repository_url: https://maven.pkg.github.com/OWNER/REPOSITORY # optional, maven repo url group_id: my.group # optional, maven group id artifact_id: artifact # optional, maven artifact id - version: 24.0.4 # optional, defaults to latest + version: 24.0.5 # optional, defaults to latest username: user # optional, cf. https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry#authenticating-to-github-packages password: pat # optional, provide a PAT for accessing Github's Apache Maven registry properties: # optional, list of key-values diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index b029fee..148d84d 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -1,6 +1,6 @@ --- ### Configuration specific to keycloak -keycloak_quarkus_version: 24.0.4 +keycloak_quarkus_version: 24.0.5 keycloak_quarkus_archive: "keycloak-{{ keycloak_quarkus_version }}.zip" keycloak_quarkus_download_url: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}" keycloak_quarkus_installdir: "{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_quarkus_version }}" diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 34ec365..51fd6d8 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -2,7 +2,7 @@ argument_specs: main: options: keycloak_quarkus_version: - default: "24.0.4" + default: "24.0.5" description: "keycloak.org package version" type: "str" keycloak_quarkus_archive: From f6fdae4aa80283c9155f0e203efeb5f8b9d7bc22 Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Wed, 16 Oct 2024 07:24:42 +0000 Subject: [PATCH 080/135] Update changelog for release 2.4.3 Signed-off-by: ansible-middleware-core --- CHANGELOG.rst | 14 +++++++++++--- changelogs/changelog.yaml | 9 +++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index aafdac2..37a3923 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,11 +1,19 @@ -============= -Release Notes -============= +============================================= +middleware\_automation.keycloak Release Notes +============================================= .. contents:: Topics This changelog describes changes after version 0.2.6. +v2.4.3 +====== + +Minor Changes +------------- + +- Update keycloak to 24.0.5 `#241 `_ + v2.4.2 ====== diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 41a4076..40a7db3 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -604,3 +604,12 @@ releases: - 237.yaml - 239.yaml release_date: '2024-09-26' + 2.4.3: + changes: + minor_changes: + - 'Update keycloak to 24.0.5 `#241 `_ + + ' + fragments: + - 241.yaml + release_date: '2024-10-16' From 333d55ad734a21018d7c8bf02d6f66cb66d46933 Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Wed, 16 Oct 2024 07:24:57 +0000 Subject: [PATCH 081/135] Bump version to 2.4.4 --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index a782353..a6511a8 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "2.4.3" +version: "2.4.4" readme: README.md authors: - Romain Pelisse From 68a0f8842332a82133a6e6b0b77eb6c84e73c8a0 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Fri, 22 Nov 2024 08:08:09 +0100 Subject: [PATCH 082/135] keycloak_quarkus: Rebuild config and restart service for local providers (#249) --- roles/keycloak_quarkus/README.md | 2 +- roles/keycloak_quarkus/tasks/install.yml | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 034c021..e63db68 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -200,7 +200,7 @@ keycloak_quarkus_providers: - id: http-client # required; "{{ id }}.jar" identifies the file name on RHBK spi: connections # required if neither url, local_path nor maven are specified; required for setting properties default: true # optional, whether to set default for spi, default false - restart: true # optional, whether to restart, default true + restart: true # optional, whether to rebuild config and restart the service after deploying, default true url: https://.../.../custom_spi.jar # optional, url for download via http local_path: my_theme_spi.jar # optional, path on local controller for SPI to be uploaded maven: # optional, for download using maven diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index 809e07e..d8ce657 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -218,7 +218,7 @@ become: true loop: "{{ keycloak_quarkus_providers }}" when: item.url is defined and item.url | length > 0 - notify: "{{ ['rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or not item.restart else [] }}" + notify: "{{ ['rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}" # this requires the `lxml` package to be installed; we redirect this step to localhost such that we do need to install it on the remote hosts - name: "Download custom providers to localhost using maven" @@ -235,7 +235,7 @@ loop: "{{ keycloak_quarkus_providers }}" when: item.maven is defined no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" - notify: "{{ ['rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or not item.restart else [] }}" + notify: "{{ ['rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}" - name: "Copy maven providers" ansible.builtin.copy: @@ -249,7 +249,7 @@ when: item.maven is defined no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" -- name: "Copy providers" +- name: "Copy local providers" ansible.builtin.copy: src: "{{ item.local_path }}" dest: "{{ keycloak.home }}/providers/{{ item.id }}.jar" @@ -259,6 +259,7 @@ become: true loop: "{{ keycloak_quarkus_providers }}" when: item.local_path is defined + notify: "{{ ['rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}" - name: Ensure required folder structure for policies exists ansible.builtin.file: From d1859aaff28f77312f7fc04f4fd0c37a7ace5770 Mon Sep 17 00:00:00 2001 From: Ranabir Chakraborty Date: Tue, 3 Dec 2024 22:12:36 +0530 Subject: [PATCH 083/135] Access token lifespan is too short for ansible run --- .github/workflows/ci.yml | 2 +- roles/keycloak_realm/tasks/main.yml | 3 +++ .../keycloak_realm/tasks/manage_token_lifespan.yml | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 roles/keycloak_realm/tasks/manage_token_lifespan.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d71b46..de65d72 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,4 +16,4 @@ jobs: with: fqcn: 'middleware_automation/keycloak' molecule_tests: >- - [ "default", "overridexml", "https_revproxy", "quarkus", "quarkus-devmode", "quarkus_upgrade", "debian", "quarkus_ha" ] + [ "default", "overridexml", "https_revproxy", "quarkus", "quarkus-devmode", "quarkus_upgrade", "debian", "quarkus_ha" ] diff --git a/roles/keycloak_realm/tasks/main.yml b/roles/keycloak_realm/tasks/main.yml index 962016c..8f33fcc 100644 --- a/roles/keycloak_realm/tasks/main.yml +++ b/roles/keycloak_realm/tasks/main.yml @@ -110,3 +110,6 @@ loop_control: loop_var: client when: "'users' in client" + +- name: Provide Access token lifespan + ansible.builtin.include_tasks: manage_token_lifespan.yml diff --git a/roles/keycloak_realm/tasks/manage_token_lifespan.yml b/roles/keycloak_realm/tasks/manage_token_lifespan.yml new file mode 100644 index 0000000..f16b938 --- /dev/null +++ b/roles/keycloak_realm/tasks/manage_token_lifespan.yml @@ -0,0 +1,14 @@ +--- +- name: "Update Access token lifespan" + ansible.builtin.uri: + url: "{{ keycloak_url }}{{ keycloak_context }}/admin/realms/{{ keycloak_realm }}" + method: PUT + body: + accessTokenLifespan: 300 + validate_certs: false + body_format: json + status_code: + - 200 + - 204 + headers: + Authorization: "Bearer {{ keycloak_auth_response.json.access_token }}" From 5f534ca5669514244176c1b53bb3552b3d65fe75 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Thu, 12 Dec 2024 09:05:09 +0100 Subject: [PATCH 084/135] keycloak_quarkus: Add theme cache invalidation handler --- roles/keycloak_quarkus/handlers/main.yml | 3 +++ roles/keycloak_quarkus/tasks/install.yml | 6 +++--- .../keycloak_quarkus/tasks/invalidate_theme_cache.yml | 11 +++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 roles/keycloak_quarkus/tasks/invalidate_theme_cache.yml diff --git a/roles/keycloak_quarkus/handlers/main.yml b/roles/keycloak_quarkus/handlers/main.yml index 37d65c5..eec7789 100644 --- a/roles/keycloak_quarkus/handlers/main.yml +++ b/roles/keycloak_quarkus/handlers/main.yml @@ -1,4 +1,7 @@ --- +- name: "Invalidate {{ keycloak.service_name }} theme cache" + ansible.builtin.include_tasks: invalidate_theme_cache.yml + listen: "invalidate keycloak theme cache" # handler should be invoked anytime a [build configuration](https://www.keycloak.org/server/all-config?f=build) changes - name: "Rebuild {{ keycloak.service_name }} config" ansible.builtin.include_tasks: rebuild_config.yml diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index d8ce657..c602d8c 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -218,7 +218,7 @@ become: true loop: "{{ keycloak_quarkus_providers }}" when: item.url is defined and item.url | length > 0 - notify: "{{ ['rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}" + notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}" # this requires the `lxml` package to be installed; we redirect this step to localhost such that we do need to install it on the remote hosts - name: "Download custom providers to localhost using maven" @@ -235,7 +235,7 @@ loop: "{{ keycloak_quarkus_providers }}" when: item.maven is defined no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" - notify: "{{ ['rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}" + notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}" - name: "Copy maven providers" ansible.builtin.copy: @@ -259,7 +259,7 @@ become: true loop: "{{ keycloak_quarkus_providers }}" when: item.local_path is defined - notify: "{{ ['rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}" + notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}" - name: Ensure required folder structure for policies exists ansible.builtin.file: diff --git a/roles/keycloak_quarkus/tasks/invalidate_theme_cache.yml b/roles/keycloak_quarkus/tasks/invalidate_theme_cache.yml new file mode 100644 index 0000000..90ff67f --- /dev/null +++ b/roles/keycloak_quarkus/tasks/invalidate_theme_cache.yml @@ -0,0 +1,11 @@ +--- +# From https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/24.0/html/server_developer_guide/themes#creating_a_theme: +# If you want to manually delete the content of the themes cache, +# you can do so by deleting the data/tmp/kc-gzip-cache directory of the server distribution +# It can be useful for instance if you redeployed custom providers or custom themes without +# disabling themes caching in the previous server executions. +- name: "Delete {{ keycloak.service_name }} theme cache directory" + ansible.builtin.file: + path: "{{ keycloak.home }}/data/tmp/kc-gzip-cache" + state: absent + become: true From 5d15d37890ef472e65e420e4824bf83691913087 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Thu, 5 Dec 2024 13:26:10 +0100 Subject: [PATCH 085/135] RHBK v26: Raise default KC+RHBK versions to v26.x (#253) --- roles/keycloak_quarkus/README.md | 2 +- roles/keycloak_quarkus/defaults/main.yml | 2 +- roles/keycloak_quarkus/meta/argument_specs.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index e63db68..9211b00 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -33,7 +33,7 @@ Role Defaults | Variable | Description | Default | |:---------|:------------|:--------| -|`keycloak_quarkus_version`| keycloak.org package version | `24.0.5` | +|`keycloak_quarkus_version`| keycloak.org package version | `26.0.7` | |`keycloak_quarkus_offline_install` | Perform an offline install | `False`| |`keycloak_quarkus_dest`| Installation root path | `/opt/keycloak` | |`keycloak_quarkus_download_url` | Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 148d84d..4d158c8 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -1,6 +1,6 @@ --- ### Configuration specific to keycloak -keycloak_quarkus_version: 24.0.5 +keycloak_quarkus_version: 26.0.7 keycloak_quarkus_archive: "keycloak-{{ keycloak_quarkus_version }}.zip" keycloak_quarkus_download_url: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}" keycloak_quarkus_installdir: "{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_quarkus_version }}" diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 51fd6d8..d1a3a0e 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -2,7 +2,7 @@ argument_specs: main: options: keycloak_quarkus_version: - default: "24.0.5" + default: "26.0.7" description: "keycloak.org package version" type: "str" keycloak_quarkus_archive: @@ -468,7 +468,7 @@ argument_specs: downstream: options: rhbk_version: - default: "24.0.3" + default: "26.0.6" description: "Red Hat Build of Keycloak version" type: "str" rhbk_archive: From bf0bd9e1da64eb0a5a764b9275acd226da9e75bf Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Thu, 5 Dec 2024 13:26:57 +0100 Subject: [PATCH 086/135] RHBK v26: Update mssqj jdbc driver (KC/RHBK v26 Support #253) As per --- roles/keycloak_quarkus/defaults/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 4d158c8..ba9854b 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -138,9 +138,9 @@ keycloak_quarkus_default_jdbc: version: 2.7.4 mssql: url: 'jdbc:sqlserver://localhost:1433;databaseName=keycloak;' - version: 12.4.2 - driver_jar_url: "https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/12.4.2.jre11/mssql-jdbc-12.4.2.jre11.jar" - # cf. https://access.redhat.com/documentation/en-us/red_hat_build_of_keycloak/24.0/html/server_guide/db-#db-installing-the-microsoft-sql-server-driver + version: 12.8.1 + driver_jar_url: "https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/12.8.1.jre11/mssql-jdbc-12.8.1.jre11.jar" + # cf. https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.0/html-single/server_configuration_guide/index#db-installing-the-microsoft-sql-server-driver ### logging configuration keycloak_quarkus_log: file keycloak_quarkus_log_level: info From 0c58ae48ff949f1c8152f8076b296e4b551db011 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Mon, 9 Dec 2024 14:48:15 +0100 Subject: [PATCH 087/135] RHBK v26: Update ispn session usages (KC/RHBK v26 Support #253) Cf. --- roles/keycloak_quarkus/templates/cache-ispn.xml.j2 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/roles/keycloak_quarkus/templates/cache-ispn.xml.j2 b/roles/keycloak_quarkus/templates/cache-ispn.xml.j2 index fb11cda..8ead247 100644 --- a/roles/keycloak_quarkus/templates/cache-ispn.xml.j2 +++ b/roles/keycloak_quarkus/templates/cache-ispn.xml.j2 @@ -18,8 +18,8 @@ + xsi:schemaLocation="urn:infinispan:config:15.0 http://www.infinispan.org/schemas/infinispan-config-15.0.xsd" + xmlns="urn:infinispan:config:15.0"> {% set stack_expression='' %} {% if keycloak_quarkus_ha_enabled and keycloak_quarkus_ha_discovery == 'TCPPING' %} @@ -55,18 +55,22 @@ + + + + @@ -98,4 +102,4 @@ - \ No newline at end of file + From 58233549a7f99d3a534a4ae77d87884c5846f348 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Mon, 9 Dec 2024 14:54:24 +0100 Subject: [PATCH 088/135] keycloak.conf: Remove `config-keystore-type` (KC/RHBK v26 Support #253) Cf. --- roles/keycloak_quarkus/templates/keycloak.conf.j2 | 1 - 1 file changed, 1 deletion(-) diff --git a/roles/keycloak_quarkus/templates/keycloak.conf.j2 b/roles/keycloak_quarkus/templates/keycloak.conf.j2 index ab4024b..24972c3 100644 --- a/roles/keycloak_quarkus/templates/keycloak.conf.j2 +++ b/roles/keycloak_quarkus/templates/keycloak.conf.j2 @@ -21,7 +21,6 @@ hostname-strict-https=false # Config store config-keystore={{ keycloak_quarkus_config_key_store_file }} config-keystore-password={{ keycloak_quarkus_config_key_store_password }} -config-keystore-type=PKCS12 {% endif %} # Observability From 277e1336ee97318ca88ffd27e3fe0ea2bf0e8133 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Mon, 9 Dec 2024 15:47:06 +0100 Subject: [PATCH 089/135] RHBK v26: Migrate to `keycloak_quarkus_bootstrap_admin_user[_password]` (Process for creation of admin account changed #248) --- molecule/debian/converge.yml | 4 +- molecule/debian/verify.yml | 2 +- molecule/default/converge.yml | 4 +- molecule/default/verify.yml | 4 +- molecule/https_revproxy/converge.yml | 4 +- molecule/quarkus-devmode/converge.yml | 4 +- molecule/quarkus/converge.yml | 4 +- molecule/quarkus/verify.yml | 4 +- molecule/quarkus_ha/converge.yml | 4 +- molecule/quarkus_upgrade/vars.yml | 3 +- molecule/quarkus_upgrade/verify.yml | 4 +- roles/keycloak_quarkus/README.md | 8 +- roles/keycloak_quarkus/defaults/main.yml | 4 +- .../keycloak_quarkus/meta/argument_specs.yml | 8 +- roles/keycloak_quarkus/tasks/deprecations.yml | 96 +++++++++++++++++++ roles/keycloak_quarkus/tasks/main.yml | 2 +- roles/keycloak_quarkus/tasks/prereqs.yml | 4 +- .../templates/keycloak-sysconfig.j2 | 4 +- 18 files changed, 132 insertions(+), 35 deletions(-) diff --git a/molecule/debian/converge.yml b/molecule/debian/converge.yml index 1a7d267..a3ba17a 100644 --- a/molecule/debian/converge.yml +++ b/molecule/debian/converge.yml @@ -3,8 +3,8 @@ hosts: all vars: keycloak_quarkus_show_deprecation_warnings: false - keycloak_quarkus_admin_pass: "remembertochangeme" - keycloak_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_quarkus_host: instance keycloak_quarkus_log: file keycloak_quarkus_start_dev: true diff --git a/molecule/debian/verify.yml b/molecule/debian/verify.yml index 59bf483..863b820 100644 --- a/molecule/debian/verify.yml +++ b/molecule/debian/verify.yml @@ -2,7 +2,7 @@ - name: Verify hosts: all vars: - keycloak_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_uri: "http://localhost:{{ 8080 + ( keycloak_jboss_port_offset | default(0) ) }}" keycloak_management_port: "http://localhost:{{ 9990 + ( keycloak_jboss_port_offset | default(0) ) }}" keycloak_jboss_port_offset: 10 diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index 417713f..543b003 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -3,8 +3,8 @@ hosts: all vars: keycloak_quarkus_show_deprecation_warnings: false - keycloak_quarkus_admin_pass: "remembertochangeme" - keycloak_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_quarkus_host: instance keycloak_quarkus_log: file keycloak_quarkus_log_level: debug diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml index b880105..eac4339 100644 --- a/molecule/default/verify.yml +++ b/molecule/default/verify.yml @@ -2,7 +2,7 @@ - name: Verify hosts: all vars: - keycloak_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_uri: "http://localhost:8080" tasks: - name: Populate service facts @@ -16,7 +16,7 @@ ansible.builtin.uri: url: "{{ keycloak_uri }}/realms/master/protocol/openid-connect/token" method: POST - body: "client_id=admin-cli&username=admin&password={{ keycloak_admin_password }}&grant_type=password" + body: "client_id=admin-cli&username=admin&password={{ keycloak_quarkus_bootstrap_admin_user }}&grant_type=password" validate_certs: no register: keycloak_auth_response until: keycloak_auth_response.status == 200 diff --git a/molecule/https_revproxy/converge.yml b/molecule/https_revproxy/converge.yml index b490721..b25d731 100644 --- a/molecule/https_revproxy/converge.yml +++ b/molecule/https_revproxy/converge.yml @@ -3,8 +3,8 @@ hosts: all vars: keycloak_quarkus_show_deprecation_warnings: false - keycloak_quarkus_admin_pass: "remembertochangeme" - keycloak_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_realm: TestRealm keycloak_quarkus_host: instance keycloak_quarkus_log: file diff --git a/molecule/quarkus-devmode/converge.yml b/molecule/quarkus-devmode/converge.yml index 2e5d351..5cd4ea1 100644 --- a/molecule/quarkus-devmode/converge.yml +++ b/molecule/quarkus-devmode/converge.yml @@ -3,8 +3,8 @@ hosts: all vars: keycloak_quarkus_show_deprecation_warnings: false - keycloak_quarkus_admin_pass: "remembertochangeme" - keycloak_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_realm: TestRealm keycloak_quarkus_log: file keycloak_quarkus_frontend_url: 'http://localhost:8080/' diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index 0860a42..d134203 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -3,8 +3,8 @@ hosts: all vars: keycloak_quarkus_show_deprecation_warnings: false - keycloak_quarkus_admin_pass: "remembertochangeme" - keycloak_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_realm: TestRealm keycloak_quarkus_host: instance keycloak_quarkus_log: file diff --git a/molecule/quarkus/verify.yml b/molecule/quarkus/verify.yml index 63769dc..1ee3b61 100644 --- a/molecule/quarkus/verify.yml +++ b/molecule/quarkus/verify.yml @@ -2,7 +2,7 @@ - name: Verify hosts: all vars: - keycloak_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" tasks: - name: Populate service facts ansible.builtin.service_facts: @@ -91,7 +91,7 @@ ansible.builtin.uri: url: "https://instance:8443/realms/master/protocol/openid-connect/token" method: POST - body: "client_id=admin-cli&username=admin&password={{ keycloak_admin_password }}&grant_type=password" + body: "client_id=admin-cli&username=admin&password={{ keycloak_quarkus_bootstrap_admin_user }}&grant_type=password" validate_certs: no register: keycloak_auth_response until: keycloak_auth_response.status == 200 diff --git a/molecule/quarkus_ha/converge.yml b/molecule/quarkus_ha/converge.yml index 00246b8..0455351 100644 --- a/molecule/quarkus_ha/converge.yml +++ b/molecule/quarkus_ha/converge.yml @@ -3,8 +3,8 @@ hosts: keycloak vars: keycloak_quarkus_show_deprecation_warnings: false - keycloak_quarkus_admin_pass: "remembertochangeme" - keycloak_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_realm: TestRealm keycloak_quarkus_host: "{{ inventory_hostname }}" keycloak_quarkus_log: file diff --git a/molecule/quarkus_upgrade/vars.yml b/molecule/quarkus_upgrade/vars.yml index 81d56b5..6a60844 100644 --- a/molecule/quarkus_upgrade/vars.yml +++ b/molecule/quarkus_upgrade/vars.yml @@ -1,7 +1,6 @@ --- keycloak_quarkus_offline_install: false -keycloak_quarkus_admin_password: "remembertochangeme" -keycloak_quarkus_admin_pass: "remembertochangeme" +keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_realm: TestRealm keycloak_quarkus_host: instance keycloak_quarkus_log: file diff --git a/molecule/quarkus_upgrade/verify.yml b/molecule/quarkus_upgrade/verify.yml index f5edfd0..3214d9f 100644 --- a/molecule/quarkus_upgrade/verify.yml +++ b/molecule/quarkus_upgrade/verify.yml @@ -2,7 +2,7 @@ - name: Verify hosts: instance vars: - keycloak_quarkus_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_port: http://localhost:8080 tasks: - name: Populate service facts @@ -24,7 +24,7 @@ ansible.builtin.uri: url: "{{ keycloak_quarkus_port }}/realms/master/protocol/openid-connect/token" method: POST - body: "client_id=admin-cli&username=admin&password={{ keycloak_quarkus_admin_password }}&grant_type=password" + body: "client_id=admin-cli&username=admin&password={{ keycloak_quarkus_bootstrap_admin_password }}&grant_type=password" validate_certs: no register: keycloak_auth_response until: keycloak_auth_response.status == 200 diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 9211b00..cf45cdf 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -44,7 +44,8 @@ Role Defaults | Variable | Description | Default | |:---------|:------------|:--------| -|`keycloak_quarkus_admin_user`| Administration console user account | `admin` | +|`keycloak_quarkus_bootstrap_admin_user`| Administration console user account | `admin` | +|`keycloak_quarkus_admin_user`| Deprecated, use `keycloak_quarkus_bootstrap_admin_user` instead. | | |`keycloak_quarkus_bind_address`| Address for binding service ports | `0.0.0.0` | |`keycloak_quarkus_host`| Hostname for the Keycloak server | `localhost` | |`keycloak_quarkus_port`| The port used by the proxy when exposing the hostname | `-1` | @@ -243,7 +244,8 @@ Role Variables | Variable | Description | Required | |:---------|:------------|----------| -|`keycloak_quarkus_admin_pass`| Password of console admin account | `yes` | +|`keycloak_quarkus_bootstrap_admin_password`| Password of console admin account | `yes` | +|`keycloak_quarkus_admin_pass`| Deprecated, use `keycloak_quarkus_bootstrap_admin_password` instead. | | |`keycloak_quarkus_frontend_url`| Base URL for frontend URLs, including scheme, host, port and path | `no` | |`keycloak_quarkus_admin_url`| Base URL for accessing the administration console, including scheme, host, port and path | `no` | |`keycloak_quarkus_ks_vault_pass`| The password for accessing the keystore vault SPI | `no` | @@ -265,7 +267,7 @@ The role uses the following [custom facts](https://docs.ansible.com/ansible/late | Variable | Description | |:---------|:------------| -|`general.bootstrapped` | A custom fact indicating whether this role has been used for bootstrapping keycloak on the respective host before; set to `false` (e.g., when starting off with a new, empty database) ensures that the initial admin user as defined by `keycloak_quarkus_admin_user[_pass]` gets created | +|`general.bootstrapped` | A custom fact indicating whether this role has been used for bootstrapping keycloak on the respective host before; set to `false` (e.g., when starting off with a new, empty database) ensures that the initial admin user as defined by `keycloak_quarkus_bootstrap_admin_user[_password]` gets created | License ------- diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index ba9854b..f425396 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -27,8 +27,8 @@ keycloak_quarkus_configure_firewalld: false keycloak_quarkus_configure_iptables: false ### administrator console password -keycloak_quarkus_admin_user: admin -keycloak_quarkus_admin_pass: +keycloak_quarkus_bootstrap_admin_user: admin +keycloak_quarkus_bootstrap_admin_password: keycloak_quarkus_master_realm: master ### Configuration settings diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index d1a3a0e..f91bc90 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -68,13 +68,13 @@ argument_specs: default: "10s" description: "systemd RestartSec for service" type: "str" - keycloak_quarkus_admin_user: + keycloak_quarkus_bootstrap_admin_user: default: "admin" - description: "Administration console user account" + description: "Administration user account, only for bootstrapping" type: "str" - keycloak_quarkus_admin_pass: + keycloak_quarkus_bootstrap_admin_password: required: true - description: "Password of console admin account" + description: "Password of admin account, only for bootstrapping" type: "str" keycloak_quarkus_master_realm: default: "master" diff --git a/roles/keycloak_quarkus/tasks/deprecations.yml b/roles/keycloak_quarkus/tasks/deprecations.yml index 27ea6e3..fe269e9 100644 --- a/roles/keycloak_quarkus/tasks/deprecations.yml +++ b/roles/keycloak_quarkus/tasks/deprecations.yml @@ -49,5 +49,101 @@ notify: - print deprecation warning +# https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.0/html-single/upgrading_guide/index#new_hostname_options +- name: Check deprecation of keycloak_quarkus_frontend_url -> keycloak_quarkus_hostname + when: + - keycloak_quarkus_hostname is not defined + - keycloak_quarkus_frontend_url is defined + - keycloak_quarkus_frontend_url != '' + delegate_to: localhost + run_once: true + changed_when: keycloak_quarkus_show_deprecation_warnings + ansible.builtin.set_fact: + keycloak_quarkus_hostname: "{{ keycloak_quarkus_frontend_url }}" + deprecated_variable: "keycloak_quarkus_frontend_url" # read in deprecation handler + notify: + - print deprecation warning + +# https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.0/html-single/upgrading_guide/index#new_hostname_options +- name: Check deprecation of keycloak_quarkus_hostname_strict_https + keycloak_quarkus_host + keycloak_quarkus_port + keycloak_quarkus_path -> keycloak_quarkus_hostname + when: + - keycloak_quarkus_hostname is not defined + - keycloak_quarkus_hostname_strict_https is defined or keycloak_quarkus_frontend_url is defined or keycloak_quarkus_port is defined or keycloak_quarkus_path is defined + delegate_to: localhost + run_once: true + changed_when: keycloak_quarkus_show_deprecation_warnings + ansible.builtin.set_fact: + keycloak_quarkus_hostname: >- + {% set protocol = '' %} + {% if keycloak_quarkus_hostname_strict_https %} + {% set protocol = 'https://' %} + {% elif keycloak_quarkus_hostname_strict_https is defined and keycloak_quarkus_hostname_strict_https is False %} + {% set protocol = 'http://' %} + {% endif %} + {{ protocol }}{{ keycloak_quarkus_host }}:{{ keycloak_quarkus_port }}/{{ keycloak_quarkus_path }} + deprecated_variable: "keycloak_quarkus_hostname_strict_https or keycloak_quarkus_frontend_url or keycloak_quarkus_frontend_url or keycloak_quarkus_hostname" # read in deprecation handler + notify: + - print deprecation warning + +# https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.0/html-single/upgrading_guide/index#new_hostname_options +- name: Check deprecation of keycloak_quarkus_admin_url -> keycloak_quarkus_admin + when: + - keycloak_quarkus_admin is not defined + - keycloak_quarkus_admin_url is defined + - keycloak_quarkus_admin_url != '' + delegate_to: localhost + run_once: true + changed_when: keycloak_quarkus_show_deprecation_warnings + ansible.builtin.set_fact: + keycloak_quarkus_admin: "{{ keycloak_quarkus_admin_url }}" + deprecated_variable: "keycloak_quarkus_admin_url" # read in deprecation handler + notify: + - print deprecation warning + +# https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.0/html-single/upgrading_guide/index#new_hostname_options +- name: Check deprecation of keycloak_quarkus_hostname_strict_backchannel -> keycloak_quarkus_hostname_backchannel_dynamic + when: + - keycloak_quarkus_hostname_backchannel_dynamic is not defined + - keycloak_quarkus_hostname_strict_backchannel is defined + - keycloak_quarkus_hostname_strict_backchannel != '' + delegate_to: localhost + run_once: true + changed_when: keycloak_quarkus_show_deprecation_warnings + ansible.builtin.set_fact: + keycloak_quarkus_hostname_backchannel_dynamic: "{{ keycloak_quarkus_hostname_strict_backchannel == False }}" + deprecated_variable: "keycloak_quarkus_hostname_backchannel_dynamic" # read in deprecation handler + notify: + - print deprecation warning + +# https://github.com/keycloak/keycloak/issues/30009 +- name: Check deprecation of keycloak_quarkus_admin_user -> keycloak_quarkus_bootstrap_admin_user + when: + - keycloak_quarkus_bootstrap_admin_user is not defined + - keycloak_quarkus_admin_user is defined + - keycloak_quarkus_admin_user != '' + delegate_to: localhost + run_once: true + changed_when: keycloak_quarkus_show_deprecation_warnings + ansible.builtin.set_fact: + keycloak_quarkus_bootstrap_admin_user: "{{ keycloak_quarkus_admin_user }}" + deprecated_variable: "keycloak_quarkus_admin_user" # read in deprecation handler + notify: + - print deprecation warning + +# https://github.com/keycloak/keycloak/issues/30009 +- name: Check deprecation of keycloak_quarkus_admin_pass -> keycloak_quarkus_bootstrap_admin_password + when: + - keycloak_quarkus_bootstrap_admin_password is not defined + - keycloak_quarkus_admin_pass is defined + - keycloak_quarkus_admin_pass != '' + delegate_to: localhost + run_once: true + changed_when: keycloak_quarkus_show_deprecation_warnings + ansible.builtin.set_fact: + keycloak_quarkus_bootstrap_admin_user: "{{ keycloak_quarkus_admin_pass }}" + deprecated_variable: "keycloak_quarkus_admin_pass" # read in deprecation handler + notify: + - print deprecation warning + - name: Flush handlers ansible.builtin.meta: flush_handlers diff --git a/roles/keycloak_quarkus/tasks/main.yml b/roles/keycloak_quarkus/tasks/main.yml index bb86b2c..cca6764 100644 --- a/roles/keycloak_quarkus/tasks/main.yml +++ b/roles/keycloak_quarkus/tasks/main.yml @@ -91,7 +91,7 @@ register: keycloak_service_status changed_when: false -- name: "Notify to remove `keycloak_quarkus_admin_user[_pass]` env vars" +- name: "Notify to remove `keycloak_quarkus_bootstrap_admin_user[_password]` env vars" when: - not ansible_local.keycloak.general.bootstrapped | default(false) | bool # it was not bootstrapped prior to the current role's execution - keycloak_service_status.status.ActiveState == "active" # but it is now diff --git a/roles/keycloak_quarkus/tasks/prereqs.yml b/roles/keycloak_quarkus/tasks/prereqs.yml index 0835aab..db7555e 100644 --- a/roles/keycloak_quarkus/tasks/prereqs.yml +++ b/roles/keycloak_quarkus/tasks/prereqs.yml @@ -2,9 +2,9 @@ - name: Validate admin console password ansible.builtin.assert: that: - - keycloak_quarkus_admin_pass | length > 12 + - keycloak_quarkus_bootstrap_admin_password | length > 12 quiet: true - fail_msg: "The console administrator password is empty or invalid. Please set the keycloak_quarkus_admin_pass to a 12+ char long string" + fail_msg: "The console administrator password is empty or invalid. Please set the keycloak_quarkus_bootstrap_admin_password to a 12+ char long string" success_msg: "{{ 'Console administrator password OK' }}" - name: Validate relative path diff --git a/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 b/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 index 2c260a3..244d9aa 100644 --- a/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 +++ b/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 @@ -1,7 +1,7 @@ {{ ansible_managed | comment }} {% if not ansible_local.keycloak.general.bootstrapped | default(false) | bool %} -KEYCLOAK_ADMIN={{ keycloak_quarkus_admin_user }} -KEYCLOAK_ADMIN_PASSWORD='{{ keycloak_quarkus_admin_pass }}' +KC_BOOTSTRAP_ADMIN_USERNAME={{ keycloak_quarkus_bootstrap_admin_user }} +KC_BOOTSTRAP_ADMIN_PASSWORD='{{ keycloak_quarkus_bootstrap_admin_password }}' {% else %} {{ keycloak.bootstrap_mnemonic }} {% endif %} From 213449ec58813bb038ceb79ce4ddc2f887c10cf7 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Mon, 9 Dec 2024 15:35:05 +0100 Subject: [PATCH 090/135] RHBK v26: Add hostname v2 (KC/RHBK v26 Support #253) Cf. https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.0/html-single/upgrading_guide/index#new_hostname_options - especially the removed options --- molecule/debian/converge.yml | 2 +- molecule/default/converge.yml | 2 +- molecule/https_revproxy/converge.yml | 2 +- molecule/quarkus/converge.yml | 2 +- molecule/quarkus_ha/converge.yml | 2 +- molecule/quarkus_upgrade/vars.yml | 2 +- playbooks/keycloak_quarkus.yml | 2 +- playbooks/keycloak_quarkus_dev.yml | 2 +- roles/keycloak_quarkus/README.md | 16 +++++---- roles/keycloak_quarkus/defaults/main.yml | 13 +++---- .../keycloak_quarkus/meta/argument_specs.yml | 34 +++++++++---------- .../templates/keycloak.conf.j2 | 19 ++--------- roles/keycloak_quarkus/vars/main.yml | 2 +- 13 files changed, 43 insertions(+), 57 deletions(-) diff --git a/molecule/debian/converge.yml b/molecule/debian/converge.yml index a3ba17a..7fab040 100644 --- a/molecule/debian/converge.yml +++ b/molecule/debian/converge.yml @@ -5,7 +5,7 @@ keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" - keycloak_quarkus_host: instance + keycloak_quarkus_hostname: http://instance keycloak_quarkus_log: file keycloak_quarkus_start_dev: true keycloak_quarkus_proxy_mode: none diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index 543b003..13a771b 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -5,7 +5,7 @@ keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" - keycloak_quarkus_host: instance + keycloak_quarkus_hostname: http://instance keycloak_quarkus_log: file keycloak_quarkus_log_level: debug keycloak_quarkus_log_target: /tmp/keycloak diff --git a/molecule/https_revproxy/converge.yml b/molecule/https_revproxy/converge.yml index b25d731..7245265 100644 --- a/molecule/https_revproxy/converge.yml +++ b/molecule/https_revproxy/converge.yml @@ -6,7 +6,7 @@ keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_realm: TestRealm - keycloak_quarkus_host: instance + keycloak_quarkus_hostname: http://instance keycloak_quarkus_log: file keycloak_quarkus_http_enabled: True keycloak_quarkus_http_port: 8080 diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index d134203..9b7b3a7 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -6,7 +6,7 @@ keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_realm: TestRealm - keycloak_quarkus_host: instance + keycloak_quarkus_hostname: http://instance keycloak_quarkus_log: file keycloak_quarkus_log_level: debug # needed for the verify step keycloak_quarkus_https_key_file_enabled: true diff --git a/molecule/quarkus_ha/converge.yml b/molecule/quarkus_ha/converge.yml index 0455351..238fbb5 100644 --- a/molecule/quarkus_ha/converge.yml +++ b/molecule/quarkus_ha/converge.yml @@ -6,7 +6,7 @@ keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_realm: TestRealm - keycloak_quarkus_host: "{{ inventory_hostname }}" + keycloak_quarkus_hostname: "http://{{ inventory_hostname }}" keycloak_quarkus_log: file keycloak_quarkus_log_level: info keycloak_quarkus_https_key_file_enabled: true diff --git a/molecule/quarkus_upgrade/vars.yml b/molecule/quarkus_upgrade/vars.yml index 6a60844..52ab103 100644 --- a/molecule/quarkus_upgrade/vars.yml +++ b/molecule/quarkus_upgrade/vars.yml @@ -2,7 +2,7 @@ keycloak_quarkus_offline_install: false keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_realm: TestRealm -keycloak_quarkus_host: instance +keycloak_quarkus_hostname: http://instance keycloak_quarkus_log: file keycloak_quarkus_https_key_file_enabled: true keycloak_quarkus_log_target: /tmp/keycloak diff --git a/playbooks/keycloak_quarkus.yml b/playbooks/keycloak_quarkus.yml index f2649a5..84d1774 100644 --- a/playbooks/keycloak_quarkus.yml +++ b/playbooks/keycloak_quarkus.yml @@ -3,7 +3,7 @@ hosts: all vars: keycloak_quarkus_admin_pass: "remembertochangeme" - keycloak_quarkus_host: localhost + keycloak_quarkus_hostname: http://localhost keycloak_quarkus_port: 8443 keycloak_quarkus_log: file keycloak_quarkus_proxy_mode: none diff --git a/playbooks/keycloak_quarkus_dev.yml b/playbooks/keycloak_quarkus_dev.yml index 533db79..0c74c36 100644 --- a/playbooks/keycloak_quarkus_dev.yml +++ b/playbooks/keycloak_quarkus_dev.yml @@ -3,7 +3,7 @@ hosts: all vars: keycloak_admin_password: "remembertochangeme" - keycloak_quarkus_host: localhost + keycloak_quarkus_hostname: http://localhost keycloak_quarkus_port: 8080 keycloak_quarkus_log: file keycloak_quarkus_start_dev: true diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index cf45cdf..7f0542a 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -47,9 +47,9 @@ Role Defaults |`keycloak_quarkus_bootstrap_admin_user`| Administration console user account | `admin` | |`keycloak_quarkus_admin_user`| Deprecated, use `keycloak_quarkus_bootstrap_admin_user` instead. | | |`keycloak_quarkus_bind_address`| Address for binding service ports | `0.0.0.0` | -|`keycloak_quarkus_host`| Hostname for the Keycloak server | `localhost` | -|`keycloak_quarkus_port`| The port used by the proxy when exposing the hostname | `-1` | -|`keycloak_quarkus_path`| This should be set if proxy uses a different context-path for Keycloak | | +|`keycloak_quarkus_host`| Deprecated, use `keycloak_quarkus_hostname` instead. | | +|`keycloak_quarkus_port`| Deprecated, use `keycloak_quarkus_hostname` instead. | | +|`keycloak_quarkus_path`| Deprecated, use `keycloak_quarkus_hostname` instead. | | |`keycloak_quarkus_http_port`| HTTP listening port | `8080` | |`keycloak_quarkus_https_port`| TLS HTTP listening port | `8443` | |`keycloak_quarkus_ajp_port`| AJP port | `8009` | @@ -64,8 +64,10 @@ Role Defaults |`keycloak_quarkus_java_jvm_opts`| Other JVM settings | same as keycloak | |`keycloak_quarkus_java_opts`| JVM arguments; if overridden, it takes precedence over `keycloak_quarkus_java_*` | `{{ keycloak_quarkus_java_heap_opts + ' ' + keycloak_quarkus_java_jvm_opts }}` | |`keycloak_quarkus_additional_env_vars` | List of additional env variables of { key: str, value: str} to be put in sysconfig file | `[]` | -|`keycloak_quarkus_frontend_url`| Set the base URL for frontend URLs, including scheme, host, port and path | | -|`keycloak_quarkus_admin_url`| Set the base URL for accessing the administration console, including scheme, host, port and path | | +|`keycloak_quarkus_hostname`| Address at which is the server exposed. Can be a full URL, or just a hostname. When only hostname is provided, scheme, port and context path are resolved from the request. | | +|`keycloak_quarkus_frontend_url`| Deprecated, use `keycloak_quarkus_hostname` instead. | | +|`keycloak_quarkus_admin`| Set the base URL for accessing the administration console, including scheme, host, port and path | | +|`keycloak_quarkus_admin_url`| Deprecated, use `keycloak_quarkus_admin` instead. | | |`keycloak_quarkus_http_relative_path` | Set the path relative to / for serving resources. The path must start with a / | `/` | |`keycloak_quarkus_http_enabled`| Enable listener on HTTP port | `True` | |`keycloak_quarkus_health_check_url_path`| Path to the health check endpoint; scheme, host and keycloak_quarkus_http_relative_path will be prepended automatically | `realms/master/.well-known/openid-configuration` | @@ -117,7 +119,8 @@ Role Defaults |:---------|:------------|:--------| |`keycloak_quarkus_http_relative_path`| Set the path relative to / for serving resources. The path must start with a / | `/` | |`keycloak_quarkus_hostname_strict`| Disables dynamically resolving the hostname from request headers | `true` | -|`keycloak_quarkus_hostname_strict_backchannel`| By default backchannel URLs are dynamically resolved from request headers to allow internal and external applications. If all applications use the public URL this option should be enabled. | `false` | +|`keycloak_quarkus_hostname_backchannel_dynamic`| Enables dynamic resolving of backchannel URLs, including hostname, scheme, port and context path. Set to true if your application accesses Keycloak via a private network. If set to true, hostname option needs to be specified as a full URL. | `false` | +|`keycloak_quarkus_hostname_strict_backchannel`| Deprecated, use (the inverted!)`keycloak_quarkus_hostname_backchannel_dynamic` instead. | | #### Database configuration @@ -157,7 +160,6 @@ Role Defaults |`keycloak_quarkus_master_realm` | Name for rest authentication realm | `master` | |`keycloak_auth_client` | Authentication client for configuration REST calls | `admin-cli` | |`keycloak_force_install` | Remove pre-existing versions of service | `False` | -|`keycloak_url` | URL for configuration rest calls | `http://{{ keycloak_quarkus_host }}:{{ keycloak_http_port }}` | |`keycloak_quarkus_log`| Enable one or more log handlers in a comma-separated list | `file` | |`keycloak_quarkus_log_level`| The log level of the root category or a comma-separated list of individual categories and their levels | `info` | |`keycloak_quarkus_log_file`| Set the log file path and filename relative to keycloak home | `data/log/keycloak.log` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index f425396..805b102 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -33,9 +33,6 @@ keycloak_quarkus_master_realm: master ### Configuration settings keycloak_quarkus_bind_address: 0.0.0.0 -keycloak_quarkus_host: localhost -keycloak_quarkus_port: -1 -keycloak_quarkus_path: keycloak_quarkus_http_enabled: true keycloak_quarkus_http_port: 8080 keycloak_quarkus_https_port: 8443 @@ -81,8 +78,8 @@ keycloak_quarkus_systemd_wait_for_timeout: 60 keycloak_quarkus_systemd_wait_for_delay: 10 ### keycloak frontend url -keycloak_quarkus_frontend_url: -keycloak_quarkus_admin_url: +keycloak_quarkus_hostname: +keycloak_quarkus_admin: ### Set the path relative to / for serving resources. The path must start with a / ### (set to `/auth` for retrocompatibility with pre-quarkus releases) @@ -91,9 +88,9 @@ keycloak_quarkus_http_relative_path: / # Disables dynamically resolving the hostname from request headers. # Should always be set to true in production, unless proxy verifies the Host header. keycloak_quarkus_hostname_strict: true -# By default backchannel URLs are dynamically resolved from request headers to allow internal and external applications. -# If all applications use the public URL this option should be enabled. -keycloak_quarkus_hostname_strict_backchannel: false +# Enables dynamic resolving of backchannel URLs, including hostname, scheme, port and context path. +# Set to true if your application accesses Keycloak via a private network. If set to true, keycloak_quarkus_hostname option needs to be specified as a full URL. +keycloak_quarkus_hostname_backchannel_dynamic: false # The proxy headers that should be accepted by the server. ['', 'forwarded', 'xforwarded'] keycloak_quarkus_proxy_headers: "" diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index f91bc90..b7a9ffe 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -84,17 +84,19 @@ argument_specs: default: "0.0.0.0" description: "Address for binding service ports" type: "str" + keycloak_quarkus_hostname: + description: >- + Address at which is the server exposed. + Can be a full URL, or just a hostname. When only hostname is provided, scheme, port and context path are resolved from the request. + type: "str" keycloak_quarkus_host: - default: "localhost" - description: "Hostname for the Keycloak server" + description: "Deprecated in v26, use keycloak_quarkus_hostname instead." type: "str" keycloak_quarkus_port: - default: -1 - description: "The port used by the proxy when exposing the hostname" + description: "Deprecated in v26, use keycloak_quarkus_hostname instead." type: "int" keycloak_quarkus_path: - required: false - description: "This should be set if proxy uses a different context-path for Keycloak" + description: "Deprecated in v26, use keycloak_quarkus_hostname instead." type: "str" keycloak_quarkus_http_enabled: default: true @@ -228,11 +230,15 @@ argument_specs: type: "str" keycloak_quarkus_frontend_url: required: false - description: "Service public URL" + description: "Deprecated in v26, use keycloak_quarkus_hostname instead." + type: "str" + keycloak_quarkus_admin: + required: false + description: "Service URL for the admin console" type: "str" keycloak_quarkus_admin_url: required: false - description: "Service URL for the admin console" + description: "Deprecated in v26, use keycloak_quarkus_admin instead." type: "str" keycloak_quarkus_metrics_enabled: default: false @@ -348,24 +354,18 @@ argument_specs: description: > Disables dynamically resolving the hostname from request headers. Should always be set to true in production, unless proxy verifies the Host header. - keycloak_quarkus_hostname_strict_backchannel: + keycloak_quarkus_hostname_backchannel_dynamic: default: false type: "bool" description: > - By default backchannel URLs are dynamically resolved from request headers to allow internal and external applications. If all - applications use the public URL this option should be enabled. + Enables dynamic resolving of backchannel URLs, including hostname, scheme, port and context path. + Set to true if your application accesses Keycloak via a private network. If set to true, hostname option needs to be specified as a full URL. keycloak_quarkus_spi_sticky_session_encoder_infinispan_should_attach_route: default: true type: "bool" description: > If the route should be attached to cookies to reflect the node that owns a particular session. If false, route is not attached to cookies and we rely on the session affinity capabilities from reverse proxy - keycloak_quarkus_hostname_strict_https: - type: "bool" - required: false - description: > - By default, Keycloak requires running using TLS/HTTPS. If the service MUST run without TLS/HTTPS, then set - this option to "true" keycloak_quarkus_ks_vault_enabled: default: false type: "bool" diff --git a/roles/keycloak_quarkus/templates/keycloak.conf.j2 b/roles/keycloak_quarkus/templates/keycloak.conf.j2 index 24972c3..c535738 100644 --- a/roles/keycloak_quarkus/templates/keycloak.conf.j2 +++ b/roles/keycloak_quarkus/templates/keycloak.conf.j2 @@ -10,13 +10,6 @@ db-password={{ keycloak_quarkus_db_pass }} {% endif %} {% endif %} -{% if keycloak_quarkus_hostname_strict_https is defined and keycloak_quarkus_hostname_strict_https is sameas true -%} -hostname-strict-https=true -{% endif -%} -{% if keycloak_quarkus_hostname_strict_https is defined and keycloak_quarkus_hostname_strict_https is sameas false -%} -hostname-strict-https=false -{% endif -%} - {% if keycloak.config_key_store_enabled %} # Config store config-keystore={{ keycloak_quarkus_config_key_store_file }} @@ -48,16 +41,10 @@ https-trust-store-password={{ keycloak_quarkus_https_trust_store_password }} {% endif %} # Client URL configuration -{% if keycloak_quarkus_frontend_url %} -hostname-url={{ keycloak_quarkus_frontend_url }} -{% else %} -hostname={{ keycloak_quarkus_host }} -hostname-port={{ keycloak_quarkus_port }} -hostname-path={{ keycloak_quarkus_path }} -{% endif %} -hostname-admin-url={{ keycloak_quarkus_admin_url }} +hostname={{ keycloak_quarkus_hostname }} +hostname-admin={{ keycloak_quarkus_admin }} hostname-strict={{ keycloak_quarkus_hostname_strict | lower }} -hostname-strict-backchannel={{ keycloak_quarkus_hostname_strict_backchannel | lower }} +hostname-backchannel-dynamic={{ keycloak_quarkus_hostname_backchannel_dynamic | lower }} # Cluster {% if keycloak_quarkus_ha_enabled %} diff --git a/roles/keycloak_quarkus/vars/main.yml b/roles/keycloak_quarkus/vars/main.yml index e59af55..eb654f2 100644 --- a/roles/keycloak_quarkus/vars/main.yml +++ b/roles/keycloak_quarkus/vars/main.yml @@ -4,7 +4,7 @@ keycloak: # noqa var-naming this is an internal dict of interpolated values config_dir: "{{ keycloak_quarkus_config_dir }}" bundle: "{{ keycloak_quarkus_archive }}" service_name: "keycloak" - health_url: "{{ 'https' if keycloak_quarkus_http_enabled == False else 'http' }}://{{ keycloak_quarkus_host }}:{{ keycloak_quarkus_https_port if keycloak_quarkus_http_enabled == False else keycloak_quarkus_http_port }}{{ keycloak_quarkus_http_relative_path }}{{ '/' \ + health_url: "{{ keycloak_quarkus_hostname }}{{ keycloak_quarkus_http_relative_path }}{{ '/' \ if keycloak_quarkus_http_relative_path | length > 1 else '' }}{{ keycloak_quarkus_health_check_url_path | default('realms/master/.well-known/openid-configuration') }}" cli_path: "{{ keycloak_quarkus_home }}/bin/kcadm.sh" service_user: "{{ keycloak_quarkus_service_user }}" From d0f19b59dc326951b24a7909822e1689ae9fb79e Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 10 Dec 2024 14:14:18 +0100 Subject: [PATCH 091/135] keycloak_quarkus: Add http_management_port and http_management_relative_path options RHBK v26 exposes health endpoints and metrics on this port moving forward. Note that the scheme of the MGMT interface is defined by the overall keycloak configuration: if https is enabled and configured, th MGMT interface is exposed via https and NOT via http; this might be breaking some configured load balancer health checks --- roles/keycloak_quarkus/README.md | 4 +++- roles/keycloak_quarkus/defaults/main.yml | 1 + roles/keycloak_quarkus/meta/argument_specs.yml | 12 ++++++++++-- roles/keycloak_quarkus/tasks/firewalld.yml | 13 ++++++++++++- roles/keycloak_quarkus/tasks/prereqs.yml | 11 ++++++++++- roles/keycloak_quarkus/templates/keycloak.conf.j2 | 8 ++++++++ 6 files changed, 44 insertions(+), 5 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 7f0542a..0c8aec8 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -52,6 +52,7 @@ Role Defaults |`keycloak_quarkus_path`| Deprecated, use `keycloak_quarkus_hostname` instead. | | |`keycloak_quarkus_http_port`| HTTP listening port | `8080` | |`keycloak_quarkus_https_port`| TLS HTTP listening port | `8443` | +|`keycloak_quarkus_http_management_port`| Port of the management interface. Relevant only when something is exposed on the management interface - see the guide for details. | `9000` | |`keycloak_quarkus_ajp_port`| AJP port | `8009` | |`keycloak_quarkus_service_user`| Posix account username | `keycloak` | |`keycloak_quarkus_service_group`| Posix account group | `keycloak` | @@ -69,6 +70,7 @@ Role Defaults |`keycloak_quarkus_admin`| Set the base URL for accessing the administration console, including scheme, host, port and path | | |`keycloak_quarkus_admin_url`| Deprecated, use `keycloak_quarkus_admin` instead. | | |`keycloak_quarkus_http_relative_path` | Set the path relative to / for serving resources. The path must start with a / | `/` | +|`keycloak_quarkus_http_management_relative_path` | Set the path relative to / for serving resources from management interface. The path must start with a /. If not given, the value is inherited from HTTP options. Relevant only when something is exposed on the management interface - see the guide for details. | `/` | |`keycloak_quarkus_http_enabled`| Enable listener on HTTP port | `True` | |`keycloak_quarkus_health_check_url_path`| Path to the health check endpoint; scheme, host and keycloak_quarkus_http_relative_path will be prepended automatically | `realms/master/.well-known/openid-configuration` | |`keycloak_quarkus_https_key_file_enabled`| Enable listener on HTTPS port | `False` | @@ -152,7 +154,7 @@ Role Defaults | Variable | Description | Default | |:---------|:------------|:--------| |`keycloak_quarkus_metrics_enabled`| Whether to enable metrics | `False` | -|`keycloak_quarkus_health_enabled`| If the server should expose health check endpoints | `True` | +|`keycloak_quarkus_health_enabled`| If the server should expose health check endpoints on the management interface | `True` | |`keycloak_quarkus_archive` | keycloak install archive filename | `keycloak-{{ keycloak_quarkus_version }}.zip` | |`keycloak_quarkus_installdir` | Installation path | `{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_quarkus_version }}` | |`keycloak_quarkus_home` | Installation work directory | `{{ keycloak_quarkus_installdir }}` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 805b102..05e0455 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -36,6 +36,7 @@ keycloak_quarkus_bind_address: 0.0.0.0 keycloak_quarkus_http_enabled: true keycloak_quarkus_http_port: 8080 keycloak_quarkus_https_port: 8443 +keycloak_quarkus_http_management_port: 9000 keycloak_quarkus_ajp_port: 8009 keycloak_quarkus_jgroups_port: 7800 keycloak_quarkus_java_heap_opts: "-Xms1024m -Xmx2048m" diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index b7a9ffe..2bfbf50 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -108,7 +108,7 @@ argument_specs: type: "int" keycloak_quarkus_health_check_url_path: default: "realms/master/.well-known/openid-configuration" - description: "Path to the health check endpoint; scheme, host and keycloak_quarkus_http_relative_path will be prepended automatically" + description: "Path to the health check endpoint; scheme, hostname will be prepended automatically" type: "str" keycloak_quarkus_https_key_file_enabled: default: false @@ -184,6 +184,10 @@ argument_specs: default: 8443 description: "HTTPS port" type: "int" + keycloak_quarkus_http_management_port: + default: 9000 + description: "Port of the management interface. Relevant only when something is exposed on the management interface - see the guide for details." + type: "int" keycloak_quarkus_ajp_port: default: 8009 description: "AJP port" @@ -228,6 +232,10 @@ argument_specs: default: / description: "Set the path relative to / for serving resources. The path must start with a /" type: "str" + keycloak_quarkus_http_management_relative_path: + required: false + description: "Set the path relative to / for serving resources from management interface. The path must start with a /. If not given, the value is inherited from HTTP options. Relevant only when something is exposed on the management interface - see the guide for details." + type: "str" keycloak_quarkus_frontend_url: required: false description: "Deprecated in v26, use keycloak_quarkus_hostname instead." @@ -246,7 +254,7 @@ argument_specs: type: "bool" keycloak_quarkus_health_enabled: default: true - description: "If the server should expose health check endpoints" + description: "If the server should expose health check endpoints on the management interface" type: "bool" keycloak_quarkus_ispn_user: default: "supervisor" diff --git a/roles/keycloak_quarkus/tasks/firewalld.yml b/roles/keycloak_quarkus/tasks/firewalld.yml index 2c3ef74..2d48124 100644 --- a/roles/keycloak_quarkus/tasks/firewalld.yml +++ b/roles/keycloak_quarkus/tasks/firewalld.yml @@ -12,7 +12,7 @@ enabled: true state: started -- name: "Configure firewall for {{ keycloak.service_name }} ports" +- name: "Configure firewall for {{ keycloak.service_name }} http port" become: true ansible.posix.firewalld: port: "{{ item }}" @@ -21,5 +21,16 @@ immediate: true loop: - "{{ keycloak_quarkus_http_port }}/tcp" + when: keycloak_quarkus_http_enabled | bool + +- name: "Configure firewall for {{ keycloak.service_name }} ports" + become: true + ansible.posix.firewalld: + port: "{{ item }}" + permanent: true + state: enabled + immediate: true + loop: - "{{ keycloak_quarkus_https_port }}/tcp" + - "{{ keycloak_quarkus_http_management_port }}/tcp" - "{{ keycloak_quarkus_jgroups_port }}/tcp" diff --git a/roles/keycloak_quarkus/tasks/prereqs.yml b/roles/keycloak_quarkus/tasks/prereqs.yml index db7555e..9b633f3 100644 --- a/roles/keycloak_quarkus/tasks/prereqs.yml +++ b/roles/keycloak_quarkus/tasks/prereqs.yml @@ -7,7 +7,7 @@ fail_msg: "The console administrator password is empty or invalid. Please set the keycloak_quarkus_bootstrap_admin_password to a 12+ char long string" success_msg: "{{ 'Console administrator password OK' }}" -- name: Validate relative path +- name: Validate http_relative_path ansible.builtin.assert: that: - keycloak_quarkus_http_relative_path is regex('^/.*') @@ -15,6 +15,15 @@ fail_msg: "The relative path for keycloak_quarkus_http_relative_path must begin with /" success_msg: "{{ 'Relative path OK' }}" +- name: Validate http_management_relative_path + ansible.builtin.assert: + that: + - keycloak_quarkus_http_management_relative_path is regex('^/.*') + quiet: true + fail_msg: "The relative path for keycloak_quarkus_http_management_relative_path must begin with /" + success_msg: "{{ 'Relative mgmt path OK' }}" + when: keycloak_quarkus_http_management_relative_path is defined + - name: Validate configuration ansible.builtin.assert: that: diff --git a/roles/keycloak_quarkus/templates/keycloak.conf.j2 b/roles/keycloak_quarkus/templates/keycloak.conf.j2 index c535738..f11e3ec 100644 --- a/roles/keycloak_quarkus/templates/keycloak.conf.j2 +++ b/roles/keycloak_quarkus/templates/keycloak.conf.j2 @@ -22,9 +22,17 @@ health-enabled={{ keycloak_quarkus_health_enabled | lower }} # HTTP http-enabled={{ keycloak_quarkus_http_enabled | lower }} +{% if keycloak_quarkus_http_enabled %} http-port={{ keycloak_quarkus_http_port }} +{% endif %} http-relative-path={{ keycloak_quarkus_http_relative_path }} +# Management +http-management-port={{ keycloak_quarkus_http_management_port }} +{% if keycloak_quarkus_http_management_relative_path is defined and keycloak_quarkus_http_management_relative_path | length > 0 %} +http-management-relative-path={{ keycloak_quarkus_http_management_relative_path }} +{% endif %} + # HTTPS https-port={{ keycloak_quarkus_https_port }} {% if keycloak_quarkus_https_key_file_enabled %} From e029e1c2fd0d86d765e824d78cb6c348f28efc8e Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Fri, 13 Dec 2024 12:02:06 +0100 Subject: [PATCH 092/135] keycloak_quarkus: Introduce keycloak_quarkus_health_check_url --- roles/keycloak_quarkus/README.md | 3 ++- roles/keycloak_quarkus/meta/argument_specs.yml | 5 ++++- roles/keycloak_quarkus/vars/main.yml | 3 +-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 0c8aec8..d3abd57 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -72,7 +72,8 @@ Role Defaults |`keycloak_quarkus_http_relative_path` | Set the path relative to / for serving resources. The path must start with a / | `/` | |`keycloak_quarkus_http_management_relative_path` | Set the path relative to / for serving resources from management interface. The path must start with a /. If not given, the value is inherited from HTTP options. Relevant only when something is exposed on the management interface - see the guide for details. | `/` | |`keycloak_quarkus_http_enabled`| Enable listener on HTTP port | `True` | -|`keycloak_quarkus_health_check_url_path`| Path to the health check endpoint; scheme, host and keycloak_quarkus_http_relative_path will be prepended automatically | `realms/master/.well-known/openid-configuration` | +|`keycloak_quarkus_health_check_url`| Full URL (including scheme, host, path, fragment etc.) used for health check endpoint; keycloak_quarkus_hostname will NOT be prepended; helpful when health checks should happen against http port, but keycloak_quarkus_hostname uses https scheme per default | `` | +|`keycloak_quarkus_health_check_url_path`| Path to the health check endpoint; keycloak_quarkus_hostname will be prepended automatically; Note that keycloak_quarkus_health_check_url takes precedence over this property | `realms/master/.well-known/openid-configuration` | |`keycloak_quarkus_https_key_file_enabled`| Enable listener on HTTPS port | `False` | |`keycloak_quarkus_key_file_copy_enabled`| Enable copy of key file to target host | `False` | |`keycloak_quarkus_key_content`| Content of the TLS private key. Use `"{{ lookup('file', 'server.key.pem') }}"` to lookup a file. | `""` | diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 2bfbf50..0e4eb42 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -106,9 +106,12 @@ argument_specs: default: 8080 description: "HTTP port" type: "int" + keycloak_quarkus_health_check_url: + description: "Full URL (including scheme, host, path, fragment etc.) used for health check endpoint; keycloak_quarkus_hostname will NOT be prepended; helpful when health checks should happen against http port, but keycloak_quarkus_hostname uses https scheme per default" + type: "str" keycloak_quarkus_health_check_url_path: default: "realms/master/.well-known/openid-configuration" - description: "Path to the health check endpoint; scheme, hostname will be prepended automatically" + description: "Path to the health check endpoint; keycloak_quarkus_hostname will be prepended automatically; Note that keycloak_quarkus_health_check_url takes precedence over this property" type: "str" keycloak_quarkus_https_key_file_enabled: default: false diff --git a/roles/keycloak_quarkus/vars/main.yml b/roles/keycloak_quarkus/vars/main.yml index eb654f2..2efe5e6 100644 --- a/roles/keycloak_quarkus/vars/main.yml +++ b/roles/keycloak_quarkus/vars/main.yml @@ -4,8 +4,7 @@ keycloak: # noqa var-naming this is an internal dict of interpolated values config_dir: "{{ keycloak_quarkus_config_dir }}" bundle: "{{ keycloak_quarkus_archive }}" service_name: "keycloak" - health_url: "{{ keycloak_quarkus_hostname }}{{ keycloak_quarkus_http_relative_path }}{{ '/' \ - if keycloak_quarkus_http_relative_path | length > 1 else '' }}{{ keycloak_quarkus_health_check_url_path | default('realms/master/.well-known/openid-configuration') }}" + health_url: "{{ keycloak_quarkus_health_check_url | default(keycloak_quarkus_hostname + '/' + (keycloak_quarkus_health_check_url_path | default('realms/master/.well-known/openid-configuration'))) }}" cli_path: "{{ keycloak_quarkus_home }}/bin/kcadm.sh" service_user: "{{ keycloak_quarkus_service_user }}" service_group: "{{ keycloak_quarkus_service_group }}" From b3e93dd89b8506d3a6f283491987286d15c5185b Mon Sep 17 00:00:00 2001 From: Nils Deckert Date: Fri, 20 Dec 2024 17:21:01 +0100 Subject: [PATCH 093/135] Skip certificate checking --- roles/keycloak_realm/tasks/main.yml | 1 + roles/keycloak_realm/tasks/manage_user_client_roles.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/roles/keycloak_realm/tasks/main.yml b/roles/keycloak_realm/tasks/main.yml index 8f33fcc..7595ba3 100644 --- a/roles/keycloak_realm/tasks/main.yml +++ b/roles/keycloak_realm/tasks/main.yml @@ -15,6 +15,7 @@ ansible.builtin.uri: url: "{{ keycloak_url }}{{ keycloak_context }}/admin/realms/{{ keycloak_realm }}" method: GET + validate_certs: false status_code: - 200 - 404 diff --git a/roles/keycloak_realm/tasks/manage_user_client_roles.yml b/roles/keycloak_realm/tasks/manage_user_client_roles.yml index 3884f42..f9e0329 100644 --- a/roles/keycloak_realm/tasks/manage_user_client_roles.yml +++ b/roles/keycloak_realm/tasks/manage_user_client_roles.yml @@ -3,6 +3,7 @@ ansible.builtin.uri: url: "{{ keycloak_url }}{{ keycloak_context }}/admin/realms/{{ client_role.realm | default(keycloak_realm) }}" method: GET + validate_certs: false status_code: - 200 headers: @@ -16,6 +17,7 @@ default(keycloak_realm) }}/users/{{ (keycloak_user.json | first).id }}/role-mappings/clients/{{ (create_client_result.results | \ selectattr('end_state.clientId', 'equalto', client_role.client) | list | first).end_state.id }}/available" method: GET + validate_certs: false status_code: - 200 headers: From 86284b12c28f578910ecd3cd200cab9679b6d8ee Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 10 Dec 2024 15:10:29 +0100 Subject: [PATCH 094/135] Fix molecule tests --- molecule/debian/converge.yml | 5 ++++- molecule/debian/prepare.yml | 1 + molecule/default/converge.yml | 5 ++++- molecule/default/molecule.yml | 1 + molecule/default/prepare.yml | 2 +- molecule/default/verify.yml | 3 ++- molecule/https_revproxy/converge.yml | 5 ++--- molecule/overridexml/molecule.yml | 1 + molecule/quarkus-devmode/converge.yml | 5 ++++- molecule/quarkus-devmode/molecule.yml | 2 ++ molecule/quarkus/converge.yml | 7 +++++-- molecule/quarkus/molecule.yml | 1 + molecule/quarkus/prepare.yml | 2 +- molecule/quarkus/verify.yml | 13 +++++++------ molecule/quarkus_ha/converge.yml | 3 +-- molecule/quarkus_ha/molecule.yml | 2 ++ molecule/quarkus_upgrade/converge.yml | 2 +- molecule/quarkus_upgrade/molecule.yml | 2 ++ molecule/quarkus_upgrade/prepare.yml | 2 +- molecule/quarkus_upgrade/vars.yml | 2 +- molecule/quarkus_upgrade/verify.yml | 2 +- roles/keycloak_quarkus/README.md | 2 +- roles/keycloak_quarkus/meta/argument_specs.yml | 4 ++-- roles/keycloak_quarkus/vars/redhat.yml | 2 +- 24 files changed, 49 insertions(+), 27 deletions(-) diff --git a/molecule/debian/converge.yml b/molecule/debian/converge.yml index 7fab040..e853b38 100644 --- a/molecule/debian/converge.yml +++ b/molecule/debian/converge.yml @@ -5,14 +5,17 @@ keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" - keycloak_quarkus_hostname: http://instance + keycloak_quarkus_hostname: http://instance:8080 keycloak_quarkus_log: file keycloak_quarkus_start_dev: true keycloak_quarkus_proxy_mode: none roles: - role: keycloak_quarkus - role: keycloak_realm + keycloak_url: "{{ keycloak_quarkus_hostname }}" keycloak_context: '' + keycloak_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}" + keycloak_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}" keycloak_client_users: - username: TestUser password: password diff --git a/molecule/debian/prepare.yml b/molecule/debian/prepare.yml index 6025ef9..267386e 100644 --- a/molecule/debian/prepare.yml +++ b/molecule/debian/prepare.yml @@ -7,5 +7,6 @@ ansible.builtin.apt: name: - sudo + # - openjdk-21-jdk-headless # this is not available in ghcr.io/hspaans/molecule-containers:debian-11 (neither in debian-12) since the images are using outdated package sources - openjdk-17-jdk-headless state: present diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index 13a771b..de97748 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -5,7 +5,7 @@ keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" - keycloak_quarkus_hostname: http://instance + keycloak_quarkus_hostname: http://instance:8080 keycloak_quarkus_log: file keycloak_quarkus_log_level: debug keycloak_quarkus_log_target: /tmp/keycloak @@ -16,7 +16,10 @@ roles: - role: keycloak_quarkus - role: keycloak_realm + keycloak_url: "{{ keycloak_quarkus_hostname }}" keycloak_context: '' + keycloak_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}" + keycloak_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}" keycloak_client_users: - username: TestUser password: password diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml index c133eee..62fa2b7 100644 --- a/molecule/default/molecule.yml +++ b/molecule/default/molecule.yml @@ -11,6 +11,7 @@ platforms: - "8080/tcp" - "8443/tcp" - "8009/tcp" + - "9000/tcp" provisioner: name: ansible config_options: diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml index 899dabd..eefd5cf 100644 --- a/molecule/default/prepare.yml +++ b/molecule/default/prepare.yml @@ -22,7 +22,7 @@ - name: Download keycloak archive to controller directory ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user - url: https://github.com/keycloak/keycloak/releases/download/24.0.5/keycloak-24.0.5.zip + url: https://github.com/keycloak/keycloak/releases/download/26.0.7/keycloak-26.0.7.zip dest: /tmp/keycloak mode: '0640' delegate_to: localhost diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml index eac4339..ae21396 100644 --- a/molecule/default/verify.yml +++ b/molecule/default/verify.yml @@ -2,6 +2,7 @@ - name: Verify hosts: all vars: + keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_uri: "http://localhost:8080" tasks: @@ -16,7 +17,7 @@ ansible.builtin.uri: url: "{{ keycloak_uri }}/realms/master/protocol/openid-connect/token" method: POST - body: "client_id=admin-cli&username=admin&password={{ keycloak_quarkus_bootstrap_admin_user }}&grant_type=password" + body: "client_id=admin-cli&username={{ keycloak_quarkus_bootstrap_admin_user }}&password={{ keycloak_quarkus_bootstrap_admin_user }}&grant_type=password" validate_certs: no register: keycloak_auth_response until: keycloak_auth_response.status == 200 diff --git a/molecule/https_revproxy/converge.yml b/molecule/https_revproxy/converge.yml index 7245265..92994fa 100644 --- a/molecule/https_revproxy/converge.yml +++ b/molecule/https_revproxy/converge.yml @@ -5,13 +5,12 @@ keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" - keycloak_realm: TestRealm - keycloak_quarkus_hostname: http://instance + keycloak_quarkus_hostname: https://proxy keycloak_quarkus_log: file keycloak_quarkus_http_enabled: True keycloak_quarkus_http_port: 8080 keycloak_quarkus_proxy_mode: edge keycloak_quarkus_http_relative_path: / - keycloak_quarkus_frontend_url: https://proxy/ + keycloak_quarkus_health_check_url: http://proxy:8080/realms/master/.well-known/openid-configuration roles: - role: keycloak_quarkus diff --git a/molecule/overridexml/molecule.yml b/molecule/overridexml/molecule.yml index c133eee..62fa2b7 100644 --- a/molecule/overridexml/molecule.yml +++ b/molecule/overridexml/molecule.yml @@ -11,6 +11,7 @@ platforms: - "8080/tcp" - "8443/tcp" - "8009/tcp" + - "9000/tcp" provisioner: name: ansible config_options: diff --git a/molecule/quarkus-devmode/converge.yml b/molecule/quarkus-devmode/converge.yml index 5cd4ea1..6c0b14f 100644 --- a/molecule/quarkus-devmode/converge.yml +++ b/molecule/quarkus-devmode/converge.yml @@ -7,14 +7,17 @@ keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_realm: TestRealm keycloak_quarkus_log: file - keycloak_quarkus_frontend_url: 'http://localhost:8080/' + keycloak_quarkus_hostname: 'http://localhost:8080' keycloak_quarkus_start_dev: True keycloak_quarkus_proxy_mode: none keycloak_quarkus_java_home: /opt/openjdk/ roles: - role: keycloak_quarkus - role: keycloak_realm + keycloak_url: "{{ keycloak_quarkus_hostname }}" keycloak_context: '' + keycloak_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}" + keycloak_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}" keycloak_client_default_roles: - TestRoleAdmin - TestRoleUser diff --git a/molecule/quarkus-devmode/molecule.yml b/molecule/quarkus-devmode/molecule.yml index 191234d..5d22ab8 100644 --- a/molecule/quarkus-devmode/molecule.yml +++ b/molecule/quarkus-devmode/molecule.yml @@ -10,8 +10,10 @@ platforms: port_bindings: - "8080/tcp" - "8009/tcp" + - "9000/tcp" published_ports: - 0.0.0.0:8080:8080/tcp + - 0.0.0.0:9000:9000/TCP provisioner: name: ansible config_options: diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index 9b7b3a7..9bfbc0f 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -6,7 +6,7 @@ keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_realm: TestRealm - keycloak_quarkus_hostname: http://instance + keycloak_quarkus_hostname: https://instance:8443 keycloak_quarkus_log: file keycloak_quarkus_log_level: debug # needed for the verify step keycloak_quarkus_https_key_file_enabled: true @@ -37,7 +37,7 @@ repository_url: https://repo1.maven.org/maven2/ # https://mvnrepository.com/artifact/org.keycloak/keycloak-kerberos-federation/24.0.4 group_id: org.keycloak artifact_id: keycloak-kerberos-federation - version: 24.0.5 # optional + version: 26.0.7 # optional # username: myUser # optional # password: myPAT # optional # - id: my-static-theme @@ -51,7 +51,10 @@ roles: - role: keycloak_quarkus - role: keycloak_realm + keycloak_url: "{{ keycloak_quarkus_hostname }}" keycloak_context: '' + keycloak_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}" + keycloak_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}" keycloak_client_default_roles: - TestRoleAdmin - TestRoleUser diff --git a/molecule/quarkus/molecule.yml b/molecule/quarkus/molecule.yml index c04e300..c083e77 100644 --- a/molecule/quarkus/molecule.yml +++ b/molecule/quarkus/molecule.yml @@ -11,6 +11,7 @@ platforms: - "8080/tcp" - "8443/tcp" - "8009/tcp" + - "9000/tcp" published_ports: - 0.0.0.0:8443:8443/tcp provisioner: diff --git a/molecule/quarkus/prepare.yml b/molecule/quarkus/prepare.yml index 21a0f30..d8ef369 100644 --- a/molecule/quarkus/prepare.yml +++ b/molecule/quarkus/prepare.yml @@ -24,7 +24,7 @@ - name: Make sure a jre is available (for keytool to prepare keystore) delegate_to: localhost ansible.builtin.package: - name: "{{ 'java-17-openjdk-headless' if hera_home | length > 0 else 'openjdk-17-jdk-headless' }}" + name: "{{ 'java-21-openjdk-headless' if hera_home | length > 0 else 'openjdk-21-jdk-headless' }}" state: present become: true failed_when: false diff --git a/molecule/quarkus/verify.yml b/molecule/quarkus/verify.yml index 1ee3b61..f041d88 100644 --- a/molecule/quarkus/verify.yml +++ b/molecule/quarkus/verify.yml @@ -2,7 +2,8 @@ - name: Verify hosts: all vars: - keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" tasks: - name: Populate service facts ansible.builtin.service_facts: @@ -35,10 +36,10 @@ - name: Verify endpoint URLs ansible.builtin.assert: that: - - (openid_config.stdout | from_json)["backchannel_authentication_endpoint"] == 'https://instance/realms/master/protocol/openid-connect/ext/ciba/auth' - - (openid_config.stdout | from_json)['issuer'] == 'https://instance/realms/master' - - (openid_config.stdout | from_json)['authorization_endpoint'] == 'https://instance/realms/master/protocol/openid-connect/auth' - - (openid_config.stdout | from_json)['token_endpoint'] == 'https://instance/realms/master/protocol/openid-connect/token' + - (openid_config.stdout | from_json)["backchannel_authentication_endpoint"] == 'https://instance:8443/realms/master/protocol/openid-connect/ext/ciba/auth' + - (openid_config.stdout | from_json)['issuer'] == 'https://instance:8443/realms/master' + - (openid_config.stdout | from_json)['authorization_endpoint'] == 'https://instance:8443/realms/master/protocol/openid-connect/auth' + - (openid_config.stdout | from_json)['token_endpoint'] == 'https://instance:8443/realms/master/protocol/openid-connect/token' delegate_to: localhost - name: Check log folder @@ -91,7 +92,7 @@ ansible.builtin.uri: url: "https://instance:8443/realms/master/protocol/openid-connect/token" method: POST - body: "client_id=admin-cli&username=admin&password={{ keycloak_quarkus_bootstrap_admin_user }}&grant_type=password" + body: "client_id=admin-cli&username={{ keycloak_quarkus_bootstrap_admin_user }}&password={{ keycloak_quarkus_bootstrap_admin_password}}&grant_type=password" validate_certs: no register: keycloak_auth_response until: keycloak_auth_response.status == 200 diff --git a/molecule/quarkus_ha/converge.yml b/molecule/quarkus_ha/converge.yml index 238fbb5..d37ad79 100644 --- a/molecule/quarkus_ha/converge.yml +++ b/molecule/quarkus_ha/converge.yml @@ -5,8 +5,7 @@ keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" - keycloak_realm: TestRealm - keycloak_quarkus_hostname: "http://{{ inventory_hostname }}" + keycloak_quarkus_hostname: "http://{{ inventory_hostname }}:8080" keycloak_quarkus_log: file keycloak_quarkus_log_level: info keycloak_quarkus_https_key_file_enabled: true diff --git a/molecule/quarkus_ha/molecule.yml b/molecule/quarkus_ha/molecule.yml index 8e07e0f..43a8286 100644 --- a/molecule/quarkus_ha/molecule.yml +++ b/molecule/quarkus_ha/molecule.yml @@ -14,6 +14,7 @@ platforms: port_bindings: - "8080/tcp" - "8443/tcp" + - "9000/tcp" - name: instance2 image: registry.access.redhat.com/ubi9/ubi-init:latest pre_build_image: true @@ -26,6 +27,7 @@ platforms: port_bindings: - "8080/tcp" - "8443/tcp" + - "9000/tcp" - name: postgres image: ubuntu/postgres:14-22.04_beta pre_build_image: true diff --git a/molecule/quarkus_upgrade/converge.yml b/molecule/quarkus_upgrade/converge.yml index 6025b7c..9b57436 100644 --- a/molecule/quarkus_upgrade/converge.yml +++ b/molecule/quarkus_upgrade/converge.yml @@ -5,6 +5,6 @@ - vars.yml vars: keycloak_quarkus_show_deprecation_warnings: false - keycloak_quarkus_version: 24.0.3 + keycloak_quarkus_version: 26.0.7 roles: - role: keycloak_quarkus diff --git a/molecule/quarkus_upgrade/molecule.yml b/molecule/quarkus_upgrade/molecule.yml index 77f687f..21782e8 100644 --- a/molecule/quarkus_upgrade/molecule.yml +++ b/molecule/quarkus_upgrade/molecule.yml @@ -13,8 +13,10 @@ platforms: privileged: true port_bindings: - 8080:8080 + - "9000/tcp" published_ports: - 0.0.0.0:8080:8080/TCP + - 0.0.0.0:9000:9000/TCP provisioner: name: ansible playbooks: diff --git a/molecule/quarkus_upgrade/prepare.yml b/molecule/quarkus_upgrade/prepare.yml index bebfc68..a6892e3 100644 --- a/molecule/quarkus_upgrade/prepare.yml +++ b/molecule/quarkus_upgrade/prepare.yml @@ -5,7 +5,7 @@ - vars.yml vars: sudo_pkg_name: sudo - keycloak_quarkus_version: 23.0.7 + keycloak_quarkus_version: 24.0.5 pre_tasks: - name: Install sudo ansible.builtin.apt: diff --git a/molecule/quarkus_upgrade/vars.yml b/molecule/quarkus_upgrade/vars.yml index 52ab103..1567ae4 100644 --- a/molecule/quarkus_upgrade/vars.yml +++ b/molecule/quarkus_upgrade/vars.yml @@ -2,7 +2,7 @@ keycloak_quarkus_offline_install: false keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_realm: TestRealm -keycloak_quarkus_hostname: http://instance +keycloak_quarkus_hostname: http://instance:8080 keycloak_quarkus_log: file keycloak_quarkus_https_key_file_enabled: true keycloak_quarkus_log_target: /tmp/keycloak diff --git a/molecule/quarkus_upgrade/verify.yml b/molecule/quarkus_upgrade/verify.yml index 3214d9f..1c4a0ba 100644 --- a/molecule/quarkus_upgrade/verify.yml +++ b/molecule/quarkus_upgrade/verify.yml @@ -17,7 +17,7 @@ - name: Verify we are running on requested jvm ansible.builtin.shell: | set -eo pipefail - ps -ef | grep 'etc/alternatives/.*17' | grep -v grep + ps -ef | grep 'etc/alternatives/.*21' | grep -v grep changed_when: false - name: Verify token api call diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index d3abd57..bafc150 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -59,7 +59,7 @@ Role Defaults |`keycloak_quarkus_service_restart_always`| systemd restart always behavior activation | `False` | |`keycloak_quarkus_service_restart_on_failure`| systemd restart on-failure behavior activation | `False` | |`keycloak_quarkus_service_restartsec`| systemd RestartSec | `10s` | -|`keycloak_quarkus_jvm_package`| RHEL java package runtime | `java-17-openjdk-headless` | +|`keycloak_quarkus_jvm_package`| RHEL java package runtime | `java-21-openjdk-headless` | |`keycloak_quarkus_java_home`| JAVA_HOME of installed JRE, leave empty for using specified keycloak_quarkus_jvm_package RPM path | `None` | |`keycloak_quarkus_java_heap_opts`| Heap memory JVM setting | `-Xms1024m -Xmx2048m` | |`keycloak_quarkus_java_jvm_opts`| Other JVM settings | same as keycloak | diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 0e4eb42..3eaf5d4 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -22,7 +22,7 @@ argument_specs: description: "Perform an offline install" type: "bool" keycloak_quarkus_jvm_package: - default: "java-11-openjdk-headless" + default: "java-21-openjdk-headless" description: "RHEL java package runtime" type: "str" keycloak_quarkus_java_home: @@ -479,7 +479,7 @@ argument_specs: downstream: options: rhbk_version: - default: "26.0.6" + default: "26.0.7" description: "Red Hat Build of Keycloak version" type: "str" rhbk_archive: diff --git a/roles/keycloak_quarkus/vars/redhat.yml b/roles/keycloak_quarkus/vars/redhat.yml index c311321..9af167f 100644 --- a/roles/keycloak_quarkus/vars/redhat.yml +++ b/roles/keycloak_quarkus/vars/redhat.yml @@ -1,5 +1,5 @@ --- -keycloak_quarkus_varjvm_package: "{{ keycloak_quarkus_jvm_package | default('java-17-openjdk-headless') }}" +keycloak_quarkus_varjvm_package: "{{ keycloak_quarkus_jvm_package | default('java-21-openjdk-headless') }}" keycloak_quarkus_prereq_package_list: - "{{ keycloak_quarkus_varjvm_package }}" - unzip From fa367212079a9d55e13013fba22cb64ac28fd5a2 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Mon, 20 Jan 2025 09:44:27 +0100 Subject: [PATCH 095/135] Improve string interpolation --- roles/keycloak_quarkus/vars/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/keycloak_quarkus/vars/main.yml b/roles/keycloak_quarkus/vars/main.yml index 2efe5e6..997d7dc 100644 --- a/roles/keycloak_quarkus/vars/main.yml +++ b/roles/keycloak_quarkus/vars/main.yml @@ -4,7 +4,7 @@ keycloak: # noqa var-naming this is an internal dict of interpolated values config_dir: "{{ keycloak_quarkus_config_dir }}" bundle: "{{ keycloak_quarkus_archive }}" service_name: "keycloak" - health_url: "{{ keycloak_quarkus_health_check_url | default(keycloak_quarkus_hostname + '/' + (keycloak_quarkus_health_check_url_path | default('realms/master/.well-known/openid-configuration'))) }}" + health_url: "{{ keycloak_quarkus_health_check_url | default(keycloak_quarkus_hostname ~ '/' ~ (keycloak_quarkus_health_check_url_path | default('realms/master/.well-known/openid-configuration'))) }}" cli_path: "{{ keycloak_quarkus_home }}/bin/kcadm.sh" service_user: "{{ keycloak_quarkus_service_user }}" service_group: "{{ keycloak_quarkus_service_group }}" From 5db96afa56a589b445fc2481c08b1fad57e0fdb6 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 1 Apr 2025 15:20:37 +0200 Subject: [PATCH 096/135] Variables names must not be Ansible reserved names --- roles/keycloak/tasks/rhsso_cli.yml | 6 +++--- roles/keycloak/tasks/rhsso_patch.yml | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/roles/keycloak/tasks/rhsso_cli.yml b/roles/keycloak/tasks/rhsso_cli.yml index fd41dd6..e40dec8 100644 --- a/roles/keycloak/tasks/rhsso_cli.yml +++ b/roles/keycloak/tasks/rhsso_cli.yml @@ -2,12 +2,12 @@ - name: Ensure required params for CLI have been provided ansible.builtin.assert: that: - - query is defined + - cli_query is defined fail_msg: "Missing required parameters to execute CLI." quiet: true -- name: "Execute CLI query: {{ query }}" +- name: "Execute CLI query: {{ cli_query }}" ansible.builtin.command: > - {{ keycloak.cli_path }} --connect --command='{{ query }}' --controller={{ keycloak_host }}:{{ keycloak_management_http_port }} + {{ keycloak.cli_path }} --connect --command='{{ cli_query }}' --controller={{ keycloak_host }}:{{ keycloak_management_http_port }} changed_when: false register: cli_result diff --git a/roles/keycloak/tasks/rhsso_patch.yml b/roles/keycloak/tasks/rhsso_patch.yml index e7ac3f0..23d75bf 100644 --- a/roles/keycloak/tasks/rhsso_patch.yml +++ b/roles/keycloak/tasks/rhsso_patch.yml @@ -106,7 +106,7 @@ - name: "Check installed patches" ansible.builtin.include_tasks: rhsso_cli.yml vars: - query: "patch info" + cli_query: "patch info" args: apply: become: true @@ -121,7 +121,7 @@ - name: "Apply patch {{ patch_version }} to server" ansible.builtin.include_tasks: rhsso_cli.yml vars: - query: "patch apply {{ patch_archive }}" + cli_query: "patch apply {{ patch_archive }}" args: apply: become: true @@ -130,7 +130,7 @@ - name: "Restart server to ensure patch content is running" ansible.builtin.include_tasks: rhsso_cli.yml vars: - query: "shutdown --restart" + cli_query: "shutdown --restart" when: - cli_result.rc == 0 args: @@ -149,7 +149,7 @@ - name: "Query installed patch after restart" ansible.builtin.include_tasks: rhsso_cli.yml vars: - query: "patch info" + cli_query: "patch info" args: apply: become: true From 7f980c44d287971c1cc4d9c899518549d2c82fa6 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 8 Apr 2025 11:58:47 +0200 Subject: [PATCH 097/135] Bump major and ansible-core versions --- CONTRIBUTING.md | 2 +- README.md | 2 +- galaxy.yml | 2 +- meta/runtime.yml | 2 +- roles/keycloak/meta/main.yml | 2 +- roles/keycloak_quarkus/meta/main.yml | 2 +- roles/keycloak_realm/meta/main.yml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 21ddc18..95b60ed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ virtualenv $PATH_TO_DEV_VIRTUALENV # activate the virtual env source $PATH_TO_DEV_VIRTUALENV/bin/activate # install ansible and tools onto the virtualenv -pip install yamllint 'molecule>=6.0' 'molecule-plugins[docker]' 'ansible-core>=2.15' ansible-lint +pip install yamllint 'molecule>=6.0' 'molecule-plugins[docker]' 'ansible-core>=2.16' ansible-lint # install collection dependencies ansible-galaxy collection install -r requirements.yml # install python dependencies diff --git a/README.md b/README.md index e8c289e..9e9867d 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Collection to install and configure [Keycloak](https://www.keycloak.org/) or [Re ## Ansible version compatibility -This collection has been tested against following Ansible versions: **>=2.15.0**. +This collection has been tested against following Ansible versions: **>=2.16.0**. Plugins and modules within a collection may be tested with only specific Ansible versions. A collection may contain metadata that identifies these versions. diff --git a/galaxy.yml b/galaxy.yml index a6511a8..76c46c4 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "2.4.4" +version: "3.0.0" readme: README.md authors: - Romain Pelisse diff --git a/meta/runtime.yml b/meta/runtime.yml index 1e85b01..49c7554 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -1,2 +1,2 @@ --- -requires_ansible: ">=2.15.0" +requires_ansible: ">=2.16.0" diff --git a/roles/keycloak/meta/main.yml b/roles/keycloak/meta/main.yml index a27afb8..59499e6 100644 --- a/roles/keycloak/meta/main.yml +++ b/roles/keycloak/meta/main.yml @@ -12,7 +12,7 @@ galaxy_info: license: Apache License 2.0 - min_ansible_version: "2.15" + min_ansible_version: "2.16" platforms: - name: EL diff --git a/roles/keycloak_quarkus/meta/main.yml b/roles/keycloak_quarkus/meta/main.yml index 40a487f..65b5e50 100644 --- a/roles/keycloak_quarkus/meta/main.yml +++ b/roles/keycloak_quarkus/meta/main.yml @@ -8,7 +8,7 @@ galaxy_info: license: Apache License 2.0 - min_ansible_version: "2.15" + min_ansible_version: "2.16" platforms: - name: EL diff --git a/roles/keycloak_realm/meta/main.yml b/roles/keycloak_realm/meta/main.yml index ab861ed..97c69d2 100644 --- a/roles/keycloak_realm/meta/main.yml +++ b/roles/keycloak_realm/meta/main.yml @@ -8,7 +8,7 @@ galaxy_info: license: Apache License 2.0 - min_ansible_version: "2.15" + min_ansible_version: "2.16" platforms: - name: EL From 744766fe3bc52a50d9e0b4729179e11c149327f1 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 8 Apr 2025 15:36:38 +0200 Subject: [PATCH 098/135] update doc generation to 2.16 --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index c8f8e2d..303f3a6 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,7 +1,7 @@ antsibull>=0.17.0 antsibull-docs antsibull-changelog -ansible-core>=2.14.1 +ansible-core>=2.16.0 ansible-pygments sphinx-rtd-theme git+https://github.com/felixfontein/ansible-basic-sphinx-ext From ac0ceca35f46fbfce135648fea88deacc0b7d45b Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 9 Apr 2025 09:45:22 +0200 Subject: [PATCH 099/135] Update to ubi9 --- molecule/default/molecule.yml | 2 +- molecule/https_revproxy/molecule.yml | 4 ++-- molecule/overridexml/molecule.yml | 2 +- molecule/quarkus-devmode/molecule.yml | 2 +- molecule/quarkus/molecule.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml index 62fa2b7..011c08e 100644 --- a/molecule/default/molecule.yml +++ b/molecule/default/molecule.yml @@ -3,7 +3,7 @@ driver: name: docker platforms: - name: instance - image: registry.access.redhat.com/ubi8/ubi-init:latest + image: registry.access.redhat.com/ubi9/ubi-init:latest pre_build_image: true privileged: true command: "/usr/sbin/init" diff --git a/molecule/https_revproxy/molecule.yml b/molecule/https_revproxy/molecule.yml index 48bf375..7ad8db8 100644 --- a/molecule/https_revproxy/molecule.yml +++ b/molecule/https_revproxy/molecule.yml @@ -3,7 +3,7 @@ driver: name: docker platforms: - name: instance - image: registry.access.redhat.com/ubi8/ubi-init:latest + image: registry.access.redhat.com/ubi9/ubi-init:latest pre_build_image: true privileged: true command: "/usr/sbin/init" @@ -14,7 +14,7 @@ platforms: published_ports: - 0.0.0.0:8080:8080/tcp - name: proxy - image: registry.access.redhat.com/ubi8/ubi-init:latest + image: registry.access.redhat.com/ubi9/ubi-init:latest pre_build_image: true privileged: true command: "/usr/sbin/init" diff --git a/molecule/overridexml/molecule.yml b/molecule/overridexml/molecule.yml index 62fa2b7..011c08e 100644 --- a/molecule/overridexml/molecule.yml +++ b/molecule/overridexml/molecule.yml @@ -3,7 +3,7 @@ driver: name: docker platforms: - name: instance - image: registry.access.redhat.com/ubi8/ubi-init:latest + image: registry.access.redhat.com/ubi9/ubi-init:latest pre_build_image: true privileged: true command: "/usr/sbin/init" diff --git a/molecule/quarkus-devmode/molecule.yml b/molecule/quarkus-devmode/molecule.yml index 5d22ab8..2da7e01 100644 --- a/molecule/quarkus-devmode/molecule.yml +++ b/molecule/quarkus-devmode/molecule.yml @@ -3,7 +3,7 @@ driver: name: docker platforms: - name: instance - image: registry.access.redhat.com/ubi8/ubi-init:latest + image: registry.access.redhat.com/ubi9/ubi-init:latest pre_build_image: true privileged: true command: "/usr/sbin/init" diff --git a/molecule/quarkus/molecule.yml b/molecule/quarkus/molecule.yml index c083e77..60995cc 100644 --- a/molecule/quarkus/molecule.yml +++ b/molecule/quarkus/molecule.yml @@ -3,7 +3,7 @@ driver: name: docker platforms: - name: instance - image: registry.access.redhat.com/ubi8/ubi-init:latest + image: registry.access.redhat.com/ubi9/ubi-init:latest pre_build_image: true privileged: true command: "/usr/sbin/init" From f628b84fb0287942c5cbd40a7776d61acfb21ea5 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 9 Apr 2025 17:21:49 +0200 Subject: [PATCH 100/135] disable restart health check because of selfsigned cert --- molecule/quarkus/converge.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index 9bfbc0f..db30473 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -22,6 +22,7 @@ keycloak_quarkus_systemd_wait_for_timeout: 20 keycloak_quarkus_systemd_wait_for_delay: 2 keycloak_quarkus_systemd_wait_for_log: true + keycloak_quarkus_restart_health_check: false # would fail because of self-signed cert keycloak_quarkus_providers: - id: http-client spi: connections From 314e2f26b27d264960d72c4e235ba6c0b69f4e58 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 9 Apr 2025 17:22:22 +0200 Subject: [PATCH 101/135] Fix spell in parameter name --- molecule/quarkus/converge.yml | 2 +- molecule/quarkus/molecule.yml | 1 + molecule/quarkus/verify.yml | 8 ++++---- molecule/quarkus_ha/molecule.yml | 1 + roles/keycloak_quarkus/README.md | 2 +- roles/keycloak_quarkus/defaults/main.yml | 2 +- roles/keycloak_quarkus/meta/argument_specs.yml | 2 +- roles/keycloak_quarkus/tasks/restart.yml | 2 +- roles/keycloak_quarkus/tasks/start.yml | 1 + 9 files changed, 12 insertions(+), 9 deletions(-) diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index db30473..b7c15e1 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -52,7 +52,7 @@ roles: - role: keycloak_quarkus - role: keycloak_realm - keycloak_url: "{{ keycloak_quarkus_hostname }}" + keycloak_url: http://instance:8080 keycloak_context: '' keycloak_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}" keycloak_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}" diff --git a/molecule/quarkus/molecule.yml b/molecule/quarkus/molecule.yml index 60995cc..cf975e5 100644 --- a/molecule/quarkus/molecule.yml +++ b/molecule/quarkus/molecule.yml @@ -31,6 +31,7 @@ provisioner: ansible_python_interpreter: "{{ ansible_playbook_python }}" env: ANSIBLE_FORCE_COLOR: "true" + PYTHONHTTPSVERIFY: 0 verifier: name: ansible scenario: diff --git a/molecule/quarkus/verify.yml b/molecule/quarkus/verify.yml index f041d88..1d9d2c3 100644 --- a/molecule/quarkus/verify.yml +++ b/molecule/quarkus/verify.yml @@ -102,8 +102,8 @@ - name: "Get Clients" ansible.builtin.uri: url: "https://instance:8443/admin/realms/TestRealm/clients" + validate_certs: false headers: - validate_certs: false Authorization: "Bearer {{ keycloak_auth_response.json.access_token }}" register: keycloak_clients @@ -114,15 +114,15 @@ - name: "Get Client {{ keycloak_client_uuid }}" ansible.builtin.uri: url: "https://instance:8443/admin/realms/TestRealm/clients/{{ keycloak_client_uuid }}" + validate_certs: false headers: - validate_certs: false Authorization: "Bearer {{ keycloak_auth_response.json.access_token }}" register: keycloak_test_client - name: "Get Client roles" ansible.builtin.uri: url: "https://instance:8443/admin/realms/TestRealm/clients/{{ keycloak_client_uuid }}/roles" + validate_certs: false headers: - validate_certs: false Authorization: "Bearer {{ keycloak_auth_response.json.access_token }}" - register: keycloak_test_client_roles \ No newline at end of file + register: keycloak_test_client_roles diff --git a/molecule/quarkus_ha/molecule.yml b/molecule/quarkus_ha/molecule.yml index 43a8286..ed09971 100644 --- a/molecule/quarkus_ha/molecule.yml +++ b/molecule/quarkus_ha/molecule.yml @@ -65,6 +65,7 @@ provisioner: ansible_python_interpreter: "{{ ansible_playbook_python }}" env: ANSIBLE_FORCE_COLOR: "true" + PYTHONHTTPSVERIFY: 0 verifier: name: ansible scenario: diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index bafc150..fb30ce9 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -112,7 +112,7 @@ Role Defaults |`keycloak_quarkus_restart_strategy`| Strategy task file for restarting in HA (one of provided restart/['serial.yml','none.yml','serial_then_parallel.yml']) or path to file when providing custom strategy | `restart/serial.yml` | |`keycloak_quarkus_restart_health_check`| Whether to wait for successful health check after restart | `true` | |`keycloak_quarkus_restart_health_check_delay`| Seconds to let pass before starting healch checks | `10` | -|`keycloak_quarkus_restart_health_check_reries`| Number of attempts for successful health check before failing | `25` | +|`keycloak_quarkus_restart_health_check_retries`| Number of attempts for successful health check before failing | `25` | |`keycloak_quarkus_restart_pause`| Seconds to wait between restarts in HA strategy | `15` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 05e0455..0512960 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -163,5 +163,5 @@ keycloak_quarkus_supported_policy_types: ['password-blacklists'] keycloak_quarkus_restart_strategy: restart/serial.yml keycloak_quarkus_restart_health_check: true keycloak_quarkus_restart_health_check_delay: 10 -keycloak_quarkus_restart_health_check_reries: 25 +keycloak_quarkus_restart_health_check_retries: 25 keycloak_quarkus_restart_pause: 15 diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 3eaf5d4..4a3b7c4 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -464,7 +464,7 @@ argument_specs: description: "Seconds to let pass before starting healch checks" default: 10 type: 'int' - keycloak_quarkus_restart_health_check_reries: + keycloak_quarkus_restart_health_check_retries: description: "Number of attempts for successful health check before failing" default: 25 type: 'int' diff --git a/roles/keycloak_quarkus/tasks/restart.yml b/roles/keycloak_quarkus/tasks/restart.yml index 61356d5..3aa97f6 100644 --- a/roles/keycloak_quarkus/tasks/restart.yml +++ b/roles/keycloak_quarkus/tasks/restart.yml @@ -12,7 +12,7 @@ url: "{{ keycloak.health_url }}" register: keycloak_status until: keycloak_status.status == 200 - retries: "{{ keycloak_quarkus_restart_health_check_reries }}" + retries: "{{ keycloak_quarkus_restart_health_check_retries }}" delay: "{{ keycloak_quarkus_restart_health_check_delay }}" when: internal_force_health_check | default(keycloak_quarkus_restart_health_check) diff --git a/roles/keycloak_quarkus/tasks/start.yml b/roles/keycloak_quarkus/tasks/start.yml index a640e89..5a3ad5f 100644 --- a/roles/keycloak_quarkus/tasks/start.yml +++ b/roles/keycloak_quarkus/tasks/start.yml @@ -14,3 +14,4 @@ until: keycloak_status.status == 200 retries: 25 delay: 10 + when: internal_force_health_check | default(keycloak_quarkus_restart_health_check) From f146eb5fda5f9444c1eb071701eed5a9f14d1ddc Mon Sep 17 00:00:00 2001 From: Ranabir Chakraborty Date: Fri, 11 Apr 2025 20:52:08 +0530 Subject: [PATCH 102/135] AMW-384 Keycloak realm variable keycloak_url with hard-coded http --- roles/keycloak/defaults/main.yml | 4 ++++ roles/keycloak/vars/main.yml | 3 --- roles/keycloak_realm/defaults/main.yml | 4 ++++ roles/keycloak_realm/vars/main.yml | 4 ---- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/roles/keycloak/defaults/main.yml b/roles/keycloak/defaults/main.yml index cfa9a3f..137111f 100644 --- a/roles/keycloak/defaults/main.yml +++ b/roles/keycloak/defaults/main.yml @@ -118,3 +118,7 @@ keycloak_no_log: true ### logging configuration keycloak_log_target: /var/log/keycloak + +# locations +keycloak_url: "http://{{ keycloak_host }}:{{ keycloak_http_port + keycloak_jboss_port_offset }}" +keycloak_management_url: "http://{{ keycloak_host }}:{{ keycloak_management_http_port + keycloak_jboss_port_offset }}" diff --git a/roles/keycloak/vars/main.yml b/roles/keycloak/vars/main.yml index 7f7dfd1..fe706db 100644 --- a/roles/keycloak/vars/main.yml +++ b/roles/keycloak/vars/main.yml @@ -1,9 +1,6 @@ --- # internal variables below -# locations -keycloak_url: "http://{{ keycloak_host }}:{{ keycloak_http_port + keycloak_jboss_port_offset }}" -keycloak_management_url: "http://{{ keycloak_host }}:{{ keycloak_management_http_port + keycloak_jboss_port_offset }}" keycloak: diff --git a/roles/keycloak_realm/defaults/main.yml b/roles/keycloak_realm/defaults/main.yml index b9e1e45..a294cbe 100644 --- a/roles/keycloak_realm/defaults/main.yml +++ b/roles/keycloak_realm/defaults/main.yml @@ -54,3 +54,7 @@ keycloak_client_users: [] ### List of Keycloak User Federation keycloak_user_federation: [] + +# other settings +keycloak_url: "http://{{ keycloak_host }}:{{ keycloak_http_port + (keycloak_jboss_port_offset | default(0)) }}" +keycloak_management_url: "http://{{ keycloak_host }}:{{ keycloak_management_http_port + (keycloak_jboss_port_offset | default(0)) }}" diff --git a/roles/keycloak_realm/vars/main.yml b/roles/keycloak_realm/vars/main.yml index 7664f8c..ad9bd8e 100644 --- a/roles/keycloak_realm/vars/main.yml +++ b/roles/keycloak_realm/vars/main.yml @@ -3,7 +3,3 @@ # name of the realm to create, this is a required variable keycloak_realm: - -# other settings -keycloak_url: "http://{{ keycloak_host }}:{{ keycloak_http_port + (keycloak_jboss_port_offset | default(0)) }}" -keycloak_management_url: "http://{{ keycloak_host }}:{{ keycloak_management_http_port + (keycloak_jboss_port_offset | default(0)) }}" From 671cf4eb537a49f1cf0f67e75517ba4808705e24 Mon Sep 17 00:00:00 2001 From: Preston Doster Date: Sun, 13 Apr 2025 10:17:22 -0500 Subject: [PATCH 103/135] Updating example playbooks to use `bootstrap` admin password It looks like the underlying `quarkus` provider has changed to use `keycloak_quarkus_bootstrap_admin_password`. --- playbooks/keycloak_quarkus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playbooks/keycloak_quarkus.yml b/playbooks/keycloak_quarkus.yml index 84d1774..b8aedf2 100644 --- a/playbooks/keycloak_quarkus.yml +++ b/playbooks/keycloak_quarkus.yml @@ -2,7 +2,7 @@ - name: Playbook for Keycloak X Hosts with HTTPS enabled hosts: all vars: - keycloak_quarkus_admin_pass: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_hostname: http://localhost keycloak_quarkus_port: 8443 keycloak_quarkus_log: file From 7738e0feb1e70b6a6a68a38871c396d9e64dc097 Mon Sep 17 00:00:00 2001 From: Preston Doster Date: Sun, 13 Apr 2025 10:18:27 -0500 Subject: [PATCH 104/135] Update keycloak_quarkus_dev.yml --- playbooks/keycloak_quarkus_dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playbooks/keycloak_quarkus_dev.yml b/playbooks/keycloak_quarkus_dev.yml index 0c74c36..c8bb54e 100644 --- a/playbooks/keycloak_quarkus_dev.yml +++ b/playbooks/keycloak_quarkus_dev.yml @@ -2,7 +2,7 @@ - name: Playbook for Keycloak X Hosts in develop mode hosts: all vars: - keycloak_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_hostname: http://localhost keycloak_quarkus_port: 8080 keycloak_quarkus_log: file From e9061b29efced4c721fee7926494f779808bcb5f Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 16 Apr 2025 10:31:48 +0200 Subject: [PATCH 105/135] Rename parameters from jdbc to db --- molecule/quarkus_ha/converge.yml | 2 +- roles/keycloak_quarkus/README.md | 8 ++++---- roles/keycloak_quarkus/defaults/main.yml | 6 +++--- roles/keycloak_quarkus/meta/argument_specs.yml | 10 +++++----- roles/keycloak_quarkus/tasks/install.yml | 4 ++-- roles/keycloak_quarkus/tasks/jdbc_driver.yml | 4 ++-- roles/keycloak_quarkus/templates/keycloak.conf.j2 | 4 ++-- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/molecule/quarkus_ha/converge.yml b/molecule/quarkus_ha/converge.yml index d37ad79..fa5314f 100644 --- a/molecule/quarkus_ha/converge.yml +++ b/molecule/quarkus_ha/converge.yml @@ -24,6 +24,6 @@ keycloak_quarkus_restart_strategy: restart/serial.yml keycloak_quarkus_db_user: keycloak keycloak_quarkus_db_pass: mysecretpass - keycloak_quarkus_jdbc_url: jdbc:postgresql://postgres:5432/keycloak + keycloak_quarkus_db_url: jdbc:postgresql://postgres:5432/keycloak roles: - role: keycloak_quarkus diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index fb30ce9..03a5a7a 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -130,14 +130,14 @@ Role Defaults | Variable | Description | Default | |:---------|:------------|:--------| -|`keycloak_quarkus_jdbc_engine` | Database engine [mariadb,postres,mssql] | `postgres` | +|`keycloak_quarkus_db_engine` | Database engine [mariadb,postres,mssql] | `postgres` | |`keycloak_quarkus_db_user` | User for database connection | `keycloak-user` | |`keycloak_quarkus_db_pass` | Password for database connection | `keycloak-pass` | -|`keycloak_quarkus_jdbc_url` | JDBC URL for connecting to database | `jdbc:postgresql://localhost:5432/keycloak` | -|`keycloak_quarkus_jdbc_driver_version` | Version for JDBC driver | `9.4.1212` | +|`keycloak_quarkus_db_url` | JDBC URL for connecting to database | `jdbc:postgresql://localhost:5432/keycloak` | +|`keycloak_quarkus_db_driver_version` | Version for JDBC engine driver | `9.4.1212` | -#### Remote caches configuration +#### Cache configuration | Variable | Description | Default | |:---------|:------------|:--------| diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 0512960..14f9fc9 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -120,12 +120,12 @@ keycloak_quarkus_ispn_trust_store_path: /etc/pki/java/cacerts keycloak_quarkus_ispn_trust_store_password: changeit ### database backend engine: values [ 'postgres', 'mariadb' ] -keycloak_quarkus_jdbc_engine: postgres +keycloak_quarkus_db_engine: postgres ### database backend credentials keycloak_quarkus_db_user: keycloak-user keycloak_quarkus_db_pass: keycloak-pass -keycloak_quarkus_jdbc_url: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].url }}" -keycloak_quarkus_jdbc_driver_version: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].version }}" +keycloak_quarkus_db_url: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].url }}" +keycloak_quarkus_db_driver_version: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].version }}" # override the variables above, following defaults show minimum supported versions keycloak_quarkus_default_jdbc: postgres: diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 4a3b7c4..c793e5f 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -287,7 +287,7 @@ argument_specs: default: "changeit" description: "Password for infinispan certificate keystore" type: "str" - keycloak_quarkus_jdbc_engine: + keycloak_quarkus_db_engine: default: "postgres" description: "Database engine [mariadb,postres,mssql]" type: "str" @@ -299,12 +299,12 @@ argument_specs: default: "keycloak-pass" description: "Password for database connection" type: "str" - keycloak_quarkus_jdbc_url: - default: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].url }}" + keycloak_quarkus_db_url: + default: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].url }}" description: "JDBC URL for connecting to database" type: "str" - keycloak_quarkus_jdbc_driver_version: - default: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].version }}" + keycloak_quarkus_db_driver_version: + default: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].version }}" description: "Version for JDBC driver" type: "str" keycloak_quarkus_log: diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index c602d8c..c664d05 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -202,11 +202,11 @@ - keycloak_quarkus_cert_file_copy_enabled is defined and keycloak_quarkus_cert_file_copy_enabled - keycloak_quarkus_cert_file_src | length > 0 -- name: "Install {{ keycloak_quarkus_jdbc_engine }} JDBC driver" +- name: "Install {{ keycloak_quarkus_db_engine }} JDBC driver" ansible.builtin.include_tasks: jdbc_driver.yml when: - rhbk_enable is defined and rhbk_enable - - keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url is defined + - keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].driver_jar_url is defined - name: "Download custom providers via http" ansible.builtin.get_url: diff --git a/roles/keycloak_quarkus/tasks/jdbc_driver.yml b/roles/keycloak_quarkus/tasks/jdbc_driver.yml index 880a915..ba3f4b8 100644 --- a/roles/keycloak_quarkus/tasks/jdbc_driver.yml +++ b/roles/keycloak_quarkus/tasks/jdbc_driver.yml @@ -7,9 +7,9 @@ (keycloak_quarkus_jdbc_download_user is undefined and keycloak_quarkus_jdbc_download_pass is not undefined) or (keycloak_quarkus_jdbc_download_pass is undefined and keycloak_quarkus_jdbc_download_user is not undefined) -- name: "Retrieve JDBC Driver from {{ keycloak_jdbc_download_url | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url) }}" +- name: "Retrieve JDBC Driver from {{ keycloak_jdbc_download_url | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].driver_jar_url) }}" ansible.builtin.get_url: - url: "{{ keycloak_quarkus_jdbc_download_url | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].driver_jar_url) }}" + url: "{{ keycloak_quarkus_jdbc_download_url | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].driver_jar_url) }}" dest: "{{ keycloak.home }}/providers" owner: "{{ keycloak.service_user }}" group: "{{ keycloak.service_group }}" diff --git a/roles/keycloak_quarkus/templates/keycloak.conf.j2 b/roles/keycloak_quarkus/templates/keycloak.conf.j2 index f11e3ec..6a4a6c1 100644 --- a/roles/keycloak_quarkus/templates/keycloak.conf.j2 +++ b/roles/keycloak_quarkus/templates/keycloak.conf.j2 @@ -2,8 +2,8 @@ {% if keycloak_quarkus_db_enabled %} # Database -db={{ keycloak_quarkus_jdbc_engine }} -db-url={{ keycloak_quarkus_jdbc_url }} +db={{ keycloak_quarkus_db_engine }} +db-url={{ keycloak_quarkus_db_url }} db-username={{ keycloak_quarkus_db_user }} {% if not keycloak.config_key_store_enabled %} db-password={{ keycloak_quarkus_db_pass }} From c7ce7be6c4fc0cc99d3acf5600cdfc8c28d1637f Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 16 Apr 2025 10:42:07 +0200 Subject: [PATCH 106/135] drop ajp port parameter --- roles/keycloak_quarkus/README.md | 1 - roles/keycloak_quarkus/defaults/main.yml | 1 - roles/keycloak_quarkus/meta/argument_specs.yml | 4 ---- 3 files changed, 6 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 03a5a7a..dc7ced7 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -53,7 +53,6 @@ Role Defaults |`keycloak_quarkus_http_port`| HTTP listening port | `8080` | |`keycloak_quarkus_https_port`| TLS HTTP listening port | `8443` | |`keycloak_quarkus_http_management_port`| Port of the management interface. Relevant only when something is exposed on the management interface - see the guide for details. | `9000` | -|`keycloak_quarkus_ajp_port`| AJP port | `8009` | |`keycloak_quarkus_service_user`| Posix account username | `keycloak` | |`keycloak_quarkus_service_group`| Posix account group | `keycloak` | |`keycloak_quarkus_service_restart_always`| systemd restart always behavior activation | `False` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 14f9fc9..996d950 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -37,7 +37,6 @@ keycloak_quarkus_http_enabled: true keycloak_quarkus_http_port: 8080 keycloak_quarkus_https_port: 8443 keycloak_quarkus_http_management_port: 9000 -keycloak_quarkus_ajp_port: 8009 keycloak_quarkus_jgroups_port: 7800 keycloak_quarkus_java_heap_opts: "-Xms1024m -Xmx2048m" keycloak_quarkus_java_jvm_opts: "-XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index c793e5f..d42bec3 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -191,10 +191,6 @@ argument_specs: default: 9000 description: "Port of the management interface. Relevant only when something is exposed on the management interface - see the guide for details." type: "int" - keycloak_quarkus_ajp_port: - default: 8009 - description: "AJP port" - type: "int" keycloak_quarkus_jgroups_port: default: 7800 description: "jgroups cluster tcp port" From 69a947c0b6961b35d541c141786c80d0d2ecfc9f Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 16 Apr 2025 11:34:12 +0200 Subject: [PATCH 107/135] rename _admin to _hostname_admin --- roles/keycloak_quarkus/README.md | 16 ++++++++++------ roles/keycloak_quarkus/defaults/main.yml | 2 +- roles/keycloak_quarkus/meta/argument_specs.yml | 4 ++-- roles/keycloak_quarkus/tasks/deprecations.yml | 6 +++--- .../keycloak_quarkus/templates/keycloak.conf.j2 | 2 +- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index dc7ced7..6427209 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -64,10 +64,8 @@ Role Defaults |`keycloak_quarkus_java_jvm_opts`| Other JVM settings | same as keycloak | |`keycloak_quarkus_java_opts`| JVM arguments; if overridden, it takes precedence over `keycloak_quarkus_java_*` | `{{ keycloak_quarkus_java_heap_opts + ' ' + keycloak_quarkus_java_jvm_opts }}` | |`keycloak_quarkus_additional_env_vars` | List of additional env variables of { key: str, value: str} to be put in sysconfig file | `[]` | -|`keycloak_quarkus_hostname`| Address at which is the server exposed. Can be a full URL, or just a hostname. When only hostname is provided, scheme, port and context path are resolved from the request. | | |`keycloak_quarkus_frontend_url`| Deprecated, use `keycloak_quarkus_hostname` instead. | | -|`keycloak_quarkus_admin`| Set the base URL for accessing the administration console, including scheme, host, port and path | | -|`keycloak_quarkus_admin_url`| Deprecated, use `keycloak_quarkus_admin` instead. | | +|`keycloak_quarkus_admin_url`| Deprecated, use `keycloak_quarkus_hostname_admin` instead. | | |`keycloak_quarkus_http_relative_path` | Set the path relative to / for serving resources. The path must start with a / | `/` | |`keycloak_quarkus_http_management_relative_path` | Set the path relative to / for serving resources from management interface. The path must start with a /. If not given, the value is inherited from HTTP options. Relevant only when something is exposed on the management interface - see the guide for details. | `/` | |`keycloak_quarkus_http_enabled`| Enable listener on HTTP port | `True` | @@ -119,12 +117,20 @@ Role Defaults | Variable | Description | Default | |:---------|:------------|:--------| -|`keycloak_quarkus_http_relative_path`| Set the path relative to / for serving resources. The path must start with a / | `/` | +|`keycloak_quarkus_hostname`| Address at which is the server exposed. Can be a full URL, or just a hostname. When only hostname is provided, scheme, port and context path are resolved from the request. | | +|`keycloak_quarkus_hostname_admin`| Set the base URL for accessing the administration console, including scheme, host, port and path | | |`keycloak_quarkus_hostname_strict`| Disables dynamically resolving the hostname from request headers | `true` | |`keycloak_quarkus_hostname_backchannel_dynamic`| Enables dynamic resolving of backchannel URLs, including hostname, scheme, port and context path. Set to true if your application accesses Keycloak via a private network. If set to true, hostname option needs to be specified as a full URL. | `false` | |`keycloak_quarkus_hostname_strict_backchannel`| Deprecated, use (the inverted!)`keycloak_quarkus_hostname_backchannel_dynamic` instead. | | +#### HTTP(S) configuration +| Variable | Description | Default | +|:---------|:------------|:--------| +|`keycloak_quarkus_http_relative_path`| Set the path relative to / for serving resources. The path must start with a / | `/` | + + + #### Database configuration | Variable | Description | Default | @@ -250,8 +256,6 @@ Role Variables |:---------|:------------|----------| |`keycloak_quarkus_bootstrap_admin_password`| Password of console admin account | `yes` | |`keycloak_quarkus_admin_pass`| Deprecated, use `keycloak_quarkus_bootstrap_admin_password` instead. | | -|`keycloak_quarkus_frontend_url`| Base URL for frontend URLs, including scheme, host, port and path | `no` | -|`keycloak_quarkus_admin_url`| Base URL for accessing the administration console, including scheme, host, port and path | `no` | |`keycloak_quarkus_ks_vault_pass`| The password for accessing the keystore vault SPI | `no` | |`keycloak_quarkus_alternate_download_url`| Alternate location with optional authentication for downloading RHBK | `no` | |`keycloak_quarkus_download_user`| Optional username for http authentication | `no*` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 996d950..ed3dfab 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -79,7 +79,7 @@ keycloak_quarkus_systemd_wait_for_delay: 10 ### keycloak frontend url keycloak_quarkus_hostname: -keycloak_quarkus_admin: +keycloak_quarkus_hostname_admin: ### Set the path relative to / for serving resources. The path must start with a / ### (set to `/auth` for retrocompatibility with pre-quarkus releases) diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index d42bec3..901e774 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -239,13 +239,13 @@ argument_specs: required: false description: "Deprecated in v26, use keycloak_quarkus_hostname instead." type: "str" - keycloak_quarkus_admin: + keycloak_quarkus_hostname_admin: required: false description: "Service URL for the admin console" type: "str" keycloak_quarkus_admin_url: required: false - description: "Deprecated in v26, use keycloak_quarkus_admin instead." + description: "Deprecated in v26, use keycloak_quarkus_hostname_admin instead." type: "str" keycloak_quarkus_metrics_enabled: default: false diff --git a/roles/keycloak_quarkus/tasks/deprecations.yml b/roles/keycloak_quarkus/tasks/deprecations.yml index fe269e9..3d1b9d7 100644 --- a/roles/keycloak_quarkus/tasks/deprecations.yml +++ b/roles/keycloak_quarkus/tasks/deprecations.yml @@ -86,16 +86,16 @@ - print deprecation warning # https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.0/html-single/upgrading_guide/index#new_hostname_options -- name: Check deprecation of keycloak_quarkus_admin_url -> keycloak_quarkus_admin +- name: Check deprecation of keycloak_quarkus_admin_url -> keycloak_quarkus_hostname_admin when: - - keycloak_quarkus_admin is not defined + - keycloak_quarkus_hostname_admin is not defined - keycloak_quarkus_admin_url is defined - keycloak_quarkus_admin_url != '' delegate_to: localhost run_once: true changed_when: keycloak_quarkus_show_deprecation_warnings ansible.builtin.set_fact: - keycloak_quarkus_admin: "{{ keycloak_quarkus_admin_url }}" + keycloak_quarkus_hostname_admin: "{{ keycloak_quarkus_admin_url }}" deprecated_variable: "keycloak_quarkus_admin_url" # read in deprecation handler notify: - print deprecation warning diff --git a/roles/keycloak_quarkus/templates/keycloak.conf.j2 b/roles/keycloak_quarkus/templates/keycloak.conf.j2 index 6a4a6c1..f15bbd8 100644 --- a/roles/keycloak_quarkus/templates/keycloak.conf.j2 +++ b/roles/keycloak_quarkus/templates/keycloak.conf.j2 @@ -50,7 +50,7 @@ https-trust-store-password={{ keycloak_quarkus_https_trust_store_password }} # Client URL configuration hostname={{ keycloak_quarkus_hostname }} -hostname-admin={{ keycloak_quarkus_admin }} +hostname-admin={{ keycloak_quarkus_hostname_admin }} hostname-strict={{ keycloak_quarkus_hostname_strict | lower }} hostname-backchannel-dynamic={{ keycloak_quarkus_hostname_backchannel_dynamic | lower }} From 70d61ce8dee578858f8803251b58ca8cafab520d Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 16 Apr 2025 11:58:04 +0200 Subject: [PATCH 108/135] rename ispn parameters --- roles/keycloak_quarkus/README.md | 79 ++++++++++--------- .../keycloak_quarkus/meta/argument_specs.yml | 18 ++--- 2 files changed, 46 insertions(+), 51 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 6427209..dac1dd2 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -50,9 +50,6 @@ Role Defaults |`keycloak_quarkus_host`| Deprecated, use `keycloak_quarkus_hostname` instead. | | |`keycloak_quarkus_port`| Deprecated, use `keycloak_quarkus_hostname` instead. | | |`keycloak_quarkus_path`| Deprecated, use `keycloak_quarkus_hostname` instead. | | -|`keycloak_quarkus_http_port`| HTTP listening port | `8080` | -|`keycloak_quarkus_https_port`| TLS HTTP listening port | `8443` | -|`keycloak_quarkus_http_management_port`| Port of the management interface. Relevant only when something is exposed on the management interface - see the guide for details. | `9000` | |`keycloak_quarkus_service_user`| Posix account username | `keycloak` | |`keycloak_quarkus_service_group`| Posix account group | `keycloak` | |`keycloak_quarkus_service_restart_always`| systemd restart always behavior activation | `False` | @@ -66,26 +63,8 @@ Role Defaults |`keycloak_quarkus_additional_env_vars` | List of additional env variables of { key: str, value: str} to be put in sysconfig file | `[]` | |`keycloak_quarkus_frontend_url`| Deprecated, use `keycloak_quarkus_hostname` instead. | | |`keycloak_quarkus_admin_url`| Deprecated, use `keycloak_quarkus_hostname_admin` instead. | | -|`keycloak_quarkus_http_relative_path` | Set the path relative to / for serving resources. The path must start with a / | `/` | -|`keycloak_quarkus_http_management_relative_path` | Set the path relative to / for serving resources from management interface. The path must start with a /. If not given, the value is inherited from HTTP options. Relevant only when something is exposed on the management interface - see the guide for details. | `/` | -|`keycloak_quarkus_http_enabled`| Enable listener on HTTP port | `True` | |`keycloak_quarkus_health_check_url`| Full URL (including scheme, host, path, fragment etc.) used for health check endpoint; keycloak_quarkus_hostname will NOT be prepended; helpful when health checks should happen against http port, but keycloak_quarkus_hostname uses https scheme per default | `` | |`keycloak_quarkus_health_check_url_path`| Path to the health check endpoint; keycloak_quarkus_hostname will be prepended automatically; Note that keycloak_quarkus_health_check_url takes precedence over this property | `realms/master/.well-known/openid-configuration` | -|`keycloak_quarkus_https_key_file_enabled`| Enable listener on HTTPS port | `False` | -|`keycloak_quarkus_key_file_copy_enabled`| Enable copy of key file to target host | `False` | -|`keycloak_quarkus_key_content`| Content of the TLS private key. Use `"{{ lookup('file', 'server.key.pem') }}"` to lookup a file. | `""` | -|`keycloak_quarkus_key_file`| The file path to a private key in PEM format | `/etc/pki/tls/private/server.key.pem` | -|`keycloak_quarkus_cert_file_copy_enabled`| Enable copy of cert file to target host | `False`| -|`keycloak_quarkus_cert_file_src`| Set the source file path | `""` | -|`keycloak_quarkus_cert_file`| The file path to a server certificate or certificate chain in PEM format | `/etc/pki/tls/certs/server.crt.pem` | -|`keycloak_quarkus_https_key_store_enabled`| Enable configuration of HTTPS via a key store | `False` | -|`keycloak_quarkus_key_store_file`| Deprecated, use `keycloak_quarkus_https_key_store_file` instead. || -|`keycloak_quarkus_key_store_password`| Deprecated, use `keycloak_quarkus_https_key_store_password` instead.|| -|`keycloak_quarkus_https_key_store_file`| The file path to the key store | `{{ keycloak.home }}/conf/key_store.p12` | -|`keycloak_quarkus_https_key_store_password`| Password for the key store | `""` | -|`keycloak_quarkus_https_trust_store_enabled`| Enable configuration of the https trust store | `False` | -|`keycloak_quarkus_https_trust_store_file`| The file path to the trust store | `{{ keycloak.home }}/conf/trust_store.p12` | -|`keycloak_quarkus_https_trust_store_password`| Password for the trust store | `""` | |`keycloak_quarkus_proxy_headers`| Parse reverse proxy headers (`forwarded` or `xforwarded`) | `""` | |`keycloak_quarkus_config_key_store_file`| Path to the configuration key store; only used if `keycloak_quarkus_keystore_password` is not empty | `{{ keycloak.home }}/conf/conf_store.p12` if `keycloak_quarkus_keystore_password != ''`, else `''` | |`keycloak_quarkus_config_key_store_password`| Password of the configuration keystore; if non-empty, `keycloak_quarkus_db_pass` will be saved to the keystore at `keycloak_quarkus_config_key_store_file` instead of being written to the configuration file in clear text | `""` | @@ -128,7 +107,27 @@ Role Defaults | Variable | Description | Default | |:---------|:------------|:--------| |`keycloak_quarkus_http_relative_path`| Set the path relative to / for serving resources. The path must start with a / | `/` | - +|`keycloak_quarkus_http_port`| HTTP listening port | `8080` | +|`keycloak_quarkus_https_port`| TLS HTTP listening port | `8443` | +|`keycloak_quarkus_http_management_port`| Port of the management interface. Relevant only when something is exposed on the management interface - see the guide for details. | `9000` | +|`keycloak_quarkus_https_key_store_file`| The file path to the key store | `{{ keycloak.home }}/conf/key_store.p12` | +|`keycloak_quarkus_https_key_store_password`| Password for the key store | `""` | +|`keycloak_quarkus_https_trust_store_enabled`| Enable configuration of the https trust store | `False` | +|`keycloak_quarkus_https_trust_store_file`| The file path to the trust store | `{{ keycloak.home }}/conf/trust_store.p12` | +|`keycloak_quarkus_https_trust_store_password`| Password for the trust store | `""` | +|`keycloak_quarkus_https_key_file_enabled`| Enable listener on HTTPS port | `False` | +|`keycloak_quarkus_key_file_copy_enabled`| Enable copy of key file to target host | `False` | +|`keycloak_quarkus_key_content`| Content of the TLS private key. Use `"{{ lookup('file', 'server.key.pem') }}"` to lookup a file. | `""` | +|`keycloak_quarkus_key_file`| The file path to a private key in PEM format | `/etc/pki/tls/private/server.key.pem` | +|`keycloak_quarkus_cert_file_copy_enabled`| Enable copy of cert file to target host | `False`| +|`keycloak_quarkus_cert_file_src`| Set the source file path | `""` | +|`keycloak_quarkus_cert_file`| The file path to a server certificate or certificate chain in PEM format | `/etc/pki/tls/certs/server.crt.pem` | +|`keycloak_quarkus_https_key_store_enabled`| Enable configuration of HTTPS via a key store | `False` | +|`keycloak_quarkus_key_store_file`| Deprecated, use `keycloak_quarkus_https_key_store_file` instead. || +|`keycloak_quarkus_key_store_password`| Deprecated, use `keycloak_quarkus_https_key_store_password` instead.|| +|`keycloak_quarkus_http_relative_path` | Set the path relative to / for serving resources. The path must start with a / | `/` | +|`keycloak_quarkus_http_management_relative_path` | Set the path relative to / for serving resources from management interface. The path must start with a /. If not given, the value is inherited from HTTP options. Relevant only when something is exposed on the management interface - see the guide for details. | `/` | +|`keycloak_quarkus_http_enabled`| Enable listener on HTTP port | `True` | #### Database configuration @@ -146,13 +145,25 @@ Role Defaults | Variable | Description | Default | |:---------|:------------|:--------| -|`keycloak_quarkus_ispn_user` | Username for connecting to infinispan | `supervisor` | -|`keycloak_quarkus_ispn_pass` | Password for connecting to infinispan | `supervisor` | -|`keycloak_quarkus_ispn_hosts` | host name/port for connecting to infinispan, eg. host1:11222;host2:11222 | `localhost:11222` | -|`keycloak_quarkus_ispn_sasl_mechanism` | Infinispan auth mechanism | `SCRAM-SHA-512` | -|`keycloak_quarkus_ispn_use_ssl` | Whether infinispan uses TLS connection | `false` | -|`keycloak_quarkus_ispn_trust_store_path` | Path to infinispan server trust certificate | `/etc/pki/java/cacerts` | -|`keycloak_quarkus_ispn_trust_store_password` | Password for infinispan certificate keystore | `changeit` | +|`keycloak_quarkus_cache_remote_username` | Username for connecting to infinispan | `supervisor` | +|`keycloak_quarkus_cache_remote_password` | Password for connecting to infinispan | `supervisor` | +|`keycloak_quarkus_cache_remote_host` | host name/port for connecting to infinispan, eg. host1:11222;host2:11222 | `localhost:11222` | +|`keycloak_quarkus_cache_remote_sasl_mechanism` | Infinispan auth mechanism | `SCRAM-SHA-512` | +|`keycloak_quarkus_cache_remote_tls_enabled` | Whether infinispan uses TLS connection | `false` | + + +#### Logging configuration + +| Variable | Description | Default | +|:---------|:------------|:--------| +|`keycloak_quarkus_log`| Enable one or more log handlers in a comma-separated list | `file` | +|`keycloak_quarkus_log_level`| The log level of the root category or a comma-separated list of individual categories and their levels | `info` | +|`keycloak_quarkus_log_file`| Set the log file path and filename relative to keycloak home | `data/log/keycloak.log` | +|`keycloak_quarkus_log_format`| Set a format specific to file log entries | `%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n` | +|`keycloak_quarkus_log_target`| Set the destination of the keycloak log folder link | `/var/log/keycloak` | +|`keycloak_quarkus_log_max_file_size`| Set the maximum log file size before a log rotation happens; A size configuration option recognises string in this format (shown as a regular expression): `[0-9]+[KkMmGgTtPpEeZzYy]?`. If no suffix is given, assume bytes. | `10M` | +|`keycloak_quarkus_log_max_backup_index`| Set the maximum number of archived log files to keep" | `10` | +|`keycloak_quarkus_log_file_suffix`| Set the log file handler rotation file suffix. When used, the file will be rotated based on its suffix; Note: If the suffix ends with `.zip` or `.gz`, the rotation file will also be compressed. | `.yyyy-MM-dd.zip` | #### Miscellaneous configuration @@ -168,14 +179,6 @@ Role Defaults |`keycloak_quarkus_master_realm` | Name for rest authentication realm | `master` | |`keycloak_auth_client` | Authentication client for configuration REST calls | `admin-cli` | |`keycloak_force_install` | Remove pre-existing versions of service | `False` | -|`keycloak_quarkus_log`| Enable one or more log handlers in a comma-separated list | `file` | -|`keycloak_quarkus_log_level`| The log level of the root category or a comma-separated list of individual categories and their levels | `info` | -|`keycloak_quarkus_log_file`| Set the log file path and filename relative to keycloak home | `data/log/keycloak.log` | -|`keycloak_quarkus_log_format`| Set a format specific to file log entries | `%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n` | -|`keycloak_quarkus_log_target`| Set the destination of the keycloak log folder link | `/var/log/keycloak` | -|`keycloak_quarkus_log_max_file_size`| Set the maximum log file size before a log rotation happens; A size configuration option recognises string in this format (shown as a regular expression): `[0-9]+[KkMmGgTtPpEeZzYy]?`. If no suffix is given, assume bytes. | `10M` | -|`keycloak_quarkus_log_max_backup_index`| Set the maximum number of archived log files to keep" | `10` | -|`keycloak_quarkus_log_file_suffix`| Set the log file handler rotation file suffix. When used, the file will be rotated based on its suffix; Note: If the suffix ends with `.zip` or `.gz`, the rotation file will also be compressed. | `.yyyy-MM-dd.zip` | |`keycloak_quarkus_proxy_mode`| The proxy address forwarding mode if the server is behind a reverse proxy | `edge` | |`keycloak_quarkus_start_dev`| Whether to start the service in development mode (start-dev) | `False` | |`keycloak_quarkus_transaction_xa_enabled`| Whether to use XA transactions | `True` | @@ -183,7 +186,7 @@ Role Defaults |`keycloak_quarkus_show_deprecation_warnings`| Whether deprecation warnings should be shown | `True` | -#### Vault SPI +#### Vault configuration | Variable | Description | Default | |:---------|:------------|:--------| diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 901e774..1c995db 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -255,34 +255,26 @@ argument_specs: default: true description: "If the server should expose health check endpoints on the management interface" type: "bool" - keycloak_quarkus_ispn_user: + keycloak_quarkus_cache_remote_username: default: "supervisor" description: "Username for connecting to infinispan" type: "str" - keycloak_quarkus_ispn_pass: + keycloak_quarkus_cache_remote_password: default: "supervisor" description: "Password for connecting to infinispan" type: "str" - keycloak_quarkus_ispn_hosts: + keycloak_quarkus_cache_remote_host: default: "localhost:11222" description: "host name/port for connecting to infinispan, eg. host1:11222;host2:11222" type: "str" - keycloak_quarkus_ispn_sasl_mechanism: + keycloak_quarkus_cache_remote_sasl_mechanism: default: "SCRAM-SHA-512" description: "Infinispan auth mechanism" type: "str" - keycloak_quarkus_ispn_use_ssl: + keycloak_quarkus_cache_remote_tls_enabled: default: false description: "Whether infinispan uses TLS connection" type: "bool" - keycloak_quarkus_ispn_trust_store_path: - default: "/etc/pki/java/cacerts" - description: "Path to infinispan server trust certificate" - type: "str" - keycloak_quarkus_ispn_trust_store_password: - default: "changeit" - description: "Password for infinispan certificate keystore" - type: "str" keycloak_quarkus_db_engine: default: "postgres" description: "Database engine [mariadb,postres,mssql]" From b8028d376aa5e5063ec6f49a9bb1e9adb28925b9 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Wed, 16 Apr 2025 14:16:07 +0200 Subject: [PATCH 109/135] Rename and honor parameter keycloak_quarkus_http_host --- roles/keycloak_quarkus/README.md | 3 ++- roles/keycloak_quarkus/defaults/main.yml | 3 ++- roles/keycloak_quarkus/meta/argument_specs.yml | 6 +++++- roles/keycloak_quarkus/tasks/deprecations.yml | 13 +++++++++++++ roles/keycloak_quarkus/templates/cache-ispn.xml.j2 | 2 +- roles/keycloak_quarkus/templates/keycloak.conf.j2 | 1 + 6 files changed, 24 insertions(+), 4 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index dac1dd2..7f93a9f 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -46,7 +46,7 @@ Role Defaults |:---------|:------------|:--------| |`keycloak_quarkus_bootstrap_admin_user`| Administration console user account | `admin` | |`keycloak_quarkus_admin_user`| Deprecated, use `keycloak_quarkus_bootstrap_admin_user` instead. | | -|`keycloak_quarkus_bind_address`| Address for binding service ports | `0.0.0.0` | +|`keycloak_quarkus_bind_address`| Deprecated, use `keycloak_quarkus_http_host` instead | `0.0.0.0` | |`keycloak_quarkus_host`| Deprecated, use `keycloak_quarkus_hostname` instead. | | |`keycloak_quarkus_port`| Deprecated, use `keycloak_quarkus_hostname` instead. | | |`keycloak_quarkus_path`| Deprecated, use `keycloak_quarkus_hostname` instead. | | @@ -107,6 +107,7 @@ Role Defaults | Variable | Description | Default | |:---------|:------------|:--------| |`keycloak_quarkus_http_relative_path`| Set the path relative to / for serving resources. The path must start with a / | `/` | +|`keycloak_quarkus_http_host`| The http host, ie. the address used to bind the service | `0.0.0.0` | |`keycloak_quarkus_http_port`| HTTP listening port | `8080` | |`keycloak_quarkus_https_port`| TLS HTTP listening port | `8443` | |`keycloak_quarkus_http_management_port`| Port of the management interface. Relevant only when something is exposed on the management interface - see the guide for details. | `9000` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index ed3dfab..7fefc5a 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -32,7 +32,8 @@ keycloak_quarkus_bootstrap_admin_password: keycloak_quarkus_master_realm: master ### Configuration settings -keycloak_quarkus_bind_address: 0.0.0.0 +keycloak_quarkus_bind_address: 0.0.0.0 # deprecated use keycloak_quarkus_http_host +keycloak_quarkus_http_host: 0.0.0.0 keycloak_quarkus_http_enabled: true keycloak_quarkus_http_port: 8080 keycloak_quarkus_https_port: 8443 diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 1c995db..51b0d1f 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -82,7 +82,7 @@ argument_specs: type: "str" keycloak_quarkus_bind_address: default: "0.0.0.0" - description: "Address for binding service ports" + description: "Deprecated, use `keycloak_quarkus_http_host`" type: "str" keycloak_quarkus_hostname: description: >- @@ -102,6 +102,10 @@ argument_specs: default: true description: "Enable listener on HTTP port" type: "bool" + keycloak_quarkus_http_host: + default: '0.0.0.0' + description: "HTTP host, address for binding service ports" + type: "str" keycloak_quarkus_http_port: default: 8080 description: "HTTP port" diff --git a/roles/keycloak_quarkus/tasks/deprecations.yml b/roles/keycloak_quarkus/tasks/deprecations.yml index 3d1b9d7..0d370d5 100644 --- a/roles/keycloak_quarkus/tasks/deprecations.yml +++ b/roles/keycloak_quarkus/tasks/deprecations.yml @@ -145,5 +145,18 @@ notify: - print deprecation warning +- name: Check deprecation of keycloak_quarkus_bind_address -> keycloak_quarkus_http_host + when: + - keycloak_quarkus_bind_address is defined + - keycloak_quarkus_bind_address != '0.0.0.0' + delegate_to: localhost + run_once: true + changed_when: keycloak_quarkus_show_deprecation_warnings + ansible.builtin.set_fact: + keycloak_quarkus_http_host: "{{ keycloak_quarkus_bind_address }}" + deprecated_variable: "keycloak_quarkus_bind_address" # read in deprecation handler + notify: + - print deprecation warning + - name: Flush handlers ansible.builtin.meta: flush_handlers diff --git a/roles/keycloak_quarkus/templates/cache-ispn.xml.j2 b/roles/keycloak_quarkus/templates/cache-ispn.xml.j2 index 8ead247..e546ab8 100644 --- a/roles/keycloak_quarkus/templates/cache-ispn.xml.j2 +++ b/roles/keycloak_quarkus/templates/cache-ispn.xml.j2 @@ -26,7 +26,7 @@ {% set stack_expression='stack="tcpping"' %} - + Date: Fri, 18 Apr 2025 17:59:16 +0200 Subject: [PATCH 110/135] Load envvars in kc rebuild --- molecule/quarkus/converge.yml | 3 +++ roles/keycloak_quarkus/tasks/rebuild_config.yml | 5 +---- roles/keycloak_quarkus/tasks/systemd.yml | 1 + roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index b7c15e1..cb3b9f8 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -23,6 +23,9 @@ keycloak_quarkus_systemd_wait_for_delay: 2 keycloak_quarkus_systemd_wait_for_log: true keycloak_quarkus_restart_health_check: false # would fail because of self-signed cert + keycloak_quarkus_additional_env_vars: + - key: KC_FEATURES_DISABLED + value: impersonation,kerberos keycloak_quarkus_providers: - id: http-client spi: connections diff --git a/roles/keycloak_quarkus/tasks/rebuild_config.yml b/roles/keycloak_quarkus/tasks/rebuild_config.yml index ac78504..1d43127 100644 --- a/roles/keycloak_quarkus/tasks/rebuild_config.yml +++ b/roles/keycloak_quarkus/tasks/rebuild_config.yml @@ -2,9 +2,6 @@ # cf. https://www.keycloak.org/server/configuration#_optimize_the_keycloak_startup - name: "Rebuild {{ keycloak.service_name }} config" ansible.builtin.shell: | # noqa blocked_modules shell is necessary here - {{ keycloak.home }}/bin/kc.sh build - environment: - PATH: "{{ keycloak_quarkus_java_home | default(keycloak_quarkus_pkg_java_home, true) }}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" - JAVA_HOME: "{{ keycloak_quarkus_java_home | default(keycloak_quarkus_pkg_java_home, true) }}" + env -i bash -c "set -a ; source {{ keycloak_quarkus_sysconf_file }} ; {{ keycloak.home }}/bin/kc.sh build " become: true changed_when: true diff --git a/roles/keycloak_quarkus/tasks/systemd.yml b/roles/keycloak_quarkus/tasks/systemd.yml index 47f0570..e22b9fe 100644 --- a/roles/keycloak_quarkus/tasks/systemd.yml +++ b/roles/keycloak_quarkus/tasks/systemd.yml @@ -22,4 +22,5 @@ become: true register: systemdunit notify: + - rebuild keycloak config - restart keycloak diff --git a/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 b/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 index 244d9aa..c1b1c7b 100644 --- a/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 +++ b/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 @@ -7,7 +7,7 @@ KC_BOOTSTRAP_ADMIN_PASSWORD='{{ keycloak_quarkus_bootstrap_admin_password }}' {% endif %} PATH={{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin JAVA_HOME={{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }} -JAVA_OPTS={{ keycloak_quarkus_java_opts }} +JAVA_OPTS='{{ keycloak_quarkus_java_opts }}' # Custom ENV variables {% for env in keycloak_quarkus_additional_env_vars %} From f750e93d02c3dfc18ba373bd783bbacc5b678b23 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 22 Apr 2025 14:51:15 +0200 Subject: [PATCH 111/135] Add bash to preinstalled packages --- roles/keycloak_quarkus/vars/debian.yml | 1 + roles/keycloak_quarkus/vars/redhat.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/roles/keycloak_quarkus/vars/debian.yml b/roles/keycloak_quarkus/vars/debian.yml index 29f190a..0af3443 100644 --- a/roles/keycloak_quarkus/vars/debian.yml +++ b/roles/keycloak_quarkus/vars/debian.yml @@ -2,6 +2,7 @@ keycloak_quarkus_varjvm_package: "{{ keycloak_quarkus_jvm_package | default('openjdk-17-jdk-headless') }}" keycloak_quarkus_prereq_package_list: - "{{ keycloak_quarkus_varjvm_package }}" + - bash - unzip - procps - apt diff --git a/roles/keycloak_quarkus/vars/redhat.yml b/roles/keycloak_quarkus/vars/redhat.yml index 9af167f..458c841 100644 --- a/roles/keycloak_quarkus/vars/redhat.yml +++ b/roles/keycloak_quarkus/vars/redhat.yml @@ -2,6 +2,7 @@ keycloak_quarkus_varjvm_package: "{{ keycloak_quarkus_jvm_package | default('java-21-openjdk-headless') }}" keycloak_quarkus_prereq_package_list: - "{{ keycloak_quarkus_varjvm_package }}" + - bash - unzip - procps-ng - initscripts From c86dff66ba9617ac2c528e1eca4442d3878c677c Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 22 Apr 2025 20:18:48 +0200 Subject: [PATCH 112/135] double quote sysconfig envvars --- roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 b/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 index c1b1c7b..9efd068 100644 --- a/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 +++ b/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 @@ -5,9 +5,9 @@ KC_BOOTSTRAP_ADMIN_PASSWORD='{{ keycloak_quarkus_bootstrap_admin_password }}' {% else %} {{ keycloak.bootstrap_mnemonic }} {% endif %} -PATH={{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -JAVA_HOME={{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }} -JAVA_OPTS='{{ keycloak_quarkus_java_opts }}' +PATH="{{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +JAVA_HOME="{{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }}" +JAVA_OPTS="{{ keycloak_quarkus_java_opts }}" # Custom ENV variables {% for env in keycloak_quarkus_additional_env_vars %} From 2abc580041af016b0bf17a0d9c0046380b53d092 Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Wed, 23 Apr 2025 11:47:38 +0000 Subject: [PATCH 113/135] Update changelog for release 3.0.0 Signed-off-by: ansible-middleware-core --- CHANGELOG.rst | 29 ++++++++++++++++++++++++ changelogs/changelog.yaml | 47 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 37a3923..d6b2d25 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,35 @@ middleware\_automation.keycloak Release Notes This changelog describes changes after version 0.2.6. +v3.0.0 +====== + +Minor Changes +------------- + +- Add theme cache invalidation handler `#252 `_ +- keycloak_realm: change url variables to defaults `#268 `_ + +Breaking Changes / Porting Guide +-------------------------------- + +- Bump major and ansible-core versions `#266 `_ +- Rename parameters to follow upstream `#270 `_ +- Update for keycloak v26 `#254 `_ + +Bugfixes +-------- + +- Access token lifespan is too short for ansible run `#251 `_ +- Load environment vars during kc rebuild `#274 `_ +- Rebuild config and restart service for local providers `#250 `_ +- Rename and honour parameter ``keycloak_quarkus_http_host`` `#271 `_ + +New Modules +----------- + +- middleware_automation.keycloak.keycloak_realm - Allows administration of Keycloak realm via Keycloak API + v2.4.3 ====== diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 40a7db3..3d25cd2 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -613,3 +613,50 @@ releases: fragments: - 241.yaml release_date: '2024-10-16' + 3.0.0: + changes: + breaking_changes: + - 'Bump major and ansible-core versions `#266 `_ + + ' + - 'Rename parameters to follow upstream `#270 `_ + + ' + - 'Update for keycloak v26 `#254 `_ + + ' + bugfixes: + - 'Access token lifespan is too short for ansible run `#251 `_ + + ' + - 'Load environment vars during kc rebuild `#274 `_ + + ' + - 'Rebuild config and restart service for local providers `#250 `_ + + ' + - 'Rename and honour parameter ``keycloak_quarkus_http_host`` `#271 `_ + + ' + minor_changes: + - 'Add theme cache invalidation handler `#252 `_ + + ' + - 'keycloak_realm: change url variables to defaults `#268 `_ + + ' + fragments: + - 250.yaml + - 251.yaml + - 252.yaml + - 254.yaml + - 266.yaml + - 268.yaml + - 270.yaml + - 271.yaml + - 274.yaml + modules: + - description: Allows administration of Keycloak realm via Keycloak API + name: keycloak_realm + namespace: '' + release_date: '2025-04-23' From d97044523d3a4133c0a70523aeb5ca90ebc16940 Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Wed, 23 Apr 2025 11:47:54 +0000 Subject: [PATCH 114/135] Bump version to 3.0.1 --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index 76c46c4..0b53eba 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "3.0.0" +version: "3.0.1" readme: README.md authors: - Romain Pelisse From 5beb5dcda4b8440503c2e0b55ea21e536bd07c78 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Fri, 2 May 2025 10:50:01 +0200 Subject: [PATCH 115/135] Add trigger on envvars file change --- roles/keycloak_quarkus/tasks/systemd.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/keycloak_quarkus/tasks/systemd.yml b/roles/keycloak_quarkus/tasks/systemd.yml index e22b9fe..fda37f5 100644 --- a/roles/keycloak_quarkus/tasks/systemd.yml +++ b/roles/keycloak_quarkus/tasks/systemd.yml @@ -10,6 +10,7 @@ vars: keycloak_sys_pkg_java_home: "{{ keycloak_quarkus_pkg_java_home }}" notify: + - rebuild keycloak config - restart keycloak - name: "Configure systemd unit file for keycloak service" From b8a2ebc699fc89577c446848cd34ad4ffc1d1b94 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Fri, 2 May 2025 10:55:17 +0200 Subject: [PATCH 116/135] update keycloak version --- roles/keycloak_quarkus/defaults/main.yml | 2 +- roles/keycloak_quarkus/meta/argument_specs.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 7fefc5a..1e83749 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -1,6 +1,6 @@ --- ### Configuration specific to keycloak -keycloak_quarkus_version: 26.0.7 +keycloak_quarkus_version: 26.0.8 keycloak_quarkus_archive: "keycloak-{{ keycloak_quarkus_version }}.zip" keycloak_quarkus_download_url: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}" keycloak_quarkus_installdir: "{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_quarkus_version }}" diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 51b0d1f..6b0b8c2 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -2,7 +2,7 @@ argument_specs: main: options: keycloak_quarkus_version: - default: "26.0.7" + default: "26.0.8" description: "keycloak.org package version" type: "str" keycloak_quarkus_archive: @@ -471,7 +471,7 @@ argument_specs: downstream: options: rhbk_version: - default: "26.0.7" + default: "26.0.11" description: "Red Hat Build of Keycloak version" type: "str" rhbk_archive: From 9974ab2ee1452d1af7c67966406c3588bf09306b Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Fri, 2 May 2025 11:18:57 +0200 Subject: [PATCH 117/135] update molecule scenario --- molecule/quarkus_upgrade/converge.yml | 3 +++ molecule/quarkus_upgrade/prepare.yml | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/molecule/quarkus_upgrade/converge.yml b/molecule/quarkus_upgrade/converge.yml index 9b57436..0f67169 100644 --- a/molecule/quarkus_upgrade/converge.yml +++ b/molecule/quarkus_upgrade/converge.yml @@ -5,6 +5,9 @@ - vars.yml vars: keycloak_quarkus_show_deprecation_warnings: false + keycloak_quarkus_additional_env_vars: + - key: KC_FEATURES_DISABLED + value: ciba,device-flow,impersonation,kerberos,docker keycloak_quarkus_version: 26.0.7 roles: - role: keycloak_quarkus diff --git a/molecule/quarkus_upgrade/prepare.yml b/molecule/quarkus_upgrade/prepare.yml index a6892e3..1be16d6 100644 --- a/molecule/quarkus_upgrade/prepare.yml +++ b/molecule/quarkus_upgrade/prepare.yml @@ -5,7 +5,10 @@ - vars.yml vars: sudo_pkg_name: sudo - keycloak_quarkus_version: 24.0.5 + keycloak_quarkus_version: 26.0.4 + keycloak_quarkus_additional_env_vars: + - key: KC_FEATURES_DISABLED + value: impersonation,kerberos pre_tasks: - name: Install sudo ansible.builtin.apt: @@ -44,6 +47,7 @@ changed_when: false roles: - role: keycloak_quarkus + post_tasks: - name: "Delete custom fact" ansible.builtin.file: From c45f7c0d60b3cd8a02875174d4e60572017aaed8 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Fri, 2 May 2025 11:26:27 +0200 Subject: [PATCH 118/135] Update remote cache default --- molecule/default/prepare.yml | 6 +----- roles/keycloak_quarkus/defaults/main.yml | 14 ++++++------- .../templates/quarkus.properties.j2 | 20 +++++++++---------- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml index eefd5cf..77a7723 100644 --- a/molecule/default/prepare.yml +++ b/molecule/default/prepare.yml @@ -7,10 +7,6 @@ tasks: - name: "Run preparation common to all scenario" ansible.builtin.include_tasks: ../prepare.yml - vars: - assets: - - "{{ assets_server }}/sso/7.6.0/rh-sso-7.6.0-server-dist.zip" - - "{{ assets_server }}/sso/7.6.1/rh-sso-7.6.1-patch.zip" - name: Create controller directory for downloads ansible.builtin.file: # noqa risky-file-permissions delegated, uses controller host user @@ -22,7 +18,7 @@ - name: Download keycloak archive to controller directory ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user - url: https://github.com/keycloak/keycloak/releases/download/26.0.7/keycloak-26.0.7.zip + url: https://github.com/keycloak/keycloak/releases/download/26.0.8/keycloak-26.0.8.zip dest: /tmp/keycloak mode: '0640' delegate_to: localhost diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 1e83749..66fc8e1 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -110,14 +110,12 @@ keycloak_quarkus_metrics_enabled: false keycloak_quarkus_health_enabled: true ### infinispan remote caches access (hotrod) -keycloak_quarkus_ispn_user: supervisor -keycloak_quarkus_ispn_pass: supervisor -keycloak_quarkus_ispn_hosts: "localhost:11222" -keycloak_quarkus_ispn_sasl_mechanism: SCRAM-SHA-512 -keycloak_quarkus_ispn_use_ssl: false -# if ssl is enabled, import ispn server certificate here -keycloak_quarkus_ispn_trust_store_path: /etc/pki/java/cacerts -keycloak_quarkus_ispn_trust_store_password: changeit +keycloak_quarkus_cache_remote_username: supervisor +keycloak_quarkus_cache_remote_password: supervisor +keycloak_quarkus_cache_remote_host: "localhost:11222" +keycloak_quarkus_cache_remote_tls_enabled: false +keycloak_quarkus_cache_remote_sasl_mechanism: SCRAM-SHA-512 + ### database backend engine: values [ 'postgres', 'mariadb' ] keycloak_quarkus_db_engine: postgres diff --git a/roles/keycloak_quarkus/templates/quarkus.properties.j2 b/roles/keycloak_quarkus/templates/quarkus.properties.j2 index 93152e8..06d9077 100644 --- a/roles/keycloak_quarkus/templates/quarkus.properties.j2 +++ b/roles/keycloak_quarkus/templates/quarkus.properties.j2 @@ -1,22 +1,22 @@ {{ ansible_managed | comment }} {% if keycloak_quarkus_ha_enabled %} {% if keycloak_quarkus_version.split('.')[0] | int < 22 %} -quarkus.infinispan-client.server-list={{ keycloak_quarkus_ispn_hosts }} -quarkus.infinispan-client.auth-username={{ keycloak_quarkus_ispn_user }} -quarkus.infinispan-client.auth-password={{ keycloak_quarkus_ispn_pass }} +quarkus.infinispan-client.server-list={{ keycloak_quarkus_cache_remote_host }} +quarkus.infinispan-client.auth-username={{ keycloak_quarkus_cache_remote_username }} +quarkus.infinispan-client.auth-password={{ keycloak_quarkus_cache_remote_password }} {% else %} -quarkus.infinispan-client.hosts={{ keycloak_quarkus_ispn_hosts }} -quarkus.infinispan-client.username={{ keycloak_quarkus_ispn_user }} -quarkus.infinispan-client.password={{ keycloak_quarkus_ispn_pass }} +quarkus.infinispan-client.hosts={{ keycloak_quarkus_cache_remote_host }} +quarkus.infinispan-client.username={{ keycloak_quarkus_cache_remote_username }} +quarkus.infinispan-client.password={{ keycloak_quarkus_cache_remote_password }} {% endif %} quarkus.infinispan-client.client-intelligence=HASH_DISTRIBUTION_AWARE quarkus.infinispan-client.use-auth=true quarkus.infinispan-client.auth-realm=default quarkus.infinispan-client.auth-server-name=infinispan -quarkus.infinispan-client.sasl-mechanism={{ keycloak_quarkus_ispn_sasl_mechanism }} -{% if keycloak_quarkus_ispn_use_ssl %} -quarkus.infinispan-client.trust-store={{ keycloak_quarkus_ispn_trust_store_path }} -quarkus.infinispan-client.trust-store-password={{ keycloak_quarkus_ispn_trust_store_password }} +quarkus.infinispan-client.sasl-mechanism={{ keycloak_quarkus_cache_remote_sasl_mechanism }} +{% if keycloak_quarkus_cache_remote_tls_enabled %} +quarkus.infinispan-client.trust-store={{ keycloak_quarkus_https_trust_store_file }} +quarkus.infinispan-client.trust-store-password={{ keycloak_quarkus_https_trust_store_password }} quarkus.infinispan-client.trust-store-type=jks {% endif %} #quarkus.infinispan-client.use-schema-registration=true From 88be78926073dbaebb769eb8b02f321c024e653f Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Fri, 2 May 2025 09:48:54 +0000 Subject: [PATCH 119/135] Update changelog for release 3.0.1 Signed-off-by: ansible-middleware-core --- CHANGELOG.rst | 13 +++++++++++++ changelogs/changelog.yaml | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d6b2d25..b290328 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,19 @@ middleware\_automation.keycloak Release Notes This changelog describes changes after version 0.2.6. +v3.0.1 +====== + +Minor Changes +------------- + +- Version update to 26.0.8 / rhbk 26.0.11 `#277 `_ + +Bugfixes +-------- + +- Trigger rebuild handler on envvars file change `#276 `_ + v3.0.0 ====== diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 3d25cd2..9b09e13 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -660,3 +660,17 @@ releases: name: keycloak_realm namespace: '' release_date: '2025-04-23' + 3.0.1: + changes: + bugfixes: + - 'Trigger rebuild handler on envvars file change `#276 `_ + + ' + minor_changes: + - 'Version update to 26.0.8 / rhbk 26.0.11 `#277 `_ + + ' + fragments: + - 276.yaml + - 277.yaml + release_date: '2025-05-02' From e0c4b1e1ff14c8ba09487dd9c64448a3e91803bd Mon Sep 17 00:00:00 2001 From: ansible-middleware-core Date: Fri, 2 May 2025 09:49:08 +0000 Subject: [PATCH 120/135] Bump version to 3.0.2 --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index 0b53eba..006207e 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: middleware_automation name: keycloak -version: "3.0.1" +version: "3.0.2" readme: README.md authors: - Romain Pelisse From 5cd400b053d8a2a973fa5703f41df7f7dcbffdb0 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 6 May 2025 15:16:50 +0200 Subject: [PATCH 121/135] feat: introduce `checksum` for keycloak_quarkus_providers (#279) --- molecule/quarkus/converge.yml | 3 +++ roles/keycloak_quarkus/README.md | 4 ++++ roles/keycloak_quarkus/tasks/install.yml | 2 ++ 3 files changed, 9 insertions(+) diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index cb3b9f8..a836d1a 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -36,6 +36,9 @@ value: 10 - id: spid-saml url: https://github.com/italia/spid-keycloak-provider/releases/download/24.0.2/spid-provider.jar + - id: spid-saml-w-checksum + url: https://github.com/italia/spid-keycloak-provider/releases/download/24.0.2/spid-provider.jar + checksum: sha256:fbb50e73739d7a6d35b5bff611b1c01668b29adf6f6259624b95e466a305f377 - id: keycloak-kerberos-federation maven: repository_url: https://repo1.maven.org/maven2/ # https://mvnrepository.com/artifact/org.keycloak/keycloak-kerberos-federation/24.0.4 diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 7f93a9f..b5e7686 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -228,6 +228,10 @@ keycloak_quarkus_providers: properties: # optional, list of key-values - key: default-connection-pool-size value: 10 + checksum: sha256:D98291AC[...]B6DC7B97 # optional, checksum used to verify integrity: + # for `url` SPIs, use format: :, cf. ; + # for `local_path` SPIs, use SHA1 format + # for `maven` SPIs, this field is ignored since maven has integrity verification methods enabled by default ``` the definition above will generate the following build command: diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index c664d05..4719b64 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -215,6 +215,7 @@ owner: "{{ keycloak.service_user }}" group: "{{ keycloak.service_group }}" mode: '0640' + checksum: "{{ item.checksum | default(omit) }}" become: true loop: "{{ keycloak_quarkus_providers }}" when: item.url is defined and item.url | length > 0 @@ -244,6 +245,7 @@ owner: "{{ keycloak.service_user }}" group: "{{ keycloak.service_group }}" mode: '0640' + checksum: "{{ item.checksum | default(omit) }}" become: true loop: "{{ keycloak_quarkus_providers }}" when: item.maven is defined From 0936d415c7fd460570d8304cb263be8382efd31a Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Fri, 9 May 2025 15:20:57 +0200 Subject: [PATCH 122/135] ci: update test linking removed url --- molecule/quarkus/converge.yml | 9 +++++---- roles/keycloak_quarkus/README.md | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index a836d1a..65da7bd 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -23,6 +23,7 @@ keycloak_quarkus_systemd_wait_for_delay: 2 keycloak_quarkus_systemd_wait_for_log: true keycloak_quarkus_restart_health_check: false # would fail because of self-signed cert + keycloak_quarkus_version: 26.2.0 keycloak_quarkus_additional_env_vars: - key: KC_FEATURES_DISABLED value: impersonation,kerberos @@ -50,10 +51,10 @@ # - id: my-static-theme # local_path: /tmp/my-static-theme.jar keycloak_quarkus_policies: - - name: "xato-net-10-million-passwords.txt" - url: "https://github.com/danielmiessler/SecLists/raw/master/Passwords/xato-net-10-million-passwords.txt" - - name: "xato-net-10-million-passwords-10.txt" - url: "https://github.com/danielmiessler/SecLists/raw/master/Passwords/xato-net-10-million-passwords-10.txt" + - name: "cain-and-abel.txt" + url: "https://github.com/danielmiessler/SecLists/raw/master/Passwords/Software/cain-and-abel.txt" + - name: "john-the-ripper.txt" + url: "https://github.com/danielmiessler/SecLists/raw/master/Passwords/Software/john-the-ripper.txt" type: password-blacklists roles: - role: keycloak_quarkus diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index b5e7686..2d87c2b 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -251,9 +251,9 @@ Provider definition: ```yaml keycloak_quarkus_policies: - - name: xato-net-10-million-passwords.txt # required, resulting file name - url: https://github.com/danielmiessler/SecLists/raw/master/Passwords/xato-net-10-million-passwords.txt # required, url for download - type: password-blacklists # optional, defaults to `password-blacklists`; supported values: [`password-blacklists`] + - name: john-the-ripper.txt # required, resulting file name + url: https://github.com/danielmiessler/SecLists/raw/master/Passwords/Software/john-the-ripper.txt # required, url for download + type: password-blacklists # optional, defaults to `password-blacklists`; supported values: [`password-blacklists`] ``` From c614af127e8270f68ec341e646d6e611df120054 Mon Sep 17 00:00:00 2001 From: Jonathan Wright Date: Tue, 13 May 2025 09:15:38 -0500 Subject: [PATCH 123/135] Add var to set the jgroups IP per host This is useful if the default route does not represent the network you want/need to use for cluster communication. --- molecule/debian/prepare.yml | 5 +++++ roles/keycloak_quarkus/README.md | 1 + roles/keycloak_quarkus/defaults/main.yml | 1 + roles/keycloak_quarkus/meta/argument_specs.yml | 4 ++++ roles/keycloak_quarkus/tasks/main.yml | 4 ++-- 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/molecule/debian/prepare.yml b/molecule/debian/prepare.yml index 267386e..ed21958 100644 --- a/molecule/debian/prepare.yml +++ b/molecule/debian/prepare.yml @@ -10,3 +10,8 @@ # - openjdk-21-jdk-headless # this is not available in ghcr.io/hspaans/molecule-containers:debian-11 (neither in debian-12) since the images are using outdated package sources - openjdk-17-jdk-headless state: present + - name: "Install iproute2" + ansible.builtin.apt: + name: + - iproute2 + state: present diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 2d87c2b..7aedb45 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -79,6 +79,7 @@ Role Defaults |`keycloak_quarkus_ha_enabled`| Enable auto configuration for database backend, clustering and remote caches on infinispan | `False` | |`keycloak_quarkus_ha_discovery`| Discovery protocol for HA cluster members | `TCPPING` | |`keycloak_quarkus_db_enabled`| Enable auto configuration for database backend | `True` if `keycloak_quarkus_ha_enabled` is True, else `False` | +|`keycloak_quarkus_jgroups_ip`| Host jgroups IP. If changing this variable you must make sure it is always set for all hosts in your cluster. | `{{ ansible_default_ipv4.address }}` | |`keycloak_quarkus_jgroups_port`| jgroups cluster tcp port | `7800` | |`keycloak_quarkus_systemd_wait_for_port` | Whether systemd unit should wait for keycloak port before returning | `{{ keycloak_quarkus_ha_enabled }}` | |`keycloak_quarkus_systemd_wait_for_port_number`| Which port the systemd unit should wait for | `{{ keycloak_quarkus_https_port }}` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 66fc8e1..b4d5a37 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -38,6 +38,7 @@ keycloak_quarkus_http_enabled: true keycloak_quarkus_http_port: 8080 keycloak_quarkus_https_port: 8443 keycloak_quarkus_http_management_port: 9000 +keycloak_quarkus_jgroups_ip: "{{ ansible_default_ipv4.address }}" keycloak_quarkus_jgroups_port: 7800 keycloak_quarkus_java_heap_opts: "-Xms1024m -Xmx2048m" keycloak_quarkus_java_jvm_opts: "-XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 6b0b8c2..cdf280b 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -195,6 +195,10 @@ argument_specs: default: 9000 description: "Port of the management interface. Relevant only when something is exposed on the management interface - see the guide for details." type: "int" + keycloak_quarkus_jgroups_ip: + default: "{{ ansible_default_ipv4.address }}" + description: Host jgroups IP. If changing this variable you must make sure it is always set for all hosts in your cluster. + type: "str" keycloak_quarkus_jgroups_port: default: 7800 description: "jgroups cluster tcp port" diff --git a/roles/keycloak_quarkus/tasks/main.yml b/roles/keycloak_quarkus/tasks/main.yml index cca6764..5c7126d 100644 --- a/roles/keycloak_quarkus/tasks/main.yml +++ b/roles/keycloak_quarkus/tasks/main.yml @@ -39,8 +39,8 @@ { "name": item, "address": 'jgroups-' + item, - "inventory_host": hostvars[item].ansible_default_ipv4.address | default(item) + '[' + (keycloak_quarkus_jgroups_port | string) + ']', - "value": hostvars[item].ansible_default_ipv4.address | default(item) + "inventory_host": hostvars[item].keycloak_quarkus_jgroups_ip | default(item) + '[' + (keycloak_quarkus_jgroups_port | string) + ']', + "value": hostvars[item].keycloak_quarkus_jgroups_ip | default(item) } ] }} loop: "{{ ansible_play_batch }}" From a70aece0d9e1ac698d48221701a4d2754546544c Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Mon, 19 May 2025 14:10:29 +0200 Subject: [PATCH 124/135] chore: RHBK v26.2: Update recommended JDBC driver versions --- roles/keycloak_quarkus/defaults/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index b4d5a37..8b7c16d 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -125,19 +125,19 @@ keycloak_quarkus_db_user: keycloak-user keycloak_quarkus_db_pass: keycloak-pass keycloak_quarkus_db_url: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].url }}" keycloak_quarkus_db_driver_version: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].version }}" -# override the variables above, following defaults show minimum supported versions +# override the variables above, following defaults show recommended version as per +# https://access.redhat.com/articles/7033107 keycloak_quarkus_default_jdbc: postgres: url: 'jdbc:postgresql://localhost:5432/keycloak' - version: 9.4.1212 + version: 42.7.5 mariadb: url: 'jdbc:mariadb://localhost:3306/keycloak' - version: 2.7.4 + version: 3.5.2 mssql: url: 'jdbc:sqlserver://localhost:1433;databaseName=keycloak;' version: 12.8.1 driver_jar_url: "https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/12.8.1.jre11/mssql-jdbc-12.8.1.jre11.jar" - # cf. https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.0/html-single/server_configuration_guide/index#db-installing-the-microsoft-sql-server-driver ### logging configuration keycloak_quarkus_log: file keycloak_quarkus_log_level: info From 8093b1af2acea4dcc854f2164eefbb53c8c28089 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Mon, 19 May 2025 14:10:45 +0200 Subject: [PATCH 125/135] chore: RHBK v26.2: Bump RHBK version to v26.2.4 --- roles/keycloak_quarkus/meta/argument_specs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index cdf280b..0f437b7 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -475,7 +475,7 @@ argument_specs: downstream: options: rhbk_version: - default: "26.0.11" + default: "26.2.4" description: "Red Hat Build of Keycloak version" type: "str" rhbk_archive: From f8c75de5d5f822cb0b3cfd6cccecd562ca658f92 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Mon, 19 May 2025 14:12:50 +0200 Subject: [PATCH 126/135] chore: RHBK v26.2: Bump KC version to v26.2.4 --- roles/keycloak_quarkus/README.md | 2 +- roles/keycloak_quarkus/defaults/main.yml | 2 +- roles/keycloak_quarkus/meta/argument_specs.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 7aedb45..2460674 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -33,7 +33,7 @@ Role Defaults | Variable | Description | Default | |:---------|:------------|:--------| -|`keycloak_quarkus_version`| keycloak.org package version | `26.0.7` | +|`keycloak_quarkus_version`| keycloak.org package version | `26.2.4` | |`keycloak_quarkus_offline_install` | Perform an offline install | `False`| |`keycloak_quarkus_dest`| Installation root path | `/opt/keycloak` | |`keycloak_quarkus_download_url` | Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 8b7c16d..cbea292 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -1,6 +1,6 @@ --- ### Configuration specific to keycloak -keycloak_quarkus_version: 26.0.8 +keycloak_quarkus_version: 26.2.4 keycloak_quarkus_archive: "keycloak-{{ keycloak_quarkus_version }}.zip" keycloak_quarkus_download_url: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}" keycloak_quarkus_installdir: "{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_quarkus_version }}" diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 0f437b7..95d42f4 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -2,7 +2,7 @@ argument_specs: main: options: keycloak_quarkus_version: - default: "26.0.8" + default: "26.2.4" description: "keycloak.org package version" type: "str" keycloak_quarkus_archive: From 8f95bcb9e67250ad0cf332fd3cbfa73d584c53b8 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Mon, 19 May 2025 14:38:45 +0200 Subject: [PATCH 127/135] feat(HA): Change default ispn discovery mechanism to JDBCPING as per v26.2.* (#282) --- roles/keycloak_quarkus/README.md | 2 +- roles/keycloak_quarkus/defaults/main.yml | 2 +- roles/keycloak_quarkus/templates/cache-ispn.xml.j2 | 14 +++++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 2460674..0da7272 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -77,7 +77,7 @@ Role Defaults | Variable | Description | Default | |:---------|:------------|:--------| |`keycloak_quarkus_ha_enabled`| Enable auto configuration for database backend, clustering and remote caches on infinispan | `False` | -|`keycloak_quarkus_ha_discovery`| Discovery protocol for HA cluster members | `TCPPING` | +|`keycloak_quarkus_ha_discovery`| Discovery protocol for HA cluster members | `JDBCPING` | |`keycloak_quarkus_db_enabled`| Enable auto configuration for database backend | `True` if `keycloak_quarkus_ha_enabled` is True, else `False` | |`keycloak_quarkus_jgroups_ip`| Host jgroups IP. If changing this variable you must make sure it is always set for all hosts in your cluster. | `{{ ansible_default_ipv4.address }}` | |`keycloak_quarkus_jgroups_port`| jgroups cluster tcp port | `7800` | diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index cbea292..ee12214 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -70,7 +70,7 @@ keycloak_quarkus_config_key_store_password: '' ### Enable configuration for database backend, clustering and remote caches on infinispan keycloak_quarkus_ha_enabled: false -keycloak_quarkus_ha_discovery: "TCPPING" +keycloak_quarkus_ha_discovery: "JDBCPING" ### Enable database configuration, must be enabled when HA is configured keycloak_quarkus_db_enabled: "{{ keycloak_quarkus_ha_enabled }}" keycloak_quarkus_systemd_wait_for_port: "{{ keycloak_quarkus_ha_enabled }}" diff --git a/roles/keycloak_quarkus/templates/cache-ispn.xml.j2 b/roles/keycloak_quarkus/templates/cache-ispn.xml.j2 index e546ab8..2d745d5 100644 --- a/roles/keycloak_quarkus/templates/cache-ispn.xml.j2 +++ b/roles/keycloak_quarkus/templates/cache-ispn.xml.j2 @@ -22,7 +22,8 @@ xmlns="urn:infinispan:config:15.0"> {% set stack_expression='' %} -{% if keycloak_quarkus_ha_enabled and keycloak_quarkus_ha_discovery == 'TCPPING' %} +{% if keycloak_quarkus_ha_enabled %} +{% if keycloak_quarkus_ha_discovery == 'TCPPING' %} {% set stack_expression='stack="tcpping"' %} @@ -35,6 +36,9 @@ /> +{% elif keycloak_quarkus_ha_discovery == 'JDBCPING' %} +{% set stack_expression='stack="JDBC_PING2"' %} +{% endif %} {% endif %} @@ -93,6 +97,14 @@ + + + + + + + + From d23ae39c258989a88ca2474bdf74502612bb2cf4 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Mon, 19 May 2025 14:44:06 +0200 Subject: [PATCH 128/135] chore(molecule): RHBK v26.2 (#282) --- molecule/default/prepare.yml | 2 +- molecule/quarkus/converge.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml index 77a7723..44d4a91 100644 --- a/molecule/default/prepare.yml +++ b/molecule/default/prepare.yml @@ -18,7 +18,7 @@ - name: Download keycloak archive to controller directory ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user - url: https://github.com/keycloak/keycloak/releases/download/26.0.8/keycloak-26.0.8.zip + url: https://github.com/keycloak/keycloak/releases/download/26.2.4/keycloak-26.2.4.zip dest: /tmp/keycloak mode: '0640' delegate_to: localhost diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index 65da7bd..1114478 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -23,7 +23,7 @@ keycloak_quarkus_systemd_wait_for_delay: 2 keycloak_quarkus_systemd_wait_for_log: true keycloak_quarkus_restart_health_check: false # would fail because of self-signed cert - keycloak_quarkus_version: 26.2.0 + keycloak_quarkus_version: 26.2.4 keycloak_quarkus_additional_env_vars: - key: KC_FEATURES_DISABLED value: impersonation,kerberos @@ -45,7 +45,7 @@ repository_url: https://repo1.maven.org/maven2/ # https://mvnrepository.com/artifact/org.keycloak/keycloak-kerberos-federation/24.0.4 group_id: org.keycloak artifact_id: keycloak-kerberos-federation - version: 26.0.7 # optional + version: 26.2.4 # optional # username: myUser # optional # password: myPAT # optional # - id: my-static-theme From 0a5fc3ae25137413283af34a69fa8cae1706f4fc Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 20 May 2025 10:35:01 +0200 Subject: [PATCH 129/135] restructure molecule tests --- .github/workflows/ci.yml | 13 +- molecule/quarkus_ha_remote/converge.yml | 29 + molecule/quarkus_ha_remote/molecule.yml | 104 +++ .../postgresql/postgresql.conf | 750 ++++++++++++++++++ molecule/quarkus_ha_remote/prepare.yml | 44 + molecule/quarkus_ha_remote/roles | 1 + molecule/quarkus_ha_remote/verify.yml | 29 + 7 files changed, 968 insertions(+), 2 deletions(-) create mode 100644 molecule/quarkus_ha_remote/converge.yml create mode 100644 molecule/quarkus_ha_remote/molecule.yml create mode 100644 molecule/quarkus_ha_remote/postgresql/postgresql.conf create mode 100644 molecule/quarkus_ha_remote/prepare.yml create mode 120000 molecule/quarkus_ha_remote/roles create mode 100644 molecule/quarkus_ha_remote/verify.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de65d72..16b6802 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,14 +6,23 @@ on: - main pull_request: workflow_dispatch: + inputs: + debug_verbosity: + description: 'ANSIBLE_VERBOSITY envvar value' + required: false schedule: - cron: '15 6 * * *' jobs: ci: - uses: ansible-middleware/github-actions/.github/workflows/ci.yml@main + uses: ansible-middleware/github-actions/.github/workflows/cish.yml@main secrets: inherit with: fqcn: 'middleware_automation/keycloak' + debug_verbosity: "${{ github.event.inputs.debug_verbosity }}" molecule_tests: >- - [ "default", "overridexml", "https_revproxy", "quarkus", "quarkus-devmode", "quarkus_upgrade", "debian", "quarkus_ha" ] + [ "default", "debian", "quarkus_ha", "quarkus_ha_remote" ] + podman_tests_current: >- + [ "default", "quarkus", "quarkus-devmode", "quarkus_upgrade" ] + podman_tests_next: >- + [ "default", "quarkus", "quarkus-devmode", "quarkus_upgrade" ] diff --git a/molecule/quarkus_ha_remote/converge.yml b/molecule/quarkus_ha_remote/converge.yml new file mode 100644 index 0000000..fa5314f --- /dev/null +++ b/molecule/quarkus_ha_remote/converge.yml @@ -0,0 +1,29 @@ +--- +- name: Converge + hosts: keycloak + vars: + keycloak_quarkus_show_deprecation_warnings: false + keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" + keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" + keycloak_quarkus_hostname: "http://{{ inventory_hostname }}:8080" + keycloak_quarkus_log: file + keycloak_quarkus_log_level: info + keycloak_quarkus_https_key_file_enabled: true + keycloak_quarkus_key_file_copy_enabled: true + keycloak_quarkus_key_content: "{{ lookup('file', inventory_hostname + '.key') }}" + keycloak_quarkus_cert_file_copy_enabled: true + keycloak_quarkus_cert_file_src: "{{ inventory_hostname }}.pem" + keycloak_quarkus_ks_vault_enabled: true + keycloak_quarkus_ks_vault_file: "/opt/keycloak/vault/keystore.p12" + keycloak_quarkus_ks_vault_pass: keystorepassword + keycloak_quarkus_systemd_wait_for_port: true + keycloak_quarkus_systemd_wait_for_timeout: 20 + keycloak_quarkus_systemd_wait_for_delay: 2 + keycloak_quarkus_systemd_wait_for_log: true + keycloak_quarkus_ha_enabled: true + keycloak_quarkus_restart_strategy: restart/serial.yml + keycloak_quarkus_db_user: keycloak + keycloak_quarkus_db_pass: mysecretpass + keycloak_quarkus_db_url: jdbc:postgresql://postgres:5432/keycloak + roles: + - role: keycloak_quarkus diff --git a/molecule/quarkus_ha_remote/molecule.yml b/molecule/quarkus_ha_remote/molecule.yml new file mode 100644 index 0000000..673b021 --- /dev/null +++ b/molecule/quarkus_ha_remote/molecule.yml @@ -0,0 +1,104 @@ +--- +driver: + name: docker +platforms: + - name: instance1 + image: registry.access.redhat.com/ubi9/ubi-init:latest + pre_build_image: true + privileged: true + command: "/usr/sbin/init" + groups: + - keycloak + networks: + - name: rhbk + port_bindings: + - "8080/tcp" + - "8443/tcp" + - "9000/tcp" + - name: instance2 + image: registry.access.redhat.com/ubi9/ubi-init:latest + pre_build_image: true + privileged: true + command: "/usr/sbin/init" + groups: + - keycloak + networks: + - name: rhbk + port_bindings: + - "8080/tcp" + - "8443/tcp" + - "9000/tcp" + - name: infinispan1 + image: registry.access.redhat.com/ubi9/ubi-init:latest + pre_build_image: true + privileged: true + command: "/usr/sbin/init" + groups: + - infinispan + networks: + - name: rhbk + port_bindings: + - "11222/tcp" + - name: infinispan2 + image: registry.access.redhat.com/ubi9/ubi-init:latest + pre_build_image: true + privileged: true + command: "/usr/sbin/init" + groups: + - infinispan + networks: + - name: rhbk + port_bindings: + - "11222/tcp" + - name: postgres + image: ubuntu/postgres:14-22.04_beta + pre_build_image: true + privileged: true + command: postgres + groups: + - database + networks: + - name: rhbk + port_bindings: + - "5432/tcp" + mounts: + - type: bind + target: /etc/postgresql/postgresql.conf + source: ${PWD}/molecule/quarkus_ha/postgresql/postgresql.conf + env: + POSTGRES_USER: keycloak + POSTGRES_PASSWORD: mysecretpass + POSTGRES_DB: keycloak + POSTGRES_HOST_AUTH_METHOD: trust +provisioner: + name: ansible + config_options: + defaults: + interpreter_python: auto_silent + ssh_connection: + pipelining: false + playbooks: + prepare: prepare.yml + converge: converge.yml + verify: verify.yml + inventory: + host_vars: + localhost: + ansible_python_interpreter: "{{ ansible_playbook_python }}" + env: + ANSIBLE_FORCE_COLOR: "true" + PYTHONHTTPSVERIFY: 0 +verifier: + name: ansible +scenario: + test_sequence: + - cleanup + - destroy + - create + - prepare + - converge + - idempotence + - side_effect + - verify + - cleanup + - destroy diff --git a/molecule/quarkus_ha_remote/postgresql/postgresql.conf b/molecule/quarkus_ha_remote/postgresql/postgresql.conf new file mode 100644 index 0000000..d702576 --- /dev/null +++ b/molecule/quarkus_ha_remote/postgresql/postgresql.conf @@ -0,0 +1,750 @@ +# ----------------------------- +# PostgreSQL configuration file +# ----------------------------- +# +# This file consists of lines of the form: +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are introduced with +# "#" anywhere on a line. The complete list of parameter names and allowed +# values can be found in the PostgreSQL documentation. +# +# The commented-out settings shown in this file represent the default values. +# Re-commenting a setting is NOT sufficient to revert it to the default value; +# you need to reload the server. +# +# This file is read on server startup and when the server receives a SIGHUP +# signal. If you edit the file on a running system, you have to SIGHUP the +# server for the changes to take effect, run "pg_ctl reload", or execute +# "SELECT pg_reload_conf()". Some parameters, which are marked below, +# require a server shutdown and restart to take effect. +# +# Any parameter can also be given as a command-line option to the server, e.g., +# "postgres -c log_connections=on". Some parameters can be changed at run time +# with the "SET" SQL command. +# +# Memory units: kB = kilobytes Time units: ms = milliseconds +# MB = megabytes s = seconds +# GB = gigabytes min = minutes +# TB = terabytes h = hours +# d = days + + +#------------------------------------------------------------------------------ +# FILE LOCATIONS +#------------------------------------------------------------------------------ + +# The default values of these variables are driven from the -D command-line +# option or PGDATA environment variable, represented here as ConfigDir. + +#data_directory = 'ConfigDir' # use data in another directory + # (change requires restart) +#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file + # (change requires restart) +#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file + # (change requires restart) + +# If external_pid_file is not explicitly set, no extra PID file is written. +#external_pid_file = '' # write an extra PID file + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONNECTIONS AND AUTHENTICATION +#------------------------------------------------------------------------------ + +# - Connection Settings - + +listen_addresses = '*' # what IP address(es) to listen on; + # comma-separated list of addresses; + # defaults to 'localhost'; use '*' for all + # (change requires restart) +#port = 5432 # (change requires restart) +#max_connections = 100 # (change requires restart) +#superuser_reserved_connections = 3 # (change requires restart) +#unix_socket_directories = '/tmp' # comma-separated list of directories + # (change requires restart) +#unix_socket_group = '' # (change requires restart) +#unix_socket_permissions = 0777 # begin with 0 to use octal notation + # (change requires restart) +#bonjour = off # advertise server via Bonjour + # (change requires restart) +#bonjour_name = '' # defaults to the computer name + # (change requires restart) + +# - TCP settings - +# see "man 7 tcp" for details + +#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; + # 0 selects the system default +#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; + # 0 selects the system default +#tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default +#tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds; + # 0 selects the system default + +# - Authentication - + +#authentication_timeout = 1min # 1s-600s +#password_encryption = md5 # md5 or scram-sha-256 +#db_user_namespace = off + +# GSSAPI using Kerberos +#krb_server_keyfile = '' +#krb_caseins_users = off + +# - SSL - + +#ssl = off +#ssl_ca_file = '' +#ssl_cert_file = 'server.crt' +#ssl_crl_file = '' +#ssl_key_file = 'server.key' +#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers +#ssl_prefer_server_ciphers = on +#ssl_ecdh_curve = 'prime256v1' +#ssl_min_protocol_version = 'TLSv1' +#ssl_max_protocol_version = '' +#ssl_dh_params_file = '' +#ssl_passphrase_command = '' +#ssl_passphrase_command_supports_reload = off + + +#------------------------------------------------------------------------------ +# RESOURCE USAGE (except WAL) +#------------------------------------------------------------------------------ + +# - Memory - + +#shared_buffers = 32MB # min 128kB + # (change requires restart) +#huge_pages = try # on, off, or try + # (change requires restart) +#temp_buffers = 8MB # min 800kB +#max_prepared_transactions = 0 # zero disables the feature + # (change requires restart) +# Caution: it is not advisable to set max_prepared_transactions nonzero unless +# you actively intend to use prepared transactions. +#work_mem = 4MB # min 64kB +#maintenance_work_mem = 64MB # min 1MB +#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem +#max_stack_depth = 2MB # min 100kB +#shared_memory_type = mmap # the default is the first option + # supported by the operating system: + # mmap + # sysv + # windows + # (change requires restart) +#dynamic_shared_memory_type = posix # the default is the first option + # supported by the operating system: + # posix + # sysv + # windows + # mmap + # (change requires restart) + +# - Disk - + +#temp_file_limit = -1 # limits per-process temp file space + # in kB, or -1 for no limit + +# - Kernel Resources - + +#max_files_per_process = 1000 # min 25 + # (change requires restart) + +# - Cost-Based Vacuum Delay - + +#vacuum_cost_delay = 0 # 0-100 milliseconds (0 disables) +#vacuum_cost_page_hit = 1 # 0-10000 credits +#vacuum_cost_page_miss = 10 # 0-10000 credits +#vacuum_cost_page_dirty = 20 # 0-10000 credits +#vacuum_cost_limit = 200 # 1-10000 credits + +# - Background Writer - + +#bgwriter_delay = 200ms # 10-10000ms between rounds +#bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables +#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round +#bgwriter_flush_after = 0 # measured in pages, 0 disables + +# - Asynchronous Behavior - + +#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching +#max_worker_processes = 8 # (change requires restart) +#max_parallel_maintenance_workers = 2 # taken from max_parallel_workers +#max_parallel_workers_per_gather = 2 # taken from max_parallel_workers +#parallel_leader_participation = on +#max_parallel_workers = 8 # maximum number of max_worker_processes that + # can be used in parallel operations +#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate + # (change requires restart) +#backend_flush_after = 0 # measured in pages, 0 disables + + +#------------------------------------------------------------------------------ +# WRITE-AHEAD LOG +#------------------------------------------------------------------------------ + +# - Settings - + +#wal_level = replica # minimal, replica, or logical + # (change requires restart) +#fsync = on # flush data to disk for crash safety + # (turning this off can cause + # unrecoverable data corruption) +#synchronous_commit = on # synchronization level; + # off, local, remote_write, remote_apply, or on +#wal_sync_method = fsync # the default is the first option + # supported by the operating system: + # open_datasync + # fdatasync (default on Linux) + # fsync + # fsync_writethrough + # open_sync +#full_page_writes = on # recover from partial page writes +#wal_compression = off # enable compression of full-page writes +#wal_log_hints = off # also do full page writes of non-critical updates + # (change requires restart) +#wal_init_zero = on # zero-fill new WAL files +#wal_recycle = on # recycle WAL files +#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers + # (change requires restart) +#wal_writer_delay = 200ms # 1-10000 milliseconds +#wal_writer_flush_after = 1MB # measured in pages, 0 disables + +#commit_delay = 0 # range 0-100000, in microseconds +#commit_siblings = 5 # range 1-1000 + +# - Checkpoints - + +#checkpoint_timeout = 5min # range 30s-1d +#max_wal_size = 1GB +#min_wal_size = 80MB +#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0 +#checkpoint_flush_after = 0 # measured in pages, 0 disables +#checkpoint_warning = 30s # 0 disables + +# - Archiving - + +#archive_mode = off # enables archiving; off, on, or always + # (change requires restart) +#archive_command = '' # command to use to archive a logfile segment + # placeholders: %p = path of file to archive + # %f = file name only + # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' +#archive_timeout = 0 # force a logfile segment switch after this + # number of seconds; 0 disables + +# - Archive Recovery - + +# These are only used in recovery mode. + +#restore_command = '' # command to use to restore an archived logfile segment + # placeholders: %p = path of file to restore + # %f = file name only + # e.g. 'cp /mnt/server/archivedir/%f %p' + # (change requires restart) +#archive_cleanup_command = '' # command to execute at every restartpoint +#recovery_end_command = '' # command to execute at completion of recovery + +# - Recovery Target - + +# Set these only when performing a targeted recovery. + +#recovery_target = '' # 'immediate' to end recovery as soon as a + # consistent state is reached + # (change requires restart) +#recovery_target_name = '' # the named restore point to which recovery will proceed + # (change requires restart) +#recovery_target_time = '' # the time stamp up to which recovery will proceed + # (change requires restart) +#recovery_target_xid = '' # the transaction ID up to which recovery will proceed + # (change requires restart) +#recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed + # (change requires restart) +#recovery_target_inclusive = on # Specifies whether to stop: + # just after the specified recovery target (on) + # just before the recovery target (off) + # (change requires restart) +#recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID + # (change requires restart) +#recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown' + # (change requires restart) + + +#------------------------------------------------------------------------------ +# REPLICATION +#------------------------------------------------------------------------------ + +# - Sending Servers - + +# Set these on the master and on any standby that will send replication data. + +#max_wal_senders = 10 # max number of walsender processes + # (change requires restart) +#wal_keep_segments = 0 # in logfile segments; 0 disables +#wal_sender_timeout = 60s # in milliseconds; 0 disables + +#max_replication_slots = 10 # max number of replication slots + # (change requires restart) +#track_commit_timestamp = off # collect timestamp of transaction commit + # (change requires restart) + +# - Master Server - + +# These settings are ignored on a standby server. + +#synchronous_standby_names = '' # standby servers that provide sync rep + # method to choose sync standbys, number of sync standbys, + # and comma-separated list of application_name + # from standby(s); '*' = all +#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed + +# - Standby Servers - + +# These settings are ignored on a master server. + +#primary_conninfo = '' # connection string to sending server + # (change requires restart) +#primary_slot_name = '' # replication slot on sending server + # (change requires restart) +#promote_trigger_file = '' # file name whose presence ends recovery +#hot_standby = on # "off" disallows queries during recovery + # (change requires restart) +#max_standby_archive_delay = 30s # max delay before canceling queries + # when reading WAL from archive; + # -1 allows indefinite delay +#max_standby_streaming_delay = 30s # max delay before canceling queries + # when reading streaming WAL; + # -1 allows indefinite delay +#wal_receiver_status_interval = 10s # send replies at least this often + # 0 disables +#hot_standby_feedback = off # send info from standby to prevent + # query conflicts +#wal_receiver_timeout = 60s # time that receiver waits for + # communication from master + # in milliseconds; 0 disables +#wal_retrieve_retry_interval = 5s # time to wait before retrying to + # retrieve WAL after a failed attempt +#recovery_min_apply_delay = 0 # minimum delay for applying changes during recovery + +# - Subscribers - + +# These settings are ignored on a publisher. + +#max_logical_replication_workers = 4 # taken from max_worker_processes + # (change requires restart) +#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers + + +#------------------------------------------------------------------------------ +# QUERY TUNING +#------------------------------------------------------------------------------ + +# - Planner Method Configuration - + +#enable_bitmapscan = on +#enable_hashagg = on +#enable_hashjoin = on +#enable_indexscan = on +#enable_indexonlyscan = on +#enable_material = on +#enable_mergejoin = on +#enable_nestloop = on +#enable_parallel_append = on +#enable_seqscan = on +#enable_sort = on +#enable_tidscan = on +#enable_partitionwise_join = off +#enable_partitionwise_aggregate = off +#enable_parallel_hash = on +#enable_partition_pruning = on + +# - Planner Cost Constants - + +#seq_page_cost = 1.0 # measured on an arbitrary scale +#random_page_cost = 4.0 # same scale as above +#cpu_tuple_cost = 0.01 # same scale as above +#cpu_index_tuple_cost = 0.005 # same scale as above +#cpu_operator_cost = 0.0025 # same scale as above +#parallel_tuple_cost = 0.1 # same scale as above +#parallel_setup_cost = 1000.0 # same scale as above + +#jit_above_cost = 100000 # perform JIT compilation if available + # and query more expensive than this; + # -1 disables +#jit_inline_above_cost = 500000 # inline small functions if query is + # more expensive than this; -1 disables +#jit_optimize_above_cost = 500000 # use expensive JIT optimizations if + # query is more expensive than this; + # -1 disables + +#min_parallel_table_scan_size = 8MB +#min_parallel_index_scan_size = 512kB +#effective_cache_size = 4GB + +# - Genetic Query Optimizer - + +#geqo = on +#geqo_threshold = 12 +#geqo_effort = 5 # range 1-10 +#geqo_pool_size = 0 # selects default based on effort +#geqo_generations = 0 # selects default based on effort +#geqo_selection_bias = 2.0 # range 1.5-2.0 +#geqo_seed = 0.0 # range 0.0-1.0 + +# - Other Planner Options - + +#default_statistics_target = 100 # range 1-10000 +#constraint_exclusion = partition # on, off, or partition +#cursor_tuple_fraction = 0.1 # range 0.0-1.0 +#from_collapse_limit = 8 +#join_collapse_limit = 8 # 1 disables collapsing of explicit + # JOIN clauses +#force_parallel_mode = off +#jit = on # allow JIT compilation +#plan_cache_mode = auto # auto, force_generic_plan or + # force_custom_plan + + +#------------------------------------------------------------------------------ +# REPORTING AND LOGGING +#------------------------------------------------------------------------------ + +# - Where to Log - + +#log_destination = 'stderr' # Valid values are combinations of + # stderr, csvlog, syslog, and eventlog, + # depending on platform. csvlog + # requires logging_collector to be on. + +# This is used when logging to stderr: +#logging_collector = off # Enable capturing of stderr and csvlog + # into log files. Required to be on for + # csvlogs. + # (change requires restart) + +# These are only used if logging_collector is on: +#log_directory = 'log' # directory where log files are written, + # can be absolute or relative to PGDATA +#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, + # can include strftime() escapes +#log_file_mode = 0600 # creation mode for log files, + # begin with 0 to use octal notation +#log_truncate_on_rotation = off # If on, an existing log file with the + # same name as the new log file will be + # truncated rather than appended to. + # But such truncation only occurs on + # time-driven rotation, not on restarts + # or size-driven rotation. Default is + # off, meaning append to existing files + # in all cases. +#log_rotation_age = 1d # Automatic rotation of logfiles will + # happen after that time. 0 disables. +#log_rotation_size = 10MB # Automatic rotation of logfiles will + # happen after that much log output. + # 0 disables. + +# These are relevant when logging to syslog: +#syslog_facility = 'LOCAL0' +#syslog_ident = 'postgres' +#syslog_sequence_numbers = on +#syslog_split_messages = on + +# This is only relevant when logging to eventlog (win32): +# (change requires restart) +#event_source = 'PostgreSQL' + +# - When to Log - + +#log_min_messages = warning # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic + +#log_min_error_statement = error # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic (effectively off) + +#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements + # and their durations, > 0 logs only + # statements running at least this number + # of milliseconds + +#log_transaction_sample_rate = 0.0 # Fraction of transactions whose statements + # are logged regardless of their duration. 1.0 logs all + # statements from all transactions, 0.0 never logs. + +# - What to Log - + +#debug_print_parse = off +#debug_print_rewritten = off +#debug_print_plan = off +#debug_pretty_print = on +#log_checkpoints = off +#log_connections = off +#log_disconnections = off +#log_duration = off +#log_error_verbosity = default # terse, default, or verbose messages +#log_hostname = off +#log_line_prefix = '%m [%p] ' # special values: + # %a = application name + # %u = user name + # %d = database name + # %r = remote host and port + # %h = remote host + # %p = process ID + # %t = timestamp without milliseconds + # %m = timestamp with milliseconds + # %n = timestamp with milliseconds (as a Unix epoch) + # %i = command tag + # %e = SQL state + # %c = session ID + # %l = session line number + # %s = session start timestamp + # %v = virtual transaction ID + # %x = transaction ID (0 if none) + # %q = stop here in non-session + # processes + # %% = '%' + # e.g. '<%u%%%d> ' +#log_lock_waits = off # log lock waits >= deadlock_timeout +#log_statement = 'none' # none, ddl, mod, all +#log_replication_commands = off +#log_temp_files = -1 # log temporary files equal or larger + # than the specified size in kilobytes; + # -1 disables, 0 logs all temp files +#log_timezone = 'GMT' + +#------------------------------------------------------------------------------ +# PROCESS TITLE +#------------------------------------------------------------------------------ + +#cluster_name = '' # added to process titles if nonempty + # (change requires restart) +#update_process_title = on + + +#------------------------------------------------------------------------------ +# STATISTICS +#------------------------------------------------------------------------------ + +# - Query and Index Statistics Collector - + +#track_activities = on +#track_counts = on +#track_io_timing = off +#track_functions = none # none, pl, all +#track_activity_query_size = 1024 # (change requires restart) +#stats_temp_directory = 'pg_stat_tmp' + + +# - Monitoring - + +#log_parser_stats = off +#log_planner_stats = off +#log_executor_stats = off +#log_statement_stats = off + + +#------------------------------------------------------------------------------ +# AUTOVACUUM +#------------------------------------------------------------------------------ + +#autovacuum = on # Enable autovacuum subprocess? 'on' + # requires track_counts to also be on. +#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and + # their durations, > 0 logs only + # actions running at least this number + # of milliseconds. +#autovacuum_max_workers = 3 # max number of autovacuum subprocesses + # (change requires restart) +#autovacuum_naptime = 1min # time between autovacuum runs +#autovacuum_vacuum_threshold = 50 # min number of row updates before + # vacuum +#autovacuum_analyze_threshold = 50 # min number of row updates before + # analyze +#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum +#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze +#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum + # (change requires restart) +#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age + # before forced vacuum + # (change requires restart) +#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for + # autovacuum, in milliseconds; + # -1 means use vacuum_cost_delay +#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for + # autovacuum, -1 means use + # vacuum_cost_limit + + +#------------------------------------------------------------------------------ +# CLIENT CONNECTION DEFAULTS +#------------------------------------------------------------------------------ + +# - Statement Behavior - + +#client_min_messages = notice # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # log + # notice + # warning + # error +#search_path = '"$user", public' # schema names +#row_security = on +#default_tablespace = '' # a tablespace name, '' uses the default +#temp_tablespaces = '' # a list of tablespace names, '' uses + # only default tablespace +#default_table_access_method = 'heap' +#check_function_bodies = on +#default_transaction_isolation = 'read committed' +#default_transaction_read_only = off +#default_transaction_deferrable = off +#session_replication_role = 'origin' +#statement_timeout = 0 # in milliseconds, 0 is disabled +#lock_timeout = 0 # in milliseconds, 0 is disabled +#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled +#vacuum_freeze_min_age = 50000000 +#vacuum_freeze_table_age = 150000000 +#vacuum_multixact_freeze_min_age = 5000000 +#vacuum_multixact_freeze_table_age = 150000000 +#vacuum_cleanup_index_scale_factor = 0.1 # fraction of total number of tuples + # before index cleanup, 0 always performs + # index cleanup +#bytea_output = 'hex' # hex, escape +#xmlbinary = 'base64' +#xmloption = 'content' +#gin_fuzzy_search_limit = 0 +#gin_pending_list_limit = 4MB + +# - Locale and Formatting - + +#datestyle = 'iso, mdy' +#intervalstyle = 'postgres' +#timezone = 'GMT' +#timezone_abbreviations = 'Default' # Select the set of available time zone + # abbreviations. Currently, there are + # Default + # Australia (historical usage) + # India + # You can create your own file in + # share/timezonesets/. +#extra_float_digits = 1 # min -15, max 3; any value >0 actually + # selects precise output mode +#client_encoding = sql_ascii # actually, defaults to database + # encoding + +# These settings are initialized by initdb, but they can be changed. +#lc_messages = 'C' # locale for system error message + # strings +#lc_monetary = 'C' # locale for monetary formatting +#lc_numeric = 'C' # locale for number formatting +#lc_time = 'C' # locale for time formatting + +# default configuration for text search +#default_text_search_config = 'pg_catalog.simple' + +# - Shared Library Preloading - + +#shared_preload_libraries = '' # (change requires restart) +#local_preload_libraries = '' +#session_preload_libraries = '' +#jit_provider = 'llvmjit' # JIT library to use + +# - Other Defaults - + +#dynamic_library_path = '$libdir' + + +#------------------------------------------------------------------------------ +# LOCK MANAGEMENT +#------------------------------------------------------------------------------ + +#deadlock_timeout = 1s +#max_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_relation = -2 # negative values mean + # (max_pred_locks_per_transaction + # / -max_pred_locks_per_relation) - 1 +#max_pred_locks_per_page = 2 # min 0 + + +#------------------------------------------------------------------------------ +# VERSION AND PLATFORM COMPATIBILITY +#------------------------------------------------------------------------------ + +# - Previous PostgreSQL Versions - + +#array_nulls = on +#backslash_quote = safe_encoding # on, off, or safe_encoding +#escape_string_warning = on +#lo_compat_privileges = off +#operator_precedence_warning = off +#quote_all_identifiers = off +#standard_conforming_strings = on +#synchronize_seqscans = on + +# - Other Platforms and Clients - + +#transform_null_equals = off + + +#------------------------------------------------------------------------------ +# ERROR HANDLING +#------------------------------------------------------------------------------ + +#exit_on_error = off # terminate session on any error? +#restart_after_crash = on # reinitialize after backend crash? +#data_sync_retry = off # retry or panic on failure to fsync + # data? + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONFIG FILE INCLUDES +#------------------------------------------------------------------------------ + +# These options allow settings to be loaded from files other than the +# default postgresql.conf. Note that these are directives, not variable +# assignments, so they can usefully be given more than once. + +#include_dir = '...' # include files ending in '.conf' from + # a directory, e.g., 'conf.d' +#include_if_exists = '...' # include file only if it exists +#include = '...' # include file + + +#------------------------------------------------------------------------------ +# CUSTOMIZED OPTIONS +#------------------------------------------------------------------------------ + +# Add settings for extensions here diff --git a/molecule/quarkus_ha_remote/prepare.yml b/molecule/quarkus_ha_remote/prepare.yml new file mode 100644 index 0000000..dff1821 --- /dev/null +++ b/molecule/quarkus_ha_remote/prepare.yml @@ -0,0 +1,44 @@ +--- +- name: Prepare + hosts: keycloak + tasks: + - name: "Display hera_home if defined." + ansible.builtin.set_fact: + hera_home: "{{ lookup('env', 'HERA_HOME') }}" + + - name: "Ensure common prepare phase are set." + ansible.builtin.include_tasks: ../prepare.yml + + - name: Create certificate request + ansible.builtin.command: "openssl req -x509 -newkey rsa:4096 -keyout {{ inventory_hostname }}.key -out {{ inventory_hostname }}.pem -sha256 -days 365 -nodes -subj '/CN={{ inventory_hostname }}'" + delegate_to: localhost + changed_when: False + + - name: Create vault directory + become: true + ansible.builtin.file: + state: directory + path: "/opt/keycloak/vault" + mode: 0755 + + - name: Make sure a jre is available (for keytool to prepare keystore) + delegate_to: localhost + ansible.builtin.package: + name: "{{ 'java-17-openjdk-headless' if hera_home | length > 0 else 'openjdk-17-jdk-headless' }}" + state: present + become: true + failed_when: false + + - name: Create vault keystore + ansible.builtin.command: keytool -importpass -alias TestRealm_testalias -keystore keystore.p12 -storepass keystorepassword + delegate_to: localhost + register: keytool_cmd + changed_when: False + failed_when: not 'already exists' in keytool_cmd.stdout and keytool_cmd.rc != 0 + + - name: Copy certificates and vault + become: true + ansible.builtin.copy: + src: keystore.p12 + dest: /opt/keycloak/vault/keystore.p12 + mode: 0444 diff --git a/molecule/quarkus_ha_remote/roles b/molecule/quarkus_ha_remote/roles new file mode 120000 index 0000000..b741aa3 --- /dev/null +++ b/molecule/quarkus_ha_remote/roles @@ -0,0 +1 @@ +../../roles \ No newline at end of file diff --git a/molecule/quarkus_ha_remote/verify.yml b/molecule/quarkus_ha_remote/verify.yml new file mode 100644 index 0000000..c1a2fb9 --- /dev/null +++ b/molecule/quarkus_ha_remote/verify.yml @@ -0,0 +1,29 @@ +--- +- name: Verify + hosts: keycloak + tasks: + - name: Populate service facts + ansible.builtin.service_facts: + + - name: Check if keycloak service started + ansible.builtin.assert: + that: + - ansible_facts.services["keycloak.service"]["state"] == "running" + - ansible_facts.services["keycloak.service"]["status"] == "enabled" + fail_msg: "Service not running" + + - name: Set internal envvar + ansible.builtin.set_fact: + hera_home: "{{ lookup('env', 'HERA_HOME') }}" + + - name: Check log file + become: true + ansible.builtin.stat: + path: /var/log/keycloak/keycloak.log + register: keycloak_log_file + + - name: Check if keycloak file exists + ansible.builtin.assert: + that: + - keycloak_log_file.stat.exists + - not keycloak_log_file.stat.isdir From 88e4ea8d9907bf68a4c57e88b516ea6442f408d6 Mon Sep 17 00:00:00 2001 From: Helmut Wolf Date: Tue, 20 May 2025 13:51:47 +0200 Subject: [PATCH 130/135] fix: MVN provider invokes KC config rebuild --- roles/keycloak_quarkus/tasks/install.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/keycloak_quarkus/tasks/install.yml b/roles/keycloak_quarkus/tasks/install.yml index 4719b64..b188e6c 100644 --- a/roles/keycloak_quarkus/tasks/install.yml +++ b/roles/keycloak_quarkus/tasks/install.yml @@ -236,7 +236,6 @@ loop: "{{ keycloak_quarkus_providers }}" when: item.maven is defined no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" - notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}" - name: "Copy maven providers" ansible.builtin.copy: @@ -250,6 +249,7 @@ loop: "{{ keycloak_quarkus_providers }}" when: item.maven is defined no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" + notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}" - name: "Copy local providers" ansible.builtin.copy: From 0403939c038bd49715f7099982f16f833e28b259 Mon Sep 17 00:00:00 2001 From: Ranabir Chakraborty Date: Tue, 20 May 2025 20:59:59 +0530 Subject: [PATCH 131/135] AMW-398 Use tags to decorate the role workflow --- roles/keycloak/tasks/debian.yml | 6 ++++- roles/keycloak/tasks/main.yml | 24 ++++++++++++++--- roles/keycloak/tasks/redhat.yml | 6 ++++- roles/keycloak_quarkus/tasks/debian.yml | 6 ++++- roles/keycloak_quarkus/tasks/main.yml | 36 ++++++++++++++++++++----- roles/keycloak_quarkus/tasks/redhat.yml | 6 ++++- 6 files changed, 70 insertions(+), 14 deletions(-) diff --git a/roles/keycloak/tasks/debian.yml b/roles/keycloak/tasks/debian.yml index ffb1348..acfadcc 100644 --- a/roles/keycloak/tasks/debian.yml +++ b/roles/keycloak/tasks/debian.yml @@ -1,6 +1,10 @@ --- - name: Include firewall config tasks - ansible.builtin.include_tasks: iptables.yml + ansible.builtin.include_tasks: + file: iptables.yml + apply: + tags: + - firewall when: keycloak_configure_iptables tags: - firewall diff --git a/roles/keycloak/tasks/main.yml b/roles/keycloak/tasks/main.yml index a21f359..f826b63 100644 --- a/roles/keycloak/tasks/main.yml +++ b/roles/keycloak/tasks/main.yml @@ -1,22 +1,38 @@ --- # tasks file for keycloak - name: Check prerequisites - ansible.builtin.include_tasks: prereqs.yml + ansible.builtin.include_tasks: + file: prereqs.yml + apply: + tags: + - prereqs tags: - prereqs - name: Distro specific tasks - ansible.builtin.include_tasks: "{{ ansible_os_family | lower }}.yml" + ansible.builtin.include_tasks: + file: "{{ ansible_os_family | lower }}.yml" + apply: + tags: + - unbound tags: - unbound - name: Include install tasks - ansible.builtin.include_tasks: install.yml + ansible.builtin.include_tasks: + file: install.yml + apply: + tags: + - install tags: - install - name: Include systemd tasks - ansible.builtin.include_tasks: systemd.yml + ansible.builtin.include_tasks: + file: systemd.yml + apply: + tags: + - systemd tags: - systemd diff --git a/roles/keycloak/tasks/redhat.yml b/roles/keycloak/tasks/redhat.yml index 596834b..ece5772 100644 --- a/roles/keycloak/tasks/redhat.yml +++ b/roles/keycloak/tasks/redhat.yml @@ -1,6 +1,10 @@ --- - name: Include firewall config tasks - ansible.builtin.include_tasks: firewalld.yml + ansible.builtin.include_tasks: + file: firewalld.yml + apply: + tags: + - firewall when: keycloak_configure_firewalld tags: - firewall diff --git a/roles/keycloak_quarkus/tasks/debian.yml b/roles/keycloak_quarkus/tasks/debian.yml index 4a36661..7e59204 100644 --- a/roles/keycloak_quarkus/tasks/debian.yml +++ b/roles/keycloak_quarkus/tasks/debian.yml @@ -1,6 +1,10 @@ --- - name: Include firewall config tasks - ansible.builtin.include_tasks: iptables.yml + ansible.builtin.include_tasks: + file: iptables.yml + apply: + tags: + - firewall when: keycloak_quarkus_configure_iptables tags: - firewall diff --git a/roles/keycloak_quarkus/tasks/main.yml b/roles/keycloak_quarkus/tasks/main.yml index 5c7126d..6a7a4b0 100644 --- a/roles/keycloak_quarkus/tasks/main.yml +++ b/roles/keycloak_quarkus/tasks/main.yml @@ -1,34 +1,58 @@ --- # tasks file for keycloak - name: Check prerequisites - ansible.builtin.include_tasks: prereqs.yml + ansible.builtin.include_tasks: + file: prereqs.yml + apply: + tags: + - prereqs tags: - prereqs - always - name: Check for deprecations - ansible.builtin.include_tasks: deprecations.yml + ansible.builtin.include_tasks: + file: deprecations.yml + apply: + tags: + - always tags: - always - name: Distro specific tasks - ansible.builtin.include_tasks: "{{ ansible_os_family | lower }}.yml" + ansible.builtin.include_tasks: + file: "{{ ansible_os_family | lower }}.yml" + apply: + tags: + - unbound tags: - unbound - name: Include install tasks - ansible.builtin.include_tasks: install.yml + ansible.builtin.include_tasks: + file: install.yml + apply: + tags: + - install tags: - install - name: Include systemd tasks - ansible.builtin.include_tasks: systemd.yml + ansible.builtin.include_tasks: + file: systemd.yml + apply: + tags: + - systemd tags: - systemd - name: Include configuration key store tasks + ansible.builtin.include_tasks: + file: config_store.yml + apply: + tags: + - install when: keycloak.config_key_store_enabled - ansible.builtin.include_tasks: config_store.yml tags: - install diff --git a/roles/keycloak_quarkus/tasks/redhat.yml b/roles/keycloak_quarkus/tasks/redhat.yml index 093b930..26d552b 100644 --- a/roles/keycloak_quarkus/tasks/redhat.yml +++ b/roles/keycloak_quarkus/tasks/redhat.yml @@ -1,6 +1,10 @@ --- - name: Include firewall config tasks - ansible.builtin.include_tasks: firewalld.yml + ansible.builtin.include_tasks: + file: firewalld.yml + apply: + tags: + - firewall when: keycloak_quarkus_configure_firewalld tags: - firewall From 476bc0ec0bc8c8eabd2475aa059c01cac2eead9e Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Tue, 20 May 2025 10:40:59 +0200 Subject: [PATCH 132/135] update test config --- .github/workflows/ci.yml | 6 +++--- molecule/default/molecule.yml | 4 +++- molecule/quarkus/molecule.yml | 4 +++- molecule/quarkus/prepare.yml | 2 +- molecule/{quarkus-devmode => quarkus_devmode}/converge.yml | 0 molecule/{quarkus-devmode => quarkus_devmode}/molecule.yml | 4 +++- molecule/{quarkus-devmode => quarkus_devmode}/prepare.yml | 0 molecule/{quarkus-devmode => quarkus_devmode}/roles | 0 molecule/{quarkus-devmode => quarkus_devmode}/verify.yml | 0 molecule/quarkus_upgrade/molecule.yml | 6 +++++- 10 files changed, 18 insertions(+), 8 deletions(-) rename molecule/{quarkus-devmode => quarkus_devmode}/converge.yml (100%) rename molecule/{quarkus-devmode => quarkus_devmode}/molecule.yml (93%) rename molecule/{quarkus-devmode => quarkus_devmode}/prepare.yml (100%) rename molecule/{quarkus-devmode => quarkus_devmode}/roles (100%) rename molecule/{quarkus-devmode => quarkus_devmode}/verify.yml (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16b6802..b9509a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,8 +21,8 @@ jobs: fqcn: 'middleware_automation/keycloak' debug_verbosity: "${{ github.event.inputs.debug_verbosity }}" molecule_tests: >- - [ "default", "debian", "quarkus_ha", "quarkus_ha_remote" ] + [ "debian", "quarkus_ha", "quarkus_ha_remote" ] podman_tests_current: >- - [ "default", "quarkus", "quarkus-devmode", "quarkus_upgrade" ] + [ "default", "quarkus", "quarkus_devmode", "quarkus_upgrade" ] podman_tests_next: >- - [ "default", "quarkus", "quarkus-devmode", "quarkus_upgrade" ] + [ "default", "quarkus", "quarkus_devmode", "quarkus_upgrade" ] diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml index 011c08e..587a3c8 100644 --- a/molecule/default/molecule.yml +++ b/molecule/default/molecule.yml @@ -1,6 +1,6 @@ --- driver: - name: docker + name: podman platforms: - name: instance image: registry.access.redhat.com/ubi9/ubi-init:latest @@ -29,6 +29,8 @@ provisioner: ansible_python_interpreter: "{{ ansible_playbook_python }}" env: ANSIBLE_FORCE_COLOR: "true" + PROXY: "${PROXY}" + NO_PROXY: "${NO_PROXY}" verifier: name: ansible scenario: diff --git a/molecule/quarkus/molecule.yml b/molecule/quarkus/molecule.yml index cf975e5..1b4c8c5 100644 --- a/molecule/quarkus/molecule.yml +++ b/molecule/quarkus/molecule.yml @@ -1,6 +1,6 @@ --- driver: - name: docker + name: podman platforms: - name: instance image: registry.access.redhat.com/ubi9/ubi-init:latest @@ -32,6 +32,8 @@ provisioner: env: ANSIBLE_FORCE_COLOR: "true" PYTHONHTTPSVERIFY: 0 + PROXY: "${PROXY}" + NO_PROXY: "${NO_PROXY}" verifier: name: ansible scenario: diff --git a/molecule/quarkus/prepare.yml b/molecule/quarkus/prepare.yml index d8ef369..abe2518 100644 --- a/molecule/quarkus/prepare.yml +++ b/molecule/quarkus/prepare.yml @@ -24,7 +24,7 @@ - name: Make sure a jre is available (for keytool to prepare keystore) delegate_to: localhost ansible.builtin.package: - name: "{{ 'java-21-openjdk-headless' if hera_home | length > 0 else 'openjdk-21-jdk-headless' }}" + name: java-21-openjdk-headless state: present become: true failed_when: false diff --git a/molecule/quarkus-devmode/converge.yml b/molecule/quarkus_devmode/converge.yml similarity index 100% rename from molecule/quarkus-devmode/converge.yml rename to molecule/quarkus_devmode/converge.yml diff --git a/molecule/quarkus-devmode/molecule.yml b/molecule/quarkus_devmode/molecule.yml similarity index 93% rename from molecule/quarkus-devmode/molecule.yml rename to molecule/quarkus_devmode/molecule.yml index 2da7e01..0ae28b3 100644 --- a/molecule/quarkus-devmode/molecule.yml +++ b/molecule/quarkus_devmode/molecule.yml @@ -1,6 +1,6 @@ --- driver: - name: docker + name: podman platforms: - name: instance image: registry.access.redhat.com/ubi9/ubi-init:latest @@ -31,6 +31,8 @@ provisioner: ansible_python_interpreter: "{{ ansible_playbook_python }}" env: ANSIBLE_FORCE_COLOR: "true" + PROXY: "${PROXY}" + NO_PROXY: "${NO_PROXY}" verifier: name: ansible scenario: diff --git a/molecule/quarkus-devmode/prepare.yml b/molecule/quarkus_devmode/prepare.yml similarity index 100% rename from molecule/quarkus-devmode/prepare.yml rename to molecule/quarkus_devmode/prepare.yml diff --git a/molecule/quarkus-devmode/roles b/molecule/quarkus_devmode/roles similarity index 100% rename from molecule/quarkus-devmode/roles rename to molecule/quarkus_devmode/roles diff --git a/molecule/quarkus-devmode/verify.yml b/molecule/quarkus_devmode/verify.yml similarity index 100% rename from molecule/quarkus-devmode/verify.yml rename to molecule/quarkus_devmode/verify.yml diff --git a/molecule/quarkus_upgrade/molecule.yml b/molecule/quarkus_upgrade/molecule.yml index 21782e8..b121079 100644 --- a/molecule/quarkus_upgrade/molecule.yml +++ b/molecule/quarkus_upgrade/molecule.yml @@ -4,7 +4,7 @@ dependency: options: requirements-file: molecule/requirements.yml driver: - name: docker + name: podman platforms: - name: instance image: registry.access.redhat.com/ubi9/ubi-init:latest @@ -27,6 +27,10 @@ provisioner: host_vars: localhost: ansible_python_interpreter: "{{ ansible_playbook_python }}" + env: + ANSIBLE_FORCE_COLOR: "true" + PROXY: "${PROXY}" + NO_PROXY: "${NO_PROXY}" verifier: name: ansible scenario: From b569e4e7136a34f2d4014b424f346a7375267065 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Thu, 29 May 2025 20:14:04 +0200 Subject: [PATCH 133/135] update remote cache test --- molecule/quarkus_ha_remote/converge.yml | 20 +++++++++++++++++++ molecule/quarkus_ha_remote/molecule.yml | 26 +------------------------ molecule/quarkus_ha_remote/prepare.yml | 2 +- molecule/requirements.yml | 1 + 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/molecule/quarkus_ha_remote/converge.yml b/molecule/quarkus_ha_remote/converge.yml index fa5314f..e31ad72 100644 --- a/molecule/quarkus_ha_remote/converge.yml +++ b/molecule/quarkus_ha_remote/converge.yml @@ -1,4 +1,20 @@ --- +- name: Converge + hosts: infinispan + roles: + - role: middleware_automation.infinispan.infinispan + infinispan_service_name: infinispan + infinispan_supervisor_password: remembertochangeme + infinispan_keycloak_caches: true + infinispan_keycloak_persistence: False + infinispan_jdbc_engine: postgres + infinispan_jdbc_url: jdbc:postgresql://postgres:5432/keycloak + infinispan_jdbc_driver_version: 9.4.1212 + infinispan_jdbc_user: keycloak + infinispan_jdbc_pass: mysecretpass + infinispan_users: + - { name: 'testuser', password: 'test', roles: 'observer' } + - name: Converge hosts: keycloak vars: @@ -25,5 +41,9 @@ keycloak_quarkus_db_user: keycloak keycloak_quarkus_db_pass: mysecretpass keycloak_quarkus_db_url: jdbc:postgresql://postgres:5432/keycloak + keycloak_quarkus_cache_remote_username: supervisor + keycloak_quarkus_cache_remote_password: remembertochangeme + keycloak_quarkus_cache_remote_host: "infinispan1:11222" + keycloak_quarkus_cache_remote_tls_enabled: false roles: - role: keycloak_quarkus diff --git a/molecule/quarkus_ha_remote/molecule.yml b/molecule/quarkus_ha_remote/molecule.yml index 673b021..23d8db6 100644 --- a/molecule/quarkus_ha_remote/molecule.yml +++ b/molecule/quarkus_ha_remote/molecule.yml @@ -2,20 +2,7 @@ driver: name: docker platforms: - - name: instance1 - image: registry.access.redhat.com/ubi9/ubi-init:latest - pre_build_image: true - privileged: true - command: "/usr/sbin/init" - groups: - - keycloak - networks: - - name: rhbk - port_bindings: - - "8080/tcp" - - "8443/tcp" - - "9000/tcp" - - name: instance2 + - name: keycloak1 image: registry.access.redhat.com/ubi9/ubi-init:latest pre_build_image: true privileged: true @@ -39,17 +26,6 @@ platforms: - name: rhbk port_bindings: - "11222/tcp" - - name: infinispan2 - image: registry.access.redhat.com/ubi9/ubi-init:latest - pre_build_image: true - privileged: true - command: "/usr/sbin/init" - groups: - - infinispan - networks: - - name: rhbk - port_bindings: - - "11222/tcp" - name: postgres image: ubuntu/postgres:14-22.04_beta pre_build_image: true diff --git a/molecule/quarkus_ha_remote/prepare.yml b/molecule/quarkus_ha_remote/prepare.yml index dff1821..16ae9b9 100644 --- a/molecule/quarkus_ha_remote/prepare.yml +++ b/molecule/quarkus_ha_remote/prepare.yml @@ -1,6 +1,6 @@ --- - name: Prepare - hosts: keycloak + hosts: 'keycloak:infinispan' tasks: - name: "Display hera_home if defined." ansible.builtin.set_fact: diff --git a/molecule/requirements.yml b/molecule/requirements.yml index 5c8bb43..125a922 100644 --- a/molecule/requirements.yml +++ b/molecule/requirements.yml @@ -2,6 +2,7 @@ collections: - name: middleware_automation.common - name: middleware_automation.jbcs + - name: middleware_automation.infinispan - name: community.general - name: ansible.posix - name: community.docker From 18de37706feb6fee2e9102aeca22367f72bdf02c Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Thu, 29 May 2025 20:31:57 +0200 Subject: [PATCH 134/135] tune Xmx Xms jvm args --- molecule/default/converge.yml | 1 + molecule/quarkus/converge.yml | 1 + molecule/quarkus_devmode/converge.yml | 2 ++ 3 files changed, 4 insertions(+) diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index de97748..e617b59 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -13,6 +13,7 @@ keycloak_quarkus_proxy_mode: none keycloak_quarkus_offline_install: true keycloak_quarkus_download_path: /tmp/keycloak/ + keycloak_quarkus_java_heap_opts: "-Xms640m -Xmx640m " roles: - role: keycloak_quarkus - role: keycloak_realm diff --git a/molecule/quarkus/converge.yml b/molecule/quarkus/converge.yml index 1114478..fa2d70f 100644 --- a/molecule/quarkus/converge.yml +++ b/molecule/quarkus/converge.yml @@ -24,6 +24,7 @@ keycloak_quarkus_systemd_wait_for_log: true keycloak_quarkus_restart_health_check: false # would fail because of self-signed cert keycloak_quarkus_version: 26.2.4 + keycloak_quarkus_java_heap_opts: "-Xms1024m -Xmx1024m" keycloak_quarkus_additional_env_vars: - key: KC_FEATURES_DISABLED value: impersonation,kerberos diff --git a/molecule/quarkus_devmode/converge.yml b/molecule/quarkus_devmode/converge.yml index 6c0b14f..a596478 100644 --- a/molecule/quarkus_devmode/converge.yml +++ b/molecule/quarkus_devmode/converge.yml @@ -11,6 +11,8 @@ keycloak_quarkus_start_dev: True keycloak_quarkus_proxy_mode: none keycloak_quarkus_java_home: /opt/openjdk/ + keycloak_quarkus_java_heap_opts: "-Xms640m -Xmx640m" + roles: - role: keycloak_quarkus - role: keycloak_realm From de0ea02272a6bb80e5abf871139a7c0c833507a8 Mon Sep 17 00:00:00 2001 From: Guido Grazioli Date: Thu, 29 May 2025 21:03:03 +0200 Subject: [PATCH 135/135] ci: move intermittent test back --- .github/workflows/ci.yml | 6 +++--- molecule/quarkus/molecule.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b9509a1..a622526 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,8 +21,8 @@ jobs: fqcn: 'middleware_automation/keycloak' debug_verbosity: "${{ github.event.inputs.debug_verbosity }}" molecule_tests: >- - [ "debian", "quarkus_ha", "quarkus_ha_remote" ] + [ "debian", "quarkus", "quarkus_ha", "quarkus_ha_remote" ] podman_tests_current: >- - [ "default", "quarkus", "quarkus_devmode", "quarkus_upgrade" ] + [ "default", "quarkus_devmode", "quarkus_upgrade" ] podman_tests_next: >- - [ "default", "quarkus", "quarkus_devmode", "quarkus_upgrade" ] + [ "default", "quarkus_devmode", "quarkus_upgrade" ] diff --git a/molecule/quarkus/molecule.yml b/molecule/quarkus/molecule.yml index 1b4c8c5..20ca3bc 100644 --- a/molecule/quarkus/molecule.yml +++ b/molecule/quarkus/molecule.yml @@ -1,6 +1,6 @@ --- driver: - name: podman + name: docker platforms: - name: instance image: registry.access.redhat.com/ubi9/ubi-init:latest