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:
@@ -78,6 +78,47 @@ func TestManifestV1ValidationRequiresStableFieldsAndCapabilityDependencies(t *te
|
||||
}
|
||||
}
|
||||
|
||||
func TestManifestV2ValidationRequiresExplicitMultiTenantRuntimeContract(t *testing.T) {
|
||||
valid := ManifestV2{
|
||||
SchemaVersion: 2, TenantMode: "multi_tenant",
|
||||
Issuer: "https://auth.example.com/issuer/shared", ApplicationID: "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||
Capabilities: []string{"oidc_login", "api_access", "machine_to_machine", "token_introspection", "session_revocation"},
|
||||
Audience: "urn:easyai:resource:bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", Scopes: []string{"openid", "gateway.access"},
|
||||
Clients: ManifestClients{
|
||||
BrowserLogin: &ManifestClient{ClientID: "browser"},
|
||||
MachineToMachine: &ManifestClient{ClientID: "service"},
|
||||
},
|
||||
TenantContext: &ManifestTenantContext{
|
||||
Endpoint: "https://auth.example.com/api/v1/runtime/tenants/{tenantId}",
|
||||
Audience: "urn:easyai:auth-center:tenant-context",
|
||||
Scope: "tenant.context.read",
|
||||
},
|
||||
SecurityEvents: &ManifestSecurityEvents{
|
||||
TransmitterIssuer: "https://auth.example.com/ssf",
|
||||
ConfigurationEndpoint: "https://auth.example.com/.well-known/ssf-configuration/ssf",
|
||||
Audience: "urn:easyai:ssf:receiver:bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||
},
|
||||
}
|
||||
if err := valid.Validate("production"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, mutate := range []func(*ManifestV2){
|
||||
func(value *ManifestV2) { value.TenantMode = "" },
|
||||
func(value *ManifestV2) { value.TenantID = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" },
|
||||
func(value *ManifestV2) { value.TenantContext = nil },
|
||||
func(value *ManifestV2) { value.TenantContext.Scope = "" },
|
||||
func(value *ManifestV2) { value.TenantContext.Audience = "urn:wrong" },
|
||||
} {
|
||||
candidate := valid
|
||||
contextCopy := *valid.TenantContext
|
||||
candidate.TenantContext = &contextCopy
|
||||
mutate(&candidate)
|
||||
if err := candidate.Validate("production"); err == nil {
|
||||
t.Fatalf("invalid Manifest V2 was accepted: %#v", candidate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnboardingClientRejectsRedirects(t *testing.T) {
|
||||
target := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "should not be reached", http.StatusTeapot)
|
||||
|
||||
Reference in New Issue
Block a user