forked from baron/baron-sso
조직도 기능 추가
This commit is contained in:
@@ -105,11 +105,23 @@ func (h *UserHandler) ListUsers(c *fiber.Ctx) error {
|
||||
if profile != nil {
|
||||
for _, t := range profile.ManageableTenants {
|
||||
manageableSlugs[strings.ToLower(t.Slug)] = true
|
||||
manageableSlugs[strings.ToLower(t.ID)] = true // Add ID as well
|
||||
}
|
||||
// Include primary tenant slug if not already there
|
||||
if profile.CompanyCode != "" {
|
||||
manageableSlugs[strings.ToLower(profile.CompanyCode)] = true
|
||||
}
|
||||
if profile.TenantID != nil {
|
||||
manageableSlugs[strings.ToLower(*profile.TenantID)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var targetTenantID string
|
||||
if tenantSlug != "" && h.TenantService != nil {
|
||||
t, err := h.TenantService.GetTenantBySlug(c.Context(), tenantSlug)
|
||||
if err == nil && t != nil {
|
||||
targetTenantID = strings.ToLower(t.ID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,16 +135,17 @@ func (h *UserHandler) ListUsers(c *fiber.Ctx) error {
|
||||
email := strings.ToLower(extractTraitString(identity.Traits, "email"))
|
||||
name := strings.ToLower(extractTraitString(identity.Traits, "name"))
|
||||
compCode := strings.ToLower(extractTraitString(identity.Traits, "companyCode"))
|
||||
tID := strings.ToLower(extractTraitString(identity.Traits, "tenant_id"))
|
||||
|
||||
// Tenant Admin filtering
|
||||
if requesterRole == domain.RoleTenantAdmin {
|
||||
if !manageableSlugs[compCode] {
|
||||
if !manageableSlugs[compCode] && !manageableSlugs[tID] {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Dedicated tenantSlug filter
|
||||
if tenantSlug != "" && !strings.EqualFold(compCode, tenantSlug) {
|
||||
if tenantSlug != "" && !strings.EqualFold(compCode, tenantSlug) && tID != targetTenantID {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -261,15 +274,17 @@ func (h *UserHandler) CreateUser(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Email string `json:"email"`
|
||||
LoginID string `json:"loginId"`
|
||||
Password string `json:"password"`
|
||||
Name string `json:"name"`
|
||||
Phone string `json:"phone"`
|
||||
Role string `json:"role"`
|
||||
CompanyCode string `json:"companyCode"`
|
||||
Department string `json:"department"`
|
||||
Metadata map[string]any `json:"metadata"`
|
||||
Email string `json:"email"`
|
||||
LoginID string `json:"loginId"`
|
||||
Password string `json:"password"`
|
||||
Name string `json:"name"`
|
||||
Phone string `json:"phone"`
|
||||
Role string `json:"role"`
|
||||
CompanyCode string `json:"companyCode"`
|
||||
Department string `json:"department"`
|
||||
Position string `json:"position"`
|
||||
JobTitle string `json:"jobTitle"`
|
||||
Metadata map[string]any `json:"metadata"`
|
||||
}
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errorJSON(c, fiber.StatusBadRequest, "invalid request body")
|
||||
@@ -321,6 +336,8 @@ func (h *UserHandler) CreateUser(c *fiber.Ctx) error {
|
||||
|
||||
attributes := map[string]interface{}{
|
||||
"department": req.Department,
|
||||
"position": req.Position,
|
||||
"jobTitle": req.JobTitle,
|
||||
"affiliationType": "internal",
|
||||
"companyCode": req.CompanyCode,
|
||||
"grade": role,
|
||||
@@ -454,6 +471,8 @@ type bulkUserItem struct {
|
||||
Role string `json:"role"`
|
||||
TenantSlug string `json:"tenantSlug"`
|
||||
Department string `json:"department"`
|
||||
Position string `json:"position"`
|
||||
JobTitle string `json:"jobTitle"`
|
||||
Metadata map[string]any `json:"metadata"`
|
||||
}
|
||||
|
||||
@@ -570,6 +589,8 @@ func (h *UserHandler) BulkCreateUsers(c *fiber.Ctx) error {
|
||||
|
||||
attributes := map[string]interface{}{
|
||||
"department": dept,
|
||||
"position": strings.TrimSpace(item.Position),
|
||||
"jobTitle": strings.TrimSpace(item.JobTitle),
|
||||
"affiliationType": "internal",
|
||||
"companyCode": tenantSlug,
|
||||
"tenant_id": tItem.ID,
|
||||
@@ -845,6 +866,8 @@ func (h *UserHandler) BulkUpdateUsers(c *fiber.Ctx) error {
|
||||
Role *string `json:"role"`
|
||||
CompanyCode *string `json:"companyCode"`
|
||||
Department *string `json:"department"`
|
||||
Position *string `json:"position"`
|
||||
JobTitle *string `json:"jobTitle"`
|
||||
}
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errorJSON(c, fiber.StatusBadRequest, "invalid request body")
|
||||
@@ -879,6 +902,16 @@ func (h *UserHandler) BulkUpdateUsers(c *fiber.Ctx) error {
|
||||
results := make([]map[string]any, 0, len(req.UserIDs))
|
||||
|
||||
for _, id := range req.UserIDs {
|
||||
// [Safety] Cannot delete yourself
|
||||
if id == requester.ID {
|
||||
results = append(results, map[string]any{
|
||||
"id": id,
|
||||
"success": false,
|
||||
"message": "cannot delete your own account for safety",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
identity, err := h.KratosAdmin.GetIdentity(c.Context(), id)
|
||||
if err != nil {
|
||||
results = append(results, map[string]any{"id": id, "success": false, "message": "user not found"})
|
||||
@@ -924,6 +957,12 @@ func (h *UserHandler) BulkUpdateUsers(c *fiber.Ctx) error {
|
||||
if req.Department != nil {
|
||||
traits["department"] = *req.Department
|
||||
}
|
||||
if req.Position != nil {
|
||||
traits["position"] = *req.Position
|
||||
}
|
||||
if req.JobTitle != nil {
|
||||
traits["jobTitle"] = *req.JobTitle
|
||||
}
|
||||
|
||||
state := identity.State
|
||||
if req.Status != nil {
|
||||
@@ -958,6 +997,12 @@ func (h *UserHandler) BulkUpdateUsers(c *fiber.Ctx) error {
|
||||
if req.Department != nil {
|
||||
localUser.Department = *req.Department
|
||||
}
|
||||
if req.Position != nil {
|
||||
localUser.Position = *req.Position
|
||||
}
|
||||
if req.JobTitle != nil {
|
||||
localUser.JobTitle = *req.JobTitle
|
||||
}
|
||||
|
||||
// Resolve TenantID if changing companyCode
|
||||
if req.CompanyCode != nil && h.TenantService != nil {
|
||||
@@ -1011,6 +1056,16 @@ func (h *UserHandler) BulkDeleteUsers(c *fiber.Ctx) error {
|
||||
results := make([]map[string]any, 0, len(req.UserIDs))
|
||||
|
||||
for _, id := range req.UserIDs {
|
||||
// [Safety] Cannot delete yourself
|
||||
if id == requester.ID {
|
||||
results = append(results, map[string]any{
|
||||
"id": id,
|
||||
"success": false,
|
||||
"message": "cannot delete your own account for safety",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
identity, err := h.KratosAdmin.GetIdentity(c.Context(), id)
|
||||
if err != nil {
|
||||
results = append(results, map[string]any{"id": id, "success": false, "message": "user not found"})
|
||||
@@ -1093,6 +1148,8 @@ func (h *UserHandler) UpdateUser(c *fiber.Ctx) error {
|
||||
Status *string `json:"status"`
|
||||
CompanyCode *string `json:"companyCode"`
|
||||
Department *string `json:"department"`
|
||||
Position *string `json:"position"`
|
||||
JobTitle *string `json:"jobTitle"`
|
||||
Metadata map[string]any `json:"metadata"`
|
||||
}
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
@@ -1176,6 +1233,12 @@ func (h *UserHandler) UpdateUser(c *fiber.Ctx) error {
|
||||
if req.Department != nil {
|
||||
traits["department"] = strings.TrimSpace(*req.Department)
|
||||
}
|
||||
if req.Position != nil {
|
||||
traits["position"] = strings.TrimSpace(*req.Position)
|
||||
}
|
||||
if req.JobTitle != nil {
|
||||
traits["jobTitle"] = strings.TrimSpace(*req.JobTitle)
|
||||
}
|
||||
if req.Role != nil {
|
||||
role := domain.NormalizeRole(*req.Role)
|
||||
if role == "" {
|
||||
@@ -1189,6 +1252,7 @@ func (h *UserHandler) UpdateUser(c *fiber.Ctx) error {
|
||||
coreTraits := map[string]bool{
|
||||
"email": true, "name": true, "phone_number": true,
|
||||
"grade": true, "companyCode": true, "department": true,
|
||||
"position": true, "jobTitle": true,
|
||||
"affiliationType": true, "role": true, "tenant_id": true,
|
||||
"custom_login_ids": true, "id": true,
|
||||
}
|
||||
@@ -1339,6 +1403,12 @@ func (h *UserHandler) DeleteUser(c *fiber.Ctx) error {
|
||||
|
||||
// [New] Check access scope before deletion
|
||||
requester, _ := c.Locals("user_profile").(*domain.UserProfileResponse)
|
||||
|
||||
// [Safety] Cannot delete yourself
|
||||
if requester != nil && userID == requester.ID {
|
||||
return errorJSON(c, fiber.StatusForbidden, "cannot delete your own account for safety")
|
||||
}
|
||||
|
||||
if requester != nil && domain.NormalizeRole(requester.Role) == domain.RoleTenantAdmin {
|
||||
identity, err := h.KratosAdmin.GetIdentity(c.Context(), userID)
|
||||
if err == nil && identity != nil {
|
||||
@@ -1407,14 +1477,16 @@ func (h *UserHandler) mapIdentitySummary(ctx context.Context, identity service.K
|
||||
Status: normalizeStatus(identity.State),
|
||||
CompanyCode: compCode,
|
||||
Department: extractTraitString(traits, "department"),
|
||||
Position: extractTraitString(traits, "position"),
|
||||
JobTitle: extractTraitString(traits, "jobTitle"),
|
||||
Metadata: make(domain.JSONMap),
|
||||
CreatedAt: formatTime(identity.CreatedAt),
|
||||
UpdatedAt: formatTime(identity.UpdatedAt),
|
||||
}
|
||||
|
||||
// [New] Fetch all manageable tenants (for Multi-tenancy support)
|
||||
// [New] Fetch all joined tenants (for Multi-tenancy support)
|
||||
if h.TenantService != nil {
|
||||
if joined, err := h.TenantService.ListManageableTenants(ctx, identity.ID); err == nil {
|
||||
if joined, err := h.TenantService.ListJoinedTenants(ctx, identity.ID); err == nil {
|
||||
summary.JoinedTenants = joined
|
||||
}
|
||||
}
|
||||
@@ -1426,6 +1498,7 @@ func (h *UserHandler) mapIdentitySummary(ctx context.Context, identity service.K
|
||||
coreTraits := map[string]bool{
|
||||
"email": true, "name": true, "phone_number": true,
|
||||
"grade": true, "companyCode": true, "department": true,
|
||||
"position": true, "jobTitle": true,
|
||||
"affiliationType": true, "role": true, "tenant_id": true,
|
||||
"custom_login_ids": true, "id": true,
|
||||
}
|
||||
@@ -1476,6 +1549,8 @@ func (h *UserHandler) mapToLocalUser(identity service.KratosIdentity) *domain.Us
|
||||
Status: normalizeStatus(identity.State),
|
||||
CompanyCode: compCode,
|
||||
Department: extractTraitString(traits, "department"),
|
||||
Position: extractTraitString(traits, "position"),
|
||||
JobTitle: extractTraitString(traits, "jobTitle"),
|
||||
AffiliationType: extractTraitString(traits, "affiliationType"),
|
||||
CreatedAt: identity.CreatedAt,
|
||||
UpdatedAt: identity.UpdatedAt,
|
||||
@@ -1501,6 +1576,7 @@ func (h *UserHandler) mapToLocalUser(identity service.KratosIdentity) *domain.Us
|
||||
coreTraits := map[string]bool{
|
||||
"email": true, "name": true, "phone_number": true,
|
||||
"grade": true, "companyCode": true, "department": true,
|
||||
"position": true, "jobTitle": true,
|
||||
"affiliationType": true, "role": true, "tenant_id": true, "company_code": true,
|
||||
"custom_login_ids": true, "id": true,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user