From ff084fbd96b443f175a0d99b6e5621da9b404cdb Mon Sep 17 00:00:00 2001 From: Thomas Woerner <twoerner@redhat.com> Date: Mon, 4 Dec 2023 19:38:54 +0100 Subject: [PATCH] ipaidp: Fix validation and reset of parameters The uri parameters auth_uri, dev_auth_uri, token_uri, userinfo_uri and keys_uri have not been validated before. Also the base_url was not normalized. The auth_uri, dev_auth_uri, token_uri and userinfo_uri need to be set for new entries, but might be empty or empty string for reset or updates. The ipaidpclientsecret needs to be decoded from binary string in find_idp result to not trigger no change ipd_mod calls. The code for validate_uri and base_url normalization has been copied from the ipaserver idp plugin. ansible_freeipa_module: urlparse from urllib.parse with a fallback to six.moves.urllib.parse is imported and also exported. urlparse is needed for validate_uri in ipaidp module. Resolves: RHEL-17954, RHEL-17955, RHEL-17957 and RHEL-17958 --- .../module_utils/ansible_freeipa_module.py | 8 +- plugins/modules/ipaidp.py | 60 +++++-- tests/idp/test_idp.yml | 156 ++++++++++++++++++ 3 files changed, 208 insertions(+), 16 deletions(-) diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py index dfec4c58..190f585a 100644 --- a/plugins/module_utils/ansible_freeipa_module.py +++ b/plugins/module_utils/ansible_freeipa_module.py @@ -30,7 +30,8 @@ __all__ = ["gssapi", "netaddr", "api", "ipalib_errors", "Env", "kinit_password", "kinit_keytab", "run", "DN", "VERSION", "paths", "tasks", "get_credentials_if_valid", "Encoding", "DNSName", "getargspec", "certificate_loader", - "write_certificate_list", "boolean", "template_str"] + "write_certificate_list", "boolean", "template_str", + "urlparse"] import os # ansible-freeipa requires locale to be C, IPA requires utf-8. @@ -147,6 +148,11 @@ try: except ImportError: _dcerpc_bindings_installed = False # pylint: disable=invalid-name + try: + from urllib.parse import urlparse + except ImportError: + from ansible.module_utils.six.moves.urllib.parse import urlparse + except ImportError as _err: ANSIBLE_FREEIPA_MODULE_IMPORT_ERROR = str(_err) diff --git a/plugins/modules/ipaidp.py b/plugins/modules/ipaidp.py index a3c1cea7..8c0aea6b 100644 --- a/plugins/modules/ipaidp.py +++ b/plugins/modules/ipaidp.py @@ -185,7 +185,7 @@ RETURN = """ from ansible.module_utils.ansible_freeipa_module import \ - IPAAnsibleModule, compare_args_ipa, template_str + IPAAnsibleModule, compare_args_ipa, template_str, urlparse from ansible.module_utils import six from copy import deepcopy import string @@ -274,7 +274,14 @@ def find_idp(module, name): # An exception is raised if idp name is not found. return None - return _result["result"] + res = _result["result"] + + # Decode binary string secret + if "ipaidpclientsecret" in res and len(res["ipaidpclientsecret"]) > 0: + res["ipaidpclientsecret"][0] = \ + res["ipaidpclientsecret"][0].decode("ascii") + + return res def gen_args(auth_uri, dev_auth_uri, token_uri, userinfo_uri, keys_uri, @@ -333,6 +340,16 @@ def convert_provider_to_endpoints(module, _args, provider): _args.update(points) +def validate_uri(module, uri): + try: + parsed = urlparse(uri, 'https') + except Exception: + module.fail_json(msg="Invalid URI '%s': not an https scheme" % uri) + + if not parsed.netloc: + module.fail_json(msg="Invalid URI '%s': missing netloc" % uri) + + def main(): ansible_module = IPAAnsibleModule( argument_spec=dict( @@ -426,19 +443,6 @@ def main(): if provider not in idp_providers: ansible_module.fail_json( msg="Provider '%s' is unknown" % provider) - else: - if not auth_uri: - ansible_module.fail_json( - msg="Parameter '%s' is missing" % "auth_uri") - if not dev_auth_uri: - ansible_module.fail_json( - msg="Parameter '%s' is missing" % "dev_auth_uri") - if not token_uri: - ansible_module.fail_json( - msg="Parameter '%s' is missing" % "token_uri") - if not userinfo_uri: - ansible_module.fail_json( - msg="Parameter '%s' is missing" % "userinfo_uri") invalid = ["rename", "delete_continue"] else: # state renamed and absent @@ -459,6 +463,19 @@ def main(): ansible_module.params_fail_used_invalid(invalid, state) + # Empty client_id test + if client_id is not None and client_id == "": + ansible_module.fail_json(msg="'client_id' is required") + + # Normalize base_url + if base_url is not None and base_url.startswith('https://'): + base_url = base_url[len('https://'):] + + # Validate uris + for uri in [auth_uri, dev_auth_uri, token_uri, userinfo_uri, keys_uri]: + if uri is not None and uri != "": + validate_uri(ansible_module, uri) + # Init changed = False @@ -507,6 +524,19 @@ def main(): res_find): commands.append([name, "idp_mod", args]) else: + if "ipaidpauthendpoint" not in args: + ansible_module.fail_json( + msg="Parameter '%s' is missing" % "auth_uri") + if "ipaidpdevauthendpoint" not in args: + ansible_module.fail_json( + msg="Parameter '%s' is missing" % "dev_auth_uri") + if "ipaidptokenendpoint" not in args: + ansible_module.fail_json( + msg="Parameter '%s' is missing" % "token_uri") + if "ipaidpuserinfoendpoint" not in args: + ansible_module.fail_json( + msg="Parameter '%s' is missing" % "userinfo_uri") + commands.append([name, "idp_add", args]) elif state == "absent": diff --git a/tests/idp/test_idp.yml b/tests/idp/test_idp.yml index c2c78e38..d72ba1c5 100644 --- a/tests/idp/test_idp.yml +++ b/tests/idp/test_idp.yml @@ -62,6 +62,162 @@ register: result failed_when: result.changed or result.failed + - name: Ensure keycloak idp my-keycloak-idp is present with all params + ipaidp: + name: my-keycloak-idp + organization: main + base_url: https://keycloak.idm.example.com:8443/auth + keys_uri: https://keycloak.idm.example.com:8443/certs + issuer_url: https://keycloak.idm.example.com:8443/issuer + secret: secret + scope: https://keycloak.idm.example.com:8443/scope + idp_user_id: testuser + client_id: my-client-id + register: result + failed_when: not result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp is present with all params, again + ipaidp: + name: my-keycloak-idp + organization: main + base_url: https://keycloak.idm.example.com:8443/auth + keys_uri: https://keycloak.idm.example.com:8443/certs + issuer_url: https://keycloak.idm.example.com:8443/issuer + secret: secret + scope: https://keycloak.idm.example.com:8443/scope + idp_user_id: testuser + client_id: my-client-id + register: result + # failed_when: result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp auth_uri is empty + ipaidp: + name: my-keycloak-idp + auth_uri: "" + client_id: my-client-id + register: result + failed_when: not result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp auth_uri is empty, again + ipaidp: + name: my-keycloak-idp + auth_uri: "" + client_id: my-client-id + register: result + failed_when: result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp dev_auth_uri is empty + ipaidp: + name: my-keycloak-idp + dev_auth_uri: "" + client_id: my-client-id + register: result + failed_when: not result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp dev_auth_uri is empty, again + ipaidp: + name: my-keycloak-idp + dev_auth_uri: "" + client_id: my-client-id + register: result + failed_when: result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp token_uri is empty + ipaidp: + name: my-keycloak-idp + token_uri: "" + client_id: my-client-id + register: result + failed_when: not result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp token_uri is empty, again + ipaidp: + name: my-keycloak-idp + token_uri: "" + client_id: my-client-id + register: result + failed_when: result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp userinfo_uri is empty + ipaidp: + name: my-keycloak-idp + userinfo_uri: "" + client_id: my-client-id + register: result + failed_when: not result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp userinfo_uri is empty, again + ipaidp: + name: my-keycloak-idp + userinfo_uri: "" + client_id: my-client-id + register: result + failed_when: result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp keys_uri is empty + ipaidp: + name: my-keycloak-idp + keys_uri: "" + client_id: my-client-id + register: result + failed_when: not result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp keys_uri is empty, again + ipaidp: + name: my-keycloak-idp + keys_uri: "" + client_id: my-client-id + register: result + failed_when: result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp issuer_url is empty + ipaidp: + name: my-keycloak-idp + issuer_url: "" + client_id: my-client-id + register: result + failed_when: not result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp issuer_url is empty, again + ipaidp: + name: my-keycloak-idp + issuer_url: "" + client_id: my-client-id + register: result + failed_when: result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp scope is empty + ipaidp: + name: my-keycloak-idp + scope: "" + client_id: my-client-id + register: result + failed_when: not result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp scope is empty, again + ipaidp: + name: my-keycloak-idp + scope: "" + client_id: my-client-id + register: result + failed_when: result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp idp_user_id is empty + ipaidp: + name: my-keycloak-idp + idp_user_id: "" + client_id: my-client-id + register: result + failed_when: not result.changed or result.failed + + - name: Ensure keycloak idp my-keycloak-idp idp_user_id is empty, again + ipaidp: + name: my-keycloak-idp + idp_user_id: "" + client_id: my-client-id + register: result + failed_when: result.changed or result.failed + - name: Ensure idp my-keycloak-idp is absent ipaidp: name: my-keycloak-idp -- GitLab