Compare commits

..

2 Commits

Author SHA1 Message Date
Romain Pelisse
57b3cb380b Rework Molecule prepare phase to install sudo only if root on target 2024-03-04 21:30:23 +01:00
Romain Pelisse
d8286dfca7 Rework Molecule prepare phase to install sudo only if root on target 2024-03-04 21:13:06 +01:00
214 changed files with 3942 additions and 26125 deletions

View File

@@ -1,6 +1,4 @@
# .ansible-lint # .ansible-lint
profile: production
exclude_paths: exclude_paths:
- .cache/ - .cache/
- .github/ - .github/
@@ -30,13 +28,14 @@ warn_list:
- name[casing] - name[casing]
- fqcn[action] - fqcn[action]
- schema[meta] - schema[meta]
- var-naming[no-role-prefix]
- key-order[task] - key-order[task]
- blocked_modules - blocked_modules
- run-once[task]
skip_list: skip_list:
- vars_should_not_be_used - vars_should_not_be_used
- file_is_small_enough - file_is_small_enough
- file_has_valid_name - name[template]
use_default_rules: true use_default_rules: true
parseable: true

View File

@@ -5,21 +5,14 @@ on:
branches: branches:
- main - main
pull_request: pull_request:
workflow_dispatch:
inputs:
debug_verbosity:
description: 'ANSIBLE_VERBOSITY envvar value'
required: false
schedule: schedule:
- cron: '15 6 * * *' - cron: '15 6 * * *'
jobs: jobs:
ci: ci:
uses: ansible-middleware/github-actions/.github/workflows/ci.yml@rootperm uses: ansible-middleware/github-actions/.github/workflows/ci.yml@main
secrets: inherit secrets: inherit
with: with:
fqcn: 'middleware_automation/keycloak' fqcn: 'middleware_automation/keycloak'
root_permission_varname: 'keycloak_install_requires_become'
debug_verbosity: "${{ github.event.inputs.debug_verbosity }}"
molecule_tests: >- molecule_tests: >-
[ "debian", "sso_test", "quarkus", "quarkus_ha", "quarkus_ha_remote", "quarkus_ha_26.4_below", "default", "quarkus_devmode", "quarkus_upgrade", "keycloak_modules" ] [ "default", "overridexml", "https_revproxy", "quarkus", "quarkus-devmode" ]

View File

@@ -15,4 +15,3 @@ jobs:
with: with:
fqcn: 'middleware_automation/keycloak' fqcn: 'middleware_automation/keycloak'
collection_fqcn: 'middleware_automation.keycloak' collection_fqcn: 'middleware_automation.keycloak'
historical_docs: 'false'

View File

@@ -2,27 +2,20 @@
name: Release collection name: Release collection
on: on:
workflow_dispatch: workflow_dispatch:
inputs:
release_summary:
description: 'Optional release summary for changelogs'
required: false
jobs: jobs:
release: release:
uses: ansible-middleware/github-actions/.github/workflows/release.yml@main uses: ansible-middleware/github-actions/.github/workflows/release.yml@main
with: with:
collection_fqcn: 'middleware_automation.keycloak' collection_fqcn: 'middleware_automation.keycloak'
downstream_name: 'rhbk'
release_summary: "${{ github.event.inputs.release_summary }}"
secrets: secrets:
galaxy_token: ${{ secrets.ANSIBLE_GALAXY_API_KEY }} galaxy_token: ${{ secrets.ANSIBLE_GALAXY_API_KEY }}
jira_webhook: ${{ secrets.JIRA_WEBHOOK_CREATE_VERSION }}
dispatch: dispatch:
needs: release needs: release
strategy: strategy:
matrix: matrix:
repo: ['ansible-middleware/ansible-middleware-ee'] repo: ['ansible-middleware/cross-dc-rhsso-demo', 'ansible-middleware/flange-demo', 'ansible-middleware/ansible-middleware-ee']
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Repository Dispatch - name: Repository Dispatch

View File

@@ -1,26 +0,0 @@
name: Collect traffic stats
on:
schedule:
- cron: "51 23 * * 0"
workflow_dispatch:
jobs:
traffic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: "gh-pages"
- 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: "gh-pages"

3
.gitignore vendored
View File

@@ -12,6 +12,3 @@ docs/_build/
*.retry *.retry
changelogs/.plugin-cache.yaml changelogs/.plugin-cache.yaml
*.pem *.pem
*.key
*.p12
.ansible/

View File

@@ -15,8 +15,7 @@ rules:
commas: commas:
max-spaces-after: -1 max-spaces-after: -1
level: error level: error
comments: comments: disable
min-spaces-from-content: 1
comments-indentation: disable comments-indentation: disable
document-start: disable document-start: disable
empty-lines: empty-lines:
@@ -31,8 +30,4 @@ rules:
new-lines: new-lines:
type: unix type: unix
trailing-spaces: disable trailing-spaces: disable
truthy: disable truthy: disable
octal-values:
forbid-implicit-octal: true
forbid-explicit-octal: true

View File

@@ -6,303 +6,6 @@ middleware\_automation.keycloak Release Notes
This changelog describes changes after version 0.2.6. This changelog describes changes after version 0.2.6.
v3.0.10
=======
Major Changes
-------------
- Fixing linting issues `#357 <https://github.com/ansible-middleware/keycloak/pull/357>`_
Minor Changes
-------------
- AMW-571 keycloak_identity_provider hide_on_login_page not working `#356 <https://github.com/ansible-middleware/keycloak/pull/356>`_
- Fixing vars naming issue `#359 <https://github.com/ansible-middleware/keycloak/pull/359>`_
- Removing from __future__ import annotations broke runtime type hints using Sequence and union syntax, causing sanity import failures and molecule module errors `#358 <https://github.com/ansible-middleware/keycloak/pull/358>`_
v3.0.9
======
Bugfixes
--------
- AMW-551 Providing correct rhbk version `#344 <https://github.com/ansible-middleware/keycloak/pull/344>`_
v3.0.8
======
v3.0.7
======
Major Changes
-------------
- Migrate Keycloak modules from the community.general collection to Keycloak collection. `#341 <https://github.com/ansible-middleware/keycloak/pull/341>`_
Minor Changes
-------------
- Fixing common module usage `#343 <https://github.com/ansible-middleware/keycloak/pull/343>`_
- fix #336: https://github.com/ansible-middleware/common/pull/38 `#338 <https://github.com/ansible-middleware/keycloak/pull/338>`_
Bugfixes
--------
- Fix molecule tests `#339 <https://github.com/ansible-middleware/keycloak/pull/339>`_
v3.0.6
======
Major Changes
-------------
- AMW-540 Fix the upstream collection requirements with common v1.2.4 `#337 <https://github.com/ansible-middleware/keycloak/pull/337>`_
v3.0.5
======
Minor Changes
-------------
- AMW-528 Deployment fails in keycloak_quarkus due to missing escalation variables `#335 <https://github.com/ansible-middleware/keycloak/pull/335>`_
v3.0.4
======
Major Changes
-------------
- AMW-467 Download keycloak binary from password protected HTTP location `#321 <https://github.com/ansible-middleware/keycloak/pull/321>`_
- v26.4.x compability `#317 <https://github.com/ansible-middleware/keycloak/pull/317>`_
Minor Changes
-------------
- AMW-518 Validating arguments against arg spec 'main' fails unexpectedly. `#324 <https://github.com/ansible-middleware/keycloak/pull/324>`_
Bugfixes
--------
- Removing parseable from lint file as Additional properties are not allowed `#319 <https://github.com/ansible-middleware/keycloak/pull/319>`_
v3.0.3
======
Major Changes
-------------
- Update to keycloak 26.3.0 `#293 <https://github.com/ansible-middleware/keycloak/pull/293>`_
- ansible-core 2.19 compatibility `#310 <https://github.com/ansible-middleware/keycloak/pull/310>`_
Minor Changes
-------------
- Allow to install provider jars from remote paths `#303 <https://github.com/ansible-middleware/keycloak/pull/303>`_
- Declared proxy_mode as deprecated, updated quarkus and realm readme `#306 <https://github.com/ansible-middleware/keycloak/pull/306>`_
- Fix config_key_store_file description to match variable name `#308 <https://github.com/ansible-middleware/keycloak/pull/308>`_
Bugfixes
--------
- keycloak collection CI label is showing no status `#312 <https://github.com/ansible-middleware/keycloak/pull/312>`_
- keycloak_realm: allow secret in keycloak_clients `#304 <https://github.com/ansible-middleware/keycloak/pull/304>`_
v3.0.2
======
Minor Changes
-------------
- New ``checksum`` property for keycloak_quarkus_providers `#280 <https://github.com/ansible-middleware/keycloak/pull/280>`_
- New parameter to set the jgroups host IP address `#281 <https://github.com/ansible-middleware/keycloak/pull/281>`_
- Session storage / distributed caches `#287 <https://github.com/ansible-middleware/keycloak/pull/287>`_
- Update keycloak/RHBK to v26.2.4 `#283 <https://github.com/ansible-middleware/keycloak/pull/283>`_
Bugfixes
--------
- Fix ``keycloak_quarkus_force_install`` parameter being ignored by install `#296 <https://github.com/ansible-middleware/keycloak/pull/296>`_
- Fix alternate download location being ignored (JBossNeworkAPI always used) `#298 <https://github.com/ansible-middleware/keycloak/pull/298>`_
- Run config rebuild after SPI providers update `#285 <https://github.com/ansible-middleware/keycloak/pull/285>`_
- Use jdk21 as default in debian `#289 <https://github.com/ansible-middleware/keycloak/pull/289>`_
- keycloak_realm: federation default provider type should be a string `#302 <https://github.com/ansible-middleware/keycloak/pull/302>`_
v3.0.1
======
Minor Changes
-------------
- Version update to 26.0.8 / rhbk 26.0.11 `#277 <https://github.com/ansible-middleware/keycloak/pull/277>`_
Bugfixes
--------
- Trigger rebuild handler on envvars file change `#276 <https://github.com/ansible-middleware/keycloak/pull/276>`_
v3.0.0
======
Minor Changes
-------------
- Add theme cache invalidation handler `#252 <https://github.com/ansible-middleware/keycloak/pull/252>`_
- keycloak_realm: change url variables to defaults `#268 <https://github.com/ansible-middleware/keycloak/pull/268>`_
Breaking Changes / Porting Guide
--------------------------------
- Bump major and ansible-core versions `#266 <https://github.com/ansible-middleware/keycloak/pull/266>`_
- Rename parameters to follow upstream `#270 <https://github.com/ansible-middleware/keycloak/pull/270>`_
- Update for keycloak v26 `#254 <https://github.com/ansible-middleware/keycloak/pull/254>`_
Bugfixes
--------
- Access token lifespan is too short for ansible run `#251 <https://github.com/ansible-middleware/keycloak/pull/251>`_
- Load environment vars during kc rebuild `#274 <https://github.com/ansible-middleware/keycloak/pull/274>`_
- Rebuild config and restart service for local providers `#250 <https://github.com/ansible-middleware/keycloak/pull/250>`_
- Rename and honour parameter ``keycloak_quarkus_http_host`` `#271 <https://github.com/ansible-middleware/keycloak/pull/271>`_
New Modules
-----------
- middleware_automation.keycloak.keycloak_realm - Allows administration of Keycloak realm via Keycloak API
v2.4.3
======
Minor Changes
-------------
- Update keycloak to 24.0.5 `#241 <https://github.com/ansible-middleware/keycloak/pull/241>`_
v2.4.2
======
Minor Changes
-------------
- New parameter ``keycloak_quarkus_download_path`` `#239 <https://github.com/ansible-middleware/keycloak/pull/239>`_
Bugfixes
--------
- Add wait_for_port number parameter `#237 <https://github.com/ansible-middleware/keycloak/pull/237>`_
v2.4.1
======
Release Summary
---------------
Internal release, documentation or test changes only.
v2.4.0
======
Major Changes
-------------
- Enable by default health check on restart `#234 <https://github.com/ansible-middleware/keycloak/pull/234>`_
- Update minimum ansible-core version > 2.15 `#232 <https://github.com/ansible-middleware/keycloak/pull/232>`_
v2.3.0
======
Major Changes
-------------
- Allow for custom providers hosted on maven repositories `#223 <https://github.com/ansible-middleware/keycloak/pull/223>`_
- Restart handler strategy behaviour `#231 <https://github.com/ansible-middleware/keycloak/pull/231>`_
Minor Changes
-------------
- Add support for policy files `#225 <https://github.com/ansible-middleware/keycloak/pull/225>`_
- Allow to add extra custom env vars in sysconfig file `#229 <https://github.com/ansible-middleware/keycloak/pull/229>`_
- Download from alternate URL with optional http authentication `#220 <https://github.com/ansible-middleware/keycloak/pull/220>`_
- Update Keycloak to version 24.0.4 `#218 <https://github.com/ansible-middleware/keycloak/pull/218>`_
- ``proxy-header`` enhancement `#227 <https://github.com/ansible-middleware/keycloak/pull/227>`_
Bugfixes
--------
- ``kc.sh build`` uses configured jdk `#211 <https://github.com/ansible-middleware/keycloak/pull/211>`_
v2.2.2
======
Minor Changes
-------------
- Copying of key material for TLS configuration `#210 <https://github.com/ansible-middleware/keycloak/pull/210>`_
- Validate certs parameter for JDBC driver downloads `#207 <https://github.com/ansible-middleware/keycloak/pull/207>`_
Bugfixes
--------
- Turn off controller privilege escalation `#209 <https://github.com/ansible-middleware/keycloak/pull/209>`_
v2.2.1
======
Release Summary
---------------
Internal release, documentation or test changes only.
Bugfixes
--------
- JDBC provider: fix clause in argument validation `#204 <https://github.com/ansible-middleware/keycloak/pull/204>`_
v2.2.0
======
Major Changes
-------------
- Support java keystore for configuration of sensitive options `#189 <https://github.com/ansible-middleware/keycloak/pull/189>`_
Minor Changes
-------------
- Add ``wait_for_port`` and ``wait_for_log`` systemd unit logic `#199 <https://github.com/ansible-middleware/keycloak/pull/199>`_
- Customize jdbc driver downloads, optional authentication `#202 <https://github.com/ansible-middleware/keycloak/pull/202>`_
- Keystore-based vault SPI configuration `#196 <https://github.com/ansible-middleware/keycloak/pull/196>`_
- New ``keycloak_quarkus_hostname_strict_https`` parameter `#195 <https://github.com/ansible-middleware/keycloak/pull/195>`_
- Providers config and custom providers `#201 <https://github.com/ansible-middleware/keycloak/pull/201>`_
- Remove administrator credentials from files once keycloak is bootstrapped `#197 <https://github.com/ansible-middleware/keycloak/pull/197>`_
- Update keycloak to 24.0 `#194 <https://github.com/ansible-middleware/keycloak/pull/194>`_
v2.1.2
======
Release Summary
---------------
Internal release, documentation or test changes only.
v2.1.1
======
Minor Changes
-------------
- Add reverse ``proxy_headers`` config, supersedes ``proxy_mode`` `#187 <https://github.com/ansible-middleware/keycloak/pull/187>`_
- Debian/Ubuntu compatibility `#178 <https://github.com/ansible-middleware/keycloak/pull/178>`_
- Use ``keycloak_realm`` as default for sub-entities `#180 <https://github.com/ansible-middleware/keycloak/pull/180>`_
Bugfixes
--------
- Fix permissions on controller-side downloaded artifacts `#184 <https://github.com/ansible-middleware/keycloak/pull/184>`_
- JVM args moved to ``JAVA_OPTS`` envvar (instead of JAVA_OPTS_APPEND) `#186 <https://github.com/ansible-middleware/keycloak/pull/186>`_
- Unrelax configuration file permissions `#191 <https://github.com/ansible-middleware/keycloak/pull/191>`_
- Utilize comment filter for ``ansible_managed`` annotations `#176 <https://github.com/ansible-middleware/keycloak/pull/176>`_
v2.1.0 v2.1.0
====== ======
@@ -551,11 +254,6 @@ Minor Changes
v1.0.4 v1.0.4
====== ======
Release Summary
---------------
Internal release, documentation or test changes only.
v1.0.3 v1.0.3
====== ======

View File

@@ -1,37 +1,3 @@
## 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.16' 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 ## Contributor's Guidelines

142
README.md
View File

