Skip to content

Commit 133a0b4

Browse files
committed
Add OIDC authentication flows tests for federation
This adds support for testing all OIDC authentication methods: - v3oidcpassword (Resource Owner Password Credentials) - v3oidcclientcredentials (Client Credentials) - v3oidcaccesstoken (Access Token Reuse) - v3oidcauthcode (Authorization Code) Note: v3oidcdeviceauthz requires Python 3.10+ and is not available in OSP18 which ships with Python 3.9.
1 parent e5fa7b6 commit 133a0b4

11 files changed

+786
-4
lines changed

galaxy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace: cifmw
88
name: general
99

1010
# The version of the collection. Must be compatible with semantic versioning
11-
version: 1.0.0
11+
version: 1.0.0+e5fa7b63
1212

1313
# The path to the Markdown (.md) readme file. This path is relative to the root of the collection
1414
readme: README.md

roles/federation/README.md

Lines changed: 149 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,150 @@
1-
federation
2-
=========
1+
# federation
32

4-
This role will setup Openstack for user federation. The keycloak system will be used for the IdP provider.
3+
This role sets up OpenStack Keystone federation with Keycloak (Red Hat SSO) as the Identity Provider.
4+
5+
## Overview
6+
7+
The federation role configures:
8+
- Keycloak realm(s) with test users and groups
9+
- Keystone Identity Provider and protocol configuration
10+
- OIDC authentication for OpenStack CLI
11+
- Comprehensive authentication testing
12+
13+
## Supported OIDC Authentication Methods
14+
15+
This role supports testing all OIDC authentication methods available in keystoneauth1:
16+
17+
| Plugin Name | Description | Status |
18+
|-------------|-------------|--------|
19+
| `v3oidcpassword` | Resource Owner Password Credentials flow | ✅ Supported |
20+
| `v3oidcclientcredentials` | Client Credentials flow | ✅ Supported |
21+
| `v3oidcaccesstoken` | Reuse existing access token | ✅ Supported |
22+
| `v3oidcauthcode` | Authorization Code flow | ✅ Supported |
23+
| `v3oidcdeviceauthz` | Device Authorization flow (RFC 8628) | ⚠️ Requires Python 3.10+ |
24+
25+
## Variables
26+
27+
### Infrastructure Configuration
28+
29+
| Variable | Default | Description |
30+
|----------|---------|-------------|
31+
| `cifmw_federation_keycloak_namespace` | `openstack` | Kubernetes namespace for Keycloak |
32+
| `cifmw_federation_run_osp_cmd_namespace` | `openstack` | Kubernetes namespace for openstackclient |
33+
| `cifmw_federation_domain` | - | Base domain for service URLs |
34+
35+
### Keycloak Configuration
36+
37+
| Variable | Default | Description |
38+
|----------|---------|-------------|
39+
| `cifmw_federation_keycloak_realm` | `openstack` | Primary Keycloak realm name |
40+
| `cifmw_federation_keycloak_realm2` | `openstack2` | Secondary realm (multirealm mode) |
41+
| `cifmw_federation_keycloak_admin_username` | `admin` | Keycloak admin username |
42+
| `cifmw_federation_keycloak_admin_password` | `nomoresecrets` | Keycloak admin password |
43+
| `cifmw_federation_deploy_multirealm` | `false` | Deploy multiple realms |
44+
45+
### Test Users
46+
47+
| Variable | Default | Description |
48+
|----------|---------|-------------|
49+
| `cifmw_federation_keycloak_testuser1_username` | `kctestuser1` | Test user 1 username |
50+
| `cifmw_federation_keycloak_testuser1_password` | `nomoresecrets1` | Test user 1 password |
51+
| `cifmw_federation_keycloak_testuser2_username` | `kctestuser2` | Test user 2 username |
52+
| `cifmw_federation_keycloak_testuser2_password` | `nomoresecrets2` | Test user 2 password |
53+
54+
### Keystone Integration
55+
56+
| Variable | Default | Description |
57+
|----------|---------|-------------|
58+
| `cifmw_federation_IdpName` | `kcIDP` | Identity Provider name in Keystone |
59+
| `cifmw_federation_keystone_domain` | `SSO` | Keystone domain for federated users |
60+
| `cifmw_federation_mapping_name` | `SSOmap` | Keystone mapping name |
61+
| `cifmw_federation_project_name` | `SSOproject` | Project for federated users |
62+
| `cifmw_federation_group_name` | `SSOgroup` | Group for federated users |
63+
64+
### OIDC Client Configuration
65+
66+
| Variable | Default | Description |
67+
|----------|---------|-------------|
68+
| `cifmw_federation_keystone_OIDC_ClientID` | `rhoso` | OIDC client ID |
69+
| `cifmw_federation_keystone_OIDC_ClientSecret` | `COX8bmlKAWn56XCGMrKQJj7dgHNAOl6f` | OIDC client secret |
70+
| `cifmw_federation_keystone_OIDC_Scope` | `openid email profile` | OIDC scopes |
71+
72+
### Testing Configuration
73+
74+
| Variable | Default | Description |
75+
|----------|---------|-------------|
76+
| `cifmw_federation_run_oidc_auth_tests` | `true` | Run comprehensive OIDC auth tests |
77+
78+
## Task Files
79+
80+
### Main Tasks
81+
82+
- `hook_pre_deploy.yml` - Deploys Keycloak before OpenStack
83+
- `hook_post_deploy.yml` - Configures federation after OpenStack deployment
84+
- `hook_controlplane_config.yml` - Adds federation config to control plane
85+
86+
### Setup Tasks
87+
88+
- `run_keycloak_setup.yml` - Deploy Keycloak operator and instance
89+
- `run_keycloak_realm_setup.yml` - Configure Keycloak realm, users, and client
90+
- `run_keycloak_client_setup.yml` - Enable advanced client features (Service Accounts, Device Auth)
91+
- `run_openstack_setup.yml` - Configure Keystone IdP and mappings
92+
- `run_openstack_auth_setup.yml` - Deploy authentication scripts to openstackclient pod
93+
94+
### Test Tasks
95+
96+
- `run_openstack_auth_test.yml` - Basic v3oidcpassword authentication test
97+
- `run_openstack_oidc_auth_tests.yml` - Comprehensive OIDC authentication test suite
98+
99+
## Authentication Scripts
100+
101+
The following scripts are deployed to `/home/cloud-admin/` in the openstackclient pod:
102+
103+
| Script | Description |
104+
|--------|-------------|
105+
| `get-token.sh <user>` | Get token using v3oidcpassword |
106+
| `oidc-clientcredentials.sh` | Configure v3oidcclientcredentials auth |
107+
| `oidc-accesstoken.sh <token>` | Configure v3oidcaccesstoken auth |
108+
| `oidc-authcode.sh <code>` | Configure v3oidcauthcode auth |
109+
| `get-keycloak-token.sh` | Helper to obtain tokens from Keycloak |
110+
111+
### Example Usage
112+
113+
```bash
114+
# v3oidcpassword - Password flow
115+
kubectl exec -n openstack openstackclient -- bash -c \
116+
'source /home/cloud-admin/kctestuser1 && openstack token issue'
117+
118+
# v3oidcclientcredentials - Client Credentials flow
119+
kubectl exec -n openstack openstackclient -- bash -c \
120+
'source /home/cloud-admin/oidc-clientcredentials.sh && openstack token issue'
121+
122+
# v3oidcaccesstoken - Access Token flow
123+
ACCESS_TOKEN=$(/home/cloud-admin/get-keycloak-token.sh access_token kctestuser1 nomoresecrets1)
124+
kubectl exec -n openstack openstackclient -- bash -c \
125+
"source /home/cloud-admin/oidc-accesstoken.sh '$ACCESS_TOKEN' && openstack token issue"
126+
127+
# v3oidcauthcode - Authorization Code flow
128+
AUTH_CODE=$(/home/cloud-admin/get-keycloak-token.sh auth_code kctestuser1 nomoresecrets1)
129+
kubectl exec -n openstack openstackclient -- bash -c \
130+
"source /home/cloud-admin/oidc-authcode.sh '$AUTH_CODE' && openstack token issue"
131+
```
132+
133+
## Test Execution
134+
135+
The comprehensive OIDC authentication tests are automatically run during the `hook_post_deploy.yml` phase when `cifmw_federation_run_oidc_auth_tests` is `true` (default).
136+
137+
To run the tests manually:
138+
139+
```yaml
140+
- name: Run OIDC authentication tests
141+
ansible.builtin.include_role:
142+
name: federation
143+
tasks_from: run_openstack_oidc_auth_tests.yml
144+
```
145+
146+
## Notes
147+
148+
- **Device Authorization Flow**: The `v3oidcdeviceauthz` plugin requires keystoneauth1 with Python 3.10+ support. OSP18 ships with Python 3.9 and does not include this plugin.
149+
- **Multirealm**: CLI-based OIDC authentication testing only works in single realm mode. Multirealm federation is supported for Horizon-based authentication.
150+
- **Keycloak Client**: The role automatically enables Service Accounts and Device Authorization on the Keycloak client to support all authentication methods.

