#!/usr/bin/env python3 """Keycloak OIDC integration test — validates Keycloak can issue tokens that are accepted by La Suite Docs.""" import argparse import os import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) from utils.tests.helpers import ( http_get, http_post, load_toml_credentials, resolve_domain, ) def main(): if os.environ.get('SKIP_INTEGRATION') == '1': print("SKIP: SKIP_INTEGRATION=1 is set, skipping OIDC integration test") sys.exit(0) parser = argparse.ArgumentParser() parser.add_argument('--domain', default=os.environ.get('TEST_DOMAIN')) args = parser.parse_args() kc_domain = args.domain or resolve_domain('keycloak') docs_domain = resolve_domain('lasuite-docs') kc_url = f"https://{kc_domain}" docs_url = f"https://{docs_domain}" # Load credentials from lasuite-docs recipe dir creds_path = os.path.join(os.path.dirname(__file__), '..', '..', 'lasuite-docs') creds = load_toml_credentials(creds_path, 'keycloak') if creds is None: print("FAIL: Credentials file not found: lasuite-docs/keycloak-test-credentials..toml") print("Run recipe-info/lasuite-docs/setup_keycloak_integration.py first.") sys.exit(1) print("Testing Keycloak OIDC integration with La Suite Docs") print() # Step 1: Check that La Suite Docs is deployed and reachable print("Step 1: Checking La Suite Docs is reachable ...") status, _ = http_get(docs_url) if status == 0 or status >= 500: print(f" FAIL: La Suite Docs at {docs_url} returned HTTP {status}") print(" Make sure lasuite-docs is deployed before running this test.") sys.exit(1) print(f" PASS: La Suite Docs is reachable (HTTP {status})") # Step 2: Verify OIDC discovery endpoint print("Step 2: Checking Keycloak OIDC discovery endpoint ...") discovery_url = f"{kc_url}/realms/{creds['kc_realm']}/.well-known/openid-configuration" status, data = http_get(discovery_url) if status != 200: print(f" FAIL: OIDC discovery endpoint returned HTTP {status} (expected 200)") sys.exit(1) issuer = (data or {}).get("issuer", "") print(f" PASS: OIDC discovery endpoint OK (issuer: {issuer})") # Step 3: Obtain token from Keycloak print("Step 3: Obtaining token from Keycloak ...") token_url = f"{kc_url}/realms/{creds['kc_realm']}/protocol/openid-connect/token" status, data = http_post( token_url, data={ "grant_type": "password", "client_id": creds["kc_client_id"], "client_secret": creds["kc_client_secret"], "username": creds["kc_test_user"], "password": creds["kc_test_pass"], "scope": "openid email", }, content_type="application/x-www-form-urlencoded", ) access_token = (data or {}).get("access_token", "") if not access_token: error = (data or {}).get("error_description", (data or {}).get("error", "unknown")) print(f" FAIL: Could not obtain token from Keycloak: {error}") sys.exit(1) print(f" PASS: Obtained access token from Keycloak ({len(access_token)} chars)") # Step 4: Use token to authenticate against Docs API print("Step 4: Accessing Docs API with Keycloak token ...") status, body = http_get( f"{docs_url}/api/v1.0/users/me/", headers={"Authorization": f"Bearer {access_token}"}, ) if status != 200: print(f" FAIL: Docs API returned HTTP {status} (expected 200)") sys.exit(1) user_email = (body or {}).get("email", "") expected_email = creds.get("kc_test_email", f"{creds['kc_test_user']}@test.example.com") if user_email == expected_email: print(f" PASS: Docs API returned user with email '{user_email}'") else: print(f" FAIL: Expected email '{expected_email}', got '{user_email}'") sys.exit(1) print() print("PASS: Keycloak OIDC integration test passed — tokens accepted by Docs") if __name__ == '__main__': main()