diff --git a/changelogs/fragments/7308-onepassword-multi-acc.yml b/changelogs/fragments/7308-onepassword-multi-acc.yml new file mode 100644 index 0000000000..47676cddfa --- /dev/null +++ b/changelogs/fragments/7308-onepassword-multi-acc.yml @@ -0,0 +1,3 @@ +minor_changes: + - onepassword lookup plugin - introduce ``account_id`` option which allows specifying which account to use (https://github.com/ansible-collections/community.general/pull/7308). + - onepassword_raw lookup plugin - introduce ``account_id`` option which allows specifying which account to use (https://github.com/ansible-collections/community.general/pull/7308). diff --git a/plugins/lookup/onepassword.py b/plugins/lookup/onepassword.py index 624d19e48a..2bf1992374 100644 --- a/plugins/lookup/onepassword.py +++ b/plugins/lookup/onepassword.py @@ -38,6 +38,10 @@ DOCUMENTATION = ''' type: str subdomain: description: The 1Password subdomain to authenticate against. + account_id: + description: The account ID to target. + type: str + version_added: 7.5.0 username: description: The username used to sign in. secret_key: @@ -55,6 +59,7 @@ DOCUMENTATION = ''' performed an initial sign in (meaning C(~/.op/config), C(~/.config/op/config) or C(~/.config/.op/config) exists), then only the C(master_password) is required. You may optionally specify O(subdomain) in this scenario, otherwise the last used subdomain will be used by C(op). - This lookup can perform an initial login by providing O(subdomain), O(username), O(secret_key), and O(master_password). + - Can target a specific account by providing the O(account_id). - Due to the B(very) sensitive nature of these credentials, it is B(highly) recommended that you only pass in the minimal credentials needed at any given time. Also, store these credentials in an Ansible Vault using a key that is equal to or greater in strength to the 1Password master password. @@ -93,6 +98,12 @@ EXAMPLES = """ master_password=vault_master_password, username='tweety@acme.com', secret_key=vault_secret_key) + +- name: Retrieve password from specific account + ansible.builtin.debug: + var: lookup('community.general.onepassword', + 'HAL 9000', + account_id='abc123') """ RETURN = """ @@ -119,13 +130,23 @@ from ansible_collections.community.general.plugins.module_utils.onepassword impo class OnePassCLIBase(with_metaclass(abc.ABCMeta, object)): bin = "op" - def __init__(self, subdomain=None, domain="1password.com", username=None, secret_key=None, master_password=None, service_account_token=None): + def __init__( + self, + subdomain=None, + domain="1password.com", + username=None, + secret_key=None, + master_password=None, + service_account_token=None, + account_id=None, + ): self.subdomain = subdomain self.domain = domain self.username = username self.master_password = master_password self.secret_key = secret_key self.service_account_token = service_account_token + self.account_id = account_id self._path = None self._version = None @@ -293,7 +314,9 @@ class OnePassCLIv1(OnePassCLIBase): def assert_logged_in(self): args = ["get", "account"] - if self.subdomain: + if self.account_id: + args.extend(["--account", self.account_id]) + elif self.subdomain: account = "{subdomain}.{domain}".format(subdomain=self.subdomain, domain=self.domain) args.extend(["--account", account]) @@ -326,6 +349,10 @@ class OnePassCLIv1(OnePassCLIBase): def get_raw(self, item_id, vault=None, token=None): args = ["get", "item", item_id] + + if self.account_id: + args.extend(["--account", self.account_id]) + if vault is not None: args += ["--vault={0}".format(vault)] @@ -502,7 +529,9 @@ class OnePassCLIv2(OnePassCLIBase): # an interactive prompt. Only run 'op account get' after first listing accounts to see # if there are any previously configured accounts. args = ["account", "get"] - if self.subdomain: + if self.account_id: + args.extend(["--account", self.account_id]) + elif self.subdomain: account = "{subdomain}.{domain}".format(subdomain=self.subdomain, domain=self.domain) args.extend(["--account", account]) @@ -533,6 +562,10 @@ class OnePassCLIv2(OnePassCLIBase): def get_raw(self, item_id, vault=None, token=None): args = ["item", "get", item_id, "--format", "json"] + + if self.account_id: + args.extend(["--account", self.account_id]) + if vault is not None: args += ["--vault={0}".format(vault)] @@ -559,13 +592,14 @@ class OnePassCLIv2(OnePassCLIBase): class OnePass(object): def __init__(self, subdomain=None, domain="1password.com", username=None, secret_key=None, master_password=None, - service_account_token=None): + service_account_token=None, account_id=None): self.subdomain = subdomain self.domain = domain self.username = username self.secret_key = secret_key self.master_password = master_password self.service_account_token = service_account_token + self.account_id = account_id self.logged_in = False self.token = None @@ -578,7 +612,7 @@ class OnePass(object): for cls in OnePassCLIBase.__subclasses__(): if cls.supports_version == version.split(".")[0]: try: - return cls(self.subdomain, self.domain, self.username, self.secret_key, self.master_password, self.service_account_token) + return cls(self.subdomain, self.domain, self.username, self.secret_key, self.master_password, self.service_account_token, self.account_id) except TypeError as e: raise AnsibleLookupError(e) @@ -642,8 +676,9 @@ class LookupModule(LookupBase): secret_key = self.get_option("secret_key") master_password = self.get_option("master_password") service_account_token = self.get_option("service_account_token") + account_id = self.get_option("account_id") - op = OnePass(subdomain, domain, username, secret_key, master_password, service_account_token) + op = OnePass(subdomain, domain, username, secret_key, master_password, service_account_token, account_id) op.assert_logged_in() values = [] diff --git a/plugins/lookup/onepassword_raw.py b/plugins/lookup/onepassword_raw.py index 1257cb80c4..2ce5095400 100644 --- a/plugins/lookup/onepassword_raw.py +++ b/plugins/lookup/onepassword_raw.py @@ -35,6 +35,10 @@ DOCUMENTATION = ''' version_added: 6.0.0 default: '1password.com' type: str + account_id: + description: The account ID to target. + type: str + version_added: 7.5.0 username: description: The username used to sign in. secret_key: @@ -52,6 +56,7 @@ DOCUMENTATION = ''' performed an initial sign in (meaning C(~/.op/config exists)), then only the O(master_password) is required. You may optionally specify O(subdomain) in this scenario, otherwise the last used subdomain will be used by C(op). - This lookup can perform an initial login by providing O(subdomain), O(username), O(secret_key), and O(master_password). + - Can target a specific account by providing the O(account_id). - Due to the B(very) sensitive nature of these credentials, it is B(highly) recommended that you only pass in the minimal credentials needed at any given time. Also, store these credentials in an Ansible Vault using a key that is equal to or greater in strength to the 1Password master password. @@ -96,8 +101,9 @@ class LookupModule(LookupBase): secret_key = self.get_option("secret_key") master_password = self.get_option("master_password") service_account_token = self.get_option("service_account_token") + account_id = self.get_option("account_id") - op = OnePass(subdomain, domain, username, secret_key, master_password, service_account_token) + op = OnePass(subdomain, domain, username, secret_key, master_password, service_account_token, account_id) op.assert_logged_in() values = []