1
0
Fork 0
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:
Felix Fontein 2023-05-05 07:56:48 +02:00 committed by GitHub
parent 57cfd1b46d
commit be1a905f6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 55 additions and 7 deletions

View 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)."

View file

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