11#! /usr/bin/env bash
2- # test-lab-09-04.sh — Lab 09-04: SSO Integration
3- # Module 09: iRedMail email server
4- # iredmail with Keycloak OIDC/SAML authentication
2+ # test-lab-09-04.sh — Lab 09-04: iRedMail SSO Integration
3+ # Tests: Keycloak running, LDAP federation config, Keycloak user sync from LDAP
54set -euo pipefail
6-
7- LAB_ID=" 09-04"
8- LAB_NAME=" SSO Integration"
9- MODULE=" iredmail"
105COMPOSE_FILE=" docker/docker-compose.sso.yml"
11- PASS=0
12- FAIL=0
13-
14- # ── Colors ────────────────────────────────────────────────────────────────────
15- RED=' \033[0;31m' ; GREEN=' \033[0;32m' ; YELLOW=' \033[1;33m'
16- CYAN=' \033[0;36m' ; NC=' \033[0m'
17-
18- pass () { echo -e " ${GREEN} [PASS]${NC} $1 " ; (( PASS++ )) ; }
19- fail () { echo -e " ${RED} [FAIL]${NC} $1 " ; (( FAIL++ )) ; }
20- info () { echo -e " ${CYAN} [INFO]${NC} $1 " ; }
21- warn () { echo -e " ${YELLOW} [WARN]${NC} $1 " ; }
6+ KC_PORT=" 8087"
7+ PASS=0; FAIL=0
8+ pass () { echo " [PASS] $1 " ; PASS=$(( PASS+ 1 )) ; }
9+ fail () { echo " [FAIL] $1 " ; FAIL=$(( FAIL+ 1 )) ; }
10+ section () { echo ; echo " === $1 ===" ; }
2211
23- echo -e " ${CYAN} ======================================${NC} "
24- echo -e " ${CYAN} Lab ${LAB_ID} : ${LAB_NAME}${NC} "
25- echo -e " ${CYAN} Module: ${MODULE}${NC} "
26- echo -e " ${CYAN} ======================================${NC} "
27- echo " "
12+ section " Container health"
13+ for c in iredmail-sso-ldap iredmail-sso-smtp-relay iredmail-sso-keycloak iredmail-sso-app; do
14+ if docker inspect --format ' {{.State.Running}}' " $c " 2> /dev/null | grep -q true ; then
15+ pass " Container $c is running"
16+ else
17+ fail " Container $c is not running"
18+ fi
19+ done
2820
29- # ── PHASE 1: Setup ────────────────────────────────────────────────────────────
30- info " Phase 1: Setup"
31- docker compose -f " ${COMPOSE_FILE} " up -d
32- info " Waiting 30s for ${MODULE} to initialize..."
33- sleep 30
21+ section " OpenLDAP connectivity"
22+ if timeout 5 bash -c ' echo > /dev/tcp/localhost/389' 2> /dev/null; then
23+ pass " OpenLDAP :389 reachable"
24+ else
25+ fail " OpenLDAP :389 not reachable"
26+ fi
27+ LDAP_SEARCH=$( ldapsearch -x -H ldap://localhost:389 \
28+ -b " dc=lab,dc=local" \
29+ -D " cn=admin,dc=lab,dc=local" \
30+ -w Lab04Password! -LLL -z 1 dn 2> /dev/null | head -3) || LDAP_SEARCH=" "
31+ if echo " $LDAP_SEARCH " | grep -q " dn:" ; then
32+ pass " LDAP admin bind and search successful"
33+ else
34+ fail " LDAP admin bind failed"
35+ fi
3436
35- # ── PHASE 2: Health Checks ────────────────────────────────────────────────────
36- info " Phase 2: Health Checks"
37+ section " Keycloak health"
38+ KC_HEALTH=$( curl -sf " http://localhost:${KC_PORT} /health/ready" 2> /dev/null) || KC_HEALTH=" "
39+ if echo " $KC_HEALTH " | grep -q " UP" ; then
40+ pass " Keycloak health/ready = UP"
41+ else
42+ fail " Keycloak health/ready not UP"
43+ fi
3744
38- if docker compose -f " ${COMPOSE_FILE} " ps | grep -q " running\|Up" ; then
39- pass " Container is running"
45+ section " Keycloak admin API + realm"
46+ KC_TOKEN=$( curl -sf -X POST \
47+ " http://localhost:${KC_PORT} /realms/master/protocol/openid-connect/token" \
48+ -H " Content-Type: application/x-www-form-urlencoded" \
49+ -d " client_id=admin-cli&username=admin&password=Lab04Admin!&grant_type=password" 2> /dev/null \
50+ | grep -o ' "access_token":"[^"]*"' | cut -d' "' -f4) || KC_TOKEN=" "
51+ if [ -n " $KC_TOKEN " ]; then
52+ pass " Keycloak admin token obtained"
4053else
41- fail " Container is not running "
54+ fail " Keycloak admin login failed "
4255fi
4356
44- # ── PHASE 3: Functional Tests ─────────────────────────────────────────────────
45- info " Phase 3: Functional Tests (Lab 04 — SSO Integration)"
57+ if [ -n " $KC_TOKEN " ]; then
58+ curl -sf -X POST " http://localhost:${KC_PORT} /admin/realms" \
59+ -H " Authorization: Bearer $KC_TOKEN " -H " Content-Type: application/json" \
60+ -d ' {"realm":"it-stack","enabled":true}' 2> /dev/null || true
61+ REALM_CHECK=$( curl -sf " http://localhost:${KC_PORT} /admin/realms/it-stack" \
62+ -H " Authorization: Bearer $KC_TOKEN " 2> /dev/null) || REALM_CHECK=" "
63+ if echo " $REALM_CHECK " | grep -q ' "realm":"it-stack"' ; then
64+ pass " Keycloak realm 'it-stack' exists"
65+ else
66+ fail " Keycloak realm 'it-stack' not found"
67+ fi
68+ else
69+ fail " Skipping realm check (no admin token)"
70+ fi
4671
47- # TODO: Add module-specific functional tests here
48- # Example:
49- # if curl -sf http://localhost:25/health > /dev/null 2>&1; then
50- # pass "Health endpoint responds"
51- # else
52- # fail "Health endpoint not reachable"
53- # fi
72+ section " Keycloak LDAP user federation"
73+ if [ -n " $KC_TOKEN " ]; then
74+ LDAP_PROVIDER=$( curl -sf -X POST \
75+ " http://localhost:${KC_PORT} /admin/realms/it-stack/components" \
76+ -H " Authorization: Bearer $KC_TOKEN " \
77+ -H " Content-Type: application/json" \
78+ -d ' {"name":"ldap","providerId":"ldap","providerType":"org.keycloak.storage.UserStorageProvider","config":{"vendor":["other"],"connectionUrl":["ldap://ldap:389"],"bindDn":["cn=readonly,dc=lab,dc=local"],"bindCredential":["ReadOnlyPass04!"],"usersDn":["ou=People,dc=lab,dc=local"],"usernameLDAPAttribute":["mail"],"rdnLDAPAttribute":["uid"],"uuidLDAPAttribute":["entryUUID"],"userObjectClasses":["inetOrgPerson"],"importEnabled":["true"],"syncRegistrations":["false"]}}' \
79+ 2> /dev/null; echo $? ) || LDAP_PROVIDER=0
80+ COMPONENTS=$( curl -sf \
81+ " http://localhost:${KC_PORT} /admin/realms/it-stack/components?type=org.keycloak.storage.UserStorageProvider" \
82+ -H " Authorization: Bearer $KC_TOKEN " 2> /dev/null) || COMPONENTS=" "
83+ if echo " $COMPONENTS " | grep -q ' "providerId":"ldap"' ; then
84+ pass " Keycloak LDAP user federation provider registered"
85+ else
86+ fail " Keycloak LDAP user federation not found"
87+ fi
88+ else
89+ fail " Skipping LDAP federation check (no admin token)"
90+ fi
5491
55- warn " Functional tests for Lab 09-04 pending implementation"
92+ section " iRedMail LDAP config in env"
93+ IM_ENV=$( docker inspect iredmail-sso-app --format ' {{json .Config.Env}}' 2> /dev/null) || IM_ENV=" []"
94+ if echo " $IM_ENV " | grep -q " LDAP_SERVER_HOST=ldap" ; then
95+ pass " LDAP_SERVER_HOST=ldap configured"
96+ else
97+ fail " LDAP_SERVER_HOST not set to 'ldap'"
98+ fi
99+ if echo " $IM_ENV " | grep -q ' "LDAP_BIND_DN=cn=readonly,dc=lab,dc=local"' ; then
100+ pass " LDAP_BIND_DN = cn=readonly,dc=lab,dc=local"
101+ else
102+ fail " LDAP_BIND_DN not configured"
103+ fi
56104
57- # ── PHASE 4: Cleanup ──────────────────────────────────────────────────────────
58- info " Phase 4: Cleanup"
59- docker compose -f " ${COMPOSE_FILE} " down -v --remove-orphans
60- info " Cleanup complete"
105+ section " Roundcube webmail"
106+ HTTP_CODE=$( curl -sw ' %{http_code}' -o /dev/null http://localhost:9080/mail/ 2> /dev/null) || HTTP_CODE=" 000"
107+ if echo " $HTTP_CODE " | grep -qE " ^(200|301|302)" ; then
108+ pass " Roundcube /mail/ HTTP $HTTP_CODE "
109+ else
110+ fail " Roundcube /mail/ returned $HTTP_CODE "
111+ fi
61112
62- # ── Results ───────────────────────────────────────────────────────────────────
63- echo " "
64- echo -e " ${CYAN} ======================================${NC} "
65- echo -e " Lab ${LAB_ID} Complete"
66- echo -e " ${GREEN} PASS: ${PASS}${NC} | ${RED} FAIL: ${FAIL}${NC} "
67- echo -e " ${CYAN} ======================================${NC} "
113+ section " SMTP port"
114+ SMTP_BANNER=$( echo " QUIT" | timeout 5 nc localhost 9025 2> /dev/null | head -1) || SMTP_BANNER=" "
115+ if echo " $SMTP_BANNER " | grep -q " 220" ; then
116+ pass " SMTP :9025 banner OK"
117+ else
118+ fail " SMTP :9025 banner failed"
119+ fi
68120
69- if [ " ${FAIL} " -gt 0 ]; then
70- exit 1
121+ section " Keycloak OIDC discovery"
122+ KC_OIDC=$( curl -sf " http://localhost:${KC_PORT} /realms/it-stack/.well-known/openid-configuration" 2> /dev/null) || KC_OIDC=" "
123+ if echo " $KC_OIDC " | grep -q ' "issuer"' ; then
124+ pass " Keycloak OIDC discovery reachable"
125+ else
126+ fail " Keycloak OIDC discovery failed"
71127fi
128+
129+ echo
130+ echo " ====================================="
131+ echo " iRedMail Lab 09-04 Results"
132+ echo " PASS: $PASS FAIL: $FAIL "
133+ echo " ====================================="
134+ [ " $FAIL " -eq 0 ] && exit 0 || exit 1
0 commit comments