Add topology auth policies + journey findings notes

Concelier:
- Register Topology.Read, Topology.Manage, Topology.Admin authorization
  policies mapped to OrchRead/OrchOperate/PlatformContextRead/IntegrationWrite
  scopes. Previously these policies were referenced by endpoints but never
  registered, causing System.InvalidOperationException on every topology
  API call.

Gateway routes:
- Simplified targets/environments routes (removed specific sub-path routes,
  use catch-all patterns instead)
- Changed environments base route to JobEngine (where CRUD lives)
- Changed to ReverseProxy type for all topology routes

KNOWN ISSUE (not yet fixed):
- ReverseProxy routes don't forward the gateway's identity envelope to
  Concelier. The regions/targets/bindings endpoints return 401 because
  hasPrincipal=False — the gateway authenticates the user but doesn't
  pass the identity to the backend via ReverseProxy. Microservice routes
  use Valkey transport which includes envelope headers. Topology endpoints
  need either: (a) Valkey transport registration in Concelier, or
  (b) Concelier configured to accept raw bearer tokens on ReverseProxy paths.
  This is an architecture-level fix.

Journey findings collected so far:
- Integration wizard (Harbor + GitHub App): works end-to-end
- Advisory Check All: fixed (parallel individual checks)
- Mirror domain creation: works, generate-immediately fails silently
- Topology wizard Step 1 (Region): blocked by auth passthrough issue
- Topology wizard Step 2 (Environment): POST to JobEngine needs verify
- User ID resolution: raw hashes shown everywhere

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-03-16 08:12:39 +02:00
parent 602df77467
commit da76d6e93e
223 changed files with 24763 additions and 489 deletions

View File

