mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
SaaS fixes (#6578)
Co-authored-by: Claude Opus 4.8 <[email protected]> Co-authored-by: James Brunton <[email protected]> Co-authored-by: Reece Browne <[email protected]> Co-authored-by: ConnorYoh <[email protected]> Co-authored-by: Reece <[email protected]> Co-authored-by: EthanHealy01 <[email protected]> Co-authored-by: Ludy <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
James Brunton
Reece Browne
ConnorYoh
Reece
EthanHealy01
Ludy
parent
96accea984
commit
ddf78d11ae
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
# Validate MCP OAuth discovery when the saas profile is active, i.e. when the
|
||||
# Supabase security chain coexists with the MCP chain. Guards the regression
|
||||
# where /.well-known/oauth-protected-resource/<subpath> fell through to the
|
||||
# Supabase chain's default Spring Security metadata filter and was served
|
||||
# WITHOUT authorization_servers, sending MCP clients to Stirling for OAuth.
|
||||
#
|
||||
# Prereq: docker-compose -f docker-compose-saas.yml -f docker-compose-saas-mcp.override.yml up
|
||||
# Env: MCP_AUTH_ISSUERURI must match the issuer the stack was started with.
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
BASE_URL="${BASE_URL:-http://localhost:8080}"
|
||||
ISSUER="${MCP_AUTH_ISSUERURI:-https://auth.example.com}"
|
||||
PRM_URL="$BASE_URL/.well-known/oauth-protected-resource"
|
||||
MCP_URL="$BASE_URL/mcp"
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
pass() { echo -e "${GREEN}✓${NC} $1"; PASS=$((PASS + 1)); }
|
||||
fail() { echo -e "${RED}✗ $1${NC}"; FAIL=$((FAIL + 1)); }
|
||||
|
||||
echo -e "${BLUE}Validating MCP discovery with the saas profile (two-chain interplay)${NC}"
|
||||
echo -e "${BLUE}Issuer:${NC} $ISSUER"
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}[0] Liveness${NC}"
|
||||
if curl -sf "$BASE_URL/api/v1/info/status" 2>/dev/null | grep -q "UP"; then
|
||||
pass "Stirling PDF (saas flavor) is UP"
|
||||
else
|
||||
fail "Stirling PDF is not UP at $BASE_URL"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}[1] Root metadata document${NC}"
|
||||
ROOT_CODE=$(curl -s -o /tmp/saas_prm_root.json -w "%{http_code}" "$PRM_URL")
|
||||
ROOT_BODY=$(cat /tmp/saas_prm_root.json 2>/dev/null)
|
||||
[ "$ROOT_CODE" = "200" ] && pass "GET $PRM_URL -> 200" || fail "GET $PRM_URL -> $ROOT_CODE"
|
||||
if echo "$ROOT_BODY" | grep -q "\"authorization_servers\"" && echo "$ROOT_BODY" | grep -qF "$ISSUER"; then
|
||||
pass "root metadata advertises the configured authorization server"
|
||||
else
|
||||
fail "root metadata missing authorization_servers/issuer: $ROOT_BODY"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}[2] Path-inserted metadata document (the regression)${NC}"
|
||||
SUB_CODE=$(curl -s -o /tmp/saas_prm_sub.json -w "%{http_code}" "$PRM_URL/mcp")
|
||||
SUB_BODY=$(cat /tmp/saas_prm_sub.json 2>/dev/null)
|
||||
[ "$SUB_CODE" = "200" ] && pass "GET $PRM_URL/mcp -> 200" || fail "GET $PRM_URL/mcp -> $SUB_CODE"
|
||||
if echo "$SUB_BODY" | grep -q "\"authorization_servers\"" && echo "$SUB_BODY" | grep -qF "$ISSUER"; then
|
||||
pass "path-inserted metadata served by the MCP chain (authorization_servers present)"
|
||||
else
|
||||
fail "path-inserted metadata fell through to the Supabase chain default filter: $SUB_BODY"
|
||||
fi
|
||||
if echo "$SUB_BODY" | grep -q "mcp.tools.read"; then
|
||||
pass "path-inserted metadata advertises mcp.tools scopes"
|
||||
else
|
||||
fail "path-inserted metadata missing mcp.tools scopes"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}[3] 401 challenge points at the path-inserted metadata URL${NC}"
|
||||
HDRS=$(curl -s -o /dev/null -D - -X POST "$MCP_URL" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"jsonrpc":"2.0","id":1,"method":"ping"}')
|
||||
CODE=$(echo "$HDRS" | head -1 | grep -o '[0-9][0-9][0-9]')
|
||||
[ "$CODE" = "401" ] && pass "POST /mcp with no token -> 401" || fail "POST /mcp no token -> $CODE (expected 401)"
|
||||
if echo "$HDRS" | grep -i '^WWW-Authenticate:' | grep -q 'oauth-protected-resource/mcp'; then
|
||||
pass "WWW-Authenticate advertises resource_metadata at the path-inserted URL"
|
||||
else
|
||||
fail "WWW-Authenticate header wrong: $(echo "$HDRS" | grep -i '^WWW-Authenticate:')"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}[4] Chain isolation${NC}"
|
||||
API_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/api/v1/user/me")
|
||||
if [ "$API_CODE" = "401" ] || [ "$API_CODE" = "403" ] || [ "$API_CODE" = "404" ]; then
|
||||
pass "Supabase chain still guards regular API routes ($API_CODE)"
|
||||
else
|
||||
fail "Unexpected status on regular API route: $API_CODE"
|
||||
fi
|
||||
GARBAGE=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$MCP_URL" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer not-a-jwt" \
|
||||
-d '{"jsonrpc":"2.0","id":1,"method":"ping"}')
|
||||
[ "$GARBAGE" = "401" ] && pass "garbage bearer token at /mcp -> 401" || fail "garbage token -> $GARBAGE"
|
||||
echo ""
|
||||
|
||||
echo -e "${BLUE}Results: ${GREEN}$PASS passed${NC}, ${RED}$FAIL failed${NC}"
|
||||
[ "$FAIL" -eq 0 ] || exit 1
|
||||
Reference in New Issue
Block a user