---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

- name: Create CRL 1
  x509_crl:
    path: '{{ remote_tmp_dir }}/ca-crl1.crl'
    privatekey_path: '{{ remote_tmp_dir }}/ca.key'
    issuer:
      CN: Ansible
    last_update: 20191013000000Z
    next_update: 20191113000000Z
    revoked_certificates:
      - path: '{{ remote_tmp_dir }}/cert-1.pem'
        revocation_date: 20191013000000Z
      - path: '{{ remote_tmp_dir }}/cert-2.pem'
        revocation_date: 20191013000000Z
        reason: key_compromise
        reason_critical: true
        invalidity_date: 20191012000000Z
      - serial_number: 1234
        revocation_date: 20191001000000Z

- name: Retrieve CRL 1 infos
  set_fact:
    crl_1_info_1: >-
      {{ lookup('file', remote_tmp_dir ~ '/ca-crl1.crl') | community.crypto.x509_crl_info }}

- name: Retrieve CRL 1 infos
  set_fact:
    crl_1_info_2: >-
      {{ lookup('file', remote_tmp_dir ~ '/ca-crl1.crl') | b64encode | community.crypto.x509_crl_info }}

- name: Validate CRL 1 info
  assert:
    that:
      - crl_1_info_1.format == 'pem'
      - crl_1_info_1.digest == 'ecdsa-with-SHA256'
      - crl_1_info_1.issuer | length == 1
      - crl_1_info_1.issuer.commonName == 'Ansible'
      - crl_1_info_1.issuer_ordered | length == 1
      - crl_1_info_1.last_update == '20191013000000Z'
      - crl_1_info_1.next_update == '20191113000000Z'
      - crl_1_info_1.revoked_certificates | length == 3
      - crl_1_info_1.revoked_certificates[0].invalidity_date is none
      - crl_1_info_1.revoked_certificates[0].invalidity_date_critical == false
      - crl_1_info_1.revoked_certificates[0].issuer is none
      - crl_1_info_1.revoked_certificates[0].issuer_critical == false
      - crl_1_info_1.revoked_certificates[0].reason is none
      - crl_1_info_1.revoked_certificates[0].reason_critical == false
      - crl_1_info_1.revoked_certificates[0].revocation_date == '20191013000000Z'
      - crl_1_info_1.revoked_certificates[0].serial_number == certificate_infos.results[0].serial_number
      - crl_1_info_1.revoked_certificates[1].invalidity_date == '20191012000000Z'
      - crl_1_info_1.revoked_certificates[1].invalidity_date_critical == false
      - crl_1_info_1.revoked_certificates[1].issuer is none
      - crl_1_info_1.revoked_certificates[1].issuer_critical == false
      - crl_1_info_1.revoked_certificates[1].reason == 'key_compromise'
      - crl_1_info_1.revoked_certificates[1].reason_critical == true
      - crl_1_info_1.revoked_certificates[1].revocation_date == '20191013000000Z'
      - crl_1_info_1.revoked_certificates[1].serial_number == certificate_infos.results[1].serial_number
      - crl_1_info_1.revoked_certificates[2].invalidity_date is none
      - crl_1_info_1.revoked_certificates[2].invalidity_date_critical == false
      - crl_1_info_1.revoked_certificates[2].issuer is none
      - crl_1_info_1.revoked_certificates[2].issuer_critical == false
      - crl_1_info_1.revoked_certificates[2].reason is none
      - crl_1_info_1.revoked_certificates[2].reason_critical == false
      - crl_1_info_1.revoked_certificates[2].revocation_date == '20191001000000Z'
      - crl_1_info_1.revoked_certificates[2].serial_number == 1234
      - crl_1_info_1 == crl_1_info_2

- name: Recreate CRL 1 as DER file
  x509_crl:
    path: '{{ remote_tmp_dir }}/ca-crl1.crl'
    privatekey_path: '{{ remote_tmp_dir }}/ca.key'
    format: der
    issuer:
      CN: Ansible
    last_update: 20191013000000Z
    next_update: 20191113000000Z
    revoked_certificates:
      - path: '{{ remote_tmp_dir }}/cert-1.pem'
        revocation_date: 20191013000000Z
      - path: '{{ remote_tmp_dir }}/cert-2.pem'
        revocation_date: 20191013000000Z
        reason: key_compromise
        reason_critical: true
        invalidity_date: 20191012000000Z
      - serial_number: 1234
        revocation_date: 20191001000000Z

- name: Read ca-crl1.crl
  slurp:
    src: "{{ remote_tmp_dir }}/ca-crl1.crl"
  register: content

