Skip to content

History / Roles and Permissions

Revisions

  • RBAC wiki: verified against the code, and two stale claims corrected Checked every claim the Roles pages make against what the code actually does — not by reading the wiki back to myself, but by generating the truth from capabilities.php / the manifests and comparing: 16 modules ✓ 76 capabilities ✓ 143 guarded endpoints ✓ every module has a <module>.manage umbrella ✓ the umbrella satisfies every capability in its module ✓ 9 personal-preference tabs declare no capability ✓ an undefined key grants nothing ✓ module 'system' resolves to is_admin ✓ the registry is derived from the manifests ✓ every per-module capability count and every 'sensitive' badge ✓ Two things were stale, both now fixed: 1. The setting-key list in settings_keys.php was described as "a residual list of modules not yet converted, which shrinks to nothing". It doesn't: every module IS converted, and what remains is the System module's keys — permanently, by design, because System is deliberately administrator-only. Corrected in the wiki and in the code comment. 2. The "guard writes, never reads" rule appeared WITHOUT its credentials exception on both Roles & Permissions and Security. Only the dev guide carried it. Since that exception exists precisely because the rule was applied too comfortably — get_mailboxes was a "read", and it shipped the plaintext OAuth secret to every analyst — stating the rule without it is the dangerous half. Also documented two manifest features that had gone in late and never been written up: a tab may carry a plain 'label' instead of 'label_key' where a module isn't translated, and renderSettingsTabBar() takes the tab-switcher's function name (Problem Management's is pmsTab(), and its panels are id="tab-x" not "x-tab"). A converter that assumes the conventions renders a tab bar whose tabs open nothing. Plus the full field reference for a manifest tab, and the table of shared endpoints that need per-key authorisation — a recurring shape, not a one-off.

    @edmozley edmozley committed Jul 14, 2026
  • RBAC roll-out complete: all 16 modules, and the rules it taught us Roles & Permissions: the roll-out table is now the finished picture — 16 modules, 76 capabilities, 143 guarded endpoints, with the sensitive ones marked. Records why the System module is deliberately NOT converted: it is ~20 areas, every one of them administration by definition, with no operational half to separate out — splitting it would create permissions only administrators would ever hold, which is what is_admin already means. Developer Guide — the six rules the roll-out taught, promoted above the mechanics because they matter more: 1. A read that returns CREDENTIALS is not an operational read. get_mailboxes was a "read", and it shipped the plaintext OAuth client secret to every analyst. 2. A config change offered as a shortcut from an operational screen still needs the config permission (Kanban columns; CMDB property options). 3. Creating is not always administering (create a tag inline: everyday; delete one: administration). 4. An endpoint serving several tabs cannot have one guard — authorise per setting key. A recurring shape, not a one-off. 5. A feature reachable from several modules needs requireAnyModuleAccessJson(). 6. Converting a module is an AUDIT of it. Expect to find, not merely to move. Two new traps, both real: - CRLF. A guard-insertion script searching for "}\n" found nothing in the CRLF files, strpos returned false, false+2 = 2, and the guard was spliced into the middle of "<?php" in ten files — which STILL PASSED php -l, because short open tags made "<?" valid. The HTTP test caught it, the linter did not. - The audit can hide a hole as well as find one. D005 reported db_verify — the endpoint that creates every table in the database — as "signature authenticated", because it scanned for the word relay_secret and db_verify declares a relay_secret COLUMN. Match the call, not the word. And check your write-detection covers what you think: the same tool called CREATE TABLE a read.

    @edmozley edmozley committed Jul 14, 2026
  • Wiki: split the PHP floor page, refresh Roles & Permissions, restructure the sidebar The PHP floor page had grown to 861 lines and was three documents in a trench coat: a decision, a language tour, and a deep argument about permission keys. Split into three, each standing alone: Raising-the-PHP-Floor (307) the DECISION — where we are, who breaks, the verdict, and the constants-to-enums bridge PHP-8-Language-Features (261) the 101 tour of 8.0/8.1 with before/after from real code, including the features that WOULDN'T help us, said plainly PHP-Enums-and-RBAC (353) the one feature the argument turns on — and the reason a capability is a Cap:: constant and never a string. Worth reading even if the floor never moves, because it explains the permission system. Section numbers and cross-references rewritten by meaning rather than by find-and-replace, since the sections changed pages. Roles & Permissions was stale: written before the manifest became the single declaration and before the endpoint audit existed. Now says that capabilities are derived from one file per module (and why the four-list version was the bug), and carries a table of what D005 has actually found — the shared settings endpoint, six Intune endpoints, all 49 RFP Builder endpoints, unauthenticated audit-log forgery, and an LMS learner reaching the authoring settings. Every one found by hand, by accident, which is the point. Sidebar: the security pages are now grouped by the three layers they belong to (module access → capabilities → administrator), rather than a flat list, and all nine RBAC/PHP pages are linked. The two new PHP pages nest under the floor page.

    @edmozley edmozley committed Jul 13, 2026
  • RBAC: bring the wiki up to date with per-tab capabilities Both Roles pages predated the per-tab decision and described a system that no longer exists: rbacCapabilities() (now capRegistry), bare-string guards (now Cap:: constants), "one manage per module, split later" (we went per-tab), and a rename that orphans grants and needs a data migration (there's an alias map now). Neither mentioned the settings manifest, the shared-endpoint key ownership, the sensitive badge, the umbrella, or the security fix. Roles & Permissions — rewritten around the per-tab model: why a tab is the unit (vCenter credentials sit next to a lookup list), the umbrella, sensitive badging, and a full "how it's enforced" section covering all three mechanisms — panels not rendered rather than hidden, hard guards on every write endpoint, and per-KEY authorisation on the shared settings endpoint (including the #829 hole it closed). Plus what is deliberately NOT guarded, and why gating a read breaks the module. Roles — Developer Guide — rewritten as the extension guide: why capabilities are constants (a mistyped string 403s silently and is invisible to admins, who bypass the check — so the typo becomes a privilege escalation), adding/renaming/removing a capability, and the full six-step recipe for converting a module, worked against Asset Management. Now carries the traps that actually bit: a get_* endpoint that isn't a read (get_vcenter performs the sync and Servers calls it), endpoints with no guard at all, and the reminder that converting a module is an audit of it. Plus the three-actor HTTP test matrix and a checklist. Security — the Authorisation section was wrong, not just stale: it claimed the System module "cannot be disabled" and is always accessible to active analysts, which stopped being true when is_admin shipped. Rewritten as the three layers, each with its gate and its default, and the note that hiding a button is never the control. Raising the PHP floor — made the constants-to-enums bridge concrete rather than asserted. Shows the actual migration: call sites are a symbol rename an IDE can do and the compiler verifies (a missed one FATALS; you cannot half-finish it), against hunting 200 string literals where a miss keeps working and a typo 403s in silence. The real work is confined to one file. Also states the honest caveat: constants make the typo loud, an enum makes the wrong thing impossible.

    @edmozley edmozley committed Jul 13, 2026
  • LMS authoring + AI + Roles: new deep-dive pages, refreshed LMS hub - LMS.md: rewritten for the two course kinds (authored + SCORM), the learner/manager split, correct table names, links to the new sub-pages. - LMS-Authoring.md: native content model, editor, native player, server-side grading (answer key never on the wire), the CMI-reuse trick, learner/manager enforcement, SCORM upload safety. - LMS-AI-Authoring.md: mechanics of the three AI helpers (outline / article→lesson / lesson→quiz) — grounding, JSON contracts, the draft-never-writes safety model. - Roles-and-Permissions.md: RBAC Layer 2 (settings permissions) — two layers, deny-by-default, is_admin bypass, server-side enforcement, the LMS pilot. - Roles-Developer-Guide.md: add/edit/remove a capability and wire a module's settings in — registry, guards, the operational-vs-settings split, splitting a capability later, gotchas. - _Sidebar.md + Home.md wired up.

    @edmozley edmozley committed Jul 12, 2026