1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Keycloak set client authentification flows by name (#8428)

* first commit

* Add change logs

* fix sanity

* Sanity 2

* Test unset flows

* Update plugins/modules/keycloak_client.py

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

* Update plugins/modules/keycloak_client.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update changelogs/fragments/8428-assign-auth-flow-by-name-keycloak-client.yaml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Remove double traitement from "alias"

* Update plugins/modules/keycloak_client.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/keycloak_client.py

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Andre Desrosiers <andre.desrosiers@ssss.gouv.qc.ca>
Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
desand01 2024-06-17 01:06:47 -04:00 committed by GitHub
parent df7fe19bbe
commit b11da288d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 240 additions and 1 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- keycloak_client - assign auth flow by name (https://github.com/ansible-collections/community.general/pull/8428).

View file

@ -340,6 +340,42 @@ options:
description: description:
- Override realm authentication flow bindings. - Override realm authentication flow bindings.
type: dict type: dict
suboptions:
browser:
description:
- Flow ID of the browser authentication flow.
- O(authentication_flow_binding_overrides.browser)
and O(authentication_flow_binding_overrides.browser_name) are mutually exclusive.
type: str
browser_name:
description:
- Flow name of the browser authentication flow.
- O(authentication_flow_binding_overrides.browser)
and O(authentication_flow_binding_overrides.browser_name) are mutually exclusive.
aliases:
- browserName
type: str
version_added: 9.1.0
direct_grant:
description:
- Flow ID of the direct grant authentication flow.
- O(authentication_flow_binding_overrides.direct_grant)
and O(authentication_flow_binding_overrides.direct_grant_name) are mutually exclusive.
aliases:
- directGrant
type: str
direct_grant_name:
description:
- Flow name of the direct grant authentication flow.
- O(authentication_flow_binding_overrides.direct_grant)
and O(authentication_flow_binding_overrides.direct_grant_name) are mutually exclusive.
aliases:
- directGrantName
type: str
version_added: 9.1.0
aliases: aliases:
- authenticationFlowBindingOverrides - authenticationFlowBindingOverrides
version_added: 3.4.0 version_added: 3.4.0
@ -781,6 +817,64 @@ def sanitize_cr(clientrep):
return normalise_cr(result) return normalise_cr(result)
def get_authentication_flow_id(flow_name, realm, kc):
""" Get the authentication flow ID based on the flow name, realm, and Keycloak client.
Args:
flow_name (str): The name of the authentication flow.
realm (str): The name of the realm.
kc (KeycloakClient): The Keycloak client instance.
Returns:
str: The ID of the authentication flow.
Raises:
KeycloakAPIException: If the authentication flow with the given name is not found in the realm.
"""
flow = kc.get_authentication_flow_by_alias(flow_name, realm)
if flow:
return flow["id"]
kc.module.fail_json(msg='Authentification flow %s not found in realm %s' % (flow_name, realm))
def flow_binding_from_dict_to_model(newClientFlowBinding, realm, kc):
""" Convert a dictionary representing client flow bindings to a model representation.
Args:
newClientFlowBinding (dict): A dictionary containing client flow bindings.
realm (str): The name of the realm.
kc (KeycloakClient): An instance of the KeycloakClient class.
Returns:
dict: A dictionary representing the model flow bindings. The dictionary has two keys:
- "browser" (str or None): The ID of the browser authentication flow binding, or None if not provided.
- "direct_grant" (str or None): The ID of the direct grant authentication flow binding, or None if not provided.
Raises:
KeycloakAPIException: If the authentication flow with the given name is not found in the realm.
"""
modelFlow = {
"browser": None,
"direct_grant": None
}
for k, v in newClientFlowBinding.items():
if not v:
continue
if k == "browser":
modelFlow["browser"] = v
elif k == "browser_name":
modelFlow["browser"] = get_authentication_flow_id(v, realm, kc)
elif k == "direct_grant":
modelFlow["direct_grant"] = v
elif k == "direct_grant_name":
modelFlow["direct_grant"] = get_authentication_flow_id(v, realm, kc)
return modelFlow
def main(): def main():
""" """
Module execution Module execution
@ -799,6 +893,13 @@ def main():
config=dict(type='dict'), config=dict(type='dict'),
) )
authentication_flow_spec = dict(
browser=dict(type='str'),
browser_name=dict(type='str', aliases=['browserName']),
direct_grant=dict(type='str', aliases=['directGrant']),
direct_grant_name=dict(type='str', aliases=['directGrantName']),
)
meta_args = dict( meta_args = dict(
state=dict(default='present', choices=['present', 'absent']), state=dict(default='present', choices=['present', 'absent']),
realm=dict(type='str', default='master'), realm=dict(type='str', default='master'),
@ -838,7 +939,13 @@ def main():
use_template_scope=dict(type='bool', aliases=['useTemplateScope']), use_template_scope=dict(type='bool', aliases=['useTemplateScope']),
use_template_mappers=dict(type='bool', aliases=['useTemplateMappers']), use_template_mappers=dict(type='bool', aliases=['useTemplateMappers']),
always_display_in_console=dict(type='bool', aliases=['alwaysDisplayInConsole']), always_display_in_console=dict(type='bool', aliases=['alwaysDisplayInConsole']),
authentication_flow_binding_overrides=dict(type='dict', aliases=['authenticationFlowBindingOverrides']), authentication_flow_binding_overrides=dict(
type='dict',
aliases=['authenticationFlowBindingOverrides'],
options=authentication_flow_spec,
required_one_of=[['browser', 'direct_grant', 'browser_name', 'direct_grant_name']],
mutually_exclusive=[['browser', 'browser_name'], ['direct_grant', 'direct_grant_name']],
),
protocol_mappers=dict(type='list', elements='dict', options=protmapper_spec, aliases=['protocolMappers']), protocol_mappers=dict(type='list', elements='dict', options=protmapper_spec, aliases=['protocolMappers']),
authorization_settings=dict(type='dict', aliases=['authorizationSettings']), authorization_settings=dict(type='dict', aliases=['authorizationSettings']),
default_client_scopes=dict(type='list', elements='str', aliases=['defaultClientScopes']), default_client_scopes=dict(type='list', elements='str', aliases=['defaultClientScopes']),
@ -900,6 +1007,8 @@ def main():
# they are not specified # they are not specified
if client_param == 'protocol_mappers': if client_param == 'protocol_mappers':
new_param_value = [dict((k, v) for k, v in x.items() if x[k] is not None) for x in new_param_value] new_param_value = [dict((k, v) for k, v in x.items() if x[k] is not None) for x in new_param_value]
elif client_param == 'authentication_flow_binding_overrides':
new_param_value = flow_binding_from_dict_to_model(new_param_value, realm, kc)
changeset[camel(client_param)] = new_param_value changeset[camel(client_param)] = new_param_value

View file

@ -103,3 +103,131 @@
assert: assert:
that: that:
- check_client_when_present_and_changed is changed - check_client_when_present_and_changed is changed
- name: Desire client with flow binding overrides
community.general.keycloak_client:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_id }}"
state: present
redirect_uris: '{{redirect_uris1}}'
attributes: '{{client_attributes1}}'
protocol_mappers: '{{protocol_mappers1}}'
authentication_flow_binding_overrides:
browser_name: browser
direct_grant_name: direct grant
register: desire_client_with_flow_binding_overrides
- name: Assert flows are set
assert:
that:
- desire_client_with_flow_binding_overrides is changed
- "'authenticationFlowBindingOverrides' in desire_client_with_flow_binding_overrides.end_state"
- desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.browser | length > 0
- desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.direct_grant | length > 0
- name: Backup flow UUIDs
set_fact:
flow_browser_uuid: "{{ desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.browser }}"
flow_direct_grant_uuid: "{{ desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.direct_grant }}"
- name: Desire client with flow binding overrides remove direct_grant_name
community.general.keycloak_client:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_id }}"
state: present
redirect_uris: '{{redirect_uris1}}'
attributes: '{{client_attributes1}}'
protocol_mappers: '{{protocol_mappers1}}'
authentication_flow_binding_overrides:
browser_name: browser
register: desire_client_with_flow_binding_overrides
- name: Assert flows are updated
assert:
that:
- desire_client_with_flow_binding_overrides is changed
- "'authenticationFlowBindingOverrides' in desire_client_with_flow_binding_overrides.end_state"
- desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.browser | length > 0
- "'direct_grant' not in desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides"
- name: Desire client with flow binding overrides remove browser add direct_grant
community.general.keycloak_client:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_id }}"
state: present
redirect_uris: '{{redirect_uris1}}'
attributes: '{{client_attributes1}}'
protocol_mappers: '{{protocol_mappers1}}'
authentication_flow_binding_overrides:
direct_grant_name: direct grant
register: desire_client_with_flow_binding_overrides
- name: Assert flows are updated
assert:
that:
- desire_client_with_flow_binding_overrides is changed
- "'authenticationFlowBindingOverrides' in desire_client_with_flow_binding_overrides.end_state"
- "'browser' not in desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides"
- desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.direct_grant | length > 0
- name: Desire client with flow binding overrides with UUIDs
community.general.keycloak_client:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_id }}"
state: present
redirect_uris: '{{redirect_uris1}}'
attributes: '{{client_attributes1}}'
protocol_mappers: '{{protocol_mappers1}}'
authentication_flow_binding_overrides:
browser: "{{ flow_browser_uuid }}"
direct_grant: "{{ flow_direct_grant_uuid }}"
register: desire_client_with_flow_binding_overrides
- name: Assert flows are updated
assert:
that:
- desire_client_with_flow_binding_overrides is changed
- "'authenticationFlowBindingOverrides' in desire_client_with_flow_binding_overrides.end_state"
- desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.browser == flow_browser_uuid
- desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.direct_grant == flow_direct_grant_uuid
- name: Unset flow binding overrides
community.general.keycloak_client:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_id }}"
state: present
redirect_uris: '{{redirect_uris1}}'
attributes: '{{client_attributes1}}'
protocol_mappers: '{{protocol_mappers1}}'
authentication_flow_binding_overrides:
browser: "{{ None }}"
direct_grant: null
register: desire_client_with_flow_binding_overrides
- name: Assert flows are removed
assert:
that:
- desire_client_with_flow_binding_overrides is changed
- "'authenticationFlowBindingOverrides' in desire_client_with_flow_binding_overrides.end_state"
- "'browser' not in desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides"
- "'direct_grant' not in desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides"