From 206e367dbff756a2eea7678c090c2b2f6c32bf57 Mon Sep 17 00:00:00 2001 From: chengcheng Date: Tue, 28 Jul 2026 18:02:59 +0800 Subject: [PATCH] =?UTF-8?q?fix(identity):=20=E5=85=BC=E5=AE=B9=E5=A4=9A?= =?UTF-8?q?=E7=A7=9F=E6=88=B7=20V2=20=E6=97=A0=E5=9B=BA=E5=AE=9A=E7=A7=9F?= =?UTF-8?q?=E6=88=B7=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 多租户 Revision 改为按 Token tid 动态解析租户,配对和验证不再要求 default 映射;单租户 V1 继续保留固定映射门禁。同步补齐 tenantMode 共享契约与 OpenAPI 产物。\n\n验证:Web 测试 129 项通过;pnpm lint 通过;Web 生产构建通过;pnpm openapi 通过。 --- apps/api/docs/swagger.json | 15 +++++++++++ apps/api/docs/swagger.yaml | 10 +++++++ .../pages/admin/UnifiedIdentityPanel.test.tsx | 18 ++++++++++++- .../src/pages/admin/UnifiedIdentityPanel.tsx | 27 ++++++++++++++----- packages/contracts/src/index.ts | 1 + 5 files changed, 63 insertions(+), 8 deletions(-) diff --git a/apps/api/docs/swagger.json b/apps/api/docs/swagger.json index e4f5bc9..c50cfe6 100644 --- a/apps/api/docs/swagger.json +++ b/apps/api/docs/swagger.json @@ -9753,6 +9753,9 @@ "tenantKey": { "type": "string" }, + "tenantName": { + "type": "string" + }, "userGroupId": { "type": "string" }, @@ -12376,9 +12379,21 @@ "supersededAt": { "type": "string" }, + "tenantContextAudience": { + "type": "string" + }, + "tenantContextEndpoint": { + "type": "string" + }, + "tenantContextScope": { + "type": "string" + }, "tenantId": { "type": "string" }, + "tenantMode": { + "type": "string" + }, "tokenIntrospection": { "type": "boolean" }, diff --git a/apps/api/docs/swagger.yaml b/apps/api/docs/swagger.yaml index 514e8cb..33e5f5a 100644 --- a/apps/api/docs/swagger.yaml +++ b/apps/api/docs/swagger.yaml @@ -32,6 +32,8 @@ definitions: type: string tenantKey: type: string + tenantName: + type: string userGroupId: type: string userGroupKey: @@ -1858,8 +1860,16 @@ definitions: $ref: '#/definitions/identity.RevisionState' supersededAt: type: string + tenantContextAudience: + type: string + tenantContextEndpoint: + type: string + tenantContextScope: + type: string tenantId: type: string + tenantMode: + type: string tokenIntrospection: type: boolean updatedAt: diff --git a/apps/web/src/pages/admin/UnifiedIdentityPanel.test.tsx b/apps/web/src/pages/admin/UnifiedIdentityPanel.test.tsx index 605a8d2..53689a9 100644 --- a/apps/web/src/pages/admin/UnifiedIdentityPanel.test.tsx +++ b/apps/web/src/pages/admin/UnifiedIdentityPanel.test.tsx @@ -9,6 +9,7 @@ import { executeIdentityOperation, isStaleIdentityOperation, pairingFailureMessage, + requiresLocalTenantMapping, } from './UnifiedIdentityPanel'; describe('UnifiedIdentityPanel', () => { @@ -20,12 +21,26 @@ describe('UnifiedIdentityPanel', () => { expect(html).toContain('Gateway API 公网地址'); expect(html).not.toContain('value="/gateway-api"'); expect(html).toContain('Gateway Web 地址'); - expect(html).toContain('Gateway 本地租户映射'); + expect(html).toContain('Gateway 本地租户映射(仅单租户 V1)'); + expect(html).toContain('多租户 V2 无需填写'); + const localTenantInput = html.match(/]+placeholder="default"[^>]*>/)?.[0] ?? ''; + expect(localTenantInput).not.toContain('required'); + expect(localTenantInput).not.toContain('value="default"'); expect(html).not.toContain('Machine Client Secret'); expect(html).not.toContain('SSF Transmitter Issuer'); }); }); +describe('local tenant mapping compatibility', () => { + it('does not require a fixed mapping for multi-tenant V2 revisions', () => { + expect(requiresLocalTenantMapping('multi_tenant')).toBe(false); + }); + + it('keeps the fixed mapping requirement for single-tenant V1 revisions', () => { + expect(requiresLocalTenantMapping('single_tenant')).toBe(true); + }); +}); + describe('PairingProgressCard', () => { it('shows a safe actionable discovery failure and a recovery action', () => { const html = renderToStaticMarkup( undefined} pairing={{ @@ -192,6 +207,7 @@ function identityRevision( id: `${state}-revision`, state, schemaVersion: 1, + tenantMode: 'single_tenant', authCenterUrl: 'https://auth.example.com', issuer: 'https://issuer.example.com', scopes: ['openid'], diff --git a/apps/web/src/pages/admin/UnifiedIdentityPanel.tsx b/apps/web/src/pages/admin/UnifiedIdentityPanel.tsx index 215b316..fff69b6 100644 --- a/apps/web/src/pages/admin/UnifiedIdentityPanel.tsx +++ b/apps/web/src/pages/admin/UnifiedIdentityPanel.tsx @@ -24,7 +24,7 @@ export function UnifiedIdentityPanel(props: { token: string }) { const [configuration, setConfiguration] = useState(null); const [form, setForm] = useState(defaultPairingInput); const [showPairingForm, setShowPairingForm] = useState(false); - const [localTenantKey, setLocalTenantKey] = useState('default'); + const [localTenantKey, setLocalTenantKey] = useState(''); const [legacyJwtEnabled, setLegacyJwtEnabled] = useState(false); const [loading, setLoading] = useState(false); const [message, setMessage] = useState(''); @@ -120,6 +120,7 @@ export function UnifiedIdentityPanel(props: { token: string }) { const previous = configuration?.previous; const pairing = configuration?.pairing; const runtime = configuration?.runtime; + const localTenantMappingRequired = requiresLocalTenantMapping(draft?.tenantMode); const cleanupPending = pairing?.status === 'cancelled' && pairing.cleanupStatus === 'pending'; const cleanupCompleted = pairing?.status === 'cancelled' && pairing.cleanupStatus === 'completed'; const shouldShowPairing = !active && (showPairingForm || !pairing || cleanupCompleted); @@ -188,7 +189,9 @@ export function UnifiedIdentityPanel(props: { token: string }) { {draft.state === 'draft' && (
- + {localTenantMappingRequired + ? + :

多租户 V2 按 Token `tid` 动态解析租户,不使用固定的本地租户映射。

}