forked from baron/baron-sso
네이버 계정 정합성 맞춤
This commit is contained in:
@@ -4,7 +4,9 @@ import (
|
||||
"baron-sso-backend/internal/domain"
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -28,7 +30,13 @@ type hanmacEmailEvaluation struct {
|
||||
LocalPart string
|
||||
}
|
||||
|
||||
func (h *UserHandler) evaluateHanmacImportEmail(ctx context.Context, item bulkUserItem, scope *hanmacEmailScope, usedLocalParts map[string]bool) hanmacEmailEvaluation {
|
||||
type hanmacLocalPartOwner struct {
|
||||
UserID string
|
||||
Email string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (h *UserHandler) evaluateHanmacImportEmail(ctx context.Context, item bulkUserItem, scope *hanmacEmailScope, usedLocalParts map[string]hanmacLocalPartOwner) hanmacEmailEvaluation {
|
||||
originalEmail := strings.TrimSpace(item.Email)
|
||||
name := strings.TrimSpace(item.Name)
|
||||
evaluation := hanmacEmailEvaluation{
|
||||
@@ -68,9 +76,9 @@ func (h *UserHandler) evaluateHanmacImportEmail(ctx context.Context, item bulkUs
|
||||
}
|
||||
|
||||
evaluation.LocalPart = localPart
|
||||
if usedLocalParts[localPart] {
|
||||
if owner, exists := usedLocalParts[localPart]; exists {
|
||||
evaluation.Status = "blockingError"
|
||||
evaluation.Message = "한맥가족 내에서 이미 사용 중인 이메일 ID입니다."
|
||||
evaluation.Message = formatHanmacLocalPartConflictMessage(localPart, owner)
|
||||
evaluation.Blocking = true
|
||||
return evaluation
|
||||
}
|
||||
@@ -88,6 +96,14 @@ func (h *UserHandler) evaluateHanmacImportEmail(ctx context.Context, item bulkUs
|
||||
}
|
||||
|
||||
func (h *UserHandler) ensureHanmacCreateEmailAllowed(ctx context.Context, email string, tenantSlug string, tenantID string) error {
|
||||
return h.ensureHanmacEmailAllowedWithLog(ctx, email, tenantSlug, tenantID, "", "hanmac create email local-part conflict")
|
||||
}
|
||||
|
||||
func (h *UserHandler) ensureHanmacEmailAllowed(ctx context.Context, email string, tenantSlug string, tenantID string, currentUserID string) error {
|
||||
return h.ensureHanmacEmailAllowedWithLog(ctx, email, tenantSlug, tenantID, currentUserID, "hanmac email local-part conflict")
|
||||
}
|
||||
|
||||
func (h *UserHandler) ensureHanmacEmailAllowedWithLog(ctx context.Context, email string, tenantSlug string, tenantID string, currentUserID string, logMessage string) error {
|
||||
scope, err := h.resolveHanmacEmailScope(ctx)
|
||||
if err != nil || scope == nil || !scope.ContainsTenant(tenantID, tenantSlug) {
|
||||
return nil
|
||||
@@ -102,8 +118,22 @@ func (h *UserHandler) ensureHanmacCreateEmailAllowed(ctx context.Context, email
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if usedLocalParts[localPart] {
|
||||
return fmt.Errorf("한맥가족 내에서 이미 사용 중인 이메일 ID입니다.")
|
||||
if owner, exists := usedLocalParts[localPart]; exists {
|
||||
ownerUserID := strings.TrimSpace(owner.UserID)
|
||||
if currentUserID != "" && ownerUserID != "" && ownerUserID == strings.TrimSpace(currentUserID) {
|
||||
return nil
|
||||
}
|
||||
slog.Warn(
|
||||
logMessage,
|
||||
"requestedEmail", email,
|
||||
"localPart", localPart,
|
||||
"ownerUserID", owner.UserID,
|
||||
"ownerEmail", owner.Email,
|
||||
"ownerName", owner.Name,
|
||||
"tenantID", tenantID,
|
||||
"tenantSlug", tenantSlug,
|
||||
)
|
||||
return fmt.Errorf("%s", formatHanmacLocalPartConflictMessage(localPart, owner))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -149,8 +179,8 @@ func (h *UserHandler) resolveHanmacEmailScope(ctx context.Context) (*hanmacEmail
|
||||
return scope, nil
|
||||
}
|
||||
|
||||
func (h *UserHandler) loadHanmacLocalParts(ctx context.Context, scope *hanmacEmailScope) (map[string]bool, error) {
|
||||
used := make(map[string]bool)
|
||||
func (h *UserHandler) loadHanmacLocalParts(ctx context.Context, scope *hanmacEmailScope) (map[string]hanmacLocalPartOwner, error) {
|
||||
used := make(map[string]hanmacLocalPartOwner)
|
||||
if h.UserRepo == nil || scope == nil {
|
||||
return used, nil
|
||||
}
|
||||
@@ -160,7 +190,7 @@ func (h *UserHandler) loadHanmacLocalParts(ctx context.Context, scope *hanmacEma
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addUserEmailLocalParts(used, users)
|
||||
addUserEmailLocalPartOwners(used, users)
|
||||
}
|
||||
|
||||
if len(scope.SlugList) > 0 {
|
||||
@@ -168,7 +198,7 @@ func (h *UserHandler) loadHanmacLocalParts(ctx context.Context, scope *hanmacEma
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addUserEmailLocalParts(used, users)
|
||||
addUserEmailLocalPartOwners(used, users)
|
||||
}
|
||||
|
||||
return used, nil
|
||||
@@ -210,31 +240,79 @@ func isTenantDescendantOf(tenant domain.Tenant, rootID string, tenantByID map[st
|
||||
return false
|
||||
}
|
||||
|
||||
func addUserEmailLocalParts(target map[string]bool, users []domain.User) {
|
||||
func addUserEmailLocalPartOwners(target map[string]hanmacLocalPartOwner, users []domain.User) {
|
||||
for _, user := range users {
|
||||
localPart, err := domain.ExtractNormalizedEmailLocalPart(user.Email)
|
||||
if err == nil && localPart != "" {
|
||||
target[localPart] = true
|
||||
if err != nil || localPart == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := target[localPart]; exists {
|
||||
continue
|
||||
}
|
||||
target[localPart] = hanmacLocalPartOwner{
|
||||
UserID: strings.TrimSpace(user.ID),
|
||||
Email: strings.TrimSpace(user.Email),
|
||||
Name: strings.TrimSpace(user.Name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func nextAvailableHanmacLocalPart(base string, usedLocalParts map[string]bool) string {
|
||||
func formatHanmacLocalPartConflictMessage(localPart string, owner hanmacLocalPartOwner) string {
|
||||
message := fmt.Sprintf("한맥가족 내에서 이미 사용 중인 이메일 ID입니다. local-part=%s", strings.TrimSpace(localPart))
|
||||
if owner.Email != "" {
|
||||
message += ", 사용 계정=" + owner.Email
|
||||
}
|
||||
if owner.Name != "" {
|
||||
message += ", 사용자=" + owner.Name
|
||||
}
|
||||
if owner.UserID != "" {
|
||||
message += ", 사용자 ID=" + owner.UserID
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
func nextAvailableHanmacLocalPart(base string, usedLocalParts map[string]hanmacLocalPartOwner) string {
|
||||
base = strings.ToLower(strings.TrimSpace(base))
|
||||
if base == "" {
|
||||
return ""
|
||||
}
|
||||
if !usedLocalParts[base] {
|
||||
if _, exists := usedLocalParts[base]; !exists {
|
||||
return base
|
||||
}
|
||||
for index := 1; ; index++ {
|
||||
candidate := fmt.Sprintf("%s%d", base, index)
|
||||
if !usedLocalParts[candidate] {
|
||||
|
||||
stem, nextIndex := splitTrailingNumericSuffix(base)
|
||||
if stem == "" {
|
||||
stem = base
|
||||
}
|
||||
for index := nextIndex; ; index++ {
|
||||
candidate := fmt.Sprintf("%s%d", stem, index)
|
||||
if _, exists := usedLocalParts[candidate]; !exists {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func splitTrailingNumericSuffix(value string) (string, int) {
|
||||
value = strings.ToLower(strings.TrimSpace(value))
|
||||
if value == "" {
|
||||
return "", 1
|
||||
}
|
||||
index := len(value)
|
||||
for index > 0 && value[index-1] >= '0' && value[index-1] <= '9' {
|
||||
index--
|
||||
}
|
||||
if index == len(value) {
|
||||
return value, 1
|
||||
}
|
||||
stem := value[:index]
|
||||
suffix := value[index:]
|
||||
number, err := strconv.Atoi(suffix)
|
||||
if err != nil {
|
||||
return value, 1
|
||||
}
|
||||
return stem, number + 1
|
||||
}
|
||||
|
||||
func appendUniqueString(values []string, value string) []string {
|
||||
if slices.Contains(values, value) {
|
||||
return values
|
||||
|
||||
Reference in New Issue
Block a user