From 41214d4426a993cd56188d3ef996b4c88aeb0b0e Mon Sep 17 00:00:00 2001 From: Ryan Conway Date: Fri, 18 Jan 2019 20:49:51 +0000 Subject: [PATCH] Merge pull request #50160 from Rylon/backport/2.7/47213 Fix an issue retrieving some types of 1Password items. (#47213) --- ...onepassword_facts_fix_password_lookup.yaml | 2 + .../modules/identity/onepassword_facts.py | 57 ++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 changelogs/fragments/47213-onepassword_facts_fix_password_lookup.yaml diff --git a/changelogs/fragments/47213-onepassword_facts_fix_password_lookup.yaml b/changelogs/fragments/47213-onepassword_facts_fix_password_lookup.yaml new file mode 100644 index 00000000000..f887ea13c53 --- /dev/null +++ b/changelogs/fragments/47213-onepassword_facts_fix_password_lookup.yaml @@ -0,0 +1,2 @@ +bugfixes: + - onepassword_facts - Fix an issue looking up some 1Password items which have a 'password' attribute alongside the 'fields' attribute, not inside it. diff --git a/lib/ansible/modules/identity/onepassword_facts.py b/lib/ansible/modules/identity/onepassword_facts.py index 0b285d69891..85333bd0d7a 100644 --- a/lib/ansible/modules/identity/onepassword_facts.py +++ b/lib/ansible/modules/identity/onepassword_facts.py @@ -138,9 +138,18 @@ import os import re from subprocess import Popen, PIPE +from ansible.module_utils._text import to_bytes, to_native from ansible.module_utils.basic import AnsibleModule +class AnsibleModuleError(Exception): + def __init__(self, results): + self.results = results + + def __repr__(self): + return self.results + + class OnePasswordFacts(object): def __init__(self): @@ -149,7 +158,53 @@ class OnePasswordFacts(object): self.token = {} terms = module.params.get('search_terms') - self.terms = self.parse_search_terms(module.params.get('search_terms')) + self.terms = self.parse_search_terms(terms) + + def _run(self, args, expected_rc=0, command_input=None, ignore_errors=False): + command = [self.cli_path] + args + p = Popen(command, stdout=PIPE, stderr=PIPE, stdin=PIPE) + out, err = p.communicate(input=command_input) + rc = p.wait() + if not ignore_errors and rc != expected_rc: + raise AnsibleModuleError(to_native(err)) + return rc, out, err + + def _parse_field(self, data_json, item_id, field_name, section_title=None): + data = json.loads(data_json) + + if ('documentAttributes' in data['details']): + # This is actually a document, let's fetch the document data instead! + document = self._run(["get", "document", data['overview']['title']]) + return {'document': document[0].strip()} + + else: + # This is not a document, let's try to find the requested field + + # Some types of 1Password items have a 'password' field directly alongside the 'fields' attribute, + # not inside it, so we need to check there first. + if (field_name in data['details']): + return {field_name: data['details'][field_name]} + + # Otherwise we continue looking inside the 'fields' attribute for the specified field. + else: + if section_title is None: + for field_data in data['details'].get('fields', []): + if field_data.get('name').lower() == field_name.lower(): + return {field_name: field_data.get('value', '')} + + # Not found it yet, so now lets see if there are any sections defined + # and search through those for the field. If a section was given, we skip + # any non-matching sections, otherwise we search them all until we find the field. + for section_data in data['details'].get('sections', []): + if section_title is not None and section_title.lower() != section_data['title'].lower(): + continue + for field_data in section_data.get('fields', []): + if field_data.get('t').lower() == field_name.lower(): + return {field_name: field_data.get('v', '')} + + # We will get here if the field could not be found in any section and the item wasn't a document to be downloaded. + optional_section_title = '' if section_title is None else " in the section '%s'" % section_title + module.fail_json(msg="Unable to find an item in 1Password named '%s' with the field '%s'%s." % (item_id, field_name, optional_section_title)) def parse_search_terms(self, terms): processed_terms = []