[3.0] Load permissions before applying bans in User::enforceBans() - #9319
Merged
Sesquipedalian merged 2 commits intoJul 29, 2026
Conversation
User::$me->enforceBans() is called immediately after a successful login, at which point User::setMe() has replaced User::$me with a freshly loaded User instance whose permissions have not been loaded yet. Iterating over $this->permission_sets then fataled with "Typed property SMF\User::$permission_sets must not be accessed before initialization". Admins never hit this because enforceBans() returns early for them. Mirrors the guard that the deprecated banPermissions() compat function already has. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Jul 29, 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.
Note
This change was produced by an LLM. The code, the commit message and this
description were all written by Claude (Anthropic), driven by @albertlast. It has
not yet had human code review.
Everything stated below was verified by actually running it against a PostgreSQL
install of this branch, rather than only reasoned about. Even so, please review it
as untrusted work: the diagnosis may be right while the fix is not what SMF would
prefer stylistically or architecturally.
Description
Logging in as any non-admin member dies with a fatal error:
Forum::load()establishes the order the rest of the code relies on — permissionsfirst, bans second:
Login2::DoLogin()breaks that order.User::setMe()swapsUser::$mefor afreshly loaded instance representing the member who just logged in, and nothing has
called
loadPermissions()on that new instance:enforceBans()ends by applying bans and warnings to the permission sets:$permission_setsis a typed property with no default, so reading it on thatinstance throws. Admins never hit this, because
enforceBans()returns early forthem — which is why a forum looks healthy until the first ordinary member tries to
log in.
The deprecated
banPermissions()compatibility function inSubs-Compat.phpalreadyguards this exact case:
This gives
User::enforceBans()the same guard.Note that
$permission_setsdeliberately keeps no default value.banPermissions()above depends on the uninitialised state to decide whether to load, and defaulting
the property to
[]would paper over the fatal while silently skipping thebans-and-warnings pass instead of loading the sets.
How this was verified
PostgreSQL 17 / PHP 8.4.23.
The baseline was not plain
release-3.0: on that branch a login cannot get farenough to reach this code, because it dies first in the path fixed by #9310. The
baseline used was
release-3.0plus the seven pending fixes #9310–#9317, which isthe first state in which a login reaches
DoLogin().POST ?action=login2as an ordinary member → HTTP 500, and the messageabove recorded in the error log. The login cookie is set before the fatal, so the
member ends up in the confusing state of being logged in on the next request while
the login itself reported an error.
index.phpreturnsHTTP 200 as that member. Repeated three times from clean sessions, all 302.
cannot_accessban on the samemember and confirmed the forum still refuses them —
index.phpreturns HTTP 403with "you are banned from using this forum".
One caveat found while checking that last point: for a banned member, the login
request itself then fails with a different, pre-existing error —
SMF\Actions\Login2::load(): Return value must be of type SMF\Actions\Logout, SMF\Actions\Login2 returned,from the
Logout::call()in the force-logout branch ofenforceBans(). That isunrelated to this patch: it reproduces identically on the baseline without it, and it
happens earlier in the method than the code touched here. Mentioned only so it is not
mistaken for a side effect.
php -lclean.Relationship to other PRs
Touches
Sources/User.phpinsideenforceBans(), at the end of the method (~2236).#9310 touches ~2103 and #9311 touches ~2190 in the same method, so all three are
independent hunks; verified to merge cleanly alongside them.
Issues References (Fixes|Related|Closes)