roles/federation/defaults/main.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,20 @@ cifmw_federation_keystone_idp1_provider_filename: "keycloak-{{ cifmw_federation_
147147
cifmw_federation_keystone_idp2_conf_filename: "keycloak-{{ cifmw_federation_keycloak_namespace }}.{{ cifmw_federation_domain }}%2Fauth%2Frealms%2F{{ cifmw_federation_keycloak_realm2 }}.conf"
148148
cifmw_federation_keystone_idp2_client_filename: "keycloak-{{ cifmw_federation_keycloak_namespace }}.{{ cifmw_federation_domain }}%2Fauth%2Frealms%2F{{ cifmw_federation_keycloak_realm2 }}.client"
149149
cifmw_federation_keystone_idp2_provider_filename: "keycloak-{{ cifmw_federation_keycloak_namespace }}.{{ cifmw_federation_domain }}%2Fauth%2Frealms%2F{{ cifmw_federation_keycloak_realm2 }}.provider"
150+
151+
# =============================================================================
152+
# OIDC AUTHENTICATION TESTING
153+
# =============================================================================
154+
# Configuration for comprehensive OIDC authentication method testing
155+
#
156+
# When enabled, tests all supported OIDC authentication methods:
157+
# - v3oidcpassword: Resource Owner Password Credentials flow
158+
# - v3oidcclientcredentials: Client Credentials flow
159+
# - v3oidcaccesstoken: Access Token Reuse flow
160+
# - v3oidcauthcode: Authorization Code flow
161+
#
162+
# Note: v3oidcdeviceauthz (Device Authorization flow) requires Python 3.10+
163+
# and is not available in OSP18.
164+
165+
# Enable/disable comprehensive OIDC authentication method tests
166+
cifmw_federation_run_oidc_auth_tests: true

