Critical fix: mTLS uses completely different API endpoint than OAuth2. KEY CHANGE: OAuth2 and mTLS now use different base URLs automatically based on auth method. CONFIGURATION: - OAuth2: https://ppr.dam.ferrero.com/otmmapi - mTLS: https://dev-auth.app-api.ferrero.com/00003/mm URLs are automatically selected based on --auth-pfx flag: - No flag: Uses DAM_BASE_URL (OAuth2 endpoint) - --auth-pfx: Uses DAM_MTLS_BASE_URL (mTLS endpoint) IMPLEMENTATION: 1. .env: Added DAM_MTLS_BASE_URL variable 2. config.yaml: Added mtls_base_url configuration 3. dam_client.py: Auto-selects base_url in __init__ based on use_mtls flag 4. All API calls automatically use correct endpoint EXAMPLE ENDPOINT TRANSFORMATION: OAuth2: https://ppr.dam.ferrero.com/otmmapi/v6/search/text mTLS: https://dev-auth.app-api.ferrero.com/00003/mm/v6/search/text (Same path, different host/prefix) TESTING STATUS: ✓ Certificate loads successfully ✓ Correct base URL selected based on mode ⚠️ HTTP 403 from current IP (likely IP whitelist) ✓ Ready to test from whitelisted IP location ALL SCRIPTS UPDATED: ✓ a1_to_a2_download.py - Uses correct URL with --auth-pfx ✓ a5_to_a6_download.py - Uses correct URL with --auth-pfx ✓ b1_to_b2_download.py - Uses correct URL with --auth-pfx ✓ test_connection.py - Uses correct URL with --auth-pfx NEW DEBUG SCRIPT: - test_mtls_debug.py - Detailed request/response logging BACKWARD COMPATIBILITY: ✓ OAuth2 completely unchanged (default) ✓ No impact on existing workflows ✓ Can test mTLS from whitelisted IP when ready Next: Test from whitelisted IP location to verify mTLS works end-to-end. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
223 lines
6.9 KiB
Python
Executable file
223 lines
6.9 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""
|
|
mTLS Authentication Debug Script
|
|
Performs detailed testing with full request/response logging
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import json
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
|
|
|
from shared.config_loader import load_config
|
|
from shared.dam_client import pfx_to_pem
|
|
import requests
|
|
|
|
# Enable detailed logging
|
|
import logging
|
|
import http.client as http_client
|
|
|
|
http_client.HTTPConnection.debuglevel = 1
|
|
logging.basicConfig()
|
|
logging.getLogger().setLevel(logging.DEBUG)
|
|
requests_log = logging.getLogger("requests.packages.urllib3")
|
|
requests_log.setLevel(logging.DEBUG)
|
|
requests_log.propagate = True
|
|
|
|
print("=" * 80)
|
|
print("mTLS AUTHENTICATION DEBUG TEST")
|
|
print("=" * 80)
|
|
print("")
|
|
|
|
# Load config
|
|
config = load_config('config/config.yaml')
|
|
cert_path = config['dam']['mtls_cert_path']
|
|
cert_password = config['dam']['mtls_cert_password']
|
|
base_url = config['dam']['base_url']
|
|
|
|
print("Configuration:")
|
|
print(" Base URL: {}".format(base_url))
|
|
print(" Cert Path: {}".format(cert_path))
|
|
print(" Cert Password: {}".format('*' * len(cert_password)))
|
|
print("")
|
|
|
|
# Test 1: Simple base endpoint
|
|
print("=" * 80)
|
|
print("TEST 1: GET /v6 (Base API Endpoint)")
|
|
print("=" * 80)
|
|
|
|
try:
|
|
with pfx_to_pem(cert_path, cert_password) as cert:
|
|
print("\nCertificate converted to PEM: {}".format(cert))
|
|
print("\nSending request...")
|
|
print(" URL: {}/v6".format(base_url))
|
|
print(" Method: GET")
|
|
print(" Certificate: {}".format(cert))
|
|
print(" Verify SSL: True")
|
|
print("")
|
|
|
|
response = requests.get(
|
|
"{}/v6".format(base_url),
|
|
cert=cert,
|
|
verify=True,
|
|
timeout=30
|
|
)
|
|
|
|
print("\nRESPONSE RECEIVED:")
|
|
print(" Status Code: {}".format(response.status_code))
|
|
print(" Reason: {}".format(response.reason))
|
|
print("")
|
|
print("Response Headers:")
|
|
for key, value in response.headers.items():
|
|
print(" {}: {}".format(key, value))
|
|
print("")
|
|
print("Response Cookies:")
|
|
for key, value in response.cookies.items():
|
|
print(" {}: {}".format(key, value))
|
|
print("")
|
|
print("Response Body:")
|
|
try:
|
|
body_json = response.json()
|
|
print(json.dumps(body_json, indent=2)[:1000])
|
|
except:
|
|
print(response.text[:1000])
|
|
|
|
except Exception as e:
|
|
print("\n✗ ERROR: {}".format(str(e)))
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
# Test 2: Login endpoint
|
|
print("\n" + "=" * 80)
|
|
print("TEST 2: GET /otdsws/login (Session Login)")
|
|
print("=" * 80)
|
|
|
|
try:
|
|
with pfx_to_pem(cert_path, cert_password) as cert:
|
|
login_url = base_url.replace('/otmmapi', '/otdsws/login')
|
|
|
|
print("\nSending request...")
|
|
print(" URL: {}".format(login_url))
|
|
print(" Method: GET")
|
|
print(" Certificate: {}".format(cert))
|
|
print("")
|
|
|
|
response = requests.get(
|
|
login_url,
|
|
cert=cert,
|
|
verify=True,
|
|
timeout=30
|
|
)
|
|
|
|
print("\nRESPONSE RECEIVED:")
|
|
print(" Status Code: {}".format(response.status_code))
|
|
print(" Reason: {}".format(response.reason))
|
|
print("")
|
|
print("Response Headers:")
|
|
for key, value in response.headers.items():
|
|
print(" {}: {}".format(key, value))
|
|
print("")
|
|
print("Response Cookies:")
|
|
for key, value in response.cookies.items():
|
|
print(" {}: {}".format(key, value))
|
|
print("")
|
|
print("Response Body:")
|
|
print(response.text[:1000])
|
|
|
|
except Exception as e:
|
|
print("\n✗ ERROR: {}".format(str(e)))
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
# Test 3: Search campaigns with session
|
|
print("\n" + "=" * 80)
|
|
print("TEST 3: POST /v6/search/text (Campaign Search)")
|
|
print("=" * 80)
|
|
|
|
try:
|
|
with pfx_to_pem(cert_path, cert_password) as cert:
|
|
import urllib.parse
|
|
|
|
search_condition = {
|
|
"search_condition_list": {
|
|
"search_condition": [
|
|
{
|
|
"type": "com.artesia.search.SearchScalarCondition",
|
|
"metadata_field_id": "ARTESIA.FIELD.CONTAINER TYPE NAME",
|
|
"relational_operator_id": "ARTESIA.OPERATOR.CHAR.CONTAINS",
|
|
"value": "GLOBALCAMPAING",
|
|
"left_paren": "(",
|
|
"right_paren": ")"
|
|
},
|
|
{
|
|
"type": "com.artesia.search.SearchScalarCondition",
|
|
"metadata_field_id": "FERRERO.FIELD.CAMPAIGN TYPE",
|
|
"relational_operator_id": "ARTESIA.OPERATOR.CHAR.CONTAINS",
|
|
"value": "Local Adaptation",
|
|
"relational_operator": "and"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
search_condition_str = json.dumps(search_condition)
|
|
search_condition_encoded = urllib.parse.quote(search_condition_str)
|
|
|
|
url = "{}/v6/search/text?load_type=metadata&search_config_id=18&search_condition_list={}".format(
|
|
base_url,
|
|
search_condition_encoded
|
|
)
|
|
|
|
print("\nSending request...")
|
|
print(" URL: {}...".format(url[:100]))
|
|
print(" Method: GET")
|
|
print(" Certificate: {}".format(cert))
|
|
print("")
|
|
|
|
# Create session to maintain cookies
|
|
session = requests.Session()
|
|
session.cert = cert
|
|
session.verify = True
|
|
|
|
# Try to get login first
|
|
login_url = base_url.replace('/otmmapi', '/otdsws/login')
|
|
print("First, attempting login...")
|
|
login_response = session.get(login_url, timeout=30)
|
|
print(" Login Status: {}".format(login_response.status_code))
|
|
|
|
if login_response.cookies:
|
|
print(" Cookies from login:")
|
|
for key, value in login_response.cookies.items():
|
|
print(" {}: {}".format(key, value[:50] if len(value) > 50 else value))
|
|
|
|
print("\nNow searching campaigns with session...")
|
|
response = session.get(url, headers={'Accept': 'application/json'}, timeout=30)
|
|
|
|
print("\nRESPONSE RECEIVED:")
|
|
print(" Status Code: {}".format(response.status_code))
|
|
print(" Reason: {}".format(response.reason))
|
|
print("")
|
|
print("Response Headers:")
|
|
for key, value in response.headers.items():
|
|
print(" {}: {}".format(key, value))
|
|
print("")
|
|
print("Response Cookies:")
|
|
for key, value in response.cookies.items():
|
|
print(" {}: {}".format(key, value))
|
|
print("")
|
|
print("Response Body:")
|
|
try:
|
|
body_json = response.json()
|
|
print(json.dumps(body_json, indent=2)[:2000])
|
|
except:
|
|
print(response.text[:2000])
|
|
|
|
except Exception as e:
|
|
print("\n✗ ERROR: {}".format(str(e)))
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
print("\n" + "=" * 80)
|
|
print("DEBUG TEST COMPLETE")
|
|
print("=" * 80)
|