@@ -870,71 +870,26 @@ async function setupHarness(page: Page): Promise<void> {
});
},
);
await page.route('**/api/v1/trust/dashboard**', (route) =>
route.fulfill({
await page.route('**/api/v1/administration/trust-signing', (route) => {
const pathname = new URL(route.request().url()).pathname;
if (!pathname.endsWith('/api/v1/administration/trust-signing')) {
return route.fallback();
}
return route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
keys: {
total: 3,
active: 1,
expiringSoon: 1,
expired: 1,
revoked: 0,
pendingRotation: 0,
},
issuers: {
total: 3,
fullTrust: 2,
partialTrust: 1,
minimalTrust: 0,
untrusted: 0,
blocked: 0,
averageTrustScore: 87.3,
},
certificates: {
total: 3,
valid: 2,
expiringSoon: 1,
expired: 0,
revoked: 0,
invalidChains: 0,
},
recentEvents: [],
expiryAlerts: [
{
keyId: 'key-002',
keyName: 'SBOM Signing Key',
expiresAt: '2026-04-15T00:00:00.000Z',
daysUntilExpiry: 39,
severity: 'warning',
purpose: 'sbom_signing',
suggestedAction: 'Schedule key rotation before expiry.',
},
],
inventory: { keys: 3, issuers: 3, certificates: 3 },
signals: [],
legacyAliases: [],
evidenceConsumerPath: '/evidence-audit/proofs',
}),
}),
);
await page.route('**/api/v1/trust/keys/expiry-alerts**', (route) =>
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{
keyId: 'key-002',
keyName: 'SBOM Signing Key',
purpose: 'sbom_signing',
expiresAt: '2026-04-15T00:00:00.000Z',
daysUntilExpiry: 39,
severity: 'warning',
suggestedAction: 'Schedule key rotation before expiry.',
},
]),
}),
);
await page.route('**/api/v1/trust/keys**', (route) => {
});
});
await page.route('**/api/v1/administration/trust-signing/keys**', (route) => {
const pathname = new URL(route.request().url()).pathname;
if (!pathname.endsWith('/api/v1/trust/keys')) {
if (!pathname.endsWith('/trust-signing/keys')) {
return route.fallback();
}
@@ -945,44 +900,140 @@ async function setupHarness(page: Page): Promise<void> {
items: [
{
keyId: 'key-001',
tenantId: adminSession.tenant,
name: 'Production Key',
description: 'Main signing key',
keyType: 'asymmetric',
alias: 'Production Key',
algorithm: 'RS256',
keySize: 2048,
purpose: 'attestation',
status: 'active',
publicKeyFingerprint: 'sha256:prod-key',
currentVersion: 2,
createdAt: '2026-01-01T00:00:00.000Z',
expiresAt: '2027-01-01T00:00:00.000Z',
lastUsedAt: '2026-03-06T10:00:00.000Z',
usageCount: 100,
updatedAt: '2026-03-06T10:00:00.000Z',
updatedBy: 'operator',
},
{
keyId: 'key-002',
tenantId: adminSession.tenant,
name: 'SBOM Signing Key',
description: 'Secondary signing key',
keyType: 'asymmetric',
alias: 'SBOM Signing Key',
algorithm: 'RS256',
keySize: 2048,
purpose: 'sbom_signing',
status: 'expiring_soon',
publicKeyFingerprint: 'sha256:sbom-key',
currentVersion: 1,
createdAt: '2026-01-15T00:00:00.000Z',
expiresAt: '2026-04-15T00:00:00.000Z',
lastUsedAt: '2026-03-05T09:00:00.000Z',
usageCount: 50,
updatedAt: '2026-03-05T09:00:00.000Z',
updatedBy: 'operator',
},
],
totalCount: 2,
pageNumber: 1,
pageSize: 20,
totalPages: 1,
count: 2,
limit: 200,
offset: 0,
}),
});
});
await page.route('**/api/v1/administration/trust-signing/issuers**', (route) => {
const pathname = new URL(route.request().url()).pathname;
if (!pathname.endsWith('/trust-signing/issuers')) {
return route.fallback();
}
return route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
items: [
{
issuerId: 'issuer-001',
name: 'Core Root CA',
issuerUri: 'https://issuer.example/root',
trustLevel: 'full',
status: 'active',
createdAt: '2026-01-01T00:00:00.000Z',
updatedAt: '2026-02-01T00:00:00.000Z',
updatedBy: 'operator',
},
{
issuerId: 'issuer-002',
name: 'Intermediate CA',
issuerUri: 'https://issuer.example/intermediate',
trustLevel: 'full',
status: 'active',
createdAt: '2026-01-01T00:00:00.000Z',
updatedAt: '2026-02-01T00:00:00.000Z',
updatedBy: 'operator',
},
{
issuerId: 'issuer-003',
name: 'Partner CA',
issuerUri: 'https://partner.example/ca',
trustLevel: 'partial',
status: 'active',
createdAt: '2026-01-15T00:00:00.000Z',
updatedAt: '2026-02-15T00:00:00.000Z',
updatedBy: 'operator',
},
],
count: 3,
limit: 200,
offset: 0,
}),
});
});
await page.route('**/api/v1/administration/trust-signing/certificates**', (route) => {
const pathname = new URL(route.request().url()).pathname;
if (!pathname.endsWith('/trust-signing/certificates')) {
return route.fallback();
}
return route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
items: [
{
certificateId: 'cert-001',
keyId: 'key-001',
issuerId: 'issuer-001',
serialNumber: 'SER-001',
status: 'active',
notBefore: '2026-01-01T00:00:00.000Z',
notAfter: '2027-01-01T00:00:00.000Z',
createdAt: '2026-01-01T00:00:00.000Z',
updatedAt: '2026-02-01T00:00:00.000Z',
updatedBy: 'operator',
},
{
certificateId: 'cert-002',
keyId: 'key-002',
issuerId: 'issuer-002',
serialNumber: 'SER-002',
status: 'active',
notBefore: '2026-01-15T00:00:00.000Z',
notAfter: '2026-04-15T00:00:00.000Z',
createdAt: '2026-01-15T00:00:00.000Z',
updatedAt: '2026-02-15T00:00:00.000Z',
updatedBy: 'operator',
},
{
certificateId: 'cert-003',
keyId: null,
issuerId: 'issuer-003',
serialNumber: 'SER-003',
status: 'active',
notBefore: '2026-02-01T00:00:00.000Z',
notAfter: '2027-02-01T00:00:00.000Z',
createdAt: '2026-02-01T00:00:00.000Z',
updatedAt: '2026-03-01T00:00:00.000Z',
updatedBy: 'operator',
},
],
count: 3,
limit: 200,
offset: 0,
}),
});
});
await page.route('**/api/v1/administration/trust-signing/transparency-log**', (route) =>
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ item: null }),
}),
);
await page.route('**/api/v1/integrations**', (route) => {
const request = route.request();
const pathname = new URL(request.url()).pathname;
@@ -1435,7 +1486,7 @@ async function setupHarness(page: Page): Promise<void> {
requestUrl.includes('/api/v2/security/sbom-explorer') ||
requestUrl.includes('/policy/api/') ||
requestUrl.includes('/api/v1/integrations') ||
requestUrl.includes('/api/v1/trust/') ||
requestUrl.includes('/api/v1/administration/trust-signing') ||
requestUrl.includes('/api/v1/audit/') ||
requestUrl.includes('/api/v1/authority/quotas') ||
requestUrl.includes('/api/v1/gateway/rate-limits') ||

View File

@@ -210,14 +210,79 @@ async function setupHarness(page: Page): Promise<void> {
);
await page.route('**/doctor/api/v1/doctor/trends**', (route) => fulfillJson(route, []));
await page.route('**/api/v1/approvals**', (route) => fulfillJson(route, []));
await page.route('**/api/v1/trust/dashboard**', (route) =>
fulfillJson(route, {
keys: { total: 12, active: 9, expiringSoon: 2, expired: 1, revoked: 0, pendingRotation: 1 },
issuers: { total: 8, fullTrust: 3, partialTrust: 3, minimalTrust: 1, untrusted: 1, blocked: 0, averageTrustScore: 86.4 },
certificates: { total: 5, valid: 4, expiringSoon: 1, expired: 0, revoked: 0, invalidChains: 0 },
recentEvents: [],
expiryAlerts: [],
})
await page.route('**/api/v1/administration/trust-signing', (route) => {
const pathname = new URL(route.request().url()).pathname;
if (!pathname.endsWith('/api/v1/administration/trust-signing')) {
return route.fallback();
}
return fulfillJson(route, {
inventory: { keys: 12, issuers: 8, certificates: 5 },
signals: [],
legacyAliases: [],
evidenceConsumerPath: '/evidence-audit/proofs',
});
});
await page.route('**/api/v1/administration/trust-signing/keys**', (route) => {
const pathname = new URL(route.request().url()).pathname;
if (!pathname.endsWith('/trust-signing/keys')) return route.fallback();
return fulfillJson(route, {
items: Array.from({ length: 12 }, (_, i) => ({
keyId: `key-${String(i + 1).padStart(3, '0')}`,
alias: `Key ${i + 1}`,
algorithm: 'Ed25519',
status: i < 9 ? 'active' : i < 11 ? 'expiring_soon' : 'expired',
currentVersion: 1,
createdAt: '2026-01-01T00:00:00Z',
updatedAt: '2026-02-01T00:00:00Z',
updatedBy: 'operator',
})),
count: 12,
limit: 200,
offset: 0,
});
});
await page.route('**/api/v1/administration/trust-signing/issuers**', (route) => {
const pathname = new URL(route.request().url()).pathname;
if (!pathname.endsWith('/trust-signing/issuers')) return route.fallback();
return fulfillJson(route, {
items: Array.from({ length: 8 }, (_, i) => ({
issuerId: `issuer-${String(i + 1).padStart(3, '0')}`,
name: `Issuer ${i + 1}`,
issuerUri: `https://issuer.example/${i + 1}`,
trustLevel: i < 3 ? 'full' : i < 6 ? 'partial' : i < 7 ? 'minimal' : 'untrusted',
status: 'active',
createdAt: '2026-01-01T00:00:00Z',
updatedAt: '2026-02-01T00:00:00Z',
updatedBy: 'operator',
})),
count: 8,
limit: 200,
offset: 0,
});
});
await page.route('**/api/v1/administration/trust-signing/certificates**', (route) => {
const pathname = new URL(route.request().url()).pathname;
if (!pathname.endsWith('/trust-signing/certificates')) return route.fallback();
return fulfillJson(route, {
items: Array.from({ length: 5 }, (_, i) => ({
certificateId: `cert-${String(i + 1).padStart(3, '0')}`,
keyId: `key-${String(i + 1).padStart(3, '0')}`,
issuerId: `issuer-${String(i + 1).padStart(3, '0')}`,
serialNumber: `SER-${String(i + 1).padStart(3, '0')}`,
status: 'active',
notBefore: '2026-01-01T00:00:00Z',
notAfter: '2027-01-01T00:00:00Z',
createdAt: '2026-01-01T00:00:00Z',
updatedAt: '2026-02-01T00:00:00Z',
updatedBy: 'operator',
})),
count: 5,
limit: 200,
offset: 0,
});
});
await page.route('**/api/v1/administration/trust-signing/transparency-log**', (route) =>
fulfillJson(route, { item: null })
);
await page.route(watchlistEntriesRoute, (route) =>
fulfillJson(route, { items: watchlistEntries, totalCount: watchlistEntries.length })