---

- name: Install tools
  package:
    name:
      - pwgen
      - openssl
  when: rand_pass_tools_installed is not defined or not rand_pass_tools_installed

# Mark tool sas installed so we do not check each time, as it can be slow
- set_fact: rand_pass_tools_installed=True

# Check if a non encrypted file exists. We do it first for backward compatibility
- name: Check if password file exists
  stat: path={{ pass_file }}
  register: rand_pass_file_clear

# Check if and encrypted file exists using pbkdf2 key derivation
- name: Check if an encrypted using PBKDF2 password file exists
  stat: path={{ pass_file }}.aes256-pbkdf2
  register: rand_pass_file_enc_pbkdf2

# Now check if an encrypted file exists (without pbkdf2)
- name: Check if an encrypted password file exists
  stat: path={{ pass_file }}.aes256
  register: rand_pass_file_enc

# When no clear nor encrypted file exists, generate a random pass with pwgen
- when:
    - not rand_pass_file_clear.stat.exists
    - not rand_pass_file_enc.stat.exists
    - not rand_pass_file_enc_pbkdf2.stat.exists
  block:
    - name: Generate a random password
      shell: pwgen {% if complex | default(True) %}-y -r \`\'\"\\\|\^\# {% endif %}-s {{ pass_size | default(50) }} 1
      register: rand_pass_new

    - set_fact: rand_pass_new={{ rand_pass_new.stdout | trim }}

# Check if openssl supports pbkdf2
- name: Check if openssl supports pbkdf2 key derivation
  command: openssl enc -pbkdf2 -list
  register: pass_openssl_pbkdf2
  failed_when: False
  changed_when: False

# New pass generation ? Encrypt it with openssl, unless encryption is disabled, or the global rand_pass_encryption_key isn't defined
# If openssl supports PBKDF2 key derivation, use it
- when:
    - not rand_pass_file_clear.stat.exists
    - not rand_pass_file_enc.stat.exists
    - not rand_pass_file_enc_pbkdf2.stat.exists
    - encryption | default(True)
    - rand_pass_encryption_key is defined
  block:
    - name: Encrypt the generated password
      shell: openssl enc -e -a -aes256 {% if pass_openssl_pbkdf2.rc == 0 %}-pbkdf2 {% endif %}-pass pass:{{ rand_pass_encryption_key | quote }}
      args:
        stdin: "{{ rand_pass_new }}"
      register: rand_pass_enc
      no_log: True
    - copy: content={{ rand_pass_enc.stdout | trim }} dest={{ pass_file }}.aes256{% if pass_openssl_pbkdf2.rc == 0 %}-pbkdf2{% endif %} mode=600

# New pass generation but with encryption disabled, or the global rand_pass_encryption_key not defined
# in this case, store the password as plain text
- when:
    - not rand_pass_file_clear.stat.exists
    - not rand_pass_file_enc.stat.exists
    - not rand_pass_file_enc_pbkdf2.stat.exists
    - not encryption | default(True) or rand_pass_encryption_key is not defined
  name: Store the generated password as clear text
  copy: content={{ rand_pass_new }} dest={{ pass_file }} mode=600

# Read the encrypted pass (with PBKDF2)
- when:
    - not rand_pass_file_clear.stat.exists
    - not rand_pass_file_enc.stat.exists
    - rand_pass_file_enc_pbkdf2.stat.exists or (rand_pass_new is defined and pass_openssl_pbkdf2.rc == 0)
    - pass_openssl_pbkdf2.rc == 0
    - encryption | default(True)
    - rand_pass_encryption_key is defined
  block:

    - name: Read the encrypted (with PBKDF2) password
      slurp: src={{ pass_file }}.aes256-pbkdf2
      register: rand_pass_enc_pbkdf2

    - name: Decrypt (using PBKDF2) the password
      shell: openssl enc -d -a -aes256 -pbkdf2 -pass pass:{{ rand_pass_encryption_key | quote }}
      args:
        stdin: "{{ rand_pass_enc_pbkdf2.content | b64decode | trim }}"
      register: rand_pass_dec_pbkdf2
      changed_when: False
      no_log: True

    - set_fact: rand_pass_dec_pbkdf2={{ rand_pass_dec_pbkdf2.stdout | trim }}

# Read the encrypted pass (without PBKDF2)
- when:
    - not rand_pass_file_clear.stat.exists
    - rand_pass_file_enc.stat.exists or (rand_pass_new is defined and pass_openssl_pbkdf2.rc != 0)
    - not rand_pass_file_enc_pbkdf2.stat.exists
    - encryption | default(True)
    - rand_pass_encryption_key is defined
  block:

    - name: Read the encrypted password
      slurp: src={{ pass_file }}.aes256
      register: rand_pass_enc

    - name: Decrypt the password
      shell: openssl enc -d -a -aes256 -pass pass:{{ rand_pass_encryption_key | quote }}
      args:
        stdin: "{{ rand_pass_enc.content | b64decode | trim }}"
      register: rand_pass_dec
      changed_when: False
      no_log: True

    - set_fact: rand_pass_dec={{ rand_pass_dec.stdout }}

# Read unencrypted pass file
- when: not encryption | default(True) or rand_pass_encryption_key is not defined or rand_pass_file_clear.stat.exists
  block:
    - name: Read the clear text password
      slurp: src={{ pass_file }}
      register: rand_pass_clear

    - set_fact: rand_pass_clear={{ rand_pass_clear.content | b64decode | trim }}

# Reencrypt the password from clear text when possible
- when:
    - rand_pass_file_clear.stat.exists
    - encryption | default(True)
    - rand_pass_encryption_key is defined
  block:

    - name: Re encrypt the clear text password
      shell: openssl enc -e -a -aes256 {% if pass_openssl_pbkdf2.rc == 0 %}-pbkdf2 {% endif %}-pass pass:{{ rand_pass_encryption_key | quote }}
      args:
        stdin: "{{ rand_pass_clear }}"
      register: rand_pass_reenc
      no_log: True

    - name: Store the re encrypted password
      copy: content={{ rand_pass_reenc.stdout | trim }} dest={{ pass_file }}.aes256{% if pass_openssl_pbkdf2.rc == 0 %}-pbkdf2{% endif %} mode=600

    - name: Remove the clear text pass file
      file: path={{ pass_file }} state=absent

# Reencrypt the password from aes256 to aes256 with PBKDF2 when possible
- when:
    - rand_pass_file_enc.stat.exists
    - not rand_pass_file_enc_pbkdf2.stat.exists
    - pass_openssl_pbkdf2.rc == 0
    - encryption | default(True)
    - rand_pass_encryption_key is defined
  block:

    - name: Re encrypt the password using PBKDF2
      shell: openssl enc -e -a -aes256 -pbkdf2 -pass pass:{{ rand_pass_encryption_key | quote }}
      args:
        stdin: "{{ rand_pass_dec }}"
      register: rand_pass_reenc
      no_log: True

    - name: Store the re encrypted password with PBKDF2
      copy: content={{ rand_pass_reenc.stdout | trim }} dest={{ pass_file }}.aes256-pbkdf2 mode=600

    - name: Remove the encrypted pass file without PBKDF2
      file: path={{ pass_file }}.aes256 state=absent

# Now set either the decrypted, or the clear text pass in the rand_pass variable which will be used by the caller
- set_fact:
    rand_pass: >-
      {%- if rand_pass_dec_pbkdf2 is defined and rand_pass_dec_pbkdf2.skipped is not defined -%}{{ rand_pass_dec_pbkdf2 }}
      {%- elif rand_pass_dec is defined and rand_pass_dec.skipped is not defined -%}{{ rand_pass_dec }}
      {%- elif rand_pass_clear is defined and rand_pass_clear.skipped is not defined -%}{{ rand_pass_clear }}
      {%- else -%}{%- endif -%}

