[PR #7116/f8652571 backport][stable-8] Support 1Password Connect (#5588) (#7536)

Support 1Password Connect (#5588) (#7116)

* Support 1Password Connect (#5588)

- Support 1Password Connect with the opv2 client

* Follow pep8, be less explicit

* Update changelog to include PR

* 1Password Connect host and token are now also parameters

* Get argument values from the environment or lookup arguments

* Move imports

* Force using Connect token and host at the same time

* Update unit tests

* Update documentation

* Additional tests

(cherry picked from commit f8652571f7)

Co-authored-by: Xeryus Stokkel <XeryusTC@users.noreply.github.com>
This commit is contained in:
patchback[bot] 2023-11-16 21:02:54 +01:00 committed by GitHub
commit a76537b24f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 104 additions and 6 deletions

View file

@ -52,6 +52,18 @@ DOCUMENTATION = '''
- Only works with 1Password CLI version 2 or later.
type: str
version_added: 7.1.0
connect_host:
description: The host for 1Password Connect. Must be used in combination with O(connect_token).
type: str
env:
- name: OP_CONNECT_HOST
version_added: 8.1.0
connect_token:
description: The token for 1Password Connect. Must be used in combination with O(connect_host).
type: str
env:
- name: OP_CONNECT_TOKEN
version_added: 8.1.0
vault:
description: Vault containing the item to retrieve (case-insensitive). If absent will search all vaults.
notes:
@ -119,7 +131,7 @@ import json
import subprocess
from ansible.plugins.lookup import LookupBase
from ansible.errors import AnsibleLookupError
from ansible.errors import AnsibleLookupError, AnsibleOptionsError
from ansible.module_utils.common.process import get_bin_path
from ansible.module_utils.common.text.converters import to_bytes, to_text
from ansible.module_utils.six import with_metaclass
@ -139,6 +151,8 @@ class OnePassCLIBase(with_metaclass(abc.ABCMeta, object)):
master_password=None,
service_account_token=None,
account_id=None,
connect_host=None,
connect_token=None,
):
self.subdomain = subdomain
self.domain = domain
@ -147,6 +161,8 @@ class OnePassCLIBase(with_metaclass(abc.ABCMeta, object)):
self.secret_key = secret_key
self.service_account_token = service_account_token
self.account_id = account_id
self.connect_host = connect_host
self.connect_token = connect_token
self._path = None
self._version = None
@ -325,6 +341,10 @@ class OnePassCLIv1(OnePassCLIBase):
return not bool(rc)
def full_signin(self):
if self.connect_host or self.connect_token:
raise AnsibleLookupError(
"1Password Connect is not available with 1Password CLI version 1. Please use version 2 or later.")
if self.service_account_token:
raise AnsibleLookupError(
"1Password CLI version 1 does not support Service Accounts. Please use version 2 or later.")
@ -510,6 +530,9 @@ class OnePassCLIv2(OnePassCLIBase):
return ""
def assert_logged_in(self):
if self.connect_host and self.connect_token:
return True
if self.service_account_token:
args = ["whoami"]
environment_update = {"OP_SERVICE_ACCOUNT_TOKEN": self.service_account_token}
@ -569,6 +592,15 @@ class OnePassCLIv2(OnePassCLIBase):
if vault is not None:
args += ["--vault={0}".format(vault)]
if self.connect_host and self.connect_token:
if vault is None:
raise AnsibleLookupError("'vault' is required with 1Password Connect")
environment_update = {
"OP_CONNECT_HOST": self.connect_host,
"OP_CONNECT_TOKEN": self.connect_token,
}
return self._run(args, environment_update=environment_update)
if self.service_account_token:
if vault is None:
raise AnsibleLookupError("'vault' is required with 'service_account_token'")
@ -592,7 +624,7 @@ 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, account_id=None):
service_account_token=None, account_id=None, connect_host=None, connect_token=None):
self.subdomain = subdomain
self.domain = domain
self.username = username
@ -600,6 +632,8 @@ class OnePass(object):
self.master_password = master_password
self.service_account_token = service_account_token
self.account_id = account_id
self.connect_host = connect_host
self.connect_token = connect_token
self.logged_in = False
self.token = None
@ -612,7 +646,8 @@ 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, self.account_id)
return cls(self.subdomain, self.domain, self.username, self.secret_key, self.master_password, self.service_account_token,
self.account_id, self.connect_host, self.connect_token)
except TypeError as e:
raise AnsibleLookupError(e)
@ -677,8 +712,13 @@ class LookupModule(LookupBase):
master_password = self.get_option("master_password")
service_account_token = self.get_option("service_account_token")
account_id = self.get_option("account_id")
connect_host = self.get_option("connect_host")
connect_token = self.get_option("connect_token")
op = OnePass(subdomain, domain, username, secret_key, master_password, service_account_token, account_id)
if (connect_host or connect_token) and None in (connect_host, connect_token):
raise AnsibleOptionsError("connect_host and connect_token are required together")
op = OnePass(subdomain, domain, username, secret_key, master_password, service_account_token, account_id, connect_host, connect_token)
op.assert_logged_in()
values = []

View file

@ -49,6 +49,18 @@ DOCUMENTATION = '''
- Only works with 1Password CLI version 2 or later.
type: string
version_added: 7.1.0
connect_host:
description: The host for 1Password Connect. Must be used in combination with O(connect_token).
type: str
env:
- name: OP_CONNECT_HOST
version_added: 8.1.0
connect_token:
description: The token for 1Password Connect. Must be used in combination with O(connect_host).
type: str
env:
- name: OP_CONNECT_TOKEN
version_added: 8.1.0
vault:
description: Vault containing the item to retrieve (case-insensitive). If absent will search all vaults.
notes:
@ -86,6 +98,7 @@ RETURN = """
import json
from ansible_collections.community.general.plugins.lookup.onepassword import OnePass
from ansible.errors import AnsibleOptionsError
from ansible.plugins.lookup import LookupBase
@ -102,8 +115,13 @@ class LookupModule(LookupBase):
master_password = self.get_option("master_password")
service_account_token = self.get_option("service_account_token")
account_id = self.get_option("account_id")
connect_host = self.get_option("connect_host")
connect_token = self.get_option("connect_token")
op = OnePass(subdomain, domain, username, secret_key, master_password, service_account_token, account_id)
if (connect_host or connect_token) and None in (connect_host, connect_token):
raise AnsibleOptionsError("connect_host and connect_token are required together")
op = OnePass(subdomain, domain, username, secret_key, master_password, service_account_token, account_id, connect_host, connect_token)
op.assert_logged_in()
values = []