summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/general/plugins/modules/keyring.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
commit975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch)
tree89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/community/general/plugins/modules/keyring.py
parentInitial commit. (diff)
downloadansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz
ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/community/general/plugins/modules/keyring.py')
-rw-r--r--ansible_collections/community/general/plugins/modules/keyring.py279
1 files changed, 279 insertions, 0 deletions
diff --git a/ansible_collections/community/general/plugins/modules/keyring.py b/ansible_collections/community/general/plugins/modules/keyring.py
new file mode 100644
index 000000000..ada22ed58
--- /dev/null
+++ b/ansible_collections/community/general/plugins/modules/keyring.py
@@ -0,0 +1,279 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2022, Alexander Hussey <ahussey@redhat.com>
+# 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
+"""
+Ansible Module - community.general.keyring
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: keyring
+version_added: 5.2.0
+author:
+ - Alexander Hussey (@ahussey-redhat)
+short_description: Set or delete a passphrase using the Operating System's native keyring
+description: >-
+ This module uses the L(keyring Python library, https://pypi.org/project/keyring/)
+ to set or delete passphrases for a given service and username from the OS' native keyring.
+requirements:
+ - keyring (Python library)
+ - gnome-keyring (application - required for headless Gnome keyring access)
+ - dbus-run-session (application - required for headless Gnome keyring access)
+extends_documentation_fragment:
+ - community.general.attributes
+attributes:
+ check_mode:
+ support: full
+ diff_mode:
+ support: none
+options:
+ service:
+ description: The name of the service.
+ required: true
+ type: str
+ username:
+ description: The user belonging to the service.
+ required: true
+ type: str
+ user_password:
+ description: The password to set.
+ required: false
+ type: str
+ aliases:
+ - password
+ keyring_password:
+ description: Password to unlock keyring.
+ required: true
+ type: str
+ state:
+ description: Whether the password should exist.
+ required: false
+ default: present
+ type: str
+ choices:
+ - present
+ - absent
+"""
+
+EXAMPLES = r"""
+- name: Set a password for test/test1
+ community.general.keyring:
+ service: test
+ username: test1
+ user_password: "{{ user_password }}"
+ keyring_password: "{{ keyring_password }}"
+
+- name: Delete the password for test/test1
+ community.general.keyring:
+ service: test
+ username: test1
+ user_password: "{{ user_password }}"
+ keyring_password: "{{ keyring_password }}"
+ state: absent
+"""
+
+try:
+ from shlex import quote
+except ImportError:
+ from pipes import quote
+import traceback
+
+from ansible.module_utils.basic import AnsibleModule, missing_required_lib
+
+try:
+ import keyring
+
+ HAS_KEYRING = True
+ KEYRING_IMP_ERR = None
+except ImportError:
+ HAS_KEYRING = False
+ KEYRING_IMP_ERR = traceback.format_exc()
+
+
+def del_passphrase(module):
+ """
+ Attempt to delete a passphrase in the keyring using the Python API and fallback to using a shell.
+ """
+ if module.check_mode:
+ return None
+ try:
+ keyring.delete_password(module.params["service"], module.params["username"])
+ return None
+ except keyring.errors.KeyringLocked as keyring_locked_err: # pylint: disable=unused-variable
+ delete_argument = (
+ 'echo "%s" | gnome-keyring-daemon --unlock\nkeyring del %s %s\n'
+ % (
+ quote(module.params["keyring_password"]),
+ quote(module.params["service"]),
+ quote(module.params["username"]),
+ )
+ )
+ dummy, dummy, stderr = module.run_command(
+ "dbus-run-session -- /bin/bash",
+ use_unsafe_shell=True,
+ data=delete_argument,
+ encoding=None,
+ )
+
+ if not stderr.decode("UTF-8"):
+ return None
+ return stderr.decode("UTF-8")
+
+
+def set_passphrase(module):
+ """
+ Attempt to set passphrase in the keyring using the Python API and fallback to using a shell.
+ """
+ if module.check_mode:
+ return None
+ try:
+ keyring.set_password(
+ module.params["service"],
+ module.params["username"],
+ module.params["user_password"],
+ )
+ return None
+ except keyring.errors.KeyringLocked as keyring_locked_err: # pylint: disable=unused-variable
+ set_argument = (
+ 'echo "%s" | gnome-keyring-daemon --unlock\nkeyring set %s %s\n%s\n'
+ % (
+ quote(module.params["keyring_password"]),
+ quote(module.params["service"]),
+ quote(module.params["username"]),
+ quote(module.params["user_password"]),
+ )
+ )
+ dummy, dummy, stderr = module.run_command(
+ "dbus-run-session -- /bin/bash",
+ use_unsafe_shell=True,
+ data=set_argument,
+ encoding=None,
+ )
+ if not stderr.decode("UTF-8"):
+ return None
+ return stderr.decode("UTF-8")
+
+
+def get_passphrase(module):
+ """
+ Attempt to retrieve passphrase from keyring using the Python API and fallback to using a shell.
+ """
+ try:
+ passphrase = keyring.get_password(
+ module.params["service"], module.params["username"]
+ )
+ return passphrase
+ except keyring.errors.KeyringLocked:
+ pass
+ except keyring.errors.InitError:
+ pass
+ except AttributeError:
+ pass
+ get_argument = 'echo "%s" | gnome-keyring-daemon --unlock\nkeyring get %s %s\n' % (
+ quote(module.params["keyring_password"]),
+ quote(module.params["service"]),
+ quote(module.params["username"]),
+ )
+ dummy, stdout, dummy = module.run_command(
+ "dbus-run-session -- /bin/bash",
+ use_unsafe_shell=True,
+ data=get_argument,
+ encoding=None,
+ )
+ try:
+ return stdout.decode("UTF-8").splitlines()[1] # Only return the line containing the password
+ except IndexError:
+ return None
+
+
+def run_module():
+ """
+ Attempts to retrieve a passphrase from a keyring.
+ """
+ result = dict(
+ changed=False,
+ msg="",
+ )
+
+ module_args = dict(
+ service=dict(type="str", required=True),
+ username=dict(type="str", required=True),
+ keyring_password=dict(type="str", required=True, no_log=True),
+ user_password=dict(
+ type="str", required=False, no_log=True, aliases=["password"]
+ ),
+ state=dict(
+ type="str", required=False, default="present", choices=["absent", "present"]
+ ),
+ )
+
+ module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)
+
+ if not HAS_KEYRING:
+ module.fail_json(msg=missing_required_lib("keyring"), exception=KEYRING_IMP_ERR)
+
+ passphrase = get_passphrase(module)
+ if module.params["state"] == "present":
+ if passphrase is not None:
+ if passphrase == module.params["user_password"]:
+ result["msg"] = "Passphrase already set for %s@%s" % (
+ module.params["service"],
+ module.params["username"],
+ )
+ if passphrase != module.params["user_password"]:
+ set_result = set_passphrase(module)
+ if set_result is None:
+ result["changed"] = True
+ result["msg"] = "Passphrase has been updated for %s@%s" % (
+ module.params["service"],
+ module.params["username"],
+ )
+ if set_result is not None:
+ module.fail_json(msg=set_result)
+ if passphrase is None:
+ set_result = set_passphrase(module)
+ if set_result is None:
+ result["changed"] = True
+ result["msg"] = "Passphrase has been updated for %s@%s" % (
+ module.params["service"],
+ module.params["username"],
+ )
+ if set_result is not None:
+ module.fail_json(msg=set_result)
+
+ if module.params["state"] == "absent":
+ if not passphrase:
+ result["result"] = "Passphrase already absent for %s@%s" % (
+ module.params["service"],
+ module.params["username"],
+ )
+ if passphrase:
+ del_result = del_passphrase(module)
+ if del_result is None:
+ result["changed"] = True
+ result["msg"] = "Passphrase has been removed for %s@%s" % (
+ module.params["service"],
+ module.params["username"],
+ )
+ if del_result is not None:
+ module.fail_json(msg=del_result)
+
+ module.exit_json(**result)
+
+
+def main():
+ """
+ main module loop
+ """
+ run_module()
+
+
+if __name__ == "__main__":
+ main()