fix: exempt /api/clubs/me from tenant validation

- Add path exemption in TenantValidationMiddleware for /api/clubs/me
- Change authorization policy from RequireMember to RequireViewer
- Fix KEYCLOAK_CLIENT_ID in docker-compose.yml (workclub-app not workclub-api)
- Endpoint now works without X-Tenant-Id header as intended
- Other endpoints still protected by tenant validation

This fixes the chicken-and-egg problem where frontend needs to call
/api/clubs/me to discover available clubs before selecting a tenant.
This commit is contained in:
WorkClub Automation
2026-03-05 21:32:37 +01:00
parent 18be0fb183
commit ffc4062eba
45 changed files with 5519 additions and 579 deletions

View File

@@ -0,0 +1,95 @@
#!/bin/bash
# Phase 6: Edge Cases (Scenarios 52-57)
source /tmp/qa-test-env.sh
echo "=========================================="
echo "Phase 6: Edge Cases & Security (S52-S57)"
echo "=========================================="
echo ""
# Scenario 52: Invalid JWT (malformed)
echo "=== SCENARIO 52: Invalid JWT (Malformed Token) ==="
curl -s -w "\nHTTP:%{http_code}\n" "$API_BASE/api/tasks" \
-H "Authorization: Bearer invalid.malformed.token" \
-H "X-Tenant-Id: $TENANT_TENNIS" | tee .sisyphus/evidence/final-qa/s52-invalid-jwt.json
echo ""
# Scenario 53: Missing Authorization Header
echo "=== SCENARIO 53: Missing Authorization Header ==="
curl -s -w "\nHTTP:%{http_code}\n" "$API_BASE/api/tasks" \
-H "X-Tenant-Id: $TENANT_TENNIS" | tee .sisyphus/evidence/final-qa/s53-no-auth.json
echo ""
# Scenario 54: Valid token but unauthorized tenant (tenant not in claims)
echo "=== SCENARIO 54: Unauthorized Tenant Access ==="
FAKE_TENANT="99999999-9999-9999-9999-999999999999"
curl -s -w "\nHTTP:%{http_code}\n" "$API_BASE/api/tasks" \
-H "Authorization: Bearer $TOKEN_ADMIN" \
-H "X-Tenant-Id: $FAKE_TENANT" | tee .sisyphus/evidence/final-qa/s54-unauthorized-tenant.json
echo ""
# Scenario 55: SQL Injection Attempt
echo "=== SCENARIO 55: SQL Injection Attempt ==="
curl -s -w "\nHTTP:%{http_code}\n" -X POST "$API_BASE/api/tasks" \
-H "Authorization: Bearer $TOKEN_ADMIN" \
-H "X-Tenant-Id: $TENANT_TENNIS" \
-H "Content-Type: application/json" \
-d '{"title":"Test\"; DROP TABLE work_items; --","description":"SQL injection test","dueDate":"2026-03-20T23:59:59Z"}' \
| tee .sisyphus/evidence/final-qa/s55-sql-injection.json
echo ""
# Scenario 56: XSS Attempt in Task Title
echo "=== SCENARIO 56: XSS Attempt ==="
curl -s -w "\nHTTP:%{http_code}\n" -X POST "$API_BASE/api/tasks" \
-H "Authorization: Bearer $TOKEN_ADMIN" \
-H "X-Tenant-Id: $TENANT_TENNIS" \
-H "Content-Type: application/json" \
-d '{"title":"<script>alert(\"XSS\")</script>","description":"XSS test","dueDate":"2026-03-20T23:59:59Z"}' \
| tee .sisyphus/evidence/final-qa/s56-xss-attempt.json
echo ""
# Scenario 57: Concurrent Shift Signup (Race Condition)
echo "=== SCENARIO 57: Concurrent Operations ==="
echo "Creating shift with capacity 1..."
RACE_SHIFT=$(curl -s -X POST "$API_BASE/api/shifts" \
-H "Authorization: Bearer $TOKEN_ADMIN" \
-H "X-Tenant-Id: $TENANT_TENNIS" \
-H "Content-Type: application/json" \
-d '{
"title":"Race Condition Test Shift",
"startTime":"2026-03-25T10:00:00Z",
"endTime":"2026-03-25T12:00:00Z",
"capacity":1
}')
RACE_SHIFT_ID=$(echo $RACE_SHIFT | jq -r '.id')
echo "Shift ID: $RACE_SHIFT_ID"
if [ "$RACE_SHIFT_ID" != "null" ] && [ -n "$RACE_SHIFT_ID" ]; then
echo "Attempting concurrent signups (member1 and member2 simultaneously)..."
curl -s -w "\nMEMBER1_HTTP:%{http_code}\n" -X POST "$API_BASE/api/shifts/$RACE_SHIFT_ID/signup" \
-H "Authorization: Bearer $TOKEN_MEMBER1" \
-H "X-Tenant-Id: $TENANT_TENNIS" &
PID1=$!
curl -s -w "\nMEMBER2_HTTP:%{http_code}\n" -X POST "$API_BASE/api/shifts/$RACE_SHIFT_ID/signup" \
-H "Authorization: Bearer $TOKEN_MEMBER2" \
-H "X-Tenant-Id: $TENANT_TENNIS" &
PID2=$!
wait $PID1
wait $PID2
echo ""
echo "Verifying final signup count (should be 1, one should have failed)..."
curl -s "$API_BASE/api/shifts/$RACE_SHIFT_ID" \
-H "Authorization: Bearer $TOKEN_ADMIN" \
-H "X-Tenant-Id: $TENANT_TENNIS" | jq '{signups: .signups | length, capacity: .capacity}'
else
echo "❌ SKIP: Could not create race condition test shift"
fi | tee -a .sisyphus/evidence/final-qa/s57-race-condition.json
echo ""
echo "=========================================="
echo "Edge Cases Complete!"
echo "=========================================="