diff --git a/README.md b/README.md index 5acaf9d..971ed4e 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Use `ansible-vault encrypt_string` to encrypt it and use it like below ansible_user : "{{ lookup('viczem.keepass.keepass', 'path/to/entry', 'username') }}" ansible_become_pass : "{{ lookup('viczem.keepass.keepass', 'path/to/entry', 'password') }}" - ansible_custom_field : "{{ lookup('viczem.keepass.keepass', 'path/to/entry', 'custom_properties', 'a_custom_property_name') }}" + custom_field : "{{ lookup('viczem.keepass.keepass', 'path/to/entry', 'custom_properties', 'a_custom_property_name') }}" + attachment : "{{ lookup('viczem.keepass.keepass', 'path/to/entry', 'attachments', 'a_file_name') }}" More examples see in [/doc/examples](/doc/examples). diff --git a/doc/examples/example-playbook.yml b/doc/examples/example-playbook.yml index 1af582f..93fc48a 100644 --- a/doc/examples/example-playbook.yml +++ b/doc/examples/example-playbook.yml @@ -10,6 +10,7 @@ slash_login: "{{ lookup('viczem.keepass.keepass', 'slash\\/group/slash\\/title', 'username') }}" slash_url: "{{ lookup('viczem.keepass.keepass', 'slash\\/group/slash\\/title', 'url') }}" pork_custom_property: "{{ lookup('viczem.keepass.keepass', 'example/pork', 'custom_properties', 'pork_custom_property')}}" + attachment: "{{ lookup('viczem.keepass.keepass', 'example/pork', 'attachments', 'test.txt')}}" tasks: @@ -19,6 +20,9 @@ - debug: msg: "fetch entry: '/examples/ham'; username: '{{ ham_login }}'; password: '{{ ham_password }}'" + - debug: + msg: "fetch entry: '/examples/port'; attachments: 'text.txt' - '{{ attachment }}'" + - name: pause to emulate long time operation (greater than keepass_ttl) pause: seconds: 5 diff --git a/galaxy.yml b/galaxy.yml index 28254b0..cd8af3e 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -8,7 +8,7 @@ namespace: viczem name: keepass # The version of the collection. Must be compatible with semantic versioning -version: 0.5.1 +version: 0.6.0 # The path to the Markdown (.md) readme file. This path is relative to the root of the collection readme: README.md diff --git a/plugins/lookup/keepass.py b/plugins/lookup/keepass.py index b496801..3609dbd 100644 --- a/plugins/lookup/keepass.py +++ b/plugins/lookup/keepass.py @@ -21,7 +21,7 @@ DOCUMENTATION = """ lookup: keepass author: Victor Zemtsov - version_added: '0.5.1' + version_added: '0.6.0' short_description: Fetching data from KeePass file description: - This lookup returns a value of a property of a KeePass entry @@ -39,6 +39,7 @@ - "{{ lookup('keepass', 'path/to/entry', 'username') }}" - "{{ lookup('keepass', 'path/to/entry', 'password') }}" - "{{ lookup('keepass', 'path/to/entry', 'custom_properties', 'my_prop_name') }}" + - "{{ lookup('keepass', 'path/to/entry', 'attachments', 'my_file_name') }}" """ display = Display() @@ -186,6 +187,7 @@ def _keepass_socket(kdbx, kdbx_key, sock_path, ttl=60, kdbx_password=None): Socket messages have multiline format. First line is a command for both messages are request and response """ + tmp_files = [] try: os.umask(0o177) with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s: @@ -302,6 +304,40 @@ def _keepass_socket(kdbx, kdbx_key, sock_path, ttl=60, kdbx_password=None): ) ) break + if prop == "attachments": + if arg_len == 2: + conn.send( + _resp( + "fetch", + 1, + "attachment key is not set for '%s'" % arg[0], + ) + ) + break + + prop_key = arg[2] + attachment = None + for _ in entry.attachments: + if _.filename == prop_key: + attachment = _ + break + if attachment is None: + conn.send( + _resp( + "fetch", + 1, + "attachment '%s' is not found " + "for '%s'" % (prop_key, path), + ) + ) + break + + tmp_file = tempfile.mkstemp(f".{attachment.filename}")[1] + with open(tmp_file, "wb") as f: + f.write(attachment.data) + tmp_files.append(tmp_file) + conn.send(_resp("fetch", 0, tmp_file)) + break if not hasattr(entry, prop): conn.send( @@ -327,6 +363,9 @@ def _keepass_socket(kdbx, kdbx_key, sock_path, ttl=60, kdbx_password=None): except KeyboardInterrupt: pass finally: + for tmp_file in tmp_files: + if os.path.exists(tmp_file): + os.remove(tmp_file) if os.path.exists(sock_path): os.remove(sock_path)