---
# tasks file for ipaclient

- name: Install - Ensure that IPA client packages are installed
  ansible.builtin.package:
    name: "{{ ipaclient_packages }}"
    state: present
  when: ipaclient_install_packages | bool

- name: Install - Set ipaclient_servers
  ansible.builtin.set_fact:
    ipaclient_servers: "{{ groups['ipaservers'] | list }}"
  when: groups.ipaservers is defined and ipaclient_servers is not defined

- name: Install - Set ipaclient_servers from cluster inventory
  ansible.builtin.set_fact:
    ipaclient_servers: "{{ groups['ipaserver'] | list }}"
  when: ipaclient_no_dns_lookup | bool and groups.ipaserver is defined and
        ipaclient_servers is not defined

- name: Install - Check that either password or keytab is set
  ansible.builtin.fail:
    msg: "ipaadmin_password and ipaadmin_keytab cannot be used together"
  when: ipaadmin_keytab is defined and ipaadmin_password is defined

- name: Install - Set default principal if no keytab is given
  ansible.builtin.set_fact:
    ipaadmin_principal: admin
  when: ipaadmin_principal is undefined and ipaclient_keytab is undefined

- name: Install - DNS resolver configuration
  when: ipaclient_configure_dns_resolver | bool
        and not ipaclient_on_master | bool
  block:

  - name: Install - Fail on missing ipaclient_domain and ipaserver_domain
    ansible.builtin.fail:
      msg: "ipaclient_domain or ipaserver_domain is required for ipaclient_configure_dns_resolver"
    when: ipaserver_domain is not defined and ipaclient_domain is not defined

  - name: Install - Fail on missing ipaclient_servers
    ansible.builtin.fail:
      msg: "ipaclient_dns_servers is required for ipaclient_configure_dns_resolver"
    when: ipaclient_dns_servers is not defined

  - name: Install - Configure DNS resolver
    ipaclient_configure_dns_resolver:
      nameservers: "{{ ipaclient_dns_servers }}"
      searchdomains: "{{ ipaserver_domain | default(ipaclient_domain) }}"
      state: present

- name: Install - IPA client test
  ipaclient_test:
    ### basic ###
    domain: "{{ ipaserver_domain | default(ipaclient_domain) | default(omit) }}"
    servers: "{{ ipaclient_servers | default(omit) }}"
    realm: "{{ ipaserver_realm | default(ipaclient_realm) | default(omit) }}"
    hostname: "{{ ipaclient_hostname | default(ansible_facts['fqdn']) }}"
    ntp_servers: "{{ ipaclient_ntp_servers | default(omit) }}"
    ntp_pool: "{{ ipaclient_ntp_pool | default(omit) }}"
    no_ntp: "{{ ipaclient_no_ntp }}"
    force_ntpd: "{{ ipaclient_force_ntpd }}"
    nisdomain: "{{ ipaclient_nisdomain | default(omit) }}"
    no_nisdomain: "{{ ipaclient_no_nisdomain }}"
    kinit_attempts: "{{ ipaclient_kinit_attempts }}"
    ca_cert_files: "{{ ipaclient_ca_cert_file | default(omit) }}"
    configure_firefox: "{{ ipaclient_configure_firefox }}"
    firefox_dir: "{{ ipaclient_firefox_dir | default(omit) }}"
    ip_addresses: "{{ ipaclient_ip_addresses | default(omit) }}"
    all_ip_addresses: "{{ ipaclient_all_ip_addresses }}"
    on_master: "{{ ipaclient_on_master }}"
    ### sssd ###
    enable_dns_updates: "{{ ipassd_enable_dns_updates
                            | default(ipasssd_enable_dns_updates) }}"
  register: result_ipaclient_test

