Commit Graph

11 Commits

Author SHA1 Message Date
186c1eaf2b Gate the guest Wi-Fi card on its own key, and gate its poll endpoint properly
The home card used to be grant-gated by the shell's card table, which left the
poll endpoint behind `auth` alone — and that endpoint returns a LIVE CREDENTIAL:
the guest network's current rotating password and its join QR code. "Any logged-in
user" was never an adequate gate for that; the card table was doing the work, and
the card table is gone.

New key rather than reuse: unifi.guest_wifi exists precisely so front-desk staff
can be given the guest password WITHOUT any other network access. Reusing
unifi.settings would have meant handing out controller configuration to get a
Wi-Fi password read.

Authorised inside the controller rather than by route middleware, so the endpoint
stays outside the unifi page-permission groups — it is a home-page widget, not a
unifi page.

Nobody holds the new key until it is granted, so the card is invisible until then.
That is the intended direction: capability is assigned deliberately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDA3fQbsa3gaHCFoV1Q8Gg
2026-07-30 12:15:05 -04:00
c93f73ce02 Remove the page-access matrix: routes, controller methods, model
The last of the hand-rolled Access tabs. Its UI went in the previous commit; this
removes what sat behind it — the five settings/pages-access routes, the controller
methods serving them, the grant model, and the orphaned nav-visibility resolver that
read it. The table itself is dropped by a shell migration.

Nothing consulted any of it. Page access is a permission held through a group.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDA3fQbsa3gaHCFoV1Q8Gg
2026-07-29 14:31:07 -04:00
cac47b74bd A2: retire the per-app grant layer (nav resolver + page-grant middleware)
Removes this app's NavVisibilityRegistry resolver and, where it had one, its
page-grant middleware. Sidebar visibility and route access now come from the
same place as everything else: a permission, held through a group.

Nav visibility already had a better answer than the resolvers. NavItem::
visibleTo() applies required_permission to any route prefix that is NOT
registered — central-notifications documents exactly that and is why it never
registered one — and HandleInertiaRequests then applies a safety net reading
the TARGET route's own `permission:` middleware. The resolvers were overriding
both with a lookup against a grant table that is now empty, so a permission
holder could open a page while its sidebar link stayed hidden. That
inconsistency is what this removes.

The middleware only ever NARROWED routes that already carry `permission:`,
with one exception worth naming: payments' legacy rule had NO permission
bypass, so a payments.view holder was denied the ledger it gates. Deleting it
fixes that rather than changing it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDA3fQbsa3gaHCFoV1Q8Gg
2026-07-28 21:58:09 -04:00
7c6bf99201 Delete the bespoke page-grant middleware (A2)
This app attached an Enforce*PageGrant middleware at runtime via a
RouteMatched listener, consulting its own private *_page_grants table to
decide whether the request could proceed.

Safe to remove because the middleware only ever NARROWED routes that
already carry a `permission:` gate. Three independent audits agreed on
that for all eleven apps: no route relied on it as its sole
authorization, so deleting it cannot widen access — the permission the
route already declares still decides.

Two cases were not simply narrowing, and both were handled first:

- unifi's guest-Wi-Fi card poll was `auth`-only and leaked the live guest
  password to any signed-in account. Closed in 9cbc8e6 before this, so
  this commit does not reopen it.
- google's middleware had NO permission-holder bypass, so it could deny a
  legitimate google.manage holder. Deleting it restores their access.

What is deliberately NOT done here: the grant tables, models and the fold
in User::pageGrantedPermissions() all remain, so a grant row can still ADD
access. It can no longer 404 anybody. The fold goes last, together with
the characterisation tests that pin it.

Verified: pages in all four de-gated apps return 200, the affected suites
pass, and dashboard:doctor's advisories drop from 18 to 7.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDA3fQbsa3gaHCFoV1Q8Gg
2026-07-28 19:19:39 -04:00
070001ffff Guest Wi-Fi dashboard card (network + rotating password + join QR)
New home card 'unifi.guest_wifi': shows the guest SSID, the current
rotated password (unifi.password_rotation.last_password), and a WPA
join QR built from the WIFI: string. Polls app/unifi/guest-card/data
(auth) every 60s so it updates the moment the rotation engine changes
the password — the QR regenerates client-side to match. Self-hides
until enabled with an SSID and a rotated password; grant-gated via
Admin -> Settings -> Dashboard Cards. New settings (WiFi rotation
section): enable + network name + card title.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 19:37:44 -04:00
462a1a3611 feat(1.13.0): page grants on the standard admissions-style pattern
unifi_page_grants gains role + default grantee types and can_view
(deny-by-default "Everyone else" row); enforcement moves from the
RouteMatched listener — where request->user() is always null and the
check silently failed open — into route-appended middleware with the
permission-holder pass. Pages-access endpoints gain role search +
default-row handling.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 21:31:41 -04:00
429cd44ac5 fix: register unifi pages with shell NavVisibilityRegistry; v1.12.1
The Access tab persists user/group grants in unifi_page_grants and the
existing RouteMatched listener honors them on the request path, but
NavItem::visibleTo() only consulted the page's required_permission —
so a granted user never saw the menu entry to reach the page. Register
the unifi.* prefix with the shell's NavVisibilityRegistry so the
sidebar lists exactly the pages the grant table allows.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-05 12:12:58 -04:00
f5848907f5 feat(access): strict allowlist + add groups by search
* UnifiPageGrant::userCanAccess no longer falls back to "open" when a
  page has no grants saved. Pages now require an explicit grant for
  every non-super-admin user — either a direct user grant or via a
  group they belong to. Matches the new dashboard-wide access model.
* Route enforcement returns 404 (was 403) so ungranted users can't even
  confirm the page exists.
* New /settings/pages-access/groups/search endpoint mirrors the
  user typeahead. Groups are no longer all listed by default — only
  super-admin groups (locked-on) and groups with at least one existing
  grant show up in the matrix. Operators add more via search.

v1.7.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 19:59:28 -04:00
a33f2885ff feat(access): per-page user/group grants, snap-in-local
A snap-in-owned access mechanism. Adds:
  - unifi_page_grants table (nav_item_id, grantee_type, grantee_id)
    with cascadeOnDelete from nav_items so uninstalling the snap-in
    wipes its grant rows automatically
  - UnifiPageGrant model + ::userCanAccess(user, navItem) helper
  - UnifiPagesAccessController (index + update), super-admin only
  - RouteMatched listener in UnifiServiceProvider that 403s any
    unifi.* route if the matched nav_item has grants and the user
    isn't a super-admin / granted user / member of a granted group

Semantics: a page with NO grants stays open per the existing
permission middleware (no behaviour change). The moment grants are
added, ONLY super-admins and listed users/groups can see/open the
page. Super-admins always pass; their access can't be removed.

v1.4.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 16:47:57 -04:00
0802ef35f3 feat: password rotation, PPSK management, VLAN/AP groups
- Add password rotation: RotatePasswords console command + migration + service updates
- Add PPSK management: UnifiPpsk model, migration, SyncPpskSchedules console
- Add VLAN groups and AP groups: VlanGroupController, ApGroupController, model, migration
- Add RebootAllAps console command
- Add in_alert column to device states
- Wire new features through service provider, routes, and existing controllers/services

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 17:54:24 -04:00
Joel Wedemire
ce3217d8f4 feat: initial commit — UniFi snap-in package
Full UniFi dashboard snap-in including:
- WiFi/client/device stats with time-series snapshots
- Client Dashboard with traffic, satisfaction, signal, download charts
- Webhook alerting with debounced offline/online detection
- AP snapshot collection, client snapshot collection
- Device classification (type and OS) from OUI/hostname heuristics
- Webhook cooldown, templates, and multi-platform delivery

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 23:00:05 -07:00