1
0
forked from baron/baron-sso

feat: implement dynamic tenant provisioning and remove hardcoded company mappings

This commit is contained in:
2026-04-06 16:13:03 +09:00
parent 003f12f008
commit c78604df06
9 changed files with 125 additions and 67 deletions

View File

@@ -521,6 +521,14 @@ func (h *AuthHandler) Signup(c *fiber.Ctx) error {
slog.Warn("[Signup] Attempted to join non-active tenant by domain", "email", req.Email, "tenant", tenant.Slug, "status", tenant.Status)
return errorJSON(c, fiber.StatusForbidden, "Your organization's tenant is currently not active.")
}
} else {
// [New Policy] Try dynamic provisioning via Group Policies if tenant doesn't exist
tenant, err := h.TenantService.ProvisionTenantByDomain(c.Context(), domainName)
if err == nil && tenant != nil {
slog.Info("[Signup] Auto-provisioned tenant via group policy", "email", req.Email, "tenant", tenant.Slug)
companyCode = tenant.Slug
tenantID = &tenant.ID
}
}
}
@@ -529,8 +537,6 @@ func (h *AuthHandler) Signup(c *fiber.Ctx) error {
tenant, err := h.TenantService.GetTenantBySlug(c.Context(), req.CompanyCode)
if err == nil && tenant != nil {
if tenant.Status == domain.TenantStatusActive {
// Policy: Should we allow manual joining by Slug?
// For now, let's allow it but log it as manual.
slog.Info("[Signup] Assigning tenant by manual slug", "email", req.Email, "tenant", tenant.Slug)
companyCode = tenant.Slug
tenantID = &tenant.ID
@@ -538,54 +544,18 @@ func (h *AuthHandler) Signup(c *fiber.Ctx) error {
return errorJSON(c, fiber.StatusForbidden, "The specified organization is not active.")
}
} else {
// If companyCode provided but not found, we automatically create one
// [New Policy] 자동 생성 로직 추가
slog.Info("[Signup] CompanyCode not found, creating new tenant automatically", "slug", req.CompanyCode)
// Determine name from CompanyCode
tenantName := req.CompanyCode
// Map slug to localized name if possible
slugToName := map[string]string{
"HANMAC": "한맥",
"SAMAN": "삼안",
"JANGHEON": "장헌",
"HALLA": "한라",
"PTC": "PTC",
"BARON": "바론",
}
if name, ok := slugToName[strings.ToUpper(req.CompanyCode)]; ok {
tenantName = name
}
// Create the tenant
// Note: creatorID is unknown at this point, will be set via Read-Model sync later
newTenant, err := h.TenantService.RegisterTenant(c.Context(),
tenantName,
req.CompanyCode,
domain.TenantTypeCompany,
"Automatically created during signup",
nil, // domains
nil, // parentID
"", // creatorID (will sync later)
)
if err != nil {
// Handle race condition: if tenant was created by another request just now
if strings.Contains(err.Error(), "already exists") {
newTenant, err = h.TenantService.GetTenantBySlug(c.Context(), req.CompanyCode)
}
if err != nil || newTenant == nil {
slog.Error("[Signup] Failed to create tenant automatically", "slug", req.CompanyCode, "error", err)
return errorJSON(c, fiber.StatusInternalServerError, "Failed to initialize organization.")
}
}
slog.Info("[Signup] Successfully created missing tenant", "slug", req.CompanyCode, "id", newTenant.ID)
tenantID = &newTenant.ID
companyCode = newTenant.Slug
// [New Policy] Do NOT create tenants automatically with hardcoded names.
// Only allow joining existing tenants.
slog.Warn("[Signup] Attempted to join non-existent organization", "slug", req.CompanyCode, "email", req.Email)
return errorJSON(c, fiber.StatusNotFound, "The specified organization code was not found. Please contact your administrator.")
}
}
if tenantID == nil {
slog.Warn("[Signup] No tenant assigned to user", "email", req.Email)
return errorJSON(c, fiber.StatusBadRequest, "We couldn't identify your organization. Please provide a company code or use your corporate email.")
}
// Normalize Phone (E.164 형태로 보관)
normalizedPhone := strings.ReplaceAll(req.Phone, "-", "")
normalizedPhone = strings.ReplaceAll(normalizedPhone, " ", "")