Gate PIN (D3): decouple claim PIN from override authority (AdminEnrolled) — stacked on #1071#1074
Merged
Conversation
Everyone — supervisors included — may now self-enrol a CLAIM PIN at the kiosk (attribution only); the old supervisor self-enrol block is removed. Override authority is conferred solely by an admin enrolment: a new GateStaffPin.AdminEnrolled flag (migration, default false), set true only by AdminSetPinAsync (/Gate/Admin). AuthorizeOverrideAsync now requires AdminEnrolled=true AND the correct PIN AND a live supervisor role — so an attacker cold-setting a supervisor's PIN at the anonymous kiosk gains attribution spoofing only (already possible for any staffer), never override power. Removes the now-dead BlockedSupervisor mode + SupervisorMustBeAdminEnrolled result + their view/CSS. Persists AdminEnrolled on insert/update/merge. DEPLOY: the migration backfills existing rows to false (fail-safe), so an admin must re-enrol each supervisor's PIN via /Gate/Admin before doors open or overrides won't work. A blanket-true backfill was rejected (fail-open). Security-reviewed (sound, no blocker); EF-migration-reviewed (fixed a bool HasDefaultValue sentinel; regenerated clean). 122 gate tests green. Live-verified: a supervisor now reaches the Set flow, not the block. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
eb1df0b to
0ce3233
Compare
3 tasks
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.
Phase D3 of the gate PIN rework — stacked on #1071 (contains its commit until that merges). Fixes the "everyone should be able to set a PIN" block (Peter Drier, as an Admin, couldn't self-enrol) — nobodies-collective#909-adjacent.
The decouple
One
GateStaffPinrow now has anAdminEnrolledbool that splits the two jobs a PIN used to do:AdminEnrolled=false). It attributes scans. The old supervisor self-enrol block is gone./Gate/Admin→AdminEnrolled=true).AuthorizeOverrideAsyncnow requiresAdminEnrolled=trueand the correct PIN and a live supervisor role.So an attacker cold-setting a supervisor's PIN at the anonymous kiosk gains attribution spoofing only (already possible for any staffer) — never override power. This closes the hole the old block existed for, while unblocking self-enrolment for all. (A peer reviewer initially argued the decouple was unnecessary; I disagreed and a security review confirmed the decouple is what actually closes the hole.)
The migration backfills all existing rows to
AdminEnrolled=false(fail-safe — no one silently keeps override). This means every already-enrolled supervisor loses override authority on deploy until an admin re-enrols them. A blanket backfill totruewas rejected by both reviews (fail-open — it would re-grant override to any pre-existing self-set PIN, defeating the feature) and a targeted backfill can't run (the supervisor set lives in another section — a cross-section reach forbidden in a migration).→ After deploying this, an admin must re-set each gate lead's PIN via
/Gate/Adminbefore the gate opens, or overrides will not work. (This is the same action needed to set up gate leads anyway.)Reviews
HasDefaultValue(false)bool-sentinel (this repo's known anti-pattern); migration regenerated clean (AddColumn defaultValue:falsebackfill, cleanDown, snapshot consistent).Verification
Build clean · 122 gate + 4 web tests green ·
dotnet formatclean. Live-verified on the tablet: a supervisor (Dev Admin) now reaches the normal Set flow instead of the old "blocked" screen; the migration applied cleanly to a real Postgres.Needs your sign-off, Peter (new auth surface + migration). Not for merge without review.
🤖 Generated with Claude Code