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

allow specifying a 1Password account ID #7308

Merged
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
3 changes: 3 additions & 0 deletions changelogs/fragments/7308-onepassword-multi-acc.yml
Original file line number Diff line number Diff line change
@@ -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).
47 changes: 41 additions & 6 deletions plugins/lookup/onepassword.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
type: str
subdomain:
description: The 1Password subdomain to authenticate against.
account_id:
description: The account ID to target.
white-hat marked this conversation as resolved.
Show resolved Hide resolved
type: str
version_added: 7.5.0
white-hat marked this conversation as resolved.
Show resolved Hide resolved
username:
description: The username used to sign in.
secret_key:
Expand All @@ -55,6 +59,7 @@
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.
Expand Down Expand Up @@ -93,6 +98,12 @@
master_password=vault_master_password,
username='[email protected]',
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 = """
Expand All @@ -119,13 +130,23 @@
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
Expand Down Expand Up @@ -293,7 +314,9 @@ def _parse_field(self, data_json, field_name, section_title):

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])

Expand Down Expand Up @@ -326,6 +349,10 @@ def full_signin(self):

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)]

Expand Down Expand Up @@ -502,7 +529,9 @@ def assert_logged_in(self):
# 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])

Expand Down Expand Up @@ -533,6 +562,10 @@ def full_signin(self):

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)]

Expand All @@ -559,13 +592,14 @@ def signin(self):

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
Expand All @@ -578,7 +612,7 @@ def _get_cli_class(self):
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)

Expand Down Expand Up @@ -642,8 +676,9 @@ def run(self, terms, variables=None, **kwargs):
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 = []
Expand Down
8 changes: 7 additions & 1 deletion plugins/lookup/onepassword_raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
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:
Expand All @@ -52,6 +56,7 @@
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.
Expand Down Expand Up @@ -96,8 +101,9 @@ def run(self, terms, variables=None, **kwargs):
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 = []
Expand Down