Fix feedback auth for expired users
This commit is contained in:
@@ -129,6 +129,8 @@ compose_up_retry() {
|
||||
|
||||
export IMAGE_TAG="$NEW_TAG"
|
||||
export POLYWEATHER_API_BASE_URL="${POLYWEATHER_FRONTEND_INTERNAL_API_BASE_URL:-http://polyweather_web:8000}"
|
||||
export SUPABASE_URL="${SUPABASE_URL:-${NEXT_PUBLIC_SUPABASE_URL:-}}"
|
||||
export SUPABASE_ANON_KEY="${SUPABASE_ANON_KEY:-${NEXT_PUBLIC_SUPABASE_ANON_KEY:-}}"
|
||||
pull_ok=0
|
||||
for pull_attempt in $(seq 1 6); do
|
||||
docker compose pull && pull_ok=1 && break
|
||||
|
||||
@@ -71,6 +71,7 @@ services:
|
||||
NEXT_PUBLIC_SUPABASE_URL: ${NEXT_PUBLIC_SUPABASE_URL}
|
||||
NEXT_PUBLIC_WALLETCONNECT_POLYGON_RPC_URL: ${NEXT_PUBLIC_WALLETCONNECT_POLYGON_RPC_URL:-https://polygon-bor-rpc.publicnode.com}
|
||||
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID: ${NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID:-}
|
||||
POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN: ${POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN}
|
||||
POLYWEATHER_API_BASE_URL: http://polyweather_web:8000
|
||||
POLYWEATHER_AUTH_ENABLED: ${POLYWEATHER_AUTH_ENABLED:-true}
|
||||
POLYWEATHER_AUTH_REQUIRED: ${POLYWEATHER_AUTH_REQUIRED:-true}
|
||||
@@ -120,7 +121,11 @@ services:
|
||||
POLYWEATHER_SCAN_TERMINAL_BUILD_TIMEOUT_SEC: '30'
|
||||
POLYWEATHER_SCAN_TERMINAL_MAX_WORKERS: ${POLYWEATHER_SCAN_TERMINAL_MAX_WORKERS:-4}
|
||||
POLYWEATHER_SCAN_TERMINAL_PREWARM_ENABLED: ${POLYWEATHER_SCAN_TERMINAL_PREWARM_ENABLED:-false}
|
||||
POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN: ${POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN}
|
||||
POLYWEATHER_SERVICE_ROLE: web
|
||||
SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
|
||||
SUPABASE_SERVICE_ROLE_KEY: ${SUPABASE_SERVICE_ROLE_KEY}
|
||||
SUPABASE_URL: ${SUPABASE_URL}
|
||||
UVICORN_WORKERS: ${UVICORN_WORKERS:-2}
|
||||
healthcheck:
|
||||
interval: 30s
|
||||
|
||||
@@ -50,6 +50,9 @@ export async function GET(req: NextRequest) {
|
||||
cache: "no-store",
|
||||
});
|
||||
const raw = await res.text();
|
||||
if (res.status === 401 || res.status === 403) {
|
||||
return applyAuthResponseCookies(emptyFeedbackResponse(), auth.response);
|
||||
}
|
||||
if (!res.ok) {
|
||||
return applyAuthResponseCookies(
|
||||
buildUpstreamErrorResponse(res.status, raw, {
|
||||
|
||||
@@ -72,6 +72,7 @@ export function runTests() {
|
||||
assert(
|
||||
feedbackRouteSource.includes("emptyFeedbackResponse") &&
|
||||
feedbackRouteSource.includes("if (!auth.authUserId)") &&
|
||||
feedbackRouteSource.includes("if (res.status === 401 || res.status === 403)") &&
|
||||
!feedbackRouteSource.includes("const authError = requireBackendPaymentAuth(auth);"),
|
||||
"feedback GET proxy must return an empty optional list instead of surfacing a 401 when no Supabase user identity is verified",
|
||||
);
|
||||
|
||||
@@ -297,6 +297,36 @@ def test_frontend_proxy_uses_internal_backend_url_not_public_site():
|
||||
)
|
||||
|
||||
|
||||
def test_frontend_and_web_share_supabase_forwarded_identity_secrets():
|
||||
compose = (ROOT / "docker-compose.yml").read_text(encoding="utf-8")
|
||||
script = (ROOT / "deploy.sh").read_text(encoding="utf-8")
|
||||
frontend_block = compose.split(" polyweather_frontend:", 1)[1].split(
|
||||
"\n polyweather_web:",
|
||||
1,
|
||||
)[0]
|
||||
web_block = compose.split(" polyweather_web:", 1)[1].split(
|
||||
"\n polyweather_collector:",
|
||||
1,
|
||||
)[0]
|
||||
|
||||
assert (
|
||||
"POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN: ${POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN}"
|
||||
in frontend_block
|
||||
)
|
||||
assert "SUPABASE_URL: ${SUPABASE_URL}" in web_block
|
||||
assert "SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}" in web_block
|
||||
assert "SUPABASE_SERVICE_ROLE_KEY: ${SUPABASE_SERVICE_ROLE_KEY}" in web_block
|
||||
assert (
|
||||
"POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN: ${POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN}"
|
||||
in web_block
|
||||
)
|
||||
assert 'export SUPABASE_URL="${SUPABASE_URL:-${NEXT_PUBLIC_SUPABASE_URL:-}}"' in script
|
||||
assert (
|
||||
'export SUPABASE_ANON_KEY="${SUPABASE_ANON_KEY:-${NEXT_PUBLIC_SUPABASE_ANON_KEY:-}}"'
|
||||
in script
|
||||
)
|
||||
|
||||
|
||||
def test_web_container_raises_open_file_limit_for_sse_and_proxy_load():
|
||||
compose = (ROOT / "docker-compose.yml").read_text(encoding="utf-8")
|
||||
web_block = compose.split(" polyweather_web:", 1)[1].split(
|
||||
|
||||
@@ -2,8 +2,10 @@ from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
from starlette.requests import Request
|
||||
|
||||
from src.database.db_manager import DBManager
|
||||
import web.core as web_core
|
||||
from web.services import feedback_api
|
||||
from web.services import ops_api
|
||||
|
||||
@@ -296,3 +298,42 @@ def test_list_current_user_feedback_requires_identity(tmp_path, monkeypatch):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
feedback_api.list_current_user_feedback(anonymous_request, limit=20)
|
||||
assert exc_info.value.status_code == 401
|
||||
|
||||
|
||||
def test_list_current_user_feedback_accepts_forwarded_identity_when_supabase_unconfigured(
|
||||
tmp_path, monkeypatch
|
||||
):
|
||||
db = DBManager(str(tmp_path / "polyweather-feedback-forwarded.db"))
|
||||
db.append_user_feedback(
|
||||
category="bug",
|
||||
message="Forwarded identity feedback.",
|
||||
user_id="user-a",
|
||||
user_email="a@example.com",
|
||||
)
|
||||
db.append_user_feedback(
|
||||
category="bug",
|
||||
message="Other user feedback.",
|
||||
user_id="user-b",
|
||||
user_email="b@example.com",
|
||||
)
|
||||
monkeypatch.setattr(feedback_api, "DBManager", lambda: db)
|
||||
monkeypatch.setattr(web_core, "_ENTITLEMENT_TOKEN", "backend-token")
|
||||
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "supabase_url", "")
|
||||
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "anon_key", "")
|
||||
|
||||
request = Request(
|
||||
{
|
||||
"type": "http",
|
||||
"headers": [
|
||||
(b"x-polyweather-entitlement", b"backend-token"),
|
||||
(b"x-polyweather-auth-user-id", b"user-a"),
|
||||
(b"x-polyweather-auth-email", b"a@example.com"),
|
||||
(b"authorization", b"Bearer access-token"),
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
payload = feedback_api.list_current_user_feedback(request, limit=20)
|
||||
|
||||
assert payload["total"] == 1
|
||||
assert payload["feedback"][0]["message"] == "Forwarded identity feedback."
|
||||
|
||||
+2
-2
@@ -233,15 +233,15 @@ def _bind_forwarded_supabase_identity(request: Request) -> bool:
|
||||
|
||||
|
||||
def _bind_optional_supabase_identity(request: Request) -> None:
|
||||
if _bind_forwarded_supabase_identity(request):
|
||||
return
|
||||
if not SUPABASE_ENTITLEMENT.configured:
|
||||
return
|
||||
access_token = extract_bearer_token(request.headers.get("authorization"))
|
||||
if not access_token:
|
||||
_bind_forwarded_supabase_identity(request)
|
||||
return
|
||||
identity = SUPABASE_ENTITLEMENT.get_identity(access_token)
|
||||
if not identity:
|
||||
_bind_forwarded_supabase_identity(request)
|
||||
return
|
||||
request.state.auth_user_id = identity.user_id
|
||||
request.state.auth_email = identity.email
|
||||
|
||||
Reference in New Issue
Block a user