Repository navigation
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1bb10333-1d49-4426-896d-d9a04178b836
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1bb10333-1d49-4426-896d-d9a04178b836
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1bb10333-1d49-4426-896d-d9a04178b836
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Administrator session restoration without provider-backed assignments can deadlock with authorization continuation or Copilot changes because of inconsistent lock ordering, aborting requests.
4 open issues
What changed in this PR
This PR strengthens authority locking across Hub session restoration, global sign-out, and sensitive administrator operations to reduce deadlocks and prevent use of revoked sessions.
Changes:
- Adds ordered provider locks and an exclusive global-revocation lock helper.
- Acquires authority locks before sensitive work and rechecks revocation during session restoration.
- Adds regression coverage for lock ordering and overlapping revocation.
| File | Description |
|---|---|
hub/server/repositories/session-revocation-states/lock-session-revocation-state-for-update/index.ts |
Adds exclusive locking of global revocation state. |
hub/server/repositories/authentication-providers/lock-authentication-providers-for-session-authority/index.ts |
Locks provider rows in key order. |
hub/server/domains/session-revocation/end-all-hub-sessions/index.ts |
Locks providers and revocation state before ending sessions. |
hub/server/domains/session-revocation/end-all-hub-sessions/index.db.test.ts |
Tests revocation lock order and concurrent administrator continuation. |
hub/server/domains/copilot-connection/run-copilot-connection-transaction/index.ts |
Acquires authority locks before Copilot operations. |
hub/server/domains/copilot-connection/copilot-connection.db.test.ts |
Tests authority locking before session and connection locks. |
hub/server/domains/admission/run-session-restore-transaction/types.ts |
Adds the restoration revocation-lock interface. |
hub/server/domains/admission/run-session-restore-transaction/index.ts |
Wires the revocation-lock repository into restoration. |
hub/server/domains/admission/restore-hub-session/index.ts |
Rechecks revocation under lock before membership checks. |
hub/server/domains/admission/restore-hub-session/index.test.ts |
Expands restoration lock-order assertions. |
hub/server/domains/admission/authority-locks.db.test.ts |
Tests restoration waiting and rejection after revocation. |
hub/server/domains/administrator-authorization/continue-administrator-authorization/index.ts |
Acquires provider and revocation locks before continuation. |
Review effort: Max (dynamic)
Comment on lines
+23
to
+24
| const lockedRevocation = await repositories.lockRevocation() | ||
| if (!lockedRevocation || lockedRevocation.revision !== revocation.revision) return null |
Comment on lines
+127
to
+166
| async function waitsForProvider() { | ||
| for (let attempt = 0; attempt < 100; attempt += 1) { | ||
| const result = await inspector.query<{ blocked: boolean }>( | ||
| `SELECT EXISTS ( | ||
| SELECT 1 FROM pg_stat_activity waiter | ||
| WHERE waiter.datname = current_database() | ||
| AND waiter.wait_event_type = 'Lock' | ||
| AND waiter.query LIKE '%authentication_providers%' | ||
| AND $1 = ANY(pg_blocking_pids(waiter.pid)) | ||
| ) AS blocked`, | ||
| [providerPid], | ||
| ) | ||
| if (result.rows[0]?.blocked) return true | ||
| await new Promise<void>((resolve) => setImmediate(resolve)) | ||
| } | ||
| return false | ||
| } | ||
| expect(await waitsForProvider()).toBe(true) | ||
|
|
||
| await providerHolder.query("COMMIT") | ||
| providerTransactionOpen = false | ||
| let waitsForRevocation = false | ||
| for (let attempt = 0; attempt < 100; attempt += 1) { | ||
| const result = await inspector.query<{ blocked: boolean }>( | ||
| `SELECT EXISTS ( | ||
| SELECT 1 FROM pg_stat_activity waiter | ||
| WHERE waiter.datname = current_database() | ||
| AND waiter.wait_event_type = 'Lock' | ||
| AND waiter.query LIKE '%session_revocation_states%' | ||
| AND $1 = ANY(pg_blocking_pids(waiter.pid)) | ||
| ) AS blocked`, | ||
| [revocationPid], | ||
| ) | ||
| if (result.rows[0]?.blocked) { | ||
| waitsForRevocation = true | ||
| break | ||
| } | ||
| await new Promise<void>((resolve) => setImmediate(resolve)) | ||
| } | ||
| expect(waitsForRevocation).toBe(true) |
| import { describe, expect, it } from "vitest" | ||
|
|
||
| import { getDatabaseUrl } from "../../../../test/database/get-database-url" | ||
| import { continueAdministratorAuthorization } from "../../../domains/administrator-authorization/continue-administrator-authorization/index.ts" |
Comment on lines
+6
to
+11
| await database.$queryRaw` | ||
| SELECT "id" | ||
| FROM "authentication_providers" | ||
| ORDER BY "key" | ||
| FOR UPDATE | ||
| ` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Why
Ending all Hub sessions can overlap with edits and other sensitive operations. These operations need a consistent order so they do not deadlock or continue with revoked authority.
What changes
Session restoration, revocation, and sensitive operations now acquire authority locks in the same order. Operations that wait for revocation recheck the session before continuing.
Storybook screenshots
No story presentation changes.