From 74f2e1d28b01e25fe2c894381b7fff1996cedfd9 Mon Sep 17 00:00:00 2001 From: grembo Date: Sun, 31 Jul 2022 22:13:27 +0200 Subject: [PATCH] passwordstore: Add some real gopass integration tests (#5030) * passwordstore: Add some real go tests This is work in progress. * passwordstore: Fix gopass init * Init gopass store in explicit path in integration test * passwordstore: Show versions of tools in integration test * passwordstore: Install gopass from different location on Debian Part of integration tests * passwordstore: Add changelog fragment for #5030 * passwordstore: Address review feedback --- .../lookup_passwordstore/tasks/package.yml | 21 ++ .../tasks/password_tests.yml | 125 ++++++++++++ .../lookup_passwordstore/tasks/tests.yml | 180 ++++-------------- .../lookup_passwordstore/vars/Alpine.yml | 1 + .../lookup_passwordstore/vars/Archlinux.yml | 1 + .../lookup_passwordstore/vars/Fedora.yml | 1 + .../lookup_passwordstore/vars/FreeBSD.yml | 1 + 7 files changed, 182 insertions(+), 148 deletions(-) create mode 100644 tests/integration/targets/lookup_passwordstore/tasks/password_tests.yml diff --git a/tests/integration/targets/lookup_passwordstore/tasks/package.yml b/tests/integration/targets/lookup_passwordstore/tasks/package.yml index 8ec108e089..0ced77c42e 100644 --- a/tests/integration/targets/lookup_passwordstore/tasks/package.yml +++ b/tests/integration/targets/lookup_passwordstore/tasks/package.yml @@ -31,6 +31,26 @@ disable_gpg_check: yes when: ansible_facts.pkg_mgr in ['zypper', 'community.general.zypper'] +# See https://github.com/gopasspw/gopass/issues/1849#issuecomment-802789285 +- name: Install gopass on Debian + when: ansible_facts.os_family == 'Debian' + become: yes + block: + - name: Fetch gopass repo keyring + ansible.builtin.get_url: + url: https://packages.gopass.pw/repos/gopass/gopass-archive-keyring.gpg + dest: /usr/share/keyrings/gopass-archive-keyring.gpg + - name: Add gopass repo + ansible.builtin.apt_repository: + repo: "deb [arch=amd64,arm64,armhf \ + signed-by=/usr/share/keyrings/gopass-archive-keyring.gpg] \ + https://packages.gopass.pw/repos/gopass stable main" + state: present + - name: Update apt-cache and install gopass package + ansible.builtin.apt: + name: gopass + update_cache: yes + - name: Install on macOS when: ansible_facts.distribution == 'MacOSX' block: @@ -48,6 +68,7 @@ name: - gnupg2 - pass + - gopass state: present update_homebrew: no become: yes diff --git a/tests/integration/targets/lookup_passwordstore/tasks/password_tests.yml b/tests/integration/targets/lookup_passwordstore/tasks/password_tests.yml new file mode 100644 index 0000000000..d519304f06 --- /dev/null +++ b/tests/integration/targets/lookup_passwordstore/tasks/password_tests.yml @@ -0,0 +1,125 @@ + - name: Create a password ({{ backend }}) + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'test-pass length=8 create=yes', backend=backend) }}" + + - name: Fetch password from an existing file ({{ backend }}) + set_fact: + readpass: "{{ lookup('community.general.passwordstore', 'test-pass', backend=backend) }}" + + - name: Verify password ({{ backend }}) + assert: + that: + - readpass == newpass + + - name: Create a password with equal sign ({{ backend }}) + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'test-pass-equal userpass=SimpleSample= create=yes', backend=backend) }}" + + - name: Fetch a password with equal sign ({{ backend }}) + set_fact: + readpass: "{{ lookup('community.general.passwordstore', 'test-pass-equal', backend=backend) }}" + + - name: Verify password ({{ backend }}) + assert: + that: + - readpass == newpass + + - name: Create a password using missing=create ({{ backend }}) + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'test-missing-create missing=create length=8', backend=backend) }}" + + - name: Fetch password from an existing file ({{ backend }}) + set_fact: + readpass: "{{ lookup('community.general.passwordstore', 'test-missing-create', backend=backend) }}" + + - name: Verify password ({{ backend }}) + assert: + that: + - readpass == newpass + + - name: Fetch password from existing file using missing=empty ({{ backend }}) + set_fact: + readpass: "{{ lookup('community.general.passwordstore', 'test-missing-create missing=empty', backend=backend) }}" + + - name: Verify password ({{ backend }}) + assert: + that: + - readpass == newpass + + - name: Fetch password from non-existing file using missing=empty ({{ backend }}) + set_fact: + readpass: "{{ query('community.general.passwordstore', 'test-missing-pass missing=empty', backend=backend) }}" + + - name: Verify password ({{ backend }}) + assert: + that: + - readpass == [ none ] + + - name: Create the YAML password ({{ backend }}) + command: "{{ backend }} insert -m -f test-yaml-pass" + args: + stdin: | + testpassword + key: | + multi + line + + - name: Fetch a password with YAML subkey ({{ backend }}) + set_fact: + readyamlpass: "{{ lookup('community.general.passwordstore', 'test-yaml-pass subkey=key', backend=backend) }}" + + - name: Read a yaml subkey ({{ backend }}) + assert: + that: + - readyamlpass == 'multi\nline\n' + + - name: Create a non-YAML multiline file ({{ backend }}) + command: "{{ backend }} insert -m -f test-multiline-pass" + args: + stdin: | + testpassword + random additional line + + - name: Fetch password from multiline file ({{ backend }}) + set_fact: + readyamlpass: "{{ lookup('community.general.passwordstore', 'test-multiline-pass', backend=backend) }}" + + - name: Multiline pass only returns first line ({{ backend }}) + assert: + that: + - readyamlpass == 'testpassword' + + - name: Fetch all from multiline file ({{ backend }}) + set_fact: + readyamlpass: "{{ lookup('community.general.passwordstore', 'test-multiline-pass returnall=yes', backend=backend) }}" + + - name: Multiline pass returnall returns everything in the file ({{ backend }}) + assert: + that: + - readyamlpass == 'testpassword\nrandom additional line\n' + + - name: Create a password in a folder ({{ backend }}) + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'folder/test-pass length=8 create=yes', backend=backend) }}" + + - name: Fetch password from folder ({{ backend }}) + set_fact: + readpass: "{{ lookup('community.general.passwordstore', 'folder/test-pass', backend=backend) }}" + + - name: Verify password from folder ({{ backend }}) + assert: + that: + - readpass == newpass + + - name: Try to read folder as passname ({{ backend }}) + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'folder', backend=backend) }}" + ignore_errors: true + register: eval_error + + - name: Make sure reading folder as passname failed ({{ backend }}) + assert: + that: + - eval_error is failed + - '"passname folder not found" in eval_error.msg' + when: backend != "gopass" # Remove this line once gopass backend can handle this diff --git a/tests/integration/targets/lookup_passwordstore/tasks/tests.yml b/tests/integration/targets/lookup_passwordstore/tasks/tests.yml index 5ac9a948a9..19f7283360 100644 --- a/tests/integration/targets/lookup_passwordstore/tasks/tests.yml +++ b/tests/integration/targets/lookup_passwordstore/tasks/tests.yml @@ -41,12 +41,10 @@ - name: Try to find gopass in path command: which gopass register: result - ignore_errors: yes - name: Store path of gopass executable set_fact: - gopasspath: "{{ (result.rc == 0) | - ternary(result.stdout, (passpath | dirname, 'gopass') | path_join) }}" + gopasspath: "{{ result.stdout }}" - name: Move original gopass into place if there was a leftover command: @@ -57,6 +55,18 @@ args: removes: "{{ gopasspath }}.testorig" +- name: Get versions of tools + command: "{{ item }} --version" + register: versions + loop: + - "{{ gpg2_bin }}" + - pass + - gopass + +- name: Output versions of tools + debug: + msg: "{{ versions.results | map(attribute='stdout_lines') }}" + # How to generate a new GPG key: # gpg2 --batch --gen-key input # See templates/input # gpg2 --list-secret-keys --keyid-format LONG @@ -70,150 +80,22 @@ - name: Trust key shell: echo "D3E1CC8934E97270CEB066023AF1BD3619AB496A:6:" | {{ gpg2_bin }} --import-ownertrust -- name: Initialise passwordstore +- name: Initialise pass passwordstore command: pass init ansible-test -- name: Create a password - set_fact: - newpass: "{{ lookup('community.general.passwordstore', 'test-pass length=8 create=yes') }}" +- name: Initialise gopass passwordstore + command: gopass init --path $HOME/.gopass-store ansible-test + args: + creates: "{{ lookup('env','HOME') }}/.gopass-store" -- name: Fetch password from an existing file - set_fact: - readpass: "{{ lookup('community.general.passwordstore', 'test-pass') }}" - -- name: Verify password - assert: - that: - - readpass == newpass - -- name: Create a password with equal sign - set_fact: - newpass: "{{ lookup('community.general.passwordstore', 'test-pass-equal userpass=SimpleSample= create=yes') }}" - -- name: Fetch a password with equal sign - set_fact: - readpass: "{{ lookup('community.general.passwordstore', 'test-pass-equal') }}" - -- name: Verify password - assert: - that: - - readpass == newpass - -- name: Create a password using missing=create - set_fact: - newpass: "{{ lookup('community.general.passwordstore', 'test-missing-create missing=create length=8') }}" - -- name: Fetch password from an existing file - set_fact: - readpass: "{{ lookup('community.general.passwordstore', 'test-missing-create') }}" - -- name: Verify password - assert: - that: - - readpass == newpass - -- name: Fetch password from existing file using missing=empty - set_fact: - readpass: "{{ lookup('community.general.passwordstore', 'test-missing-create missing=empty') }}" - -- name: Verify password - assert: - that: - - readpass == newpass - -- name: Fetch password from non-existing file using missing=empty - set_fact: - readpass: "{{ query('community.general.passwordstore', 'test-missing-pass missing=empty') }}" - -- name: Verify password - assert: - that: - - readpass == [ none ] - -# As inserting multiline passwords on the commandline would require something -# like expect, simply create it by using default gpg on a file with the correct -# structure. -- name: Create the YAML password content - copy: - dest: "~/.password-store/test-yaml-pass" - content: | - testpassword - key: | - multi - line - -- name: Read .gpg-id from .password-store - set_fact: - gpgid: "{{ lookup('file', '~/.password-store/.gpg-id') }}" - -- name: Encrypt the file using the gpg key - command: "{{ gpg2_bin }} --batch --encrypt -r {{ gpgid }} ~/.password-store/test-yaml-pass" - -- name: Fetch a password with YAML subkey - set_fact: - readyamlpass: "{{ lookup('community.general.passwordstore', 'test-yaml-pass subkey=key') }}" - -- name: Read a yaml subkey - assert: - that: - - readyamlpass == 'multi\nline' - -- name: Create a non-YAML multiline file - copy: - dest: "~/.password-store/test-multiline-pass" - content: | - testpassword - random additional line - -- name: Read .gpg-id from .password-store - set_fact: - gpgid: "{{ lookup('file', '~/.password-store/.gpg-id') }}" - -- name: Encrypt the file using the gpg key - command: "{{ gpg2_bin }} --batch --encrypt -r {{ gpgid }} ~/.password-store/test-multiline-pass" - -- name: Fetch password from multiline file - set_fact: - readyamlpass: "{{ lookup('community.general.passwordstore', 'test-multiline-pass') }}" - -- name: Multiline pass only returns first line - assert: - that: - - readyamlpass == 'testpassword' - -- name: Fetch all from multiline file - set_fact: - readyamlpass: "{{ lookup('community.general.passwordstore', 'test-multiline-pass returnall=yes') }}" - -- name: Multiline pass returnall returns everything in the file - assert: - that: - - readyamlpass == 'testpassword\nrandom additional line' - -- name: Create a password in a folder - set_fact: - newpass: "{{ lookup('community.general.passwordstore', 'folder/test-pass length=8 create=yes') }}" - -- name: Fetch password from folder - set_fact: - readpass: "{{ lookup('community.general.passwordstore', 'folder/test-pass') }}" - -- name: Verify password from folder - assert: - that: - - readpass == newpass - -- name: Try to read folder as passname - set_fact: - newpass: "{{ lookup('community.general.passwordstore', 'folder') }}" - ignore_errors: true - register: eval_error - -- name: Make sure reading folder as passname failed - assert: - that: - - eval_error is failed - - '"passname folder not found" in eval_error.msg' +# these tests should apply to all backends +- name: Password tests + include_tasks: password_tests.yml + loop: + - pass + - gopass + loop_control: + loop_var: backend - name: Change passwordstore location explicitly set_fact: @@ -289,11 +171,13 @@ args: removes: "{{ passpath }}.testorig" -- name: Very basic gopass compatibility test +# This are in addition to the real gopass tests above +# and verify plugin logic +- name: gopass plugin logic tests vars: passwordstore_backend: "gopass" block: - - name: check if gopass executable exists + - name: Check if gopass executable exists stat: path: "{{ gopasspath }}" register: gopass_check @@ -322,11 +206,11 @@ dest: "{{ gopasspath }}" mode: '0755' - - name: Try to read folder as passname using gopass + - name: Try to read folder as passname using gopass mock set_fact: newpass: "{{ lookup('community.general.passwordstore', 'folder') }}" - - name: Verify password received from gopass + - name: Verify password received from gopass mock assert: that: - newpass == "gopass_ok" diff --git a/tests/integration/targets/lookup_passwordstore/vars/Alpine.yml b/tests/integration/targets/lookup_passwordstore/vars/Alpine.yml index 3d1c4d45d5..05778f6938 100644 --- a/tests/integration/targets/lookup_passwordstore/vars/Alpine.yml +++ b/tests/integration/targets/lookup_passwordstore/vars/Alpine.yml @@ -1,2 +1,3 @@ passwordstore_packages: + - gopass - pass diff --git a/tests/integration/targets/lookup_passwordstore/vars/Archlinux.yml b/tests/integration/targets/lookup_passwordstore/vars/Archlinux.yml index 3d1c4d45d5..05778f6938 100644 --- a/tests/integration/targets/lookup_passwordstore/vars/Archlinux.yml +++ b/tests/integration/targets/lookup_passwordstore/vars/Archlinux.yml @@ -1,2 +1,3 @@ passwordstore_packages: + - gopass - pass diff --git a/tests/integration/targets/lookup_passwordstore/vars/Fedora.yml b/tests/integration/targets/lookup_passwordstore/vars/Fedora.yml index 3d1c4d45d5..05778f6938 100644 --- a/tests/integration/targets/lookup_passwordstore/vars/Fedora.yml +++ b/tests/integration/targets/lookup_passwordstore/vars/Fedora.yml @@ -1,2 +1,3 @@ passwordstore_packages: + - gopass - pass diff --git a/tests/integration/targets/lookup_passwordstore/vars/FreeBSD.yml b/tests/integration/targets/lookup_passwordstore/vars/FreeBSD.yml index 39e51fbc2e..82e5f61849 100644 --- a/tests/integration/targets/lookup_passwordstore/vars/FreeBSD.yml +++ b/tests/integration/targets/lookup_passwordstore/vars/FreeBSD.yml @@ -1,3 +1,4 @@ passwordstore_packages: + - gopass - gnupg - password-store