roles/federation/tasks/hook_post_deploy.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,26 @@
7878
- "{{ cifmw_federation_keycloak_testuser1_username }}"
7979
- "{{ cifmw_federation_keycloak_testuser2_username }}"
8080
when: not cifmw_federation_deploy_multirealm|bool
81+
82+
# =============================================================================
83+
# Comprehensive OIDC Authentication Methods Testing
84+
# =============================================================================
85+
# Tests all supported OIDC authentication methods when enabled.
86+
# This requires Keycloak client to be configured for Service Accounts
87+
# and Device Authorization.
88+
89+
- name: Configure Keycloak client for all OIDC auth methods
90+
ansible.builtin.include_role:
91+
name: federation
92+
tasks_from: run_keycloak_client_setup.yml
93+
when:
94+
- not cifmw_federation_deploy_multirealm | bool
95+
- cifmw_federation_run_oidc_auth_tests | default(true) | bool
96+
97+
- name: Run comprehensive OIDC authentication method tests
98+
ansible.builtin.include_role:
99+
name: federation
100+
tasks_from: run_openstack_oidc_auth_tests.yml
101+
when:
102+
- not cifmw_federation_deploy_multirealm | bool
103+
- cifmw_federation_run_oidc_auth_tests | default(true) | bool
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
# Copyright Red Hat, Inc.
3+
# All Rights Reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License. You may obtain
7+
# a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
# License for the specific language governing permissions and limitations
15+
# under the License.
16+
17+
# =============================================================================
18+
# Keycloak Client Configuration for OIDC Authentication Methods
19+
# =============================================================================
20+
# This task configures the Keycloak client to support all OIDC authentication
21+
# methods including:
22+
# - Service Accounts (for Client Credentials flow)
23+
# - Device Authorization (for Device Authorization flow)
24+
#
25+
# Prerequisites:
26+
# - Keycloak must be deployed and accessible
27+
# - The OIDC client (rhoso) must already exist
28+
# =============================================================================
29+
30+
- name: Get Keycloak admin token
31+
ansible.builtin.uri:
32+
url: "{{ cifmw_federation_keycloak_url }}/auth/realms/master/protocol/openid-connect/token"
33+
method: POST
34+
body_format: form-urlencoded
35+
body:
36+
grant_type: password
37+
client_id: admin-cli
38+
username: "{{ cifmw_federation_keycloak_admin_username }}"
39+
password: "{{ cifmw_federation_keycloak_admin_password }}"
40+
validate_certs: "{{ cifmw_federation_keycloak_url_validate_certs }}"
41+
status_code: 200
42+
register: federation_keycloak_admin_token_response
43+
44+
- name: Set admin token fact
45+
ansible.builtin.set_fact:
46+
federation_keycloak_admin_token: "{{ federation_keycloak_admin_token_response.json.access_token }}"
47+
48+
- name: Get current client configuration
49+
ansible.builtin.uri:
50+
url: "{{ cifmw_federation_keycloak_url }}/auth/admin/realms/{{ cifmw_federation_keycloak_realm }}/clients?clientId={{ cifmw_federation_keystone_OIDC_ClientID }}"
51+
method: GET
52+
headers:
53+
Authorization: "Bearer {{ federation_keycloak_admin_token }}"
54+
validate_certs: "{{ cifmw_federation_keycloak_url_validate_certs }}"
55+
status_code: 200
56+
register: federation_keycloak_client_response
57+
58+
- name: Extract client ID
59+
ansible.builtin.set_fact:
60+
federation_keycloak_client_uuid: "{{ federation_keycloak_client_response.json[0].id }}"
61+
when: federation_keycloak_client_response.json | length > 0
62+
63+
- name: Display current client configuration
64+
ansible.builtin.debug:
65+
msg:
66+
- "Client ID: {{ cifmw_federation_keystone_OIDC_ClientID }}"
67+
- "Client UUID: {{ federation_keycloak_client_uuid }}"
68+
- "Service Accounts Enabled: {{ federation_keycloak_client_response.json[0].serviceAccountsEnabled | default(false) }}"
69+
- "Device Authorization: {{ federation_keycloak_client_response.json[0].attributes.get('oauth2.device.authorization.grant.enabled', 'false') | default('false') }}"
70+
when: federation_keycloak_client_response.json | length > 0
71+
72+
- name: Update client to enable Service Accounts and Device Authorization
73+
ansible.builtin.uri:
74+
url: "{{ cifmw_federation_keycloak_url }}/auth/admin/realms/{{ cifmw_federation_keycloak_realm }}/clients/{{ federation_keycloak_client_uuid }}"
75+
method: PUT
76+
headers:
77+
Authorization: "Bearer {{ federation_keycloak_admin_token }}"
78+
Content-Type: application/json
79+
body_format: json
80+
body:
81+
clientId: "{{ cifmw_federation_keystone_OIDC_ClientID }}"
82+
serviceAccountsEnabled: true
83+
directAccessGrantsEnabled: true
84+
standardFlowEnabled: true
85+
implicitFlowEnabled: true
86+
publicClient: false
87+
attributes:
88+
oauth2.device.authorization.grant.enabled: "true"
89+
validate_certs: "{{ cifmw_federation_keycloak_url_validate_certs }}"
90+
status_code: 204
91+
when: federation_keycloak_client_response.json | length > 0
92+
register: federation_keycloak_client_update
93+
94+
- name: Verify updated client configuration
95+
ansible.builtin.uri:
96+
url: "{{ cifmw_federation_keycloak_url }}/auth/admin/realms/{{ cifmw_federation_keycloak_realm }}/clients/{{ federation_keycloak_client_uuid }}"
97+
method: GET
98+
headers:
99+
Authorization: "Bearer {{ federation_keycloak_admin_token }}"
100+
validate_certs: "{{ cifmw_federation_keycloak_url_validate_certs }}"
101+
status_code: 200
102+
register: federation_keycloak_client_updated
103+
when: federation_keycloak_client_response.json | length > 0
104+
105+
- name: Display updated client configuration
106+
ansible.builtin.debug:
107+
msg:
108+
- "Client updated successfully"
109+
- "Service Accounts Enabled: {{ federation_keycloak_client_updated.json.serviceAccountsEnabled }}"
110+
- "Direct Access Grants: {{ federation_keycloak_client_updated.json.directAccessGrantsEnabled }}"
111+
- "Standard Flow: {{ federation_keycloak_client_updated.json.standardFlowEnabled }}"
112+
- "Device Authorization: {{ federation_keycloak_client_updated.json.attributes.get('oauth2.device.authorization.grant.enabled', 'false') }}"
113+
when:
114+
- federation_keycloak_client_response.json | length > 0
115+
- federation_keycloak_client_updated is defined
116+