@@ -1,18 +1,17 @@
# Ansible Collection - middleware_automation.keycloak # Ansible Collection - middleware_automation.keycloak
<!--start build_status --> <!--start build_status -->
[![Build Status](https://github.com/ansible-middleware/keycloak/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/ansible-middleware/keycloak/actions/workflows/ci.yml) [![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.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.** > **_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.**
<!--end build_status --> <!--end build_status -->
<!--start description -->
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). 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).
<!--end description -->
<!--start requires_ansible--> <!--start requires_ansible-->
## Ansible version compatibility ## Ansible version compatibility
This collection has been tested against following Ansible versions: **>=2.16.0**. This collection has been tested against following Ansible versions: **>=2.14.0**.
Plugins and modules within a collection may be tested with only specific Ansible versions. A collection may contain metadata that identifies these versions. Plugins and modules within a collection may be tested with only specific Ansible versions. A collection may contain metadata that identifies these versions.
<!--end requires_ansible--> <!--end requires_ansible-->
@@ -40,7 +39,6 @@ collections:
The keycloak collection also depends on the following python packages to be present on the controller host: The keycloak collection also depends on the following python packages to be present on the controller host:
* netaddr * netaddr
* lxml
A requirement file is provided to install: A requirement file is provided to install:
@@ -49,61 +47,23 @@ A requirement file is provided to install:
<!--start roles_paths --> <!--start roles_paths -->
### Included roles ### Included roles
* [`keycloak_quarkus`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_quarkus/README.md): role for installing keycloak (>= 19.0.0, quarkus based). * [`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_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`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak/README.md): role for installing legacy keycloak (<= 19.0, wildfly based). * [`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).
<!--end roles_paths --> <!--end roles_paths -->
### Included modules
All Keycloak administration modules from `community.general` are provided in this collection for Keycloak 17+ (Quarkus). Use `auth_keycloak_url` without the legacy `/auth` context path (for example `http://localhost:8080`). Set `keycloak_context` to `/auth` only when automating WildFly-based Keycloak with the `keycloak` role.
* `keycloak_authentication`: manage authentication flows and executions using Keycloak Admin REST API.
* `keycloak_authentication_flow`: manage custom authentication flows and flow executions.
* `keycloak_authentication_required_actions`: manage required actions available in realm authentication.
* `keycloak_authentication_v2`: manage authentication flows with newer Keycloak API handling.
* `keycloak_authz_authorization_scope`: manage authorization scopes for a client resource server.
* `keycloak_authz_custom_policy`: manage custom authorization policies for a client resource server.
* `keycloak_authz_permission`: manage authorization permissions for a client resource server.
* `keycloak_authz_permission_info`: retrieve authorization permission information for a client resource server.
* `keycloak_client`: manage Keycloak clients (create/update/delete).
* `keycloak_client_rolemapping`: manage client role mappings for users and groups.
* `keycloak_client_rolescope`: manage client role scope mappings.
* `keycloak_client_scope`: manage client scopes and protocol mappers (replaces `community.general.keycloak_clientscope`).
* `keycloak_client_scope_type`: manage default and optional client scope assignments.
* `keycloak_clientsecret_info`: retrieve client secret information.
* `keycloak_clientsecret_regenerate`: regenerate a client secret.
* `keycloak_clienttemplate`: manage legacy client templates.
* `keycloak_component`: manage realm components.
* `keycloak_component_info`: retrieve realm component information.
* `keycloak_group`: manage realm groups and subgroups.
* `keycloak_identity_provider`: manage identity provider instances and configuration.
* `keycloak_realm`: manage realms (create/update/delete).
* `keycloak_realm_info`: retrieve realm information.
* `keycloak_realm_key`: manage realm key providers.
* `keycloak_realm_keys_metadata_info`: retrieve realm keys metadata.
* `keycloak_realm_localization`: manage realm localization texts.
* `keycloak_realm_rolemapping`: manage realm role mappings for users and groups.
* `keycloak_role`: manage realm and client roles.
* `keycloak_user`: manage users (create/update/delete).
* `keycloak_user_execute_actions_email`: trigger execute-actions emails for users.
* `keycloak_user_federation`: manage user federation providers (for example LDAP/AD).
* `keycloak_user_rolemapping`: manage user role mappings.
* `keycloak_userprofile`: manage user profile configuration.
## Usage ## Usage
The collection provides roles to install Keycloak and modules to manage realms, clients, users, and related settings via the [Keycloak Admin REST API](https://www.keycloak.org/docs-api/latest/rest-api/index.html).
For Quarkus-based Keycloak (17+), set `auth_keycloak_url` to the server root URL without the legacy `/auth` path, for example `http://localhost:8080`. When using the legacy `keycloak` role with WildFly-based Keycloak, set `keycloak_context` to `/auth` in the `keycloak_realm` role. ### Install Playbook
<!--start rhbk_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).
Both playbooks include the `keycloak` role, with different settings, as described in the following sections.
### Install Keycloak For full service configuration details, refer to the [keycloak role README](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak/README.md).
<!--end rhbk_playbook -->
* [`playbooks/keycloak_quarkus.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_quarkus.yml) installs Keycloak >= 17 using the `keycloak_quarkus` role.
* [`playbooks/keycloak.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak.yml) installs legacy Keycloak (<= 19) using the `keycloak` role.
For full service configuration details, refer to the [keycloak_quarkus role README](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_quarkus/README.md) or the [keycloak role README](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak/README.md).
#### Install from controller node (offline) #### Install from controller node (offline)
@@ -124,15 +84,15 @@ keycloak_offline_install: true
It is possible to perform downloads from alternate sources, using the `keycloak_download_url` variable; make sure the final downloaded filename matches with the source filename (ie. keycloak-legacy-x.y.zip or rh-sso-x.y.z-server-dist.zip). It is possible to perform downloads from alternate sources, using the `keycloak_download_url` variable; make sure the final downloaded filename matches with the source filename (ie. keycloak-legacy-x.y.zip or rh-sso-x.y.z-server-dist.zip).
#### Example installation command ### Example installation command
Execute the following command from the source root directory: Execute the following command from the source root directory
```bash
ansible-playbook -i <ansible_hosts> playbooks/keycloak_quarkus.yml -e keycloak_quarkus_bootstrap_admin_password=<changeme>
``` ```
ansible-playbook -i <ansible_hosts> -e @rhn-creds.yml playbooks/keycloak.yml -e keycloak_admin_password=<changeme>
```
- `keycloak_quarkus_bootstrap_admin_password` password for the administration console user account. - `keycloak_admin_password` Password for the administration console user account.
- `ansible_hosts` is the inventory, below is an example inventory for deploying to localhost - `ansible_hosts` is the inventory, below is an example inventory for deploying to localhost
``` ```
@@ -140,26 +100,27 @@ ansible-playbook -i <ansible_hosts> playbooks/keycloak_quarkus.yml -e keycloak_q
localhost ansible_connection=local 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.
### Configure with roles
## Configuration
### Config Playbook
<!--start rhbk_realm_playbook --> <!--start rhbk_realm_playbook -->
* [`playbooks/keycloak_realm.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_realm.yml) creates or updates provided realm, user federation(s), client(s), client role(s) and client user(s). [`playbooks/keycloak_realm.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_realm.yml) creates or updates provided realm, user federation(s), client(s), client role(s) and client user(s).
<!--end rhbk_realm_playbook --> <!--end rhbk_realm_playbook -->
* [`playbooks/keycloak_realm_client.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_realm_client.yml) creates a realm with clients, roles and users using the `keycloak_realm` role.
* [`playbooks/keycloak_federation.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_federation.yml) configures user federation providers.
#### Example configuration command ### Example configuration command
Execute the following command from the source root directory: Execute the following command from the source root directory:
```bash ```bash
ansible-playbook -i <ansible_hosts> playbooks/keycloak_realm.yml -e keycloak_realm_admin_password=<changeme> -e keycloak_realm_realm=test ansible-playbook -i <ansible_hosts> playbooks/keycloak_realm.yml -e keycloak_admin_password=<changeme> -e keycloak_realm=test
``` ```
- `keycloak_realm_admin_password` password for the administration console user account. - `keycloak_admin_password` password for the administration console user account.
- `keycloak_realm_realm` name of the realm to be created/used. - `keycloak_realm` name of the realm to be created/used.
- `ansible_hosts` is the inventory, below is an example inventory for deploying to localhost - `ansible_hosts` is the inventory, below is an example inventory for deploying to localhost
``` ```
@@ -170,55 +131,14 @@ ansible-playbook -i <ansible_hosts> playbooks/keycloak_realm.yml -e keycloak_rea
For full configuration details, refer to the [keycloak_realm role README](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_realm/README.md). For full configuration details, refer to the [keycloak_realm role README](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_realm/README.md).
<!--end rhbk_realm_readme --> <!--end rhbk_realm_readme -->
### Configure with modules
Module playbooks target an already running Keycloak instance. All modules use the `middleware_automation.keycloak` collection namespace.
* [`playbooks/keycloak_client_scope.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_client_scope.yml) creates a client scope with protocol mappers using the `keycloak_client_scope` module.
* [`playbooks/keycloak_authentication_flow.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_authentication_flow.yml) creates a custom authentication flow with execution steps using the `keycloak_authentication_flow` module.
Example task using shared authentication defaults:
```yaml
- hosts: localhost
module_defaults:
group/middleware_automation.keycloak.keycloak:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: "{{ keycloak_admin_password }}"
tasks:
- name: Create a user in a realm
middleware_automation.keycloak.keycloak_user:
realm: TestRealm
username: testuser
first_name: Test
last_name: User
email: testuser@example.com
enabled: true
state: present
```
When migrating from `community.general`, replace the collection prefix in playbooks (for example `community.general.keycloak_user` becomes `middleware_automation.keycloak.keycloak_user`) and use `keycloak_client_scope` instead of `keycloak_clientscope`.
## Support
<!--start support --> <!--start support -->
For bug reports and feature requests, use [GitHub Issues](https://github.com/ansible-middleware/keycloak/issues).
<!--end support --> <!--end support -->
## Release and Upgrade Notes
For details on changes between versions, please see the [CHANGELOG](https://github.com/ansible-middleware/keycloak/blob/main/CHANGELOG.rst) for this collection.
## License ## License
Apache License v2.0 or later Apache License v2.0 or later
<!--start license --> <!--start license -->
See [LICENSE](https://github.com/ansible-middleware/keycloak/blob/main/LICENSE) to view the full text. See [LICENSE](LICENSE) to view the full text.
<!--end license --> <!--end license -->

View File

@@ -1,10 +1,8 @@
python3-dev [compile platform:dpkg]
python3-devel [compile platform:rpm] python3-devel [compile platform:rpm]
python39-devel [compile platform:centos-8 platform:rhel-8] python39-devel [compile platform:centos-8 platform:rhel-8]
git-lfs [platform:rpm platform:dpkg] git-lfs [platform:rpm]
python3-netaddr [platform:rpm platform:dpkg] python3-netaddr [platform:rpm]
python3-lxml [platform:rpm platform:dpkg] python3-lxml [platform:rpm]
python3-jmespath [platform:rpm platform:dpkg] python3-jmespath [platform:rpm]
python3-requests [platform:rpm platform:dpkg] python3-requests [platform:rpm]
podman [platform:rpm platform:dpkg]

View File

@@ -59,10 +59,6 @@ releases:
- 31.yaml - 31.yaml
release_date: '2022-05-09' release_date: '2022-05-09'
1.0.4: 1.0.4:
changes:
release_summary: 'Internal release, documentation or test changes only.
'
release_date: '2022-05-11' release_date: '2022-05-11'
1.0.5: 1.0.5:
changes: changes:
@@ -423,440 +419,3 @@ releases:
- 167.yaml - 167.yaml
- 171.yaml - 171.yaml
release_date: '2024-02-28' release_date: '2024-02-28'
2.1.1:
changes:
bugfixes:
- 'Fix permissions on controller-side downloaded artifacts `#184 <https://github.com/ansible-middleware/keycloak/pull/184>`_
'
- 'JVM args moved to ``JAVA_OPTS`` envvar (instead of JAVA_OPTS_APPEND) `#186
<https://github.com/ansible-middleware/keycloak/pull/186>`_
'
- 'Unrelax configuration file permissions `#191 <https://github.com/ansible-middleware/keycloak/pull/191>`_
'
- 'Utilize comment filter for ``ansible_managed`` annotations `#176 <https://github.com/ansible-middleware/keycloak/pull/176>`_
'
minor_changes:
- 'Add reverse ``proxy_headers`` config, supersedes ``proxy_mode`` `#187 <https://github.com/ansible-middleware/keycloak/pull/187>`_
'
- 'Debian/Ubuntu compatibility `#178 <https://github.com/ansible-middleware/keycloak/pull/178>`_
'
- 'Use ``keycloak_realm`` as default for sub-entities `#180 <https://github.com/ansible-middleware/keycloak/pull/180>`_
'
fragments:
- 176.yaml
- 178.yaml
- 180.yaml
- 184.yaml
- 186.yaml
- 187.yaml
- 191.yaml
release_date: '2024-04-17'
2.1.2:
changes:
release_summary: 'Internal release, documentation or test changes only.
'
release_date: '2024-04-17'
2.2.0:
changes:
major_changes:
- 'Support java keystore for configuration of sensitive options `#189 <https://github.com/ansible-middleware/keycloak/pull/189>`_
'
minor_changes:
- 'Add ``wait_for_port`` and ``wait_for_log`` systemd unit logic `#199 <https://github.com/ansible-middleware/keycloak/pull/199>`_
'
- 'Customize jdbc driver downloads, optional authentication `#202 <https://github.com/ansible-middleware/keycloak/pull/202>`_
'
- 'Keystore-based vault SPI configuration `#196 <https://github.com/ansible-middleware/keycloak/pull/196>`_
'
- 'New ``keycloak_quarkus_hostname_strict_https`` parameter `#195 <https://github.com/ansible-middleware/keycloak/pull/195>`_
'
- 'Providers config and custom providers `#201 <https://github.com/ansible-middleware/keycloak/pull/201>`_
'
- 'Remove administrator credentials from files once keycloak is bootstrapped
`#197 <https://github.com/ansible-middleware/keycloak/pull/197>`_
'
- 'Update keycloak to 24.0 `#194 <https://github.com/ansible-middleware/keycloak/pull/194>`_
'
fragments:
- 189.yaml
- 194.yaml
- 195.yaml
- 196.yaml
- 197.yaml
- 199.yaml
- 201.yaml
- 202.yaml
release_date: '2024-05-01'
2.2.1:
changes:
bugfixes:
- 'JDBC provider: fix clause in argument validation `#204 <https://github.com/ansible-middleware/keycloak/pull/204>`_
'
release_summary: Internal release, documentation or test changes only.
fragments:
- 204.yaml
- v2.2.1-devel_summary.yaml
release_date: '2024-05-02'
2.2.2:
changes:
bugfixes:
- 'Turn off controller privilege escalation `#209 <https://github.com/ansible-middleware/keycloak/pull/209>`_
'
minor_changes:
- 'Copying of key material for TLS configuration `#210 <https://github.com/ansible-middleware/keycloak/pull/210>`_
'
- 'Validate certs parameter for JDBC driver downloads `#207 <https://github.com/ansible-middleware/keycloak/pull/207>`_
'
fragments:
- 207.yaml
- 209.yaml
- 210.yaml
release_date: '2024-05-06'
2.3.0:
changes:
bugfixes:
- '``kc.sh build`` uses configured jdk `#211 <https://github.com/ansible-middleware/keycloak/pull/211>`_
'
major_changes:
- 'Allow for custom providers hosted on maven repositories `#223 <https://github.com/ansible-middleware/keycloak/pull/223>`_
'
- 'Restart handler strategy behaviour `#231 <https://github.com/ansible-middleware/keycloak/pull/231>`_
'
minor_changes:
- 'Add support for policy files `#225 <https://github.com/ansible-middleware/keycloak/pull/225>`_
'
- 'Allow to add extra custom env vars in sysconfig file `#229 <https://github.com/ansible-middleware/keycloak/pull/229>`_
'
- 'Download from alternate URL with optional http authentication `#220 <https://github.com/ansible-middleware/keycloak/pull/220>`_
'
- 'Update Keycloak to version 24.0.4 `#218 <https://github.com/ansible-middleware/keycloak/pull/218>`_
'
- '``proxy-header`` enhancement `#227 <https://github.com/ansible-middleware/keycloak/pull/227>`_
'
fragments:
- 211.yaml
- 218.yaml
- 220.yaml
- 223.yaml
- 225.yaml
- 227.yaml
- 229.yaml
- 231.yaml
release_date: '2024-05-20'
2.4.0:
changes:
major_changes:
- 'Enable by default health check on restart `#234 <https://github.com/ansible-middleware/keycloak/pull/234>`_
'
- 'Update minimum ansible-core version > 2.15 `#232 <https://github.com/ansible-middleware/keycloak/pull/232>`_
'
fragments:
- 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'
2.4.2:
changes:
bugfixes:
- 'Add wait_for_port number parameter `#237 <https://github.com/ansible-middleware/keycloak/pull/237>`_
'
minor_changes:
- 'New parameter ``keycloak_quarkus_download_path`` `#239 <https://github.com/ansible-middleware/keycloak/pull/239>`_
'
fragments:
- 237.yaml
- 239.yaml
release_date: '2024-09-26'
2.4.3:
changes:
minor_changes:
- 'Update keycloak to 24.0.5 `#241 <https://github.com/ansible-middleware/keycloak/pull/241>`_
'
fragments:
- 241.yaml
release_date: '2024-10-16'
3.0.0:
changes:
breaking_changes:
- 'Bump major and ansible-core versions `#266 <https://github.com/ansible-middleware/keycloak/pull/266>`_
'
- 'Rename parameters to follow upstream `#270 <https://github.com/ansible-middleware/keycloak/pull/270>`_
'
- 'Update for keycloak v26 `#254 <https://github.com/ansible-middleware/keycloak/pull/254>`_
'
bugfixes:
- 'Access token lifespan is too short for ansible run `#251 <https://github.com/ansible-middleware/keycloak/pull/251>`_
'
- 'Load environment vars during kc rebuild `#274 <https://github.com/ansible-middleware/keycloak/pull/274>`_
'
- 'Rebuild config and restart service for local providers `#250 <https://github.com/ansible-middleware/keycloak/pull/250>`_
'
- 'Rename and honour parameter ``keycloak_quarkus_http_host`` `#271 <https://github.com/ansible-middleware/keycloak/pull/271>`_
'
minor_changes:
- 'Add theme cache invalidation handler `#252 <https://github.com/ansible-middleware/keycloak/pull/252>`_
'
- 'keycloak_realm: change url variables to defaults `#268 <https://github.com/ansible-middleware/keycloak/pull/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'
3.0.1:
changes:
bugfixes:
- 'Trigger rebuild handler on envvars file change `#276 <https://github.com/ansible-middleware/keycloak/pull/276>`_
'
minor_changes:
- 'Version update to 26.0.8 / rhbk 26.0.11 `#277 <https://github.com/ansible-middleware/keycloak/pull/277>`_
'
fragments:
- 276.yaml
- 277.yaml
release_date: '2025-05-02'
3.0.10:
changes:
major_changes:
- 'Fixing linting issues `#357 <https://github.com/ansible-middleware/keycloak/pull/357>`_
'
minor_changes:
- 'AMW-571 keycloak_identity_provider hide_on_login_page not working `#356 <https://github.com/ansible-middleware/keycloak/pull/356>`_
'
- 'Fixing vars naming issue `#359 <https://github.com/ansible-middleware/keycloak/pull/359>`_
'
- 'Removing from __future__ import annotations broke runtime type hints using
Sequence and union syntax, causing sanity import failures and molecule module
errors `#358 <https://github.com/ansible-middleware/keycloak/pull/358>`_
'
fragments:
- 356.yaml
- 357.yaml
- 358.yaml
- 359.yaml
release_date: '2026-06-25'
3.0.2:
changes:
bugfixes:
- 'Fix ``keycloak_quarkus_force_install`` parameter being ignored by install
`#296 <https://github.com/ansible-middleware/keycloak/pull/296>`_
'
- 'Fix alternate download location being ignored (JBossNeworkAPI always used)
`#298 <https://github.com/ansible-middleware/keycloak/pull/298>`_
'
- 'Run config rebuild after SPI providers update `#285 <https://github.com/ansible-middleware/keycloak/pull/285>`_
'
- 'Use jdk21 as default in debian `#289 <https://github.com/ansible-middleware/keycloak/pull/289>`_
'
- 'keycloak_realm: federation default provider type should be a string `#302
<https://github.com/ansible-middleware/keycloak/pull/302>`_
'
minor_changes:
- 'New ``checksum`` property for keycloak_quarkus_providers `#280 <https://github.com/ansible-middleware/keycloak/pull/280>`_
'
- 'New parameter to set the jgroups host IP address `#281 <https://github.com/ansible-middleware/keycloak/pull/281>`_
'
- 'Session storage / distributed caches `#287 <https://github.com/ansible-middleware/keycloak/pull/287>`_
'
- 'Update keycloak/RHBK to v26.2.4 `#283 <https://github.com/ansible-middleware/keycloak/pull/283>`_
'
fragments:
- 280.yaml
- 281.yaml
- 283.yaml
- 285.yaml
- 287.yaml
- 289.yaml
- 296.yaml
- 298.yaml
- 302.yaml
release_date: '2025-07-01'
3.0.3:
changes:
bugfixes:
- 'keycloak collection CI label is showing no status `#312 <https://github.com/ansible-middleware/keycloak/pull/312>`_
'
- 'keycloak_realm: allow secret in keycloak_clients `#304 <https://github.com/ansible-middleware/keycloak/pull/304>`_
'
major_changes:
- 'Update to keycloak 26.3.0 `#293 <https://github.com/ansible-middleware/keycloak/pull/293>`_
'
- 'ansible-core 2.19 compatibility `#310 <https://github.com/ansible-middleware/keycloak/pull/310>`_
'
minor_changes:
- 'Allow to install provider jars from remote paths `#303 <https://github.com/ansible-middleware/keycloak/pull/303>`_
'
- 'Declared proxy_mode as deprecated, updated quarkus and realm readme `#306
<https://github.com/ansible-middleware/keycloak/pull/306>`_
'
- 'Fix config_key_store_file description to match variable name `#308 <https://github.com/ansible-middleware/keycloak/pull/308>`_
'
fragments:
- 293.yaml
- 303.yaml
- 304.yaml
- 306.yaml
- 308.yaml
- 310.yaml
- 312.yaml
release_date: '2025-12-16'
3.0.4:
changes:
bugfixes:
- 'Removing parseable from lint file as Additional properties are not allowed
`#319 <https://github.com/ansible-middleware/keycloak/pull/319>`_
'
major_changes:
- 'AMW-467 Download keycloak binary from password protected HTTP location `#321
<https://github.com/ansible-middleware/keycloak/pull/321>`_
'
- 'v26.4.x compability `#317 <https://github.com/ansible-middleware/keycloak/pull/317>`_
'
minor_changes:
- 'AMW-518 Validating arguments against arg spec ''main'' fails unexpectedly.
`#324 <https://github.com/ansible-middleware/keycloak/pull/324>`_
'
fragments:
- 317.yaml
- 319.yaml
- 321.yaml
- 324.yaml
release_date: '2026-05-20'
3.0.5:
changes:
minor_changes:
- 'AMW-528 Deployment fails in keycloak_quarkus due to missing escalation variables
`#335 <https://github.com/ansible-middleware/keycloak/pull/335>`_
'
fragments:
- 335.yaml
release_date: '2026-05-20'
3.0.6:
changes:
major_changes:
- 'AMW-540 Fix the upstream collection requirements with common v1.2.4 `#337
<https://github.com/ansible-middleware/keycloak/pull/337>`_
'
fragments:
- 337.yaml
release_date: '2026-05-26'
3.0.7:
changes:
bugfixes:
- 'Fix molecule tests `#339 <https://github.com/ansible-middleware/keycloak/pull/339>`_
'
major_changes:
- 'Migrate Keycloak modules from the community.general collection to Keycloak
collection. `#341 <https://github.com/ansible-middleware/keycloak/pull/341>`_
'
minor_changes:
- 'Fixing common module usage `#343 <https://github.com/ansible-middleware/keycloak/pull/343>`_
'
- 'fix #336: https://github.com/ansible-middleware/common/pull/38 `#338 <https://github.com/ansible-middleware/keycloak/pull/338>`_
'
fragments:
- 338.yaml
- 339.yaml
- 341.yaml
- 343.yaml
release_date: '2026-06-01'
3.0.8:
release_date: '2026-06-09'
3.0.9:
changes:
bugfixes:
- 'AMW-551 Providing correct rhbk version `#344 <https://github.com/ansible-middleware/keycloak/pull/344>`_
'
fragments:
- 344.yaml
release_date: '2026-06-11'

View File

@@ -11,22 +11,22 @@ notesdir: fragments
prelude_section_name: release_summary prelude_section_name: release_summary
prelude_section_title: Release Summary prelude_section_title: Release Summary
sections: sections:
- - major_changes - - major_changes
- Major Changes - Major Changes
- - minor_changes - - minor_changes
- Minor Changes - Minor Changes
- - breaking_changes - - breaking_changes
- Breaking Changes / Porting Guide - Breaking Changes / Porting Guide
- - deprecated_features - - deprecated_features
- Deprecated Features - Deprecated Features
- - removed_features - - removed_features
- Removed Features - Removed Features
- - security_fixes - - security_fixes
- Security Fixes - Security Fixes
- - bugfixes - - bugfixes
- Bugfixes - Bugfixes
- - known_issues - - known_issues
- Known Issues - Known Issues
title: middleware_automation.keycloak title: middleware_automation.keycloak
trivial_section_name: trivial trivial_section_name: trivial
use_fqcn: true use_fqcn: true

View File

@@ -7,7 +7,7 @@
</div> </div>
<hr/> <hr/>
<div role="contentinfo"> <div role="contentinfo">
<p>&#169; Copyright 2024, Red Hat, Inc.</p> <p>&#169; Copyright 2022, Red Hat, Inc.</p>
</div> </div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a> <a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
@@ -18,4 +18,4 @@
</section> </section>
</div> </div>
</body> </body>
</html> </html>

View File

@@ -24,15 +24,14 @@
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu"> <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<p class="caption" role="heading"><span class="caption-text">Middleware Automation</span></p> <p class="caption" role="heading"><span class="caption-text">Middleware Automation</span></p>
<ul> <ul>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/infinispan/main/">Infinispan / Red Hat Data Grid</a></li> <li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/infinispan/">Infinispan / Red Hat Data Grid</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/keycloak/main/">Keycloak / Red Hat Single Sign-On</a></li> <li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/keycloak/">Keycloak / Red Hat Single Sign-On</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/wildfly/main/">Wildfly / Red Hat JBoss EAP</a></li> <li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/wildfly/">Wildfly / Red Hat JBoss EAP</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/jws/main/">Tomcat / Red Hat JWS</a></li> <li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/jws/">Tomcat / Red Hat JWS</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/amq/main/">ActiveMQ / Red Hat AMQ Broker</a></li> <li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/amq/">ActiveMQ / Red Hat AMQ Broker</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/amq_streams/main/">Kafka / Red Hat AMQ Streams</a></li> <li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/amq_streams/">Kafka / Red Hat AMQ Streams</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/common/main/">Ansible Middleware utilities</a></li> <li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/redhat-csp-download/">Red Hat CSP Download</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/redhat-csp-download/main/">Red Hat CSP Download</a></li> <li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/ansible_collections_jcliff/">JCliff</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/ansible_collections_jcliff/main/">JCliff</a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -44,6 +44,7 @@ extensions = [
'sphinx.ext.autodoc', 'sphinx.ext.autodoc',
'sphinx.ext.intersphinx', 'sphinx.ext.intersphinx',
'sphinx_antsibull_ext', 'sphinx_antsibull_ext',
'ansible_basic_sphinx_ext',
] ]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
@@ -63,7 +64,7 @@ master_doc = 'index'
# #
# This is also used if you do content translation via gettext catalogs. # This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases. # Usually you set "language" from the command line for these cases.
language = 'en' language = None
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.

View File

@@ -10,25 +10,30 @@ Welcome to Keycloak Collection documentation
README README
plugins/index plugins/index
roles/index roles/index
Changelog <CHANGELOG>
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
:caption: Developer documentation :caption: Developer documentation
Developing <developing> testing
Testing <testing> developing
Releasing <releasing> releasing
.. toctree::
:maxdepth: 2
:caption: General
Changelog <CHANGELOG>
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
:caption: Middleware collections :caption: Middleware collections
Keycloak / Red Hat Single Sign-On <https://ansible-middleware.github.io/keycloak/main/> Infinispan / Red Hat Data Grid <https://ansible-middleware.github.io/infinispan/>
Infinispan / Red Hat Data Grid <https://ansible-middleware.github.io/infinispan/main/> Keycloak / Red Hat Single Sign-On <https://ansible-middleware.github.io/keycloak/>
Wildfly / Red Hat JBoss EAP <https://ansible-middleware.github.io/wildfly/main/> Wildfly / Red Hat JBoss EAP <https://ansible-middleware.github.io/wildfly/>
Tomcat / Red Hat JWS <https://ansible-middleware.github.io/jws/main/> Tomcat / Red Hat JWS <https://ansible-middleware.github.io/jws/>
ActiveMQ / Red Hat AMQ Broker <https://ansible-middleware.github.io/amq/main/> ActiveMQ / Red Hat AMQ Broker <https://ansible-middleware.github.io/amq/>
Kafka / Red Hat AMQ Streams <https://ansible-middleware.github.io/amq_streams/main/> Kafka / Red Hat AMQ Streams <https://ansible-middleware.github.io/amq_streams/>
Ansible Middleware utilities <https://ansible-middleware.github.io/common/main/> Red Hat CSP Download <https://ansible-middleware.github.io/redhat-csp-download/>
JCliff <https://ansible-middleware.github.io/ansible_collections_jcliff/main/> JCliff <https://ansible-middleware.github.io/ansible_collections_jcliff/>

View File

@@ -1,7 +1,8 @@
antsibull>=0.17.0 antsibull>=0.17.0
antsibull-docs antsibull-docs
antsibull-changelog antsibull-changelog
ansible-core>=2.16.0 ansible-core>=2.14.1
ansible-pygments ansible-pygments
sphinx-rtd-theme sphinx-rtd-theme
git+https://github.com/felixfontein/ansible-basic-sphinx-ext
myst-parser myst-parser

View File

@@ -4,7 +4,24 @@
The collection is tested with a [molecule](https://github.com/ansible-community/molecule) setup covering the included roles and verifying correct installation and idempotency. 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: In order to run the molecule tests locally with python 3.9 available, after cloning the repository:
The test scenarios are available on the source code repository each on his own subdirectory under [molecule/](https://github.com/ansible-middleware/keycloak/molecule).
```
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.
## Test playbooks ## Test playbooks
@@ -12,7 +29,15 @@ The test scenarios are available on the source code repository each on his own s
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: 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 as in developing # 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
# create inventory for localhost # create inventory for localhost
cat << EOF > inventory cat << EOF > inventory
[keycloak] [keycloak]

View File

@@ -1,14 +1,13 @@
--- ---
namespace: middleware_automation namespace: middleware_automation
name: keycloak name: keycloak
version: "3.0.11" version: "2.1.1"
readme: README.md readme: README.md
authors: authors:
- Romain Pelisse <rpelisse@redhat.com> - Romain Pelisse <rpelisse@redhat.com>
- Guido Grazioli <ggraziol@redhat.com> - Guido Grazioli <ggraziol@redhat.com>
- Pavan Kumar Motaparthi <pmotapar@redhat.com> - Pavan Kumar Motaparthi <pmotapar@redhat.com>
- Helmut Wolf <hwo@world-direct.at> - Helmut Wolf <hwo@world-direct.at>
- Harsha Cherukuri <hcheruku@redhat.com>
description: Install and configure a keycloak, or Red Hat Single Sign-on, service. description: Install and configure a keycloak, or Red Hat Single Sign-on, service.
license_file: "LICENSE" license_file: "LICENSE"
tags: tags:
@@ -27,7 +26,7 @@ tags:
- middleware - middleware
- a4mw - a4mw
dependencies: dependencies:
"middleware_automation.common": ">=1.2.1" "middleware_automation.common": ">=1.1.0"
"ansible.posix": ">=1.4.0" "ansible.posix": ">=1.4.0"
repository: https://github.com/ansible-middleware/keycloak repository: https://github.com/ansible-middleware/keycloak
documentation: https://ansible-middleware.github.io/keycloak documentation: https://ansible-middleware.github.io/keycloak
@@ -38,7 +37,6 @@ build_ignore:
- .github - .github
- .ansible-lint - .ansible-lint
- .yamllint - .yamllint
- .DS_Store
- '*.tar.gz' - '*.tar.gz'
- '*.zip' - '*.zip'
- molecule - molecule

View File

@@ -1,62 +1,2 @@
--- ---
requires_ansible: ">=2.16.0" requires_ansible: ">=2.14.0"
action_groups:
keycloak:
- keycloak_authentication
- keycloak_authentication_flow
- keycloak_authentication_required_actions
- keycloak_authentication_v2
- keycloak_authz_authorization_scope
- keycloak_authz_custom_policy
- keycloak_authz_permission
- keycloak_authz_permission_info
- keycloak_client
- keycloak_client_rolemapping
- keycloak_client_rolescope
- keycloak_client_scope
- keycloak_client_scope_type
- keycloak_client_scope_rolemappings
- keycloak_clientsecret_info
- keycloak_clientsecret_regenerate
- keycloak_clienttemplate
- keycloak_component
- keycloak_component_info
- keycloak_group
- keycloak_identity_provider
- keycloak_realm
- keycloak_realm_info
- keycloak_realm_key
- keycloak_realm_keys_metadata_info
- keycloak_realm_localization
- keycloak_realm_rolemapping
- keycloak_role
- keycloak_user
- keycloak_user_federation
- keycloak_user_rolemapping
- keycloak_userprofile
- keycloak_user_execute_actions_email
plugin_routing:
modules:
keycloak_clientscope:
redirect: middleware_automation.keycloak.keycloak_client_scope
deprecation:
removal_version: 5.0.0
warning_text: >-
The module has been renamed to keycloak_client_scope for Keycloak 17+ (Quarkus).
Update playbooks to use middleware_automation.keycloak.keycloak_client_scope.
keycloak_clientscope_type:
redirect: middleware_automation.keycloak.keycloak_client_scope_type
deprecation:
removal_version: 5.0.0
warning_text: >-
The module has been renamed to keycloak_client_scope_type for Keycloak 17+ (Quarkus).
Update playbooks to use middleware_automation.keycloak.keycloak_client_scope_type.
keycloak_clientscope_rolemappings:
redirect: middleware_automation.keycloak.keycloak_client_scope_rolemappings
deprecation:
removal_version: 5.0.0
warning_text: >-
The module has been renamed to keycloak_client_scope_rolemappings for Keycloak 17+ (Quarkus).
Update playbooks to use middleware_automation.keycloak.keycloak_client_scope_rolemappings.

View File

@@ -1,45 +0,0 @@
---
- name: Converge
hosts: all
vars_files:
- ../group_vars/all/vars.yml
vars:
keycloak_quarkus_show_deprecation_warnings: false
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
keycloak_quarkus_bootstrap_admin_user: "remembertochangeme"
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_realm_url: "{{ keycloak_quarkus_hostname }}"
keycloak_realm_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}"
keycloak_realm_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}"
keycloak_realm_client_users:
- username: TestUser
password: password
client_roles:
- client: TestClient
role: TestRoleUser
realm: "{{ keycloak_realm_realm }}"
- username: TestAdmin
password: password
client_roles:
- client: TestClient
role: TestRoleUser
realm: "{{ keycloak_realm_realm }}"
- client: TestClient
role: TestRoleAdmin
realm: "{{ keycloak_realm_realm }}"
keycloak_realm_realm: TestRealm
keycloak_realm_clients:
- name: TestClient
realm: "{{ keycloak_realm_realm }}"
public_client: "{{ keycloak_realm_client_public }}"
web_origins: "{{ keycloak_realm_client_web_origins }}"
users: "{{ keycloak_realm_client_users }}"
client_id: TestClient
attributes:
post.logout.redirect.uris: '/public/logout'

View File

@@ -1,48 +0,0 @@
---
driver:
name: docker
platforms:
- name: instance
image: ghcr.io/hspaans/molecule-containers:debian-13
pre_build_image: true
privileged: true
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "8009/tcp"
cgroupns_mode: host
command: "/lib/systemd/systemd"
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
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: /usr/bin/python3
env:
ANSIBLE_FORCE_COLOR: "true"
ANSIBLE_REMOTE_TMP: /tmp/.ansible/tmp
verifier:
name: ansible
scenario:
test_sequence:
- cleanup
- destroy
- create
- prepare
- converge
- idempotence
- side_effect
- verify
- cleanup
- destroy

View File

@@ -1,13 +0,0 @@
---
- name: Prepare
hosts: all
vars_files:
- ../group_vars/all/vars.yml
gather_facts: yes
tasks:
- name: Install sudo
ansible.builtin.apt:
name:
- sudo
- openjdk-21-jdk-headless
- iproute2

View File

@@ -1,40 +0,0 @@
---
- name: Verify
hosts: all
vars:
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
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"
- name: Verify openid config
block:
- name: Fetch openID config # noqa blocked_modules command-instead-of-module
ansible.builtin.shell: |
set -o pipefail
curl http://localhost:8080/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"] == 'http://localhost:8080/realms/master/protocol/openid-connect/ext/ciba/auth'
- (openid_config.stdout | from_json)['issuer'] == 'http://localhost:8080/realms/master'
- (openid_config.stdout | from_json)['authorization_endpoint'] == 'http://localhost:8080/realms/master/protocol/openid-connect/auth'
- (openid_config.stdout | from_json)['token_endpoint'] == 'http://localhost:8080/realms/master/protocol/openid-connect/token'
delegate_to: localhost
when:
- hera_home is defined
- hera_home | length == 0

View File

@@ -1,48 +1,62 @@
--- ---
- name: Converge - name: Converge
hosts: all hosts: all
vars_files: vars:
- ../group_vars/all/vars.yml keycloak_admin_password: "remembertochangeme"
vars: keycloak_jvm_package: java-11-openjdk-headless
keycloak_quarkus_show_deprecation_warnings: false keycloak_modcluster_enabled: True
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_modcluster_urls:
keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" - host: myhost1
keycloak_quarkus_hostname: http://instance:8080 port: 16667
keycloak_quarkus_log: file - host: myhost2
keycloak_quarkus_log_level: debug port: 16668
keycloak_quarkus_log_target: /tmp/keycloak keycloak_jboss_port_offset: 10
keycloak_quarkus_start_dev: true keycloak_log_target: /tmp/keycloak
keycloak_quarkus_proxy_mode: none
keycloak_quarkus_offline_install: true
keycloak_quarkus_download_path: /tmp/keycloak/
keycloak_quarkus_java_heap_opts: "-Xms640m -Xmx640m "
roles: roles:
- role: keycloak_quarkus - role: keycloak
- role: keycloak_realm tasks:
keycloak_realm_url: "{{ keycloak_quarkus_hostname }}" - name: Keycloak Realm Role
keycloak_realm_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}" ansible.builtin.include_role:
keycloak_realm_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}" name: keycloak_realm
keycloak_realm_client_users: vars:
- username: TestUser keycloak_client_default_roles:
password: password - TestRoleAdmin
client_roles: - TestRoleUser
- client: TestClient keycloak_client_users:
role: TestRoleUser - username: TestUser
realm: "{{ keycloak_realm_realm }}" password: password
- username: TestAdmin client_roles:
password: password - client: TestClient
client_roles: role: TestRoleUser
- client: TestClient realm: "{{ keycloak_realm }}"
role: TestRoleUser - username: TestAdmin
realm: "{{ keycloak_realm_realm }}" password: password
- client: TestClient client_roles:
role: TestRoleAdmin - client: TestClient
realm: "{{ keycloak_realm_realm }}" role: TestRoleUser
keycloak_realm_realm: TestRealm realm: "{{ keycloak_realm }}"
keycloak_realm_clients: - client: TestClient
- name: TestClient role: TestRoleAdmin
realm: "{{ keycloak_realm_realm }}" realm: "{{ keycloak_realm }}"
public_client: "{{ keycloak_realm_client_public }}" keycloak_realm: TestRealm
web_origins: "{{ keycloak_realm_client_web_origins }}" keycloak_clients:
users: "{{ keycloak_realm_client_users }}" - name: TestClient
client_id: 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

View File

@@ -3,15 +3,14 @@ driver:
name: docker name: docker
platforms: platforms:
- name: instance - name: instance
image: registry.access.redhat.com/ubi9/ubi-init:latest image: registry.access.redhat.com/ubi8/ubi-init:latest
pre_build_image: true pre_build_image: true
privileged: true privileged: true
command: "/usr/sbin/init" command: "/usr/sbin/init"
exposed_ports: port_bindings:
- "8080/tcp" - "8080/tcp"
- "8443/tcp" - "8443/tcp"
- "8009/tcp" - "8009/tcp"
- "9000/tcp"
provisioner: provisioner:
name: ansible name: ansible
config_options: config_options:
@@ -24,16 +23,11 @@ provisioner:
converge: converge.yml converge: converge.yml
verify: verify.yml verify: verify.yml
inventory: inventory:
group_vars:
all:
keycloak_install_requires_become: true
host_vars: host_vars:
localhost: localhost:
ansible_python_interpreter: "{{ ansible_playbook_python }}" ansible_python_interpreter: "{{ ansible_playbook_python }}"
env: env:
ANSIBLE_FORCE_COLOR: "true" ANSIBLE_FORCE_COLOR: "true"
PROXY: "${PROXY}"
NO_PROXY: "${NO_PROXY}"
verifier: verifier:
name: ansible name: ansible
scenario: scenario:

View File

@@ -1,70 +1,22 @@
--- ---
- name: Prepare - name: Prepare
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
gather_facts: yes gather_facts: yes
vars: vars:
sudo_pkg_name: sudo sudo_pkg_name: sudo
tasks: tasks:
- name: "Run preparation common to all scenario" - name: "Run preparation common to all scenario"
ansible.builtin.include_tasks: ../prepare.yml 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 - name: Install JDK8
ansible.builtin.file: # noqa risky-file-permissions delegated, uses controller host user become: yes
path: /tmp/keycloak ansible.builtin.yum:
state: directory name:
mode: '0750' - java-1.8.0-openjdk
delegate_to: localhost state: present
run_once: true
- 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.6.2/keycloak-26.6.2.zip
dest: /tmp/keycloak
mode: '0640'
validate_certs: false
delegate_to: localhost
run_once: true
ignore_errors: true
- name: Attempt RHBK download using redhat.runtimes_common collection
when:
- rhn_username is defined
- rhn_username | length > 0
block:
- name: Retrieve RHBK product download using Unified Downloads API
middleware_automation.common.product_search:
client_id: "{{ rhn_username }}"
client_secret: "{{ rhn_password }}"
product_type: DISTRIBUTION
product_version: "{{ keycloak_quarkus_version | default('26.6.2') }}"
product_category: "RHBK"
register: rhn_products
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
run_once: true
ignore_errors: true
- name: Determine install zipfile from search results
ansible.builtin.set_fact:
rhn_matched_products: "{{ rhn_products.results | selectattr('file_name', 'match', '.*keycloak-' + (keycloak_quarkus_version | default('26.6.2')) + '.zip$') }}"
delegate_to: localhost
run_once: true
when:
- rhn_products is defined
- rhn_products.results is defined
- name: Download Red Hat Build of Keycloak
middleware_automation.common.product_download:
client_id: "{{ rhn_username }}"
client_secret: "{{ rhn_password }}"
product_id: "{{ (rhn_matched_products | first).id }}"
dest: "/tmp/keycloak/keycloak-{{ keycloak_quarkus_version | default('26.6.2') }}.zip"
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
run_once: true
when:
- rhn_matched_products is defined
- rhn_matched_products | length > 0
ignore_errors: true

View File

@@ -2,9 +2,11 @@
- name: Verify - name: Verify
hosts: all hosts: all
vars: vars:
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_admin_password: "remembertochangeme"
keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_jvm_package: java-11-openjdk-headless
keycloak_uri: "http://localhost:8080" 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
tasks: tasks:
- name: Populate service facts - name: Populate service facts
ansible.builtin.service_facts: ansible.builtin.service_facts:
@@ -13,13 +15,75 @@
that: that:
- ansible_facts.services["keycloak.service"]["state"] == "running" - ansible_facts.services["keycloak.service"]["state"] == "running"
- ansible_facts.services["keycloak.service"]["status"] == "enabled" - 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 - name: Verify token api call
ansible.builtin.uri: ansible.builtin.uri:
url: "{{ keycloak_uri }}/realms/master/protocol/openid-connect/token" url: "{{ keycloak_uri }}/auth/realms/master/protocol/openid-connect/token"
method: POST method: POST
body: "client_id=admin-cli&username={{ keycloak_quarkus_bootstrap_admin_user }}&password={{ keycloak_quarkus_bootstrap_admin_user }}&grant_type=password" body: "client_id=admin-cli&username=admin&password={{ keycloak_admin_password }}&grant_type=password"
validate_certs: no validate_certs: no
register: keycloak_auth_response register: keycloak_auth_response
until: keycloak_auth_response.status == 200 until: keycloak_auth_response.status == 200
retries: 2 retries: 2
delay: 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

View File

@@ -1,31 +0,0 @@
---
keycloak_download_validate_certs: false
keycloak_validate_certs: false
keycloak_quarkus_download_validate_certs: false
keycloak_quarkus_validate_certs: false
keycloak_realm_validate_certs: false
keycloak_quarkus_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_systemd_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_install_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_firewalld_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_iptables_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_jdbc_driver_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_config_store_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_restart_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_start_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_rebuild_config_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_fastpackages_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_bootstrapped_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_invalidate_theme_cache_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_systemd_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_install_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_firewalld_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_iptables_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_jdbc_driver_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_restart_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_start_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_stop_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_fastpackages_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_rhsso_patch_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
molecule_prepare_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"

View File

@@ -1,17 +1,16 @@
--- ---
- name: Converge - name: Converge
hosts: all hosts: all
vars_files: vars:
- ../group_vars/all/vars.yml keycloak_quarkus_admin_pass: "remembertochangeme"
vars: keycloak_admin_password: "remembertochangeme"
keycloak_quarkus_show_deprecation_warnings: false keycloak_realm: TestRealm
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_host: instance
keycloak_quarkus_bootstrap_admin_user: "remembertochangeme"
keycloak_quarkus_hostname: "http://proxy:{{ keycloak_quarkus_http_port }}"
keycloak_quarkus_log: file keycloak_quarkus_log: file
keycloak_quarkus_http_enabled: True keycloak_quarkus_http_enabled: True
keycloak_quarkus_http_port: 8080 keycloak_quarkus_http_port: 8080
keycloak_quarkus_proxy_mode: edge keycloak_quarkus_proxy_mode: edge
keycloak_quarkus_http_relative_path: / keycloak_quarkus_http_relative_path: /
keycloak_quarkus_frontend_url: https://proxy/
roles: roles:
- role: keycloak_quarkus - role: keycloak_quarkus

View File

@@ -3,24 +3,24 @@ driver:
name: docker name: docker
platforms: platforms:
- name: instance - name: instance
image: registry.access.redhat.com/ubi9/ubi-init:latest image: registry.access.redhat.com/ubi8/ubi-init:latest
pre_build_image: true pre_build_image: true
privileged: true privileged: true
command: "/usr/sbin/init" command: "/usr/sbin/init"
networks: networks:
- name: keycloak - name: keycloak
exposed_ports: port_bindings:
- "8080/tcp" - "8080/tcp"
published_ports: published_ports:
- 0.0.0.0:8080:8080/tcp - 0.0.0.0:8080:8080/tcp
- name: proxy - name: proxy
image: registry.access.redhat.com/ubi9/ubi-init:latest image: registry.access.redhat.com/ubi8/ubi-init:latest
pre_build_image: true pre_build_image: true
privileged: true privileged: true
command: "/usr/sbin/init" command: "/usr/sbin/init"
networks: networks:
- name: keycloak - name: keycloak
exposed_ports: port_bindings:
- "443/tcp" - "443/tcp"
published_ports: published_ports:
- 0.0.0.0:443:443/tcp - 0.0.0.0:443:443/tcp

View File

@@ -1,8 +1,6 @@
--- ---
- name: Prepare - name: Prepare
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
tasks: tasks:
- name: Install sudo - name: Install sudo
ansible.builtin.dnf: ansible.builtin.dnf:
@@ -29,8 +27,6 @@
pre_tasks: pre_tasks:
- name: Create certificate request - 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=proxy' ansible.builtin.command: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=proxy'
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost delegate_to: localhost
changed_when: false changed_when: false
- name: Make certificate directory - name: Make certificate directory
@@ -43,11 +39,11 @@
src: "{{ item.name }}" src: "{{ item.name }}"
dest: "{{ item.dest }}" dest: "{{ item.dest }}"
mode: 0444 mode: 0444
become: "{{ molecule_prepare_require_privilege_escalation | default(true) }}" become: true
loop: loop:
- { name: 'cert.pem', dest: '/etc/nginx/tls/certificate.crt' } - { name: 'cert.pem', dest: '/etc/nginx/tls/certificate.crt' }
- { name: 'key.pem', dest: '/etc/nginx/tls/certificate.key' } - { name: 'key.pem', dest: '/etc/nginx/tls/certificate.key' }
- name: Update CA trust - name: Update CA trust
ansible.builtin.command: update-ca-trust ansible.builtin.command: update-ca-trust
changed_when: false changed_when: false
become: "{{ molecule_prepare_require_privilege_escalation | default(true) }}" become: true

View File

@@ -1,2 +0,0 @@
---
- import_playbook: ../default/converge.yml

View File

@@ -1,51 +0,0 @@
---
driver:
name: docker
platforms:
- name: instance
image: registry.access.redhat.com/ubi9/ubi-init:latest
pre_build_image: true
privileged: true
command: "/usr/sbin/init"
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "8009/tcp"
- "9000/tcp"
provisioner:
name: ansible
config_options:
defaults:
interpreter_python: auto_silent
roles_path: ../../roles
ssh_connection:
pipelining: false
playbooks:
prepare: prepare.yml
converge: converge.yml
verify: verify.yml
inventory:
group_vars:
all:
keycloak_install_requires_become: true
host_vars:
localhost:
ansible_python_interpreter: "{{ ansible_playbook_python }}"
env:
ANSIBLE_FORCE_COLOR: "true"
PROXY: "${PROXY}"
NO_PROXY: "${NO_PROXY}"
verifier:
name: ansible
scenario:
test_sequence:
- cleanup
- destroy
- create
- prepare
- converge
- idempotence
- side_effect
- verify
- cleanup
- destroy

View File

@@ -1,2 +0,0 @@
---
- import_playbook: ../default/prepare.yml

View File

@@ -1,705 +0,0 @@
---
- name: Verify migrated Keycloak modules
hosts: all
vars:
auth_keycloak_url: http://instance:8080
auth_realm: master
auth_username: remembertochangeme
auth_password: remembertochangeme
target_realm: TestRealm
role: molecule-role
client_role: molecule-client-role
client: molecule-client
scope: molecule-scope
group: molecule-group
user: molecule-user
flow: molecule-flow
flow_v2: molecule-flow-v2
auth_copy: molecule-auth-copy
idp: molecule-idp
ephemeral_realm: molecule-realm
template: molecule-template
realm_key: molecule-aes-key
authz_scope: molecule-authz-scope
authz_permission: molecule-authz-perm
federation: molecule-federation
component: molecule-component
migrated_keycloak_modules:
- keycloak_authentication
- keycloak_authentication_flow
- keycloak_authentication_required_actions
- keycloak_authentication_v2
- keycloak_authz_authorization_scope
- keycloak_authz_custom_policy
- keycloak_authz_permission
- keycloak_authz_permission_info
- keycloak_client
- keycloak_client_rolemapping
- keycloak_client_rolescope
- keycloak_client_scope
- keycloak_client_scope_type
- keycloak_client_scope_rolemappings
- keycloak_clientsecret_info
- keycloak_clientsecret_regenerate
- keycloak_clienttemplate
- keycloak_component
- keycloak_component_info
- keycloak_group
- keycloak_identity_provider
- keycloak_realm
- keycloak_realm_info
- keycloak_realm_key
- keycloak_realm_keys_metadata_info
- keycloak_realm_localization
- keycloak_realm_rolemapping
- keycloak_role
- keycloak_user
- keycloak_user_execute_actions_email
- keycloak_user_federation
- keycloak_user_rolemapping
- keycloak_userprofile
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: Verify migrated modules are discoverable by ansible-doc # noqa command-instead-of-module
ansible.builtin.command:
cmd: "ansible-doc -t module middleware_automation.keycloak.{{ item }}"
loop: "{{ migrated_keycloak_modules }}"
delegate_to: localhost
register: docs_check
changed_when: false
- name: Ensure module docs check succeeded
ansible.builtin.assert:
that:
- docs_check is not failed
- name: Provision shared test fixtures
module_defaults:
group/middleware_automation.keycloak.keycloak:
auth_keycloak_url: "{{ auth_keycloak_url }}"
auth_realm: "{{ auth_realm }}"
auth_username: "{{ auth_username }}"
auth_password: "{{ auth_password }}"
validate_certs: false
block:
- name: Reset fixtures from a previous verify run
middleware_automation.keycloak.keycloak_user:
realm: "{{ target_realm }}"
username: "{{ user }}"
state: absent
failed_when: false
- name: keycloak_role — create realm role for module tests
middleware_automation.keycloak.keycloak_role:
realm: "{{ target_realm }}"
name: "{{ role }}"
state: present
- name: keycloak_client — create confidential client for module tests
middleware_automation.keycloak.keycloak_client:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
name: "{{ client }}"
enabled: true
public_client: false
standard_flow_enabled: true
client_authenticator_type: client-secret
secret: molecule-client-secret
state: present
- name: keycloak_client — enable authorization services on test client
middleware_automation.keycloak.keycloak_client:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
service_accounts_enabled: true
authorization_services_enabled: true
full_scope_allowed: false
state: present
- name: keycloak_role — create client role for rolemapping tests
middleware_automation.keycloak.keycloak_role:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
name: "{{ client_role }}"
state: present
- name: keycloak_client_scope — create client scope
middleware_automation.keycloak.keycloak_client_scope:
realm: "{{ target_realm }}"
name: "{{ scope }}"
state: present
- name: keycloak_group — create group
middleware_automation.keycloak.keycloak_group:
realm: "{{ target_realm }}"
name: "{{ group }}"
state: present
- name: keycloak_user — create user
middleware_automation.keycloak.keycloak_user:
realm: "{{ target_realm }}"
username: "{{ user }}"
first_name: Molecule
last_name: User
email: molecule-user@example.invalid
enabled: true
state: present
- name: keycloak_authentication_flow — create browser-style flow
middleware_automation.keycloak.keycloak_authentication_flow:
realm: "{{ target_realm }}"
alias: "{{ flow }}"
description: Molecule module test authentication flow
executions:
- provider_id: auth-cookie
requirement: REQUIRED
state: present
- name: keycloak_realm_info — query TestRealm (public endpoint, no admin auth)
middleware_automation.keycloak.keycloak_realm_info:
auth_keycloak_url: "{{ auth_keycloak_url }}"
realm: "{{ target_realm }}"
validate_certs: false
- name: Exercise migrated modules against running Keycloak
module_defaults:
group/middleware_automation.keycloak.keycloak:
auth_keycloak_url: "{{ auth_keycloak_url }}"
auth_realm: "{{ auth_realm }}"
auth_username: "{{ auth_username }}"
auth_password: "{{ auth_password }}"
validate_certs: false
block:
- name: keycloak_realm_keys_metadata_info — query realm keys
middleware_automation.keycloak.keycloak_realm_keys_metadata_info:
realm: "{{ target_realm }}"
- name: keycloak_component_info — list realm components
middleware_automation.keycloak.keycloak_component_info:
realm: "{{ target_realm }}"
- name: keycloak_realm — create ephemeral realm
middleware_automation.keycloak.keycloak_realm:
id: "{{ ephemeral_realm }}"
realm: "{{ ephemeral_realm }}"
enabled: true
state: present
- name: keycloak_realm_localization — set locale override
middleware_automation.keycloak.keycloak_realm_localization:
parent_id: "{{ target_realm }}"
locale: en
state: present
overrides:
- key: molecule.module.test
value: molecule module test
- name: keycloak_realm_key — create generated AES key component
middleware_automation.keycloak.keycloak_realm_key:
parent_id: "{{ target_realm }}"
name: "{{ realm_key }}"
provider_id: aes-generated
state: present
- name: keycloak_authentication — copy browser flow
middleware_automation.keycloak.keycloak_authentication:
realm: "{{ target_realm }}"
alias: "{{ auth_copy }}"
copyFrom: browser
state: present
- name: keycloak_authentication_v2 — manage flow with safe-swap semantics
middleware_automation.keycloak.keycloak_authentication_v2:
realm: "{{ target_realm }}"
alias: "{{ flow_v2 }}"
description: Molecule module test flow v2
authenticationExecutions:
- requirement: REQUIRED
providerId: auth-cookie
state: present
- name: keycloak_authentication_required_actions — ensure VERIFY_EMAIL is enabled
middleware_automation.keycloak.keycloak_authentication_required_actions:
realm: "{{ target_realm }}"
state: present
required_actions:
- alias: VERIFY_EMAIL
enabled: true
- name: keycloak_userprofile — set unmanaged attribute policy
middleware_automation.keycloak.keycloak_userprofile:
parent_id: "{{ target_realm }}"
state: present
config:
kc_user_profile_config:
- unmanagedAttributePolicy: ENABLED
- name: keycloak_identity_provider — create OIDC broker stub
middleware_automation.keycloak.keycloak_identity_provider:
realm: "{{ target_realm }}"
alias: "{{ idp }}"
provider_id: oidc
enabled: false
config:
authorizationUrl: http://localhost:8080/realms/master/protocol/openid-connect/auth
tokenUrl: http://localhost:8080/realms/master/protocol/openid-connect/token
clientId: molecule-idp-client
state: present
- name: keycloak_identity_provider — set hide_on_login via top-level param
middleware_automation.keycloak.keycloak_identity_provider:
realm: "{{ target_realm }}"
alias: "{{ idp }}"
hide_on_login: true
state: present
register: idp_hide_on_login_result
- name: Assert hide_on_login is set correctly in end_state
ansible.builtin.assert:
that:
- idp_hide_on_login_result is changed
- idp_hide_on_login_result.end_state.hideOnLogin == true
- "'hide_on_login' not in (idp_hide_on_login_result.end_state.config | default({}))"
- name: keycloak_identity_provider — set hide_on_login idempotency check
middleware_automation.keycloak.keycloak_identity_provider:
realm: "{{ target_realm }}"
alias: "{{ idp }}"
hide_on_login: true
state: present
register: idp_hide_on_login_idempotent_result
- name: Assert hide_on_login idempotency (no change)
ansible.builtin.assert:
that:
- idp_hide_on_login_idempotent_result is not changed
- idp_hide_on_login_idempotent_result.end_state.hideOnLogin == true
- name: keycloak_identity_provider — set hide_on_login via legacy config key (backward compat)
middleware_automation.keycloak.keycloak_identity_provider:
realm: "{{ target_realm }}"
alias: "{{ idp }}"
hide_on_login: false
state: present
register: idp_hide_on_login_off_result
- name: Assert hide_on_login can be toggled off via top-level param
ansible.builtin.assert:
that:
- idp_hide_on_login_off_result is changed
- idp_hide_on_login_off_result.end_state.hideOnLogin == false
- "'hide_on_login_page' not in (idp_hide_on_login_off_result.end_state.config | default({}))"
- name: keycloak_clienttemplate — create client template
middleware_automation.keycloak.keycloak_clienttemplate:
realm: "{{ target_realm }}"
name: "{{ template }}"
protocol: openid-connect
state: present
register: clienttemplate_result
failed_when:
- clienttemplate_result is failed
- "'404' not in (clienttemplate_result.msg | default(''))"
- "'Not Found' not in (clienttemplate_result.msg | default(''))"
- name: keycloak_client_scope_type — attach scope as optional on realm
middleware_automation.keycloak.keycloak_client_scope_type:
realm: "{{ target_realm }}"
optional_client_scopes:
- "{{ scope }}"
- name: keycloak_user_rolemapping — assign realm role to user
middleware_automation.keycloak.keycloak_user_rolemapping:
realm: "{{ target_realm }}"
target_username: "{{ user }}"
state: present
roles:
- name: "{{ role }}"
- name: keycloak_realm_rolemapping — assign realm role to group
middleware_automation.keycloak.keycloak_realm_rolemapping:
realm: "{{ target_realm }}"
group_name: "{{ group }}"
state: present
roles:
- name: "{{ role }}"
- name: keycloak_client_rolemapping — assign client role to group
middleware_automation.keycloak.keycloak_client_rolemapping:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
group_name: "{{ group }}"
state: present
roles:
- name: "{{ client_role }}"
- name: keycloak_client_rolescope — restrict realm role on client
middleware_automation.keycloak.keycloak_client_rolescope:
realm: "{{ target_realm }}"
target_client_id: "{{ client }}"
role_names:
- "{{ role }}"
state: present
- name: keycloak_client_scope_rolemappings — map client roles to client scope
middleware_automation.keycloak.keycloak_client_scope_rolemappings:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
client_scope_id: "{{ scope }}"
role_names:
- "{{ client_role }}"
register: client_scope_rolemappings_result
- name: Assert client scope role mappings were created
ansible.builtin.assert:
that:
- client_scope_rolemappings_result is changed
- client_scope_rolemappings_result.end_state | length == 1
- name: keycloak_client_scope_rolemappings — remap client role (idempotency)
middleware_automation.keycloak.keycloak_client_scope_rolemappings:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
client_scope_id: "{{ scope }}"
role_names:
- "{{ client_role }}"
register: client_scope_rolemappings_idempotent_result
- name: Assert client scope role mappings are idempotent
ansible.builtin.assert:
that:
- client_scope_rolemappings_idempotent_result is not changed
- client_scope_rolemappings_idempotent_result.end_state | length == 1
- name: keycloak_client_scope_rolemappings — map realm role to client scope
middleware_automation.keycloak.keycloak_client_scope_rolemappings:
realm: "{{ target_realm }}"
client_scope_id: "{{ scope }}"
role_names:
- "{{ role }}"
register: client_scope_realm_rolemappings_result
- name: Assert realm role was mapped to client_scope
ansible.builtin.assert:
that:
- client_scope_realm_rolemappings_result is changed
- client_scope_realm_rolemappings_result.end_state | length == 1
- name: keycloak_user — set email_verified explicitly
middleware_automation.keycloak.keycloak_user:
realm: "{{ target_realm }}"
username: "{{ user }}"
email_verified: true
state: present
register: user_email_verified_result
- name: Assert email_verified was set
ansible.builtin.assert:
that:
- user_email_verified_result is changed
- user_email_verified_result.end_state.emailVerified == true
- name: keycloak_user — leave email_verified unchanged with no_defaults
middleware_automation.keycloak.keycloak_user:
realm: "{{ target_realm }}"
username: "{{ user }}"
email_verified_behavior: no_defaults
state: present
register: user_email_verified_idempotent_result
- name: Assert email_verified is unchanged
ansible.builtin.assert:
that:
- user_email_verified_idempotent_result is not changed
- user_email_verified_idempotent_result.end_state.emailVerified == true
- name: keycloak_user — set required actions
middleware_automation.keycloak.keycloak_user:
realm: "{{ target_realm }}"
username: "{{ user }}"
required_actions:
- UPDATE_PASSWORD
- VERIFY_EMAIL
state: present
register: user_required_actions_result
- name: Assert required actions were set
ansible.builtin.assert:
that:
- user_required_actions_result is changed
- "'UPDATE_PASSWORD' in user_required_actions_result.end_state.requiredActions"
- "'VERIFY_EMAIL' in user_required_actions_result.end_state.requiredActions"
- name: keycloak_user — leave required actions unchanged when omitted
middleware_automation.keycloak.keycloak_user:
realm: "{{ target_realm }}"
username: "{{ user }}"
state: present
register: user_required_actions_idempotent_result
- name: Assert required actions are unchanged
ansible.builtin.assert:
that:
- user_required_actions_idempotent_result is not changed
- "'UPDATE_PASSWORD' in user_required_actions_idempotent_result.end_state.requiredActions"
- "'VERIFY_EMAIL' in user_required_actions_idempotent_result.end_state.requiredActions"
- name: keycloak_clientsecret_info — read client secret
middleware_automation.keycloak.keycloak_clientsecret_info:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
no_log: true
- name: keycloak_clientsecret_regenerate — rotate client secret
middleware_automation.keycloak.keycloak_clientsecret_regenerate:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
no_log: true
- name: keycloak_authz_authorization_scope — create authorization scope
middleware_automation.keycloak.keycloak_authz_authorization_scope:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
name: "{{ authz_scope }}"
display_name: Molecule module test scope
state: present
- name: keycloak_authz_permission — create scope permission
middleware_automation.keycloak.keycloak_authz_permission:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
name: "{{ authz_permission }}"
permission_type: scope
scopes:
- "{{ authz_scope }}"
state: present
- name: keycloak_authz_permission_info — query scope permission
middleware_automation.keycloak.keycloak_authz_permission_info:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
name: "{{ authz_permission }}"
- name: keycloak_authz_custom_policy — requires deployed policy provider on server
middleware_automation.keycloak.keycloak_authz_custom_policy:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
name: molecule-script-policy
policy_type: script-policy.js
state: present
register: authz_custom_policy_result
failed_when:
- authz_custom_policy_result is failed
- "'No policy provider' not in (authz_custom_policy_result.msg | default(''))"
- "'Policy provider' not in (authz_custom_policy_result.msg | default(''))"
- "'405' not in (authz_custom_policy_result.msg | default(''))"
- "'Method Not Allowed' not in (authz_custom_policy_result.msg | default(''))"
- "'not found' not in (authz_custom_policy_result.msg | default('') | lower)"
- name: keycloak_user_execute_actions_email — trigger execute-actions email
middleware_automation.keycloak.keycloak_user_execute_actions_email:
realm: "{{ target_realm }}"
username: "{{ user }}"
actions:
- VERIFY_EMAIL
register: execute_actions_email_result
failed_when:
- execute_actions_email_result is failed
- "'Connection refused' in (execute_actions_email_result.msg | default(''))"
- "'Failed to send' in (execute_actions_email_result.msg | default(''))"
- name: keycloak_user_federation — remove non-existent federation (module API test)
middleware_automation.keycloak.keycloak_user_federation:
realm: "{{ target_realm }}"
name: "{{ federation }}"
state: absent
- name: keycloak_component — remove non-existent component (module API test)
middleware_automation.keycloak.keycloak_component:
parent_id: "{{ target_realm }}"
name: "{{ component }}"
provider_id: ldap
provider_type: org.keycloak.storage.UserStorageProvider
state: absent
- name: Remove shared test fixtures
module_defaults:
group/middleware_automation.keycloak.keycloak:
auth_keycloak_url: "{{ auth_keycloak_url }}"
auth_realm: "{{ auth_realm }}"
auth_username: "{{ auth_username }}"
auth_password: "{{ auth_password }}"
validate_certs: false
block:
- name: keycloak_authz_custom_policy — remove custom policy if created
middleware_automation.keycloak.keycloak_authz_custom_policy:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
name: molecule-script-policy
policy_type: script-policy.js
state: absent
failed_when: false
- name: keycloak_authz_permission — remove scope permission
middleware_automation.keycloak.keycloak_authz_permission:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
name: "{{ authz_permission }}"
permission_type: scope
state: absent
- name: keycloak_authz_authorization_scope — remove authorization scope
middleware_automation.keycloak.keycloak_authz_authorization_scope:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
name: "{{ authz_scope }}"
state: absent
- name: keycloak_client_scope_rolemappings — remove realm role from client scope
middleware_automation.keycloak.keycloak_client_scope_rolemappings:
realm: "{{ target_realm }}"
client_scope_id: "{{ scope }}"
role_names:
- "{{ role }}"
state: absent
- name: keycloak_client_scope_rolemappings — remove client role from client scope
middleware_automation.keycloak.keycloak_client_scope_rolemappings:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
client_scope_id: "{{ scope }}"
role_names:
- "{{ client_role }}"
state: absent
- name: keycloak_client_rolescope — remove role scope mapping
middleware_automation.keycloak.keycloak_client_rolescope:
realm: "{{ target_realm }}"
target_client_id: "{{ client }}"
role_names:
- "{{ role }}"
state: absent
- name: keycloak_client_rolemapping — remove group client role mapping
middleware_automation.keycloak.keycloak_client_rolemapping:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
group_name: "{{ group }}"
state: absent
roles:
- name: "{{ client_role }}"
- name: keycloak_realm_rolemapping — remove group realm role mapping
middleware_automation.keycloak.keycloak_realm_rolemapping:
realm: "{{ target_realm }}"
group_name: "{{ group }}"
state: absent
roles:
- name: "{{ role }}"
- name: keycloak_user_rolemapping — remove user role mapping
middleware_automation.keycloak.keycloak_user_rolemapping:
realm: "{{ target_realm }}"
target_username: "{{ user }}"
state: absent
roles:
- name: "{{ role }}"
- name: keycloak_identity_provider — remove OIDC provider
middleware_automation.keycloak.keycloak_identity_provider:
realm: "{{ target_realm }}"
alias: "{{ idp }}"
state: absent
- name: keycloak_authentication_v2 — remove flow
middleware_automation.keycloak.keycloak_authentication_v2:
realm: "{{ target_realm }}"
alias: "{{ flow_v2 }}"
state: absent
- name: keycloak_authentication — remove copied flow
middleware_automation.keycloak.keycloak_authentication:
realm: "{{ target_realm }}"
alias: "{{ auth_copy }}"
state: absent
- name: keycloak_realm_key — remove generated key
middleware_automation.keycloak.keycloak_realm_key:
parent_id: "{{ target_realm }}"
name: "{{ realm_key }}"
provider_id: aes-generated
state: absent
- name: keycloak_realm_localization — remove locale override
middleware_automation.keycloak.keycloak_realm_localization:
parent_id: "{{ target_realm }}"
locale: en
state: absent
overrides:
- key: molecule.module.test
- name: keycloak_realm — remove ephemeral realm
middleware_automation.keycloak.keycloak_realm:
id: "{{ ephemeral_realm }}"
realm: "{{ ephemeral_realm }}"
state: absent
- name: keycloak_clienttemplate — remove client template
middleware_automation.keycloak.keycloak_clienttemplate:
realm: "{{ target_realm }}"
name: "{{ template }}"
state: absent
failed_when: false
- name: keycloak_authentication_flow — remove authentication flow
middleware_automation.keycloak.keycloak_authentication_flow:
realm: "{{ target_realm }}"
alias: "{{ flow }}"
state: absent
- name: keycloak_user — remove user
middleware_automation.keycloak.keycloak_user:
realm: "{{ target_realm }}"
username: "{{ user }}"
state: absent
- name: keycloak_group — remove group
middleware_automation.keycloak.keycloak_group:
realm: "{{ target_realm }}"
name: "{{ group }}"
state: absent
- name: keycloak_role — remove client role
middleware_automation.keycloak.keycloak_role:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
name: "{{ client_role }}"
state: absent
- name: keycloak_client — remove confidential client
middleware_automation.keycloak.keycloak_client:
realm: "{{ target_realm }}"
client_id: "{{ client }}"
state: absent
- name: keycloak_client_scope — remove client scope
middleware_automation.keycloak.keycloak_client_scope:
realm: "{{ target_realm }}"
name: "{{ scope }}"
state: absent
- name: keycloak_role — remove realm role
middleware_automation.keycloak.keycloak_role:
realm: "{{ target_realm }}"
name: "{{ role }}"
state: absent

View File

@@ -1,15 +1,55 @@
--- ---
- name: Converge - name: Converge
hosts: all hosts: all
vars_files: vars:
- ../group_vars/all/vars.yml
vars:
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
keycloak_admin_password: "remembertochangeme" keycloak_admin_password: "remembertochangeme"
keycloak_quarkus_hostname: "http://instance:8080"
keycloak_config_override_template: custom.xml.j2 keycloak_config_override_template: custom.xml.j2
keycloak_http_port: 8081 keycloak_http_port: 8081
keycloak_management_http_port: 19990 keycloak_management_http_port: 19990
keycloak_service_runas: True keycloak_service_runas: True
roles: roles:
- role: keycloak - 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
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

View File

@@ -3,15 +3,14 @@ driver:
name: docker name: docker
platforms: platforms:
- name: instance - name: instance
image: registry.access.redhat.com/ubi9/ubi-init:latest image: registry.access.redhat.com/ubi8/ubi-init:latest
pre_build_image: true pre_build_image: true
privileged: true privileged: true
command: "/usr/sbin/init" command: "/usr/sbin/init"
exposed_ports: port_bindings:
- "8080/tcp" - "8080/tcp"
- "8443/tcp" - "8443/tcp"
- "8009/tcp" - "8009/tcp"
- "9000/tcp"
provisioner: provisioner:
name: ansible name: ansible
config_options: config_options:

View File

@@ -1,8 +1,6 @@
--- ---
- name: Prepare - name: Prepare
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
gather_facts: yes gather_facts: yes
vars: vars:
sudo_pkg_name: sudo sudo_pkg_name: sudo

View File

@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<!-- this is a custom file --> <!-- {{ ansible_managed }} -->
<server xmlns="urn:jboss:domain:16.0"> <server xmlns="urn:jboss:domain:16.0">
<extensions> <extensions>
<extension module="org.jboss.as.clustering.infinispan"/> <extension module="org.jboss.as.clustering.infinispan"/>
@@ -44,7 +44,7 @@
</audit-log> </audit-log>
<management-interfaces> <management-interfaces>
<http-interface http-authentication-factory="management-http-authentication"> <http-interface http-authentication-factory="management-http-authentication">
<http-upgrade enabled="true" sasl-authentication-factory="management-sasl-authentication"/> <http-upgrade enabled="true"/>
<socket-binding http="management-http"/> <socket-binding http="management-http"/>
</http-interface> </http-interface>
</management-interfaces> </management-interfaces>
@@ -481,8 +481,8 @@
<default-provider>default</default-provider> <default-provider>default</default-provider>
<provider name="default" enabled="true"> <provider name="default" enabled="true">
<properties> <properties>
<property name="frontendUrl" value="${keycloak.frontendUrl:}"/> <property name="frontendUrl" value="{{ keycloak_modcluster.frontend_url }}"/>
<property name="forceBackendUrlToFrontendUrl" value="false"/> <property name="forceBackendUrlToFrontendUrl" value="true"/>
</properties> </properties>
</provider> </provider>
</spi> </spi>
@@ -520,8 +520,7 @@
<subsystem xmlns="urn:jboss:domain:undertow:12.0" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other" statistics-enabled="${wildfly.undertow.statistics-enabled:${wildfly.statistics-enabled:false}}"> <subsystem xmlns="urn:jboss:domain:undertow:12.0" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other" statistics-enabled="${wildfly.undertow.statistics-enabled:${wildfly.statistics-enabled:false}}">
<buffer-cache name="default"/> <buffer-cache name="default"/>
<server name="default-server"> <server name="default-server">
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/> <http-listener name="default" socket-binding="http"/>
<https-listener name="https" socket-binding="https" ssl-context="applicationSSC" enable-http2="true"/>
<host name="default-host" alias="localhost"> <host name="default-host" alias="localhost">
<location name="/" handler="welcome-content"/> <location name="/" handler="welcome-content"/>
<http-invoker http-authentication-factory="application-http-authentication"/> <http-invoker http-authentication-factory="application-http-authentication"/>
@@ -534,25 +533,20 @@
<handlers> <handlers>
<file name="welcome-content" path="${jboss.home.dir}/welcome-content"/> <file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
</handlers> </handlers>
<application-security-domains>
<application-security-domain name="other" security-domain="ApplicationDomain"/>
</application-security-domains>
</subsystem> </subsystem>
<subsystem xmlns="urn:jboss:domain:weld:4.0"/> <subsystem xmlns="urn:jboss:domain:weld:4.0"/>
</profile> </profile>
<interfaces> <interfaces>
<interface name="management"> <interface name="management">
<inet-address value="127.0.0.1"/> <inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
</interface> </interface>
<interface name="public"> <interface name="public">
<inet-address value="127.0.0.1"/> <inet-address value="${jboss.bind.address:127.0.0.1}"/>
</interface> </interface>
</interfaces> </interfaces>
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}"> <socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
<socket-binding name="http" port="8081"/> <socket-binding name="http" port="8081"/>
<socket-binding name="https" port="8443"/>
<socket-binding name="management-http" interface="management" port="19990"/> <socket-binding name="management-http" interface="management" port="19990"/>
<socket-binding name="management-https" interface="management" port="19991"/>
<socket-binding name="txn-recovery-environment" port="4712"/> <socket-binding name="txn-recovery-environment" port="4712"/>
<socket-binding name="txn-status-manager" port="4713"/> <socket-binding name="txn-status-manager" port="4713"/>
<outbound-socket-binding name="mail-smtp"> <outbound-socket-binding name="mail-smtp">

View File

@@ -1,10 +1,6 @@
--- ---
- name: Verify - name: Verify
hosts: all hosts: all
vars:
keycloak_uri: "http://localhost:8081"
keycloak_management_port: "http://localhost:19990"
keycloak_admin_password: "remembertochangeme"
tasks: tasks:
- name: Populate service facts - name: Populate service facts
ansible.builtin.service_facts: ansible.builtin.service_facts:
@@ -13,20 +9,3 @@
that: that:
- ansible_facts.services["keycloak.service"]["state"] == "running" - ansible_facts.services["keycloak.service"]["state"] == "running"
- ansible_facts.services["keycloak.service"]["status"] == "enabled" - 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_1.8.0/' | 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"
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

View File

@@ -3,45 +3,39 @@
ansible.builtin.debug: ansible.builtin.debug:
msg: "Ansible version is {{ ansible_version.full }}" msg: "Ansible version is {{ ansible_version.full }}"
- name: "Set package name for sudo"
ansible.builtin.set_fact:
sudo_pkg_name: sudo
- name: "Ensure {{ sudo_pkg_name }} is installed (if user is root)." - name: "Ensure {{ sudo_pkg_name }} is installed (if user is root)."
ansible.builtin.yum: ansible.builtin.yum:
name: "{{ sudo_pkg_name }}" name: "{{ sudo_pkg_name }}"
state: present
when: when:
- ansible_user_id == 'root' - ansible_user_id == 'root'
- name: Gather the package facts - name: Gather the package facts
ansible.builtin.package_facts: ansible.builtin.package_facts:
manager: auto manager: auto
- name: "Check if sudo is installed." - name: "Check if {{ sudo_pkg_name }} is installed."
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- sudo_pkg_name in ansible_facts.packages - sudo_pkg_name in ansible_facts.packages
fail_msg: "sudo is not installed on target system"
- name: "Install iproute" - name: Install sudo
become: yes
ansible.builtin.yum: ansible.builtin.yum:
name: name:
- sudo
- iproute - iproute
state: present state: present
when:
- ansible_user_id == 'root'
- name: "Retrieve assets server from env" - name: "Retrieve assets server from env"
ansible.builtin.set_fact: ansible.builtin.set_fact:
assets_server: "{{ lookup('env', 'MIDDLEWARE_DOWNLOAD_RELEASE_SERVER_URL') }}" assets_server: "{{ lookup('env','MIDDLEWARE_DOWNLOAD_RELEASE_SERVER_URL') }}"
- name: "Download artefacts only if assets_server is set" - name: "Download artefacts only if assets_server is set"
when: when:
- assets_server is defined - assets_server is defined
- assets_server | length > 0 - assets_server | length > 0
- assets is defined
- assets | length > 0
block: block:
- name: "Set offline when assets server from env is defined" - name: "Set offline when assets server from env is defined"
ansible.builtin.set_fact: ansible.builtin.set_fact:
@@ -52,7 +46,6 @@
url: "{{ asset }}" url: "{{ asset }}"
dest: "{{ lookup('env', 'PWD') }}" dest: "{{ lookup('env', 'PWD') }}"
validate_certs: no validate_certs: no
mode: '0644'
delegate_to: localhost delegate_to: localhost
loop: "{{ assets }}" loop: "{{ assets }}"
loop_control: loop_control:

View File

@@ -0,0 +1,44 @@
---
- name: Converge
hosts: all
vars:
keycloak_quarkus_admin_pass: "remembertochangeme"
keycloak_admin_password: "remembertochangeme"
keycloak_realm: TestRealm
keycloak_quarkus_log: file
keycloak_quarkus_frontend_url: '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_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

View File

@@ -3,17 +3,15 @@ driver:
name: docker name: docker
platforms: platforms:
- name: instance - name: instance
image: registry.access.redhat.com/ubi9/ubi-init:latest image: registry.access.redhat.com/ubi8/ubi-init:latest
pre_build_image: true pre_build_image: true
privileged: true privileged: true
command: "/usr/sbin/init" command: "/usr/sbin/init"
exposed_ports: port_bindings:
- "8080/tcp" - "8080/tcp"
- "8009/tcp" - "8009/tcp"
- "9000/tcp"
published_ports: published_ports:
- 0.0.0.0:8080:8080/tcp - 0.0.0.0:8080:8080/tcp
- 0.0.0.0:9000:9000/TCP
provisioner: provisioner:
name: ansible name: ansible
config_options: config_options:
@@ -31,8 +29,6 @@ provisioner:
ansible_python_interpreter: "{{ ansible_playbook_python }}" ansible_python_interpreter: "{{ ansible_playbook_python }}"
env: env:
ANSIBLE_FORCE_COLOR: "true" ANSIBLE_FORCE_COLOR: "true"
PROXY: "${PROXY}"
NO_PROXY: "${NO_PROXY}"
verifier: verifier:
name: ansible name: ansible
scenario: scenario:

View File

@@ -0,0 +1,22 @@
---
- name: Prepare
hosts: all
become: yes
tasks:
- name: Install sudo
ansible.builtin.yum:
name:
- sudo
- java-17-openjdk-headless
state: present
- name: Link default logs directory
ansible.builtin.file:
state: link
src: /usr/lib/jvm/jre-17-openjdk
dest: /opt/openjdk
force: true
- name: "Display hera_home if defined."
ansible.builtin.set_fact:
hera_home: "{{ lookup('env', 'HERA_HOME') }}"

View File

@@ -1,95 +1,45 @@
--- ---
- name: Converge - name: Converge
hosts: all hosts: all
vars_files: vars:
- ../group_vars/all/vars.yml keycloak_quarkus_admin_pass: "remembertochangeme"
vars: keycloak_admin_password: "remembertochangeme"
keycloak_quarkus_show_deprecation_warnings: false keycloak_realm: TestRealm
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_host: instance
keycloak_quarkus_bootstrap_admin_user: "remembertochangeme"
keycloak_realm_realm: TestRealm
keycloak_quarkus_hostname: https://instance:8443
keycloak_quarkus_log: file keycloak_quarkus_log: file
keycloak_quarkus_log_level: debug # needed for the verify step keycloak_quarkus_https_key_file_enabled: True
keycloak_quarkus_https_key_file_enabled: true keycloak_quarkus_key_file: "/opt/keycloak/certs/key.pem"
keycloak_quarkus_key_file_copy_enabled: true keycloak_quarkus_cert_file: "/opt/keycloak/certs/cert.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 keycloak_quarkus_log_target: /tmp/keycloak
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_restart_health_check: false # would fail because of self-signed cert
keycloak_quarkus_version: 26.6.2
keycloak_quarkus_java_heap_opts: "-Xms1024m -Xmx1024m"
keycloak_quarkus_additional_env_vars:
- key: KC_FEATURES_DISABLED
value: impersonation,kerberos
keycloak_quarkus_providers:
- id: http-client
spi: connections
default: true
restart: true
properties:
- key: default-connection-pool-size
value: 10
- id: spid-saml
url: https://github.com/italia/spid-keycloak-provider/releases/download/26.5.6/spid-provider.jar
- id: spid-saml-w-checksum
url: https://github.com/italia/spid-keycloak-provider/releases/download/26.5.6/spid-provider.jar
checksum: sha256:2ddafc389a5f017d8665bfdfa2f72b3784fc74b9f3a482e796fa89a5ba5cc95b
- 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: 26.6.3 # optional
# username: myUser # optional
# password: myPAT # optional
# - id: my-static-theme
# local_path: /tmp/my-static-theme.jar
keycloak_quarkus_policies:
- 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: roles:
- role: keycloak_quarkus - role: keycloak_quarkus
- role: keycloak_realm - role: keycloak_realm
keycloak_realm_url: http://instance:8080 keycloak_context: ''
keycloak_realm_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}" keycloak_client_default_roles:
keycloak_realm_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}"
keycloak_realm_client_default_roles:
- TestRoleAdmin - TestRoleAdmin
- TestRoleUser - TestRoleUser
keycloak_realm_client_users: keycloak_client_users:
- username: TestUser - username: TestUser
password: password password: password
client_roles: client_roles:
- client: TestClient - client: TestClient
role: TestRoleUser role: TestRoleUser
realm: "{{ keycloak_realm_realm }}" realm: "{{ keycloak_realm }}"
- username: TestAdmin - username: TestAdmin
password: password password: password
client_roles: client_roles:
- client: TestClient - client: TestClient
role: TestRoleUser role: TestRoleUser
realm: "{{ keycloak_realm_realm }}" realm: "{{ keycloak_realm }}"
- client: TestClient - client: TestClient
role: TestRoleAdmin role: TestRoleAdmin
realm: "{{ keycloak_realm_realm }}" realm: "{{ keycloak_realm }}"
keycloak_realm_realm: TestRealm keycloak_realm: TestRealm
keycloak_realm_clients: keycloak_clients:
- name: TestClient - name: TestClient
roles: "{{ keycloak_realm_client_default_roles }}" roles: "{{ keycloak_client_default_roles }}"
realm: "{{ keycloak_realm_realm }}" realm: "{{ keycloak_realm }}"
public_client: "{{ keycloak_realm_client_public }}" public_client: "{{ keycloak_client_public }}"
web_origins: "{{ keycloak_realm_client_web_origins }}" web_origins: "{{ keycloak_client_web_origins }}"
users: "{{ keycloak_realm_client_users }}" users: "{{ keycloak_client_users }}"
client_id: TestClient client_id: TestClient

View File

@@ -3,15 +3,14 @@ driver:
name: docker name: docker
platforms: platforms:
- name: instance - name: instance
image: registry.access.redhat.com/ubi9/ubi-init:latest image: registry.access.redhat.com/ubi8/ubi-init:latest
pre_build_image: true pre_build_image: true
privileged: true privileged: true
command: "/usr/sbin/init" command: "/usr/sbin/init"
exposed_ports: port_bindings:
- "8080/tcp" - "8080/tcp"
- "8443/tcp" - "8443/tcp"
- "8009/tcp" - "8009/tcp"
- "9000/tcp"
published_ports: published_ports:
- 0.0.0.0:8443:8443/tcp - 0.0.0.0:8443:8443/tcp
provisioner: provisioner:
@@ -31,9 +30,6 @@ provisioner:
ansible_python_interpreter: "{{ ansible_playbook_python }}" ansible_python_interpreter: "{{ ansible_playbook_python }}"
env: env:
ANSIBLE_FORCE_COLOR: "true" ANSIBLE_FORCE_COLOR: "true"
PYTHONHTTPSVERIFY: 0
PROXY: "${PROXY}"
NO_PROXY: "${NO_PROXY}"
verifier: verifier:
name: ansible name: ansible
scenario: scenario:

View File

@@ -1,50 +1,34 @@
--- ---
- name: Prepare - name: Prepare
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
tasks: tasks:
- name: Install sudo
become: yes
ansible.builtin.yum:
name: sudo
state: present
- name: "Display hera_home if defined." - name: "Display hera_home if defined."
ansible.builtin.set_fact: ansible.builtin.set_fact:
hera_home: "{{ lookup('env', 'HERA_HOME') }}" hera_home: "{{ lookup('env', 'HERA_HOME') }}"
- name: "Ensure common prepare phase are set."
ansible.builtin.include_tasks: ../prepare.yml
- name: Create certificate request - 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' ansible.builtin.command: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=instance'
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost delegate_to: localhost
changed_when: false changed_when: False
- name: Create vault directory - name: Create conf directory # risky-file-permissions in test user account does not exist yet
become: "{{ molecule_prepare_require_privilege_escalation | default(true) }}"
ansible.builtin.file: ansible.builtin.file:
state: directory state: directory
path: "/opt/keycloak/vault" path: "/opt/keycloak/certs/"
mode: '0755' mode: 0755
- name: Make sure a jre is available (for keytool to prepare keystore) - name: Copy certificates
delegate_to: localhost become: yes
ansible.builtin.package:
name: java-21-openjdk-headless
state: present
become: "{{ molecule_prepare_require_privilege_escalation | default(true) }}"
failed_when: false
- name: Create vault keystore
ansible.builtin.command: keytool -importpass -alias TestRealm_testalias -keystore keystore.p12 -storepass keystorepassword
args:
chdir: "{{ playbook_dir }}"
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: "{{ molecule_prepare_require_privilege_escalation | default(true) }}"
ansible.builtin.copy: ansible.builtin.copy:
src: keystore.p12 src: "{{ item }}"
dest: /opt/keycloak/vault/keystore.p12 dest: "/opt/keycloak/certs/{{ item }}"
mode: '0444' mode: 0444
loop:
- cert.pem
- key.pem

View File

@@ -1,11 +1,6 @@
--- ---
- name: Verify - name: Verify
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
vars:
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
keycloak_quarkus_bootstrap_admin_user: "remembertochangeme"
tasks: tasks:
- name: Populate service facts - name: Populate service facts
ansible.builtin.service_facts: ansible.builtin.service_facts:
@@ -15,7 +10,6 @@
that: that:
- ansible_facts.services["keycloak.service"]["state"] == "running" - ansible_facts.services["keycloak.service"]["state"] == "running"
- ansible_facts.services["keycloak.service"]["status"] == "enabled" - ansible_facts.services["keycloak.service"]["status"] == "enabled"
fail_msg: "Service not running"
- name: Set internal envvar - name: Set internal envvar
ansible.builtin.set_fact: ansible.builtin.set_fact:
@@ -38,15 +32,15 @@
- name: Verify endpoint URLs - name: Verify endpoint URLs
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- (openid_config.stdout | from_json)["backchannel_authentication_endpoint"] == 'https://instance:8443/realms/master/protocol/openid-connect/ext/ciba/auth' - (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:8443/realms/master' - (openid_config.stdout | from_json)['issuer'] == 'https://instance/realms/master'
- (openid_config.stdout | from_json)['authorization_endpoint'] == 'https://instance:8443/realms/master/protocol/openid-connect/auth' - (openid_config.stdout | from_json)['authorization_endpoint'] == 'https://instance/realms/master/protocol/openid-connect/auth'
- (openid_config.stdout | from_json)['token_endpoint'] == 'https://instance:8443/realms/master/protocol/openid-connect/token' - (openid_config.stdout | from_json)['token_endpoint'] == 'https://instance/realms/master/protocol/openid-connect/token'
delegate_to: localhost delegate_to: localhost
- name: Check log folder - name: Check log folder
ansible.builtin.stat: ansible.builtin.stat:
path: /tmp/keycloak path: "/tmp/keycloak"
register: keycloak_log_folder register: keycloak_log_folder
- name: Check that keycloak log folder exists and is a link - name: Check that keycloak log folder exists and is a link
@@ -55,12 +49,10 @@
- keycloak_log_folder.stat.exists - keycloak_log_folder.stat.exists
- not keycloak_log_folder.stat.isdir - not keycloak_log_folder.stat.isdir
- keycloak_log_folder.stat.islnk - keycloak_log_folder.stat.islnk
fail_msg: "Service log symlink not correctly created"
- name: Check log file - name: Check log file
become: "{{ molecule_prepare_require_privilege_escalation | default(true) }}"
ansible.builtin.stat: ansible.builtin.stat:
path: /tmp/keycloak/keycloak.log path: "/tmp/keycloak/keycloak.log"
register: keycloak_log_file register: keycloak_log_file
- name: Check if keycloak file exists - name: Check if keycloak file exists
@@ -70,9 +62,8 @@
- not keycloak_log_file.stat.isdir - not keycloak_log_file.stat.isdir
- name: Check default log folder - name: Check default log folder
become: "{{ molecule_prepare_require_privilege_escalation | default(true) }}"
ansible.builtin.stat: ansible.builtin.stat:
path: /var/log/keycloak path: "/var/log/keycloak"
register: keycloak_default_log_folder register: keycloak_default_log_folder
failed_when: false failed_when: false
@@ -80,51 +71,3 @@
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- not keycloak_default_log_folder.stat.exists - not keycloak_default_log_folder.stat.exists
- name: Verify vault SPI in logfile
become: "{{ molecule_prepare_require_privilege_escalation | default(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
- name: Verify token api call
ansible.builtin.uri:
url: "https://instance:8443/realms/master/protocol/openid-connect/token"
method: POST
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
retries: 2
delay: 2
- name: "Get Clients"
ansible.builtin.uri:
url: "https://instance:8443/admin/realms/TestRealm/clients"
validate_certs: false
headers:
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://instance:8443/admin/realms/TestRealm/clients/{{ keycloak_client_uuid }}"
validate_certs: false
headers:
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:
Authorization: "Bearer {{ keycloak_auth_response.json.access_token }}"
register: keycloak_test_client_roles

View File

@@ -1,51 +0,0 @@
---
- name: Converge
hosts: all
vars_files:
- ../group_vars/all/vars.yml
vars:
keycloak_quarkus_show_deprecation_warnings: false
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
keycloak_quarkus_bootstrap_admin_user: "remembertochangeme"
keycloak_realm_realm: TestRealm
keycloak_quarkus_log: file
keycloak_quarkus_hostname: 'http://localhost:8080'
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
keycloak_realm_url: "{{ keycloak_quarkus_hostname }}"
keycloak_realm_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}"
keycloak_realm_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}"
keycloak_realm_client_default_roles:
- TestRoleAdmin
- TestRoleUser
keycloak_realm_client_users:
- username: TestUser
password: password
client_roles:
- client: TestClient
role: TestRoleUser
realm: "{{ keycloak_realm_realm }}"
- username: TestAdmin
password: password
client_roles:
- client: TestClient
role: TestRoleUser
realm: "{{ keycloak_realm_realm }}"
- client: TestClient
role: TestRoleAdmin
realm: "{{ keycloak_realm_realm }}"
keycloak_realm_realm: TestRealm
keycloak_realm_clients:
- name: TestClient
roles: "{{ keycloak_realm_client_default_roles }}"
realm: "{{ keycloak_realm_realm }}"
public_client: "{{ keycloak_realm_client_public }}"
web_origins: "{{ keycloak_realm_client_web_origins }}"
users: "{{ keycloak_realm_client_users }}"
client_id: TestClient

View File

@@ -1,51 +0,0 @@
---
- name: Prepare
hosts: all
vars_files:
- ../group_vars/all/vars.yml
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: Install JDK17
become: "{{ molecule_prepare_require_privilege_escalation | default(true) }}"
ansible.builtin.yum:
name:
- java-17-openjdk-headless
state: present
when:
- ansible_facts.os_family == 'RedHat'
- name: Link default logs directory
become: "{{ molecule_prepare_require_privilege_escalation | default(true) }}"
ansible.builtin.file:
state: link
src: "{{ item }}"
dest: /opt/openjdk
force: true
with_fileglob:
- /usr/lib/jvm/java-17-openjdk*
when:
- ansible_facts.os_family == "Debian"
- name: Link default logs directory
ansible.builtin.file:
state: link
src: /usr/lib/jvm/jre-17-openjdk
dest: /opt/openjdk
force: true
when:
- ansible_facts.os_family == "RedHat"
- name: "Display hera_home if defined."
ansible.builtin.set_fact:
hera_home: "{{ lookup('env', 'HERA_HOME') }}"

View File

@@ -1 +0,0 @@
../../roles

View File

@@ -1,31 +0,0 @@
---
- name: Converge
hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
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

View File

@@ -1,82 +0,0 @@
---
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
exposed_ports:
- "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
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "9000/tcp"
- name: postgres
image: ubuntu/postgres:14-22.04_beta
pre_build_image: true
privileged: true
command: postgres
groups:
- database
networks:
- name: rhbk
exposed_ports:
- "5432/tcp"
mounts:
- type: bind
target: /etc/postgresql/postgresql.conf
source: ${MOLECULE_PROJECT_DIRECTORY}/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

View File

@@ -1,750 +0,0 @@
# -----------------------------
# 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

View File

@@ -1,48 +0,0 @@
---
- name: Prepare
hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
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 }}'"
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost
changed_when: False
- name: Create vault directory
become: "{{ molecule_prepare_require_privilege_escalation | default(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: "{{ molecule_prepare_require_privilege_escalation | default(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: "{{ molecule_prepare_require_privilege_escalation | default(true) }}"
ansible.builtin.copy:
src: keystore.p12
dest: /opt/keycloak/vault/keystore.p12
mode: 0444

View File

@@ -1 +0,0 @@
../../roles

View File

@@ -1,31 +0,0 @@
---
- name: Verify
hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
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: "{{ molecule_prepare_require_privilege_escalation | default(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

View File

@@ -1,32 +0,0 @@
---
- name: Converge
hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
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
keycloak_quarkus_version: 26.3.5
roles:
- role: keycloak_quarkus

View File

@@ -1,82 +0,0 @@
---
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
exposed_ports:
- "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
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "9000/tcp"
- name: postgres
image: ubuntu/postgres:14-22.04_beta
pre_build_image: true
privileged: true
command: postgres
groups:
- database
networks:
- name: rhbk
exposed_ports:
- "5432/tcp"
mounts:
- type: bind
target: /etc/postgresql/postgresql.conf
source: ${MOLECULE_PROJECT_DIRECTORY}/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

View File

@@ -1,750 +0,0 @@
# -----------------------------
# 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

View File

@@ -1,48 +0,0 @@
---
- name: Prepare
hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
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 }}'"
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost
changed_when: False
- name: Create vault directory
become: "{{ molecule_prepare_require_privilege_escalation | default(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: "{{ molecule_prepare_require_privilege_escalation | default(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: "{{ molecule_prepare_require_privilege_escalation | default(true) }}"
ansible.builtin.copy:
src: keystore.p12
dest: /opt/keycloak/vault/keystore.p12
mode: 0444

View File

@@ -1 +0,0 @@
../../roles

View File

@@ -1,31 +0,0 @@
---
- name: Verify
hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
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: "{{ molecule_prepare_require_privilege_escalation | default(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

View File

@@ -1,70 +0,0 @@
---
- name: Converge
hosts: infinispan
vars_files:
- ../group_vars/all/vars.yml
vars:
ansible_become: "{{ keycloak_install_requires_become | default(true) }}"
roles:
- role: middleware_automation.infinispan.infinispan
infinispan_service_name: infinispan
infinispan_supervisor_password: remembertochangeme
infinispan_keycloak_caches: true
infinispan_keycloak_persistence: False
infinispan_jgroups_discovery: TCPPING
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_bind_address: 0.0.0.0
infinispan_users:
- { name: 'testuser', password: 'test', roles: 'observer' }
- name: Converge
hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
pre_tasks:
- name: Wait for Infinispan Hot Rod port before starting Keycloak
ansible.builtin.wait_for:
host: infinispan1
port: 11222
timeout: 120
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: 120
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
keycloak_quarkus_cache_remote: true
keycloak_quarkus_cache_remote_username: supervisor
keycloak_quarkus_cache_remote_password: remembertochangeme
keycloak_quarkus_cache_remote_host: "infinispan1"
keycloak_quarkus_cache_remote_port: 11222
keycloak_quarkus_cache_remote_tls_enabled: false
keycloak_quarkus_additional_env_vars:
- key: KC_FEATURES
value: clusterless
- key: KC_FEATURES_DISABLED
value: persistent-user-sessions
roles:
- role: keycloak_quarkus

View File

@@ -1,80 +0,0 @@
---
driver:
name: docker
platforms:
- name: keycloak1
image: registry.access.redhat.com/ubi9/ubi-init:latest
pre_build_image: true
privileged: true
command: "/usr/sbin/init"
groups:
- keycloak
networks:
- name: rhbk
exposed_ports:
- "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
exposed_ports:
- "11222/tcp"
- name: postgres
image: ubuntu/postgres:14-22.04_beta
pre_build_image: true
privileged: true
command: postgres
groups:
- database
networks:
- name: rhbk
exposed_ports:
- "5432/tcp"
mounts:
- type: bind
target: /etc/postgresql/postgresql.conf
source: ${MOLECULE_PROJECT_DIRECTORY}/molecule/quarkus_ha_remote/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

View File

@@ -1,750 +0,0 @@
# -----------------------------
# 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

View File

@@ -1,50 +0,0 @@
---
- name: Prepare
hosts: 'keycloak:infinispan'
vars_files:
- ../group_vars/all/vars.yml
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 }}'"
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost
changed_when: False
- name: Create vault directory
become: "{{ molecule_prepare_require_privilege_escalation | default(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: "{{ molecule_prepare_require_privilege_escalation | default(true) }}"
failed_when: false
- name: Create vault keystore
ansible.builtin.command: keytool -importpass -alias TestRealm_testalias -keystore keystore.p12 -storepass keystorepassword
args:
chdir: "{{ playbook_dir }}"
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: "{{ molecule_prepare_require_privilege_escalation | default(true) }}"
ansible.builtin.copy:
src: keystore.p12
dest: /opt/keycloak/vault/keystore.p12
mode: 0444

View File

@@ -1 +0,0 @@
../../roles

View File

@@ -1,31 +0,0 @@
---
- name: Verify
hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
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: "{{ molecule_prepare_require_privilege_escalation | default(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

View File

@@ -1,14 +0,0 @@
---
- name: Converge
hosts: all
vars_files:
- ../group_vars/all/vars.yml
- 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.6.2
roles:
- role: keycloak_quarkus

View File

@@ -1,49 +0,0 @@
---
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
exposed_ports:
- "8080/tcp"
- "9000/tcp"
published_ports:
- 0.0.0.0:8080:8080/TCP
- 0.0.0.0:9000:9000/TCP
provisioner:
name: ansible
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"
PROXY: "${PROXY}"
NO_PROXY: "${NO_PROXY}"
verifier:
name: ansible
scenario:
test_sequence:
- dependency
- cleanup
- destroy
- syntax
- create
- prepare
- converge
- idempotence
- side_effect
- verify
- cleanup
- destroy

View File

@@ -1,59 +0,0 @@
---
- name: Prepare
hosts: all
vars_files:
- ../group_vars/all/vars.yml
- vars.yml
vars:
sudo_pkg_name: sudo
keycloak_quarkus_version: 26.6.1
keycloak_quarkus_additional_env_vars:
- key: KC_FEATURES_DISABLED
value: impersonation,kerberos
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'
args:
chdir: "{{ playbook_dir }}"
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: "{{ molecule_prepare_require_privilege_escalation | default(true) }}"

View File

@@ -1 +0,0 @@
../../roles

View File

@@ -1,13 +0,0 @@
---
keycloak_quarkus_offline_install: false
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
keycloak_quarkus_realm: TestRealm
keycloak_quarkus_hostname: http://instance:8080
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

View File

@@ -1,31 +0,0 @@
---
- name: Verify
hosts: instance
vars:
keycloak_quarkus_bootstrap_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["keycloak.service"]["state"] == "running"
- ansible_facts.services["keycloak.service"]["status"] == "enabled"
- name: Verify Java 21 runtime is installed (UBI/RHEL)
ansible.builtin.command:
cmd: rpm -q java-21-openjdk-headless
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_bootstrap_admin_password }}&grant_type=password"
validate_certs: no
register: keycloak_auth_response
until: keycloak_auth_response.status == 200
retries: 45
delay: 5

View File

@@ -2,13 +2,10 @@
collections: collections:
- name: middleware_automation.common - name: middleware_automation.common
- name: middleware_automation.jbcs - name: middleware_automation.jbcs
- name: middleware_automation.infinispan
- name: community.general - name: community.general
- name: ansible.posix - name: ansible.posix
- name: community.docker - name: community.docker
version: ">=3.8.0" version: ">=1.9.1"
- name: containers.podman
version: ">=1.8.1"
roles: roles:
- name: elan.simple_nginx_reverse_proxy - name: elan.simple_nginx_reverse_proxy

View File

@@ -1,22 +0,0 @@
---
- name: Converge
hosts: all
vars_files:
- ../group_vars/all/vars.yml
vars:
keycloak_admin_password: "remembertochangeme"
keycloak_enable: true
keycloak_offline_install: false
roles:
- role: keycloak
- role: keycloak_realm
keycloak_realm_url: "http://instance:8080"
keycloak_realm_context: "/auth"
keycloak_realm_admin_user: "admin"
keycloak_realm_admin_password: "remembertochangeme"
keycloak_realm_realm: TestRealm
keycloak_realm_clients:
- name: TestClient
realm: "{{ keycloak_realm_realm }}"
public_client: true
client_id: TestClient

View File

@@ -1,31 +0,0 @@
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: instance
image: registry.access.redhat.com/ubi9/ubi-init:latest
command: /usr/sbin/init
privileged: true
pre_build_image: true
provisioner:
name: ansible
config_options:
defaults:
interpreter_python: auto_silent
callbacks_enabled: profile_tasks, timer, yaml
ssh_connection:
pipelining: false
playbooks:
prepare: prepare.yml
converge: converge.yml
verify: verify.yml
env:
ANSIBLE_ROLES_PATH: "../../roles"
inventory:
host_vars:
instance:
ansible_user: root
verifier:
name: ansible

View File

@@ -1,11 +0,0 @@
---
- name: Prepare
hosts: all
vars_files:
- ../group_vars/all/vars.yml
gather_facts: yes
vars:
sudo_pkg_name: sudo
tasks:
- name: "Run preparation common to all scenario"
ansible.builtin.include_tasks: ../prepare.yml

View File

@@ -1,26 +0,0 @@
---
- name: Verify
hosts: all
vars:
keycloak_admin_password: "remembertochangeme"
keycloak_admin_user: "admin"
keycloak_uri: "http://localhost:8080"
keycloak_context: "/auth"
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"
- name: Verify token api call
ansible.builtin.uri:
url: "{{ keycloak_uri }}{{ keycloak_context }}/realms/master/protocol/openid-connect/token"
method: POST
body: "client_id=admin-cli&username={{ keycloak_admin_user }}&password={{ keycloak_admin_password }}&grant_type=password"
validate_certs: no
register: keycloak_auth_response
until: keycloak_auth_response.status == 200
retries: 2
delay: 2

View File

@@ -1,27 +0,0 @@
---
- name: Playbook for Keycloak Authentication Flow Configuration
hosts: all
vars:
keycloak_admin_user: admin
keycloak_admin_password: "remembertochangeme"
keycloak_url: "http://localhost:8080"
keycloak_realm: TestRealm
tasks:
- name: Create authentication flow with executions
middleware_automation.keycloak.keycloak_authentication_flow:
auth_keycloak_url: "{{ keycloak_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
alias: my-browser-flow
description: "Custom browser authentication flow"
provider_id: basic-flow
executions:
- provider_id: auth-cookie
requirement: ALTERNATIVE
- provider_id: auth-password
requirement: REQUIRED
- provider_id: auth-otp-form
requirement: ALTERNATIVE
state: present

View File

@@ -1,48 +0,0 @@
---
- name: Playbook for Keycloak Client Scope Configuration
hosts: all
vars:
keycloak_admin_user: admin
keycloak_admin_password: "remembertochangeme"
keycloak_url: "http://localhost:8080"
keycloak_realm: TestRealm
tasks:
- name: Create client scope with protocol mappers
middleware_automation.keycloak.keycloak_client_scope:
auth_keycloak_url: "{{ keycloak_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
name: TestClientScope
description: "Client scope created via Ansible"
protocol: openid-connect
protocol_mappers:
- name: email
protocolMapper: oidc-usermodel-attribute-mapper
config:
user.attribute: email
claim.name: email
jsonType.label: String
id.token.claim: "true"
access.token.claim: "true"
userinfo.token.claim: "true"
- name: firstName
protocolMapper: oidc-usermodel-attribute-mapper
config:
user.attribute: firstName
claim.name: given_name
jsonType.label: String
id.token.claim: "true"
access.token.claim: "true"
userinfo.token.claim: "true"
- name: username
protocolMapper: oidc-usermodel-attribute-mapper
config:
user.attribute: username
claim.name: preferred_username
jsonType.label: String
id.token.claim: "true"
access.token.claim: "true"
userinfo.token.claim: "true"
state: present

View File

@@ -6,9 +6,9 @@
ansible.builtin.include_role: ansible.builtin.include_role:
name: keycloak_realm name: keycloak_realm
vars: vars:
keycloak_realm_admin_password: "remembertochangeme" keycloak_admin_password: "remembertochangeme"
keycloak_realm_realm: TestRealm keycloak_realm: TestRealm
keycloak_realm_user_federation: keycloak_user_federation:
- realm: TestRealm - realm: TestRealm
name: my-ldap name: my-ldap
provider_id: ldap provider_id: ldap
@@ -48,13 +48,13 @@
ldap.full.name.attribute: cn ldap.full.name.attribute: cn
read.only: true read.only: true
write.only: false write.only: false
keycloak_realm_clients: keycloak_clients:
- name: TestClient1 - name: TestClient1
client_id: TestClient1 client_id: TestClient1
roles: roles:
- TestClient1Admin - TestClient1Admin
- TestClient1User - TestClient1User
realm: "{{ keycloak_realm_realm }}" realm: "{{ keycloak_realm }}"
public_client: true public_client: true
web_origins: web_origins:
- http://testclient1origin/application - http://testclient1origin/application
@@ -65,4 +65,4 @@
client_roles: client_roles:
- client: TestClient1 - client: TestClient1
role: TestClient1User role: TestClient1User
realm: "{{ keycloak_realm_realm }}" realm: "{{ keycloak_realm }}"

View File

@@ -2,8 +2,8 @@
- name: Playbook for Keycloak X Hosts with HTTPS enabled - name: Playbook for Keycloak X Hosts with HTTPS enabled
hosts: all hosts: all
vars: vars:
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_admin_pass: "remembertochangeme"
keycloak_quarkus_hostname: http://localhost keycloak_quarkus_host: localhost
keycloak_quarkus_port: 8443 keycloak_quarkus_port: 8443
keycloak_quarkus_log: file keycloak_quarkus_log: file
keycloak_quarkus_proxy_mode: none keycloak_quarkus_proxy_mode: none

View File

@@ -2,8 +2,8 @@
- name: Playbook for Keycloak X Hosts in develop mode - name: Playbook for Keycloak X Hosts in develop mode
hosts: all hosts: all
vars: vars:
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_admin_password: "remembertochangeme"
keycloak_quarkus_hostname: http://localhost keycloak_quarkus_host: localhost
keycloak_quarkus_port: 8080 keycloak_quarkus_port: 8080
keycloak_quarkus_log: file keycloak_quarkus_log: file
keycloak_quarkus_start_dev: true keycloak_quarkus_start_dev: true

View File

@@ -2,8 +2,8 @@
- name: Playbook for Keycloak Hosts - name: Playbook for Keycloak Hosts
hosts: all hosts: all
vars: vars:
keycloak_realm_admin_password: "remembertochangeme" keycloak_admin_password: "remembertochangeme"
keycloak_realm_clients: keycloak_clients:
- name: TestClient1 - name: TestClient1
client_id: TestClient1 client_id: TestClient1
roles: roles:
@@ -23,4 +23,4 @@
realm: TestRealm realm: TestRealm
roles: roles:
- role: middleware_automation.keycloak.keycloak_realm - role: middleware_automation.keycloak.keycloak_realm
keycloak_realm_realm: TestRealm keycloak_realm: TestRealm

View File

@@ -1,39 +0,0 @@
---
- name: Playbook for Keycloak Realm and Client Configuration
hosts: all
tasks:
- name: Keycloak Realm Role
ansible.builtin.include_role:
name: middleware_automation.keycloak.keycloak_realm
vars:
keycloak_realm_admin_password: "remembertochangeme"
keycloak_realm_realm: TestRealm
keycloak_realm_client_default_roles:
- TestRoleAdmin
- TestRoleUser
keycloak_realm_client_users:
- username: TestUser
password: password
client_roles:
- client: TestClient1
role: TestRoleUser
realm: TestRealm
- username: TestAdmin
password: password
client_roles:
- client: TestClient1
role: TestRoleUser
realm: TestRealm
- client: TestClient1
role: TestRoleAdmin
realm: TestRealm
keycloak_realm_clients:
- name: TestClient1
client_id: TestClient1
roles: "{{ keycloak_realm_client_default_roles }}"
realm: TestRealm
public_client: true
web_origins:
- http://testclient1origin/application
- http://testclient1origin/other
users: "{{ keycloak_realm_client_users }}"

View File

@@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Eike Frost <ei@kefro.st>
# 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)
from __future__ import annotations
__metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r'''
options: {}
attributes:
action_group:
description: Use C(group/middleware_automation.keycloak.keycloak) in C(module_defaults) to set defaults for this module.
support: full
membership:
- middleware_automation.keycloak.keycloak
'''

View File

@@ -5,7 +5,6 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
from __future__ import annotations
__metaclass__ = type __metaclass__ = type

View File

@@ -5,7 +5,6 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
from __future__ import annotations
__metaclass__ = type __metaclass__ = type
@@ -56,11 +55,7 @@ options:
description: description:
- Authentication token for Keycloak API. - Authentication token for Keycloak API.
type: str type: str
version_added: 3.0.0
refresh_token:
description:
- Authentication refresh token for Keycloak API.
type: str
validate_certs: validate_certs:
description: description:
@@ -73,9 +68,11 @@ options:
- Controls the HTTP connections timeout period (in seconds) to Keycloak API. - Controls the HTTP connections timeout period (in seconds) to Keycloak API.
type: int type: int
default: 10 default: 10
version_added: 4.5.0
http_agent: http_agent:
description: description:
- Configures the HTTP User-Agent header. - Configures the HTTP User-Agent header.
type: str type: str
default: Ansible default: Ansible
version_added: 5.4.0
''' '''

View File

@@ -1,34 +0,0 @@
# Copyright (c) Ansible Project
# 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
# Note that this module util is **PRIVATE** to the collection. It can have breaking changes at any time.
# Do not use this from other collections or standalone plugins/modules!
from __future__ import (absolute_import, division, print_function)
from __future__ import annotations
__metaclass__ = type
import typing as t
def merge_settings_without_absent_nulls(
existing_settings: dict[str, t.Any], desired_settings: dict[str, t.Any]
) -> dict[str, t.Any]:
"""
Merges existing and desired settings into a new dictionary while excluding null values in desired settings that are absent in the existing settings.
This ensures idempotency by treating absent keys in existing settings and null values in desired settings as equivalent, preventing unnecessary updates.
Args:
existing_settings (dict): Dictionary representing the current settings in Keycloak
desired_settings (dict): Dictionary representing the desired settings
Returns:
dict: A new dictionary containing all entries from existing_settings and desired_settings,
excluding null values in desired_settings whose corresponding keys are not present in existing_settings
"""
existing = existing_settings or {}
desired = desired_settings or {}
return {**existing, **{k: v for k, v in desired.items() if v is not None or k in existing}}

File diff suppressed because it is too large Load Diff

View File

@@ -1,78 +0,0 @@
# Copyright (c) 2022, John Cant <a.johncant@gmail.com>
# 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)
from __future__ import annotations
__metaclass__ = type
import typing as t
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.middleware_automation.keycloak.plugins.module_utils.identity.keycloak.keycloak import (
KeycloakAPI,
keycloak_argument_spec,
)
def keycloak_clientsecret_module() -> AnsibleModule:
"""
Returns an AnsibleModule definition for modules that interact with a client
secret.
:return: argument_spec dict
"""
argument_spec = keycloak_argument_spec()
meta_args = dict(
realm=dict(default="master"),
id=dict(type="str"),
client_id=dict(type="str", aliases=["clientId"]),
)
argument_spec.update(meta_args)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
required_one_of=(
[
["id", "client_id"],
["token", "auth_realm", "auth_username", "auth_password", "auth_client_id", "auth_client_secret"],
]
),
required_together=([["auth_username", "auth_password"]]),
mutually_exclusive=[["token", "auth_realm"], ["token", "auth_username"], ["token", "auth_password"]],
)
return module
def keycloak_clientsecret_module_resolve_params(module: AnsibleModule, kc: KeycloakAPI) -> tuple[str, dict[str, t.Any]]:
"""
Given an AnsibleModule definition for keycloak_clientsecret_*, and a
KeycloakAPI client, resolve the params needed to interact with the Keycloak
client secret, looking up the client by clientId if necessary via an API
call.
:return: tuple of id, realm
"""
realm = module.params.get("realm")
id = module.params.get("id")
client_id = module.params.get("client_id")
# only lookup the client_id if id isn't provided.
# in the case that both are provided, prefer the ID, since it is one
# less lookup.
if id is None:
# Due to the required_one_of spec, client_id is guaranteed to not be None
client = kc.get_client_by_client_id(client_id, realm=realm)
if client is None:
module.fail_json(msg=f"Client does not exist {client_id}")
id = client["id"]
return id, realm

View File

@@ -1,520 +0,0 @@
# Copyright (c) 2019, INSPQ <philippe.gauthier@inspq.qc.ca>
# 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)
from __future__ import annotations
__metaclass__ = type
DOCUMENTATION = r"""
module: keycloak_authentication
short_description: Configure authentication in Keycloak
description:
- This module actually can only make a copy of an existing authentication flow, add an execution to it and configure it.
- It can also delete the flow.
# Originally added in community.general 3.3.0
version_added: "3.0.0"
attributes:
check_mode:
support: full
diff_mode:
support: full
action_group:
# Originally added in community.general 10.2.0
version_added: "3.0.0"
options:
realm:
description:
- The name of the realm in which is the authentication.
required: true
type: str
alias:
description:
- Alias for the authentication flow.
required: true
type: str
description:
description:
- Description of the flow.
type: str
providerId:
description:
- C(providerId) for the new flow when not copied from an existing flow.
choices: ["basic-flow", "client-flow"]
type: str
copyFrom:
description:
- C(flowAlias) of the authentication flow to use for the copy.
type: str
authenticationExecutions:
description:
- Configuration structure for the executions.
type: list
elements: dict
suboptions:
providerId:
description:
- C(providerID) for the new flow when not copied from an existing flow.
type: str
displayName:
description:
- Name of the execution or subflow to create or update.
type: str
requirement:
description:
- Control status of the subflow or execution.
choices: ["REQUIRED", "ALTERNATIVE", "DISABLED", "CONDITIONAL"]
type: str
flowAlias:
description:
- Alias of parent flow.
type: str
authenticationConfig:
description:
- Describe the config of the authentication.
type: dict
index:
description:
- Priority order of the execution.
type: int
subFlowType:
description:
- For new subflows, optionally specify the type.
- Is only used at creation.
choices: ["basic-flow", "form-flow"]
default: "basic-flow"
type: str
state:
description:
- Control if the authentication flow must exists or not.
choices: ["present", "absent"]
default: present
type: str
force:
type: bool
default: false
description:
- If V(true), allows to remove the authentication flow and recreate it.
extends_documentation_fragment:
- middleware_automation.keycloak.keycloak
- middleware_automation.keycloak.actiongroup_keycloak
- middleware_automation.keycloak.attributes
author:
- Philippe Gauthier (@elfelip)
- Gaëtan Daubresse (@Gaetan2907)
"""
EXAMPLES = r"""
- name: Create an authentication flow from first broker login and add an execution to it.
middleware_automation.keycloak.keycloak_authentication:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: password
realm: master
alias: "Copy of first broker login"
copyFrom: "first broker login"
authenticationExecutions:
- providerId: "test-execution1"
requirement: "REQUIRED"
authenticationConfig:
alias: "test.execution1.property"
config:
test1.property: "value"
- providerId: "test-execution2"
requirement: "REQUIRED"
authenticationConfig:
alias: "test.execution2.property"
config:
test2.property: "value"
state: present
- name: Re-create the authentication flow
middleware_automation.keycloak.keycloak_authentication:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: password
realm: master
alias: "Copy of first broker login"
copyFrom: "first broker login"
authenticationExecutions:
- providerId: "test-provisioning"
requirement: "REQUIRED"
authenticationConfig:
alias: "test.provisioning.property"
config:
test.provisioning.property: "value"
state: present
force: true
- name: Create an authentication flow with subflow containing an execution.
middleware_automation.keycloak.keycloak_authentication:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: password
realm: master
alias: "Copy of first broker login"
copyFrom: "first broker login"
authenticationExecutions:
- providerId: "test-execution1"
requirement: "REQUIRED"
- displayName: "New Subflow"
requirement: "REQUIRED"
- providerId: "auth-cookie"
requirement: "REQUIRED"
flowAlias: "New Sublow"
state: present
- name: Remove authentication.
middleware_automation.keycloak.keycloak_authentication:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: password
realm: master
alias: "Copy of first broker login"
state: absent
"""
RETURN = r"""
msg:
description: Message as to what action was taken.
returned: always
type: str
end_state:
description: Representation of the authentication after module execution.
returned: on success
type: dict
sample:
{
"alias": "Copy of first broker login",
"authenticationExecutions": [
{
"alias": "review profile config",
"authenticationConfig": {
"alias": "review profile config",
"config": {
"update.profile.on.first.login": "missing"
},
"id": "6f09e4fb-aad4-496a-b873-7fa9779df6d7"
},
"configurable": true,
"displayName": "Review Profile",
"id": "8f77dab8-2008-416f-989e-88b09ccf0b4c",
"index": 0,
"level": 0,
"providerId": "idp-review-profile",
"requirement": "REQUIRED",
"requirementChoices": [
"REQUIRED",
"ALTERNATIVE",
"DISABLED"
]
}
],
"builtIn": false,
"description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
"id": "bc228863-5887-4297-b898-4d988f8eaa5c",
"providerId": "basic-flow",
"topLevel": true
}
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.middleware_automation.keycloak.plugins.module_utils.identity.keycloak.keycloak import (
KeycloakAPI,
KeycloakError,
get_token,
is_struct_included,
keycloak_argument_spec,
)
def find_exec_in_executions(searched_exec, executions):
"""
Search if exec is contained in the executions.
:param searched_exec: Execution to search for.
:param executions: List of executions.
:return: Index of the execution, -1 if not found..
"""
for i, existing_exec in enumerate(executions, start=0):
if (
"providerId" in existing_exec
and "providerId" in searched_exec
and existing_exec["providerId"] == searched_exec["providerId"]
or "displayName" in existing_exec
and "displayName" in searched_exec
and existing_exec["displayName"] == searched_exec["displayName"]
):
return i
return -1
def create_or_update_executions(kc, config, realm="master"):
"""
Create or update executions for an authentication flow.
:param kc: Keycloak API access.
:param config: Representation of the authentication flow including its executions.
:param realm: Realm
:return: tuple (changed, dict(before, after)
WHERE
bool changed indicates if changes have been made
dict(str, str) shows state before and after creation/update
"""
try:
changed = False
after = ""
before = ""
execution = None
if config.get("authenticationExecutions") is not None:
# Get existing executions on the Keycloak server for this alias
existing_executions = kc.get_executions_representation(config, realm=realm)
for new_exec_index, new_exec in enumerate(config["authenticationExecutions"], start=0):
if new_exec["index"] is not None:
new_exec_index = new_exec["index"]
exec_found = False
# Get flowalias parent if given
if new_exec["flowAlias"] is not None:
flow_alias_parent = new_exec["flowAlias"]
else:
flow_alias_parent = config["alias"]
# Check if same providerId or displayName name between existing and new execution
exec_index = find_exec_in_executions(new_exec, existing_executions)
if exec_index != -1:
# Remove key that doesn't need to be compared with existing_exec
exclude_key = ["flowAlias", "subFlowType"]
for key in new_exec:
if new_exec[key] is None:
exclude_key.append(key)
# Compare the executions to see if it need changes
if (
not is_struct_included(new_exec, existing_executions[exec_index], exclude_key)
or exec_index != new_exec_index
):
exec_found = True
if new_exec["index"] is None:
new_exec_index = exec_index
before += f"{existing_executions[exec_index]}\n"
execution = existing_executions[exec_index].copy()
# Remove exec from list in case 2 exec with same name
existing_executions[exec_index].clear()
elif new_exec["providerId"] is not None:
kc.create_execution(new_exec, flowAlias=flow_alias_parent, realm=realm)
execution = kc.get_executions_representation(config, realm=realm)[exec_index]
exec_found = True
exec_index = new_exec_index
after += f"{new_exec}\n"
elif new_exec["displayName"] is not None:
kc.create_subflow(
new_exec["displayName"], flow_alias_parent, realm=realm, flowType=new_exec["subFlowType"]
)
execution = kc.get_executions_representation(config, realm=realm)[exec_index]
exec_found = True
exec_index = new_exec_index
after += f"{new_exec}\n"
if exec_found:
changed = True
if exec_index != -1:
# Update the existing execution
updated_exec = {"id": execution["id"]}
# add the execution configuration
if new_exec["authenticationConfig"] is not None:
if "authenticationConfig" in execution and "id" in execution["authenticationConfig"]:
kc.delete_authentication_config(execution["authenticationConfig"]["id"], realm=realm)
kc.add_authenticationConfig_to_execution(
updated_exec["id"], new_exec["authenticationConfig"], realm=realm
)
for key in new_exec:
# remove unwanted key for the next API call
if key not in ("flowAlias", "authenticationConfig", "subFlowType"):
updated_exec[key] = new_exec[key]
if new_exec["requirement"] is not None:
if "priority" in execution:
updated_exec["priority"] = execution["priority"]
kc.update_authentication_executions(flow_alias_parent, updated_exec, realm=realm)
diff = exec_index - new_exec_index
kc.change_execution_priority(updated_exec["id"], diff, realm=realm)
after += f"{kc.get_executions_representation(config, realm=realm)[new_exec_index]}\n"
return changed, dict(before=before, after=after)
except Exception as e:
kc.module.fail_json(
msg=f"Could not create or update executions for authentication flow {config['alias']} in realm {realm}: {e}"
)
def main():
"""
Module execution
:return:
"""
argument_spec = keycloak_argument_spec()
meta_args = dict(
realm=dict(type="str", required=True),
alias=dict(type="str", required=True),
providerId=dict(type="str", choices=["basic-flow", "client-flow"]),
description=dict(type="str"),
copyFrom=dict(type="str"),
authenticationExecutions=dict(
type="list",
elements="dict",
options=dict(
providerId=dict(type="str"),
displayName=dict(type="str"),
requirement=dict(choices=["REQUIRED", "ALTERNATIVE", "DISABLED", "CONDITIONAL"], type="str"),
flowAlias=dict(type="str"),
authenticationConfig=dict(type="dict"),
index=dict(type="int"),
subFlowType=dict(choices=["basic-flow", "form-flow"], default="basic-flow", type="str"),
),
),
state=dict(choices=["absent", "present"], default="present"),
force=dict(type="bool", default=False),
)
argument_spec.update(meta_args)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
required_one_of=(
[["token", "auth_realm", "auth_username", "auth_password", "auth_client_id", "auth_client_secret"]]
),
required_together=([["auth_username", "auth_password"]]),
required_by={"refresh_token": "auth_realm"},
)
result = dict(changed=False, msg="", flow={})
# 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")
force = module.params.get("force")
new_auth_repr = {
"alias": module.params.get("alias"),
"copyFrom": module.params.get("copyFrom"),
"providerId": module.params.get("providerId"),
"authenticationExecutions": module.params.get("authenticationExecutions"),
"description": module.params.get("description"),
"builtIn": module.params.get("builtIn"),
"subflow": module.params.get("subflow"),
}
auth_repr = kc.get_authentication_flow_by_alias(alias=new_auth_repr["alias"], realm=realm)
# Cater for when it doesn't exist (an empty dict)
if not auth_repr:
if state == "absent":
# Do nothing and exit
if module._diff:
result["diff"] = dict(before="", after="")
result["changed"] = False
result["end_state"] = {}
result["msg"] = f"{new_auth_repr['alias']} absent"
module.exit_json(**result)
elif state == "present":
# Process a creation
result["changed"] = True
if module._diff:
result["diff"] = dict(before="", after=new_auth_repr)
if module.check_mode:
module.exit_json(**result)
# If copyFrom is defined, create authentication flow from a copy
if "copyFrom" in new_auth_repr and new_auth_repr["copyFrom"] is not None:
auth_repr = kc.copy_auth_flow(config=new_auth_repr, realm=realm)
else: # Create an empty authentication flow
auth_repr = kc.create_empty_auth_flow(config=new_auth_repr, realm=realm)
# If the authentication still not exist on the server, raise an exception.
if auth_repr is None:
result["msg"] = f"Authentication just created not found: {new_auth_repr}"
module.fail_json(**result)
# Configure the executions for the flow
create_or_update_executions(kc=kc, config=new_auth_repr, realm=realm)
# Get executions created
exec_repr = kc.get_executions_representation(config=new_auth_repr, realm=realm)
if exec_repr is not None:
auth_repr["authenticationExecutions"] = exec_repr
result["end_state"] = auth_repr
else:
if state == "present":
# Process an update
if force: # If force option is true
# Delete the actual authentication flow
result["changed"] = True
if module._diff:
result["diff"] = dict(before=auth_repr, after=new_auth_repr)
if module.check_mode:
module.exit_json(**result)
kc.delete_authentication_flow_by_id(id=auth_repr["id"], realm=realm)
# If copyFrom is defined, create authentication flow from a copy
if "copyFrom" in new_auth_repr and new_auth_repr["copyFrom"] is not None:
auth_repr = kc.copy_auth_flow(config=new_auth_repr, realm=realm)
else: # Create an empty authentication flow
auth_repr = kc.create_empty_auth_flow(config=new_auth_repr, realm=realm)
# If the authentication still not exist on the server, raise an exception.
if auth_repr is None:
result["msg"] = f"Authentication just created not found: {new_auth_repr}"
module.fail_json(**result)
# Configure the executions for the flow
if module.check_mode:
module.exit_json(**result)
changed, diff = create_or_update_executions(kc=kc, config=new_auth_repr, realm=realm)
result["changed"] |= changed
if module._diff:
result["diff"] = diff
# Get executions created
exec_repr = kc.get_executions_representation(config=new_auth_repr, realm=realm)
if exec_repr is not None:
auth_repr["authenticationExecutions"] = exec_repr
result["end_state"] = auth_repr
else:
# Process a deletion (because state was not 'present')
result["changed"] = True
if module._diff:
result["diff"] = dict(before=auth_repr, after="")
if module.check_mode:
module.exit_json(**result)
# delete it
kc.delete_authentication_flow_by_id(id=auth_repr["id"], realm=realm)
result["msg"] = f"Authentication flow: {new_auth_repr['alias']} id: {auth_repr['id']} is deleted"
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -1,300 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2024, Contributors to the middleware_automation.keycloak collection
# 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)
from __future__ import annotations
__metaclass__ = type
DOCUMENTATION = '''
---
module: keycloak_authentication_flow
short_description: Allows administration of Keycloak authentication flows via Keycloak API
version_added: "3.0.0"
description:
- This module allows you to add, remove or modify Keycloak authentication flows via the Keycloak REST API.
It requires access to the REST API via OpenID Connect; the user connecting and the client 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 client definition with the scope tailored
to your needs and a user having the expected roles.
- This module supports creating new top-level authentication flows, copying existing flows,
and adding execution steps to a flow.
attributes:
check_mode:
support: full
diff_mode:
support: full
options:
state:
description:
- State of the authentication flow.
- On V(present), the flow will be created if it does not yet exist.
- On V(absent), the flow will be removed if it exists.
default: 'present'
type: str
choices:
- present
- absent
alias:
type: str
required: true
description:
- Alias (name) of the authentication flow.
description:
type: str
description:
- Description of the authentication flow.
default: ''
realm:
type: str
description:
- The Keycloak realm under which this authentication flow resides.
default: 'master'
provider_id:
type: str
description:
- The provider ID for the flow.
default: 'basic-flow'
aliases:
- providerId
copy_from:
type: str
description:
- If set, the new flow is created as a copy of the flow with this alias.
- Cannot be used together with O(executions).
aliases:
- copyFrom
executions:
type: list
elements: dict
description:
- A list of executions (authenticator steps) to add to the flow.
- Each execution is a dict with keys C(provider_id) (or C(providerId)) and C(requirement).
- Executions are only added when the flow is first created.
default: []
suboptions:
provider_id:
type: str
required: true
description:
- The authenticator provider ID (e.g. V(auth-cookie), V(auth-password), V(auth-otp-form)).
aliases:
- providerId
requirement:
type: str
required: true
description:
- The requirement level for this execution.
choices:
- REQUIRED
- ALTERNATIVE
- DISABLED
- CONDITIONAL
extends_documentation_fragment:
- middleware_automation.keycloak.keycloak
- middleware_automation.keycloak.actiongroup_keycloak
- middleware_automation.keycloak.attributes
author:
- Paulo Menon (@paulomenon)
'''
EXAMPLES = '''
- name: Create an authentication flow with executions
middleware_automation.keycloak.keycloak_authentication_flow:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: password
realm: TestRealm
alias: my-browser-flow
description: "Custom browser flow"
provider_id: basic-flow
executions:
- provider_id: auth-cookie
requirement: ALTERNATIVE
- provider_id: auth-password
requirement: REQUIRED
- provider_id: auth-otp-form
requirement: ALTERNATIVE
state: present
delegate_to: localhost
- name: Create an authentication flow by copying an existing one
middleware_automation.keycloak.keycloak_authentication_flow:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: password
realm: TestRealm
alias: my-copy-of-browser
copy_from: browser
state: present
delegate_to: localhost
- name: Create a flow using token authentication
middleware_automation.keycloak.keycloak_authentication_flow:
auth_keycloak_url: http://localhost:8080
token: MY_TOKEN
realm: TestRealm
alias: my-flow
state: present
delegate_to: localhost
- name: Delete an authentication flow
middleware_automation.keycloak.keycloak_authentication_flow:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: password
realm: TestRealm
alias: my-browser-flow
state: absent
delegate_to: localhost
'''
RETURN = '''
msg:
description: Message as to what action was taken.
returned: always
type: str
sample: "Authentication flow my-browser-flow has been created"
end_state:
description: Representation of the authentication flow after module execution.
returned: on success
type: dict
sample: {
"id": "uuid-here",
"alias": "my-browser-flow",
"providerId": "basic-flow",
"topLevel": true,
"builtIn": false
}
'''
from ansible_collections.middleware_automation.keycloak.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, \
keycloak_argument_spec, get_token, KeycloakError
from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = keycloak_argument_spec()
execution_spec = dict(
provider_id=dict(type='str', required=True, aliases=['providerId']),
requirement=dict(type='str', required=True, choices=['REQUIRED', 'ALTERNATIVE', 'DISABLED', 'CONDITIONAL']),
)
meta_args = dict(
state=dict(type='str', default='present', choices=['present', 'absent']),
alias=dict(type='str', required=True),
description=dict(type='str', default=''),
realm=dict(type='str', default='master'),
provider_id=dict(type='str', default='basic-flow', aliases=['providerId']),
copy_from=dict(type='str', aliases=['copyFrom']),
executions=dict(type='list', default=[], options=execution_spec, elements='dict'),
)
argument_spec.update(meta_args)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True,
required_one_of=([['token', 'auth_realm', 'auth_username', 'auth_password']]),
required_together=([['auth_realm', 'auth_username', 'auth_password']]),
mutually_exclusive=[['copy_from', 'executions']])
result = dict(changed=False, msg='', diff={}, end_state={})
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')
alias = module.params.get('alias')
state = module.params.get('state')
description = module.params.get('description')
provider_id = module.params.get('provider_id')
copy_from = module.params.get('copy_from')
executions = module.params.get('executions')
before_flow = kc.get_authentication_flow_by_alias(alias, realm=realm)
flow_exists = bool(before_flow)
if state == 'absent':
if flow_exists:
result['changed'] = True
if module._diff:
result['diff'] = dict(before=before_flow, after='')
if module.check_mode:
module.exit_json(**result)
kc.delete_authentication_flow_by_id(before_flow['id'], realm=realm)
result['msg'] = "Authentication flow {alias} has been deleted".format(alias=alias)
else:
result['msg'] = "Authentication flow {alias} does not exist, doing nothing".format(alias=alias)
result['end_state'] = {}
module.exit_json(**result)
if flow_exists:
result['changed'] = False
result['end_state'] = before_flow
result['msg'] = "Authentication flow {alias} already exists".format(alias=alias)
module.exit_json(**result)
result['changed'] = True
flow_config = {
'alias': alias,
'description': description,
'providerId': provider_id,
}
if module._diff:
result['diff'] = dict(before='', after=flow_config)
if module.check_mode:
module.exit_json(**result)
if copy_from:
flow_config['copyFrom'] = copy_from
after_flow = kc.copy_auth_flow(flow_config, realm=realm)
result['msg'] = "Authentication flow {alias} has been created (copied from {src})".format(alias=alias, src=copy_from)
else:
after_flow = kc.create_empty_auth_flow(flow_config, realm=realm)
if executions:
for execution in executions:
exec_rep = {
'providerId': execution['provider_id'],
'requirement': execution['requirement'],
}
kc.create_execution(exec_rep, alias, realm=realm)
result['msg'] = "Authentication flow {alias} has been created".format(alias=alias)
after_flow = kc.get_authentication_flow_by_alias(alias, realm=realm)
result['end_state'] = after_flow
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -1,465 +0,0 @@
# Copyright (c) 2017, Eike Frost <ei@kefro.st>
# Copyright (c) 2021, Christophe Gilles <christophe.gilles54@gmail.com>
# 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)
from __future__ import annotations
__metaclass__ = type
DOCUMENTATION = r"""
module: keycloak_authentication_required_actions
short_description: Allows administration of Keycloak authentication required actions
description:
- This module can register, update and delete required actions.
- It also filters out any duplicate required actions by their alias. The first occurrence is preserved.
# Originally added in community.general 7.1.0
version_added: "3.0.0"
attributes:
check_mode:
support: full
diff_mode:
support: full
action_group:
# Originally added in community.general 10.2.0
version_added: "3.0.0"
options:
realm:
description:
- The name of the realm in which are the authentication required actions.
required: true
type: str
required_actions:
elements: dict
description:
- Authentication required action.
suboptions:
alias:
description:
- Unique name of the required action.
required: true
type: str
config:
description:
- Configuration for the required action.
type: dict
defaultAction:
description:
- Indicates whether new users have the required action assigned to them.
type: bool
enabled:
description:
- Indicates, if the required action is enabled or not.
type: bool
name:
description:
- Displayed name of the required action. Required for registration.
type: str
priority:
description:
- Priority of the required action.
type: int
providerId:
description:
- Provider ID of the required action. Required for registration.
type: str
type: list
state:
choices: ["absent", "present"]
description:
- Control if the realm authentication required actions are going to be registered/updated (V(present)) or deleted (V(absent)).
required: true
type: str
extends_documentation_fragment:
- middleware_automation.keycloak.keycloak
- middleware_automation.keycloak.actiongroup_keycloak
- middleware_automation.keycloak.attributes
author:
- Skrekulko (@Skrekulko)
"""
EXAMPLES = r"""
- name: Register a new required action.
middleware_automation.keycloak.keycloak_authentication_required_actions:
auth_client_id: "admin-cli"
auth_keycloak_url: "http://localhost:8080"
auth_password: "password"
auth_realm: "master"
auth_username: "admin"
realm: "master"
required_actions:
- alias: "TERMS_AND_CONDITIONS"
name: "Terms and conditions"
providerId: "TERMS_AND_CONDITIONS"
enabled: true
state: "present"
- name: Update the newly registered required action.
middleware_automation.keycloak.keycloak_authentication_required_actions:
auth_client_id: "admin-cli"
auth_keycloak_url: "http://localhost:8080"
auth_password: "password"
auth_realm: "master"
auth_username: "admin"
realm: "master"
required_actions:
- alias: "TERMS_AND_CONDITIONS"
enabled: false
state: "present"
- name: Delete the updated registered required action.
middleware_automation.keycloak.keycloak_authentication_required_actions:
auth_client_id: "admin-cli"
auth_keycloak_url: "http://localhost:8080"
auth_password: "password"
auth_realm: "master"
auth_username: "admin"
realm: "master"
required_actions:
- alias: "TERMS_AND_CONDITIONS"
state: "absent"
"""
RETURN = r"""
msg:
description: Message as to what action was taken.
returned: always
type: str
end_state:
description: Representation of the authentication required actions after module execution.
returned: on success
type: complex
contains:
alias:
description:
- Unique name of the required action.
sample: test-provider-id
type: str
config:
description:
- Configuration for the required action.
sample: {}
type: dict
defaultAction:
description:
- Indicates whether new users have the required action assigned to them.
sample: false
type: bool
enabled:
description:
- Indicates, if the required action is enabled or not.
sample: false
type: bool
name:
description:
- Displayed name of the required action. Required for registration.
sample: Test provider ID
type: str
priority:
description:
- Priority of the required action.
sample: 90
type: int
providerId:
description:
- Provider ID of the required action. Required for registration.
sample: test-provider-id
type: str
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.middleware_automation.keycloak.plugins.module_utils.identity.keycloak.keycloak import (
KeycloakAPI,
KeycloakError,
get_token,
keycloak_argument_spec,
)
def sanitize_required_actions(objects):
for obj in objects:
alias = obj["alias"]
name = obj["name"]
provider_id = obj["providerId"]
if not name:
obj["name"] = alias
if provider_id != alias:
obj["providerId"] = alias
return objects
def filter_duplicates(objects):
filtered_objects = {}
for obj in objects:
alias = obj["alias"]
if alias not in filtered_objects:
filtered_objects[alias] = obj
return list(filtered_objects.values())
def main():
"""
Module execution
:return:
"""
argument_spec = keycloak_argument_spec()
meta_args = dict(
realm=dict(type="str", required=True),
required_actions=dict(
type="list",
elements="dict",
options=dict(
alias=dict(type="str", required=True),
config=dict(type="dict"),
defaultAction=dict(type="bool"),
enabled=dict(type="bool"),
name=dict(type="str"),
priority=dict(type="int"),
providerId=dict(type="str"),
),
),
state=dict(type="str", choices=["present", "absent"], required=True),
)
argument_spec.update(meta_args)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
required_one_of=(
[["token", "auth_realm", "auth_username", "auth_password", "auth_client_id", "auth_client_secret"]]
),
required_together=([["auth_username", "auth_password"]]),
required_by={"refresh_token": "auth_realm"},
)
result = dict(changed=False, msg="", end_state={}, diff=dict(before={}, after={}))
# 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)
# Convenience variables
realm = module.params.get("realm")
desired_required_actions = module.params.get("required_actions")
state = module.params.get("state")
# Sanitize required actions
desired_required_actions = sanitize_required_actions(desired_required_actions)
# Filter out duplicate required actions
desired_required_actions = filter_duplicates(desired_required_actions)
# Get required actions
before_required_actions = kc.get_required_actions(realm=realm)
if state == "present":
# Initialize empty lists to hold the required actions that need to be
# registered, updated, and original ones of the updated one
register_required_actions = []
before_updated_required_actions = []
updated_required_actions = []
# Loop through the desired required actions and check if they exist in the before required actions
for desired_required_action in desired_required_actions:
found = False
# Loop through the before required actions and check if the aliases match
for before_required_action in before_required_actions:
if desired_required_action["alias"] == before_required_action["alias"]:
update_required = False
# Fill in the parameters
for k, v in before_required_action.items():
if k not in desired_required_action or desired_required_action[k] is None:
desired_required_action[k] = v
# Loop through the keys of the desired and before required actions
# and check if there are any differences between them
for key in desired_required_action.keys():
if (
key in before_required_action
and desired_required_action[key] != before_required_action[key]
):
update_required = True
break
# If there are differences, add the before and desired required actions
# to their respective lists for updating
if update_required:
before_updated_required_actions.append(before_required_action)
updated_required_actions.append(desired_required_action)
found = True
break
# If the desired required action is not found in the before required actions,
# add it to the list of required actions to register
if not found:
# Check if name is provided
if "name" not in desired_required_action or desired_required_action["name"] is None:
module.fail_json(
msg=f"Unable to register required action {desired_required_action['alias']} in realm {realm}: name not included"
)
# Check if provider ID is provided
if "providerId" not in desired_required_action or desired_required_action["providerId"] is None:
module.fail_json(
msg=f"Unable to register required action {desired_required_action['alias']} in realm {realm}: providerId not included"
)
register_required_actions.append(desired_required_action)
# Handle diff
if module._diff:
diff_required_actions = updated_required_actions.copy()
diff_required_actions.extend(register_required_actions)
result["diff"] = dict(before=before_updated_required_actions, after=diff_required_actions)
# Handle changed
if register_required_actions or updated_required_actions:
result["changed"] = True
# Handle check mode
if module.check_mode:
if register_required_actions or updated_required_actions:
result["change"] = True
result["msg"] = "Required actions would be registered/updated"
else:
result["change"] = False
result["msg"] = "Required actions would not be registered/updated"
module.exit_json(**result)
# Register required actions
if register_required_actions:
for register_required_action in register_required_actions:
kc.register_required_action(realm=realm, rep=register_required_action)
kc.update_required_action(
alias=register_required_action["alias"], realm=realm, rep=register_required_action
)
# Update required actions
if updated_required_actions:
for updated_required_action in updated_required_actions:
kc.update_required_action(
alias=updated_required_action["alias"], realm=realm, rep=updated_required_action
)
# Initialize the final list of required actions
final_required_actions = []
# Iterate over the before_required_actions
for before_required_action in before_required_actions:
# Check if there is an updated_required_action with the same alias
updated_required_action_found = False
for updated_required_action in updated_required_actions:
if updated_required_action["alias"] == before_required_action["alias"]:
# Merge the two dictionaries, favoring the values from updated_required_action
merged_dict = {}
for key in before_required_action.keys():
if key in updated_required_action:
merged_dict[key] = updated_required_action[key]
else:
merged_dict[key] = before_required_action[key]
for key in updated_required_action.keys():
if key not in before_required_action:
merged_dict[key] = updated_required_action[key]
# Add the merged dictionary to the final list of required actions
final_required_actions.append(merged_dict)
# Mark the updated_required_action as found
updated_required_action_found = True
# Stop looking for updated_required_action
break
# If no matching updated_required_action was found, add the before_required_action to the final list of required actions
if not updated_required_action_found:
final_required_actions.append(before_required_action)
# Append any remaining updated_required_actions that were not merged
for updated_required_action in updated_required_actions:
if not any(updated_required_action["alias"] == action["alias"] for action in final_required_actions):
final_required_actions.append(updated_required_action)
# Append newly registered required actions
final_required_actions.extend(register_required_actions)
# Handle message and end state
result["msg"] = "Required actions registered/updated"
result["end_state"] = final_required_actions
else:
# Filter out the deleted required actions
final_required_actions = []
delete_required_actions = []
for before_required_action in before_required_actions:
delete_action = False
for desired_required_action in desired_required_actions:
if before_required_action["alias"] == desired_required_action["alias"]:
delete_action = True
break
if not delete_action:
final_required_actions.append(before_required_action)
else:
delete_required_actions.append(before_required_action)
# Handle diff
if module._diff:
result["diff"] = dict(before=before_required_actions, after=final_required_actions)
# Handle changed
if delete_required_actions:
result["changed"] = True
# Handle check mode
if module.check_mode:
if final_required_actions:
result["change"] = True
result["msg"] = "Required actions would be deleted"
else:
result["change"] = False
result["msg"] = "Required actions would not be deleted"
module.exit_json(**result)
# Delete required actions
if delete_required_actions:
for delete_required_action in delete_required_actions:
kc.delete_required_action(alias=delete_required_action["alias"], realm=realm)
# Handle message and end state
result["msg"] = "Required actions deleted"
result["end_state"] = final_required_actions
module.exit_json(**result)
if __name__ == "__main__":
main()

Some files were not shown because too many files have changed in this diff Show More