|
|
|
|
@ -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 = []
|
|
|
|
|
|