- name: Retrieve CRL 1 infos from DER (raw bytes)
  set_fact:
    crl_1_info_4: >-
      {{ content.content | b64decode | community.crypto.x509_crl_info }}
  # Ansible 2.9 and ansible-base 2.10 on Python 2 mangle bytes, so do not run this on these versions
  when: ansible_version.string is version('2.11', '>=') or ansible_python.version.major > 2

- name: Retrieve CRL 1 infos from DER (Base64 encoded)
  set_fact:
    crl_1_info_5: >-
      {{ content.content | community.crypto.x509_crl_info }}

- name: Validate CRL 1
  assert:
    that:
      - crl_1_info_4 is not defined or crl_1_info_4.format == 'der'
      - crl_1_info_5.format == 'der'
      - crl_1_info_4 is not defined or crl_1_info_4 == crl_1_info_5

- name: Create CRL 2
  x509_crl:
    path: '{{ remote_tmp_dir }}/ca-crl2.crl'
    privatekey_path: '{{ remote_tmp_dir }}/ca.key'
    issuer_ordered:
      - CN: Ansible
      - CN: CRL
      - countryName: US
      - CN: Test
    last_update: +0d
    next_update: +0d
    revoked_certificates:
      - path: '{{ remote_tmp_dir }}/cert-2.pem'
        reason: key_compromise
        reason_critical: true
        invalidity_date: 20191012000000Z
    ignore_timestamps: false
    crl_mode: update
    return_content: true
  register: crl_2_change

- name: Retrieve CRL 2 infos
  set_fact:
    crl_2_info_1: >-
      {{ lookup('file', remote_tmp_dir ~ '/ca-crl2.crl') | community.crypto.x509_crl_info(list_revoked_certificates=false) }}

- name: Create CRL 2 (changed order)
  x509_crl:
    path: '{{ remote_tmp_dir }}/ca-crl2.crl'
    privatekey_path: '{{ remote_tmp_dir }}/ca.key'
    issuer_ordered:
      - CN: Ansible
      - countryName: US
      - CN: CRL
      - CN: Test
    last_update: +0d
    next_update: +0d
    revoked_certificates:
      - path: '{{ remote_tmp_dir }}/cert-2.pem'
        reason: key_compromise
        reason_critical: true
        invalidity_date: 20191012000000Z
    ignore_timestamps: true
    crl_mode: update
    return_content: true
  register: crl_2_change_order

- name: Retrieve CRL 2 infos again
  set_fact:
    crl_2_info_2: >-
      {{ lookup('file', remote_tmp_dir ~ '/ca-crl2.crl') | community.crypto.x509_crl_info(list_revoked_certificates=false) }}

- name: Validate CRL 2 info
  assert:
    that:
      - "'revoked_certificates' not in crl_2_info_1"
      - >
        crl_2_info_1.issuer_ordered == [
          ['commonName', 'Ansible'],
          ['commonName', 'CRL'],
          ['countryName', 'US'],
          ['commonName', 'Test'],
        ]
      - >
        crl_2_info_2.issuer_ordered == [
          ['commonName', 'Ansible'],
          ['countryName', 'US'],
          ['commonName', 'CRL'],
          ['commonName', 'Test'],
        ]

- name: Create CRL 3
  x509_crl:
    path: '{{ remote_tmp_dir }}/ca-crl3.crl'
    privatekey_path: '{{ remote_tmp_dir }}/ca.key'
    issuer:
      CN: Ansible
    last_update: +0d
    next_update: +0d
    revoked_certificates:
      - serial_number: 1234
        revocation_date: 20191001000000Z
        # * cryptography < 2.1 strips username and password from URIs. To avoid problems, we do
        #   not pass usernames and passwords for URIs when the cryptography version is < 2.1.
        # * Python 3.5 before 3.5.8 rc 1 has a bug in urllib.parse.urlparse() that results in an
        #   error if a Unicode netloc has a username or password included.
        #   (https://github.com/ansible-collections/community.crypto/pull/436#issuecomment-1101737134)
        #   This affects the Python 3.5 included in Ansible 2.9's default test container; to avoid
        #   this, we also do not pass usernames and passwords for Python 3.5.
        issuer:
          - "DNS:ca.example.org"
          - "DNS:ﬀóò.ḃâŗ.çøṁ"
          - "email:foo@ḃâŗ.çøṁ"
          - "URI:https://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'admin:hunter2@' }}ﬀóò.ḃâŗ.çøṁ/baz?foo=bar"
          - "URI:https://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'goo@' }}www.straße.de"
          - "URI:https://straße.de:8080"
          - "URI:http://gefäß.org"
          - "URI:http://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'a:b@' }}ä:1"
        issuer_critical: true
  register: crl_3