- name: Install - Client deployment
  when: not ansible_check_mode and
        not (result_ipaclient_test.client_already_configured and
            not ipaclient_allow_repair | bool and not ipaclient_force_join | bool)
  block:
  - name: Install - Cleanup leftover ccache
    ansible.builtin.file:
      path: "/etc/ipa/.dns_ccache"
      state: absent

  - name: Install - Configure NTP
    ipaclient_setup_ntp:
      ### basic ###
      ntp_servers: "{{ result_ipaclient_test.ntp_servers | default(omit) }}"
      ntp_pool: "{{ result_ipaclient_test.ntp_pool | default(omit) }}"
      no_ntp: "{{ ipaclient_no_ntp }}"
      # force_ntpd: "{{ ipaclient_force_ntpd }}"
      on_master: "{{ ipaclient_on_master }}"
      ### additional ###
      servers: "{{ result_ipaclient_test.servers }}"
      domain: "{{ result_ipaclient_test.domain }}"

  - name: Install - Make sure One-Time Password is enabled if it's already defined
    ansible.builtin.set_fact:
      ipaclient_use_otp: "yes"
    when: ipaclient_otp is defined

  - name: Install - Disable One-Time Password for on_master
    ansible.builtin.set_fact:
      ipaclient_use_otp: "no"
    when: ipaclient_use_otp | bool and ipaclient_on_master | bool

  - name: Install - Test if IPA client has working krb5.keytab
    ipaclient_test_keytab:
      servers: "{{ result_ipaclient_test.servers }}"
      domain: "{{ result_ipaclient_test.domain }}"
      realm: "{{ result_ipaclient_test.realm }}"
      hostname: "{{ result_ipaclient_test.hostname }}"
      kdc: "{{ result_ipaclient_test.kdc }}"
      kinit_attempts: "{{ ipaclient_kinit_attempts | default(omit) }}"
    register: result_ipaclient_test_keytab

  - name: Install - Disable One-Time Password for client with working
          krb5.keytab
    ansible.builtin.set_fact:
      ipaclient_use_otp: "no"
    when: ipaclient_use_otp | bool and
          result_ipaclient_test_keytab.krb5_keytab_ok and
          not ipaclient_force_join | bool

  # The following block is executed when using OTP to enroll IPA client and
  # the OTP isn't predefined, ie when ipaclient_use_otp is set and
  # ipaclient_otp is not set.
  # It connects to ipaserver and add the host with --random option in order
  # to create a OneTime Password
  # If a keytab is specified in the hostent, then the hostent will be disabled
  # if ipaclient_use_otp is set.
  - name: Install - Obtain OTP
    when: ipaclient_use_otp | bool and ipaclient_otp is not defined
    block:
    - name: Install - Keytab or password is required for getting otp
      ansible.builtin.fail:
        msg: "Keytab or password is required for getting otp"
      when: ipaadmin_keytab is undefined and ipaadmin_password is undefined

    - name: Install - Create temporary file for keytab
      ansible.builtin.tempfile:
        state: file
        prefix: ipaclient_temp_
        path: /root
      register: keytab_temp
      delegate_to: "{{ result_ipaclient_test.servers[0] }}"
      when: ipaadmin_keytab is defined

    - name: Install - Copy keytab to server temporary file
      ansible.builtin.copy:
        src: "{{ ipaadmin_keytab }}"
        dest: "{{ keytab_temp.path }}"
        mode: 0600
      delegate_to: "{{ result_ipaclient_test.servers[0] }}"
      when: ipaadmin_keytab is defined

    - name: Install - Get One-Time Password for client enrollment
      no_log: true
      ipaclient_get_otp:
        ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}"
        ipaadmin_password: "{{ ipaadmin_password | default(omit) }}"
        ipaadmin_keytab: "{{ keytab_temp.path | default(omit) }}"
        hostname: "{{ result_ipaclient_test.hostname }}"
      register: result_ipaclient_get_otp
      delegate_to: "{{ result_ipaclient_test.servers[0] }}"

    - name: Install - Store the previously obtained OTP
      no_log: true
      ansible.builtin.set_fact:
        ipaadmin_orig_password: "{{ ipaadmin_password | default(omit) }}"
        ipaadmin_password: "{{ result_ipaclient_get_otp.host.randompassword
                               if result_ipaclient_get_otp.host is defined }}"
    rescue:
    - name: Install - Report error for OTP generation
      ansible.builtin.debug:
        msg: "{{ result_ipaclient_get_otp.msg }}"
      when: result_ipaclient_get_otp is failed
      failed_when: true

    always:
    - name: Install - Remove keytab temporary file
      ansible.builtin.file:
        path: "{{ keytab_temp.path }}"
        state: absent
      delegate_to: "{{ result_ipaclient_test.servers[0] }}"
      when: keytab_temp.path is defined

  - name: Store predefined OTP in admin_password
    no_log: true
    ansible.builtin.set_fact:
      ipaadmin_orig_password: "{{ ipaadmin_password | default(omit) }}"
      ipaadmin_password: "{{ ipaclient_otp }}"
    when: ipaclient_otp is defined

  - name: Install - Check keytab, principal and keytab
    when: not ipaclient_on_master | bool
    block:
    # This block is executed only when
    # not (not ipaclient_on_master | bool and
    #      not result_ipaclient_join.changed and
    #      not ipaclient_allow_repair | bool and
    #      (result_ipaclient_test_keytab.krb5_keytab_ok or
    #       (result_ipaclient_join.already_joined is defined and
    #        result_ipaclient_join.already_joined)))

    - name: Install - Check if principal and keytab are set
      ansible.builtin.fail:
        msg: "Admin principal and client keytab cannot be used together"
      when: ipaadmin_principal is defined and ipaclient_keytab is defined

    - name: Install - Check if one of password or keytabs are set
      ansible.builtin.fail:
        msg: "At least one of password or keytabs must be specified"
      when: not result_ipaclient_test_keytab.krb5_keytab_ok
            and ipaadmin_password is undefined
            and ipaadmin_keytab is undefined
            and ipaclient_keytab is undefined

  - name: "Install - From host keytab, purge {{ result_ipaclient_test.realm }}"
    ansible.builtin.command: >
      /usr/sbin/ipa-rmkeytab
      -k /etc/krb5.keytab
      -r "{{ result_ipaclient_test.realm }}"
    register: result_ipa_rmkeytab
    # Do not fail on error codes 3 and 5:
    #   3 - Unable to open keytab
    #   5 - Principal name or realm not found in keytab
    #   7 - Failed to set cursor, typically when errcode
    #       would be issued in past
    failed_when: result_ipa_rmkeytab.rc != 0 and
                 result_ipa_rmkeytab.rc != 3 and
                 result_ipa_rmkeytab.rc != 5 and
                 result_ipa_rmkeytab.rc != 7
    when: (ipaclient_use_otp | bool or ipaclient_force_join | bool) and not ipaclient_on_master | bool

  - name: Install - Backup and set hostname
    ipaclient_set_hostname:
      hostname: "{{ result_ipaclient_test.hostname }}"
    when: not ipaclient_on_master | bool

  - name: Install - Create temporary krb5 configuration
    ipaclient_temp_krb5:
      servers: "{{ result_ipaclient_test.servers }}"
      domain: "{{ result_ipaclient_test.domain }}"
      realm: "{{ result_ipaclient_test.realm }}"
      hostname: "{{ result_ipaclient_test.hostname }}"
      kdc: "{{ result_ipaclient_test.kdc }}"
    register: result_ipaclient_temp_krb5

  - name: Install - Join IPA
    ipaclient_join:
      servers: "{{ result_ipaclient_test.servers }}"
      realm: "{{ result_ipaclient_test.realm }}"
      basedn: "{{ result_ipaclient_test.basedn }}"
      hostname: "{{ result_ipaclient_test.hostname }}"
      force_join: "{{ ipaclient_force_join | default(omit) }}"
      principal: "{{ ipaadmin_principal if not ipaclient_use_otp | bool and
                     ipaclient_keytab is not defined else omit }}"
      password: "{{ ipaadmin_password | default(omit) }}"
      keytab: "{{ ipaclient_keytab | default(omit) }}"
      admin_keytab: "{{ ipaadmin_keytab if ipaadmin_keytab is defined and not ipaclient_use_otp | bool else omit }}"
      # ca_cert_file: "{{ ipaclient_ca_cert_file | default(omit) }}"
      kinit_attempts: "{{ ipaclient_kinit_attempts | default(omit) }}"
      krb_name: "{{ result_ipaclient_temp_krb5.krb_name }}"
    register: result_ipaclient_join
    when: not ipaclient_on_master | bool and
          (not result_ipaclient_test_keytab.krb5_keytab_ok or
              ipaclient_force_join)

  - name: Install - Allow repair checks
    when: not ipaclient_on_master | bool and
          not result_ipaclient_join.changed and
          not ipaclient_allow_repair | bool and
          (result_ipaclient_test_keytab.krb5_keytab_ok or
              (result_ipaclient_join.already_joined is defined and
                  result_ipaclient_join.already_joined))
    block:
    - name: The krb5 configuration is not correct
      ansible.builtin.fail:
        msg: >
          The krb5 configuration is not correct, please enable allow_repair
          to fix this.
      when: not result_ipaclient_test_keytab.krb5_conf_ok
    - name: IPA test failed
      ansible.builtin.fail:
        msg: "The IPA test failed, please enable allow_repair to fix this."
      when: not result_ipaclient_test_keytab.ping_test_ok
    - name: Fail due to missing ca.crt file
      ansible.builtin.fail:
        msg: >
          The ca.crt file is missing, please enable allow_repair to fix this.
      when: not result_ipaclient_test_keytab.ca_crt_exists

  - name: Install - Configuration
    when: not (not ipaclient_on_master | bool and
          not result_ipaclient_join.changed and
          not ipaclient_allow_repair | bool
              and (result_ipaclient_test_keytab.krb5_keytab_ok
              or (result_ipaclient_join.already_joined is defined
              and result_ipaclient_join.already_joined)))
    block:
    - name: Install - Configure IPA default.conf
      ipaclient_ipa_conf:
        servers: "{{ result_ipaclient_test.servers }}"
        domain: "{{ result_ipaclient_test.domain }}"
        realm: "{{ result_ipaclient_test.realm }}"
        hostname: "{{ result_ipaclient_test.hostname }}"
        basedn: "{{ result_ipaclient_test.basedn }}"
      when: not ipaclient_on_master | bool

    - name: Install - Configure SSSD
      ipaclient_setup_sssd:
        servers: "{{ result_ipaclient_test.servers }}"
        domain: "{{ result_ipaclient_test.domain }}"
        realm: "{{ result_ipaclient_test.realm }}"
        hostname: "{{ result_ipaclient_test.hostname }}"
        on_master: "{{ ipaclient_on_master }}"
        no_ssh: "{{ ipaclient_no_ssh }}"
        no_sshd: "{{ ipaclient_no_sshd }}"
        no_sudo: "{{ ipaclient_no_sudo }}"
        all_ip_addresses: "{{ ipaclient_all_ip_addresses }}"
        fixed_primary: "{{ ipassd_fixed_primary
                           | default(ipasssd_fixed_primary) }}"
        permit: "{{ ipassd_permit | default(ipasssd_permit) }}"
        enable_dns_updates: "{{ ipassd_enable_dns_updates
                                | default(ipasssd_enable_dns_updates) }}"
        preserve_sssd: "{{ ipassd_preserve_sssd
                           | default(ipasssd_preserve_sssd) }}"
        no_krb5_offline_passwords:
          "{{ ipassd_no_krb5_offline_passwords
              | default(ipasssd_no_krb5_offline_passwords) }}"

    - name: Install - IPA API calls for remaining enrollment parts
      ipaclient_api:
        servers: "{{ result_ipaclient_test.servers }}"
        realm: "{{ result_ipaclient_test.realm }}"
        hostname: "{{ result_ipaclient_test.hostname }}"
        # debug: true
        krb_name: "{{ result_ipaclient_temp_krb5.krb_name }}"
      register: result_ipaclient_api

    - name: Install - Fix IPA ca
      ipaclient_fix_ca:
        servers: "{{ result_ipaclient_test.servers }}"
        realm: "{{ result_ipaclient_test.realm }}"
        basedn: "{{ result_ipaclient_test.basedn }}"
        allow_repair: "{{ ipaclient_allow_repair }}"
        krb_name: "{{ result_ipaclient_temp_krb5.krb_name }}"
      when: not ipaclient_on_master | bool and
            result_ipaclient_test_keytab.krb5_keytab_ok and
            not result_ipaclient_test_keytab.ca_crt_exists

    - name: Install - Create IPA NSS database
      ipaclient_setup_nss:
        servers: "{{ result_ipaclient_test.servers }}"
        domain: "{{ result_ipaclient_test.domain }}"
        realm: "{{ result_ipaclient_test.realm }}"
        basedn: "{{ result_ipaclient_test.basedn }}"
        hostname: "{{ result_ipaclient_test.hostname }}"
        subject_base: "{{ result_ipaclient_api.subject_base }}"
        principal: "{{ ipaadmin_principal | default(omit) }}"
        mkhomedir: "{{ ipaclient_mkhomedir }}"
        ca_enabled: "{{ result_ipaclient_api.ca_enabled }}"
        on_master: "{{ ipaclient_on_master }}"
        dnsok: "{{ result_ipaclient_test.dnsok }}"
        enable_dns_updates: "{{ ipassd_enable_dns_updates
                                | default(ipasssd_enable_dns_updates) }}"
        all_ip_addresses: "{{ ipaclient_all_ip_addresses }}"
        ip_addresses: "{{ ipaclient_ip_addresses | default(omit) }}"
        request_cert: "{{ ipaclient_request_cert }}"
        preserve_sssd: "{{ ipassd_preserve_sssd
                           | default(ipasssd_preserve_sssd) }}"
        no_ssh: "{{ ipaclient_no_ssh }}"
        no_sshd: "{{ ipaclient_no_sshd }}"
        no_sudo: "{{ ipaclient_no_sudo }}"
        subid: "{{ ipaclient_subid }}"
        fixed_primary: "{{ ipassd_fixed_primary
                           | default(ipasssd_fixed_primary) }}"
        permit: "{{ ipassd_permit | default(ipasssd_permit) }}"
        no_krb5_offline_passwords:
          "{{ ipassd_no_krb5_offline_passwords
              | default(ipasssd_no_krb5_offline_passwords) }}"
        no_dns_sshfp: "{{ ipaclient_no_dns_sshfp }}"
        nosssd_files: "{{ result_ipaclient_test.nosssd_files }}"
        selinux_works: "{{ result_ipaclient_test.selinux_works }}"
        krb_name: "{{ result_ipaclient_temp_krb5.krb_name }}"

    - name: Install - Configure SSH and SSHD
      ipaclient_setup_ssh:
        servers: "{{ result_ipaclient_test.servers }}"
        sssd: "{{ result_ipaclient_test.sssd }}"
        no_ssh: "{{ ipaclient_no_ssh }}"
        ssh_trust_dns: "{{ ipaclient_ssh_trust_dns }}"
        no_sshd: "{{ ipaclient_no_sshd }}"

    - name: Install - Configure automount
      ipaclient_setup_automount:
        servers: "{{ result_ipaclient_test.servers }}"
        sssd: "{{ result_ipaclient_test.sssd }}"
        automount_location: "{{ ipaclient_automount_location | default(omit) }}"

    - name: Install - Configure firefox
      ipaclient_setup_firefox:
        firefox_dir: "{{ ipaclient_firefox_dir | default(omit) }}"
        domain: "{{ result_ipaclient_test.domain }}"
      when: ipaclient_configure_firefox | bool

    - name: Install - Configure NIS
      ipaclient_setup_nis:
        domain: "{{ result_ipaclient_test.domain }}"
        nisdomain: "{{ ipaclient_nisdomain | default(omit) }}"
      when: not ipaclient_no_nisdomain | bool

    - name: Remove temporary krb5.conf
      ansible.builtin.file:
        path: "{{ result_ipaclient_temp_krb5.krb_name }}"
        state: absent
      when: result_ipaclient_temp_krb5.krb_name is defined

    - name: Install - Configure krb5 for IPA realm
      ipaclient_setup_krb5:
        realm: "{{ result_ipaclient_test.realm }}"
        domain: "{{ result_ipaclient_test.domain }}"
        servers: "{{ result_ipaclient_test.servers }}"
        kdc: "{{ result_ipaclient_test.kdc }}"
        dnsok: "{{ result_ipaclient_test.dnsok }}"
        client_domain: "{{ result_ipaclient_test.client_domain }}"
        hostname: "{{ result_ipaclient_test.hostname }}"
        sssd: "{{ result_ipaclient_test.sssd }}"
        force: "{{ ipaclient_force }}"
        # on_master: "{{ ipaclient_on_master }}"
      when: not ipaclient_on_master | bool

    - name: Install - Configure certmonger
      ipaclient_setup_certmonger:
        realm: "{{ result_ipaclient_test.realm }}"
        hostname: "{{ result_ipaclient_test.hostname }}"
        subject_base: "{{ result_ipaclient_api.subject_base }}"
        ca_enabled: "{{ result_ipaclient_api.ca_enabled }}"
        request_cert: "{{ ipaclient_request_cert }}"
      when: not ipaclient_on_master | bool

  always:
  - name: Install - Restore original admin password if overwritten by OTP
    no_log: true
    ansible.builtin.set_fact:
        ipaadmin_password: "{{ ipaadmin_orig_password }}"
    when: ipaclient_use_otp | bool and ipaadmin_orig_password is defined

  - name: Cleanup leftover ccache
    ansible.builtin.file:
      path: "/etc/ipa/.dns_ccache"
      state: absent

  - name: Remove temporary krb5.conf
    ansible.builtin.file:
      path: "{{ result_ipaclient_temp_krb5.krb_name }}"
      state: absent
    when: result_ipaclient_temp_krb5.krb_name is defined

  - name: Remove temporary krb5.conf backup
    ansible.builtin.file:
      path: "{{ result_ipaclient_temp_krb5.krb_name }}.ipabkp"
      state: absent
    when: result_ipaclient_temp_krb5.krb_name is defined
