Skip to main content
Version: Latest

6. Permission Safety — No Platform/Root-Admin Leakage

6.1 Two entirely separate permission catalogs

  • Shumoul.Domain.Constants.PermissionConstants (BackEnd) — ~250 nested classes, 2,278 constants — the tenant-operational catalog (InventoryItems, SalesInvoice, Accounts, CostCenters, Users, Branches, and every other day-to-day business permission).
  • Shumoul.Framework.MultiTenancy.Api.Constants.TenancyPermissions (MultiTenancyApi) — 36 nested classes — the root/SaaS-admin-only catalog: HostingServers, SharedDbConnectionStrings, Tenants, SubscriptionPlans/Packages/Features/PackageFeatures/PlanPackages/ExtraFeatures/ FeatureClaims/FeatureReportActions/FeatureSettingsGroups/PackageFeatureRules, BusinessActivities, AccountingChartTemplates, InventoryAccountCategoryTemplates, CostCenterTemplates, TenantStarterKits, ProductCatalogTemplates, ProductCatalogPrerequisiteTemplates, DemoSubscriptions, MenuClaims (admin CRUD), TaxTypes/TaxPeriods/TaxGroups (Saas), Lookups/LookupGroups (Saas), PaymentMethods (Saas), Notifications/NotificationTemplates/NotificationTopics(Subscribers) (admin), TenantSubscriptions/TenantSubscriptionLogs (cross-tenant admin view), ProformaInvoices (Admin.*). Every controller using [MustHavePermission(TenancyPermissions...)] is routed under api/Saas/* — confirmed by grepping all 40 such controllers.

6.2 MenuClaims.json merges both catalogs — but seeding never crosses the line

MenuClaims.json (17,418 lines, 2,916 distinct claim keys) is a single UI-menu tree used by both the root tenant's admin panel and every customer tenant's admin panel — it deliberately contains both catalogs' permission strings (confirmed by two sync tests asserting it's a superset of both PermissionConstants and TenancyPermissions). This is why MenuClaims.json alone cannot be trusted to curate a customer package's permissions — as the task instructions warned.

The actual seeding-time safety backstop is structural, in ApplicationDbSeeder.SeedRolesAsync:

if (tenant == root tenant)
seed ALL of PermissionConstants + ALL of TenancyPermissions to Admin role — unconditional
else // regular/customer tenant
permissions = PermissionConstants values, filtered to only those in tenantClaims (via FeatureMenuClaim)
seed those filtered PermissionConstants + unconditionally seed all of TenantPermissions (small set)
// TenancyPermissions is NEVER seeded for a non-root tenant, by construction

A customer tenant can never receive a TenancyPermissions.* role claim through any package/feature configuration — the non-root branch only ever intersects against PermissionConstants values. This means the real risk for this task was never "TenancyPermissions leaking in" (structurally impossible); it was accidentally wiring a quasi-platform-flavored PermissionConstants entry (e.g. DemoSubscriptionHistory, OAuthClient, delivery-integration diagnostics) into a customer package.

6.3 What this task actually touched — and what it did not

This task never touched MenuClaims.json, FeatureMenuClaim, or any PermissionConstants/ TenancyPermissions value at all. The four Shumoul packages are linked only to existing SubscriptionFeature keys (ProductsManagement, InventoryManagement, PointofSalesApp, SalesManagement, AccountManagement, PayablesManagement, ReceivablesManagement, ProjectManagement) — every one of which is a normal, already-existing, already-reviewed tenant feature with no platform/admin flavor whatsoever. Shumoul.Framework.MultiTenancy.Test.Packages. ShumoulPackageFeatureSeederShould.EmbeddedSeedFile_OnlyReferencesKnownTenantOperationalFeatures (see Chapter 11) guards this permanently — it fails immediately if a future edit introduces any feature key outside this known-safe set.

Since SubscriptionPackageFeatureRule/SubscriptionFeatureReportAction/SubscriptionFeatureSettingsGroup are all pure catalog/entitlement data (never raw permission strings), and since no SubscriptionPackage-to-FeatureMenuClaim wiring was added or modified by this task, there is no path by which this work could have granted a customer tenant any elevated or platform permission.