---
- name: Ensure that the roles runs with default parameters
  hosts: all
  become: true

  tasks:
    - name: Import role
      import_role:
        name: linux-system-roles.firewall

    - name: Get default zone
      command: firewall-offline-cmd --get-default-zone
      register: __default_zone
      changed_when: false

    - name: Test firewalld posix compatibility
      block:

        # INIT TEST

        - name: Remove custom zone
          command: firewall-offline-cmd --delete-zone=custom
          register: result
          failed_when: result.failed and "INVALID_ZONE" not in result.stderr
          changed_when: false

        - name: Reset internal zone to defaults
          command: firewall-offline-cmd --load-zone-defaults=internal
          register: result
          failed_when: result.failed and "NO_DEFAULTS" not in result.stderr
          changed_when: false

        - name: Reset trusted zone to defaults
          command: firewall-offline-cmd --load-zone-defaults=trusted
          register: result
          failed_when: result.failed and "NO_DEFAULTS" not in result.stderr
          changed_when: false

        - name: Reset dmz zone to defaults
          command: firewall-offline-cmd --load-zone-defaults=dmz
          register: result
          failed_when: result.failed and "NO_DEFAULTS" not in result.stderr
          changed_when: false

        - name: Reset drop zone to defaults
          command: firewall-offline-cmd --load-zone-defaults=drop
          register: result
          failed_when: result.failed and "NO_DEFAULTS" not in result.stderr
          changed_when: false

        - name: Reset public zone to defaults
          command: firewall-offline-cmd --load-zone-defaults=public
          register: result
          failed_when: result.failed and "NO_DEFAULTS" not in result.stderr
          changed_when: false

        - name: Reset default zone to defaults
          shell:
            cmd: |
              zone=$(firewall-offline-cmd --get-default-zone)
              firewall-offline-cmd --load-zone-defaults=$zone
          register: result
          failed_when: result.failed and "NO_DEFAULTS" not in result.stderr
          changed_when: false

        - name: Create custom zone
          # noqa no-changed-when
          command: firewall-offline-cmd --new-zone=custom
          register: result
          failed_when: result.failed or not result.changed

        - name: Reload firewalld
          # noqa no-changed-when
          command: firewall-cmd --reload
          register: result
          failed_when: result.failed or not result.changed
          when: __firewall_is_booted

        # TEST

        - name: Permit traffic in default zone for https service
          firewall_lib:
            service: https
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Permit traffic in default zone for https service, again
          firewall_lib:
            service: https
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Permit traffic in default zone on port 8081/tcp
          firewall_lib:
            port: 8081/tcp
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Permit traffic in default zone on port 8081/tcp, again
          firewall_lib:
            port: 8081/tcp
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Allow port 161-162/udp in permanent default zone
          firewall_lib:
            port: 161-162/udp
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Allow port 161-162/udp in permanent default zone, again
          firewall_lib:
            port: 161-162/udp
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Do not permit traffic in default zone on port 8081/tcp
          firewall_lib:
            port: 8081/tcp
            permanent: true
            state: disabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Do not permit traffic in default zone on port 8081/tcp, again
          firewall_lib:
            port: 8081/tcp
            permanent: true
            state: disabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Allow service http in permanent dmz zone
          firewall_lib:
            zone: dmz
            service: http
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Allow service http in permanent dmz zone, again
          firewall_lib:
            zone: dmz
            service: http
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Allow service ftp with limitation using rich rule in permanent
                default zone
          firewall_lib:
            rich_rule: rule service name="ftp" audit limit value="1/m" accept
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Allow service ftp with limitation using rich rule in permanent
                default zone, again
          firewall_lib:
            rich_rule: rule service name="ftp" audit limit value="1/m" accept
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Allow source 192.0.2.0/24 in internal zone
          firewall_lib:
            source: 192.0.2.0/24
            zone: internal
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Allow source 192.0.2.0/24 in internal zone, again
          firewall_lib:
            source: 192.0.2.0/24
            permanent: true
            zone: internal
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Allow nm interface eth0 in permanent trusted zone
          firewall_lib:
            zone: trusted
            interface: eth0
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Allow nm interface eth0 in permanent trusted zone, again
          firewall_lib:
            zone: trusted
            interface: eth0
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Disable nm interface eth0 in permanent trusted zone
          firewall_lib:
            zone: trusted
            interface: eth0
            permanent: true
            state: disabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Disable nm interface eth0 in permanent trusted zone, again
          firewall_lib:
            zone: trusted
            interface: eth0
            permanent: true
            state: disabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Allow interface eth2 in permanent trusted zone
          firewall_lib:
            zone: trusted
            interface: eth2
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Allow interface eth2 in permanent trusted zone, again
          firewall_lib:
            zone: trusted
            interface: eth2
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Allow masquerading in permanent dmz zone
          firewall_lib:
            masquerade: true
            permanent: true
            zone: dmz
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Allow masquerading in permanent dmz zone, again
          firewall_lib:
            masquerade: true
            permanent: true
            zone: dmz
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Ensure permanent custom zone exists (no change)
          firewall_lib:
            zone: custom
            state: enabled
            permanent: true
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Ensure ICMP block inversion in permanent drop zone
          firewall_lib:
            zone: drop
            permanent: true
            icmp_block_inversion: true
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Ensure ICMP block inversion in permanent drop zone, again
          firewall_lib:
            zone: drop
            permanent: true
            icmp_block_inversion: true
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Block ICMP echo-request in permanent drop zone
          firewall_lib:
            zone: drop
            state: enabled
            permanent: true
            icmp_block: echo-request
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Block ICMP echo-request in permanent drop zone, again
          firewall_lib:
            zone: drop
            state: enabled
            permanent: true
            icmp_block: echo-request
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Ensure default target in permanent internal zone (no change)
          firewall_lib:
            zone: internal
            state: enabled
            permanent: true
            target: default
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Ensure target ACCEPT in permanent internal zone
          firewall_lib:
            zone: internal
            state: enabled
            permanent: true
            target: ACCEPT
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Ensure target ACCEPT in permanent internal zone, again
          firewall_lib:
            zone: internal
            state: enabled
            permanent: true
            target: ACCEPT
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Ensure default target in permanent internal zone
          firewall_lib:
            zone: internal
            state: enabled
            permanent: true
            target: default
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or not result.changed

        - name: Ensure default target in permanent internal zone, again
          firewall_lib:
            zone: internal
            state: enabled
            permanent: true
            target: default
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result.failed or result.changed

        - name: Redirect port 443 to 8443 with Rich Rule in permanent and
                runtime public zone
          firewall_lib:
            rich_rule: rule family=ipv4 forward-port port=443 protocol=tcp
                      to-port=8443
            zone: public
            permanent: true
            runtime: true
            state: enabled
          register: result
          failed_when: result.failed or not result.changed
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Redirect port 443 to 8443 with Rich Rule in permanent and
                runtime public zone, again
          firewall_lib:
            rich_rule: rule family=ipv4 forward-port port=443 protocol=tcp
                      to-port=8443
            zone: public
            permanent: true
            runtime: true
            state: enabled
          register: result
          failed_when: result.failed or result.changed
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Firewalld custom zone
          firewall_lib:
            zone: customzone
            state: present
            permanent: true
            runtime: true
          register: result
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Assert firewalld custom zone
          assert:
            that:
              - result is changed
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Add masquerade to custom zone
          firewall_lib:
            masquerade: true
            zone: customzone
            state: enabled
            permanent: true
            runtime: true
          register: result
          failed_when: result is failed or result is not changed
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Add masquerade to custom zone, again
          firewall_lib:
            masquerade: true
            zone: customzone
            state: enabled
            permanent: true
            runtime: true
          register: result
          failed_when: result is failed or result is changed
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Set the default zone to something other than dmz
          # --set-default-zone not idempotent: https://bugzilla.redhat.com/show_bug.cgi?id=2363037
          shell: |
            cur_zone=$(firewall-offline-cmd --get-default-zone)
            if [ "$cur_zone" != public ]; then
              firewall-offline-cmd --set-default-zone public
            fi
          changed_when: false

        - name: Set default zone (runtime)
          firewall_lib:
            set_default_zone: dmz
            state: enabled
          register: result
          failed_when: result is failed or result is not changed
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Set default zone, again (runtime)
          firewall_lib:
            set_default_zone: dmz
            state: enabled
          register: result
          failed_when: result is failed or result is changed
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Add multiple custom services
          firewall_lib:
            service:
              - custom
              - another_custom
            permanent: true
            state: present
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is not failed or result is changed

        - name: Add custom service without details
          firewall_lib:
            service: customservice
            permanent: true
            state: present
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is not changed

        - name: Add custom service that already exists
          firewall_lib:
            service: customservice
            permanent: true
            state: present
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is changed

        - name: Add description to custom service
          firewall_lib:
            service: customservice
            description: "This is a custom service that does absolutely nothing"
            permanent: true
            state: present
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is not changed

        - name: Add short description to custom service
          firewall_lib:
            service: customservice
            short: "custom service"
            permanent: true
            state: present
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is not changed

        - name: Add source_ports to custom service
          firewall_lib:
            service: customservice
            source_port: 333/tcp
            permanent: true
            state: present
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is not changed

        - name: Add source_ports to custom service again
          firewall_lib:
            service: customservice
            source_port: 333/tcp
            permanent: true
            state: present
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is changed

        - name: Add invalid protocol to custom service
          firewall_lib:
            service: customservice
            protocol: nonexistentprotocol
            permanent: true
            state: present
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is not failed or result is changed

        - name: Add valid protocol to custom service
          firewall_lib:
            service: customservice
            protocol: icmp
            permanent: true
            state: present
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is not changed

        - name: Remove valid protocol from customservice
          firewall_lib:
            service: customservice
            protocol: icmp
            permanent: true
            state: absent
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is not changed

        - name: Remove ports from custom service without deleting service
          firewall_lib:
            service: customservice
            source_port: 333/tcp
            permanent: true
            state: absent
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is not changed

        - name: Add destination addresses
          firewall_lib:
            service: customservice
            destination:
              - 146.19.6.63
              - "aaaa:aaaa:aaaa:aaaa::"
            state: present
            permanent: true
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is not changed

        - name: Add includes
          firewall_lib:
            service: customservice
            includes:
              - https
              - ldaps
            state: present
            permanent: true
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is not changed
          when:
            - ansible_distribution in ["RedHat", "CentOS", "Fedora"]
            - ansible_distribution_major_version is version("8", ">=")

        - name: Add includes again to check idempotence
          firewall_lib:
            service: customservice
            includes:
              - https
              - ldaps
            state: present
            permanent: true
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is changed
          when:
            - ansible_distribution in ["RedHat", "CentOS", "Fedora"]
            - ansible_distribution_major_version is version("8", ">=")

        - name: Delete custom service
          firewall_lib:
            service: customservice
            permanent: true
            state: absent
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is not changed

        - name: Delete custom service again
          firewall_lib:
            service: customservice
            permanent: true
            state: absent
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is changed

        - name: Add custom service with all the elements at once
          firewall_lib:
            permanent: true
            state: present
            service: customservice
            short: Custom service
            port: 333/tcp
            source_port: 333/tcp
            protocol: ipv6
            description: Custom Service that does not do a single thing
            destination:
              - 123.45.6.78
              - "aaaa:aaaa:aaaa:aaa:aaaa:aaaa:aaaa::"
            # these two don't exist yet in RHEL 7
            helper_module: "{{ 'nf_conntrack_ftp'
              if ansible_distribution in ['RedHat', 'CentOS', 'Fedora']
              and ansible_distribution_major_version is version('8', '>=')
              else omit }}"
            includes: "{{ ['https', 'ldaps']
              if ansible_distribution in ['RedHat', 'CentOS', 'Fedora']
              and ansible_distribution_major_version is version('8', '>=')
              else omit }}"
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is not changed

        - name: Verify service settings
          command: firewall-offline-cmd --info-service customservice
          register: info
          changed_when: false
          failed_when: >
              info.failed
              or "ports: 333/tcp" not in info.stdout
              or "source-ports: 333/tcp" not in info.stdout
              or "protocols: ipv6" not in info.stdout
              or "destination: ipv4:123.45.6.78 ipv6:aaaa:aaaa:aaaa:aaa:aaaa:aaaa:aaaa::"
                 not in info.stdout

        - name: Verify includes and helper service settings
          assert:
            that:
              - '"includes: https ldaps" in info.stdout'
              - '"helpers: nf_conntrack_ftp" in info.stdout'
          when: ansible_distribution in ['RedHat', 'CentOS', 'Fedora']
                and ansible_distribution_major_version is version('8', '>=')

        - name: Add helper module that already is on customservice
          firewall_lib:
            service: customservice
            helper_module: nf_conntrack_ftp
            permanent: true
            state: present
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is changed
          when: ansible_distribution in ['RedHat', 'CentOS', 'Fedora']
                and ansible_distribution_major_version is version('8', '>=')

        - name: Forward port 40 to 0.0.0.0:8080 (string)
          firewall_lib:
            forward_port: 40/tcp;8080;0.0.0.0
            state: enabled
          register: result
          failed_when: result is failed or result is not changed
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Forward same port again (string)
          firewall_lib:
            forward_port: 40/tcp;8080;0.0.0.0
            state: enabled
          register: result
          failed_when: result is failed or result is changed
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Disable forward port (string)
          firewall_lib:
            forward_port: 40/tcp;8080;0.0.0.0
            state: disabled
          register: result
          failed_when: result is failed or result is not changed
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Forward port (dict form)
          firewall_lib:
            forward_port:
              port: 40
              proto: tcp
              toport: 8080
              toaddr: 0.0.0.0
            state: enabled
          register: result
          failed_when: result is failed or result is not changed
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Forward port again (dict form)
          firewall_lib:
            forward_port:
              - port: 40
                proto: tcp
                toport: 8080
                toaddr: 0.0.0.0
            state: enabled
          register: result
          failed_when: result is failed or result is changed
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Disable forward port (dict form)
          firewall_lib:
            forward_port:
              - port: 40
                proto: tcp
                toport: 8080
                toaddr: 0.0.0.0
            state: disabled
          register: result
          failed_when: result is failed or result is not changed
          # runtime not supported with offline backend
          when: __firewall_is_booted

        - name: Forward port 40 to 0.0.0.0:8080 (permanent)
          firewall_lib:
            forward_port: 40/tcp;8080;0.0.0.0
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is not changed

        - name: Forward same port again (permanent)
          firewall_lib:
            forward_port: 40/tcp;8080;0.0.0.0
            permanent: true
            state: enabled
            online: "{{ __firewall_is_booted }}"
          register: result
          failed_when: result is failed or result is changed

      always:

        - name: Cleanup
          tags:
            - tests::cleanup
          block:
            # CLEANUP: RESET TO ZONE DEFAULTS

            - name: Remove custom zone
              command: firewall-offline-cmd --delete-zone=custom
              register: result
              failed_when: result.failed and "INVALID_ZONE" not in result.stderr
              changed_when: false

            - name: Remove customzone zone
              command: firewall-offline-cmd --delete-zone=customzone
              register: result
              failed_when: result.failed and "INVALID_ZONE" not in result.stderr
              changed_when: false

            - name: Reset internal zone to defaults
              command: firewall-offline-cmd --load-zone-defaults=internal
              register: result
              failed_when: result.failed and "NO_DEFAULTS" not in result.stderr
              changed_when: false

            - name: Reset trusted zone to defaults
              command: firewall-offline-cmd --load-zone-defaults=trusted
              register: result
              failed_when: result.failed and "NO_DEFAULTS" not in result.stderr
              changed_when: false

            - name: Reset dmz zone to defaults
              command: firewall-offline-cmd --load-zone-defaults=dmz
              register: result
              failed_when: result.failed and "NO_DEFAULTS" not in result.stderr
              changed_when: false

            - name: Reset drop zone to defaults
              command: firewall-offline-cmd --load-zone-defaults=drop
              register: result
              failed_when: result.failed and "NO_DEFAULTS" not in result.stderr
              changed_when: false

            - name: Reset public zone to defaults
              command: firewall-offline-cmd --load-zone-defaults=public
              register: result
              failed_when: result.failed and "NO_DEFAULTS" not in result.stderr
              changed_when: false

            - name: Reset default zone to defaults
              command: firewall-offline-cmd --load-zone-defaults=public
              register: result
              failed_when: result.failed and "NO_DEFAULTS" not in result.stderr
              changed_when: false

            - name: Reset default zone
              # --set-default-zone not idempotent: https://bugzilla.redhat.com/show_bug.cgi?id=2363037
              shell: |
                cur_zone=$(firewall-offline-cmd --get-default-zone)
                if [ "$cur_zone" != {{ __default_zone.stdout | quote }} ]; then
                  firewall-offline-cmd --set-default-zone={{ __default_zone.stdout | quote }}
                fi
              changed_when: false

            - name: Reload firewalld
              # noqa no-changed-when
              command: firewall-cmd --reload
              register: result
              failed_when: result.failed or not result.changed
              when: __firewall_is_booted
