Skip to content

[2.x] fix: allow non-admins to upload a poll image before the poll exists - #136

Merged
imorland merged 1 commit into
2.xfrom
im/upload-image-before-poll-exists
Aug 2, 2026
Merged

[2.x] fix: allow non-admins to upload a poll image before the poll exists#136
imorland merged 1 commit into
2.xfrom
im/upload-image-before-poll-exists

Conversation

@imorland

@imorland imorland commented Aug 2, 2026

Copy link
Copy Markdown
Member

Fixes #131 — reported by @ekumanov, who also diagnosed the cause.

The bug

Adding an image to a poll while creating it returned 403 for every non-admin, no matter which permissions they held. Editing an existing poll's image worked, because that path takes the if ($pollId) branch and correctly checks assertCan('edit', $poll).

The composer uploads the image before the poll is saved, so on that path there is no model to authorize against. It asserted:

$actor->assertCan('startPoll');
$actor->assertCan('startGlobalPoll');

startPoll is a policy ability on Post (Access\PostPolicy::startPoll). With a null model, core's Gate::allows() only consults GLOBAL policies — so the Post policy is never evaluated — and falls through to:

return $actor->isAdmin() || $actor->hasPermission($ability);

startPoll is never registered as a grantable permission (the admin extender registers startGlobalPoll, uploadPollImages and startPollGroup), so hasPermission('startPoll') is always false and only the isAdmin() branch could ever pass.

The two asserts were also AND-ed, so even where the first check passed, a user creating a discussion poll was required to hold startGlobalPoll as well.

The fix

Both controllers now share assertCanStartAnyPoll(), which permits the upload when the actor can start a global poll or holds discussion.polls.start — the grantable permission the canStartPolls forum attribute already uses. uploadPollImages is still asserted first, so the upload stays behind both "may upload poll images" and "could actually be creating a poll".

On the design question left open in the issue: I kept the poll-start requirement rather than letting uploadPollImages stand alone, since the request writes a file to disk and the second check keeps that tied to a plausible poll.

Note that UploadPollOptionImageController overrides handle() and carried its own copy of the same broken branch, so option images were affected identically.

Tests

New UploadPollImageAuthorizationTest covers the pre-save upload path for both poll and option images, the admin case, and a user who may upload but cannot start a poll anywhere (403).

Proven red first: the two pre-save cases return 403 against the current controllers and 200 with the fix, while the negative and admin cases hold in both directions. Full suite 122/122, PHPStan clean.

The composer uploads a poll image before the poll is saved, so there is
no model to authorize against on that path. It asserted 'startPoll',
which is a policy ability on Post: with a null model core's Gate consults
only GLOBAL policies, finds none, and falls back to
hasPermission('startPoll'). No group can hold that — it is not a
registered permission — so the check could only ever pass for admins.

The two asserts were also AND-ed, so a user creating a global poll was
additionally required to hold the discussion-poll ability, and vice
versa.

Both upload controllers now share assertCanStartAnyPoll(), which allows
the upload when the actor can start a global poll OR holds
discussion.polls.start — the grantable permission the canStartPolls forum
attribute already uses. uploadPollImages is still asserted first, so the
upload remains behind both 'may upload images' and 'could actually be
making a poll'.

Fixes #131
@imorland
imorland requested a review from a team as a code owner August 2, 2026 15:49
@imorland imorland changed the title fix: allow non-admins to upload a poll image before the poll exists [2.x] fix: allow non-admins to upload a poll image before the poll exists Aug 2, 2026
@imorland
imorland merged commit 16632ab into 2.x Aug 2, 2026
23 checks passed
@imorland
imorland deleted the im/upload-image-before-poll-exists branch August 2, 2026 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Non-admins get 403 uploading a poll image before the poll is saved

1 participant