1
0
forked from baron/baron-sso

네이버 계정 정합성 맞춤

This commit is contained in:
2026-06-15 19:54:09 +09:00
parent 8e9d015443
commit 4d468cd39f
97 changed files with 5837 additions and 2031 deletions

View File

@@ -29,6 +29,9 @@ func Run(db *gorm.DB) error {
if err := SanitizeLegacyUserMetadata(db); err != nil {
return fmt.Errorf("legacy user metadata sanitize failed: %w", err)
}
if err := CanonicalizeUserAppointmentTenants(db); err != nil {
return fmt.Errorf("user appointment tenant canonicalization failed: %w", err)
}
slog.Info("[Bootstrap] User seed skipped (Kratos is SoT)")
slog.Info("[Bootstrap] Bootstrap completed successfully.")
@@ -50,7 +53,6 @@ func migrateSchemas(db *gorm.DB) error {
&domain.TenantDomain{},
&domain.User{},
&domain.UserLoginID{},
&domain.UserProjectionState{},
&domain.UserGroup{},
&domain.ApiKey{},
&domain.IdentityProviderConfig{},

View File

@@ -15,6 +15,42 @@ where metadata ? 'hanmacFamily'
or metadata ? 'userType'
`
const canonicalizeUserAppointmentTenantsSQL = `
with normalized as (
select
u.id,
jsonb_agg(
case
when jsonb_typeof(item.value) = 'object' and t.id is not null then
item.value || jsonb_build_object(
'tenantId', t.id::text,
'tenantSlug', t.slug,
'tenantName', t.name
)
else item.value
end
order by item.ordinality
) as appointments
from users u
cross join lateral jsonb_array_elements(u.metadata -> 'additionalAppointments') with ordinality as item(value, ordinality)
left join tenants t on t.id::text = item.value ->> 'tenantId'
and t.deleted_at is null
where jsonb_typeof(u.metadata -> 'additionalAppointments') = 'array'
group by u.id
),
changed as (
select u.id, normalized.appointments
from users u
join normalized on normalized.id = u.id
where u.metadata -> 'additionalAppointments' is distinct from normalized.appointments
)
update users u
set metadata = jsonb_set(u.metadata, '{additionalAppointments}', changed.appointments, true),
updated_at = now()
from changed
where changed.id = u.id
`
// SanitizeLegacyUserMetadata removes legacy UI classification flags from Baron user metadata.
func SanitizeLegacyUserMetadata(db *gorm.DB) error {
if db == nil {
@@ -32,3 +68,21 @@ func SanitizeLegacyUserMetadata(db *gorm.DB) error {
slog.Info("[Bootstrap] Legacy user metadata sanitized", "rowsAffected", result.RowsAffected)
return nil
}
// CanonicalizeUserAppointmentTenants rewrites appointment display fields from the tenant UUID source.
func CanonicalizeUserAppointmentTenants(db *gorm.DB) error {
if db == nil {
return fmt.Errorf("database is not configured")
}
if !db.Migrator().HasTable("users") || !db.Migrator().HasTable("tenants") {
slog.Info("[Bootstrap] User appointment tenant canonicalization skipped because required tables do not exist")
return nil
}
result := db.Exec(canonicalizeUserAppointmentTenantsSQL)
if result.Error != nil {
return fmt.Errorf("canonicalize user appointment tenants: %w", result.Error)
}
slog.Info("[Bootstrap] User appointment tenant metadata canonicalized", "rowsAffected", result.RowsAffected)
return nil
}

View File

@@ -114,6 +114,89 @@ func TestCanonicalizeLegacyUserStatuses(t *testing.T) {
}
}
func TestCanonicalizeUserAppointmentTenantsUsesTenantUUID(t *testing.T) {
db := openBootstrapPostgresTestDB(t)
if err := db.AutoMigrate(&domain.Tenant{}, &domain.User{}); err != nil {
t.Fatalf("failed to migrate users and tenants tables: %v", err)
}
tenant := domain.Tenant{
ID: "30000000-0000-0000-0000-000000000101",
Type: domain.TenantTypeOrganization,
Name: "통합시스템",
Slug: "intigrated-system",
Status: domain.TenantStatusActive,
}
if err := db.Create(&tenant).Error; err != nil {
t.Fatalf("failed to create tenant: %v", err)
}
user := domain.User{
ID: "30000000-0000-0000-0000-000000000201",
Email: "appointment@example.com",
Name: "Appointment User",
Role: domain.RoleUser,
Status: domain.UserStatusActive,
Metadata: domain.JSONMap{
"additionalAppointments": []any{
map[string]any{
"tenantId": tenant.ID,
"tenantSlug": "tech-planning",
"tenantName": "기술기획",
"grade": "연구원",
},
map[string]any{
"tenantId": "30000000-0000-0000-0000-000000000999",
"tenantSlug": "unknown-old",
"tenantName": "Unknown Old",
},
},
},
}
if err := db.Create(&user).Error; err != nil {
t.Fatalf("failed to create user: %v", err)
}
if err := CanonicalizeUserAppointmentTenants(db); err != nil {
t.Fatalf("CanonicalizeUserAppointmentTenants returned error: %v", err)
}
if err := CanonicalizeUserAppointmentTenants(db); err != nil {
t.Fatalf("CanonicalizeUserAppointmentTenants must be idempotent: %v", err)
}
var got domain.User
if err := db.First(&got, "id = ?", user.ID).Error; err != nil {
t.Fatalf("failed to load canonicalized user: %v", err)
}
appointments, ok := got.Metadata["additionalAppointments"].([]any)
if !ok || len(appointments) != 2 {
t.Fatalf("additionalAppointments = %#v, want two appointments", got.Metadata["additionalAppointments"])
}
first, ok := appointments[0].(map[string]any)
if !ok {
t.Fatalf("first appointment = %#v, want object", appointments[0])
}
if first["tenantId"] != tenant.ID {
t.Fatalf("tenantId = %#v, want %s", first["tenantId"], tenant.ID)
}
if first["tenantSlug"] != tenant.Slug {
t.Fatalf("tenantSlug = %#v, want %s", first["tenantSlug"], tenant.Slug)
}
if first["tenantName"] != tenant.Name {
t.Fatalf("tenantName = %#v, want %s", first["tenantName"], tenant.Name)
}
if first["grade"] != "연구원" {
t.Fatalf("grade = %#v, want preserved value", first["grade"])
}
second, ok := appointments[1].(map[string]any)
if !ok {
t.Fatalf("second appointment = %#v, want object", appointments[1])
}
if second["tenantSlug"] != "unknown-old" || second["tenantName"] != "Unknown Old" {
t.Fatalf("unknown tenant appointment must be preserved: %#v", second)
}
}
func TestRunSanitizesLegacyUserMetadata(t *testing.T) {
db := openBootstrapPostgresTestDB(t)
if err := db.AutoMigrate(&domain.User{}); err != nil {