In a multi-tenant SaaS the riskiest role is the cross-tenant superuser. One person who opens the Netorigo Admin and can see every tenant's data, switching tenants with one click. One half-written guard, one forgotten RLS policy, and "Tenant A's data leaks to Tenant B" pops out of the database. Below we describe the globalRoleTier=superuser design pattern as we've come to solve it on Netorigo.
Two dimensions
The Netorigo user model combines two independent dimensions:
- Tenant-level role —
tenant_user_rolestable, rows are(user_id, tenant_id, role_id). One user can be on 5 tenants simultaneously, each with a different role. - Global tier —
users.global_role_tierenum:member(default),support,superuser.
member users can only see tenants where they have an explicit role. support users can see all tenants but in read-only mode. superuser can see and modify everything.
The dual dimension is necessary because global_role_tier does not replace tenant-level roles — it supplements them. Even a superuser works on a concrete tenant, just without needing a tenant-level role on it.
The active tenant_id
Every logged-in user session has an "active tenant_id" — the backend RequestContext.tenant_id value. For member users it comes from the URL (/admin/[tenantId]/...) and a guard verifies that the user actually has a role on this tenant.
For superuser the active tenant_id also comes from the URL, but without the extra check. But we add two things:
- The header-bus audits every new tenant entry. A
superuser_tenant_switchevent is recorded (audit log) every time a superuser enters a new tenant. Fields:user_id,from_tenant_id,to_tenant_id,at_timestamp,ip_address,user_agent,reason(optional text — UI prompt "Why are you entering?"). - The
RequestContext.actor_rolecarries asuperuser_overrideflag. A query in the Catalog module knows the current user is a superuser, even though the tenant role would bemember— and it shows up in the audit log.
RLS as the last line of defense
PostgreSQL Row-Level Security (RLS) policies are placed on every multi-tenant table. The policy reads the current_setting('netorigo.tenant_id') session variable. At the start of every HTTP request, the backend sets it on the connection pulled from the connection pool. If the tenant_id doesn't match, SELECT returns 0 rows.
For superusers it's special: the backend also sets netorigo.is_superuser=true on the connection, and the RLS policy is: tenant_id = current_setting('netorigo.tenant_id') OR current_setting('netorigo.is_superuser')::bool = true. So the superuser can cross tenants without turning off RLS.
This two-line policy has saved us from three audits. If someone forgets a guard in backend code, RLS still catches the leak.
The "don't become your own incident" pattern
The superuser role needs to be handled with both hands. Our practice:
- Total of 6 globally configured superusers. Not 30, not 100. The CTO + 2 senior backend devs + 2 SREs + the security lead.
- Mandatory MFA. Hardware key (YubiKey) WebAuthn, not SMS, not TOTP.
- Auto-expire role. A superuser is not set permanently — it expires 90 days after every manifest update. To keep it active you need to renew via system email confirmation.
- Tenant-switch emails to the tenant owner. If a superuser enters a tenant admin, the tenant's primary operator receives an email: "A Netorigo support member entered your admin interface. Reason: <text>. If you don't recognize this, contact security@netorigo.com immediately."
- Audit-log retention 7 years. Superuser tenant-switch events are never deleted.
The "break-glass" mode
Emergency scenario: a major client reports a critical bug on Sunday evening, and every senior superuser is on vacation. In that case a 7th "break-glass" superuser can be activated through a two-key process: the CTO + the CFO each invoke their YubiKeys against the Vault to mint a temporary credential good for 4 hours. Every action is audit-logged, and at session close (or after 4 hours) the credential is automatically destroyed.
We've used it once in two years. It worked.
Closing
The power of the superuser is critical in every multi-tenant SaaS. Two dimensions (tenant-role + global-tier), audit log on every tenant switch, RLS as the last safety net, mandatory MFA, auto-expire, break-glass mode. This complete pattern is exactly what Netorigo enterprise contracts get audited against — a senior CISO understands in 30 minutes why we won't be the next headline.