fix: bind background push to operator identity (Closes #1374)
CI / lint (pull_request) Successful in 3m18s
CI / build-release (pull_request) Successful in 8s
CI / browser-journey (pull_request) Successful in 5m47s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-25 02:06:25 +00:00
1 parent d1adbe72d6
commit b40a44fc45
5 files changed
+128 -11

No files matched your search

+7 -4
View File
@@ -252,11 +252,14 @@ async def managed_session_active(management_id: str) -> bool:
return status == "active"
async def managed_session_statuses(management_ids) -> dict[str, str]:
async def managed_session_statuses(
management_ids, *, expected_principal_id: int | None = None
) -> dict[str, str]:
options = {"idle_timeout_seconds": idle_timeout_seconds()}
if expected_principal_id is not None:
options["expected_principal_id"] = expected_principal_id
return await asyncio.to_thread(
_session_store().managed_statuses,
management_ids,
idle_timeout_seconds=idle_timeout_seconds(),
_session_store().managed_statuses, management_ids, **options
)
+16 -4
View File
@@ -136,6 +136,18 @@ async def _following_push_snapshot() -> dict:
return await get_following(Response())
async def _identity_bound_push_session_statuses(
management_ids: list[str],
) -> dict[str, str]:
user = await gitea_proxy.current_user()
principal_id = user.get("id") if isinstance(user, dict) else None
if isinstance(principal_id, bool) or not isinstance(principal_id, int) or principal_id <= 0:
raise ValueError("Authenticated Gitea identity is unavailable")
return await dashboard_auth.managed_session_statuses(
management_ids, expected_principal_id=principal_id
)
async def _push_poll_loop() -> None:
interval = max(5.0, float(os.getenv("STACKCHAIN_PUSH_POLL_SECONDS", "30")))
deadline_interval = max(
@@ -160,7 +172,7 @@ async def _push_poll_loop() -> None:
_push_subscription_store,
_push_configuration(),
gitea_proxy.unread_notification_snapshot,
session_statuses=dashboard_auth.managed_session_statuses,
session_statuses=_identity_bound_push_session_statuses,
lease_seconds=lease_seconds,
send_timeout_seconds=send_timeout,
max_concurrency=max_concurrency,
@@ -172,7 +184,7 @@ async def _push_poll_loop() -> None:
_push_subscription_store,
_push_configuration(),
gitea_proxy.assigned_issue_snapshot,
session_statuses=dashboard_auth.managed_session_statuses,
session_statuses=_identity_bound_push_session_statuses,
send_timeout_seconds=send_timeout,
lease_seconds=lease_seconds,
max_concurrency=max_concurrency,
@@ -183,7 +195,7 @@ async def _push_poll_loop() -> None:
_push_subscription_store,
_push_configuration(),
_following_push_snapshot,
session_statuses=dashboard_auth.managed_session_statuses,
session_statuses=_identity_bound_push_session_statuses,
send_timeout_seconds=send_timeout,
lease_seconds=lease_seconds,
max_concurrency=max_concurrency,
@@ -194,7 +206,7 @@ async def _push_poll_loop() -> None:
_push_subscription_store,
_push_configuration(),
_start_day_plan_snapshot,
session_statuses=dashboard_auth.managed_session_statuses,
session_statuses=_identity_bound_push_session_statuses,
send_timeout_seconds=send_timeout,
lease_seconds=lease_seconds,
max_concurrency=max_concurrency,
+8 -2
View File
@@ -303,7 +303,11 @@ class SessionStore:
)[management_id]
def managed_statuses(
self, management_ids, *, idle_timeout_seconds: int
self,
management_ids,
*,
idle_timeout_seconds: int,
expected_principal_id: int | None = None,
) -> dict[str, str]:
"""Return authorization states for durable device identifiers in one read."""
requested = set(management_ids)
@@ -314,7 +318,7 @@ class SessionStore:
with self._connect() as connection:
placeholders = ",".join("?" for _ in requested)
rows = connection.execute(
"SELECT management_id, expires_at, last_active_at "
"SELECT management_id, expires_at, last_active_at, principal_id "
f"FROM active_sessions WHERE management_id IN ({placeholders})",
tuple(requested),
).fetchall()
@@ -329,6 +333,8 @@ class SessionStore:
statuses[management_id] = "revoked"
elif row[1] + idle_timeout <= now:
statuses[management_id] = "idle"
elif expected_principal_id is not None and row[2] != expected_principal_id:
statuses[management_id] = "principal_mismatch"
else:
statuses[management_id] = "active"
return statuses