Restore the CRLF endings of the two files the last commit reflowed
3bfe883c rewrote PublicProvider.tsx and the org route wholesale — 1919 and 121 lines for what was a one-line addition to each. The content was intact; the endin...
16 unenforced becomes 13 — reports and dashboard deleted, tasks kept
VIEW_DASHBOARD, VIEW_REPORTS and EXPORT_REPORTS are gone from the mirror, both parent maps, the pinned counts and useUserInfo's enumeration, and app/dashboard a...
17 unenforced becomes 16 — support topic creation takes its own right
POST /support/topics was gated on MANAGE_SUPPORT, the parent, so the child right looked dead while being the thing checked. It now takes CREATE_SUPPORT_TOPICS, ...
Creating a topic takes CREATE_SUPPORT_TOPICS, not its parent
The gate was already there and refusing callers — on MANAGE_SUPPORT, the parent — so CREATE_SUPPORT_TOPICS read as decorative in every editor while being exactl...
Campaigns take the bulk-email rights, like the recipient resolver already did
Bulk email was half-enforced and the halves were in different services. **************** in SecurityService has gated recipient resolution on MANAGE_BULK_EMAIL ...
18 unenforced becomes 17 — bulk email campaigns are gated
VIEW_BULK_EMAIL and MANAGE_BULK_EMAIL now cover the campaign and provider surfaces in EmailService, which checked nothing, joining the recipient resolver in Sec...
Stop losing the brand palette on the way to the browser
Organization.fromJSON could not read back its own output. It mapped the brand ramps only from the nested `colors` object SecurityService sends, but what it EMIT...
20 unenforced becomes 18 — marketing materials and social media are gated
VIEW_MATERIALS and VIEW_SOCIAL_MEDIA now have server enforcement, alongside their MANAGE counterparts which were client-only. 29 endpoints across seven MediaSer...
Materials and the social timeline take rights, not just ownership
MANAGE_MATERIALS and MANAGE_SOCIAL_MEDIA counted as enforced in the audit and every site was a .tsx file. Behind them 29 endpoints checked ownership and no righ...
The analyzer could not see nested arguments — six rights were never decorative
26 unenforced becomes 20, and none of it is new enforcement. The six sales-agent rights have been gated the whole time: AiSalesAgentController checks them acros...
Let a failed branding save say what actually went wrong
"Save failed: Failed to save branding identity settings" discarded the status and body of a PUT that enforces the applied security model, so a 403 whose reason ...
Stop failing a branding save over compliance flags it never sent
PUT /org/{id} derived the age ranges and the three ATF flags from the stored row whenever a request omitted them, then asserted all five against the applied sec...
Judge the ATF flags a request changes, not the stored row
**************** judges all three flags every time, so an update path had no way to say "this request does not touch tobacco". Callers filled the gap by re-deri...
28 unenforced becomes 26 — AI models and usage are gated
MANAGE_AI_MODELS gates all four endpoints of AiModelController and VIEW_AI_USAGE both of AiUsageController. Neither checked a right before, only org ownership. ...
Administering models and reading usage take the rights that name them
AiModelController and AiUsageController checked organization ownership and no right. The 403s already in the model controller compare provider.getOrganizationId...
30 unenforced becomes 28 — voicemail is gated, call routing is kept
VIEW_VOICEMAIL and MANAGE_VOICEMAIL now gate VoipVoicemailController, which asked for a right only when playing back a recording in a PHI organization and for n...
Working with voicemail takes a right, not just handling PHI
VoipVoicemailController required VIEW_CALL_LOGS on exactly one path — playback, and only when the organization handles PHI. Listing voicemails with their transc...
32 unenforced becomes 30 — mailbox administration gated, email logs deleted
VIEW_EMAIL_ACCOUNTS now gates the administrative reads on MailboxController and MANAGE_EMAIL_ACCOUNTS gates every write. Both were client-only before: the whole...
Mailbox administration takes a right, and three endpoints get a session
MailboxController enforced no right at all. MANAGE_EMAIL_ACCOUNTS existed only as a checkRight in the browser and VIEW_EMAIL_ACCOUNTS was referenced nowhere, so...
Keep a security model's app grants off the catalog
The Allowed Apps tab showed "Not Available" for every app and snapped back whenever another option was picked. loadModels() runs from a mount-only effect, so i...
Gate platform uploads on MANAGE_PLATFORM_SOUNDS, not org ownership
An operator granted "Manage Platform Sounds" on the Platform Administrators page saw the Sounds tab — the console gates on the right — opened the upload dialog,...
37 unenforced becomes 32 — CRM contacts and leads are gated
Contacts: all 26 endpoints of EmailService's ContactRestController now take the verb that names them. They checked ownership carefully and no right at all, with...
Every contact endpoint takes the verb that names it
ContactRestController checked ownership carefully — orgOf, userOf, assertBookIsMine, assertGroupIsMine, and 404 rather than 403 so somebody else's id is never c...
Creating and deleting a lead take the rights that name them
LeadController is one of the better-gated controllers in the platform — VIEW_LEADS, EDIT_LEADS, ASSIGN_LEADS, REASSIGN_LEADS_MANUALLY, VIEW_UNASSIGNED_LEADS and...
Stop the letter-fallback org logos from flashing
An org with no logo shows its initial, and on /network that initial blinked continuously. Three things stacked up. PlatformRightsProvider wraps the whole app, ...
41 unenforced becomes 37 — the document verbs are gated
CREATE, EDIT, DELETE_DOCUMENTS and MANAGE_STORAGE now gate DocsService's ImagingController and DocumentController and ConversionService's upload endpoints. The ...
Uploading a file takes CREATE_DOCUMENTS, like every other way of making one
The document rights are enforced in DocsService as of this change, but the upload endpoints are not in DocsService — kamo-internal's app/api/imaging/upload prox...
The document verbs are enforced, as the org's security model resolves them
ImagingController checked ImageAccessLevelType and nothing else, and DocumentController checked only that a session existed. Access level is a real control but ...
A loan id is not a credential — scope every LoanController read to the org
LoanController.get(uid) took a loan id and returned whatever it found. Any caller who reached the service got the loan, balances and borrower included, whicheve...
43 unenforced becomes 41 — the two notes placeholders are deleted
Mirrors the shared-lib removal of SHARE_NOTES (72) and MANAGE_NOTES_SETTINGS (73): the TS constants and the registry array, the one parent-map edge in both righ...
Renaming a member is an admin act, and now has a gate
The member profile's legal-name card was self-only, so an admin who typed a name wrong on the create form could never correct it — MemberCreateForm asks for pre...
46 unenforced becomes 43 — the notes endpoints are gated
Fifth cluster. The Notes backend is KBService, not SecurityService: the entity sits under donotmodify/dao/beans/security/ and kamo-internal proxies /api/notes/*...
Every notes endpoint takes the right that names it, and hasRight fails closed
NotesController checked no right on any of its ten endpoints. VIEW_NOTES was enforced in exactly one place — navRegistry.ts, hiding the Notes app in the browser...
Stop a page-2 request from cancelling the folder you just opened
Junk, Trash, Archive and every custom folder showed the same mail — whatever was on screen before them. The server was not the problem: replaying listMessages a...
Close the add-path route around the platform-admin lockout guard
POST replaces an existing administrator's rights, so it can take them away as well as give them — but only the PUT and DELETE paths checked whether the edit wou...
Let a granted administrator actually see Platform Administration
The section gate and the settings menu topic still asked useUserInfo's hasPlatformAdminAccess, which is owner-of-the-top-level-org or an open god-mode window an...
Send an operator with no Configuration tab somewhere real
SYSTEM_USER and MANAGE_UNIVERSE gate surfaces that are not tabs on this page, so somebody holding only those is a platform administrator the page guard admits a...
The meet seed recomputes applied rights instead of waiting for a boot
Granting the two rights on org_role_rights changes nothing anybody can observe. member_rights_applied is the flattened snapshot the session and the server-side ...
48 unenforced becomes 46 — meet create and join are gated
Fourth cluster of the 48, and it split three ways rather than the two the design predicted. The design attributed the enforced MANAGE_MEET_SETTINGS check to Ch...
Draw the callback markers as soon as the summary loads
The summary lands after the grid has drawn its rows, and AG Grid keeps the cellRenderer a column was built with and only re-creates a cell when its row changes....
A deleted upload now falls back instead of going silent
The previous commit claimed a retired sound "degrades to the layer beneath it rather than to silence". It did not. `isKnownSoundId` accepts a custom id on shape...
48 unenforced — the six PLOS rights are gated now
Gating the LOS proxy made PLOS_* enforced, but they stayed on the unenforced list, so the editors were showing 'Not yet enforced' against six live gates. Third ...
Put the callback marker after the lead id
Leading the cell with the marker pushed the id right on any row with a call waiting, so the ids no longer lined up down the column while scrolling.
The meeting reminder reaches the bell, and pops once
MeetReminderPopup drew its own Snackbar from its own subscription to **************** Nothing else knew a reminder had happened: the bell stayed at zero, the de...
The meeting reminder is a notification, not a popup of its own
The sweep published a bespoke event on /topic/meet/reminder/{memberId} that a dedicated Snackbar drew in the corner of the screen. That popup was the entire rem...
One calendar reminder, announced once
A reminder was arriving twice. The deck picks it up as a notification, and the older path picked it up as well: CalendarWebSocketContext opened a second STOMP c...
A note is owned by a member, not by an account
Mirrors the API: NoteDTO now carries memberId in place of userId. A member is the signed-in account inside ONE organisation, so opening a different org shows a ...
Read and write notes as a member, never as an account
Every endpoint now requires memberID on the session — reads included — and refuses the request without one. The reads used to need only a userID, which is globa...
Scope notes to a membership instead of an account
A note hung off USER_ID alone and carried no organisation at all. A user is global and a member is that user inside one organisation, so an account belonging to...
Re-derive the audit structurally — 57 decorative
The count moved 58 -> 52 -> 47 -> 57 because every version hard-coded a list of gate-helper names and the codebase has more than anyone can recall. Six were mis...
Like what you see shipping?
All of it arrives in your workspace on its own. Start on the free plan and read this page again in a month.
