9cbc8e682a72b7e5965a3ddcd31e023e28864966
GET /app/unifi/guest-card/data carried `web,auth` and nothing else, and
GuestCardController::data() checked nobody. So ANY signed-in account could fetch the
live guest Wi-Fi password by requesting the URL — on this dev database that is 663
students and 418 parents, none of whom can see the card in the UI.
The route comment asserted the protection that did not exist:
// ── Guest Wi-Fi home-card poll (any logged-in user; card visibility is
// grant-gated by the shell). …
Card visibility IS grant-gated, but that gate is PRESENTATION only — DashboardController
decides which cards to render for a user; it has never constrained the endpoints those
cards poll. So the comment described the card, and the endpoint was open.
Now enforced server-side: you may poll it iff you may see the card
(DashboardCardGrant::accessibleCardKeysFor), with the usual super-admin pass. Kept as
the card gate rather than a permission so the widget stays grantable to non-unifi staff,
which is the whole point of a home-page card.
Verified: a non-super user holding only the MyDocuments card is refused for
unifi.guest_wifi, and an explicit grant admits the same user.
Found while auditing whether the bespoke page-grant middleware could be deleted. This
route sits inside that middleware's prefix match, so deleting it would have removed the
last non-auth check on the endpoint — it needed closing first either way.
Two things nearby that are NOT fixed here and want their own look:
• unifi.portal.callback (`auth` only, no permission) checks only that somebody is
logged in.
• unifi.api.wifi.current-password is token-protected via hash_equals, which is fine,
but it is the same secret behind a different door — worth confirming that token's
rotation story.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDA3fQbsa3gaHCFoV1Q8Gg
Description
UniFi snap-in package for the Dashboard shell
Languages
PHP
100%