#!/usr/bin/env bash # M7.1 tenant-gating smoke test. # # Drives compliance-smoke against a live Keycloak realm with five test # users (one per tenant_status), asserts the response code on each # endpoint, and exits non-zero on any mismatch. # # Pre-reqs (one-time): # * KC up at $KC_URL with realm $KC_REALM # * Client $KC_CLIENT has direct-access-grants enabled # * Users + tenant_status mappers per certifai/keycloak/realm-export.json # * compliance-smoke binary running and reachable at $SMOKE_URL # # Usage: # scripts/smoke.sh # uses defaults below # SMOKE_URL=... scripts/smoke.sh set -euo pipefail KC_URL="${KC_URL:-http://localhost:8080}" KC_REALM="${KC_REALM:-certifai}" KC_CLIENT="${KC_CLIENT:-certifai-dashboard}" SMOKE_URL="${SMOKE_URL:-http://localhost:3010}" readonly TOKEN_ENDPOINT="${KC_URL}/realms/${KC_REALM}/protocol/openid-connect/token" PASS=0 FAIL=0 red() { printf '\033[31m%s\033[0m' "$*"; } green() { printf '\033[32m%s\033[0m' "$*"; } yellow() { printf '\033[33m%s\033[0m' "$*"; } # Fetches an access token via direct access grant. Echoes the raw token. get_token() { local user="$1" pass="$2" curl -sS -X POST "$TOKEN_ENDPOINT" \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d "grant_type=password" \ -d "client_id=${KC_CLIENT}" \ -d "username=${user}" \ -d "password=${pass}" \ -d "scope=openid" \ | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p' } # Hits SMOKE_URL$path with the given method and (optional) bearer token, # asserts the response status code matches $want. assert_status() { local label="$1" method="$2" path="$3" want="$4" token="${5:-}" local args=(-sS -o /dev/null -w '%{http_code}' -X "$method" "${SMOKE_URL}${path}") if [[ -n "$token" ]]; then args+=(-H "Authorization: Bearer ${token}") fi local got got=$(curl "${args[@]}") if [[ "$got" == "$want" ]]; then printf ' %s %s %-4s %-15s → %s\n' "$(green PASS)" "$label" "$method" "$path" "$got" PASS=$((PASS + 1)) else printf ' %s %s %-4s %-15s → got %s, want %s\n' "$(red FAIL)" "$label" "$method" "$path" "$got" "$want" FAIL=$((FAIL + 1)) fi } header() { printf '\n%s %s\n' "$(yellow '##')" "$1" } # ---- Pre-flight ---------------------------------------------------------- header "Pre-flight" if ! curl -sS -o /dev/null -w '%{http_code}\n' "${SMOKE_URL}/api/v1/health" | grep -q '^200$'; then printf ' %s smoke service not reachable at %s\n' "$(red ERR)" "$SMOKE_URL" exit 2 fi if ! curl -sS -o /dev/null -w '%{http_code}\n' "${KC_URL}/realms/${KC_REALM}/.well-known/openid-configuration" | grep -q '^200$'; then printf ' %s Keycloak realm %s not reachable at %s\n' "$(red ERR)" "$KC_REALM" "$KC_URL" exit 2 fi printf ' %s smoke service + Keycloak both up\n' "$(green OK)" # ---- Public endpoint -------------------------------------------------- header "Public endpoint (no auth required)" assert_status anon GET /api/v1/health 200 # ---- Anonymous access to protected endpoints ---------------------------- header "Anonymous → 401 on protected endpoints" assert_status anon GET /api/v1/echo 401 assert_status anon POST /api/v1/echo 401 # ---- Bad token ---------------------------------------------------------- header "Bad token → 401" assert_status bogus GET /api/v1/echo 401 "not-a-real-jwt" assert_status bogus POST /api/v1/echo 401 "not-a-real-jwt" # ---- Active tenant (admin user) ----------------------------------------- header "admin@certifai.local (active) → full access" TOKEN=$(get_token admin@certifai.local admin) if [[ -z "$TOKEN" ]]; then printf ' %s failed to fetch token for admin\n' "$(red ERR)" exit 2 fi assert_status active GET /api/v1/echo 200 "$TOKEN" assert_status active POST /api/v1/echo 200 "$TOKEN" # ---- Active tenant (USER role) ------------------------------------------ header "user@certifai.local (active) → full access" TOKEN=$(get_token user@certifai.local user) assert_status active GET /api/v1/echo 200 "$TOKEN" assert_status active POST /api/v1/echo 200 "$TOKEN" # ---- Trial tenant ------------------------------------------------------- header "trial@acme.local (trial) → full access" TOKEN=$(get_token trial@acme.local trial) assert_status trial GET /api/v1/echo 200 "$TOKEN" assert_status trial POST /api/v1/echo 200 "$TOKEN" # ---- Frozen tenant ------------------------------------------------------ header "frozen@acme.local (frozen) → read-only, writes 402" TOKEN=$(get_token frozen@acme.local frozen) assert_status frozen GET /api/v1/echo 200 "$TOKEN" assert_status frozen POST /api/v1/echo 402 "$TOKEN" # ---- Archived tenant ---------------------------------------------------- header "archived@acme.local (archived) → 410 everywhere" TOKEN=$(get_token archived@acme.local archived) assert_status archived GET /api/v1/echo 410 "$TOKEN" assert_status archived POST /api/v1/echo 410 "$TOKEN" # ---- Summary ------------------------------------------------------------ printf '\n' if [[ "$FAIL" -gt 0 ]]; then printf '%s %d passed, %d failed\n' "$(red FAIL)" "$PASS" "$FAIL" exit 1 fi printf '%s %d/%d assertions passed\n' "$(green PASS)" "$PASS" "$PASS"