feat(identity): 接入认证中心多租户登录

支持 Manifest V2 动态 tid 验证、Tenant Context 同步和租户内 JIT 投影,并保留 Manifest V1 与旧 Session 兼容。\n\n增加 tenantHint、租户切换、普通注册关闭及 application/principal/tenant 两级 SSF 撤销;迁移、定向安全测试和本地双租户跨仓 E2E 已通过。\n\nrelease_required=true;未执行 Release、Staging 或真实链路。
This commit is contained in:
2026-07-28 17:28:35 +08:00
parent 0b02e62c72
commit 5c679ff13f
45 changed files with 2986 additions and 139 deletions
@@ -0,0 +1,92 @@
ALTER TABLE gateway_identity_configuration_revisions
DROP CONSTRAINT IF EXISTS gateway_identity_configuration_revisions_schema_version_check;
ALTER TABLE gateway_identity_configuration_revisions
ADD COLUMN IF NOT EXISTS tenant_mode text NOT NULL DEFAULT 'single_tenant',
ADD COLUMN IF NOT EXISTS tenant_context_endpoint text,
ADD COLUMN IF NOT EXISTS tenant_context_audience text,
ADD COLUMN IF NOT EXISTS tenant_context_scope text,
ADD CONSTRAINT gateway_identity_configuration_revisions_schema_version_check
CHECK (schema_version IN (1,2)),
ADD CONSTRAINT gateway_identity_configuration_revisions_tenant_mode_check
CHECK (tenant_mode IN ('single_tenant','multi_tenant')),
ADD CONSTRAINT gateway_identity_configuration_revisions_manifest_mode_check
CHECK (
(state='draft' AND issuer IS NULL)
OR
(schema_version=1 AND tenant_mode='single_tenant' AND tenant_id IS NOT NULL AND local_tenant_key <> ''
AND tenant_context_endpoint IS NULL AND tenant_context_audience IS NULL AND tenant_context_scope IS NULL)
OR
(schema_version=2 AND tenant_mode='multi_tenant' AND tenant_id IS NULL AND local_tenant_key=''
AND tenant_context_endpoint IS NOT NULL AND tenant_context_audience='urn:easyai:auth-center:tenant-context'
AND tenant_context_scope='tenant.context.read')
) NOT VALID;
CREATE TABLE IF NOT EXISTS gateway_oidc_tenant_bindings (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
issuer text NOT NULL,
application_id text NOT NULL,
external_tenant_id text NOT NULL,
gateway_tenant_id uuid NOT NULL REFERENCES gateway_tenants(id) ON DELETE CASCADE,
access_status text NOT NULL DEFAULT 'active'
CHECK (access_status IN ('active','disabled')),
metadata_status text NOT NULL DEFAULT 'metadata_pending'
CHECK (metadata_status IN ('metadata_pending','synced','rejected')),
display_name text,
slug text,
metadata_version text,
metadata_etag text,
metadata_updated_at timestamptz,
last_sync_at timestamptz,
next_sync_at timestamptz NOT NULL DEFAULT now(),
sync_failure_count integer NOT NULL DEFAULT 0 CHECK (sync_failure_count >= 0),
last_error_category text,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE(issuer,application_id,external_tenant_id),
UNIQUE(id,gateway_tenant_id)
);
CREATE INDEX IF NOT EXISTS idx_gateway_oidc_tenant_bindings_sync
ON gateway_oidc_tenant_bindings(next_sync_at)
WHERE access_status='active';
CREATE TABLE IF NOT EXISTS gateway_oidc_user_bindings (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_binding_id uuid NOT NULL REFERENCES gateway_oidc_tenant_bindings(id) ON DELETE CASCADE,
subject text NOT NULL,
gateway_user_id uuid NOT NULL REFERENCES gateway_users(id) ON DELETE CASCADE,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE(tenant_binding_id,subject),
UNIQUE(id,gateway_user_id)
);
ALTER TABLE gateway_oidc_sessions
ADD COLUMN IF NOT EXISTS oidc_user_binding_id uuid
REFERENCES gateway_oidc_user_bindings(id) ON DELETE CASCADE,
ADD COLUMN IF NOT EXISTS oidc_client_id text;
CREATE INDEX IF NOT EXISTS idx_gateway_oidc_sessions_user_binding
ON gateway_oidc_sessions(oidc_user_binding_id,created_at DESC)
WHERE oidc_user_binding_id IS NOT NULL;
ALTER TABLE gateway_security_event_receipts
ADD COLUMN IF NOT EXISTS application_id text,
ADD COLUMN IF NOT EXISTS subject_type text
CHECK (subject_type IS NULL OR subject_type IN ('principal','tenant'));
ALTER TABLE gateway_oidc_revocation_watermarks
ADD COLUMN IF NOT EXISTS application_id text NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS subject_type text NOT NULL DEFAULT 'principal'
CHECK (subject_type IN ('principal','tenant'));
ALTER TABLE gateway_oidc_revocation_watermarks
DROP CONSTRAINT IF EXISTS gateway_oidc_revocation_watermarks_pkey;
ALTER TABLE gateway_oidc_revocation_watermarks
ADD PRIMARY KEY (issuer,application_id,tenant_id,subject_type,subject);
DROP INDEX IF EXISTS idx_gateway_oidc_revocation_watermarks_lookup;
CREATE INDEX idx_gateway_oidc_revocation_watermarks_lookup
ON gateway_oidc_revocation_watermarks(issuer,application_id,tenant_id,subject_type,subject,revoked_at);