Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

passwordstore lookup: allow to pass options as lookup options #5444

Merged
merged 2 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/5444-passwordstore-options.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- "passwordstore lookup plugin - allow options to be passed lookup options instead of being part of the term strings (https://github.com/ansible-collections/community.general/pull/5444)."
felixfontein marked this conversation as resolved.
Show resolved Hide resolved
74 changes: 40 additions & 34 deletions plugins/lookup/passwordstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,15 @@
_terms:
description: query key.
required: true
passwordstore:
description:
- Location of the password store.
- 'The value is decided by checking the following in order:'
- If set, this value is used.
- If C(directory) is set, that value will be used.
- If I(backend=pass), then C(~/.password-store) is used.
- If I(backend=gopass), then the C(path) field in C(~/.config/gopass/config.yml) is used,
falling back to C(~/.local/share/gopass/stores/root) if not defined.
directory:
description: The directory of the password store.
description:
- The directory of the password store.
- If I(backend=pass), the default is C(~/.password-store) is used.
- If I(backend=gopass), then the default is the C(path) field in C(~/.config/gopass/config.yml),
falling back to C(~/.local/share/gopass/stores/root) if C(path) is not defined in the gopass config.
type: path
vars:
- name: passwordstore
env:
- name: PASSWORD_STORE_DIR
create:
Expand All @@ -55,9 +53,11 @@
default: false
subkey:
description: Return a specific subkey of the password. When set to C(password), always returns the first line.
type: str
default: password
userpass:
description: Specify a password to save, instead of a generated one.
type: str
length:
description: The length of the generated password.
type: integer
Expand All @@ -67,7 +67,7 @@
type: bool
default: false
nosymbols:
description: use alphanumeric characters.
description: Use alphanumeric characters.
type: bool
default: false
missing:
Expand Down Expand Up @@ -129,14 +129,16 @@
- pass
- gopass
version_added: 5.2.0
notes:
- The lookup supports passing all options as lookup parameters since community.general 6.0.0.
'''
EXAMPLES = """
ansible.cfg: |
[passwordstore_lookup]
lock=readwrite
locktimeout=45s

playbook.yml: |
tasks.yml: |
---

# Debug is used for examples, BAD IDEA to show passwords on screen
Expand All @@ -146,45 +148,49 @@

- name: Basic lookup. Warns if example/test does not exist and returns empty string
ansible.builtin.debug:
msg: "{{ lookup('community.general.passwordstore', 'example/test missing=warn')}}"
msg: "{{ lookup('community.general.passwordstore', 'example/test', missing='warn')}}"

- name: Create pass with random 16 character password. If password exists just give the password
ansible.builtin.debug:
var: mypassword
vars:
mypassword: "{{ lookup('community.general.passwordstore', 'example/test create=true')}}"
mypassword: "{{ lookup('community.general.passwordstore', 'example/test', create=true)}}"

- name: Create pass with random 16 character password. If password exists just give the password
ansible.builtin.debug:
var: mypassword
vars:
mypassword: "{{ lookup('community.general.passwordstore', 'example/test missing=create')}}"
mypassword: "{{ lookup('community.general.passwordstore', 'example/test', missing='create')}}"

- name: Prints 'abc' if example/test does not exist, just give the password otherwise
ansible.builtin.debug:
var: mypassword
vars:
mypassword: "{{ lookup('community.general.passwordstore', 'example/test missing=empty') | default('abc', true) }}"
mypassword: >-
{{ lookup('community.general.passwordstore', 'example/test', missing='empty')
| default('abc', true) }}

- name: Different size password
ansible.builtin.debug:
msg: "{{ lookup('community.general.passwordstore', 'example/test create=true length=42')}}"
msg: "{{ lookup('community.general.passwordstore', 'example/test', create=true, length=42)}}"

- name: Create password and overwrite the password if it exists. As a bonus, this module includes the old password inside the pass file
- name: >-
Create password and overwrite the password if it exists.
As a bonus, this module includes the old password inside the pass file
ansible.builtin.debug:
msg: "{{ lookup('community.general.passwordstore', 'example/test create=true overwrite=true')}}"
msg: "{{ lookup('community.general.passwordstore', 'example/test', create=true, overwrite=true)}}"

- name: Create an alphanumeric password
ansible.builtin.debug:
msg: "{{ lookup('community.general.passwordstore', 'example/test create=true nosymbols=true') }}"
msg: "{{ lookup('community.general.passwordstore', 'example/test', create=true, nosymbols=true) }}"

- name: Return the value for user in the KV pair user, username
ansible.builtin.debug:
msg: "{{ lookup('community.general.passwordstore', 'example/test subkey=user')}}"
msg: "{{ lookup('community.general.passwordstore', 'example/test', subkey='user')}}"

- name: Return the entire password file content
ansible.builtin.set_fact:
passfilecontent: "{{ lookup('community.general.passwordstore', 'example/test returnall=true')}}"
passfilecontent: "{{ lookup('community.general.passwordstore', 'example/test', returnall=true)}}"
"""

RETURN = """
Expand Down Expand Up @@ -320,7 +326,7 @@ def parse_params(self, term):
raise AnsibleError('Passwordstore directory \'{0}\' does not exist'.format(self.paramvals['directory']))

# Set PASSWORD_STORE_UMASK if umask is set
if 'umask' in self.paramvals:
if self.paramvals.get('umask') is not None:
if len(self.paramvals['umask']) != 3:
raise AnsibleError('Passwordstore umask must have a length of 3.')
elif int(self.paramvals['umask'][0]) > 3:
Expand Down Expand Up @@ -435,8 +441,7 @@ def setup(self, variables):
unit_to_seconds = {"s": 1, "m": 60, "h": 3600}
self.lock_timeout = int(timeout[:-1]) * unit_to_seconds[timeout[-1]]

directory = variables.get('passwordstore', os.environ.get('PASSWORD_STORE_DIR', None))

directory = self.get_option('directory')
if directory is None:
if self.backend == 'gopass':
try:
Expand All @@ -448,16 +453,17 @@ def setup(self, variables):
directory = os.path.expanduser('~/.password-store')

self.paramvals = {
'subkey': 'password',
'subkey': self.get_option('subkey'),
'directory': directory,
'create': False,
'returnall': False,
'overwrite': False,
'nosymbols': False,
'userpass': '',
'length': 16,
'backup': False,
'missing': 'error',
'create': self.get_option('create'),
'returnall': self.get_option('returnall'),
'overwrite': self.get_option('overwrite'),
'nosymbols': self.get_option('nosymbols'),
'userpass': self.get_option('userpass') or '',
'length': self.get_option('length'),
'backup': self.get_option('backup'),
'missing': self.get_option('missing'),
'umask': self.get_option('umask'),
}

def run(self, terms, variables, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

- name: Create a password ({{ backend }})
set_fact:
newpass: "{{ lookup('community.general.passwordstore', 'test-pass length=8 create=yes', backend=backend) }}"
newpass: "{{ lookup('community.general.passwordstore', 'test-pass', length=8, create=true, backend=backend) }}"

- name: Fetch password from an existing file ({{ backend }})
set_fact:
Expand All @@ -18,7 +18,7 @@

- name: Create a password with equal sign ({{ backend }})
set_fact:
newpass: "{{ lookup('community.general.passwordstore', 'test-pass-equal userpass=SimpleSample= create=yes', backend=backend) }}"
newpass: "{{ lookup('community.general.passwordstore', 'test-pass-equal userpass=SimpleSample= create=true', backend=backend) }}"

- name: Fetch a password with equal sign ({{ backend }})
set_fact:
Expand All @@ -31,7 +31,7 @@

- name: Create a password using missing=create ({{ backend }})
set_fact:
newpass: "{{ lookup('community.general.passwordstore', 'test-missing-create missing=create length=8', backend=backend) }}"
newpass: "{{ lookup('community.general.passwordstore', 'test-missing-create', missing='create', length=8, backend=backend) }}"

- name: Fetch password from an existing file ({{ backend }})
set_fact:
Expand All @@ -44,7 +44,7 @@

- name: Fetch password from existing file using missing=empty ({{ backend }})
set_fact:
readpass: "{{ lookup('community.general.passwordstore', 'test-missing-create missing=empty', backend=backend) }}"
readpass: "{{ lookup('community.general.passwordstore', 'test-missing-create', missing='empty', backend=backend) }}"

- name: Verify password ({{ backend }})
assert:
Expand All @@ -53,7 +53,7 @@

- 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) }}"
readpass: "{{ query('community.general.passwordstore', 'test-missing-pass', missing='empty', backend=backend) }}"

- name: Verify password ({{ backend }})
assert:
Expand All @@ -71,7 +71,7 @@
- name: Fetch a password with YAML subkey ({{ backend }})
set_fact:
readyamlpass: "{{ lookup('community.general.passwordstore', 'test-yaml-pass subkey=key', backend=backend) }}"
readyamlpass: "{{ lookup('community.general.passwordstore', 'test-yaml-pass', subkey='key', backend=backend) }}"

- name: Read a yaml subkey ({{ backend }})
assert:
Expand All @@ -96,7 +96,7 @@

- name: Fetch all from multiline file ({{ backend }})
set_fact:
readyamlpass: "{{ lookup('community.general.passwordstore', 'test-multiline-pass returnall=yes', backend=backend) }}"
readyamlpass: "{{ lookup('community.general.passwordstore', 'test-multiline-pass', returnall='yes', backend=backend) }}"

- name: Multiline pass returnall returns everything in the file ({{ backend }})
assert:
Expand All @@ -105,7 +105,7 @@

- name: Create a password in a folder ({{ backend }})
set_fact:
newpass: "{{ lookup('community.general.passwordstore', 'folder/test-pass length=8 create=yes', backend=backend) }}"
newpass: "{{ lookup('community.general.passwordstore', 'folder/test-pass', length=8, create=true, backend=backend) }}"

- name: Fetch password from folder ({{ backend }})
set_fact:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,9 @@
that:
- eval_error is failed
- >-
"Passwordstore directory 'somenonexistentplace' does not exist" in eval_error.msg
"Passwordstore directory '" in eval_error.msg
- >-
"/somenonexistentplace' does not exist" in eval_error.msg
- name: Test pass compatibility shim detection
block:
Expand Down