从已验证的 OIDC Claim 提取用户名、显示名称、邮箱、手机号和头像,并覆盖单租户、多租户及平台用户的 JIT 创建与重复登录同步。\n\n保留 metadata.manualProfile 标记下的人工资料,限制字段长度且仅接收已验证联系方式与 HTTPS 头像。已通过 auth、httpapi、store 测试及临时 PostgreSQL 集成验证。
86 lines
2.7 KiB
Go
86 lines
2.7 KiB
Go
package store
|
|
|
|
import "testing"
|
|
|
|
func TestAuthUserFromOIDCProjectionIncludesProfile(t *testing.T) {
|
|
projected := authUserFromOIDCProjection(GatewayUser{
|
|
ID: "gateway-user", ExternalUserID: "external-user", Username: "alice",
|
|
DisplayName: "王小明", Email: "alice@example.test", Phone: "+8613800000000",
|
|
AvatarURL: "https://static.example.test/avatar.png",
|
|
}, "default")
|
|
if projected.DisplayName != "王小明" || projected.Email != "alice@example.test" ||
|
|
projected.Phone != "+8613800000000" || projected.AvatarURL != "https://static.example.test/avatar.png" {
|
|
t.Fatalf("OIDC profile was not projected: %#v", projected)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeOIDCUserInputBoundsProfileClaims(t *testing.T) {
|
|
input := normalizeOIDCUserInput(ResolveOrProvisionOIDCUserInput{
|
|
DisplayName: " 王小明 ",
|
|
Email: " alice@example.test ",
|
|
Phone: " +8613800000000 ",
|
|
AvatarURL: " https://static.example.test/avatar.png ",
|
|
})
|
|
if input.DisplayName != "王小明" || input.Email != "alice@example.test" ||
|
|
input.Phone != "+8613800000000" || input.AvatarURL != "https://static.example.test/avatar.png" {
|
|
t.Fatalf("OIDC profile was not normalized: %#v", input)
|
|
}
|
|
}
|
|
|
|
func TestOIDCProjectionKindRequiresExplicitCompatibleContext(t *testing.T) {
|
|
applicationID := "11111111-1111-4111-8111-111111111111"
|
|
tests := []struct {
|
|
name string
|
|
input ResolveOrProvisionOIDCUserInput
|
|
want string
|
|
}{
|
|
{
|
|
name: "platform in multi-tenant application",
|
|
input: ResolveOrProvisionOIDCUserInput{
|
|
ContextType: "platform", TenantMode: "multi_tenant",
|
|
ApplicationID: applicationID,
|
|
},
|
|
want: "platform",
|
|
},
|
|
{
|
|
name: "tenant in multi-tenant application",
|
|
input: ResolveOrProvisionOIDCUserInput{
|
|
ContextType: "tenant", TenantMode: "multi_tenant",
|
|
ApplicationID: applicationID,
|
|
TenantID: "22222222-2222-4222-8222-222222222222",
|
|
},
|
|
want: "multi_tenant",
|
|
},
|
|
{
|
|
name: "tenant in single-tenant application",
|
|
input: ResolveOrProvisionOIDCUserInput{
|
|
ContextType: "tenant", TenantMode: "single_tenant",
|
|
TenantID: "tenant-contract-id",
|
|
},
|
|
want: "single_tenant",
|
|
},
|
|
{
|
|
name: "missing context",
|
|
input: ResolveOrProvisionOIDCUserInput{
|
|
TenantMode: "multi_tenant", ApplicationID: applicationID,
|
|
TenantID: "22222222-2222-4222-8222-222222222222",
|
|
},
|
|
},
|
|
{
|
|
name: "platform context with tenant",
|
|
input: ResolveOrProvisionOIDCUserInput{
|
|
ContextType: "platform", TenantMode: "multi_tenant",
|
|
ApplicationID: applicationID,
|
|
TenantID: "22222222-2222-4222-8222-222222222222",
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if got := oidcProjectionKind(test.input); got != test.want {
|
|
t.Fatalf("projection kind=%q, want %q", got, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|