1
0
forked from baron/baron-sso

Merge pull request 'feature/1183-signup-personal-default' (#1187) from feature/1183-signup-personal-default into dev

Reviewed-on: baron/baron-sso#1187
This commit is contained in:
2026-06-16 18:39:35 +09:00
15 changed files with 201 additions and 175 deletions

View File

@@ -712,69 +712,21 @@ func (h *AuthHandler) Signup(c *fiber.Ctx) error {
}
// 소속이 비어 있는 일반 가입자는 PERSONAL tenant를 자동 생성해 대표소속을 보장합니다.
tenantSlug := strings.TrimSpace(req.TenantSlug)
// 모든 온라인 가입자는 기본적으로 개인(Personal) 테넌트 소속으로 가입합니다.
// 기업/가족사 소속 연동은 별도 문의를 통해 처리되므로 온라인 가입 흐름에서는 제외합니다.
req.AffiliationType = "GENERAL"
slog.Info("[Signup] Forcing AffiliationType to GENERAL (Default personal tenant signup policy)", "email", req.Email)
var tenantID *string
parts := strings.Split(req.Email, "@")
if len(parts) != 2 {
return errorJSON(c, fiber.StatusBadRequest, "Invalid email format")
}
domainName := parts[1]
// Check if this domain belongs to a predefined family affiliate
isInternal, _ := h.isAffiliateTenant(c.Context(), domainName)
// [Strict Policy] Force AffiliationType based on predefined family slugs (User cannot choose)
if isInternal {
req.AffiliationType = "AFFILIATE"
slog.Info("[Signup] Forcing AffiliationType to AFFILIATE", "email", req.Email)
} else {
req.AffiliationType = "GENERAL"
slog.Info("[Signup] Forcing AffiliationType to GENERAL", "email", req.Email)
}
if tenantSlug != "" {
// [Security] Cross-check: If domain is NOT internal, they cannot provide a tenantSlug
if !isInternal {
slog.Warn("[Signup] Security violation: non-internal email providing tenantSlug", "email", req.Email)
return errorJSON(c, fiber.StatusForbidden, "Only affiliate members can join an organization.")
}
if !affiliateSlugs[strings.ToLower(tenantSlug)] {
return errorJSON(c, fiber.StatusForbidden, "The selected organization is not a valid family affiliate.")
}
tenant, err := h.TenantService.GetTenantBySlug(c.Context(), tenantSlug)
if err == nil && tenant != nil {
if tenant.Status == domain.TenantStatusActive {
slog.Info("[Signup] Assigning tenant by manual slug", "email", req.Email, "tenant", tenant.Slug)
tenantSlug = tenant.Slug
tenantID = &tenant.ID
} else {
return errorJSON(c, fiber.StatusForbidden, "The specified organization is not active.")
}
} else {
slog.Warn("[Signup] Attempted to join non-existent organization", "slug", tenantSlug, "email", req.Email)
return errorJSON(c, fiber.StatusNotFound, "The specified organization code was not found.")
}
} else {
// If it's a family affiliate domain, they MUST select one of the family companies
if isInternal {
return errorJSON(c, fiber.StatusBadRequest, "Please select your organization.")
}
}
if tenantID == nil && req.AffiliationType == "AFFILIATE" {
return errorJSON(c, fiber.StatusBadRequest, "We couldn't verify your organization affiliation. Please check your choice.")
}
if tenantID == nil && req.AffiliationType == "GENERAL" {
tenant, err := createPersonalTenantForUser(c.Context(), h.TenantService, req.Email)
tenant, err := h.TenantService.GetTenantBySlug(c.Context(), "personal")
if err != nil || tenant == nil {
// Fallback: 만약 시드된 personal 테넌트가 없을 경우 개인별 테넌트를 자동 생성합니다.
tenant, err = createPersonalTenantForUser(c.Context(), h.TenantService, req.Email)
if err != nil {
return errorJSON(c, fiber.StatusServiceUnavailable, "failed to create personal tenant")
return errorJSON(c, fiber.StatusServiceUnavailable, "failed to resolve personal tenant")
}
tenantSlug = tenant.Slug
tenantID = &tenant.ID
}
tenantID = &tenant.ID
// Normalize Phone (E.164 형태로 보관)
normalizedPhone := domain.NormalizePhoneNumber(req.Phone)