Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
a5169cb
feat: add OpenLDAP multi-suffix Docker infrastructure (#72)
JayVDZ Mar 26, 2026
e7af851
refactor: standardise test domain naming with Reynolds-inspired conve…
JayVDZ Mar 26, 2026
88ffadb
docs: update OpenLDAP integration testing plan — Phase 1 complete (#72)
JayVDZ Mar 26, 2026
93bb543
feat: add OpenLDAP population script — Phase 2 (#72)
JayVDZ Mar 26, 2026
24bfcd5
feat: parameterise test framework for multi-directory support — Phase…
JayVDZ Mar 26, 2026
f95ba02
feat: add directory type to interactive test runner menu (#72)
JayVDZ Mar 26, 2026
601d8f4
refactor: introduce LdapDirectoryType enum, retire IsActiveDirectory …
JayVDZ Mar 26, 2026
0090743
refactor: add SambaAD directory type, make SupportsPaging computed (#72)
JayVDZ Mar 26, 2026
1cdf675
docs: update C4 and Mermaid diagrams for v0.7.1 architecture
JayVDZ Mar 26, 2026
c468bae
feat: multi-directory partition discovery and RFC 4512 schema discove…
JayVDZ Mar 26, 2026
6cc5bef
feat: Phase 5 — Scenario 1 Joiner passing against OpenLDAP at Nano sc…
JayVDZ Mar 26, 2026
db7cf90
refactor: parameterise Scenario 1 verification for multi-directory su…
JayVDZ Mar 26, 2026
925d907
refactor: parameterise Scenario 1 verification for multi-directory su…
JayVDZ Mar 26, 2026
db5615e
fix: skip changelog query during full import, use full import for Ope…
JayVDZ Mar 26, 2026
7bd76d2
fix: synthesise distinguishedName on import for non-AD directories (#72)
JayVDZ Mar 26, 2026
e09d423
feat: accesslog-based delta import for OpenLDAP (#72)
JayVDZ Mar 26, 2026
8183cd7
fix: DN-aware RDN attribute detection for multi-directory export (#72)
JayVDZ Mar 26, 2026
8fc9437
feat: Scenario 1 passing against OpenLDAP — all 8 test steps (#72)
JayVDZ Mar 26, 2026
5d146cc
docs: update plan doc with Phase 4-5 deliverables and Phase 6 roadmap…
JayVDZ Mar 26, 2026
5ddbf04
feat: parameterise Scenario 9 for OpenLDAP multi-partition testing (#72)
JayVDZ Mar 26, 2026
5f38de8
feat: add phase-specific activity messages during import connector op…
JayVDZ Mar 26, 2026
d842ae7
fix: use only SINGLE-VALUE attributes in Scenario 9 OpenLDAP import m…
JayVDZ Mar 26, 2026
25102c5
refactor: consolidate pending export reconciliation into SyncEngine (…
JayVDZ Mar 26, 2026
08d4abe
fix: offset OpenLDAP seed indices to avoid uid collision with CSV exp…
JayVDZ Mar 27, 2026
39d8328
feat: parameterise Scenarios 6 and 7 for OpenLDAP (#72)
JayVDZ Mar 27, 2026
0b259f8
fix: remove broken docker cp in Scenario 6, use host bind mount (#72)
JayVDZ Mar 27, 2026
8323adc
feat: add OpenLDAP Source/Target instances to Get-DirectoryConfig (#72)
JayVDZ Mar 27, 2026
caea4d7
docs: update plan doc — S6, S7 complete in Phase 6 (#72)
JayVDZ Mar 27, 2026
1d9de76
fix: use case-insensitive comparers for expression attribute dictiona…
JayVDZ Mar 27, 2026
988aca8
feat: parameterise Scenario 2 for OpenLDAP — blocked by #435 (#72)
JayVDZ Mar 27, 2026
44f9761
docs: update plan — S2 parameterised, blocked by #435 (#72)
JayVDZ Mar 27, 2026
0c294ce
fix: prevent attribute change cascade delete when attributes deleted …
JayVDZ Mar 27, 2026
dd8cf44
feat: allow MVA→SVA import attribute flow with first-value selection …
JayVDZ Mar 27, 2026
0a3db16
feat: update S2 and S9 import mappings for full MVA attribute set (#72)
JayVDZ Mar 27, 2026
9c85564
docs: S2 passing — unblocked by #435 (#72)
JayVDZ Mar 27, 2026
517ff98
perf: convert MVO creates from EF AddRange to COPY binary (#338)
JayVDZ Mar 27, 2026
faa7832
feat: parameterise Scenario 5 for OpenLDAP (#72)
JayVDZ Mar 27, 2026
da14503
docs: S5 passing against OpenLDAP (#72)
JayVDZ Mar 27, 2026
2c265fa
docs: update MVO COPY binary plan with progress and corrections (#436)
JayVDZ Mar 27, 2026
b8f4729
docs: update plan — S3 stub, S4/S8 remain for Phase 6 (#72)
JayVDZ Mar 27, 2026
8dda58a
docs: detail MVO update complexity analysis in plan doc (#436)
JayVDZ Mar 27, 2026
bdde1a8
feat: parameterise Scenario 4 for OpenLDAP (#72)
JayVDZ Mar 27, 2026
8e6f6af
docs: S4 passing against OpenLDAP — only S8 remains (#72)
JayVDZ Mar 27, 2026
4f4d4d4
feat: LDAP connector placeholder member handling for groupOfNames (#72)
JayVDZ Mar 28, 2026
f5634e8
feat: parameterise S8 setup and add OpenLDAP group helpers (#72)
JayVDZ Mar 28, 2026
d92b55c
wip: parameterise Invoke-Scenario8 — header, helpers, infra checks (#72)
JayVDZ Mar 28, 2026
8079fde
feat: parameterise Invoke-Scenario8 for OpenLDAP (#72)
JayVDZ Mar 28, 2026
dbef266
feat: update integration test runner for S8 OpenLDAP support (#72)
JayVDZ Mar 28, 2026
efa4f78
fix: use stdin piping for OpenLDAP LDIF imports, use ou=Groups (#72)
JayVDZ Mar 28, 2026
d966344
fix: container name matching and try/catch syntax for OpenLDAP S8 (#72)
JayVDZ Mar 28, 2026
989c2dc
docs: update plan — S8 parameterised, Gap 6 resolved (#72)
JayVDZ Mar 28, 2026
48be1c5
perf: skip nugatory attribute recall before immediate MVO deletion (#…
JayVDZ Mar 28, 2026
9cd5e11
fix: skip completed container+type combos on subsequent import pages …
JayVDZ Mar 28, 2026
3e8e385
docs: add paged results cookie fix to plan risks (#72)
JayVDZ Mar 28, 2026
691222f
feat: add partition-scoped run profiles to Scenarios 2 and 8 (#353)
JayVDZ Mar 28, 2026
34323d6
fix: tolerate CouldNotJoinDueToExistingJoin during initial S8 sync (#72)
JayVDZ Mar 28, 2026
f94c335
fix: skip base OpenLDAP population for S8 — Target must start empty (…
JayVDZ Mar 28, 2026
e3db8c2
fix: use paged results for accesslog watermark query (#72)
JayVDZ Mar 29, 2026
0217d58
feat: delta import fallback to full import for OpenLDAP accesslog (#72)
JayVDZ Mar 29, 2026
5d77a79
fix: handle accesslog size limit in both watermark and delta queries …
JayVDZ Mar 29, 2026
faf4110
fix: deduplicate accesslog entries by DN in delta import (#72)
JayVDZ Mar 29, 2026
8428afd
fix: use full confirming import for OpenLDAP delta forward sync (#72)
JayVDZ Mar 29, 2026
57a09b9
fix: use full import/sync for OpenLDAP drift detection in S8 (#72)
JayVDZ Mar 29, 2026
0a38d44
fix: skip delete objects in AddExternalIdsToCollection (#72)
JayVDZ Mar 29, 2026
2778014
fix: proper delete handling in accesslog-based delta import (#72)
JayVDZ Mar 29, 2026
6693985
fix: revert to delta ops and enable accesslog for Glitterband (#72)
JayVDZ Mar 29, 2026
c7cbe6c
fix: filter accesslog delta entries by partition scope (#72)
JayVDZ Mar 29, 2026
66ebc88
fix: do not skip delete entries during DN deduplication (#72)
JayVDZ Mar 29, 2026
fc8371a
fix: handle Reference attributes in MVO change tracking (#72)
JayVDZ Mar 29, 2026
4ce3161
fix: serialise container+objectType combos for OpenLDAP paging (#72)
JayVDZ Mar 29, 2026
75f3058
debug: add combo iteration logging for paging investigation
JayVDZ Mar 30, 2026
f45b475
fix: serialise container+objectType combos for OpenLDAP paging (#72)
JayVDZ Mar 30, 2026
45e4bad
feat: add OpenLDAP snapshot support for integration tests (#72)
JayVDZ Mar 30, 2026
1243ccf
fix: null cross-batch and cross-partition ReferenceValueId FKs (#72)
JayVDZ Mar 30, 2026
86636c9
fix: clear stale accesslog data on OpenLDAP snapshot startup (#72)
JayVDZ Mar 30, 2026
f66666e
docs: update GUID/UUID plan — mark Phase 4 (OpenLDAP/entryUUID) complete
JayVDZ Mar 30, 2026
1a285d7
feat: add bundled Keycloak IdP for zero-config devcontainer SSO (#197)
JayVDZ Mar 30, 2026
46f3c5d
fix: allow HTTP authority for bundled Keycloak in development (#197)
JayVDZ Mar 30, 2026
4a302db
fix: Keycloak realm import and OIDC configuration (#197)
JayVDZ Mar 30, 2026
54f7fba
fix: remove KC_HOSTNAME — use dual valid issuers instead (#197)
JayVDZ Mar 30, 2026
9ce1863
fix: use localhost for all Keycloak access in Docker-in-Docker (#197)
JayVDZ Mar 30, 2026
c82fab4
fix: rewrite OIDC browser redirects from Docker DNS to localhost (#197)
JayVDZ Mar 30, 2026
f5b341a
fix: add users to realm, socat bridge for port forwarding (#197)
JayVDZ Mar 30, 2026
f3ce918
fix: set ValidIssuers on OIDC handler for dual-issuer support (#197)
JayVDZ Mar 30, 2026
4fcdcf7
docs: update port references and documentation for bundled Keycloak (…
JayVDZ Mar 30, 2026
d2d61b5
docs: close devcontainer IdP plan — move to done (#197)
JayVDZ Mar 30, 2026
d8ee509
fix: replace EF Include chain with direct SQL for CSO reference loadi…
JayVDZ Mar 31, 2026
a9d9252
fix: always set scalar FK for MVO reference attributes (#72)
JayVDZ Mar 31, 2026
35a467b
fix: handle full DN in OpenLDAP group member add/remove helpers (#72)
JayVDZ Mar 31, 2026
6a62468
fix: preserve OpenLDAP snapshot images during jim-reset (#72)
JayVDZ Mar 31, 2026
8fc3459
Increasing the disk size of devcontainer to allow for more integratio…
JayVDZ Mar 31, 2026
c05605a
fix: start Keycloak socat bridge during integration tests
JayVDZ Mar 31, 2026
7f48321
fix: configure OpenLDAP accesslog database for large-scale delta impo…
JayVDZ Mar 31, 2026
4b700ee
fix: handle camelCase in SplitOnCapitalLetters for LDAP object types …
JayVDZ Mar 31, 2026
1450c0c
fix: use nohup for Keycloak socat bridge in integration test runner
JayVDZ Mar 31, 2026
c866c88
fix: fully detach Keycloak socat bridge from PowerShell process tree
JayVDZ Mar 31, 2026
62c5e8e
fix: use correct GroupContainer (Entitlements) for Scenario 8 validation
JayVDZ Mar 31, 2026
b64911a
Removing word splitting from external object type text
JayVDZ Mar 31, 2026
58a5d58
fix: prevent unnecessary delta import fallback and eliminate phantom …
JayVDZ Mar 31, 2026
c05b1a8
fix: resolve CRLF error in Keycloak socat bridge script
JayVDZ Mar 31, 2026
1fcea8b
docs: update OpenLDAP integration testing plan — S8 complete, snapsho…
JayVDZ Mar 31, 2026
5184e02
fix: use plain LDAP for Samba AD test validation searches
JayVDZ Mar 31, 2026
2b67d83
fix: use samba-tool listmembers for Samba AD group member queries in …
JayVDZ Mar 31, 2026
0213dda
feat: add 'All' directory type option to integration test runner (#72)
JayVDZ Mar 31, 2026
ba8e015
docs: add OpenLDAP and -DirectoryType All documentation (#72)
JayVDZ Mar 31, 2026
1ee2478
fix: skip Samba AD image build for OpenLDAP integration test runs (#72)
JayVDZ Apr 1, 2026
dd822c1
docs: update plan with full regression results — all scenarios pass (…
JayVDZ Apr 1, 2026
740c47c
fix: remove pre-populated noise users from S1 target directory (#72)
JayVDZ Apr 1, 2026
6d30120
fix: two-phase parallel MVO persistence to prevent cross-partition FK…
JayVDZ Apr 1, 2026
c344d12
fix: install socat in Dockerfile to guarantee Keycloak port bridge av…
JayVDZ Apr 1, 2026
107d2cc
fix: preserve OpenLDAP base image during integration test cleanup
JayVDZ Apr 1, 2026
bc037e9
docs: update plan with full regression results — all scenarios pass (…
JayVDZ Apr 1, 2026
5bda32b
feat: parallel OpenLDAP imports using connection-per-combo strategy (…
JayVDZ Apr 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ FROM mcr.microsoft.com/devcontainers/dotnet:1-9.0-bookworm
RUN rm -f /etc/apt/sources.list.d/yarn.list 2>/dev/null; \
rm -f /etc/apt/keyrings/yarn.gpg 2>/dev/null; \
apt-get update || true

# Install socat for Keycloak port-forwarding bridge.
# Docker-in-Docker proxy ports aren't auto-forwarded by VS Code Dev Containers;
# socat provides a userspace bridge that VS Code detects and forwards to the host.
RUN apt-get install -y --no-install-recommends socat \
&& rm -rf /var/lib/apt/lists/*
13 changes: 9 additions & 4 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "JIM Development Environment",
"build": {
"dockerfile": "Dockerfile"
"dockerfile": "Dockerfile"
},
// Features - pre-built development tools
"features": {
Expand Down Expand Up @@ -103,7 +103,8 @@
"forwardPorts": [
5432, // PostgreSQL
5200, // JIM.Web (HTTP) - also serves API at /api/
5201 // JIM.Web (HTTPS) - also serves API at /api/
5201, // JIM.Web (HTTPS) - also serves API at /api/
8181 // Keycloak IdP (OIDC + admin console)
],
// Port labels and auto-forward behaviour
"portsAttributes": {
Expand All @@ -118,6 +119,10 @@
"5201": {
"label": "JIM Web + API (HTTPS)",
"onAutoForward": "silent"
},
"8181": {
"label": "Keycloak IdP",
"onAutoForward": "silent"
}
},
// Container user
Expand All @@ -140,10 +145,10 @@
"capAdd": [
"SYS_PTRACE"
],
// Improved build performance
// Improved build performance and integration test directory snapshot storage
"hostRequirements": {
"cpus": 8,
"memory": "32gb",
"storage": "64gb"
"storage": "120gb"
}
}
63 changes: 49 additions & 14 deletions .devcontainer/jim-aliases.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ Database Management:
jim-db-logs - View database logs
jim-postgres-tune - Auto-tune PostgreSQL for current devcontainer specs

Identity Provider (Keycloak):
jim-keycloak - Start Keycloak IdP (for local F5 debugging)
jim-keycloak-stop - Stop Keycloak IdP
jim-keycloak-logs - View Keycloak logs

Docker Stack Management (auto-kills local JIM processes):
jim-stack - Start Docker stack
jim-stack-logs - View Docker stack logs
Expand Down Expand Up @@ -168,6 +173,32 @@ jim-db-logs() {
docker compose $(_jim_db_compose) logs -f
}

# Standalone Keycloak IdP (local debugging workflow - use with jim-db + F5)
# Starts the bundled Keycloak from docker-compose.override.yml without the full stack.
jim-keycloak() {
docker compose -f docker-compose.yml -f docker-compose.override.yml up -d jim.keycloak
_jim_keycloak_bridge
}
jim-keycloak-stop() {
pkill -f 'socat.*TCP:127.0.0.1:8180' 2>/dev/null || true
docker compose -f docker-compose.yml -f docker-compose.override.yml stop jim.keycloak
docker compose -f docker-compose.yml -f docker-compose.override.yml rm -f jim.keycloak
}
jim-keycloak-logs() {
docker compose -f docker-compose.yml -f docker-compose.override.yml logs -f jim.keycloak
}

# Start a userspace port forwarder for Keycloak so VS Code can forward it.
# Docker-in-Docker proxy ports aren't forwarded by VS Code Dev Containers
# unless they were present at devcontainer build time. This socat bridge
# runs as the vscode user, which VS Code detects and forwards to the host.
_jim_keycloak_bridge() {
pkill -f 'socat.*TCP:127.0.0.1:8180' 2>/dev/null || true
if command -v socat &>/dev/null; then
socat TCP-LISTEN:8181,fork,reuseaddr,bind=0.0.0.0 TCP:127.0.0.1:8180 &
fi
}

# Kill any locally-running JIM .NET processes (jim-web, jim-worker, jim-scheduler)
# so they don't hold ports that Docker containers need to bind
_jim_kill_local() {
Expand All @@ -181,12 +212,13 @@ _jim_kill_local() {
}

# Clear any previous aliases before defining functions (zsh cannot redefine alias as function)
unalias jim-stack jim-stack-logs jim-stack-down jim-restart jim-build jim-build-web jim-build-worker jim-build-scheduler jim-cleanup jim-reset jim-db jim-db-stop jim-db-logs 2>/dev/null || true
unalias jim-stack jim-stack-logs jim-stack-down jim-restart jim-build jim-build-web jim-build-worker jim-build-scheduler jim-cleanup jim-reset jim-db jim-db-stop jim-db-logs jim-keycloak jim-keycloak-stop jim-keycloak-logs 2>/dev/null || true

# Docker stack management
jim-stack() {
_jim_kill_local
docker compose $(_jim_compose) up -d
_jim_keycloak_bridge
}
jim-stack-logs() {
docker compose $(_jim_compose) logs -f
Expand All @@ -199,6 +231,7 @@ jim-stack-down() {
jim-restart() {
_jim_kill_local
docker compose $(_jim_compose) down && docker compose $(_jim_compose) up -d --force-recreate
_jim_keycloak_bridge
}

# Docker builds (rebuild and start services)
Expand All @@ -209,6 +242,7 @@ _jim_version_suffix() {
jim-build() {
_jim_kill_local
VERSION_SUFFIX="$(_jim_version_suffix)" docker compose $(_jim_compose) up -d --build
_jim_keycloak_bridge
}
jim-build-web() {
_jim_kill_local
Expand Down Expand Up @@ -244,15 +278,15 @@ jim-cleanup() {
df -h / | tail -1
}

# Reset (preserves Samba AD snapshot images — they take a long time to build)
# Reset (preserves Samba AD and OpenLDAP snapshot images — they take a long time to build)
jim-reset() {
docker compose $(_jim_compose) down --volumes
docker compose -f docker-compose.integration-tests.yml --profile scenario2 --profile scenario8 down --volumes --remove-orphans 2>/dev/null || true
docker rm -f samba-ad-primary samba-ad-source samba-ad-target sqlserver-hris-a oracle-hris-b postgres-target openldap-test mysql-test 2>/dev/null || true
docker image prune -af --filter "label!=jim.samba.snapshot-hash" --filter "label!=jim.samba.build-hash" 2>/dev/null || true
docker image prune -af --filter "label!=jim.samba.snapshot-hash" --filter "label!=jim.samba.build-hash" --filter "label!=jim.openldap.snapshot-hash" --filter "label!=jim.openldap.build-hash" 2>/dev/null || true
docker volume ls --format "{{.Name}}" | grep jim-integration | xargs -r docker volume rm 2>/dev/null || true
docker volume rm -f jim-db-volume jim-logs-volume 2>/dev/null || true
echo "JIM reset complete. Containers, images, and volumes removed (snapshots preserved). Run jim-build to rebuild."
echo "JIM reset complete. Containers, images, and volumes removed (Samba AD & OpenLDAP snapshots preserved). Run jim-build to rebuild."
}

# Structurizr diagram export
Expand Down Expand Up @@ -281,32 +315,33 @@ jim-diagrams() {
# Remove any stale container
docker rm -f "${container_name}" 2>/dev/null

# Start Structurizr Lite (adrs mount resolves the symlink inside the container)
echo "Starting Structurizr Lite on port ${port}..."
# Start Structurizr Local (adrs mount resolves the symlink inside the container)
# Note: structurizr/lite is deprecated; using structurizr/structurizr with 'local' command
echo "Starting Structurizr Local on port ${port}..."
docker run -d --name "${container_name}" \
-p "${port}:8080" \
-v "${structurizr_dir}:/usr/local/structurizr" \
-v "${repo_root}/docs/adrs:/usr/local/structurizr/adrs" \
structurizr/lite > /dev/null
structurizr/structurizr local > /dev/null

# Wait for Structurizr Lite to be ready
echo "Waiting for Structurizr Lite to start..."
# Wait for Structurizr Local to be ready
echo "Waiting for Structurizr Local to start..."
local attempts=0
while [ $attempts -lt 30 ]; do
if curl -sf "http://localhost:${port}/workspace/diagrams" > /dev/null 2>&1; then
if curl -sf "http://localhost:${port}/workspace/1/diagrams" > /dev/null 2>&1; then
break
fi
attempts=$((attempts + 1))
sleep 2
done

if [ $attempts -ge 30 ]; then
echo "ERROR: Structurizr Lite failed to start within 60 seconds."
echo "ERROR: Structurizr Local failed to start within 60 seconds."
docker rm -f "${container_name}" > /dev/null 2>&1
return 1
fi

echo "Structurizr Lite is ready."
echo "Structurizr Local is ready."

# Remove old images (light, dark, and legacy root-level)
rm -f "${repo_root}/docs/diagrams/images"/jim-structurizr-1-*.svg
Expand All @@ -315,12 +350,12 @@ jim-diagrams() {

# Export diagrams (light + dark)
node "${structurizr_dir}/export-diagrams.js" \
"http://localhost:${port}/workspace/diagrams" \
"http://localhost:${port}/workspace/1/diagrams" \
"${repo_root}/docs/diagrams/images"
local export_rc=$?

# Cleanup
echo "Stopping Structurizr Lite..."
echo "Stopping Structurizr Local..."
docker rm -f "${container_name}" > /dev/null 2>&1

if [ $export_rc -eq 0 ]; then
Expand Down
Loading
Loading