All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 41s
Finishes the unproven half of gap G04. Running the harness against real LLDAP, OpenLDAP and Keycloak found four defects that the full unit suite passed over. Every LLDAP user search pointed at a branch that does not exist. Config.userOU() defaulted to ou=users while LLDAP stores users under ou=people, and nothing set UserOU. LookupUser, ListUsers and ValidatePassword all derive from it, so all three silently found nothing against a stock LLDAP -- human login included, not only the export. Exposed by an export returning zero users while still emitting a membership referencing uid=admin, a snapshot the repo's own validator rejects. raw_attributes_well_formed, added one workplan earlier, rejected the LLDAP adapter's own _validation_warning annotation, so the exporter's output failed its own validation. Tooling annotations are exempt now, and a test proves the exemption does not weaken the rule. Migrated group memberships were dangling: resolveMemberDN passed a source DN through unchanged while entries were written to the target branch, so groups named entries the migrated directory does not contain. And empty groups could not load at all, since groupOfNames makes member a MUST -- they reference a placeholder entry the LDIF creates, an organizationalRole rather than a person, emitted only when some group needs it. The scenario-c compose file could not start: bitnami/openldap:2.6 does not exist, though it passed docker compose config. Pinned to the image the scenario was proved against. Proof: LLDAP -> export -> validate -> LDIF -> ldapadd into OpenLDAP 1.5.0, every entry added and every member resolving; a realm from the same export imports into Keycloak and serves discovery. KeyCape passes 5/5 conformance checks, a migrated Keycloak 4/5. Relying-party behaviour and MFA against a migrated realm remain unexercised, and credential/MFA migration is not supplied at all, so no harness can establish it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NV9oijZukGyGbRQGGKnK4P Assistant: claude-code Assistant-Model: opus Assistant-Process: 713576@bnt-lap001 Assistant-Session: 384c511d-9bce-4cb8-a676-2aef6c0c8df6
68 lines
2.8 KiB
Bash
Executable file
68 lines
2.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# test-scenario-b.sh — Scenario B: IAM swap (KeyCape -> Keycloak, same LLDAP directory).
|
|
#
|
|
# Exports the canonical directory, transforms it into a Keycloak realm, starts
|
|
# Keycloak over that realm, and runs the external conformance suite against it.
|
|
#
|
|
# Prerequisites: docker, docker compose, and `make -C src build` (root `make build` only compiles; the src target installs into bin/).
|
|
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$REPO_ROOT"
|
|
|
|
BUILD_DIR="$REPO_ROOT/build/scenario-b"
|
|
COMPOSE="docker compose -f docker-compose.scenario-b.yml"
|
|
ISSUER_URL="http://localhost:8080/realms/netkingdom"
|
|
|
|
die() { echo "scenario-b: $*" >&2; exit 1; }
|
|
|
|
# Check every prerequisite up front: failing half way through leaves a running
|
|
# stack and a half-written realm, which is harder to diagnose than not starting.
|
|
for binary in lldap-export keycape-to-keycloak; do
|
|
[ -x "bin/$binary" ] || die "bin/$binary is missing; run 'make -C src build' first"
|
|
done
|
|
command -v docker >/dev/null || die "docker is not installed"
|
|
command -v go >/dev/null || die "go is not installed"
|
|
|
|
cleanup() { $COMPOSE down -v >/dev/null 2>&1 || true; }
|
|
trap cleanup EXIT
|
|
|
|
mkdir -p "$BUILD_DIR/realm"
|
|
|
|
echo "=== Scenario B: IAM replacement ==="
|
|
|
|
echo "--- Step 1: start the directory ---"
|
|
$COMPOSE up -d lldap
|
|
timeout 120 bash -c 'until (exec 3<>/dev/tcp/127.0.0.1/3890) 2>/dev/null; do sleep 2; done' \
|
|
|| die "LLDAP did not accept connections within 120s"
|
|
|
|
echo "--- Step 2: export the canonical directory ---"
|
|
./bin/lldap-export \
|
|
--url "${LLDAP_URL:-ldap://localhost:3890}" \
|
|
--bind-dn "${LLDAP_BIND_DN:-cn=admin,ou=people,dc=netkingdom,dc=local}" \
|
|
--bind-pw "${LLDAP_BIND_PW:-adminpassword}" \
|
|
--base-dn "${LLDAP_BASE_DN:-dc=netkingdom,dc=local}" \
|
|
--output "$BUILD_DIR/canonical-export.yaml"
|
|
|
|
echo "--- Step 3: transform into a Keycloak realm ---"
|
|
./bin/keycape-to-keycloak \
|
|
--input "$BUILD_DIR/canonical-export.yaml" \
|
|
--realm netkingdom \
|
|
--issuer "${ISSUER:-$ISSUER_URL}" \
|
|
--output "$BUILD_DIR/realm/netkingdom-realm.json"
|
|
|
|
echo "--- Step 4: start Keycloak over the migrated realm ---"
|
|
$COMPOSE up -d keycloak
|
|
timeout 180 bash -c "until curl -sf '$ISSUER_URL/.well-known/openid-configuration' >/dev/null; do sleep 3; done" \
|
|
|| die "Keycloak did not serve discovery within 180s"
|
|
|
|
echo "--- Step 5: run the external conformance suite against Keycloak ---"
|
|
# The suite reads KEYCAPE_TEST_ISSUER and talks to the running issuer over HTTP.
|
|
# Run it from the module directory: the Go module root is src/, not the repo root.
|
|
KEYCAPE_TEST_ISSUER="$ISSUER_URL" \
|
|
go -C src test ./tests/conformance/... -v -count=1
|
|
|
|
echo "=== Scenario B PASSED ==="
|
|
echo "Note: this proves the migrated realm serves a conforming OIDC surface."
|
|
echo "It does not prove credential, MFA or relying-party behaviour was preserved."
|