Skip to content

History / Roles Developer Guide

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
  • Roles dev guide: the guard that fatals, and why your test says it passed Add requireCapabilityJson() but forget to require rbac.php and the endpoint doesn't refuse anyone — it dies with "Call to undefined function". That is worse than no guard: it looks protected and is broken for everyone, admins included. And PHP serves a fatal as HTTP 200, so a status-code check reports a cheerful PASS on a completely broken endpoint. That is exactly how it slipped through on Change Management (#840): one endpoint requires its includes with __DIR__ rather than a relative path, so the automated insertion missed it, and the test agreed. Documents both defences: D005 now catches it statically and reports it as BROKEN above everything else, and the testing section now says in terms — check the response BODY, never the status code alone.

    @edmozley edmozley committed Jul 13, 2026
  • Roles dev guide: say "two", not "one" — the page contradicted itself It opened with "the manifest is the single declaration… there is no second list to keep in step", and then two paragraphs later told you to write two things. A developer reading that would reasonably ask which it is. It is TWO, and that is now the first thing on the page: 1. the NAME — a Cap:: constant 2. the DETAILS — the module's manifest entry …with a diagram of the five things generated from them, and — the part that was missing — WHY it cannot be one. The name has to be a real constant, a constant has to exist before the code runs, and generating one needs a build step this project deliberately doesn't have. And the constant isn't ceremony: it IS the safety property. Cap::ASSETS_VCENTER misspelled is an immediate fatal error; 'assets.vcentre' misspelled is a silent permanent 403 the administrator never sees. So the second list is the one worth having, and two is the floor. Also clarifies that the residual block in settings_keys.php is a TO-DO list of unconverted modules, not a duplicate — it empties as the roll-out proceeds. The moving-parts table is demoted below this and now marks each row as hand-written or derived, so it reads as reference rather than as the lede.

    @edmozley edmozley committed Jul 13, 2026
  • RBAC: the manifest is now the single declaration, and document D005 Roles — Developer Guide: the recipe changed. The capability registry, the module list and the setting-key map are all DERIVED from <module>/settings/manifest.php now, so adding a capability is a constant plus a manifest entry — there is no capRegistry() to hand-edit and no settings_keys.php row to promote. Calls out the one seam derivation doesn't cross (a Cap:: constant no manifest claims, which is a capability nobody can hold — so any guard using it 403s everyone but admins, permanently and invisibly to the admin who bypasses the check). Adds a section on D005, the endpoint audit, and the discipline behind it: type safety catches a MISSPELLED permission, nothing catches one nobody wrote. Lists the five holes that were each found by hand, by accident. And the rule that keeps the tool useful — if you add an auth mechanism, teach it, because its first run gave five criticals of which four were false alarms, and a scanner that cries wolf is ignored within a week. Security: adds the auditing section so the tool is discoverable from the security page, not just the developer guide.

    @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