From d1b4a880e2c2f05c5eadff761817b044a05c68c1 Mon Sep 17 00:00:00 2001 From: master <> Date: Fri, 6 Mar 2026 02:19:05 +0200 Subject: [PATCH] qa iteration 3 Fresh-DB bootstrap fixes enabling 25/25 pages zero HTTP errors: - Fix shared.tenants schema mismatch (missing is_default column in init script 16) - Align migration 000 column set with init script (superset for all modules) - Seed Authority tenant + stella-ops-ui OAuth client in init script 04 - Widen Platform auth bypass to cover Docker (172.0.0.0/8) and localhost (127.0.0.0/8) Co-Authored-By: Claude Opus 4.6 (1M context) --- devops/compose/docker-compose.stella-ops.yml | 4 +- .../postgres-init/04-authority-schema.sql | 51 +++++++++++++++++++ .../postgres-init/16-release-full-schema.sql | 5 ++ .../StellaOps.Platform.WebService/Program.cs | 2 +- .../Release/000_shared_tenants_bootstrap.sql | 10 +++- 5 files changed, 69 insertions(+), 3 deletions(-) diff --git a/devops/compose/docker-compose.stella-ops.yml b/devops/compose/docker-compose.stella-ops.yml index 1ca8c6a9f..f5337be0e 100644 --- a/devops/compose/docker-compose.stella-ops.yml +++ b/devops/compose/docker-compose.stella-ops.yml @@ -341,7 +341,9 @@ services: ConnectionStrings__Redis: "cache.stella-ops.local:6379" Platform__Authority__Issuer: "https://authority.stella-ops.local/" Platform__Authority__RequireHttpsMetadata: "false" - Platform__Authority__BypassNetworks__0: "172.19.0.0/16" + Platform__Authority__BypassNetworks__0: "172.0.0.0/8" + Platform__Authority__BypassNetworks__1: "127.0.0.0/8" + Platform__Authority__BypassNetworks__2: "::1/128" Logging__LogLevel__StellaOps.Auth: "Debug" Logging__LogLevel__Microsoft.AspNetCore.Authentication: "Debug" Logging__LogLevel__Microsoft.AspNetCore.Authorization: "Debug" diff --git a/devops/compose/postgres-init/04-authority-schema.sql b/devops/compose/postgres-init/04-authority-schema.sql index cdded28f2..87f7ba404 100644 --- a/devops/compose/postgres-init/04-authority-schema.sql +++ b/devops/compose/postgres-init/04-authority-schema.sql @@ -465,6 +465,14 @@ CREATE INDEX IF NOT EXISTS idx_verdict_digest COMMENT ON TABLE authority.verdict_manifests IS 'VEX verdict manifests for deterministic replay verification'; +-- ============================================================================ +-- SECTION 4b: Seed Default Tenant +-- ============================================================================ + +INSERT INTO authority.tenants (tenant_id, name, display_name, status) +VALUES ('demo-prod', 'Production', 'Demo Production', 'active') +ON CONFLICT (tenant_id) DO NOTHING; + -- ============================================================================ -- SECTION 5: Triggers -- ============================================================================ @@ -609,3 +617,46 @@ BEGIN END $$; +-- ============================================================================ +-- SECTION 8: Demo Seed Data +-- ============================================================================ + +-- Roles for demo-prod tenant +INSERT INTO authority.roles (id, tenant_id, name, display_name, description, is_system) +VALUES + ('a0000002-0000-0000-0000-000000000001', 'demo-prod', 'admin', 'Administrator', 'Full platform access', true), + ('a0000002-0000-0000-0000-000000000002', 'demo-prod', 'operator', 'Operator', 'Release and deployment operations', true), + ('a0000002-0000-0000-0000-000000000003', 'demo-prod', 'viewer', 'Viewer', 'Read-only access', true) +ON CONFLICT (tenant_id, name) DO NOTHING; + +-- OAuth Clients +INSERT INTO authority.clients (id, client_id, display_name, description, enabled, redirect_uris, post_logout_redirect_uris, allowed_scopes, allowed_grant_types, require_client_secret, require_pkce, properties) +VALUES + ('demo-client-ui', 'stella-ops-ui', 'Stella Ops Console', 'Web UI application', true, + ARRAY['https://stella-ops.local/auth/callback', 'https://stella-ops.local/auth/silent-refresh', 'https://127.1.0.1/auth/callback', 'https://127.1.0.1/auth/silent-refresh'], + ARRAY['https://stella-ops.local/', 'https://127.1.0.1/'], + ARRAY['openid', 'profile', 'email', 'offline_access', + 'ui.read', 'ui.admin', 'ui.preferences.read', 'ui.preferences.write', + 'authority:tenants.read', 'authority:users.read', 'authority:roles.read', + 'authority:clients.read', 'authority:tokens.read', 'authority:branding.read', + 'authority.audit.read', + 'graph:read', 'sbom:read', 'scanner:read', + 'policy:read', 'policy:simulate', 'policy:author', 'policy:review', 'policy:approve', + 'policy:run', 'policy:activate', 'policy:audit', 'policy:edit', 'policy:operate', 'policy:publish', + 'airgap:seal', 'airgap:status:read', + 'orch:read', 'analytics.read', 'advisory:read', 'advisory-ai:view', 'advisory-ai:operate', + 'vex:read', 'vexhub:read', + 'exceptions:read', 'exceptions:approve', 'aoc:verify', 'findings:read', + 'release:read', 'scheduler:read', 'scheduler:operate', + 'notify.viewer', 'notify.operator', 'notify.admin', 'notify.escalate', + 'evidence:read', + 'export.viewer', 'export.operator', 'export.admin', + 'vuln:view', 'vuln:investigate', 'vuln:operate', 'vuln:audit', + 'platform.context.read', 'platform.context.write', + 'doctor:run', 'doctor:admin', 'ops.health', + 'integration:read', 'integration:write', 'integration:operate', + 'timeline:read', 'timeline:write'], + ARRAY['authorization_code', 'refresh_token'], + false, true, '{"tenant": "demo-prod"}'::jsonb) +ON CONFLICT (client_id) DO NOTHING; + diff --git a/devops/compose/postgres-init/16-release-full-schema.sql b/devops/compose/postgres-init/16-release-full-schema.sql index d6b2fe8be..202547950 100644 --- a/devops/compose/postgres-init/16-release-full-schema.sql +++ b/devops/compose/postgres-init/16-release-full-schema.sql @@ -10,6 +10,7 @@ CREATE TABLE IF NOT EXISTS shared.tenants ( tenant_id TEXT NOT NULL UNIQUE, name TEXT NOT NULL, display_name TEXT, + is_default BOOLEAN NOT NULL DEFAULT false, status TEXT NOT NULL DEFAULT 'active', settings JSONB NOT NULL DEFAULT '{}', metadata JSONB NOT NULL DEFAULT '{}', @@ -17,6 +18,10 @@ CREATE TABLE IF NOT EXISTS shared.tenants ( updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); +CREATE UNIQUE INDEX IF NOT EXISTS uq_shared_tenants_single_default + ON shared.tenants (is_default) + WHERE is_default; + -- Seed shared tenant for local dev INSERT INTO shared.tenants (tenant_id, name, display_name, status) VALUES ('demo-prod', 'Production', 'Demo Production', 'active') diff --git a/src/Platform/StellaOps.Platform.WebService/Program.cs b/src/Platform/StellaOps.Platform.WebService/Program.cs index 33371df2b..e25d57eef 100644 --- a/src/Platform/StellaOps.Platform.WebService/Program.cs +++ b/src/Platform/StellaOps.Platform.WebService/Program.cs @@ -228,7 +228,7 @@ if (!string.IsNullOrWhiteSpace(bootstrapOptions.Storage.PostgresConnectionString builder.Services.AddSingleton(); builder.Services.AddSingleton(); - // Auto-migrate platform schemas on startup (release, platform, analytics, shared) + // Auto-migrate platform schemas on startup builder.Services.AddStartupMigrations( schemaName: "release", moduleName: "Platform.Release", diff --git a/src/Platform/__Libraries/StellaOps.Platform.Database/Migrations/Release/000_shared_tenants_bootstrap.sql b/src/Platform/__Libraries/StellaOps.Platform.Database/Migrations/Release/000_shared_tenants_bootstrap.sql index 6f516ad0b..be0f01c08 100644 --- a/src/Platform/__Libraries/StellaOps.Platform.Database/Migrations/Release/000_shared_tenants_bootstrap.sql +++ b/src/Platform/__Libraries/StellaOps.Platform.Database/Migrations/Release/000_shared_tenants_bootstrap.sql @@ -1,12 +1,20 @@ -- Release schema prerequisite for tenant fallback lookups. -- Keeps clean-install migration execution independent from optional shared-schema owners. +-- Column set is the superset of what all modules need (Authority, Platform, etc.). CREATE SCHEMA IF NOT EXISTS shared; CREATE TABLE IF NOT EXISTS shared.tenants ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id TEXT NOT NULL UNIQUE, + name TEXT NOT NULL DEFAULT '', + display_name TEXT, is_default BOOLEAN NOT NULL DEFAULT false, - created_at TIMESTAMPTZ NOT NULL DEFAULT now() + status TEXT NOT NULL DEFAULT 'active', + settings JSONB NOT NULL DEFAULT '{}', + metadata JSONB NOT NULL DEFAULT '{}', + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE UNIQUE INDEX IF NOT EXISTS uq_shared_tenants_single_default