feat: Add native binary analyzer test utilities and implement SM2 signing tests
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
- Introduced `NativeTestBase` class for ELF, PE, and Mach-O binary parsing helpers and assertions. - Created `TestCryptoFactory` for SM2 cryptographic provider setup and key generation. - Implemented `Sm2SigningTests` to validate signing functionality with environment gate checks. - Developed console export service and store with comprehensive unit tests for export status management.
This commit is contained in:
467
docs/contracts/web-gateway-tenant-rbac.md
Normal file
467
docs/contracts/web-gateway-tenant-rbac.md
Normal file
@@ -0,0 +1,467 @@
|
||||
# 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
|
||||
<button *requireScope="'policy:activate'">Activate Policy</button>
|
||||
```
|
||||
|
||||
## 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)
|
||||
Reference in New Issue
Block a user