MLE-29912 OAuth 2.0 Authorization Code Flow: Load Balancer Session Affinity Testing - #190
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds operator and Management API support for configuring MarkLogic OAuth 2.0 (including Authorization Code flow behind HAProxy session affinity), updates bootstrap readiness detection for MarkLogic 12.1+, and introduces an opt-in integration test suite that provisions real Kubernetes resources (MarkLogic, HAProxy, Keycloak) to validate the OAuth flow end-to-end.
Changes:
- Extend
pkg/mlmanagewith OAuth external-security/App Server helpers and CA import support. - Update HAProxy and bootstrap readiness behavior to support TLS-terminated OAuth callbacks and MarkLogic 12.1+ readiness semantics.
- Add a comprehensive integration test suite (fixtures + helpers + OAuth/session-affinity tests) gated behind environment variables.
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/integration/marklogic-server/testutil/kubernetes.go | Adds kubectl-based helpers for applying objects, waiting on readiness, and collecting diagnostics. |
| test/integration/marklogic-server/testutil/kubernetes_test.go | Unit test for marshaling Kubernetes objects with GVK injected. |
| test/integration/marklogic-server/README.md | Documents integration test layout, prerequisites, running pattern, and diagnostics behavior. |
| test/integration/marklogic-server/oauth/session_affinity_test.go | Adds HAProxy SessionID affinity “contract test” using nginx backends. |
| test/integration/marklogic-server/oauth/README.md | Documents OAuth integration suites, gating env vars, and how to run each suite. |
| test/integration/marklogic-server/oauth/oauth_setup.go | Builds shared OAuth test infrastructure (TLS, Keycloak, MarkLogic cluster, HAProxy, client pod). |
| test/integration/marklogic-server/oauth/oauth_setup_test.go | Unit tests for oauth setup helpers (redirect URI validation, discovery parsing, topology). |
| test/integration/marklogic-server/oauth/oauth_load_balancer_affinity_test.go | Resource-server setup test that provisions infra and configures MarkLogic OAuth via Management API. |
| test/integration/marklogic-server/oauth/oauth_authorization_code_test.go | Authorization Code + PKCE integration test with HAProxy affinity (TC1/TC2/TC3). |
| test/integration/marklogic-server/fixtures/tls/tls.go | Generates a short-lived CA and server cert Secrets (including combined-PEM for HAProxy). |
| test/integration/marklogic-server/fixtures/tls/tls_test.go | Unit tests verifying the generated certs validate and chain correctly. |
| test/integration/marklogic-server/fixtures/oauthclient/oauthclient.go | Fixture for an in-cluster curl pod with CA mounted and a work dir for cookie jars. |
| test/integration/marklogic-server/fixtures/oauthclient/oauthclient_test.go | Unit tests for the oauth client pod fixture. |
| test/integration/marklogic-server/fixtures/marklogiccluster/marklogiccluster.go | Fixture for a 2-node TLS-enabled MarklogicCluster with operator-managed HAProxy. |
| test/integration/marklogic-server/fixtures/marklogiccluster/marklogiccluster_test.go | Unit tests validating the MarklogicCluster fixture topology. |
| test/integration/marklogic-server/fixtures/keycloak/keycloak.go | Fixture for deploying Keycloak with realm import (public + confidential clients). |
| test/integration/marklogic-server/fixtures/keycloak/keycloak_test.go | Unit tests for the Keycloak fixture and realm import content. |
| pkg/mlmanage/client.go | Adds new Client interface methods and implementations for CA import + OAuth configuration, plus payload builders/validation. |
| pkg/mlmanage/client_test.go | Adds tests for CA import request contract and OAuth ensure/create/update behaviors. |
| pkg/k8sutil/scripts/cluster-config.sh | Updates bootstrap readiness check to accept MarkLogic 12.1+ 500 XDMP-NOUSER as ready-with-TLS. |
| pkg/k8sutil/haProxy.go | Mounts the HAProxy TLS secret into the HAProxy container when frontend TLS termination is enabled. |
| pkg/k8sutil/dynamic_reconcile_test.go | Updates stub management client to implement new OAuth/CA methods. |
| internal/controller/marklogicgroup_controller_test.go | Updates fake management client to implement new OAuth/CA methods. |
Comments suppressed due to low confidence (3)
test/integration/marklogic-server/oauth/oauth_authorization_code_test.go:321
- The auth code flow script uses
curl -kwhen fetching the Keycloak login page, bypassing TLS verification and potentially masking problems with the in-cluster IdP certificate chain/hostname.
LOGIN=$(curl -sk -c "$KCJAR" -b "$KCJAR" "$LOC")
test/integration/marklogic-server/oauth/oauth_authorization_code_test.go:329
- The auth code flow script posts credentials to Keycloak with
curl -k, bypassing TLS verification even though a CA bundle is provided. This weakens the integration test by not exercising the intended TLS trust configuration.
POST=$(curl -sk -c "$KCJAR" -b "$KCJAR" --data-urlencode "username=$USER" --data-urlencode "password=$PASS" --data-urlencode "credentialId=" "$ACTION")
test/integration/marklogic-server/oauth/oauth_authorization_code_test.go:347
- The callback submission uses
curl -k, which bypasses TLS verification for the most security-sensitive step (posting the authorization code/state back to the app). Remove-kso the callback path also validates TLS using the mounted CA bundle; avoid adding--failhere since negative test cases expect non-2xx responses.
if [ "$CARRY" = "yes" ] && [ -n "$SID" ]; then
H4=$(curl -sk -b "SessionID=$SID" -D - -o "$B4" --data-urlencode "code=$CODE" --data-urlencode "state=$STATE" "$CBURL")
else
H4=$(curl -sk -D - -o "$B4" --data-urlencode "code=$CODE" --data-urlencode "state=$STATE" "$CBURL")
fi
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
The auth code flow script uses curl -k (insecure TLS) for the initial request, which bypasses certificate verification even though the test pod mounts a CA bundle (CURL_CA_BUNDLE). That undermines the test’s “TLS end-to-end” goal and can hide certificate/SAN issues. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
EnsureOAuthExternalSecurity issues a GET request before validating the config. If config.Name (or other required fields) is empty/invalid, this can hit an unintended endpoint (e.g., "/manage/v2/external-security/") and does unnecessary network I/O; validation should happen before any request. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Add OAuth Authorization Code flow support with HAProxy session affinity, plus full integration test suite
Summary
Adds Management API support for configuring MarkLogic OAuth (external security + OAuth-protected App Servers), fixes a MarkLogic 12.1 bootstrap readiness check, mounts the SSL certificate into the HAProxy container for TLS-terminated OAuth callbacks, and adds a comprehensive integration test suite validating the OAuth Authorization Code flow and HAProxy session affinity end-to-end (validated on both minikube and a real multi-node AWS EKS cluster).
Changes
pkg/mlmanage/client.go / client_test.go
Clientinterface methods:ImportCertificateAuthority- imports a PEM-encoded CA into the Security database (newdoPlainTextrequest helper).EnsureOAuthExternalSecurity- idempotently creates/updates anexternal-securityOAuth config (oauth-serverblock: flow type, vendor, client ID/secret, token/authorization server URIs, JWT issuer/algorithm/JWKS, username/role attributes).EnsureOAuthAppServer- idempotently creates/updates an OAuth-protected HTTP App Server (authentication=oauth,external-security, TLS certificate template).OAuthExternalSecurityConfig,OAuthAppServerConfig) with validation, and payload builders (BuildOAuthExternalSecurityPayload,BuildOAuthAppServerPayload).pkg/k8sutil/haProxy.go
ssl-certificatevolume (read-only) into the HAProxy container at/usr/local/etc/ssl, required for HAProxy to terminate/forward TLS for the OAuth Authorization Code callback path.pkg/k8sutil/scripts/cluster-config.sh
wait_bootstrap_readyfor MarkLogic 12.1+: the TLS-enabled bootstrap host now returns500 XDMP-NOUSERinstead of403when queried unauthenticated. The check now accepts either signal (403, or 500 withXDMP-NOUSERbody) as "ready," with proper temp-file cleanup for the captured response body.Test doubles
internal/controller/marklogicgroup_controller_test.goandpkg/k8sutil/dynamic_reconcile_test.go: updated fake/stub management clients to implement the three newClientinterface methods.New integration test suite (test/integration/marklogic-server/)
fixtures/keycloak,fixtures/tls,fixtures/marklogiccluster,fixtures/oauthclient: reusable fixtures for standing up Keycloak (public/confidential clients), TLS certs, a MarklogicCluster, and an in-cluster OAuth test client pod.testutil/kubernetes.go: shared Kubernetes test helpers.oauth/oauth_setup.go+oauth_setup_test.go: wires up MarkLogic OAuth external security/App Server, Keycloak realm, and HAProxy for Authorization Code testing.oauth/oauth_authorization_code_test.go:TestOAuthAuthorizationCodeInfrastructure- TC1 (SessionID issued before authentication), TC2 (HAProxy affinity completes the flow), TC3 (bypassing HAProxy / cross-node callback is correctly rejected as a potential CSRF/state mismatch).oauth/session_affinity_test.go,oauth/oauth_load_balancer_affinity_test.go: additional HAProxy session-affinity coverage.Testing
go test -v -run TestOAuthAuthorizationCodeInfrastructure ./test/integration/marklogic-server/oauth- all 3 subtests pass on minikube.500 XDMP-OAUTH: Novel OAuth state... potential CSRF attackwhen affinity is bypassed)