mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-07 22:02:50 +00:00
Reformat everything.
This commit is contained in:
@@ -84,7 +84,6 @@ display = Display()
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
not_exist = self.get_option("not_exist")
|
||||
|
||||
@@ -131,8 +131,7 @@ class BitwardenException(AnsibleError):
|
||||
|
||||
|
||||
class Bitwarden:
|
||||
|
||||
def __init__(self, path='bw'):
|
||||
def __init__(self, path="bw"):
|
||||
self._cli_path = path
|
||||
self._session = None
|
||||
|
||||
@@ -150,54 +149,56 @@ class Bitwarden:
|
||||
|
||||
@property
|
||||
def unlocked(self):
|
||||
out, err = self._run(['status'], stdin="")
|
||||
out, err = self._run(["status"], stdin="")
|
||||
decoded = AnsibleJSONDecoder().raw_decode(out)[0]
|
||||
return decoded['status'] == 'unlocked'
|
||||
return decoded["status"] == "unlocked"
|
||||
|
||||
def _run(self, args, stdin=None, expected_rc=0):
|
||||
if self.session:
|
||||
args += ['--session', self.session]
|
||||
args += ["--session", self.session]
|
||||
|
||||
p = Popen([self.cli_path] + args, stdout=PIPE, stderr=PIPE, stdin=PIPE)
|
||||
out, err = p.communicate(to_bytes(stdin))
|
||||
rc = p.wait()
|
||||
if rc != expected_rc:
|
||||
if len(args) > 2 and args[0] == 'get' and args[1] == 'item' and b'Not found.' in err:
|
||||
return 'null', ''
|
||||
if len(args) > 2 and args[0] == "get" and args[1] == "item" and b"Not found." in err:
|
||||
return "null", ""
|
||||
raise BitwardenException(err)
|
||||
return to_text(out, errors='surrogate_or_strict'), to_text(err, errors='surrogate_or_strict')
|
||||
return to_text(out, errors="surrogate_or_strict"), to_text(err, errors="surrogate_or_strict")
|
||||
|
||||
def _get_matches(self, search_value, search_field, collection_id=None, organization_id=None):
|
||||
"""Return matching records whose search_field is equal to key.
|
||||
"""
|
||||
"""Return matching records whose search_field is equal to key."""
|
||||
|
||||
# Prepare set of params for Bitwarden CLI
|
||||
if search_field == 'id':
|
||||
params = ['get', 'item', search_value]
|
||||
if search_field == "id":
|
||||
params = ["get", "item", search_value]
|
||||
else:
|
||||
params = ['list', 'items']
|
||||
params = ["list", "items"]
|
||||
if search_value:
|
||||
params.extend(['--search', search_value])
|
||||
params.extend(["--search", search_value])
|
||||
|
||||
if collection_id:
|
||||
params.extend(['--collectionid', collection_id])
|
||||
params.extend(["--collectionid", collection_id])
|
||||
if organization_id:
|
||||
params.extend(['--organizationid', organization_id])
|
||||
params.extend(["--organizationid", organization_id])
|
||||
|
||||
out, err = self._run(params)
|
||||
|
||||
# This includes things that matched in different fields.
|
||||
initial_matches = AnsibleJSONDecoder().raw_decode(out)[0]
|
||||
|
||||
if search_field == 'id':
|
||||
if search_field == "id":
|
||||
if initial_matches is None:
|
||||
initial_matches = []
|
||||
else:
|
||||
initial_matches = [initial_matches]
|
||||
|
||||
# Filter to only include results from the right field, if a search is requested by value or field
|
||||
return [item for item in initial_matches
|
||||
if not search_value or not search_field or item.get(search_field) == search_value]
|
||||
return [
|
||||
item
|
||||
for item in initial_matches
|
||||
if not search_value or not search_field or item.get(search_field) == search_value
|
||||
]
|
||||
|
||||
def get_field(self, field, search_value, search_field="name", collection_id=None, organization_id=None):
|
||||
"""Return a list of the specified field for records whose search_field match search_value
|
||||
@@ -211,17 +212,17 @@ class Bitwarden:
|
||||
field_matches = []
|
||||
for match in matches:
|
||||
# if there are no custom fields, then `match` has no key 'fields'
|
||||
if 'fields' in match:
|
||||
if "fields" in match:
|
||||
custom_field_found = False
|
||||
for custom_field in match['fields']:
|
||||
if field == custom_field['name']:
|
||||
field_matches.append(custom_field['value'])
|
||||
for custom_field in match["fields"]:
|
||||
if field == custom_field["name"]:
|
||||
field_matches.append(custom_field["value"])
|
||||
custom_field_found = True
|
||||
break
|
||||
if custom_field_found:
|
||||
continue
|
||||
if 'login' in match and field in match['login']:
|
||||
field_matches.append(match['login'][field])
|
||||
if "login" in match and field in match["login"]:
|
||||
field_matches.append(match["login"][field])
|
||||
continue
|
||||
if field in match:
|
||||
field_matches.append(match[field])
|
||||
@@ -236,10 +237,10 @@ class Bitwarden:
|
||||
"""Return matching IDs of collections whose name is equal to collection_name."""
|
||||
|
||||
# Prepare set of params for Bitwarden CLI
|
||||
params = ['list', 'collections', '--search', collection_name]
|
||||
params = ["list", "collections", "--search", collection_name]
|
||||
|
||||
if organization_id:
|
||||
params.extend(['--organizationid', organization_id])
|
||||
params.extend(["--organizationid", organization_id])
|
||||
|
||||
out, err = self._run(params)
|
||||
|
||||
@@ -247,21 +248,19 @@ class Bitwarden:
|
||||
initial_matches = AnsibleJSONDecoder().raw_decode(out)[0] # type: ignore[operator]
|
||||
|
||||
# Filter to only return the ID of a collections with exactly matching name
|
||||
return [item['id'] for item in initial_matches
|
||||
if str(item.get('name')).lower() == collection_name.lower()]
|
||||
return [item["id"] for item in initial_matches if str(item.get("name")).lower() == collection_name.lower()]
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms=None, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
field = self.get_option('field')
|
||||
search_field = self.get_option('search')
|
||||
collection_id = self.get_option('collection_id')
|
||||
collection_name = self.get_option('collection_name')
|
||||
organization_id = self.get_option('organization_id')
|
||||
result_count = self.get_option('result_count')
|
||||
_bitwarden.session = self.get_option('bw_session')
|
||||
field = self.get_option("field")
|
||||
search_field = self.get_option("search")
|
||||
collection_id = self.get_option("collection_id")
|
||||
collection_name = self.get_option("collection_name")
|
||||
organization_id = self.get_option("organization_id")
|
||||
result_count = self.get_option("result_count")
|
||||
_bitwarden.session = self.get_option("bw_session")
|
||||
|
||||
if not _bitwarden.unlocked:
|
||||
raise AnsibleError("Bitwarden Vault locked. Run 'bw unlock'.")
|
||||
@@ -287,7 +286,8 @@ class LookupModule(LookupBase):
|
||||
for result in results:
|
||||
if result_count is not None and len(result) != result_count:
|
||||
raise BitwardenException(
|
||||
f"Number of results doesn't match result_count! ({len(result)} != {result_count})")
|
||||
f"Number of results doesn't match result_count! ({len(result)} != {result_count})"
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ class BitwardenSecretsManagerException(AnsibleLookupError):
|
||||
|
||||
|
||||
class BitwardenSecretsManager:
|
||||
def __init__(self, path='bws'):
|
||||
def __init__(self, path="bws"):
|
||||
self._cli_path = path
|
||||
self._max_retries = 3
|
||||
self._retry_delay = 1
|
||||
@@ -100,7 +100,7 @@ class BitwardenSecretsManager:
|
||||
raise BitwardenSecretsManagerException("Max retries exceeded. Unable to retrieve secret.")
|
||||
|
||||
if "Too many requests" in err:
|
||||
delay = self._retry_delay * (2 ** retries)
|
||||
delay = self._retry_delay * (2**retries)
|
||||
sleep(delay)
|
||||
return self._run_with_retry(args, stdin, retries + 1)
|
||||
else:
|
||||
@@ -112,36 +112,31 @@ class BitwardenSecretsManager:
|
||||
p = Popen([self.cli_path] + args, stdout=PIPE, stderr=PIPE, stdin=PIPE)
|
||||
out, err = p.communicate(stdin)
|
||||
rc = p.wait()
|
||||
return to_text(out, errors='surrogate_or_strict'), to_text(err, errors='surrogate_or_strict'), rc
|
||||
return to_text(out, errors="surrogate_or_strict"), to_text(err, errors="surrogate_or_strict"), rc
|
||||
|
||||
def get_bws_version(self):
|
||||
"""Get the version of the Bitwarden Secrets Manager CLI.
|
||||
"""
|
||||
out, err, rc = self._run(['--version'])
|
||||
"""Get the version of the Bitwarden Secrets Manager CLI."""
|
||||
out, err, rc = self._run(["--version"])
|
||||
if rc != 0:
|
||||
raise BitwardenSecretsManagerException(to_text(err))
|
||||
# strip the prefix and grab the last segment, the version number
|
||||
return out.split()[-1]
|
||||
|
||||
def get_secret(self, secret_id, bws_access_token):
|
||||
"""Get and return the secret with the given secret_id.
|
||||
"""
|
||||
"""Get and return the secret with the given secret_id."""
|
||||
|
||||
# Prepare set of params for Bitwarden Secrets Manager CLI
|
||||
# Color output was not always disabled correctly with the default 'auto' setting so explicitly disable it.
|
||||
params = [
|
||||
'--color', 'no',
|
||||
'--access-token', bws_access_token
|
||||
]
|
||||
params = ["--color", "no", "--access-token", bws_access_token]
|
||||
|
||||
# bws version 0.3.0 introduced a breaking change in the command line syntax:
|
||||
# pre-0.3.0: verb noun
|
||||
# 0.3.0 and later: noun verb
|
||||
bws_version = self.get_bws_version()
|
||||
if LooseVersion(bws_version) < LooseVersion('0.3.0'):
|
||||
params.extend(['get', 'secret', secret_id])
|
||||
if LooseVersion(bws_version) < LooseVersion("0.3.0"):
|
||||
params.extend(["get", "secret", secret_id])
|
||||
else:
|
||||
params.extend(['secret', 'get', secret_id])
|
||||
params.extend(["secret", "get", secret_id])
|
||||
|
||||
out, err, rc = self._run_with_retry(params)
|
||||
if rc != 0:
|
||||
@@ -153,7 +148,7 @@ class BitwardenSecretsManager:
|
||||
class LookupModule(LookupBase):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
bws_access_token = self.get_option('bws_access_token')
|
||||
bws_access_token = self.get_option("bws_access_token")
|
||||
|
||||
return [_bitwarden_secrets_manager.get_secret(term, bws_access_token) for term in terms]
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ from ansible.parsing.splitter import parse_kv
|
||||
|
||||
try:
|
||||
import chef
|
||||
|
||||
HAS_CHEF = True
|
||||
except ImportError as missing_module:
|
||||
HAS_CHEF = False
|
||||
@@ -56,8 +57,8 @@ class LookupModule(LookupBase):
|
||||
"""
|
||||
Chef data bag lookup module
|
||||
"""
|
||||
def __init__(self, loader=None, templar=None, **kwargs):
|
||||
|
||||
def __init__(self, loader=None, templar=None, **kwargs):
|
||||
super().__init__(loader, templar, **kwargs)
|
||||
|
||||
# setup vars for data bag name and data bag item
|
||||
@@ -77,18 +78,14 @@ class LookupModule(LookupBase):
|
||||
parsed = str(arg_raw)
|
||||
setattr(self, arg, parsed)
|
||||
except ValueError:
|
||||
raise AnsibleError(
|
||||
f"can't parse arg {arg}={arg_raw} as string"
|
||||
)
|
||||
raise AnsibleError(f"can't parse arg {arg}={arg_raw} as string")
|
||||
if args:
|
||||
raise AnsibleError(
|
||||
f"unrecognized arguments to with_sequence: {list(args.keys())!r}"
|
||||
)
|
||||
raise AnsibleError(f"unrecognized arguments to with_sequence: {list(args.keys())!r}")
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
# Ensure pychef has been loaded
|
||||
if not HAS_CHEF:
|
||||
raise AnsibleError('PyChef needed for lookup plugin, try `pip install pychef`')
|
||||
raise AnsibleError("PyChef needed for lookup plugin, try `pip install pychef`")
|
||||
|
||||
for term in terms:
|
||||
self.parse_kv_args(parse_kv(term))
|
||||
@@ -96,7 +93,7 @@ class LookupModule(LookupBase):
|
||||
api_object = chef.autoconfigure()
|
||||
|
||||
if not isinstance(api_object, chef.api.ChefAPI):
|
||||
raise AnsibleError('Unable to connect to Chef Server API.')
|
||||
raise AnsibleError("Unable to connect to Chef Server API.")
|
||||
|
||||
data_bag_object = chef.DataBag(self.name)
|
||||
|
||||
|
||||
@@ -66,35 +66,35 @@ from ansible.errors import AnsibleLookupError
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
|
||||
|
||||
FQCN_RE = re.compile(r'^[A-Za-z0-9_]+\.[A-Za-z0-9_]+$')
|
||||
FQCN_RE = re.compile(r"^[A-Za-z0-9_]+\.[A-Za-z0-9_]+$")
|
||||
|
||||
|
||||
def load_collection_meta_manifest(manifest_path):
|
||||
with open(manifest_path, 'rb') as f:
|
||||
with open(manifest_path, "rb") as f:
|
||||
meta = json.load(f)
|
||||
return {
|
||||
'version': meta['collection_info']['version'],
|
||||
"version": meta["collection_info"]["version"],
|
||||
}
|
||||
|
||||
|
||||
def load_collection_meta_galaxy(galaxy_path, no_version='*'):
|
||||
with open(galaxy_path, 'rb') as f:
|
||||
def load_collection_meta_galaxy(galaxy_path, no_version="*"):
|
||||
with open(galaxy_path, "rb") as f:
|
||||
meta = yaml.safe_load(f)
|
||||
return {
|
||||
'version': meta.get('version') or no_version,
|
||||
"version": meta.get("version") or no_version,
|
||||
}
|
||||
|
||||
|
||||
def load_collection_meta(collection_pkg, no_version='*'):
|
||||
def load_collection_meta(collection_pkg, no_version="*"):
|
||||
path = os.path.dirname(collection_pkg.__file__)
|
||||
|
||||
# Try to load MANIFEST.json
|
||||
manifest_path = os.path.join(path, 'MANIFEST.json')
|
||||
manifest_path = os.path.join(path, "MANIFEST.json")
|
||||
if os.path.exists(manifest_path):
|
||||
return load_collection_meta_manifest(manifest_path)
|
||||
|
||||
# Try to load galaxy.yml
|
||||
galaxy_path = os.path.join(path, 'galaxy.yml')
|
||||
galaxy_path = os.path.join(path, "galaxy.yml")
|
||||
if os.path.exists(galaxy_path):
|
||||
return load_collection_meta_galaxy(galaxy_path, no_version=no_version)
|
||||
|
||||
@@ -105,15 +105,15 @@ class LookupModule(LookupBase):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
result = []
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
not_found = self.get_option('result_not_found')
|
||||
no_version = self.get_option('result_no_version')
|
||||
not_found = self.get_option("result_not_found")
|
||||
no_version = self.get_option("result_no_version")
|
||||
|
||||
for term in terms:
|
||||
if not FQCN_RE.match(term):
|
||||
raise AnsibleLookupError(f'"{term}" is not a FQCN')
|
||||
|
||||
try:
|
||||
collection_pkg = import_module(f'ansible_collections.{term}')
|
||||
collection_pkg = import_module(f"ansible_collections.{term}")
|
||||
except ImportError:
|
||||
# Collection not found
|
||||
result.append(not_found)
|
||||
@@ -122,8 +122,8 @@ class LookupModule(LookupBase):
|
||||
try:
|
||||
data = load_collection_meta(collection_pkg, no_version=no_version)
|
||||
except Exception as exc:
|
||||
raise AnsibleLookupError(f'Error while loading metadata for {term}: {exc}')
|
||||
raise AnsibleLookupError(f"Error while loading metadata for {term}: {exc}")
|
||||
|
||||
result.append(data.get('version', no_version))
|
||||
result.append(data.get("version", no_version))
|
||||
|
||||
return result
|
||||
|
||||
@@ -125,20 +125,19 @@ except ImportError as e:
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
|
||||
if not HAS_CONSUL:
|
||||
raise AnsibleError(
|
||||
'py-consul is required for consul_kv lookup. see https://github.com/criteo/py-consul?tab=readme-ov-file#installation')
|
||||
"py-consul is required for consul_kv lookup. see https://github.com/criteo/py-consul?tab=readme-ov-file#installation"
|
||||
)
|
||||
|
||||
# get options
|
||||
self.set_options(direct=kwargs)
|
||||
|
||||
scheme = self.get_option('scheme')
|
||||
host = self.get_option('host')
|
||||
port = self.get_option('port')
|
||||
url = self.get_option('url')
|
||||
scheme = self.get_option("scheme")
|
||||
host = self.get_option("host")
|
||||
port = self.get_option("port")
|
||||
url = self.get_option("url")
|
||||
if url is not None:
|
||||
u = urlparse(url)
|
||||
if u.scheme:
|
||||
@@ -147,8 +146,8 @@ class LookupModule(LookupBase):
|
||||
if u.port is not None:
|
||||
port = u.port
|
||||
|
||||
validate_certs = self.get_option('validate_certs')
|
||||
client_cert = self.get_option('client_cert')
|
||||
validate_certs = self.get_option("validate_certs")
|
||||
client_cert = self.get_option("client_cert")
|
||||
|
||||
values = []
|
||||
try:
|
||||
@@ -156,40 +155,41 @@ class LookupModule(LookupBase):
|
||||
params = self.parse_params(term)
|
||||
consul_api = consul.Consul(host=host, port=port, scheme=scheme, verify=validate_certs, cert=client_cert)
|
||||
|
||||
results = consul_api.kv.get(params['key'],
|
||||
token=params['token'],
|
||||
index=params['index'],
|
||||
recurse=params['recurse'],
|
||||
dc=params['datacenter'])
|
||||
results = consul_api.kv.get(
|
||||
params["key"],
|
||||
token=params["token"],
|
||||
index=params["index"],
|
||||
recurse=params["recurse"],
|
||||
dc=params["datacenter"],
|
||||
)
|
||||
if results[1]:
|
||||
# responds with a single or list of result maps
|
||||
if isinstance(results[1], list):
|
||||
for r in results[1]:
|
||||
values.append(to_text(r['Value']))
|
||||
values.append(to_text(r["Value"]))
|
||||
else:
|
||||
values.append(to_text(results[1]['Value']))
|
||||
values.append(to_text(results[1]["Value"]))
|
||||
except Exception as e:
|
||||
raise AnsibleError(
|
||||
f"Error locating '{term}' in kv store. Error was {e}")
|
||||
raise AnsibleError(f"Error locating '{term}' in kv store. Error was {e}")
|
||||
|
||||
return values
|
||||
|
||||
def parse_params(self, term):
|
||||
params = term.split(' ')
|
||||
params = term.split(" ")
|
||||
|
||||
paramvals = {
|
||||
'key': params[0],
|
||||
'token': self.get_option('token'),
|
||||
'recurse': self.get_option('recurse'),
|
||||
'index': self.get_option('index'),
|
||||
'datacenter': self.get_option('datacenter')
|
||||
"key": params[0],
|
||||
"token": self.get_option("token"),
|
||||
"recurse": self.get_option("recurse"),
|
||||
"index": self.get_option("index"),
|
||||
"datacenter": self.get_option("datacenter"),
|
||||
}
|
||||
|
||||
# parameters specified?
|
||||
try:
|
||||
for param in params[1:]:
|
||||
if param and len(param) > 0:
|
||||
name, value = param.split('=')
|
||||
name, value = param.split("=")
|
||||
if name not in paramvals:
|
||||
raise AnsibleAssertionError(f"{name} not a valid consul lookup parameter")
|
||||
paramvals[name] = value
|
||||
|
||||
@@ -98,6 +98,7 @@ CREDSTASH_INSTALLED = False
|
||||
|
||||
try:
|
||||
import credstash
|
||||
|
||||
CREDSTASH_INSTALLED = True
|
||||
except ImportError:
|
||||
CREDSTASH_INSTALLED = False
|
||||
@@ -106,28 +107,38 @@ except ImportError:
|
||||
class LookupModule(LookupBase):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
if not CREDSTASH_INSTALLED:
|
||||
raise AnsibleError('The credstash lookup plugin requires credstash to be installed.')
|
||||
raise AnsibleError("The credstash lookup plugin requires credstash to be installed.")
|
||||
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
|
||||
version = self.get_option('version')
|
||||
region = self.get_option('region')
|
||||
table = self.get_option('table')
|
||||
profile_name = self.get_option('profile_name')
|
||||
aws_access_key_id = self.get_option('aws_access_key_id')
|
||||
aws_secret_access_key = self.get_option('aws_secret_access_key')
|
||||
aws_session_token = self.get_option('aws_session_token')
|
||||
version = self.get_option("version")
|
||||
region = self.get_option("region")
|
||||
table = self.get_option("table")
|
||||
profile_name = self.get_option("profile_name")
|
||||
aws_access_key_id = self.get_option("aws_access_key_id")
|
||||
aws_secret_access_key = self.get_option("aws_secret_access_key")
|
||||
aws_session_token = self.get_option("aws_session_token")
|
||||
|
||||
context = {
|
||||
k: v for k, v in kwargs.items()
|
||||
if k not in ('version', 'region', 'table', 'profile_name', 'aws_access_key_id', 'aws_secret_access_key', 'aws_session_token')
|
||||
k: v
|
||||
for k, v in kwargs.items()
|
||||
if k
|
||||
not in (
|
||||
"version",
|
||||
"region",
|
||||
"table",
|
||||
"profile_name",
|
||||
"aws_access_key_id",
|
||||
"aws_secret_access_key",
|
||||
"aws_session_token",
|
||||
)
|
||||
}
|
||||
|
||||
kwargs_pass = {
|
||||
'profile_name': profile_name,
|
||||
'aws_access_key_id': aws_access_key_id,
|
||||
'aws_secret_access_key': aws_secret_access_key,
|
||||
'aws_session_token': aws_session_token,
|
||||
"profile_name": profile_name,
|
||||
"aws_access_key_id": aws_access_key_id,
|
||||
"aws_secret_access_key": aws_secret_access_key,
|
||||
"aws_session_token": aws_session_token,
|
||||
}
|
||||
|
||||
ret = []
|
||||
@@ -135,8 +146,8 @@ class LookupModule(LookupBase):
|
||||
try:
|
||||
ret.append(credstash.getSecret(term, version, region, table, context=context, **kwargs_pass))
|
||||
except credstash.ItemNotFound:
|
||||
raise AnsibleError(f'Key {term} not found')
|
||||
raise AnsibleError(f"Key {term} not found")
|
||||
except Exception as e:
|
||||
raise AnsibleError(f'Encountered exception while fetching {term}: {e}')
|
||||
raise AnsibleError(f"Encountered exception while fetching {term}: {e}")
|
||||
|
||||
return ret
|
||||
|
||||
@@ -89,13 +89,11 @@ from ansible.utils.display import Display
|
||||
|
||||
display = Display()
|
||||
|
||||
CLIPASSWORDSDK_CMD = os.getenv('AIM_CLIPASSWORDSDK_CMD', '/opt/CARKaim/sdk/clipasswordsdk')
|
||||
CLIPASSWORDSDK_CMD = os.getenv("AIM_CLIPASSWORDSDK_CMD", "/opt/CARKaim/sdk/clipasswordsdk")
|
||||
|
||||
|
||||
class CyberarkPassword:
|
||||
|
||||
def __init__(self, appid=None, query=None, output=None, **kwargs):
|
||||
|
||||
self.appid = appid
|
||||
self.query = query
|
||||
self.output = output
|
||||
@@ -104,7 +102,7 @@ class CyberarkPassword:
|
||||
# FailRequestOnPasswordChange, Queryformat, Reason, etc.
|
||||
self.extra_parms = []
|
||||
for key, value in kwargs.items():
|
||||
self.extra_parms.append('-p')
|
||||
self.extra_parms.append("-p")
|
||||
self.extra_parms.append(f"{key}={value}")
|
||||
|
||||
if self.appid is None:
|
||||
@@ -123,17 +121,21 @@ class CyberarkPassword:
|
||||
self.b_delimiter = b"@#@" # Known delimiter to split output results
|
||||
|
||||
def get(self):
|
||||
|
||||
result_dict = {}
|
||||
|
||||
try:
|
||||
all_parms = [
|
||||
CLIPASSWORDSDK_CMD,
|
||||
'GetPassword',
|
||||
'-p', f'AppDescs.AppID={self.appid}',
|
||||
'-p', f'Query={self.query}',
|
||||
'-o', self.output,
|
||||
'-d', self.b_delimiter]
|
||||
"GetPassword",
|
||||
"-p",
|
||||
f"AppDescs.AppID={self.appid}",
|
||||
"-p",
|
||||
f"Query={self.query}",
|
||||
"-o",
|
||||
self.output,
|
||||
"-d",
|
||||
self.b_delimiter,
|
||||
]
|
||||
all_parms.extend(self.extra_parms)
|
||||
|
||||
b_credential = b""
|
||||
@@ -146,7 +148,7 @@ class CyberarkPassword:
|
||||
if tmp_error:
|
||||
raise AnsibleError(f"ERROR => {tmp_error} ")
|
||||
|
||||
if b_credential and b_credential.endswith(b'\n'):
|
||||
if b_credential and b_credential.endswith(b"\n"):
|
||||
b_credential = b_credential[:-1]
|
||||
|
||||
output_names = self.output.split(",")
|
||||
@@ -164,13 +166,14 @@ class CyberarkPassword:
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise AnsibleError(e.output)
|
||||
except OSError as e:
|
||||
raise AnsibleError(f"ERROR - AIM not installed or clipasswordsdk not in standard location. ERROR=({e.errno}) => {e.strerror} ")
|
||||
raise AnsibleError(
|
||||
f"ERROR - AIM not installed or clipasswordsdk not in standard location. ERROR=({e.errno}) => {e.strerror} "
|
||||
)
|
||||
|
||||
return [result_dict]
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
"""
|
||||
USAGE:
|
||||
|
||||
|
||||
@@ -125,6 +125,7 @@ from ansible.template import Templar
|
||||
|
||||
try:
|
||||
from ansible.template import trust_as_template as _trust_as_template
|
||||
|
||||
HAS_DATATAGGING = True
|
||||
except ImportError:
|
||||
HAS_DATATAGGING = False
|
||||
@@ -145,7 +146,7 @@ class LookupModule(LookupBase):
|
||||
"""
|
||||
templar.available_variables = variables or {}
|
||||
quoted_expression = "{0}{1}{2}".format("{{", expression, "}}")
|
||||
if hasattr(templar, 'evaluate_expression'):
|
||||
if hasattr(templar, "evaluate_expression"):
|
||||
# This is available since the Data Tagging PR has been merged
|
||||
return templar.evaluate_expression(_make_safe(expression))
|
||||
return templar.template(quoted_expression)
|
||||
@@ -169,12 +170,11 @@ class LookupModule(LookupBase):
|
||||
if expression is not None:
|
||||
# Evaluate expression in current context
|
||||
vars = variables.copy()
|
||||
vars['item'] = current.copy()
|
||||
vars["item"] = current.copy()
|
||||
try:
|
||||
values = self.__evaluate(expression, templar, variables=vars)
|
||||
except Exception as e:
|
||||
raise AnsibleLookupError(
|
||||
f'Caught "{e}" while evaluating {key!r} with item == {current!r}')
|
||||
raise AnsibleLookupError(f'Caught "{e}" while evaluating {key!r} with item == {current!r}')
|
||||
|
||||
if isinstance(values, Mapping):
|
||||
for idx, val in sorted(values.items()):
|
||||
@@ -186,7 +186,8 @@ class LookupModule(LookupBase):
|
||||
self.__process(result, terms, index + 1, current, templar, variables)
|
||||
else:
|
||||
raise AnsibleLookupError(
|
||||
f'Did not obtain dictionary or list while evaluating {key!r} with item == {current!r}, but {type(values)}')
|
||||
f"Did not obtain dictionary or list while evaluating {key!r} with item == {current!r}, but {type(values)}"
|
||||
)
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
"""Generate list."""
|
||||
@@ -202,15 +203,14 @@ class LookupModule(LookupBase):
|
||||
vars_so_far = set()
|
||||
for index, term in enumerate(terms):
|
||||
if not isinstance(term, Mapping):
|
||||
raise AnsibleLookupError(
|
||||
f'Parameter {index} must be a dictionary, got {type(term)}')
|
||||
raise AnsibleLookupError(f"Parameter {index} must be a dictionary, got {type(term)}")
|
||||
if len(term) != 1:
|
||||
raise AnsibleLookupError(
|
||||
f'Parameter {index} must be a one-element dictionary, got {len(term)} elements')
|
||||
f"Parameter {index} must be a one-element dictionary, got {len(term)} elements"
|
||||
)
|
||||
k, v = list(term.items())[0]
|
||||
if k in vars_so_far:
|
||||
raise AnsibleLookupError(
|
||||
f'The variable {k!r} appears more than once')
|
||||
raise AnsibleLookupError(f"The variable {k!r} appears more than once")
|
||||
vars_so_far.add(k)
|
||||
if isinstance(v, str):
|
||||
data.append((k, v, None))
|
||||
@@ -218,6 +218,7 @@ class LookupModule(LookupBase):
|
||||
data.append((k, None, v))
|
||||
else:
|
||||
raise AnsibleLookupError(
|
||||
f'Parameter {k!r} (index {index}) must have a value of type string, dictionary or list, got type {type(v)}')
|
||||
f"Parameter {k!r} (index {index}) must have a value of type string, dictionary or list, got type {type(v)}"
|
||||
)
|
||||
self.__process(result, data, 0, {}, templar, variables)
|
||||
return result
|
||||
|
||||
@@ -252,8 +252,30 @@ try:
|
||||
import dns.resolver
|
||||
import dns.reversename
|
||||
import dns.rdataclass
|
||||
from dns.rdatatype import (A, AAAA, CAA, CNAME, DNAME, DNSKEY, DS, HINFO, LOC,
|
||||
MX, NAPTR, NS, NSEC3PARAM, PTR, RP, SOA, SPF, SRV, SSHFP, TLSA, TXT)
|
||||
from dns.rdatatype import (
|
||||
A,
|
||||
AAAA,
|
||||
CAA,
|
||||
CNAME,
|
||||
DNAME,
|
||||
DNSKEY,
|
||||
DS,
|
||||
HINFO,
|
||||
LOC,
|
||||
MX,
|
||||
NAPTR,
|
||||
NS,
|
||||
NSEC3PARAM,
|
||||
PTR,
|
||||
RP,
|
||||
SOA,
|
||||
SPF,
|
||||
SRV,
|
||||
SSHFP,
|
||||
TLSA,
|
||||
TXT,
|
||||
)
|
||||
|
||||
HAVE_DNS = True
|
||||
except ImportError:
|
||||
HAVE_DNS = False
|
||||
@@ -263,35 +285,35 @@ display = Display()
|
||||
|
||||
|
||||
def make_rdata_dict(rdata):
|
||||
''' While the 'dig' lookup plugin supports anything which dnspython supports
|
||||
out of the box, the following supported_types list describes which
|
||||
DNS query types we can convert to a dict.
|
||||
"""While the 'dig' lookup plugin supports anything which dnspython supports
|
||||
out of the box, the following supported_types list describes which
|
||||
DNS query types we can convert to a dict.
|
||||
|
||||
Note: adding support for RRSIG is hard work. :)
|
||||
'''
|
||||
Note: adding support for RRSIG is hard work. :)
|
||||
"""
|
||||
supported_types = {
|
||||
A: ['address'],
|
||||
AAAA: ['address'],
|
||||
CAA: ['flags', 'tag', 'value'],
|
||||
CNAME: ['target'],
|
||||
DNAME: ['target'],
|
||||
DNSKEY: ['flags', 'algorithm', 'protocol', 'key'],
|
||||
DS: ['algorithm', 'digest_type', 'key_tag', 'digest'],
|
||||
HINFO: ['cpu', 'os'],
|
||||
LOC: ['latitude', 'longitude', 'altitude', 'size', 'horizontal_precision', 'vertical_precision'],
|
||||
MX: ['preference', 'exchange'],
|
||||
NAPTR: ['order', 'preference', 'flags', 'service', 'regexp', 'replacement'],
|
||||
NS: ['target'],
|
||||
NSEC3PARAM: ['algorithm', 'flags', 'iterations', 'salt'],
|
||||
PTR: ['target'],
|
||||
RP: ['mbox', 'txt'],
|
||||
A: ["address"],
|
||||
AAAA: ["address"],
|
||||
CAA: ["flags", "tag", "value"],
|
||||
CNAME: ["target"],
|
||||
DNAME: ["target"],
|
||||
DNSKEY: ["flags", "algorithm", "protocol", "key"],
|
||||
DS: ["algorithm", "digest_type", "key_tag", "digest"],
|
||||
HINFO: ["cpu", "os"],
|
||||
LOC: ["latitude", "longitude", "altitude", "size", "horizontal_precision", "vertical_precision"],
|
||||
MX: ["preference", "exchange"],
|
||||
NAPTR: ["order", "preference", "flags", "service", "regexp", "replacement"],
|
||||
NS: ["target"],
|
||||
NSEC3PARAM: ["algorithm", "flags", "iterations", "salt"],
|
||||
PTR: ["target"],
|
||||
RP: ["mbox", "txt"],
|
||||
# RRSIG: ['type_covered', 'algorithm', 'labels', 'original_ttl', 'expiration', 'inception', 'key_tag', 'signer', 'signature'],
|
||||
SOA: ['mname', 'rname', 'serial', 'refresh', 'retry', 'expire', 'minimum'],
|
||||
SPF: ['strings'],
|
||||
SRV: ['priority', 'weight', 'port', 'target'],
|
||||
SSHFP: ['algorithm', 'fp_type', 'fingerprint'],
|
||||
TLSA: ['usage', 'selector', 'mtype', 'cert'],
|
||||
TXT: ['strings'],
|
||||
SOA: ["mname", "rname", "serial", "refresh", "retry", "expire", "minimum"],
|
||||
SPF: ["strings"],
|
||||
SRV: ["priority", "weight", "port", "target"],
|
||||
SSHFP: ["algorithm", "fp_type", "fingerprint"],
|
||||
TLSA: ["usage", "selector", "mtype", "cert"],
|
||||
TXT: ["strings"],
|
||||
}
|
||||
|
||||
rd = {}
|
||||
@@ -304,18 +326,18 @@ def make_rdata_dict(rdata):
|
||||
if isinstance(val, dns.name.Name):
|
||||
val = dns.name.Name.to_text(val)
|
||||
|
||||
if rdata.rdtype == DS and f == 'digest':
|
||||
val = dns.rdata._hexify(rdata.digest).replace(' ', '')
|
||||
if rdata.rdtype == DNSKEY and f == 'algorithm':
|
||||
if rdata.rdtype == DS and f == "digest":
|
||||
val = dns.rdata._hexify(rdata.digest).replace(" ", "")
|
||||
if rdata.rdtype == DNSKEY and f == "algorithm":
|
||||
val = int(val)
|
||||
if rdata.rdtype == DNSKEY and f == 'key':
|
||||
val = dns.rdata._base64ify(rdata.key).replace(' ', '')
|
||||
if rdata.rdtype == NSEC3PARAM and f == 'salt':
|
||||
val = dns.rdata._hexify(rdata.salt).replace(' ', '')
|
||||
if rdata.rdtype == SSHFP and f == 'fingerprint':
|
||||
val = dns.rdata._hexify(rdata.fingerprint).replace(' ', '')
|
||||
if rdata.rdtype == TLSA and f == 'cert':
|
||||
val = dns.rdata._hexify(rdata.cert).replace(' ', '')
|
||||
if rdata.rdtype == DNSKEY and f == "key":
|
||||
val = dns.rdata._base64ify(rdata.key).replace(" ", "")
|
||||
if rdata.rdtype == NSEC3PARAM and f == "salt":
|
||||
val = dns.rdata._hexify(rdata.salt).replace(" ", "")
|
||||
if rdata.rdtype == SSHFP and f == "fingerprint":
|
||||
val = dns.rdata._hexify(rdata.fingerprint).replace(" ", "")
|
||||
if rdata.rdtype == TLSA and f == "cert":
|
||||
val = dns.rdata._hexify(rdata.cert).replace(" ", "")
|
||||
|
||||
rd[f] = val
|
||||
|
||||
@@ -327,11 +349,10 @@ def make_rdata_dict(rdata):
|
||||
#
|
||||
# --------------------------------------------------------------
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
|
||||
'''
|
||||
"""
|
||||
terms contains a string with things to `dig' for. We support the
|
||||
following formats:
|
||||
example.com # A record
|
||||
@@ -344,7 +365,7 @@ class LookupModule(LookupBase):
|
||||
^^^ can be comma-sep list of names/addresses
|
||||
|
||||
... flat=0 # returns a dict; default is 1 == string
|
||||
'''
|
||||
"""
|
||||
if HAVE_DNS is False:
|
||||
raise AnsibleError("The dig lookup requires the python 'dnspython' library and it is not installed")
|
||||
|
||||
@@ -357,21 +378,21 @@ class LookupModule(LookupBase):
|
||||
|
||||
domains = []
|
||||
nameservers = []
|
||||
qtype = self.get_option('qtype')
|
||||
flat = self.get_option('flat')
|
||||
fail_on_error = self.get_option('fail_on_error')
|
||||
real_empty = self.get_option('real_empty')
|
||||
tcp = self.get_option('tcp')
|
||||
port = self.get_option('port')
|
||||
qtype = self.get_option("qtype")
|
||||
flat = self.get_option("flat")
|
||||
fail_on_error = self.get_option("fail_on_error")
|
||||
real_empty = self.get_option("real_empty")
|
||||
tcp = self.get_option("tcp")
|
||||
port = self.get_option("port")
|
||||
try:
|
||||
rdclass = dns.rdataclass.from_text(self.get_option('class'))
|
||||
rdclass = dns.rdataclass.from_text(self.get_option("class"))
|
||||
except Exception as e:
|
||||
raise AnsibleError(f"dns lookup illegal CLASS: {e}")
|
||||
myres.retry_servfail = self.get_option('retry_servfail')
|
||||
myres.retry_servfail = self.get_option("retry_servfail")
|
||||
|
||||
for t in terms:
|
||||
if t.startswith('@'): # e.g. "@10.0.1.2,192.0.2.1" is ok.
|
||||
nsset = t[1:].split(',')
|
||||
if t.startswith("@"): # e.g. "@10.0.1.2,192.0.2.1" is ok.
|
||||
nsset = t[1:].split(",")
|
||||
for ns in nsset:
|
||||
# Check if we have a valid IP address. If so, use that, otherwise
|
||||
# try to resolve name to address using system's resolver. If that
|
||||
@@ -386,35 +407,35 @@ class LookupModule(LookupBase):
|
||||
except Exception as e:
|
||||
raise AnsibleError(f"dns lookup NS: {e}")
|
||||
continue
|
||||
if '=' in t:
|
||||
if "=" in t:
|
||||
try:
|
||||
opt, arg = t.split('=', 1)
|
||||
opt, arg = t.split("=", 1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if opt == 'qtype':
|
||||
if opt == "qtype":
|
||||
qtype = arg.upper()
|
||||
elif opt == 'flat':
|
||||
elif opt == "flat":
|
||||
flat = int(arg)
|
||||
elif opt == 'class':
|
||||
elif opt == "class":
|
||||
try:
|
||||
rdclass = dns.rdataclass.from_text(arg)
|
||||
except Exception as e:
|
||||
raise AnsibleError(f"dns lookup illegal CLASS: {e}")
|
||||
elif opt == 'retry_servfail':
|
||||
elif opt == "retry_servfail":
|
||||
myres.retry_servfail = boolean(arg)
|
||||
elif opt == 'fail_on_error':
|
||||
elif opt == "fail_on_error":
|
||||
fail_on_error = boolean(arg)
|
||||
elif opt == 'real_empty':
|
||||
elif opt == "real_empty":
|
||||
real_empty = boolean(arg)
|
||||
elif opt == 'tcp':
|
||||
elif opt == "tcp":
|
||||
tcp = boolean(arg)
|
||||
|
||||
continue
|
||||
|
||||
if '/' in t:
|
||||
if "/" in t:
|
||||
try:
|
||||
domain, qtype = t.split('/')
|
||||
domain, qtype = t.split("/")
|
||||
domains.append(domain)
|
||||
except Exception:
|
||||
domains.append(t)
|
||||
@@ -428,7 +449,7 @@ class LookupModule(LookupBase):
|
||||
if len(nameservers) > 0:
|
||||
myres.nameservers = nameservers
|
||||
|
||||
if qtype.upper() == 'PTR':
|
||||
if qtype.upper() == "PTR":
|
||||
reversed_domains = []
|
||||
for domain in domains:
|
||||
try:
|
||||
@@ -450,7 +471,7 @@ class LookupModule(LookupBase):
|
||||
answers = myres.query(domain, qtype, rdclass=rdclass, tcp=tcp)
|
||||
for rdata in answers:
|
||||
s = rdata.to_text()
|
||||
if qtype.upper() == 'TXT':
|
||||
if qtype.upper() == "TXT":
|
||||
s = s[1:-1] # Strip outside quotes on TXT rdata
|
||||
|
||||
if flat:
|
||||
@@ -458,10 +479,10 @@ class LookupModule(LookupBase):
|
||||
else:
|
||||
try:
|
||||
rd = make_rdata_dict(rdata)
|
||||
rd['owner'] = answers.canonical_name.to_text()
|
||||
rd['type'] = dns.rdatatype.to_text(rdata.rdtype)
|
||||
rd['ttl'] = answers.rrset.ttl
|
||||
rd['class'] = dns.rdataclass.to_text(rdata.rdclass)
|
||||
rd["owner"] = answers.canonical_name.to_text()
|
||||
rd["type"] = dns.rdatatype.to_text(rdata.rdtype)
|
||||
rd["ttl"] = answers.rrset.ttl
|
||||
rd["class"] = dns.rdataclass.to_text(rdata.rdclass)
|
||||
|
||||
ret.append(rd)
|
||||
except Exception as err:
|
||||
@@ -473,7 +494,7 @@ class LookupModule(LookupBase):
|
||||
if fail_on_error:
|
||||
raise AnsibleError(f"Lookup failed: {err}")
|
||||
if not real_empty:
|
||||
ret.append('NXDOMAIN')
|
||||
ret.append("NXDOMAIN")
|
||||
except (dns.resolver.NoAnswer, dns.resolver.Timeout, dns.resolver.NoNameservers) as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError(f"Lookup failed: {err}")
|
||||
|
||||
@@ -57,6 +57,7 @@ HAVE_DNS = False
|
||||
try:
|
||||
import dns.resolver
|
||||
from dns.exception import DNSException
|
||||
|
||||
HAVE_DNS = True
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -73,21 +74,20 @@ from ansible.plugins.lookup import LookupBase
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
|
||||
if HAVE_DNS is False:
|
||||
raise AnsibleError("Can't LOOKUP(dnstxt): module dns.resolver is not installed")
|
||||
|
||||
real_empty = self.get_option('real_empty')
|
||||
real_empty = self.get_option("real_empty")
|
||||
|
||||
ret = []
|
||||
for term in terms:
|
||||
domain = term.split()[0]
|
||||
string = []
|
||||
try:
|
||||
answers = dns.resolver.query(domain, 'TXT')
|
||||
answers = dns.resolver.query(domain, "TXT")
|
||||
for rdata in answers:
|
||||
s = rdata.to_text()
|
||||
string.append(s[1:-1]) # Strip outside quotes on TXT rdata
|
||||
@@ -95,18 +95,18 @@ class LookupModule(LookupBase):
|
||||
except dns.resolver.NXDOMAIN:
|
||||
if real_empty:
|
||||
continue
|
||||
string = 'NXDOMAIN'
|
||||
string = "NXDOMAIN"
|
||||
except dns.resolver.Timeout:
|
||||
if real_empty:
|
||||
continue
|
||||
string = ''
|
||||
string = ""
|
||||
except dns.resolver.NoAnswer:
|
||||
if real_empty:
|
||||
continue
|
||||
string = ''
|
||||
string = ""
|
||||
except DNSException as e:
|
||||
raise AnsibleError(f"dns.resolver unhandled exception {e}")
|
||||
|
||||
ret.append(''.join(string))
|
||||
ret.append("".join(string))
|
||||
|
||||
return ret
|
||||
|
||||
@@ -140,7 +140,5 @@ class LookupModule(LookupBase):
|
||||
display.vvv(f"DevOps Secrets Vault GET /secrets/{path}")
|
||||
result.append(vault.get_secret_json(path))
|
||||
except SecretsVaultError as error:
|
||||
raise AnsibleError(
|
||||
f"DevOps Secrets Vault lookup failure: {error.message}"
|
||||
)
|
||||
raise AnsibleError(f"DevOps Secrets Vault lookup failure: {error.message}")
|
||||
return result
|
||||
|
||||
@@ -102,7 +102,7 @@ class Etcd:
|
||||
def __init__(self, url, version, validate_certs):
|
||||
self.url = url
|
||||
self.version = version
|
||||
self.baseurl = f'{self.url}/{self.version}/keys'
|
||||
self.baseurl = f"{self.url}/{self.version}/keys"
|
||||
self.validate_certs = validate_certs
|
||||
|
||||
def _parse_node(self, node):
|
||||
@@ -113,12 +113,12 @@ class Etcd:
|
||||
# the function will create a key-value at this level and
|
||||
# undoing the loop.
|
||||
path = {}
|
||||
if node.get('dir', False):
|
||||
for n in node.get('nodes', []):
|
||||
path[n['key'].split('/')[-1]] = self._parse_node(n)
|
||||
if node.get("dir", False):
|
||||
for n in node.get("nodes", []):
|
||||
path[n["key"].split("/")[-1]] = self._parse_node(n)
|
||||
|
||||
else:
|
||||
path = node['value']
|
||||
path = node["value"]
|
||||
|
||||
return path
|
||||
|
||||
@@ -135,16 +135,16 @@ class Etcd:
|
||||
try:
|
||||
# I will not support Version 1 of etcd for folder parsing
|
||||
item = json.loads(data)
|
||||
if self.version == 'v1':
|
||||
if self.version == "v1":
|
||||
# When ETCD are working with just v1
|
||||
if 'value' in item:
|
||||
value = item['value']
|
||||
if "value" in item:
|
||||
value = item["value"]
|
||||
else:
|
||||
if 'node' in item:
|
||||
if "node" in item:
|
||||
# When a usual result from ETCD
|
||||
value = self._parse_node(item['node'])
|
||||
value = self._parse_node(item["node"])
|
||||
|
||||
if 'errorCode' in item:
|
||||
if "errorCode" in item:
|
||||
# Here return an error when an unknown entry responds
|
||||
value = "ENOENT"
|
||||
except Exception:
|
||||
@@ -154,14 +154,12 @@ class Etcd:
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables, **kwargs):
|
||||
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
|
||||
validate_certs = self.get_option('validate_certs')
|
||||
url = self.get_option('url')
|
||||
version = self.get_option('version')
|
||||
validate_certs = self.get_option("validate_certs")
|
||||
url = self.get_option("url")
|
||||
version = self.get_option("version")
|
||||
|
||||
etcd = Etcd(url=url, version=version, validate_certs=validate_certs)
|
||||
|
||||
|
||||
@@ -142,6 +142,7 @@ from ansible.utils.display import Display
|
||||
|
||||
try:
|
||||
import etcd3
|
||||
|
||||
HAS_ETCD = True
|
||||
except ImportError:
|
||||
HAS_ETCD = False
|
||||
@@ -149,14 +150,14 @@ except ImportError:
|
||||
display = Display()
|
||||
|
||||
etcd3_cnx_opts = (
|
||||
'host',
|
||||
'port',
|
||||
'ca_cert',
|
||||
'cert_key',
|
||||
'cert_cert',
|
||||
'timeout',
|
||||
'user',
|
||||
'password',
|
||||
"host",
|
||||
"port",
|
||||
"ca_cert",
|
||||
"cert_key",
|
||||
"cert_cert",
|
||||
"timeout",
|
||||
"user",
|
||||
"password",
|
||||
# 'grpc_options' Etcd3Client() option currently not supported by lookup module (maybe in future ?)
|
||||
)
|
||||
|
||||
@@ -166,18 +167,16 @@ def etcd3_client(client_params):
|
||||
etcd = etcd3.client(**client_params)
|
||||
etcd.status()
|
||||
except Exception as exp:
|
||||
raise AnsibleLookupError(f'Cannot connect to etcd cluster: {exp}')
|
||||
raise AnsibleLookupError(f"Cannot connect to etcd cluster: {exp}")
|
||||
return etcd
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables, **kwargs):
|
||||
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
|
||||
if not HAS_ETCD:
|
||||
display.error(missing_required_lib('etcd3'))
|
||||
display.error(missing_required_lib("etcd3"))
|
||||
return None
|
||||
|
||||
# create the etcd3 connection parameters dict to pass to etcd3 class
|
||||
@@ -187,21 +186,21 @@ class LookupModule(LookupBase):
|
||||
# must be mangled a bit to fit in this scheme.
|
||||
# so here we use a regex to extract server and port
|
||||
match = re.compile(
|
||||
r'^(https?://)?(?P<host>(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([-_\d\w\.]+))(:(?P<port>\d{1,5}))?/?$'
|
||||
).match(self.get_option('endpoints'))
|
||||
r"^(https?://)?(?P<host>(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([-_\d\w\.]+))(:(?P<port>\d{1,5}))?/?$"
|
||||
).match(self.get_option("endpoints"))
|
||||
if match:
|
||||
if match.group('host'):
|
||||
client_params['host'] = match.group('host')
|
||||
if match.group('port'):
|
||||
client_params['port'] = match.group('port')
|
||||
if match.group("host"):
|
||||
client_params["host"] = match.group("host")
|
||||
if match.group("port"):
|
||||
client_params["port"] = match.group("port")
|
||||
|
||||
for opt in etcd3_cnx_opts:
|
||||
if self.get_option(opt):
|
||||
client_params[opt] = self.get_option(opt)
|
||||
|
||||
cnx_log = dict(client_params)
|
||||
if 'password' in cnx_log:
|
||||
cnx_log['password'] = '<redacted>'
|
||||
if "password" in cnx_log:
|
||||
cnx_log["password"] = "<redacted>"
|
||||
display.verbose(f"etcd3 connection parameters: {cnx_log}")
|
||||
|
||||
# connect to etcd3 server
|
||||
@@ -210,18 +209,18 @@ class LookupModule(LookupBase):
|
||||
ret = []
|
||||
# we can pass many keys to lookup
|
||||
for term in terms:
|
||||
if self.get_option('prefix'):
|
||||
if self.get_option("prefix"):
|
||||
try:
|
||||
for val, meta in etcd.get_prefix(term):
|
||||
if val and meta:
|
||||
ret.append({'key': to_native(meta.key), 'value': to_native(val)})
|
||||
ret.append({"key": to_native(meta.key), "value": to_native(val)})
|
||||
except Exception as exp:
|
||||
display.warning(f'Caught except during etcd3.get_prefix: {exp}')
|
||||
display.warning(f"Caught except during etcd3.get_prefix: {exp}")
|
||||
else:
|
||||
try:
|
||||
val, meta = etcd.get(term)
|
||||
if val and meta:
|
||||
ret.append({'key': to_native(meta.key), 'value': to_native(val)})
|
||||
ret.append({"key": to_native(meta.key), "value": to_native(val)})
|
||||
except Exception as exp:
|
||||
display.warning(f'Caught except during etcd3.get: {exp}')
|
||||
display.warning(f"Caught except during etcd3.get: {exp}")
|
||||
return ret
|
||||
|
||||
@@ -133,6 +133,7 @@ import re
|
||||
HAVE_SELINUX = False
|
||||
try:
|
||||
import selinux
|
||||
|
||||
HAVE_SELINUX = True
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -157,78 +158,76 @@ def selinux_context(path):
|
||||
if ret[0] != -1:
|
||||
# Limit split to 4 because the selevel, the last in the list,
|
||||
# may contain ':' characters
|
||||
context = ret[1].split(':', 3)
|
||||
context = ret[1].split(":", 3)
|
||||
return context
|
||||
|
||||
|
||||
def file_props(root, path):
|
||||
''' Returns dictionary with file properties, or return None on failure '''
|
||||
"""Returns dictionary with file properties, or return None on failure"""
|
||||
abspath = os.path.join(root, path)
|
||||
|
||||
try:
|
||||
st = os.lstat(abspath)
|
||||
except OSError as e:
|
||||
display.warning(f'filetree: Error using stat() on path {abspath} ({e})')
|
||||
display.warning(f"filetree: Error using stat() on path {abspath} ({e})")
|
||||
return None
|
||||
|
||||
ret = dict(root=root, path=path)
|
||||
|
||||
if stat.S_ISLNK(st.st_mode):
|
||||
ret['state'] = 'link'
|
||||
ret['src'] = os.readlink(abspath)
|
||||
ret["state"] = "link"
|
||||
ret["src"] = os.readlink(abspath)
|
||||
elif stat.S_ISDIR(st.st_mode):
|
||||
ret['state'] = 'directory'
|
||||
ret["state"] = "directory"
|
||||
elif stat.S_ISREG(st.st_mode):
|
||||
ret['state'] = 'file'
|
||||
ret['src'] = abspath
|
||||
ret["state"] = "file"
|
||||
ret["src"] = abspath
|
||||
else:
|
||||
display.warning(f'filetree: Error file type of {abspath} is not supported')
|
||||
display.warning(f"filetree: Error file type of {abspath} is not supported")
|
||||
return None
|
||||
|
||||
ret['uid'] = st.st_uid
|
||||
ret['gid'] = st.st_gid
|
||||
ret["uid"] = st.st_uid
|
||||
ret["gid"] = st.st_gid
|
||||
try:
|
||||
ret['owner'] = pwd.getpwuid(st.st_uid).pw_name
|
||||
ret["owner"] = pwd.getpwuid(st.st_uid).pw_name
|
||||
except KeyError:
|
||||
ret['owner'] = st.st_uid
|
||||
ret["owner"] = st.st_uid
|
||||
try:
|
||||
ret['group'] = to_text(grp.getgrgid(st.st_gid).gr_name)
|
||||
ret["group"] = to_text(grp.getgrgid(st.st_gid).gr_name)
|
||||
except KeyError:
|
||||
ret['group'] = st.st_gid
|
||||
ret['mode'] = f'0{stat.S_IMODE(st.st_mode):03o}'
|
||||
ret['size'] = st.st_size
|
||||
ret['mtime'] = st.st_mtime
|
||||
ret['ctime'] = st.st_ctime
|
||||
ret["group"] = st.st_gid
|
||||
ret["mode"] = f"0{stat.S_IMODE(st.st_mode):03o}"
|
||||
ret["size"] = st.st_size
|
||||
ret["mtime"] = st.st_mtime
|
||||
ret["ctime"] = st.st_ctime
|
||||
|
||||
if HAVE_SELINUX and selinux.is_selinux_enabled() == 1:
|
||||
context = selinux_context(abspath)
|
||||
ret['seuser'] = context[0]
|
||||
ret['serole'] = context[1]
|
||||
ret['setype'] = context[2]
|
||||
ret['selevel'] = context[3]
|
||||
ret["seuser"] = context[0]
|
||||
ret["serole"] = context[1]
|
||||
ret["setype"] = context[2]
|
||||
ret["selevel"] = context[3]
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
|
||||
basedir = self.get_basedir(variables)
|
||||
|
||||
# Regular expression for exclude
|
||||
exclude = self.get_option('exclude')
|
||||
exclude = self.get_option("exclude")
|
||||
exclude_pattern = re.compile(exclude) if exclude else None
|
||||
|
||||
ret = []
|
||||
for term in terms:
|
||||
term_file = os.path.basename(term)
|
||||
dwimmed_path = self._loader.path_dwim_relative(basedir, 'files', os.path.dirname(term))
|
||||
dwimmed_path = self._loader.path_dwim_relative(basedir, "files", os.path.dirname(term))
|
||||
path = os.path.join(dwimmed_path, term_file)
|
||||
display.debug(f"Walking '{path}'")
|
||||
for root, dirs, files in os.walk(path, topdown=True):
|
||||
|
||||
# Filter files and directories using a regular expression
|
||||
if exclude_pattern is not None:
|
||||
dirs[:] = [d for d in dirs if not exclude_pattern.match(d)]
|
||||
@@ -238,7 +237,7 @@ class LookupModule(LookupBase):
|
||||
relpath = os.path.relpath(os.path.join(root, entry), path)
|
||||
|
||||
# Skip if relpath was already processed (from another root)
|
||||
if relpath not in [entry['path'] for entry in ret]:
|
||||
if relpath not in [entry["path"] for entry in ret]:
|
||||
props = file_props(path, relpath)
|
||||
if props is not None:
|
||||
display.debug(f" found '{os.path.join(path, relpath)}'")
|
||||
|
||||
@@ -40,7 +40,6 @@ from ansible.utils.listify import listify_lookup_plugin_terms
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def _check_list_of_one_list(self, term):
|
||||
# make sure term is not a list of one (list of one..) item
|
||||
# return the final non list item if so
|
||||
@@ -53,12 +52,11 @@ class LookupModule(LookupBase):
|
||||
return term
|
||||
|
||||
def _do_flatten(self, terms, variables):
|
||||
|
||||
ret = []
|
||||
for term in terms:
|
||||
term = self._check_list_of_one_list(term)
|
||||
|
||||
if term == 'None' or term == 'null':
|
||||
if term == "None" or term == "null":
|
||||
# ignore undefined items
|
||||
break
|
||||
|
||||
|
||||
@@ -75,18 +75,21 @@ _raw:
|
||||
|
||||
try:
|
||||
import jwt
|
||||
|
||||
HAS_JWT = True
|
||||
except ImportError:
|
||||
HAS_JWT = False
|
||||
|
||||
HAS_PYTHON_JWT = False # vs pyjwt
|
||||
if HAS_JWT and hasattr(jwt, 'JWT'):
|
||||
if HAS_JWT and hasattr(jwt, "JWT"):
|
||||
HAS_PYTHON_JWT = True
|
||||
from jwt import jwk_from_pem, JWT # type: ignore[attr-defined]
|
||||
|
||||
jwt_instance = JWT()
|
||||
|
||||
try:
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
|
||||
HAS_CRYPTOGRAPHY = True
|
||||
except ImportError:
|
||||
HAS_CRYPTOGRAPHY = False
|
||||
@@ -105,13 +108,12 @@ display = Display()
|
||||
|
||||
|
||||
class PythonJWT:
|
||||
|
||||
@staticmethod
|
||||
def read_key(path, private_key=None):
|
||||
try:
|
||||
if private_key:
|
||||
return jwk_from_pem(private_key.encode('utf-8'))
|
||||
with open(path, 'rb') as pem_file:
|
||||
return jwk_from_pem(private_key.encode("utf-8"))
|
||||
with open(path, "rb") as pem_file:
|
||||
return jwk_from_pem(pem_file.read())
|
||||
except Exception as e:
|
||||
raise AnsibleError(f"Error while parsing key file: {e}")
|
||||
@@ -120,12 +122,12 @@ class PythonJWT:
|
||||
def encode_jwt(app_id, jwk, exp=600):
|
||||
now = int(time.time())
|
||||
payload = {
|
||||
'iat': now,
|
||||
'exp': now + exp,
|
||||
'iss': app_id,
|
||||
"iat": now,
|
||||
"exp": now + exp,
|
||||
"iss": app_id,
|
||||
}
|
||||
try:
|
||||
return jwt_instance.encode(payload, jwk, alg='RS256')
|
||||
return jwt_instance.encode(payload, jwk, alg="RS256")
|
||||
except Exception as e:
|
||||
raise AnsibleError(f"Error while encoding jwt: {e}")
|
||||
|
||||
@@ -135,9 +137,9 @@ def read_key(path, private_key=None):
|
||||
return PythonJWT.read_key(path, private_key)
|
||||
try:
|
||||
if private_key:
|
||||
key_bytes = private_key.encode('utf-8')
|
||||
key_bytes = private_key.encode("utf-8")
|
||||
else:
|
||||
with open(path, 'rb') as pem_file:
|
||||
with open(path, "rb") as pem_file:
|
||||
key_bytes = pem_file.read()
|
||||
return serialization.load_pem_private_key(key_bytes, password=None)
|
||||
except Exception as e:
|
||||
@@ -149,26 +151,26 @@ def encode_jwt(app_id, private_key_obj, exp=600):
|
||||
return PythonJWT.encode_jwt(app_id, private_key_obj)
|
||||
now = int(time.time())
|
||||
payload = {
|
||||
'iat': now,
|
||||
'exp': now + exp,
|
||||
'iss': app_id,
|
||||
"iat": now,
|
||||
"exp": now + exp,
|
||||
"iss": app_id,
|
||||
}
|
||||
try:
|
||||
return jwt.encode(payload, private_key_obj, algorithm='RS256')
|
||||
return jwt.encode(payload, private_key_obj, algorithm="RS256")
|
||||
except Exception as e:
|
||||
raise AnsibleError(f"Error while encoding jwt: {e}")
|
||||
|
||||
|
||||
def post_request(generated_jwt, installation_id, api_base):
|
||||
base = api_base.rstrip('/')
|
||||
base = api_base.rstrip("/")
|
||||
github_url = f"{base}/app/installations/{installation_id}/access_tokens"
|
||||
|
||||
headers = {
|
||||
"Authorization": f'Bearer {generated_jwt}',
|
||||
"Authorization": f"Bearer {generated_jwt}",
|
||||
"Accept": "application/vnd.github.v3+json",
|
||||
}
|
||||
try:
|
||||
response = open_url(github_url, headers=headers, method='POST')
|
||||
response = open_url(github_url, headers=headers, method="POST")
|
||||
except HTTPError as e:
|
||||
try:
|
||||
error_body = json.loads(e.read().decode())
|
||||
@@ -182,10 +184,10 @@ def post_request(generated_jwt, installation_id, api_base):
|
||||
raise AnsibleError(f"Unexpected data returned: {e} -- {error_body}")
|
||||
response_body = response.read()
|
||||
try:
|
||||
json_data = json.loads(response_body.decode('utf-8'))
|
||||
json_data = json.loads(response_body.decode("utf-8"))
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
raise AnsibleError(f"Error while dencoding JSON respone from github: {e}")
|
||||
return json_data.get('token')
|
||||
return json_data.get("token")
|
||||
|
||||
|
||||
def get_token(key_path, app_id, installation_id, private_key, github_url, expiry=600):
|
||||
@@ -197,12 +199,12 @@ def get_token(key_path, app_id, installation_id, private_key, github_url, expiry
|
||||
class LookupModule(LookupBase):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
if not HAS_JWT:
|
||||
raise AnsibleError('Python jwt library is required. '
|
||||
'Please install using "pip install pyjwt"')
|
||||
raise AnsibleError('Python jwt library is required. Please install using "pip install pyjwt"')
|
||||
|
||||
if not HAS_PYTHON_JWT and not HAS_CRYPTOGRAPHY:
|
||||
raise AnsibleError('Python cryptography library is required. '
|
||||
'Please install using "pip install cryptography"')
|
||||
raise AnsibleError(
|
||||
'Python cryptography library is required. Please install using "pip install cryptography"'
|
||||
)
|
||||
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
|
||||
@@ -212,12 +214,12 @@ class LookupModule(LookupBase):
|
||||
raise AnsibleOptionsError("key_path and private_key are mutually exclusive")
|
||||
|
||||
t = get_token(
|
||||
self.get_option('key_path'),
|
||||
self.get_option('app_id'),
|
||||
self.get_option('installation_id'),
|
||||
self.get_option('private_key'),
|
||||
self.get_option('github_url'),
|
||||
self.get_option('token_expiry'),
|
||||
self.get_option("key_path"),
|
||||
self.get_option("app_id"),
|
||||
self.get_option("installation_id"),
|
||||
self.get_option("private_key"),
|
||||
self.get_option("github_url"),
|
||||
self.get_option("token_expiry"),
|
||||
)
|
||||
|
||||
return [t]
|
||||
|
||||
@@ -79,7 +79,7 @@ class Hiera:
|
||||
|
||||
def get(self, hiera_key):
|
||||
pargs = [self.hiera_bin]
|
||||
pargs.extend(['-c', self.hiera_cfg])
|
||||
pargs.extend(["-c", self.hiera_cfg])
|
||||
|
||||
pargs.extend(hiera_key)
|
||||
|
||||
@@ -92,6 +92,6 @@ class LookupModule(LookupBase):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
|
||||
hiera = Hiera(self.get_option('config_file'), self.get_option('executable'))
|
||||
hiera = Hiera(self.get_option("config_file"), self.get_option("executable"))
|
||||
ret = [hiera.get(terms)]
|
||||
return ret
|
||||
|
||||
@@ -52,7 +52,6 @@ display = Display()
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
if not HAS_KEYRING:
|
||||
raise AnsibleError("Can't LOOKUP(keyring): missing required python library 'keyring'")
|
||||
|
||||
@@ -51,8 +51,7 @@ class LPassException(AnsibleError):
|
||||
|
||||
|
||||
class LPass:
|
||||
|
||||
def __init__(self, path='lpass'):
|
||||
def __init__(self, path="lpass"):
|
||||
self._cli_path = path
|
||||
|
||||
@property
|
||||
@@ -70,7 +69,7 @@ class LPass:
|
||||
rc = p.wait()
|
||||
if rc != expected_rc:
|
||||
raise LPassException(err)
|
||||
return to_text(out, errors='surrogate_or_strict'), to_text(err, errors='surrogate_or_strict')
|
||||
return to_text(out, errors="surrogate_or_strict"), to_text(err, errors="surrogate_or_strict")
|
||||
|
||||
def _build_args(self, command, args=None):
|
||||
if args is None:
|
||||
@@ -80,7 +79,7 @@ class LPass:
|
||||
return args
|
||||
|
||||
def get_field(self, key, field):
|
||||
if field in ['username', 'password', 'url', 'notes', 'id', 'name']:
|
||||
if field in ["username", "password", "url", "notes", "id", "name"]:
|
||||
out, err = self._run(self._build_args("show", [f"--{field}", key]))
|
||||
else:
|
||||
out, err = self._run(self._build_args("show", [f"--field={field}", key]))
|
||||
@@ -88,10 +87,9 @@ class LPass:
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
field = self.get_option('field')
|
||||
field = self.get_option("field")
|
||||
|
||||
lp = LPass()
|
||||
|
||||
|
||||
@@ -70,9 +70,8 @@ except ImportError:
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
'''
|
||||
"""
|
||||
terms contain any number of keys to be retrieved.
|
||||
If terms is None, all keys from the database are returned
|
||||
with their values, and if term ends in an asterisk, we
|
||||
@@ -83,13 +82,13 @@ class LookupModule(LookupBase):
|
||||
|
||||
vars:
|
||||
- lmdb_kv_db: "jp.mdb"
|
||||
'''
|
||||
"""
|
||||
if HAVE_LMDB is False:
|
||||
raise AnsibleError("Can't LOOKUP(lmdb_kv): this module requires lmdb to be installed")
|
||||
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
|
||||
db = self.get_option('db')
|
||||
db = self.get_option("db")
|
||||
|
||||
try:
|
||||
env = lmdb.open(str(db), readonly=True)
|
||||
@@ -107,7 +106,7 @@ class LookupModule(LookupBase):
|
||||
else:
|
||||
for term in terms:
|
||||
with env.begin() as txn:
|
||||
if term.endswith('*'):
|
||||
if term.endswith("*"):
|
||||
cursor = txn.cursor()
|
||||
prefix = term[:-1] # strip asterisk
|
||||
cursor.set_range(to_text(term).encode())
|
||||
|
||||
@@ -136,9 +136,9 @@ class LookupModule(LookupBase):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(direct=kwargs)
|
||||
initial_value = self.get_option("initial_value", None)
|
||||
self._override = self.get_option('override', 'error')
|
||||
self._pattern_type = self.get_option('pattern_type', 'regex')
|
||||
self._groups = self.get_option('groups', None)
|
||||
self._override = self.get_option("override", "error")
|
||||
self._pattern_type = self.get_option("pattern_type", "regex")
|
||||
self._groups = self.get_option("groups", None)
|
||||
|
||||
ret = []
|
||||
for term in terms:
|
||||
@@ -159,7 +159,7 @@ class LookupModule(LookupBase):
|
||||
return ret
|
||||
|
||||
def _is_host_in_allowed_groups(self, host_groups):
|
||||
if 'all' in self._groups:
|
||||
if "all" in self._groups:
|
||||
return True
|
||||
|
||||
group_intersection = [host_group_name for host_group_name in host_groups if host_group_name in self._groups]
|
||||
@@ -191,7 +191,9 @@ class LookupModule(LookupBase):
|
||||
result = initial_value
|
||||
|
||||
for var_name in var_merge_names:
|
||||
temp_templar = self._templar.copy_with_new_env(available_variables=variables) # tmp. switch renderer to context of current variables
|
||||
temp_templar = self._templar.copy_with_new_env(
|
||||
available_variables=variables
|
||||
) # tmp. switch renderer to context of current variables
|
||||
var_value = temp_templar.template(variables[var_name]) # Render jinja2 templates
|
||||
var_type = _verify_and_get_type(var_value)
|
||||
|
||||
|
||||
@@ -299,11 +299,13 @@ class OnePassCLIv1(OnePassCLIBase):
|
||||
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.")
|
||||
"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.")
|
||||
"1Password CLI version 1 does not support Service Accounts. Please use version 2 or later."
|
||||
)
|
||||
|
||||
required_params = [
|
||||
"subdomain",
|
||||
@@ -338,7 +340,7 @@ class OnePassCLIv1(OnePassCLIBase):
|
||||
return self._run(args)
|
||||
|
||||
def signin(self):
|
||||
self._check_required_params(['master_password'])
|
||||
self._check_required_params(["master_password"])
|
||||
|
||||
args = ["signin", "--raw"]
|
||||
if self.subdomain:
|
||||
@@ -351,6 +353,7 @@ class OnePassCLIv2(OnePassCLIBase):
|
||||
"""
|
||||
CLIv2 Syntax Reference: https://developer.1password.com/docs/cli/upgrade#step-2-update-your-scripts
|
||||
"""
|
||||
|
||||
supports_version = "2"
|
||||
|
||||
def _parse_field(self, data_json, field_name, section_title=None):
|
||||
@@ -533,9 +536,13 @@ class OnePassCLIv2(OnePassCLIBase):
|
||||
self._check_required_params(required_params)
|
||||
|
||||
args = [
|
||||
"account", "add", "--raw",
|
||||
"--address", f"{self.subdomain}.{self.domain}",
|
||||
"--email", to_bytes(self.username),
|
||||
"account",
|
||||
"add",
|
||||
"--raw",
|
||||
"--address",
|
||||
f"{self.subdomain}.{self.domain}",
|
||||
"--email",
|
||||
to_bytes(self.username),
|
||||
"--signin",
|
||||
]
|
||||
|
||||
@@ -574,7 +581,7 @@ class OnePassCLIv2(OnePassCLIBase):
|
||||
return self._add_parameters_and_run(args, vault=vault, token=token)
|
||||
|
||||
def signin(self):
|
||||
self._check_required_params(['master_password'])
|
||||
self._check_required_params(["master_password"])
|
||||
|
||||
args = ["signin", "--raw"]
|
||||
if self.subdomain:
|
||||
@@ -584,8 +591,19 @@ class OnePassCLIv2(OnePassCLIBase):
|
||||
|
||||
|
||||
class OnePass:
|
||||
def __init__(self, subdomain=None, domain="1password.com", username=None, secret_key=None, master_password=None,
|
||||
service_account_token=None, account_id=None, connect_host=None, connect_token=None, cli_class=None):
|
||||
def __init__(
|
||||
self,
|
||||
subdomain=None,
|
||||
domain="1password.com",
|
||||
username=None,
|
||||
secret_key=None,
|
||||
master_password=None,
|
||||
service_account_token=None,
|
||||
account_id=None,
|
||||
connect_host=None,
|
||||
connect_token=None,
|
||||
cli_class=None,
|
||||
):
|
||||
self.subdomain = subdomain
|
||||
self.domain = domain
|
||||
self.username = username
|
||||
@@ -607,15 +625,33 @@ class OnePass:
|
||||
|
||||
def _get_cli_class(self, cli_class=None):
|
||||
if cli_class is not None:
|
||||
return cli_class(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)
|
||||
return cli_class(
|
||||
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,
|
||||
)
|
||||
|
||||
version = OnePassCLIBase.get_current_version()
|
||||
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, self.connect_host, self.connect_token)
|
||||
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)
|
||||
|
||||
@@ -666,7 +702,6 @@ class OnePass:
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ from ansible.plugins.lookup import LookupBase
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
|
||||
|
||||
@@ -76,11 +76,7 @@ class LookupModule(LookupBase):
|
||||
raise AnsibleLookupError(f"No private key found for item {item_id}.")
|
||||
|
||||
if ssh_format:
|
||||
return (
|
||||
private_key_field.get("ssh_formats", {})
|
||||
.get("openssh", {})
|
||||
.get("value", "")
|
||||
)
|
||||
return private_key_field.get("ssh_formats", {}).get("openssh", {}).get("value", "")
|
||||
return private_key_field.get("value", "")
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
@@ -112,7 +108,4 @@ class LookupModule(LookupBase):
|
||||
)
|
||||
op.assert_logged_in()
|
||||
|
||||
return [
|
||||
self.get_ssh_key(op.get_raw(term, vault), term, ssh_format=ssh_format)
|
||||
for term in terms
|
||||
]
|
||||
return [self.get_ssh_key(op.get_raw(term, vault), term, ssh_format=ssh_format) for term in terms]
|
||||
|
||||
@@ -264,16 +264,16 @@ display = Display()
|
||||
# http://stackoverflow.com/questions/10103551/passing-data-to-subprocess-check-output
|
||||
# note: contains special logic for calling 'pass', so not a drop-in replacement for check_output
|
||||
def check_output2(*popenargs, **kwargs):
|
||||
if 'stdout' in kwargs:
|
||||
raise ValueError('stdout argument not allowed, it will be overridden.')
|
||||
if 'stderr' in kwargs:
|
||||
raise ValueError('stderr argument not allowed, it will be overridden.')
|
||||
if 'input' in kwargs:
|
||||
if 'stdin' in kwargs:
|
||||
raise ValueError('stdin and input arguments may not both be used.')
|
||||
b_inputdata = to_bytes(kwargs['input'], errors='surrogate_or_strict')
|
||||
del kwargs['input']
|
||||
kwargs['stdin'] = subprocess.PIPE
|
||||
if "stdout" in kwargs:
|
||||
raise ValueError("stdout argument not allowed, it will be overridden.")
|
||||
if "stderr" in kwargs:
|
||||
raise ValueError("stderr argument not allowed, it will be overridden.")
|
||||
if "input" in kwargs:
|
||||
if "stdin" in kwargs:
|
||||
raise ValueError("stdin and input arguments may not both be used.")
|
||||
b_inputdata = to_bytes(kwargs["input"], errors="surrogate_or_strict")
|
||||
del kwargs["input"]
|
||||
kwargs["stdin"] = subprocess.PIPE
|
||||
else:
|
||||
b_inputdata = None
|
||||
process = subprocess.Popen(*popenargs, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
|
||||
@@ -284,24 +284,20 @@ def check_output2(*popenargs, **kwargs):
|
||||
process.wait()
|
||||
raise
|
||||
retcode = process.poll()
|
||||
if retcode == 0 and (b'encryption failed: Unusable public key' in b_out or
|
||||
b'encryption failed: Unusable public key' in b_err):
|
||||
if retcode == 0 and (
|
||||
b"encryption failed: Unusable public key" in b_out or b"encryption failed: Unusable public key" in b_err
|
||||
):
|
||||
retcode = 78 # os.EX_CONFIG
|
||||
if retcode != 0:
|
||||
cmd = kwargs.get("args")
|
||||
if cmd is None:
|
||||
cmd = popenargs[0]
|
||||
raise subprocess.CalledProcessError(
|
||||
retcode,
|
||||
cmd,
|
||||
to_native(b_out + b_err, errors='surrogate_or_strict')
|
||||
)
|
||||
raise subprocess.CalledProcessError(retcode, cmd, to_native(b_out + b_err, errors="surrogate_or_strict"))
|
||||
return b_out
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
def __init__(self, loader=None, templar=None, **kwargs):
|
||||
|
||||
super().__init__(loader, templar, **kwargs)
|
||||
self.realpass = None
|
||||
|
||||
@@ -309,12 +305,11 @@ class LookupModule(LookupBase):
|
||||
if self.realpass is None:
|
||||
try:
|
||||
passoutput = to_text(
|
||||
check_output2([self.pass_cmd, "--version"], env=self.env),
|
||||
errors='surrogate_or_strict'
|
||||
check_output2([self.pass_cmd, "--version"], env=self.env), errors="surrogate_or_strict"
|
||||
)
|
||||
self.realpass = 'pass: the standard unix password manager' in passoutput
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
raise AnsibleError(f'exit code {e.returncode} while running {e.cmd}. Error output: {e.output}')
|
||||
self.realpass = "pass: the standard unix password manager" in passoutput
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise AnsibleError(f"exit code {e.returncode} while running {e.cmd}. Error output: {e.output}")
|
||||
|
||||
return self.realpass
|
||||
|
||||
@@ -329,96 +324,96 @@ class LookupModule(LookupBase):
|
||||
# next parse the optional parameters in keyvalue pairs
|
||||
try:
|
||||
for param in params[1:]:
|
||||
name, value = param.split('=', 1)
|
||||
name, value = param.split("=", 1)
|
||||
if name not in self.paramvals:
|
||||
raise AnsibleAssertionError(f'{name} not in paramvals')
|
||||
raise AnsibleAssertionError(f"{name} not in paramvals")
|
||||
self.paramvals[name] = value
|
||||
except (ValueError, AssertionError) as e:
|
||||
raise AnsibleError(e)
|
||||
# check and convert values
|
||||
try:
|
||||
for key in ['create', 'returnall', 'overwrite', 'backup', 'nosymbols']:
|
||||
for key in ["create", "returnall", "overwrite", "backup", "nosymbols"]:
|
||||
if not isinstance(self.paramvals[key], bool):
|
||||
self.paramvals[key] = boolean(self.paramvals[key])
|
||||
except (ValueError, AssertionError) as e:
|
||||
raise AnsibleError(e)
|
||||
if self.paramvals['missing'] not in ['error', 'warn', 'create', 'empty']:
|
||||
if self.paramvals["missing"] not in ["error", "warn", "create", "empty"]:
|
||||
raise AnsibleError(f"{self.paramvals['missing']} is not a valid option for missing")
|
||||
if not isinstance(self.paramvals['length'], int):
|
||||
if self.paramvals['length'].isdigit():
|
||||
self.paramvals['length'] = int(self.paramvals['length'])
|
||||
if not isinstance(self.paramvals["length"], int):
|
||||
if self.paramvals["length"].isdigit():
|
||||
self.paramvals["length"] = int(self.paramvals["length"])
|
||||
else:
|
||||
raise AnsibleError(f"{self.paramvals['length']} is not a correct value for length")
|
||||
|
||||
if self.paramvals['create']:
|
||||
self.paramvals['missing'] = 'create'
|
||||
if self.paramvals["create"]:
|
||||
self.paramvals["missing"] = "create"
|
||||
|
||||
# Collect pass environment variables from the plugin's parameters.
|
||||
self.env = os.environ.copy()
|
||||
self.env['LANGUAGE'] = 'C' # make sure to get errors in English as required by check_output2
|
||||
self.env["LANGUAGE"] = "C" # make sure to get errors in English as required by check_output2
|
||||
|
||||
if self.backend == 'gopass':
|
||||
self.env['GOPASS_NO_REMINDER'] = "YES"
|
||||
elif os.path.isdir(self.paramvals['directory']):
|
||||
if self.backend == "gopass":
|
||||
self.env["GOPASS_NO_REMINDER"] = "YES"
|
||||
elif os.path.isdir(self.paramvals["directory"]):
|
||||
# Set PASSWORD_STORE_DIR
|
||||
self.env['PASSWORD_STORE_DIR'] = self.paramvals['directory']
|
||||
self.env["PASSWORD_STORE_DIR"] = self.paramvals["directory"]
|
||||
elif self.is_real_pass():
|
||||
raise AnsibleError(f"Passwordstore directory '{self.paramvals['directory']}' does not exist")
|
||||
|
||||
# Set PASSWORD_STORE_UMASK if umask is set
|
||||
if self.paramvals.get('umask') is not None:
|
||||
if len(self.paramvals['umask']) != 3:
|
||||
raise AnsibleError('Passwordstore umask must have a length of 3.')
|
||||
elif int(self.paramvals['umask'][0]) > 3:
|
||||
raise AnsibleError('Passwordstore umask not allowed (password not user readable).')
|
||||
if self.paramvals.get("umask") is not None:
|
||||
if len(self.paramvals["umask"]) != 3:
|
||||
raise AnsibleError("Passwordstore umask must have a length of 3.")
|
||||
elif int(self.paramvals["umask"][0]) > 3:
|
||||
raise AnsibleError("Passwordstore umask not allowed (password not user readable).")
|
||||
else:
|
||||
self.env['PASSWORD_STORE_UMASK'] = self.paramvals['umask']
|
||||
self.env["PASSWORD_STORE_UMASK"] = self.paramvals["umask"]
|
||||
|
||||
def check_pass(self):
|
||||
try:
|
||||
self.passoutput = to_text(
|
||||
check_output2([self.pass_cmd, 'show'] +
|
||||
[self.passname], env=self.env),
|
||||
errors='surrogate_or_strict'
|
||||
check_output2([self.pass_cmd, "show"] + [self.passname], env=self.env), errors="surrogate_or_strict"
|
||||
).splitlines()
|
||||
self.password = self.passoutput[0]
|
||||
self.passdict = {}
|
||||
try:
|
||||
values = yaml.safe_load('\n'.join(self.passoutput[1:]))
|
||||
values = yaml.safe_load("\n".join(self.passoutput[1:]))
|
||||
for key, item in values.items():
|
||||
self.passdict[key] = item
|
||||
except (yaml.YAMLError, AttributeError):
|
||||
for line in self.passoutput[1:]:
|
||||
if ':' in line:
|
||||
name, value = line.split(':', 1)
|
||||
if ":" in line:
|
||||
name, value = line.split(":", 1)
|
||||
self.passdict[name.strip()] = value.strip()
|
||||
if (self.backend == 'gopass' or
|
||||
os.path.isfile(os.path.join(self.paramvals['directory'], f"{self.passname}.gpg"))
|
||||
or not self.is_real_pass()):
|
||||
if (
|
||||
self.backend == "gopass"
|
||||
or os.path.isfile(os.path.join(self.paramvals["directory"], f"{self.passname}.gpg"))
|
||||
or not self.is_real_pass()
|
||||
):
|
||||
# When using real pass, only accept password as found if there is a .gpg file for it (might be a tree node otherwise)
|
||||
return True
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
except subprocess.CalledProcessError as e:
|
||||
# 'not in password store' is the expected error if a password wasn't found
|
||||
if 'not in the password store' not in e.output:
|
||||
raise AnsibleError(f'exit code {e.returncode} while running {e.cmd}. Error output: {e.output}')
|
||||
if "not in the password store" not in e.output:
|
||||
raise AnsibleError(f"exit code {e.returncode} while running {e.cmd}. Error output: {e.output}")
|
||||
|
||||
if self.paramvals['missing'] == 'error':
|
||||
raise AnsibleError(f'passwordstore: passname {self.passname} not found and missing=error is set')
|
||||
elif self.paramvals['missing'] == 'warn':
|
||||
display.warning(f'passwordstore: passname {self.passname} not found')
|
||||
if self.paramvals["missing"] == "error":
|
||||
raise AnsibleError(f"passwordstore: passname {self.passname} not found and missing=error is set")
|
||||
elif self.paramvals["missing"] == "warn":
|
||||
display.warning(f"passwordstore: passname {self.passname} not found")
|
||||
|
||||
return False
|
||||
|
||||
def get_newpass(self):
|
||||
if self.paramvals['nosymbols']:
|
||||
if self.paramvals["nosymbols"]:
|
||||
chars = C.DEFAULT_PASSWORD_CHARS[:62]
|
||||
else:
|
||||
chars = C.DEFAULT_PASSWORD_CHARS
|
||||
|
||||
if self.paramvals['userpass']:
|
||||
newpass = self.paramvals['userpass']
|
||||
if self.paramvals["userpass"]:
|
||||
newpass = self.paramvals["userpass"]
|
||||
else:
|
||||
newpass = random_password(length=self.paramvals['length'], chars=chars)
|
||||
newpass = random_password(length=self.paramvals["length"], chars=chars)
|
||||
return newpass
|
||||
|
||||
def update_password(self):
|
||||
@@ -429,7 +424,6 @@ class LookupModule(LookupBase):
|
||||
subkey = self.paramvals["subkey"]
|
||||
|
||||
if subkey != "password":
|
||||
|
||||
msg_lines = []
|
||||
subkey_exists = False
|
||||
subkey_line = f"{subkey}: {newpass}"
|
||||
@@ -447,27 +441,25 @@ class LookupModule(LookupBase):
|
||||
msg_lines.insert(2, subkey_line)
|
||||
|
||||
if self.paramvals["timestamp"] and self.paramvals["backup"] and oldpass and oldpass != newpass:
|
||||
msg_lines.append(
|
||||
f"lookup_pass: old subkey '{subkey}' password was {oldpass} (Updated on {datetime})\n"
|
||||
)
|
||||
msg_lines.append(f"lookup_pass: old subkey '{subkey}' password was {oldpass} (Updated on {datetime})\n")
|
||||
|
||||
msg = os.linesep.join(msg_lines)
|
||||
|
||||
else:
|
||||
msg = newpass
|
||||
|
||||
if self.paramvals['preserve'] or self.paramvals['timestamp']:
|
||||
msg += '\n'
|
||||
if self.paramvals['preserve'] and self.passoutput[1:]:
|
||||
msg += '\n'.join(self.passoutput[1:])
|
||||
msg += '\n'
|
||||
if self.paramvals['timestamp'] and self.paramvals['backup']:
|
||||
if self.paramvals["preserve"] or self.paramvals["timestamp"]:
|
||||
msg += "\n"
|
||||
if self.paramvals["preserve"] and self.passoutput[1:]:
|
||||
msg += "\n".join(self.passoutput[1:])
|
||||
msg += "\n"
|
||||
if self.paramvals["timestamp"] and self.paramvals["backup"]:
|
||||
msg += f"lookup_pass: old password was {self.password} (Updated on {datetime})\n"
|
||||
|
||||
try:
|
||||
check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
raise AnsibleError(f'exit code {e.returncode} while running {e.cmd}. Error output: {e.output}')
|
||||
check_output2([self.pass_cmd, "insert", "-f", "-m", self.passname], input=msg, env=self.env)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise AnsibleError(f"exit code {e.returncode} while running {e.cmd}. Error output: {e.output}")
|
||||
return newpass
|
||||
|
||||
def generate_password(self):
|
||||
@@ -482,24 +474,24 @@ class LookupModule(LookupBase):
|
||||
else:
|
||||
msg = newpass
|
||||
|
||||
if self.paramvals['timestamp']:
|
||||
if self.paramvals["timestamp"]:
|
||||
msg += f"\nlookup_pass: First generated by ansible on {datetime}\n"
|
||||
|
||||
try:
|
||||
check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
raise AnsibleError(f'exit code {e.returncode} while running {e.cmd}. Error output: {e.output}')
|
||||
check_output2([self.pass_cmd, "insert", "-f", "-m", self.passname], input=msg, env=self.env)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise AnsibleError(f"exit code {e.returncode} while running {e.cmd}. Error output: {e.output}")
|
||||
|
||||
return newpass
|
||||
|
||||
def get_passresult(self):
|
||||
if self.paramvals['returnall']:
|
||||
if self.paramvals["returnall"]:
|
||||
return os.linesep.join(self.passoutput)
|
||||
if self.paramvals['subkey'] == 'password':
|
||||
if self.paramvals["subkey"] == "password":
|
||||
return self.password
|
||||
else:
|
||||
if self.paramvals['subkey'] in self.passdict:
|
||||
return self.passdict[self.paramvals['subkey']]
|
||||
if self.paramvals["subkey"] in self.passdict:
|
||||
return self.passdict[self.paramvals["subkey"]]
|
||||
else:
|
||||
if self.paramvals["missing_subkey"] == "error":
|
||||
raise AnsibleError(
|
||||
@@ -515,10 +507,10 @@ class LookupModule(LookupBase):
|
||||
|
||||
@contextmanager
|
||||
def opt_lock(self, type):
|
||||
if self.get_option('lock') == type:
|
||||
tmpdir = os.environ.get('TMPDIR', '/tmp')
|
||||
user = os.environ.get('USER')
|
||||
lockfile = os.path.join(tmpdir, f'.{user}.passwordstore.lock')
|
||||
if self.get_option("lock") == type:
|
||||
tmpdir = os.environ.get("TMPDIR", "/tmp")
|
||||
user = os.environ.get("USER")
|
||||
lockfile = os.path.join(tmpdir, f".{user}.passwordstore.lock")
|
||||
with FileLock().lock_file(lockfile, tmpdir, self.lock_timeout):
|
||||
self.locked = type
|
||||
yield
|
||||
@@ -527,40 +519,40 @@ class LookupModule(LookupBase):
|
||||
yield
|
||||
|
||||
def setup(self, variables):
|
||||
self.backend = self.get_option('backend')
|
||||
self.backend = self.get_option("backend")
|
||||
self.pass_cmd = self.backend # pass and gopass are commands as well
|
||||
self.locked = None
|
||||
timeout = self.get_option('locktimeout')
|
||||
if not re.match('^[0-9]+[smh]$', timeout):
|
||||
timeout = self.get_option("locktimeout")
|
||||
if not re.match("^[0-9]+[smh]$", timeout):
|
||||
raise AnsibleError(f"{timeout} is not a correct value for locktimeout")
|
||||
unit_to_seconds = {"s": 1, "m": 60, "h": 3600}
|
||||
self.lock_timeout = int(timeout[:-1]) * unit_to_seconds[timeout[-1]]
|
||||
|
||||
directory = self.get_option('directory')
|
||||
directory = self.get_option("directory")
|
||||
if directory is None:
|
||||
if self.backend == 'gopass':
|
||||
if self.backend == "gopass":
|
||||
try:
|
||||
with open(os.path.expanduser('~/.config/gopass/config.yml')) as f:
|
||||
directory = yaml.safe_load(f)['path']
|
||||
with open(os.path.expanduser("~/.config/gopass/config.yml")) as f:
|
||||
directory = yaml.safe_load(f)["path"]
|
||||
except (FileNotFoundError, KeyError, yaml.YAMLError):
|
||||
directory = os.path.expanduser('~/.local/share/gopass/stores/root')
|
||||
directory = os.path.expanduser("~/.local/share/gopass/stores/root")
|
||||
else:
|
||||
directory = os.path.expanduser('~/.password-store')
|
||||
directory = os.path.expanduser("~/.password-store")
|
||||
|
||||
self.paramvals = {
|
||||
'subkey': self.get_option('subkey'),
|
||||
'directory': directory,
|
||||
'create': self.get_option('create'),
|
||||
'returnall': self.get_option('returnall'),
|
||||
'overwrite': self.get_option('overwrite'),
|
||||
'nosymbols': self.get_option('nosymbols'),
|
||||
'userpass': self.get_option('userpass') or '',
|
||||
'length': self.get_option('length'),
|
||||
'backup': self.get_option('backup'),
|
||||
'missing': self.get_option('missing'),
|
||||
'umask': self.get_option('umask'),
|
||||
'timestamp': self.get_option('timestamp'),
|
||||
'preserve': self.get_option('preserve'),
|
||||
"subkey": self.get_option("subkey"),
|
||||
"directory": directory,
|
||||
"create": self.get_option("create"),
|
||||
"returnall": self.get_option("returnall"),
|
||||
"overwrite": self.get_option("overwrite"),
|
||||
"nosymbols": self.get_option("nosymbols"),
|
||||
"userpass": self.get_option("userpass") or "",
|
||||
"length": self.get_option("length"),
|
||||
"backup": self.get_option("backup"),
|
||||
"missing": self.get_option("missing"),
|
||||
"umask": self.get_option("umask"),
|
||||
"timestamp": self.get_option("timestamp"),
|
||||
"preserve": self.get_option("preserve"),
|
||||
"missing_subkey": self.get_option("missing_subkey"),
|
||||
}
|
||||
|
||||
@@ -570,25 +562,27 @@ class LookupModule(LookupBase):
|
||||
result = []
|
||||
|
||||
for term in terms:
|
||||
self.parse_params(term) # parse the input into paramvals
|
||||
with self.opt_lock('readwrite'):
|
||||
if self.check_pass(): # password file exists
|
||||
if self.paramvals['overwrite']: # if "overwrite", always update password
|
||||
with self.opt_lock('write'):
|
||||
self.parse_params(term) # parse the input into paramvals
|
||||
with self.opt_lock("readwrite"):
|
||||
if self.check_pass(): # password file exists
|
||||
if self.paramvals["overwrite"]: # if "overwrite", always update password
|
||||
with self.opt_lock("write"):
|
||||
result.append(self.update_password())
|
||||
elif (
|
||||
self.paramvals["subkey"] != "password"
|
||||
and not self.passdict.get(self.paramvals["subkey"])
|
||||
and self.paramvals["missing"] == "create"
|
||||
): # target is a subkey, this subkey is not in passdict BUT missing == create
|
||||
with self.opt_lock('write'):
|
||||
with self.opt_lock("write"):
|
||||
result.append(self.update_password())
|
||||
else:
|
||||
result.append(self.get_passresult())
|
||||
else: # password does not exist
|
||||
if self.paramvals['missing'] == 'create':
|
||||
with self.opt_lock('write'):
|
||||
if self.locked == 'write' and self.check_pass(): # lookup password again if under write lock
|
||||
if self.paramvals["missing"] == "create":
|
||||
with self.opt_lock("write"):
|
||||
if (
|
||||
self.locked == "write" and self.check_pass()
|
||||
): # lookup password again if under write lock
|
||||
result.append(self.get_passresult())
|
||||
else:
|
||||
result.append(self.generate_password())
|
||||
|
||||
@@ -78,18 +78,15 @@ from ansible.plugins.lookup import LookupBase
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
|
||||
if not HAS_PETNAME:
|
||||
raise AnsibleError('Python petname library is required. '
|
||||
'Please install using "pip install petname"')
|
||||
raise AnsibleError('Python petname library is required. Please install using "pip install petname"')
|
||||
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
words = self.get_option('words')
|
||||
length = self.get_option('length')
|
||||
prefix = self.get_option('prefix')
|
||||
separator = self.get_option('separator')
|
||||
words = self.get_option("words")
|
||||
length = self.get_option("length")
|
||||
prefix = self.get_option("prefix")
|
||||
separator = self.get_option("separator")
|
||||
|
||||
values = petname.Generate(words=words, separator=separator, letters=length)
|
||||
if prefix:
|
||||
|
||||
@@ -179,18 +179,12 @@ class LookupModule(LookupBase):
|
||||
@staticmethod
|
||||
def get_random(random_generator, chars, length):
|
||||
if not chars:
|
||||
raise AnsibleLookupError(
|
||||
"Available characters cannot be None, please change constraints"
|
||||
)
|
||||
raise AnsibleLookupError("Available characters cannot be None, please change constraints")
|
||||
return "".join(random_generator.choice(chars) for dummy in range(length))
|
||||
|
||||
@staticmethod
|
||||
def b64encode(string_value, encoding="utf-8"):
|
||||
return to_text(
|
||||
base64.b64encode(
|
||||
to_bytes(string_value, encoding=encoding, errors="surrogate_or_strict")
|
||||
)
|
||||
)
|
||||
return to_text(base64.b64encode(to_bytes(string_value, encoding=encoding, errors="surrogate_or_strict")))
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
number_chars = string.digits
|
||||
|
||||
@@ -92,12 +92,8 @@ class LookupModule(LookupBase):
|
||||
"""The random_words Ansible lookup class."""
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
|
||||
if not HAS_XKCDPASS:
|
||||
raise AnsibleLookupError(
|
||||
"Python xkcdpass library is required. "
|
||||
'Please install using "pip install xkcdpass"'
|
||||
)
|
||||
raise AnsibleLookupError('Python xkcdpass library is required. Please install using "pip install xkcdpass"')
|
||||
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
method = self.get_option("case")
|
||||
@@ -107,12 +103,8 @@ class LookupModule(LookupBase):
|
||||
numwords = self.get_option("numwords")
|
||||
|
||||
words = xp.locate_wordfile()
|
||||
wordlist = xp.generate_wordlist(
|
||||
max_length=max_length, min_length=min_length, wordfile=words
|
||||
)
|
||||
wordlist = xp.generate_wordlist(max_length=max_length, min_length=min_length, wordfile=words)
|
||||
|
||||
values = xp.generate_xkcdpassword(
|
||||
wordlist, case=method, delimiter=delimiter, numwords=numwords
|
||||
)
|
||||
values = xp.generate_xkcdpassword(wordlist, case=method, delimiter=delimiter, numwords=numwords)
|
||||
|
||||
return [values]
|
||||
|
||||
@@ -76,6 +76,7 @@ _raw:
|
||||
HAVE_REDIS = False
|
||||
try:
|
||||
import redis
|
||||
|
||||
HAVE_REDIS = True
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -86,9 +87,7 @@ from ansible.plugins.lookup import LookupBase
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables, **kwargs):
|
||||
|
||||
if not HAVE_REDIS:
|
||||
raise AnsibleError("Can't LOOKUP(redis_kv): module redis is not installed")
|
||||
|
||||
@@ -96,9 +95,9 @@ class LookupModule(LookupBase):
|
||||
self.set_options(direct=kwargs)
|
||||
|
||||
# setup connection
|
||||
host = self.get_option('host')
|
||||
port = self.get_option('port')
|
||||
socket = self.get_option('socket')
|
||||
host = self.get_option("host")
|
||||
port = self.get_option("port")
|
||||
socket = self.get_option("socket")
|
||||
if socket is None:
|
||||
conn = redis.Redis(host=host, port=port)
|
||||
else:
|
||||
@@ -113,5 +112,5 @@ class LookupModule(LookupBase):
|
||||
ret.append(to_text(res))
|
||||
except Exception as e:
|
||||
# connection failed or key not found
|
||||
raise AnsibleError(f'Encountered exception while fetching {term}: {e}')
|
||||
raise AnsibleError(f"Encountered exception while fetching {term}: {e}")
|
||||
return ret
|
||||
|
||||
@@ -78,19 +78,18 @@ display = Display()
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
@staticmethod
|
||||
def Client(server_parameters):
|
||||
return SecretServer(**server_parameters)
|
||||
|
||||
def run(self, terms, variables, **kwargs):
|
||||
if ANOTHER_LIBRARY_IMPORT_ERROR:
|
||||
raise AnsibleError('revbits_ansible must be installed to use this plugin') from ANOTHER_LIBRARY_IMPORT_ERROR
|
||||
raise AnsibleError("revbits_ansible must be installed to use this plugin") from ANOTHER_LIBRARY_IMPORT_ERROR
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
secret_server = LookupModule.Client(
|
||||
{
|
||||
"base_url": self.get_option('base_url'),
|
||||
"api_key": self.get_option('api_key'),
|
||||
"base_url": self.get_option("base_url"),
|
||||
"api_key": self.get_option("api_key"),
|
||||
}
|
||||
)
|
||||
result = []
|
||||
|
||||
@@ -46,7 +46,6 @@ from ansible.module_utils.common.text.converters import to_bytes, to_text
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def read_shelve(self, shelve_filename, key):
|
||||
"""
|
||||
Read the value of "key" from a shelve file
|
||||
@@ -68,19 +67,19 @@ class LookupModule(LookupBase):
|
||||
|
||||
try:
|
||||
for param in params:
|
||||
name, value = param.split('=')
|
||||
name, value = param.split("=")
|
||||
if name not in paramvals:
|
||||
raise AnsibleAssertionError(f'{name} not in paramvals')
|
||||
raise AnsibleAssertionError(f"{name} not in paramvals")
|
||||
paramvals[name] = value
|
||||
|
||||
except (ValueError, AssertionError) as e:
|
||||
# In case "file" or "key" are not present
|
||||
raise AnsibleError(e)
|
||||
|
||||
key = paramvals['key']
|
||||
key = paramvals["key"]
|
||||
|
||||
# Search also in the role/files directory and in the playbook directory
|
||||
shelvefile = self.find_file_in_search_path(variables, 'files', paramvals['file'])
|
||||
shelvefile = self.find_file_in_search_path(variables, "files", paramvals["file"])
|
||||
|
||||
if shelvefile:
|
||||
res = self.read_shelve(shelvefile, key)
|
||||
|
||||
@@ -260,14 +260,26 @@ from ansible.plugins.lookup import LookupBase
|
||||
from ansible.utils.display import Display
|
||||
|
||||
try:
|
||||
from delinea.secrets.server import SecretServer, SecretServerError, PasswordGrantAuthorizer, DomainPasswordGrantAuthorizer, AccessTokenAuthorizer
|
||||
from delinea.secrets.server import (
|
||||
SecretServer,
|
||||
SecretServerError,
|
||||
PasswordGrantAuthorizer,
|
||||
DomainPasswordGrantAuthorizer,
|
||||
AccessTokenAuthorizer,
|
||||
)
|
||||
|
||||
HAS_TSS_SDK = True
|
||||
HAS_DELINEA_SS_SDK = True
|
||||
HAS_TSS_AUTHORIZER = True
|
||||
except ImportError:
|
||||
try:
|
||||
from thycotic.secrets.server import SecretServer, SecretServerError, PasswordGrantAuthorizer, DomainPasswordGrantAuthorizer, AccessTokenAuthorizer
|
||||
from thycotic.secrets.server import (
|
||||
SecretServer,
|
||||
SecretServerError,
|
||||
PasswordGrantAuthorizer,
|
||||
DomainPasswordGrantAuthorizer,
|
||||
AccessTokenAuthorizer,
|
||||
)
|
||||
|
||||
HAS_TSS_SDK = True
|
||||
HAS_DELINEA_SS_SDK = False
|
||||
@@ -312,11 +324,11 @@ class TSSClient(metaclass=abc.ABCMeta): # noqa: B024
|
||||
obj = self._client.get_secret_by_path(secret_path, fetch_file_attachments)
|
||||
else:
|
||||
obj = self._client.get_secret(secret_id, fetch_file_attachments)
|
||||
for i in obj['items']:
|
||||
for i in obj["items"]:
|
||||
if file_download_path and os.path.isdir(file_download_path):
|
||||
if i['isFile']:
|
||||
if i["isFile"]:
|
||||
try:
|
||||
file_content = i['itemValue'].content
|
||||
file_content = i["itemValue"].content
|
||||
with open(os.path.join(file_download_path, f"{obj['id']}_{i['slug']}"), "wb") as f:
|
||||
f.write(file_content)
|
||||
except ValueError:
|
||||
@@ -324,7 +336,7 @@ class TSSClient(metaclass=abc.ABCMeta): # noqa: B024
|
||||
except AttributeError:
|
||||
display.warning(f"Could not read file content for {i['slug']}")
|
||||
finally:
|
||||
i['itemValue'] = "*** Not Valid For Display ***"
|
||||
i["itemValue"] = "*** Not Valid For Display ***"
|
||||
else:
|
||||
raise AnsibleOptionsError("File download path does not exist")
|
||||
return obj
|
||||
@@ -377,9 +389,7 @@ class TSSClientV1(TSSClient):
|
||||
super().__init__()
|
||||
|
||||
authorizer = self._get_authorizer(**server_parameters)
|
||||
self._client = SecretServer(
|
||||
server_parameters["base_url"], authorizer, server_parameters["api_path_uri"]
|
||||
)
|
||||
self._client = SecretServer(server_parameters["base_url"], authorizer, server_parameters["api_path_uri"])
|
||||
|
||||
@staticmethod
|
||||
def _get_authorizer(**server_parameters):
|
||||
|
||||
Reference in New Issue
Block a user