From c745aa8b2cd1baecf206b45463dfb06c06ffe71e Mon Sep 17 00:00:00 2001 From: jwed Date: Fri, 21 Aug 2026 12:48:47 -0400 Subject: [PATCH] Spec: Wi-Fi access becomes an explicit allow-list, configured in settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two changes from the operator. Session length and devices-per-role are set in the settings UI, on the existing Portal settings page rather than a new one. Two presentation decisions that are really correctness decisions: · the role is picked from a DROPDOWN of seeded roles, not typed. match_value is compared exactly, so "Student" or "studnet" matches nobody and the failure surfaces as a pupil with no Wi-Fi and nothing logged anywhere. A dropdown makes an unmatchable value impossible to enter. · session length is entered in DAYS with presets. session_minutes stays the stored unit, but a term is 172,800 minutes and nobody should type that — the same reasoning that presents cron schedules as presets instead of raw strings. And access is now an explicit allow-list: no role added means no Wi-Fi. That makes `default` an anti-feature, because a default row matches everybody and turns the allow-list into a suggestion, so it is removed from the available match types. resolveMapping() already returns null when nothing matches and null already produces the "No WiFi access configured for your account." denial, so deny-by-default is the behaviour as soon as the catch-all cannot be created. Safe to do now specifically because unifi_vlan_mappings has ZERO rows in every environment — nothing is revoked from anybody. The same change made after a school had leaned on a catch-all would cut off every unmapped person at once. VLAN stays required per row. It was not in the request, so it is called out in the spec rather than quietly dropped: the network still has to know where to put the device. --- ...08-21-byod-wifi-portal-hardening-design.md | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/superpowers/specs/2026-08-21-byod-wifi-portal-hardening-design.md b/docs/superpowers/specs/2026-08-21-byod-wifi-portal-hardening-design.md index c3207fe..759bd18 100644 --- a/docs/superpowers/specs/2026-08-21-byod-wifi-portal-hardening-design.md +++ b/docs/superpowers/specs/2026-08-21-byod-wifi-portal-hardening-design.md @@ -53,6 +53,8 @@ The portal is not the obstacle. **MAC randomization is** — see decision 2. | 4 | Add `role` as a `match_type` | Quotas were asked for per role; roles are the platform's own authority and need no external sync | | 5 | One Wi-Fi Devices page grouped by person; no profile card | Matches the requirement exactly; keeps `/admin/users` as the one directory | | 6 | Device type is **reported, not enforced**, for now | Whether `dev_cat` is populated in this environment is unverified; enforcing on unvalidated data can lock out the wrong people | +| 7 | **Explicit allow-list only** — no `default` mapping | No Wi-Fi unless an admin has added that role and set an amount. A `default` row silently grants everyone, which is the opposite of the requirement | +| 8 | Roles are chosen from a dropdown; session length entered in days | A free-text role slug that matches nothing fails silently — it presents as "this student has no Wi-Fi" with no error anywhere to explain it | --- @@ -173,6 +175,41 @@ so "one term" means one term. --- +## C2. Settings: who gets Wi-Fi, and how much + +Access is an **explicit allow-list**. A person gets Wi-Fi only if one of their +roles has been added here with an amount. No role added means no Wi-Fi, and that +is the intended answer rather than a gap to be filled by a catch-all. + +Consequences, stated so nobody re-adds the hole later: + +- **`default` is removed from the available match types.** A `default` row matches + everybody, so its existence makes the allow-list advisory. `resolveMapping()` + already returns null when nothing matches, and null already produces the + "No WiFi access configured for your account." denial — so deny-by-default is + the behaviour once the catch-all cannot be created. +- Safe to remove now: **there are zero rows in `unifi_vlan_mappings` in any + environment**, so nothing is being revoked from anyone. Doing this later, after + a school had leaned on a catch-all, would cut off every unmapped person at once. +- `email_domain`, `group` and `ou` stay available — they are narrow matches, not + catch-alls, and removing working options is not part of this. + +### The settings screen + +Extends the existing Portal settings page rather than adding another one. Per row: + +| Field | Control | Why | +|---|---|---| +| Role | **dropdown of real seeded roles** | Free text is the defect. `match_value` is a string compared exactly, so `Student` or `studnet` matches nobody and the failure surfaces as a pupil with no Wi-Fi and nothing logged. The dropdown makes an unmatchable value impossible to enter. | +| Devices | number, 1..50 | The quota. Column is already bounded. | +| Session length | **days, with presets** (a week / a month / a term / a year) | `session_minutes` stays the stored unit, but a term is 172,800 minutes and nobody should type that. Same reasoning as presenting cron schedules as presets rather than raw strings. | +| VLAN | existing select | **Still required** — the network needs to know where to put the device, and this was not in the original request, so it is called out rather than quietly dropped. | +| Order | existing `sort_order` | Precedence for multi-role people: staff above parent gives a staff-parent 4. | + +Roles already in the system and available to add: staff, student, parent, +oncall-staff, coach, alumni, applicant, board-member, contact, homeschool, +homeschool-parent, homeschool-student, open-house, tuition-payee, forms-payer. + ## D. The Wi-Fi Devices page New page under Unifi, gated on the existing `unifi.auth`, grouped by person.