Skip to content

MLE-29912 OAuth 2.0 Authorization Code Flow: Load Balancer Session Affinity Testing - #190

Merged
pengzhouml merged 4 commits into
developfrom
feature/MLE-29912-oauth-test
Jul 29, 2026
Merged

MLE-29912 OAuth 2.0 Authorization Code Flow: Load Balancer Session Affinity Testing#190
pengzhouml merged 4 commits into
developfrom
feature/MLE-29912-oauth-test

Conversation

@pengzhouml

Copy link
Copy Markdown
Collaborator

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

  • New Client interface methods:
    • ImportCertificateAuthority - imports a PEM-encoded CA into the Security database (new doPlainText request helper).
    • EnsureOAuthExternalSecurity - idempotently creates/updates an external-security OAuth config (oauth-server block: 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).
  • Added config structs (OAuthExternalSecurityConfig, OAuthAppServerConfig) with validation, and payload builders (BuildOAuthExternalSecurityPayload, BuildOAuthAppServerPayload).
  • Optional OAuth fields (client secret, redirect URI, JWT issuer URI) are only included in the payload when non-empty, avoiding unintentionally clearing existing values on update.

pkg/k8sutil/haProxy.go

  • Mounts the ssl-certificate volume (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

  • Fixes wait_bootstrap_ready for MarkLogic 12.1+: the TLS-enabled bootstrap host now returns 500 XDMP-NOUSER instead of 403 when queried unauthenticated. The check now accepts either signal (403, or 500 with XDMP-NOUSER body) as "ready," with proper temp-file cleanup for the captured response body.

Test doubles

  • internal/controller/marklogicgroup_controller_test.go and pkg/k8sutil/dynamic_reconcile_test.go: updated fake/stub management clients to implement the three new Client interface 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.
  • README.md (top-level integration dir) and oauth/README.md: document suite layout, prerequisites, env vars, and how to run each test group.

Testing

  • go test -v -run TestOAuthAuthorizationCodeInfrastructure ./test/integration/marklogic-server/oauth - all 3 subtests pass on minikube.
  • Re-validated on a real AWS EKS cluster (2-node, MarkLogic 12.1, operator built for linux/amd64, images via ECR):
    • TC1_SessionID_before_authentication - PASS
    • TC2_affinity_completes_flow - PASS
    • TC3_cross_node_callback_fails - PASS (correctly returns 500 XDMP-OAUTH: Novel OAuth state... potential CSRF attack when affinity is bypassed)
    • Confirms the flow relies only on the operator-managed in-cluster HAProxy ClusterIP service - no AWS ALB/NLB required.

Copilot AI review requested due to automatic review settings July 28, 2026 23:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/mlmanage with 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 -k when 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 -k so the callback path also validates TLS using the mounted CA bundle; avoid adding --fail here 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.

Comment thread test/integration/marklogic-server/oauth/oauth_load_balancer_affinity_test.go Outdated
Comment thread test/integration/marklogic-server/oauth/oauth_authorization_code_test.go Outdated
Comment thread pkg/mlmanage/client.go
pengzhouml and others added 3 commits July 28, 2026 19:15
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>
@pengzhouml
pengzhouml merged commit 5bfca4f into develop Jul 29, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants