Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .changeset/1369-tenant-admin-org-presentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'hotcrm': minor
---

The SaaS tenant administrator may now author their own organization's views, dashboards and reports

In the SaaS / multi-org composition (`HOTCRM_COMPOSITION=saas`), the
**Tenant Administrator** permission set now holds the platform's org-scoped
`manage_org_presentation` capability. The platform admits a metadata save from
this capability only for the types it lets each organization override (views,
dashboards, reports, translations and email templates). The save becomes an
overlay for the caller's own active organization, and other organizations see
none of it.

Everything else stays as it was. Objects, flows, apps, pages, permission sets
and positions still require `manage_metadata`, which reaches every
organization in a deployment, so a tenant admin still cannot author them. The
community app's **System Administrator** and the default composition are
unchanged.
35 changes: 21 additions & 14 deletions src/sales/profiles/tenant-admin.profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,24 @@ import { SystemAdminProfile } from './system-admin.profile';
* and no session produces. Keeping the two grants therefore says the true
* thing about a tenant admin: inside their org, they see and edit everything.
*
* ### What is deliberately DROPPED, and why it is not an oversight
*
* `customize_application`, `manage_profiles` and `manage_roles` are not
* granted. All three describe METADATA authoring, and under a walled posture
* the only metadata-authoring capability the platform has is `manage_metadata`
* — `scope: 'platform'`, which unlocks env-wide tier-B authoring (objects,
* flows) with cross-tenant reach. There is currently no key that lets a tenant
* admin author even their own org's overlays without also handing them that
* reach, so granting these three would describe an authority the deployment
* cannot safely give.
*
* Blocked-by: objectstack-ai/objectstack#12702 — org-scoped presentation
* customization authority. When that capability ships, the tenant admin gains
* it here and this paragraph shrinks to a grant.
* ### What a tenant admin may AUTHOR: their own org's presentation, no more
*
* `manage_org_presentation` IS granted. The platform declares it `scope: 'org'`
* (objectstack-ai/objectstack#12702): it admits `/meta` item writes only for
* the types whose registry entry declares `allowOrgOverride: true` — on the
* installed 17.6.0 that is view, dashboard, report, translation and
* email_template — and only as overlays in the caller's own active
* organization. Measured on 17.6.0 against the platform's own write verdict
* (`metaWriteCapabilityVerdict`): with this grant those saves are allowed when
* the session carries an active organization, and refused when it does not.
*
* `customize_application`, `manage_profiles` and `manage_roles` stay
* ungranted. They describe authoring the app's structure and security model —
* objects, apps, pages, permission sets, positions — and the registry declares
* every one of those types `allowOrgOverride: false`, so the only key that
* admits those writes is still `manage_metadata`: `scope: 'platform'`, reach
* across every organization in the deployment. Granting the three would
* describe an authority the deployment cannot safely give.
*
* `manage_sharing` IS granted: the platform declares it `scope: 'org'`
* ("Administer record sharing … beyond one's own records"), which is precisely
Expand All @@ -129,5 +133,8 @@ export const TenantAdminProfile = {
// Org-bounded by the driver's tenant predicate — see the audit above.
'view_all_data', 'modify_all_data',
'manage_sharing',
// Org-scoped `/meta` writes, only for `allowOrgOverride: true` types and
// only in the caller's own active org — see the AUTHOR note above.
'manage_org_presentation',
],
};
11 changes: 11 additions & 0 deletions test/saas-composition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,17 @@ describe('tenant_admin is an ORG admin, judged by the platform capability regist
).toBe('org');
});

it('holds the org-scoped presentation-authoring capability (#1369)', () => {
// The org-bounded subset of metadata authoring. Its scope is read off the
// installed registry, never copied here: if the platform ever widened it,
// this and the platform-scoped pin below would both go red.
expect(TenantAdminProfile.systemPermissions).toContain('manage_org_presentation');
expect(
scopeOf.get('manage_org_presentation'),
'manage_org_presentation is not org-scoped on the installed platform line — a tenant profile must not hold it',
).toBe('org');
});

it('grants NO platform-scoped capability', () => {
// Read off the platform's own registry rather than a hand-listed denylist,
// so a capability that becomes platform-scoped upstream — or a new one
Expand Down
Loading