fix(authz): block non-admin members from changing duration on published events#538
fix(authz): block non-admin members from changing duration on published events#538
Conversation
…ed events
SummitEvent::setDuration delegates to _setDuration which, when start_date
is set, silently shifts end_date to keep start_date + duration aligned.
For an event that has already been published to the schedule, this means
any non-admin caller (e.g., a track chair hitting PUT /events/{id} with
{duration} via AbstractPublishService::updateDuration) could move a live
slot with no audit, no notification, and no validation that the move is
intended.
Add the invariant at the entity setter so all callers are bound by the
same rule, regardless of which controller or service initiated the call.
The check intentionally skips when no member is supplied so trusted
internal callers (e.g., SummitService::advanceSummit during bulk schedule
shifts) continue to work.
Pairs with the track-chairs UI gate in fntechgit/track-chairs#67.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR adds authorization validation to ChangesPublished Event Duration Authorization
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
📘 OpenAPI / Swagger preview ➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-538/ This page is automatically updated on each push to this PR. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/SummitEventModelTest.php (1)
83-105: ⚡ Quick winAdd a regression test for the null-member compatibility path.
Given the documented behavior, add a test that
setDuration(..., null)still succeeds on published events for trusted internal flows; this protects against future hardening regressions.Proposed test addition
+ public function testNullMemberCanChangeDurationOnPublishedEvent(){ + $presentation = self::$presentations[0]; + $this->assertTrue($presentation->isPublished()); + + $old_end_date = $presentation->getEndDate(); + $presentation->setDuration(864000, false, null); + $new_end_date = $presentation->getEndDate(); + + $this->assertTrue($old_end_date < $new_end_date); + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/SummitEventModelTest.php` around lines 83 - 105, Add a new regression test method in SummitEventModelTest that mirrors testAdminMemberCanChangeDurationOnPublishedEvent but passes null for the $member argument to Presentation::setDuration to exercise the null-member compatibility path: grab a published presentation (self::$presentations[0]), assert isPublished(), record old end date, call $presentation->setDuration(864000, false, null) and assert no exception and that getEndDate() increased (old < new). Ensure the test name clearly indicates null-member behavior (e.g., testNullMemberCanChangeDurationOnPublishedEvent) and follows the same mocking-free flow as the admin test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/SummitEventModelTest.php`:
- Around line 83-105: Add a new regression test method in SummitEventModelTest
that mirrors testAdminMemberCanChangeDurationOnPublishedEvent but passes null
for the $member argument to Presentation::setDuration to exercise the
null-member compatibility path: grab a published presentation
(self::$presentations[0]), assert isPublished(), record old end date, call
$presentation->setDuration(864000, false, null) and assert no exception and that
getEndDate() increased (old < new). Ensure the test name clearly indicates
null-member behavior (e.g., testNullMemberCanChangeDurationOnPublishedEvent) and
follows the same mocking-free flow as the admin test.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8d22467f-83d1-41a2-962f-a71cc3209245
📒 Files selected for processing (2)
app/Models/Foundation/Summit/Events/SummitEvent.phptests/SummitEventModelTest.php
Summary
Move the published-event duration guard into
SummitEvent::setDurationso the invariant is enforced at the entity boundary, not just in the UI or a single controller.Why
For a published event,
SummitEvent::setDuration→TimeDurationRestrictedEvent::_setDurationsilently shiftsend_date = start_date + durationwheneverstart_dateis set. The track-chair edit path (PUT /api/v1/summits/{id}/events/{event_id}→OAuth2SummitEventsApiController@updateEvent→AbstractPublishService::updateDuration) callssetDuration(..., $current_member)without checking publication state, which means a chair token + curl can move a live schedule slot with no audit and no notification.Putting the check in the controller would close the chair path but leave the invariant scattered: any future service or controller that calls
setDurationwould need to remember to repeat the check. The setter is the single place every caller must pass through, so the entity should own the rule.What changed
app/Models/Foundation/Summit/Events/SummitEvent.php—setDurationnow throwsValidationExceptionwhen the event is published and the supplied$memberis non-admin for the summit.tests/SummitEventModelTest.php— adds two cases against an already-published presentation fixture: a mocked non-admin member is rejected, a mocked admin member succeeds and the end_date shifts as before.Compatibility notes
$memberisnull. Trusted internal callers likeSummitService::advanceSummit(app/Services/Model/Imp/SummitService.php:2731) callsetDurationwith no member during bulk schedule shifts and must continue to work. That mirrors the convention used elsewhere where authorization checks are gated on a user being present.testChangeEventDurationcontinues to pass because it also callssetDurationwithout a member.Test plan
phpunit tests/SummitEventModelTest.php— new tests pass; existing tests still pass.PUT /api/v1/summits/{id}/events/{published_event_id}with{"duration": 1800}returns a 412 ValidationException response (was: 200 with the slot's end_date silently moved).🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes