mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-05-30 04:49:09 -07:00
* Allow keycloak_group.py to take token as parameter for the authentification
Refactor get_token to pass module.params + Documentation
Fix unit test and add new one for token as param
Fix identation
Update plugins/modules/identity/keycloak/keycloak_client.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Update plugins/modules/identity/keycloak/keycloak_clienttemplate.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Allow keycloak_group.py to take token as parameter for the authentification
Refactor get_token to pass module.params + Documentation
* Update plugins/module_utils/identity/keycloak/keycloak.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Check if base_url is None before to check format
Update plugins/module_utils/identity/keycloak/keycloak.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Update plugins/modules/identity/keycloak/keycloak_client.py
Co-authored-by: Amin Vakil <info@aminvakil.com>
Update plugins/modules/identity/keycloak/keycloak_clienttemplate.py
Co-authored-by: Amin Vakil <info@aminvakil.com>
Switch to modern syntax for the documentation (e.g. community.general.keycloak_client)
Update keycloak_client.py
Update keycloak_clienttemplate.py
Add keycloak_authentication module to manage authentication
Minor fixex
Fix indent
* Update plugins/modules/identity/keycloak/keycloak_authentication.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Update plugins/modules/identity/keycloak/keycloak_authentication.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Update plugins/modules/identity/keycloak/keycloak_authentication.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Update plugins/modules/identity/keycloak/keycloak_authentication.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Update plugins/modules/identity/keycloak/keycloak_authentication.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Removing variable ANSIBLE_METADATA from beginning of file
Minor fix
Refactoring create_or_update_executions :add change_execution_priority function
Refactoring create_or_update_executions :add create_execution function
Refactoring create_or_update_executions: add create_subflow
Refactoring create_or_update_executions: add update_authentication_executions function
Minor fix
* Using FQCN for the examples
Minor fix
Update plugins/module_utils/identity/keycloak/keycloak.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Update plugins/module_utils/identity/keycloak/keycloak.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Update plugins/module_utils/identity/keycloak/keycloak.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Update plugins/module_utils/identity/keycloak/keycloak.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Update plugins/module_utils/identity/keycloak/keycloak.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Update plugins/module_utils/identity/keycloak/keycloak.py
Co-authored-by: Felix Fontein <felix@fontein.de>
* Update plugins/modules/identity/keycloak/keycloak_authentication.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Update plugins/modules/identity/keycloak/keycloak_authentication.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Refactoring: rename isDictEquals into is_dict_equals
Refactoring: rename variable as authentication_flow
Refactoring: rename variable as new_name
Refactoring: rename variable as flow_list
Refactoring: rename variable as new_flow
Refactoring: changing construction of dict newAuthenticationRepresentation and renaming as new_auth_repr
Minor fix
* Refactoring: rename variables with correct Python syntax (auth_repr, exec_repr)
Move create_or_update_executions function from keycloak.py to keycloak_authentication.py
Minor fix
Remove mock_create_or_update_executions not needed anymore
Fix unit test
Update plugins/module_utils/identity/keycloak/keycloak.py
is_dict_equals function return True if value1 empty
Update plugins/module_utils/identity/keycloak/keycloak.py
Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
Rename is_dict_equal as is_struct_included and rename params as struct1 and struct2
Rename variables according to Python naming conventions
Refactoring: add find_exec_in_executions function in keycloak_authentication to remove code duplication
typo
Add blank line
Add required parameter, either creds or token
Typo
try/except only surround for loop containing struct2[key]
Add sub-options to meta_args
assigment of result['changed'] after if-elif-else block
Fix CI error: parameter-type-not-in-doc
Fix unit test: none value excluded from comparison
Minor fix
Simplify is_struct_included function
Replace 'type(..) is' by isinstance(..)
Remove redundant required=True and redundant parenthesis
Add check_mode, check if value is None (None value added by argument spec checker)
Apply suggestions from code review
Update plugins/modules/identity/keycloak/keycloak_authentication.py
* Update plugins/modules/identity/keycloak/keycloak_authentication.py
* Add index paramter to configure the priority order of the execution
* Minor fix: authenticationConfig dict instead of str
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 24c5d4320f
)
Co-authored-by: Gaetan2907 <48204380+Gaetan2907@users.noreply.github.com>
This commit is contained in:
parent
82225e5850
commit
2322937a4a
4 changed files with 1323 additions and 2 deletions
|
@ -33,9 +33,9 @@ import json
|
|||
import traceback
|
||||
|
||||
from ansible.module_utils.urls import open_url
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode, quote
|
||||
from ansible.module_utils.six.moves.urllib.error import HTTPError
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils._text import to_native, to_text
|
||||
|
||||
URL_REALMS = "{url}/admin/realms"
|
||||
URL_REALM = "{url}/admin/realms/{realm}"
|
||||
|
@ -51,6 +51,17 @@ URL_CLIENTTEMPLATES = "{url}/admin/realms/{realm}/client-templates"
|
|||
URL_GROUPS = "{url}/admin/realms/{realm}/groups"
|
||||
URL_GROUP = "{url}/admin/realms/{realm}/groups/{groupid}"
|
||||
|
||||
URL_AUTHENTICATION_FLOWS = "{url}/admin/realms/{realm}/authentication/flows"
|
||||
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_EXECUTIONS = "{url}/admin/realms/{realm}/authentication/flows/{flowalias}/executions"
|
||||
URL_AUTHENTICATION_FLOW_EXECUTIONS_EXECUTION = "{url}/admin/realms/{realm}/authentication/flows/{flowalias}/executions/execution"
|
||||
URL_AUTHENTICATION_FLOW_EXECUTIONS_FLOW = "{url}/admin/realms/{realm}/authentication/flows/{flowalias}/executions/flow"
|
||||
URL_AUTHENTICATION_EXECUTION_CONFIG = "{url}/admin/realms/{realm}/authentication/executions/{id}/config"
|
||||
URL_AUTHENTICATION_EXECUTION_RAISE_PRIORITY = "{url}/admin/realms/{realm}/authentication/executions/{id}/raise-priority"
|
||||
URL_AUTHENTICATION_EXECUTION_LOWER_PRIORITY = "{url}/admin/realms/{realm}/authentication/executions/{id}/lower-priority"
|
||||
URL_AUTHENTICATION_CONFIG = "{url}/admin/realms/{realm}/authentication/config/{id}"
|
||||
|
||||
|
||||
def keycloak_argument_spec():
|
||||
"""
|
||||
|
@ -132,6 +143,59 @@ def get_token(module_params):
|
|||
}
|
||||
|
||||
|
||||
def is_struct_included(struct1, struct2, exclude=None):
|
||||
"""
|
||||
This function compare if the first parameter structure is included in the second.
|
||||
The function use every elements of struct1 and validates they are present in the struct2 structure.
|
||||
The two structure does not need to be equals for that function to return true.
|
||||
Each elements are compared recursively.
|
||||
:param struct1:
|
||||
type:
|
||||
dict for the initial call, can be dict, list, bool, int or str for recursive calls
|
||||
description:
|
||||
reference structure
|
||||
:param struct2:
|
||||
type:
|
||||
dict for the initial call, can be dict, list, bool, int or str for recursive calls
|
||||
description:
|
||||
structure to compare with first parameter.
|
||||
:param exclude:
|
||||
type:
|
||||
list
|
||||
description:
|
||||
Key to exclude from the comparison.
|
||||
default: None
|
||||
:return:
|
||||
type:
|
||||
bool
|
||||
description:
|
||||
Return True if all element of dict 1 are present in dict 2, return false otherwise.
|
||||
"""
|
||||
if isinstance(struct1, list) and isinstance(struct2, list):
|
||||
for item1 in struct1:
|
||||
if isinstance(item1, (list, dict)):
|
||||
for item2 in struct2:
|
||||
if not is_struct_included(item1, item2, exclude):
|
||||
return False
|
||||
else:
|
||||
if item1 not in struct2:
|
||||
return False
|
||||
return True
|
||||
elif isinstance(struct1, dict) and isinstance(struct2, dict):
|
||||
try:
|
||||
for key in struct1:
|
||||
if not (exclude and key in exclude):
|
||||
if not is_struct_included(struct1[key], struct2[key], exclude):
|
||||
return False
|
||||
return True
|
||||
except KeyError:
|
||||
return False
|
||||
elif isinstance(struct1, bool) and isinstance(struct2, bool):
|
||||
return struct1 == struct2
|
||||
else:
|
||||
return to_text(struct1, 'utf-8') == to_text(struct2, 'utf-8')
|
||||
|
||||
|
||||
class KeycloakAPI(object):
|
||||
""" Keycloak API access; Keycloak uses OAuth 2.0 to protect its API, an access token for which
|
||||
is obtained through OpenID connect
|
||||
|
@ -571,3 +635,254 @@ class KeycloakAPI(object):
|
|||
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to delete group %s: %s" % (groupid, str(e)))
|
||||
|
||||
def get_authentication_flow_by_alias(self, alias, realm='master'):
|
||||
"""
|
||||
Get an authentication flow by it's alias
|
||||
:param alias: Alias of the authentication flow to get.
|
||||
:param realm: Realm.
|
||||
:return: Authentication flow representation.
|
||||
"""
|
||||
try:
|
||||
authentication_flow = {}
|
||||
# Check if the authentication flow exists on the Keycloak serveraders
|
||||
authentications = json.load(open_url(URL_AUTHENTICATION_FLOWS.format(url=self.baseurl, realm=realm), method='GET', headers=self.restheaders))
|
||||
for authentication in authentications:
|
||||
if authentication["alias"] == alias:
|
||||
authentication_flow = authentication
|
||||
break
|
||||
return authentication_flow
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable get authentication flow %s: %s" % (alias, str(e)))
|
||||
|
||||
def delete_authentication_flow_by_id(self, id, realm='master'):
|
||||
"""
|
||||
Delete an authentication flow from Keycloak
|
||||
:param id: id of authentication flow to be deleted
|
||||
:param realm: realm of client to be deleted
|
||||
:return: HTTPResponse object on success
|
||||
"""
|
||||
flow_url = URL_AUTHENTICATION_FLOW.format(url=self.baseurl, realm=realm, id=id)
|
||||
|
||||
try:
|
||||
return open_url(flow_url, method='DELETE', headers=self.restheaders,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete authentication flow %s in realm %s: %s'
|
||||
% (id, realm, str(e)))
|
||||
|
||||
def copy_auth_flow(self, config, realm='master'):
|
||||
"""
|
||||
Create a new authentication flow from a copy of another.
|
||||
:param config: Representation of the authentication flow to create.
|
||||
:param realm: Realm.
|
||||
:return: Representation of the new authentication flow.
|
||||
"""
|
||||
try:
|
||||
new_name = dict(
|
||||
newName=config["alias"]
|
||||
)
|
||||
open_url(
|
||||
URL_AUTHENTICATION_FLOW_COPY.format(
|
||||
url=self.baseurl,
|
||||
realm=realm,
|
||||
copyfrom=quote(config["copyFrom"])),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(new_name))
|
||||
flow_list = json.load(
|
||||
open_url(
|
||||
URL_AUTHENTICATION_FLOWS.format(url=self.baseurl,
|
||||
realm=realm),
|
||||
method='GET',
|
||||
headers=self.restheaders))
|
||||
for flow in flow_list:
|
||||
if flow["alias"] == config["alias"]:
|
||||
return flow
|
||||
return None
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not copy authentication flow %s in realm %s: %s'
|
||||
% (config["alias"], realm, str(e)))
|
||||
|
||||
def create_empty_auth_flow(self, config, realm='master'):
|
||||
"""
|
||||
Create a new empty authentication flow.
|
||||
:param config: Representation of the authentication flow to create.
|
||||
:param realm: Realm.
|
||||
:return: Representation of the new authentication flow.
|
||||
"""
|
||||
try:
|
||||
new_flow = dict(
|
||||
alias=config["alias"],
|
||||
providerId=config["providerId"],
|
||||
description=config["description"],
|
||||
topLevel=True
|
||||
)
|
||||
open_url(
|
||||
URL_AUTHENTICATION_FLOWS.format(
|
||||
url=self.baseurl,
|
||||
realm=realm),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(new_flow))
|
||||
flow_list = json.load(
|
||||
open_url(
|
||||
URL_AUTHENTICATION_FLOWS.format(
|
||||
url=self.baseurl,
|
||||
realm=realm),
|
||||
method='GET',
|
||||
headers=self.restheaders))
|
||||
for flow in flow_list:
|
||||
if flow["alias"] == config["alias"]:
|
||||
return flow
|
||||
return None
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create empty authentication flow %s in realm %s: %s'
|
||||
% (config["alias"], realm, str(e)))
|
||||
|
||||
def update_authentication_executions(self, flowAlias, updatedExec, realm='master'):
|
||||
""" Update authentication executions
|
||||
|
||||
:param flowAlias: name of the parent flow
|
||||
:param updatedExec: JSON containing updated execution
|
||||
:return: HTTPResponse object on success
|
||||
"""
|
||||
try:
|
||||
open_url(
|
||||
URL_AUTHENTICATION_FLOW_EXECUTIONS.format(
|
||||
url=self.baseurl,
|
||||
realm=realm,
|
||||
flowalias=quote(flowAlias)),
|
||||
method='PUT',
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(updatedExec))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to update executions %s: %s" % (updatedExec, str(e)))
|
||||
|
||||
def add_authenticationConfig_to_execution(self, executionId, authenticationConfig, realm='master'):
|
||||
""" Add autenticatorConfig to the execution
|
||||
|
||||
:param executionId: id of execution
|
||||
:param authenticationConfig: config to add to the execution
|
||||
:return: HTTPResponse object on success
|
||||
"""
|
||||
try:
|
||||
open_url(
|
||||
URL_AUTHENTICATION_EXECUTION_CONFIG.format(
|
||||
url=self.baseurl,
|
||||
realm=realm,
|
||||
id=executionId),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(authenticationConfig))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to add authenticationConfig %s: %s" % (executionId, str(e)))
|
||||
|
||||
def create_subflow(self, subflowName, flowAlias, realm='master'):
|
||||
""" Create new sublow on the flow
|
||||
|
||||
:param subflowName: name of the subflow to create
|
||||
:param flowAlias: name of the parent flow
|
||||
:return: HTTPResponse object on success
|
||||
"""
|
||||
try:
|
||||
newSubFlow = {}
|
||||
newSubFlow["alias"] = subflowName
|
||||
newSubFlow["provider"] = "registration-page-form"
|
||||
newSubFlow["type"] = "basic-flow"
|
||||
open_url(
|
||||
URL_AUTHENTICATION_FLOW_EXECUTIONS_FLOW.format(
|
||||
url=self.baseurl,
|
||||
realm=realm,
|
||||
flowalias=quote(flowAlias)),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(newSubFlow))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to create new subflow %s: %s" % (subflowName, str(e)))
|
||||
|
||||
def create_execution(self, execution, flowAlias, realm='master'):
|
||||
""" Create new execution on the flow
|
||||
|
||||
:param execution: name of execution to create
|
||||
:param flowAlias: name of the parent flow
|
||||
:return: HTTPResponse object on success
|
||||
"""
|
||||
try:
|
||||
newExec = {}
|
||||
newExec["provider"] = execution["providerId"]
|
||||
newExec["requirement"] = execution["requirement"]
|
||||
open_url(
|
||||
URL_AUTHENTICATION_FLOW_EXECUTIONS_EXECUTION.format(
|
||||
url=self.baseurl,
|
||||
realm=realm,
|
||||
flowalias=quote(flowAlias)),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(newExec))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to create new execution %s: %s" % (execution["provider"], str(e)))
|
||||
|
||||
def change_execution_priority(self, executionId, diff, realm='master'):
|
||||
""" Raise or lower execution priority of diff time
|
||||
|
||||
:param executionId: id of execution to lower priority
|
||||
:param realm: realm the client is in
|
||||
:param diff: Integer number, raise of diff time if positive lower of diff time if negative
|
||||
:return: HTTPResponse object on success
|
||||
"""
|
||||
try:
|
||||
if diff > 0:
|
||||
for i in range(diff):
|
||||
open_url(
|
||||
URL_AUTHENTICATION_EXECUTION_RAISE_PRIORITY.format(
|
||||
url=self.baseurl,
|
||||
realm=realm,
|
||||
id=executionId),
|
||||
method='POST',
|
||||
headers=self.restheaders)
|
||||
elif diff < 0:
|
||||
for i in range(-diff):
|
||||
open_url(
|
||||
URL_AUTHENTICATION_EXECUTION_LOWER_PRIORITY.format(
|
||||
url=self.baseurl,
|
||||
realm=realm,
|
||||
id=executionId),
|
||||
method='POST',
|
||||
headers=self.restheaders)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to change execution priority %s: %s" % (executionId, str(e)))
|
||||
|
||||
def get_executions_representation(self, config, realm='master'):
|
||||
"""
|
||||
Get a representation of the executions for an authentication flow.
|
||||
:param config: Representation of the authentication flow
|
||||
:param realm: Realm
|
||||
:return: Representation of the executions
|
||||
"""
|
||||
try:
|
||||
# Get executions created
|
||||
executions = json.load(
|
||||
open_url(
|
||||
URL_AUTHENTICATION_FLOW_EXECUTIONS.format(
|
||||
url=self.baseurl,
|
||||
realm=realm,
|
||||
flowalias=quote(config["alias"])),
|
||||
method='GET',
|
||||
headers=self.restheaders))
|
||||
for execution in executions:
|
||||
if "authenticationConfig" in execution:
|
||||
execConfigId = execution["authenticationConfig"]
|
||||
execConfig = json.load(
|
||||
open_url(
|
||||
URL_AUTHENTICATION_CONFIG.format(
|
||||
url=self.baseurl,
|
||||
realm=realm,
|
||||
id=execConfigId),
|
||||
method='GET',
|
||||
headers=self.restheaders))
|
||||
execution["authenticationConfig"] = execConfig
|
||||
return executions
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not get executions for authentication flow %s in realm %s: %s'
|
||||
% (config["alias"], realm, str(e)))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue