Repository navigation
Judge and staff role-specific screens, gate sidebar by role - #29
Merged
Merged
Conversation
Closes #19 (chief judge / staff task screens; role-based nav visibility). Score-only visibility is already covered by the existing /scoreboard route, which every role can already reach read-only. Before this, the schema fully supported judge and staff roles (runs have judge_id/answer columns, a full tasks table exists) but there was zero web UI for either -- manual judging was only reachable via the JSON API (PUT /api/runs/{run}/judge), and nothing at all read the tasks table. Separately, the sidebar's "Administração" section rendered for every authenticated user regardless of role (gated only by Auth::check()) -- routes were protected server-side, but a team/judge/staff user would see admin links that 403'd the moment they were clicked. - app/Http/Controllers/JudgeController.php (new): /judge/runs lists pending and recently-judged runs for the current contest with a form to pick a verdict per pending run, reusing the same Score::updateScore() path Api\RunController::judge() uses. No per-judge run assignment/locking exists in the schema, so -- like BOCA's judge/runchief.php vs judge/run.php -- this is one shared queue any judge or admin can work from, rather than two separate screens. - app/Http/Controllers/StaffController.php (new): /staff/tasks lists the contest's Task rows (BOCA's staff/task.php -- balloon delivery, printing, etc.) with a "mark complete" action. - Both gated by the `role:` middleware alias, which pointed at App\Http\Middleware\CheckRole in the old (dead, Laravel <11) app/Http/Kernel.php but was never actually registered in bootstrap/app.php -- `middleware('role:judge,admin')` would have thrown "Target class [role] does not exist" if anyone had tried to use it. Registered it properly alongside the existing `admin` alias. - resources/views/layouts/app.blade.php: wrapped the Admin section in @if(isAdmin()) and added two new sections (Judge, Staff) gated the same way, so each role only sees the nav entries it can actually use. Verified: full PHPUnit suite (397 tests, 2673 assertions) passes, including new tests confirming a team user gets 403 on both new screens, a judge can view and judge a pending run (Score row created correctly), a staff member can view and complete a task, and the sidebar shows/hides each section correctly for team/judge/staff/admin users. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011u3o4QCgpcqH51edYyRPMn
…Controller The automated security review of the previous commit flagged the same cross-contest IDOR pattern already fixed in SubmitController (#22) -- JudgeController::judge() and StaffController::complete() took a Run/Task via route-model-binding but never checked it belonged to the acting judge/staff member's own contest, so either could act on another contest's data just by guessing/incrementing the id. - Added authorizeRunAccess()/authorizeTaskAccess(): 403 unless the user is an admin or the run/task's contest_id matches the user's own. - Also flagged: judging an already-'judged' run through this screen (rather than the dedicated rejudge flow, which resets state first) would double-count the attempt in Score::updateScore() -- a state-integrity issue, not just authorization. judge() now rejects that with a clear error pointing at the rejudge API. Added tests for all three: cross-contest judging/task-completion is blocked (403, no state mutated), and re-judging an already-judged run is rejected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011u3o4QCgpcqH51edYyRPMn
Member
Author
|
Pushed a follow-up fixing three issues the automated security review found: cross-contest IDOR in both |
5 of 7 tasks
matbrgz
changed the base branch from
chore/docker-deps-boca-review
to
master
September 10, 2026 11:23
This was referenced Sep 10, 2026
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.
Closes #19 — adds the missing chief judge/staff screens and fixes role-based navigation visibility. Score-only visibility is already covered by the existing
/scoreboardroute, which every role can already reach read-only, so no separate screen was added for that.Summary
Before this, the schema fully supported judge and staff roles (runs have
judge_id/answercolumns, a fulltaskstable exists) but there was zero web UI for either — manual judging was only reachable via the JSON API (PUT /api/runs/{run}/judge), and nothing at all read thetaskstable. Separately, the sidebar's "Administração" section rendered for every authenticated user regardless of role (gated only byAuth::check()) — routes were protected server-side, but a team/judge/staff user would see admin links that 403'd the moment they were clicked.app/Http/Controllers/JudgeController.php(new):/judge/runslists pending and recently-judged runs for the current contest with a form to pick a verdict per pending run, reusing the sameScore::updateScore()pathApi\RunController::judge()uses. No per-judge run assignment/locking exists in the schema, so — like BOCA'sjudge/runchief.phpvsjudge/run.php— this is one shared queue any judge or admin can work from, rather than two separate screens.app/Http/Controllers/StaffController.php(new):/staff/taskslists the contest'sTaskrows (BOCA'sstaff/task.php— balloon delivery, printing, etc.) with a "mark complete" action.role:middleware alias, which pointed atApp\Http\Middleware\CheckRolein the old (dead, Laravel <11)app/Http/Kernel.phpbut was never actually registered inbootstrap/app.php—middleware('role:judge,admin')would have thrown "Target class [role] does not exist" if anyone had tried to use it. Registered it properly alongside the existingadminalias.resources/views/layouts/app.blade.php: wrapped the Admin section in@if(isAdmin())and added two new sections (Judge, Staff) gated the same way, so each role only sees the nav entries it can actually use.Test plan
Scorerow is created correctly); a staff member can view and complete a task; the sidebar shows/hides each section correctly for team/judge/staff/admin usersStacked on #24 (base branch
chore/docker-deps-boca-review).Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
🤖 Generated with Claude Code
https://claude.ai/code/session_011u3o4QCgpcqH51edYyRPMn