# Web Gateway Tenant RBAC Contract **Contract ID:** CONTRACT-GATEWAY-RBAC-001 **Status:** APPROVED **Effective Date:** 2025-12-07 **Owners:** Gateway Guild, Authority Guild, Web UI Guild ## Overview This contract defines the tenant isolation and role-based access control (RBAC) model for the StellaOps Web Gateway, ensuring consistent authorization across all API endpoints and UI components. ## Tenant Model ### Tenant Hierarchy ``` Organization (Org) ├── Tenant A │ ├── Project 1 │ │ └── Resources... │ └── Project 2 │ └── Resources... └── Tenant B └── Project 3 └── Resources... ``` ### Tenant Identification Tenants are identified through: 1. **JWT Claims:** `tenant_id` or `stellaops:tenant` claim 2. **Header:** `X-Tenant-Id` header (for service-to-service) 3. **Path Parameter:** `/tenants/{tenantId}/...` routes ### Tenant Resolution Priority ``` 1. Path parameter (explicit) 2. JWT claim (authenticated user context) 3. X-Tenant-Id header (service-to-service) 4. Default tenant (configuration fallback) ``` ## Role Definitions ### Built-in Roles | Role | Description | Scope | |------|-------------|-------| | `org:admin` | Organization administrator | Org-wide | | `org:reader` | Organization read-only access | Org-wide | | `tenant:admin` | Tenant administrator | Single tenant | | `tenant:operator` | Can modify resources within tenant | Single tenant | | `tenant:viewer` | Read-only access to tenant | Single tenant | | `project:admin` | Project administrator | Single project | | `project:contributor` | Can modify project resources | Single project | | `project:viewer` | Read-only project access | Single project | | `policy:admin` | Policy management | Tenant-wide | | `scanner:operator` | Scanner operations | Tenant-wide | | `airgap:admin` | Air-gap operations | Tenant-wide | ### Role Hierarchy ``` org:admin ├── org:reader ├── tenant:admin │ ├── tenant:operator │ │ └── tenant:viewer │ ├── policy:admin │ ├── scanner:operator │ └── airgap:admin └── project:admin ├── project:contributor └── project:viewer ``` ## Scopes ### OAuth 2.0 Scopes | Scope | Description | Required Role | |-------|-------------|---------------| | `policy:read` | Read policies and profiles | `tenant:viewer` | | `policy:edit` | Create/modify policies | `policy:admin` | | `policy:activate` | Activate policies | `policy:admin` | | `scanner:read` | View scan results | `tenant:viewer` | | `scanner:execute` | Execute scans | `scanner:operator` | | `airgap:seal` | Seal/unseal environment | `airgap:admin` | | `airgap:status:read` | Read sealed mode status | `tenant:viewer` | | `airgap:verify` | Verify bundles | `tenant:operator` | | `export:read` | Read exports | `tenant:viewer` | | `export:create` | Create exports | `tenant:operator` | | `admin:users` | Manage users | `tenant:admin` | | `admin:settings` | Manage settings | `tenant:admin` | ### Scope Inheritance Child scopes are automatically granted when parent scope is present: ```yaml scope_inheritance: "policy:edit": ["policy:read"] "policy:activate": ["policy:read", "policy:edit"] "scanner:execute": ["scanner:read"] "export:create": ["export:read"] "admin:users": ["admin:settings"] ``` ## Resource Authorization ### Resource Types | Resource Type | Tenant Scoped | Project Scoped | Description | |--------------|---------------|----------------|-------------| | `risk_profile` | Yes | No | Risk scoring profiles | | `policy_pack` | Yes | No | Policy bundles | | `scan_result` | Yes | Yes | Scan outputs | | `export` | Yes | Yes | Export jobs | | `finding` | Yes | Yes | Vulnerability findings | | `vex_document` | Yes | Yes | VEX statements | | `sealed_mode` | Yes | No | Air-gap state | | `user` | Yes | No | Tenant users | | `project` | Yes | No | Projects | ### Authorization Rules ```yaml # authorization-rules.yaml rules: - resource: risk_profile actions: read: required_scopes: [policy:read] tenant_isolation: strict create: required_scopes: [policy:edit] tenant_isolation: strict update: required_scopes: [policy:edit] tenant_isolation: strict activate: required_scopes: [policy:activate] tenant_isolation: strict delete: required_scopes: [policy:edit] tenant_isolation: strict require_role: policy:admin - resource: scan_result actions: read: required_scopes: [scanner:read] tenant_isolation: strict project_isolation: optional create: required_scopes: [scanner:execute] tenant_isolation: strict delete: required_scopes: [scanner:execute] tenant_isolation: strict require_role: scanner:operator - resource: sealed_mode actions: read: required_scopes: [airgap:status:read] tenant_isolation: strict seal: required_scopes: [airgap:seal] tenant_isolation: strict require_role: airgap:admin audit: required unseal: required_scopes: [airgap:seal] tenant_isolation: strict require_role: airgap:admin audit: required ``` ## Tenant Isolation ### Strict Isolation All data access is tenant-scoped by default: ```sql -- Example: All queries include tenant filter SELECT * FROM findings WHERE tenant_id = @current_tenant_id AND deleted_at IS NULL; ``` ### Cross-Tenant Access Cross-tenant access is prohibited except: 1. **Organization admins** can access all tenants in their org 2. **Internal services** with explicit `cross_tenant` scope 3. **Aggregation endpoints** with `org:reader` role ### Isolation Enforcement Points | Layer | Enforcement | |-------|-------------| | Gateway | Validates tenant claim, injects X-Tenant-Id | | Service | Applies tenant filter to all queries | | Database | Row-level security (RLS) policies | | Cache | Tenant-prefixed cache keys | ## JWT Claims ### Required Claims ```json { "sub": "user-uuid", "aud": ["stellaops-api"], "iss": "https://auth.stellaops.io", "exp": 1701936000, "iat": 1701932400, "stellaops:tenant": "tenant-uuid", "stellaops:org": "org-uuid", "stellaops:roles": ["tenant:operator", "policy:admin"], "scope": "policy:read policy:edit scanner:read" } ``` ### Custom Claims | Claim | Type | Description | |-------|------|-------------| | `stellaops:tenant` | string | Current tenant UUID | | `stellaops:org` | string | Organization UUID | | `stellaops:roles` | string[] | Assigned roles | | `stellaops:projects` | string[] | Accessible projects | | `stellaops:tier` | string | Rate limit tier | ## Gateway Implementation ### Authorization Middleware ```csharp // AuthorizationMiddleware.cs public class TenantAuthorizationMiddleware { public async Task InvokeAsync(HttpContext context, RequestDelegate next) { // 1. Extract tenant from JWT/header/path var tenantId = ResolveTenantId(context); // 2. Validate tenant access if (!await ValidateTenantAccess(context.User, tenantId)) { context.Response.StatusCode = 403; return; } // 3. Set tenant context for downstream context.Items["TenantId"] = tenantId; context.Request.Headers["X-Tenant-Id"] = tenantId; await next(context); } } ``` ### Scope Authorization ```csharp // ScopeAuthorization.cs public static class ScopeAuthorization { public static IResult? RequireScope(HttpContext context, string requiredScope) { var scopes = context.User.FindFirst("scope")?.Value?.Split(' ') ?? []; if (!scopes.Contains(requiredScope) && !HasInheritedScope(scopes, requiredScope)) { return Results.Problem( title: "Forbidden", detail: $"Missing required scope: {requiredScope}", statusCode: 403); } return null; // Access granted } } ``` ## Web UI Integration ### Route Guards ```typescript // route-guards.ts export const TenantGuard: CanActivateFn = (route, state) => { const auth = inject(AuthService); const requiredRoles = route.data['roles'] as string[]; if (!auth.hasAnyRole(requiredRoles)) { return inject(Router).createUrlTree(['/unauthorized']); } return true; }; // Usage in routes { path: 'policy/studio', component: PolicyStudioComponent, canActivate: [TenantGuard], data: { roles: ['policy:admin', 'tenant:admin'] } } ``` ### Scope-Based UI Elements ```typescript // rbac.directive.ts @Directive({ selector: '[requireScope]' }) export class RequireScopeDirective { @Input() set requireScope(scope: string) { this.updateVisibility(scope); } private updateVisibility(scope: string): void { const hasScope = this.auth.hasScope(scope); this.viewContainer.clear(); if (hasScope) { this.viewContainer.createEmbeddedView(this.templateRef); } } } // Usage in templates ``` ## Audit Trail ### Audited Operations All write operations are logged with: ```json { "timestamp": "2025-12-07T10:30:00Z", "actor": { "userId": "user-uuid", "tenantId": "tenant-uuid", "roles": ["policy:admin"], "ipAddress": "192.168.1.100" }, "action": "policy.activate", "resource": { "type": "policy_pack", "id": "pack-123", "version": 5 }, "outcome": "success", "details": { "previousStatus": "approved", "newStatus": "active" } } ``` ### Sensitive Operations These operations require enhanced audit logging: - `sealed_mode.seal` / `sealed_mode.unseal` - `policy.activate` - `export.create` (with PII) - `user.role.assign` - `tenant.settings.modify` ## Configuration ### Gateway RBAC Configuration ```yaml # gateway/rbac.yaml rbac: enabled: true strictTenantIsolation: true allowCrossTenantForOrgAdmin: true defaultRole: tenant:viewer defaultScopes: - policy:read - scanner:read roleBindings: tenant:admin: scopes: - policy:read - policy:edit - policy:activate - scanner:read - scanner:execute - airgap:status:read - export:read - export:create - admin:users - admin:settings policy:admin: scopes: - policy:read - policy:edit - policy:activate ``` ## Error Responses ### 401 Unauthorized ```json { "type": "https://stellaops.org/problems/unauthorized", "title": "Unauthorized", "status": 401, "detail": "Authentication required." } ``` ### 403 Forbidden ```json { "type": "https://stellaops.org/problems/forbidden", "title": "Forbidden", "status": 403, "detail": "You do not have permission to access this resource.", "requiredScope": "policy:activate", "currentScopes": ["policy:read"] } ``` ### 404 Not Found (Tenant Isolation) ```json { "type": "https://stellaops.org/problems/not-found", "title": "Not Found", "status": 404, "detail": "Resource not found." } ``` Note: 404 is returned instead of 403 for resources in other tenants to prevent enumeration attacks. ## Changelog | Date | Version | Change | |------|---------|--------| | 2025-12-07 | 1.0.0 | Initial contract definition | ## References - [Auth Scopes Documentation](../security/auth-scopes.md) - [RBAC Documentation](../security/scopes-and-roles.md) - [Tenancy Overview](../security/tenancy-overview.md) - [Rate Limit Design](./rate-limit-design.md)