roles/federation/tasks/run_openstack_auth_setup.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,63 @@
7575
pod: openstackclient
7676
remote_path: "/home/cloud-admin/full-ca-list.crt"
7777
local_path: "{{ [ ansible_user_dir, 'ci-framework-data', 'tmp', 'full-ca-list.crt' ] | path_join }}"
78+
79+
# =============================================================================
80+
# OIDC Authentication Method Scripts
81+
# =============================================================================
82+
# These scripts support testing different OIDC authentication methods:
83+
# - v3oidcclientcredentials: Client Credentials flow
84+
# - v3oidcaccesstoken: Access Token Reuse flow
85+
# - v3oidcauthcode: Authorization Code flow
86+
87+
- name: Render OIDC client credentials script
88+
ansible.builtin.template:
89+
src: oidc-clientcredentials.sh.j2
90+
dest: "{{ [ansible_user_dir, 'ci-framework-data', 'tmp', 'oidc-clientcredentials.sh'] | path_join }}"
91+
mode: '0755'
92+
93+
- name: Copy OIDC client credentials script to pod
94+
kubernetes.core.k8s_cp:
95+
namespace: "{{ cifmw_federation_run_osp_cmd_namespace }}"
96+
pod: openstackclient
97+
remote_path: "/home/cloud-admin/oidc-clientcredentials.sh"
98+
local_path: "{{ [ansible_user_dir, 'ci-framework-data', 'tmp', 'oidc-clientcredentials.sh'] | path_join }}"
99+
100+
- name: Render OIDC access token script
101+
ansible.builtin.template:
102+
src: oidc-accesstoken.sh.j2
103+
dest: "{{ [ansible_user_dir, 'ci-framework-data', 'tmp', 'oidc-accesstoken.sh'] | path_join }}"
104+
mode: '0755'
105+
106+
- name: Copy OIDC access token script to pod
107+
kubernetes.core.k8s_cp:
108+
namespace: "{{ cifmw_federation_run_osp_cmd_namespace }}"
109+
pod: openstackclient
110+
remote_path: "/home/cloud-admin/oidc-accesstoken.sh"
111+
local_path: "{{ [ansible_user_dir, 'ci-framework-data', 'tmp', 'oidc-accesstoken.sh'] | path_join }}"
112+
113+
- name: Render OIDC authorization code script
114+
ansible.builtin.template:
115+
src: oidc-authcode.sh.j2
116+
dest: "{{ [ansible_user_dir, 'ci-framework-data', 'tmp', 'oidc-authcode.sh'] | path_join }}"
117+
mode: '0755'
118+
119+
- name: Copy OIDC authorization code script to pod
120+
kubernetes.core.k8s_cp:
121+
namespace: "{{ cifmw_federation_run_osp_cmd_namespace }}"
122+
pod: openstackclient
123+
remote_path: "/home/cloud-admin/oidc-authcode.sh"
124+
local_path: "{{ [ansible_user_dir, 'ci-framework-data', 'tmp', 'oidc-authcode.sh'] | path_join }}"
125+
126+
- name: Render Keycloak token helper script
127+
ansible.builtin.template:
128+
src: get-keycloak-token.sh.j2
129+
dest: "{{ [ansible_user_dir, 'ci-framework-data', 'tmp', 'get-keycloak-token.sh'] | path_join }}"
130+
mode: '0755'
131+
132+
- name: Copy Keycloak token helper script to pod
133+
kubernetes.core.k8s_cp:
134+
namespace: "{{ cifmw_federation_run_osp_cmd_namespace }}"
135+
pod: openstackclient
136+
remote_path: "/home/cloud-admin/get-keycloak-token.sh"
137+
local_path: "{{ [ansible_user_dir, 'ci-framework-data', 'tmp', 'get-keycloak-token.sh'] | path_join }}"

0 commit comments

Comments
 (0)