- name: Create CRL 3 (IDNA encoding)
  x509_crl:
    path: '{{ remote_tmp_dir }}/ca-crl3.crl'
    privatekey_path: '{{ remote_tmp_dir }}/ca.key'
    issuer:
      CN: Ansible
    last_update: +0d
    next_update: +0d
    revoked_certificates:
      - serial_number: 1234
        revocation_date: 20191001000000Z
        issuer:
          - "DNS:ca.example.org"
          - "DNS:xn--ff-3jad.xn--2ca8uh37e.xn--7ca8a981n"
          - "email:foo@xn--2ca8uh37e.xn--7ca8a981n"
          - "URI:https://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'admin:hunter2@' }}xn--ff-3jad.xn--2ca8uh37e.xn--7ca8a981n/baz?foo=bar"
          - "URI:https://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'goo@' }}www.xn--strae-oqa.de"
          - "URI:https://xn--strae-oqa.de:8080"
          - "URI:http://xn--gef-7kay.org"
          - "URI:http://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'a:b@' }}xn--4ca:1"
        issuer_critical: true
    ignore_timestamps: true
    name_encoding: idna
  register: crl_3_idna

- name: Create CRL 3 (Unicode encoding)
  x509_crl:
    path: '{{ remote_tmp_dir }}/ca-crl3.crl'
    privatekey_path: '{{ remote_tmp_dir }}/ca.key'
    issuer:
      CN: Ansible
    last_update: +0d
    next_update: +0d
    revoked_certificates:
      - serial_number: 1234
        revocation_date: 20191001000000Z
        issuer:
          - "DNS:ca.example.org"
          - "DNS:ﬀóò.ḃâŗ.çøṁ"
          - "email:foo@ḃâŗ.çøṁ"
          - "URI:https://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'admin:hunter2@' }}ﬀóò.ḃâŗ.çøṁ/baz?foo=bar"
          - "URI:https://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'goo@' }}www.straße.de"
          - "URI:https://straße.de:8080"
          - "URI:http://gefäß.org"
          - "URI:http://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'a:b@' }}ä:1"
        issuer_critical: true
    ignore_timestamps: true
    name_encoding: unicode
  register: crl_3_unicode

- name: Retrieve CRL 3 infos
  set_fact:
    crl_3_info: >-
      {{ lookup('file', remote_tmp_dir ~ '/ca-crl3.crl') | community.crypto.x509_crl_info(list_revoked_certificates=true) }}
    crl_3_info_idna: >-
      {{ lookup('file', remote_tmp_dir ~ '/ca-crl3.crl') | community.crypto.x509_crl_info(list_revoked_certificates=true, name_encoding='idna') }}
    crl_3_info_unicode: >-
      {{ lookup('file', remote_tmp_dir ~ '/ca-crl3.crl') | community.crypto.x509_crl_info(list_revoked_certificates=true, name_encoding='unicode') }}

- name: Validate CRL 3 info
  assert:
    that:
      - crl_3.revoked_certificates == crl_3_info.revoked_certificates
      - crl_3_idna.revoked_certificates == crl_3_info_idna.revoked_certificates
      - crl_3_unicode.revoked_certificates == crl_3_info_unicode.revoked_certificates

- name: Get invalid CRL info
  set_fact:
    result: >-
      {{ [] | community.crypto.x509_crl_info }}
  ignore_errors: true
  register: output

- name: Check that task failed and error message is OK
  assert:
    that:
      - output is failed
      - output.msg is search("The community.crypto.x509_crl_info input must be a text type, not ")

- name: Get invalid CRL info
  set_fact:
    result: >-
      {{ 'foo' | community.crypto.x509_crl_info }}
  ignore_errors: true
  register: output

- name: Check that task failed and error message is OK
  assert:
    that:
      - output is failed
      - output.msg is search("Error while decoding CRL")

- name: Get invalid CRL info
  set_fact:
    result: >-
      {{ 'foo' | community.crypto.x509_crl_info(name_encoding=[]) }}
  ignore_errors: true
  register: output

- name: Check that task failed and error message is OK
  assert:
    that:
      - output is failed
      - output.msg is search("The name_encoding option must be of a text type, not ")

- name: Get invalid name_encoding parameter
  set_fact:
    result: >-
      {{ 'bar' | community.crypto.x509_crl_info(name_encoding='foo') }}
  ignore_errors: true
  register: output

- name: Check that task failed and error message is OK
  assert:
    that:
      - output is failed
      - output.msg is search("The name_encoding option must be one of the values \"ignore\", \"idna\", or \"unicode\", not \"foo\"$")

- name: Get invalid list_revoked_certificates parameter
  set_fact:
    result: >-
      {{ 'bar' | community.crypto.x509_crl_info(list_revoked_certificates=[]) }}
  ignore_errors: true
  register: output

- name: Check that task failed and error message is OK
  assert:
    that:
      - output is failed
      - output.msg is search("The list_revoked_certificates option must be a boolean, not ")
