From c54ff7de4110a158efbeb9a4cafaf8f72d3e59c5 Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Fri, 11 Apr 2025 09:08:02 -0700 Subject: [PATCH] Filter: add support for URL-safe encoding/decoding in b64* (#84949) * Added support for URL-safe decoding into b64decode * Added support for URL-safe encoding into b64encode Fixes: #84147 Signed-off-by: Abhijeet Kasurde --- .../fragments/url_safe_b64_encode_decode.yml | 3 +++ lib/ansible/plugins/filter/b64decode.yml | 12 ++++++++++++ lib/ansible/plugins/filter/b64encode.yml | 12 ++++++++++++ lib/ansible/plugins/filter/core.py | 18 ++++++++++++------ .../targets/filter_core/tasks/main.yml | 2 ++ 5 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 changelogs/fragments/url_safe_b64_encode_decode.yml diff --git a/changelogs/fragments/url_safe_b64_encode_decode.yml b/changelogs/fragments/url_safe_b64_encode_decode.yml new file mode 100644 index 00000000000..ebdeeda485c --- /dev/null +++ b/changelogs/fragments/url_safe_b64_encode_decode.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - filter - add support for URL-safe encoding and decoding in b64encode and b64decode (https://github.com/ansible/ansible/issues/84147). diff --git a/lib/ansible/plugins/filter/b64decode.yml b/lib/ansible/plugins/filter/b64decode.yml index 5dc82e0d6bb..08ff396b309 100644 --- a/lib/ansible/plugins/filter/b64decode.yml +++ b/lib/ansible/plugins/filter/b64decode.yml @@ -21,6 +21,14 @@ DOCUMENTATION: - Defaults to using 'utf-8'. type: string required: false + urlsafe: + description: + - Decode string using URL- and filesystem-safe alphabet, + which substitutes I(-) instead of I(+) and I(_) instead of I(/) in the Base64 alphabet. + type: bool + default: false + required: false + version_added: 2.19 EXAMPLES: | # Base64 decode a string @@ -33,6 +41,10 @@ EXAMPLES: | stuff: "{{ 'QQBuAHMAaQBiAGwAZQAgAC0AIABPMIkwaDB/MAoA' | b64decode(encoding='utf-16-le') }}" # => 'Ansible - くらとみ\n' + # URL-Safe Base64 decoding + stuff: "{{ 'aHR0cHM6Ly93d3cucHl0aG9uLm9yZy9leGFtcGxlLTE=' | b64decode(urlsafe=True) }}" + # => 'https://www.python.org/example-1' + RETURN: _value: description: The contents of the Base64 encoded string. diff --git a/lib/ansible/plugins/filter/b64encode.yml b/lib/ansible/plugins/filter/b64encode.yml index 199202730c2..6e1d5d0cf89 100644 --- a/lib/ansible/plugins/filter/b64encode.yml +++ b/lib/ansible/plugins/filter/b64encode.yml @@ -17,6 +17,14 @@ DOCUMENTATION: - Defaults to using 'utf-8'. type: string required: false + urlsafe: + description: + - Encode string using URL- and filesystem-safe alphabet, + which substitutes I(-) instead of I(+) and I(_) instead of I(/) in the Base64 alphabet. + type: bool + default: false + required: false + version_added: 2.19 EXAMPLES: | # Base64 encode a string @@ -29,6 +37,10 @@ EXAMPLES: | b64stuff: "{{ 'Ansible - くらとみ\n' | b64encode(encoding='utf-16-le') }}" # => 'QQBuAHMAaQBiAGwAZQAgAC0AIABPMIkwaDB/MAoA' + # URL-safe Base64 encoding + b64stuff: "{{ 'https://www.python.org/example-1' | b64encode(urlsafe=True) }}" + # => 'aHR0cHM6Ly93d3cucHl0aG9uLm9yZy9leGFtcGxlLTE=' + RETURN: _value: description: A Base64 encoded string. diff --git a/lib/ansible/plugins/filter/core.py b/lib/ansible/plugins/filter/core.py index 0e0b4275dec..58c24e4a992 100644 --- a/lib/ansible/plugins/filter/core.py +++ b/lib/ansible/plugins/filter/core.py @@ -457,12 +457,18 @@ def extract(environment, item, container, morekeys=None): return value -def b64encode(string, encoding='utf-8'): - return to_text(base64.b64encode(to_bytes(string, encoding=encoding, errors='surrogate_or_strict'))) - - -def b64decode(string, encoding='utf-8'): - return to_text(base64.b64decode(to_bytes(string, errors='surrogate_or_strict')), encoding=encoding) +def b64encode(string, encoding='utf-8', urlsafe=False): + func = base64.b64encode + if urlsafe: + func = base64.urlsafe_b64encode + return to_text(func(to_bytes(string, encoding=encoding, errors='surrogate_or_strict'))) + + +def b64decode(string, encoding='utf-8', urlsafe=False): + func = base64.b64decode + if urlsafe: + func = base64.urlsafe_b64decode + return to_text(func(to_bytes(string, errors='surrogate_or_strict')), encoding=encoding) def flatten(mylist, levels=None, skip_nulls=True): diff --git a/test/integration/targets/filter_core/tasks/main.yml b/test/integration/targets/filter_core/tasks/main.yml index 445e66b56bf..de429181db6 100644 --- a/test/integration/targets/filter_core/tasks/main.yml +++ b/test/integration/targets/filter_core/tasks/main.yml @@ -163,6 +163,8 @@ - "'QW5zaWJsZSAtIOOBj+OCieOBqOOBvwo=' | b64decode == 'Ansible - くらとみ\n'" - "'Ansible - くらとみ\n' | b64encode(encoding='utf-16-le') == 'QQBuAHMAaQBiAGwAZQAgAC0AIABPMIkwaDB/MAoA'" - "'QQBuAHMAaQBiAGwAZQAgAC0AIABPMIkwaDB/MAoA' | b64decode(encoding='utf-16-le') == 'Ansible - くらとみ\n'" + - "'https://www.python.org/example-1' | b64encode(urlsafe=True) == 'aHR0cHM6Ly93d3cucHl0aG9uLm9yZy9leGFtcGxlLTE='" + - "'aHR0cHM6Ly93d3cucHl0aG9uLm9yZy9leGFtcGxlLTE=' | b64decode(urlsafe=True) == 'https://www.python.org/example-1'" - set_fact: x: