mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-03-21 20:59:10 +00:00
keycloak_authentication_v2: verify providerIds (fix 11583) (#11585)
* 11583 verify providerIds in keycloak_authentication_v2 * 11583 code cleanup --------- Co-authored-by: Thomas Bargetz <thomas.bargetz@rise-world.com>
This commit is contained in:
parent
d8bb637cba
commit
25b5655be7
4 changed files with 118 additions and 1 deletions
|
|
@ -103,6 +103,7 @@ URL_REALM_GROUP_ROLEMAPPINGS = "{url}/admin/realms/{realm}/groups/{group}/role-m
|
||||||
|
|
||||||
URL_CLIENTSECRET = "{url}/admin/realms/{realm}/clients/{id}/client-secret"
|
URL_CLIENTSECRET = "{url}/admin/realms/{realm}/clients/{id}/client-secret"
|
||||||
|
|
||||||
|
URL_AUTHENTICATION_AUTHENTICATOR_PROVIDERS = "{url}/admin/realms/{realm}/authentication/authenticator-providers"
|
||||||
URL_AUTHENTICATION_FLOWS = "{url}/admin/realms/{realm}/authentication/flows"
|
URL_AUTHENTICATION_FLOWS = "{url}/admin/realms/{realm}/authentication/flows"
|
||||||
URL_AUTHENTICATION_FLOW = "{url}/admin/realms/{realm}/authentication/flows/{id}"
|
URL_AUTHENTICATION_FLOW = "{url}/admin/realms/{realm}/authentication/flows/{id}"
|
||||||
URL_AUTHENTICATION_FLOW_COPY = "{url}/admin/realms/{realm}/authentication/flows/{copyfrom}/copy"
|
URL_AUTHENTICATION_FLOW_COPY = "{url}/admin/realms/{realm}/authentication/flows/{copyfrom}/copy"
|
||||||
|
|
@ -2253,6 +2254,19 @@ class KeycloakAPI:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Unable to delete role {name} for client {clientid} in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Unable to delete role {name} for client {clientid} in realm {realm}: {e}")
|
||||||
|
|
||||||
|
def get_authenticator_providers(self, realm: str = "master"):
|
||||||
|
"""
|
||||||
|
Get all available authenticator providers of the realm.
|
||||||
|
:param realm: Realm.
|
||||||
|
:return: List of authenticator provider representations.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self._request_and_deserialize(
|
||||||
|
URL_AUTHENTICATION_AUTHENTICATOR_PROVIDERS.format(url=self.baseurl, realm=realm), method="GET"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.fail_request(e, msg=f"Unable get authenticator providers in realm {realm}: {e}")
|
||||||
|
|
||||||
def get_authentication_flow_by_alias(self, alias, realm: str = "master"):
|
def get_authentication_flow_by_alias(self, alias, realm: str = "master"):
|
||||||
"""
|
"""
|
||||||
Get an authentication flow by its alias
|
Get an authentication flow by its alias
|
||||||
|
|
|
||||||
|
|
@ -810,6 +810,35 @@ def create_authentication_execution_spec_options(depth: int) -> dict[str, t.Any]
|
||||||
return options
|
return options
|
||||||
|
|
||||||
|
|
||||||
|
def validate_executions(kc: KeycloakAPI, realm: str, executions: dict) -> None:
|
||||||
|
valid_providers = kc.get_authenticator_providers(realm)
|
||||||
|
valid_provider_ids = {provider["id"] for provider in valid_providers}
|
||||||
|
|
||||||
|
invalid_provider_ids = validate_executions_rec(valid_provider_ids, executions)
|
||||||
|
if len(invalid_provider_ids) > 0:
|
||||||
|
invalid_provider_ids_str = ", ".join(f"'{item}'" for item in invalid_provider_ids)
|
||||||
|
raise ValueError(
|
||||||
|
f"The following execution providerIds are unknown and therefore invalid: {invalid_provider_ids_str}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_executions_rec(valid_provider_ids: set, executions: dict) -> list:
|
||||||
|
invalid_provider_ids = []
|
||||||
|
for execution in executions:
|
||||||
|
provider_id = execution["providerId"]
|
||||||
|
sub_flow = execution["subFlow"]
|
||||||
|
if provider_id is not None:
|
||||||
|
if provider_id not in valid_provider_ids:
|
||||||
|
invalid_provider_ids.append(provider_id)
|
||||||
|
|
||||||
|
if sub_flow is not None:
|
||||||
|
invalid_provider_ids.extend(
|
||||||
|
validate_executions_rec(valid_provider_ids, execution["authenticationExecutions"])
|
||||||
|
)
|
||||||
|
|
||||||
|
return invalid_provider_ids
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
"""Module entry point."""
|
"""Module entry point."""
|
||||||
argument_spec = keycloak_argument_spec()
|
argument_spec = keycloak_argument_spec()
|
||||||
|
|
@ -869,6 +898,14 @@ def main() -> None:
|
||||||
existing_auth_diff_repr = existing_auth_to_diff_repr(kc, realm, existing_auth)
|
existing_auth_diff_repr = existing_auth_to_diff_repr(kc, realm, existing_auth)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
try:
|
||||||
|
validate_executions(kc, realm, desired_auth["authenticationExecutions"])
|
||||||
|
except ValueError as e:
|
||||||
|
module.fail_json(
|
||||||
|
msg=f"Validation of executions failed: {e}",
|
||||||
|
exception=traceback.format_exc(),
|
||||||
|
)
|
||||||
|
|
||||||
if not existing_auth:
|
if not existing_auth:
|
||||||
if state == "absent":
|
if state == "absent":
|
||||||
# The flow does not exist and is not required; nothing to do.
|
# The flow does not exist and is not required; nothing to do.
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,7 @@
|
||||||
- name: Executing flow deletion tests
|
- name: Executing flow deletion tests
|
||||||
ansible.builtin.include_tasks:
|
ansible.builtin.include_tasks:
|
||||||
file: tests/test_flow_deletion.yml
|
file: tests/test_flow_deletion.yml
|
||||||
|
|
||||||
|
- name: Invalid providerIds in execution tests
|
||||||
|
ansible.builtin.include_tasks:
|
||||||
|
file: tests/test_invalid_poviderid_flow_creation.yml
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
# 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
|
||||||
|
---
|
||||||
|
- name: Setup Test
|
||||||
|
ansible.builtin.include_tasks:
|
||||||
|
file: test_setup.yml
|
||||||
|
|
||||||
|
- name: Flow Creation/Update <Integration Test Flow with invalid providerid in execution>
|
||||||
|
community.general.keycloak_authentication_v2:
|
||||||
|
auth_keycloak_url: "{{ url }}"
|
||||||
|
auth_realm: "{{ admin_realm }}"
|
||||||
|
auth_username: "{{ admin_user }}"
|
||||||
|
auth_password: "{{ admin_password }}"
|
||||||
|
realm: "{{ realm }}"
|
||||||
|
alias: Integration Test Flow with invalid providerid in execution
|
||||||
|
state: present
|
||||||
|
authenticationExecutions:
|
||||||
|
- providerId: idp-review-profile
|
||||||
|
requirement: REQUIRED
|
||||||
|
authenticationConfig:
|
||||||
|
alias: Integration Test Flow - review profile config
|
||||||
|
config:
|
||||||
|
update.profile.on.first.login: "missing"
|
||||||
|
- subFlow: Integration Test Flow - User creation or linking
|
||||||
|
requirement: REQUIRED
|
||||||
|
authenticationExecutions:
|
||||||
|
- providerId: invalid-providerid
|
||||||
|
requirement: ALTERNATIVE
|
||||||
|
- subFlow: Integration Test Flow - Handle Existing Account
|
||||||
|
requirement: ALTERNATIVE
|
||||||
|
authenticationExecutions:
|
||||||
|
- providerId: another-invalid-providerid
|
||||||
|
requirement: REQUIRED
|
||||||
|
- providerId: auth-cookie
|
||||||
|
requirement: REQUIRED
|
||||||
|
ignore_errors: true
|
||||||
|
register: invalid_providerid_in_flow_result
|
||||||
|
|
||||||
|
- name: Verify that invalid providerId causes failure
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- invalid_providerid_in_flow_result is failed
|
||||||
|
- invalid_providerid_in_flow_result is not changed
|
||||||
|
- >-
|
||||||
|
invalid_providerid_in_flow_result.msg == "Validation of executions failed: The following execution providerIds are unknown and therefore invalid: 'invalid-providerid', 'another-invalid-providerid'"
|
||||||
|
|
||||||
|
- name: Retrieve access token
|
||||||
|
ansible.builtin.include_tasks:
|
||||||
|
file: ../actions/fetch_access_token.yml
|
||||||
|
|
||||||
|
- name: Assert that the flow did not get created
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ url }}/admin/realms/{{ realm }}/authentication/flows/Integration%20Test%20Flow%20with%20invalid%20providerid%20in%20execution/executions"
|
||||||
|
method: GET
|
||||||
|
headers:
|
||||||
|
Accept: application/json
|
||||||
|
User-agent: Ansible
|
||||||
|
Authorization: "Bearer {{ access_token }}"
|
||||||
|
return_content: true
|
||||||
|
status_code: 404
|
||||||
|
register: flow_response
|
||||||
Loading…
Add table
Add a link
Reference in a new issue