From 38af12d03dcbcd39cf02acf41d9adb0382df6c00 Mon Sep 17 00:00:00 2001 From: Alex Bourret Date: Thu, 2 Apr 2026 12:20:20 +0200 Subject: [PATCH 1/8] storing key+certificats as passwords --- parameter-sets/credential/parameter-set.json | 8 ++++---- parameter-sets/secure-basic/parameter-set.json | 8 ++++---- parameter-sets/secure-oauth/parameter-set.json | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/parameter-sets/credential/parameter-set.json b/parameter-sets/credential/parameter-set.json index 3035645..b5202c1 100644 --- a/parameter-sets/credential/parameter-set.json +++ b/parameter-sets/credential/parameter-set.json @@ -130,15 +130,15 @@ { "name": "mtls_certificate_path", "label": "Path to certificate", - "description": "", - "type": "STRING", + "description": "or full certificate from -----BEGIN to END CERTIFICATE-----", + "type": "PASSWORD", "visibilityCondition": "model.use_mtls==true" }, { "name": "mtls_key_path", "label": "Path to key", - "description": "", - "type": "STRING", + "description": "or full key from -----BEGIN to END PRIVATE KEY-----", + "type": "PASSWORD", "visibilityCondition": "model.use_mtls==true" } ] diff --git a/parameter-sets/secure-basic/parameter-set.json b/parameter-sets/secure-basic/parameter-set.json index d54f19d..6c1fb58 100644 --- a/parameter-sets/secure-basic/parameter-set.json +++ b/parameter-sets/secure-basic/parameter-set.json @@ -55,15 +55,15 @@ { "name": "mtls_certificate_path", "label": "Path to certificate", - "description": "", - "type": "STRING", + "description": "or full certificate from -----BEGIN to END CERTIFICATE-----", + "type": "PASSWORD", "visibilityCondition": "model.use_mtls==true" }, { "name": "mtls_key_path", "label": "Path to key", - "description": "", - "type": "STRING", + "description": "or full key from -----BEGIN to END PRIVATE KEY-----", + "type": "PASSWORD", "visibilityCondition": "model.use_mtls==true" } ] diff --git a/parameter-sets/secure-oauth/parameter-set.json b/parameter-sets/secure-oauth/parameter-set.json index 79da6d0..947f096 100644 --- a/parameter-sets/secure-oauth/parameter-set.json +++ b/parameter-sets/secure-oauth/parameter-set.json @@ -64,15 +64,15 @@ { "name": "mtls_certificate_path", "label": "Path to certificate", - "description": "", - "type": "STRING", + "description": "or full certificate from -----BEGIN to END CERTIFICATE-----", + "type": "PASSWORD", "visibilityCondition": "model.use_mtls==true" }, { "name": "mtls_key_path", "label": "Path to key", - "description": "", - "type": "STRING", + "description": "or full key from -----BEGIN to END PRIVATE KEY-----", + "type": "PASSWORD", "visibilityCondition": "model.use_mtls==true" } ] From 6252b88cd2594758fc87340ec2d5caba72d40f04 Mon Sep 17 00:00:00 2001 From: Alex Bourret Date: Thu, 2 Apr 2026 12:20:54 +0200 Subject: [PATCH 2/8] using temp file for using key+certificats stored in presets --- python-lib/rest_api_client.py | 40 +++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/python-lib/rest_api_client.py b/python-lib/rest_api_client.py index 1ee4519..49ffd08 100644 --- a/python-lib/rest_api_client.py +++ b/python-lib/rest_api_client.py @@ -1,6 +1,7 @@ import requests import time import copy +import tempfile from pagination import Pagination from safe_logger import SafeLogger from loop_detector import LoopDetector @@ -184,14 +185,35 @@ def request(self, method, url, can_raise_exeption=True, **kwargs): def request_with_redirect_retry(self, method, url, **kwargs): # In case of redirection to another domain, the authorization header is not kept # If redirect_auth_header is true, another attempt is made with initial headers to the redirected url - response = self.session.request(method, url, **kwargs) + response = self.request_with_cert(method, url, **kwargs) if self.redirect_auth_header and not response.url.startswith(url): redirection_kwargs = copy.deepcopy(kwargs) redirection_kwargs.pop("params", None) # params are contained in the redirected url logger.warning("Redirection ! Accessing endpoint {} with initial authorization headers".format(response.url)) - response = self.session.request(method, response.url, **redirection_kwargs) + response = self.request_with_cert(method, response.url, **redirection_kwargs) return response + def request_with_cert(self, method, url, **kwargs): + cert = kwargs.get("cert", None) + if cert and len(cert) == 2: + if cert[0].startswith("-----BEGIN CERTIFICATE") and cert[1].startswith("-----BEGIN PRIVATE KEY"): + logger.info("mTLS certificate and key are strings") + response = None + with tempfile.NamedTemporaryFile(mode="w", suffix=".crt") as tmp_certificate: + with tempfile.NamedTemporaryFile(mode="w", suffix=".key") as tmp_key: + tmp_certificate.write( + normalize_key(cert[0]) + ) + tmp_certificate.seek(0) + tmp_key.write( + normalize_key(cert[1]) + ) + tmp_key.seek(0) + kwargs["cert"] = (tmp_certificate.name, tmp_key.name) + response = self.session.request(method, url, **kwargs) + return response + return self.session.request(method, url, **kwargs) + def paginated_api_call(self, can_raise_exeption=True): if self.pagination.params_must_be_blanked: self.requests_kwargs["params"] = {} @@ -278,3 +300,17 @@ def get_headers(response): if isinstance(response, requests.Response): return response.headers return None + + +def normalize_key(key): + tempo_text = str(key) + tempo_text = tempo_text.replace("BEGIN CERTIFICATE", "BEGINCERTIFICATE") + tempo_text = tempo_text.replace("END CERTIFICATE", "ENDCERTIFICATE") + tempo_text = tempo_text.replace("-----BEGIN PRIVATE KEY-----", "-----BEGINPRIVATEKEY-----") + tempo_text = tempo_text.replace("-----END PRIVATE KEY-----", "-----ENDPRIVATEKEY-----") + tempo_text = tempo_text.replace(" ", "\n") + tempo_text = tempo_text.replace("BEGINCERTIFICATE", "BEGIN CERTIFICATE") + tempo_text = tempo_text.replace("ENDCERTIFICATE", "END CERTIFICATE") + tempo_text = tempo_text.replace("-----BEGINPRIVATEKEY-----", "-----BEGIN PRIVATE KEY-----") + tempo_text = tempo_text.replace("-----ENDPRIVATEKEY-----", "-----END PRIVATE KEY-----") + return tempo_text From 734dd6c041e734092c9060bbc80294253f276ba7 Mon Sep 17 00:00:00 2001 From: Alex Bourret Date: Thu, 2 Apr 2026 12:21:29 +0200 Subject: [PATCH 3/8] adding mtls keys to data that should not be displayed --- python-lib/dku_constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python-lib/dku_constants.py b/python-lib/dku_constants.py index 3736450..ffab18d 100644 --- a/python-lib/dku_constants.py +++ b/python-lib/dku_constants.py @@ -1,6 +1,6 @@ class DKUConstants(object): API_RESPONSE_KEY = "api_response" - FORBIDDEN_KEYS = ["token", "password", "api_key_value", "secure_token"] + FORBIDDEN_KEYS = ["token", "password", "api_key_value", "secure_token", "mtls_key_path", "mtls_certificate_path"] FORM_DATA_BODY_FORMAT = "FORM_DATA" PLUGIN_VERSION = "1.2.7" RAW_BODY_FORMAT = "RAW" From 987c597f1620e453a40e8d3fc1e05d66777c6839 Mon Sep 17 00:00:00 2001 From: Alex Bourret Date: Thu, 2 Apr 2026 13:56:47 +0200 Subject: [PATCH 4/8] beta.5 --- python-lib/dku_constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python-lib/dku_constants.py b/python-lib/dku_constants.py index ffab18d..7e007f8 100644 --- a/python-lib/dku_constants.py +++ b/python-lib/dku_constants.py @@ -2,6 +2,6 @@ class DKUConstants(object): API_RESPONSE_KEY = "api_response" FORBIDDEN_KEYS = ["token", "password", "api_key_value", "secure_token", "mtls_key_path", "mtls_certificate_path"] FORM_DATA_BODY_FORMAT = "FORM_DATA" - PLUGIN_VERSION = "1.2.7" + PLUGIN_VERSION = "1.2.7-beta.5" RAW_BODY_FORMAT = "RAW" REPONSE_ERROR_KEY = "dku_error" From 581a267a0dc3f936b096bbff2bdc2544fd164c08 Mon Sep 17 00:00:00 2001 From: Alex Bourret Date: Thu, 16 Apr 2026 13:38:35 +0200 Subject: [PATCH 5/8] Clarify the UI --- parameter-sets/credential/parameter-set.json | 4 ++-- parameter-sets/secure-basic/parameter-set.json | 4 ++-- parameter-sets/secure-oauth/parameter-set.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/parameter-sets/credential/parameter-set.json b/parameter-sets/credential/parameter-set.json index b5202c1..0c027f6 100644 --- a/parameter-sets/credential/parameter-set.json +++ b/parameter-sets/credential/parameter-set.json @@ -130,14 +130,14 @@ { "name": "mtls_certificate_path", "label": "Path to certificate", - "description": "or full certificate from -----BEGIN to END CERTIFICATE-----", + "description": "or full certificate starting with -----BEGIN and ending with END CERTIFICATE-----", "type": "PASSWORD", "visibilityCondition": "model.use_mtls==true" }, { "name": "mtls_key_path", "label": "Path to key", - "description": "or full key from -----BEGIN to END PRIVATE KEY-----", + "description": "or full key starting with -----BEGIN and ending with END PRIVATE KEY-----", "type": "PASSWORD", "visibilityCondition": "model.use_mtls==true" } diff --git a/parameter-sets/secure-basic/parameter-set.json b/parameter-sets/secure-basic/parameter-set.json index 6c1fb58..a6b6809 100644 --- a/parameter-sets/secure-basic/parameter-set.json +++ b/parameter-sets/secure-basic/parameter-set.json @@ -55,14 +55,14 @@ { "name": "mtls_certificate_path", "label": "Path to certificate", - "description": "or full certificate from -----BEGIN to END CERTIFICATE-----", + "description": "or full certificate starting with -----BEGIN and ending with END CERTIFICATE-----", "type": "PASSWORD", "visibilityCondition": "model.use_mtls==true" }, { "name": "mtls_key_path", "label": "Path to key", - "description": "or full key from -----BEGIN to END PRIVATE KEY-----", + "description": "or full key starting with -----BEGIN and ending with END PRIVATE KEY-----", "type": "PASSWORD", "visibilityCondition": "model.use_mtls==true" } diff --git a/parameter-sets/secure-oauth/parameter-set.json b/parameter-sets/secure-oauth/parameter-set.json index 947f096..72c6437 100644 --- a/parameter-sets/secure-oauth/parameter-set.json +++ b/parameter-sets/secure-oauth/parameter-set.json @@ -64,14 +64,14 @@ { "name": "mtls_certificate_path", "label": "Path to certificate", - "description": "or full certificate from -----BEGIN to END CERTIFICATE-----", + "description": "or full certificate starting with -----BEGIN and ending with END CERTIFICATE-----", "type": "PASSWORD", "visibilityCondition": "model.use_mtls==true" }, { "name": "mtls_key_path", "label": "Path to key", - "description": "or full key from -----BEGIN to END PRIVATE KEY-----", + "description": "or full key starting with -----BEGIN and ending with END PRIVATE KEY-----", "type": "PASSWORD", "visibilityCondition": "model.use_mtls==true" } From 1cfed4416c356fbd379b65d9f28b480f48ec5912 Mon Sep 17 00:00:00 2001 From: Alex Bourret Date: Thu, 16 Apr 2026 13:38:53 +0200 Subject: [PATCH 6/8] Add normalization for RSA keys --- python-lib/rest_api_client.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python-lib/rest_api_client.py b/python-lib/rest_api_client.py index 49ffd08..9609151 100644 --- a/python-lib/rest_api_client.py +++ b/python-lib/rest_api_client.py @@ -308,7 +308,11 @@ def normalize_key(key): tempo_text = tempo_text.replace("END CERTIFICATE", "ENDCERTIFICATE") tempo_text = tempo_text.replace("-----BEGIN PRIVATE KEY-----", "-----BEGINPRIVATEKEY-----") tempo_text = tempo_text.replace("-----END PRIVATE KEY-----", "-----ENDPRIVATEKEY-----") + tempo_text = tempo_text.replace("BEGIN RSA PRIVATE KEY", "BEGINRSAPRIVATEKEY") + tempo_text = tempo_text.replace("END RSA PRIVATE KEY", "ENDRSAPRIVATEKEY") tempo_text = tempo_text.replace(" ", "\n") + tempo_text = tempo_text.replace("BEGINRSAPRIVATEKEY", "BEGIN RSA PRIVATE KEY") + tempo_text = tempo_text.replace("ENDRSAPRIVATEKEY", "END RSA PRIVATE KEY") tempo_text = tempo_text.replace("BEGINCERTIFICATE", "BEGIN CERTIFICATE") tempo_text = tempo_text.replace("ENDCERTIFICATE", "END CERTIFICATE") tempo_text = tempo_text.replace("-----BEGINPRIVATEKEY-----", "-----BEGIN PRIVATE KEY-----") From 59597ce669c0c8cbb66c5281196942cbea77f9f0 Mon Sep 17 00:00:00 2001 From: Alex Bourret Date: Thu, 16 Apr 2026 14:00:27 +0200 Subject: [PATCH 7/8] fix to accept RSA keys --- python-lib/rest_api_client.py | 38 +++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/python-lib/rest_api_client.py b/python-lib/rest_api_client.py index 9609151..12ff0a2 100644 --- a/python-lib/rest_api_client.py +++ b/python-lib/rest_api_client.py @@ -196,7 +196,7 @@ def request_with_redirect_retry(self, method, url, **kwargs): def request_with_cert(self, method, url, **kwargs): cert = kwargs.get("cert", None) if cert and len(cert) == 2: - if cert[0].startswith("-----BEGIN CERTIFICATE") and cert[1].startswith("-----BEGIN PRIVATE KEY"): + if cert[0].startswith("-----BEGIN CERTIFICATE") and cert[1].startswith("-----BEGIN "): logger.info("mTLS certificate and key are strings") response = None with tempfile.NamedTemporaryFile(mode="w", suffix=".crt") as tmp_certificate: @@ -303,18 +303,30 @@ def get_headers(response): def normalize_key(key): + PROTECTED_EXPRESSIONS = [ + "BEGIN CERTIFICATE", "END CERTIFICATE", + "BEGIN PRIVATE KEY", "END PRIVATE KEY", + "BEGIN RSA PRIVATE KEY", "END RSA PRIVATE KEY" + ] tempo_text = str(key) - tempo_text = tempo_text.replace("BEGIN CERTIFICATE", "BEGINCERTIFICATE") - tempo_text = tempo_text.replace("END CERTIFICATE", "ENDCERTIFICATE") - tempo_text = tempo_text.replace("-----BEGIN PRIVATE KEY-----", "-----BEGINPRIVATEKEY-----") - tempo_text = tempo_text.replace("-----END PRIVATE KEY-----", "-----ENDPRIVATEKEY-----") - tempo_text = tempo_text.replace("BEGIN RSA PRIVATE KEY", "BEGINRSAPRIVATEKEY") - tempo_text = tempo_text.replace("END RSA PRIVATE KEY", "ENDRSAPRIVATEKEY") + for expression_to_protect in PROTECTED_EXPRESSIONS: + protected_form = expression_to_protect.replace(" ", "") + tempo_text = tempo_text.replace(expression_to_protect, protected_form) tempo_text = tempo_text.replace(" ", "\n") - tempo_text = tempo_text.replace("BEGINRSAPRIVATEKEY", "BEGIN RSA PRIVATE KEY") - tempo_text = tempo_text.replace("ENDRSAPRIVATEKEY", "END RSA PRIVATE KEY") - tempo_text = tempo_text.replace("BEGINCERTIFICATE", "BEGIN CERTIFICATE") - tempo_text = tempo_text.replace("ENDCERTIFICATE", "END CERTIFICATE") - tempo_text = tempo_text.replace("-----BEGINPRIVATEKEY-----", "-----BEGIN PRIVATE KEY-----") - tempo_text = tempo_text.replace("-----ENDPRIVATEKEY-----", "-----END PRIVATE KEY-----") + for expression_to_protect in PROTECTED_EXPRESSIONS: + protected_form = expression_to_protect.replace(" ", "") + tempo_text = tempo_text.replace(protected_form, expression_to_protect) + # tempo_text = tempo_text.replace("BEGIN CERTIFICATE", "BEGINCERTIFICATE") + # tempo_text = tempo_text.replace("END CERTIFICATE", "ENDCERTIFICATE") + # tempo_text = tempo_text.replace("-----BEGIN PRIVATE KEY-----", "-----BEGINPRIVATEKEY-----") + # tempo_text = tempo_text.replace("-----END PRIVATE KEY-----", "-----ENDPRIVATEKEY-----") + # tempo_text = tempo_text.replace("BEGIN RSA PRIVATE KEY", "BEGINRSAPRIVATEKEY") + # tempo_text = tempo_text.replace("END RSA PRIVATE KEY", "ENDRSAPRIVATEKEY") + # tempo_text = tempo_text.replace(" ", "\n") + # tempo_text = tempo_text.replace("BEGINRSAPRIVATEKEY", "BEGIN RSA PRIVATE KEY") + # tempo_text = tempo_text.replace("ENDRSAPRIVATEKEY", "END RSA PRIVATE KEY") + # tempo_text = tempo_text.replace("BEGINCERTIFICATE", "BEGIN CERTIFICATE") + # tempo_text = tempo_text.replace("ENDCERTIFICATE", "END CERTIFICATE") + # tempo_text = tempo_text.replace("-----BEGINPRIVATEKEY-----", "-----BEGIN PRIVATE KEY-----") + # tempo_text = tempo_text.replace("-----ENDPRIVATEKEY-----", "-----END PRIVATE KEY-----") return tempo_text From b807d8573bbfd2684f6a7f0916dea91720704bc3 Mon Sep 17 00:00:00 2001 From: Alex Bourret Date: Thu, 16 Apr 2026 14:01:18 +0200 Subject: [PATCH 8/8] removing comments --- python-lib/rest_api_client.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/python-lib/rest_api_client.py b/python-lib/rest_api_client.py index 12ff0a2..cb4e824 100644 --- a/python-lib/rest_api_client.py +++ b/python-lib/rest_api_client.py @@ -316,17 +316,4 @@ def normalize_key(key): for expression_to_protect in PROTECTED_EXPRESSIONS: protected_form = expression_to_protect.replace(" ", "") tempo_text = tempo_text.replace(protected_form, expression_to_protect) - # tempo_text = tempo_text.replace("BEGIN CERTIFICATE", "BEGINCERTIFICATE") - # tempo_text = tempo_text.replace("END CERTIFICATE", "ENDCERTIFICATE") - # tempo_text = tempo_text.replace("-----BEGIN PRIVATE KEY-----", "-----BEGINPRIVATEKEY-----") - # tempo_text = tempo_text.replace("-----END PRIVATE KEY-----", "-----ENDPRIVATEKEY-----") - # tempo_text = tempo_text.replace("BEGIN RSA PRIVATE KEY", "BEGINRSAPRIVATEKEY") - # tempo_text = tempo_text.replace("END RSA PRIVATE KEY", "ENDRSAPRIVATEKEY") - # tempo_text = tempo_text.replace(" ", "\n") - # tempo_text = tempo_text.replace("BEGINRSAPRIVATEKEY", "BEGIN RSA PRIVATE KEY") - # tempo_text = tempo_text.replace("ENDRSAPRIVATEKEY", "END RSA PRIVATE KEY") - # tempo_text = tempo_text.replace("BEGINCERTIFICATE", "BEGIN CERTIFICATE") - # tempo_text = tempo_text.replace("ENDCERTIFICATE", "END CERTIFICATE") - # tempo_text = tempo_text.replace("-----BEGINPRIVATEKEY-----", "-----BEGIN PRIVATE KEY-----") - # tempo_text = tempo_text.replace("-----ENDPRIVATEKEY-----", "-----END PRIVATE KEY-----") return tempo_text