mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
ldap_search: make sure output is always UTF-8 (by allowing to Base64 encode specific values, and force-converting everything else) (#6475)
* Simplify code. * Make sure output is always UTF-8.
This commit is contained in:
parent
57cfd1b46d
commit
be1a905f6f
2 changed files with 55 additions and 7 deletions
4
changelogs/fragments/6473-ldap_search.yml
Normal file
4
changelogs/fragments/6473-ldap_search.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
breaking_changes:
|
||||||
|
- "ldap_search - convert all string-like values to UTF-8 (https://github.com/ansible-collections/community.general/issues/5704, https://github.com/ansible-collections/community.general/pull/6473)."
|
||||||
|
minor_changes:
|
||||||
|
- "ldap_search - the new ``base64_attributes`` allows to specify which attribute values should be Base64 encoded (https://github.com/ansible-collections/community.general/pull/6473)."
|
|
@ -61,6 +61,17 @@ options:
|
||||||
description:
|
description:
|
||||||
- Set to C(true) to return the full attribute schema of entries, not
|
- Set to C(true) to return the full attribute schema of entries, not
|
||||||
their attribute values. Overrides I(attrs) when provided.
|
their attribute values. Overrides I(attrs) when provided.
|
||||||
|
base64_attributes:
|
||||||
|
description:
|
||||||
|
- If provided, all attribute values returned that are listed in this option
|
||||||
|
will be Base64 encoded.
|
||||||
|
- If the special value C(*) appears in this list, all attributes will be
|
||||||
|
Base64 encoded.
|
||||||
|
- All other attribute values will be converted to UTF-8 strings. If they
|
||||||
|
contain binary data, please note that invalid UTF-8 bytes will be omitted.
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
version_added: 7.0.0
|
||||||
extends_documentation_fragment:
|
extends_documentation_fragment:
|
||||||
- community.general.ldap.documentation
|
- community.general.ldap.documentation
|
||||||
- community.general.attributes
|
- community.general.attributes
|
||||||
|
@ -81,10 +92,27 @@ EXAMPLES = r"""
|
||||||
register: ldap_group_gids
|
register: ldap_group_gids
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
RESULTS = """
|
||||||
|
results:
|
||||||
|
description:
|
||||||
|
- For every entry found, one dictionary will be returned.
|
||||||
|
- Every dictionary contains a key C(dn) with the entry's DN as a value.
|
||||||
|
- Every attribute of the entry found is added to the dictionary. If the key
|
||||||
|
has precisely one value, that value is taken directly, otherwise the key's
|
||||||
|
value is a list.
|
||||||
|
- Note that all values (for single-element lists) and list elements (for multi-valued
|
||||||
|
lists) will be UTF-8 strings. Some might contain Base64-encoded binary data; which
|
||||||
|
ones is determined by the I(base64_attributes) option.
|
||||||
|
type: list
|
||||||
|
elements: dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
import base64
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||||
from ansible.module_utils.common.text.converters import to_native
|
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
||||||
|
from ansible.module_utils.six import string_types, text_type
|
||||||
from ansible_collections.community.general.plugins.module_utils.ldap import LdapGeneric, gen_specs
|
from ansible_collections.community.general.plugins.module_utils.ldap import LdapGeneric, gen_specs
|
||||||
|
|
||||||
LDAP_IMP_ERR = None
|
LDAP_IMP_ERR = None
|
||||||
|
@ -105,6 +133,7 @@ def main():
|
||||||
filter=dict(type='str', default='(objectClass=*)'),
|
filter=dict(type='str', default='(objectClass=*)'),
|
||||||
attrs=dict(type='list', elements='str'),
|
attrs=dict(type='list', elements='str'),
|
||||||
schema=dict(type='bool', default=False),
|
schema=dict(type='bool', default=False),
|
||||||
|
base64_attributes=dict(type='list', elements='str'),
|
||||||
),
|
),
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
)
|
)
|
||||||
|
@ -118,16 +147,30 @@ def main():
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
module.fail_json(msg="Attribute action failed.", details=to_native(exception))
|
module.fail_json(msg="Attribute action failed.", details=to_native(exception))
|
||||||
|
|
||||||
module.exit_json(changed=False)
|
|
||||||
|
def _normalize_string(val, convert_to_base64):
|
||||||
|
if isinstance(val, string_types):
|
||||||
|
if isinstance(val, text_type):
|
||||||
|
val = to_bytes(val, encoding='utf-8')
|
||||||
|
if convert_to_base64:
|
||||||
|
val = to_text(base64.b64encode(val))
|
||||||
|
else:
|
||||||
|
# See https://github.com/ansible/ansible/issues/80258#issuecomment-1477038952 for details.
|
||||||
|
# We want to make sure that all strings are properly UTF-8 encoded, even if they were not,
|
||||||
|
# or happened to be byte strings.
|
||||||
|
val = to_text(val, 'utf-8', errors='replace')
|
||||||
|
# See also https://github.com/ansible-collections/community.general/issues/5704.
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
def _extract_entry(dn, attrs):
|
def _extract_entry(dn, attrs, base64_attributes):
|
||||||
extracted = {'dn': dn}
|
extracted = {'dn': dn}
|
||||||
for attr, val in list(attrs.items()):
|
for attr, val in list(attrs.items()):
|
||||||
|
convert_to_base64 = '*' in base64_attributes or attr in base64_attributes
|
||||||
if len(val) == 1:
|
if len(val) == 1:
|
||||||
extracted[attr] = val[0]
|
extracted[attr] = _normalize_string(val[0], convert_to_base64)
|
||||||
else:
|
else:
|
||||||
extracted[attr] = val
|
extracted[attr] = [_normalize_string(v, convert_to_base64) for v in val]
|
||||||
return extracted
|
return extracted
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,9 +183,10 @@ class LdapSearch(LdapGeneric):
|
||||||
self._load_scope()
|
self._load_scope()
|
||||||
self._load_attrs()
|
self._load_attrs()
|
||||||
self._load_schema()
|
self._load_schema()
|
||||||
|
self._base64_attributes = set(self.module.params['base64_attributes'] or [])
|
||||||
|
|
||||||
def _load_schema(self):
|
def _load_schema(self):
|
||||||
self.schema = self.module.boolean(self.module.params['schema'])
|
self.schema = self.module.params['schema']
|
||||||
if self.schema:
|
if self.schema:
|
||||||
self.attrsonly = 1
|
self.attrsonly = 1
|
||||||
else:
|
else:
|
||||||
|
@ -179,7 +223,7 @@ class LdapSearch(LdapGeneric):
|
||||||
if self.schema:
|
if self.schema:
|
||||||
ldap_entries.append(dict(dn=result[0], attrs=list(result[1].keys())))
|
ldap_entries.append(dict(dn=result[0], attrs=list(result[1].keys())))
|
||||||
else:
|
else:
|
||||||
ldap_entries.append(_extract_entry(result[0], result[1]))
|
ldap_entries.append(_extract_entry(result[0], result[1], self._base64_attributes))
|
||||||
return ldap_entries
|
return ldap_entries
|
||||||
except ldap.NO_SUCH_OBJECT:
|
except ldap.NO_SUCH_OBJECT:
|
||||||
self.module.fail_json(msg="Base not found: {0}".format(self.dn))
|
self.module.fail_json(msg="Base not found: {0}".format(self.dn))
|
||||||
|
|
Loading…
Reference in a new issue