1
0
forked from baron/baron-sso

feat: implement bulk user actions and organization tree search with auto-expansion

This commit is contained in:
2026-03-04 15:43:00 +09:00
parent d3b4e3ef5e
commit a5102d9b25
5 changed files with 331 additions and 3 deletions

View File

@@ -541,6 +541,155 @@ func (h *UserHandler) BulkCreateUsers(c *fiber.Ctx) error {
})
}
func (h *UserHandler) BulkUpdateUsers(c *fiber.Ctx) error {
var req struct {
UserIDs []string `json:"userIds"`
Status *string `json:"status"`
Role *string `json:"role"`
}
if err := c.BodyParser(&req); err != nil {
return errorJSON(c, fiber.StatusBadRequest, "invalid request body")
}
if len(req.UserIDs) == 0 {
return errorJSON(c, fiber.StatusBadRequest, "no user IDs provided")
}
requester, _ := c.Locals("user_profile").(*domain.UserProfileResponse)
if requester == nil {
return errorJSON(c, fiber.StatusUnauthorized, "unauthorized")
}
// Build manageable slugs map if tenant_admin
manageableSlugs := make(map[string]bool)
if requester.Role == domain.RoleTenantAdmin {
for _, t := range requester.ManageableTenants {
manageableSlugs[strings.ToLower(t.Slug)] = true
}
if requester.CompanyCode != "" {
manageableSlugs[strings.ToLower(requester.CompanyCode)] = true
}
}
results := make([]map[string]any, 0, len(req.UserIDs))
for _, id := range req.UserIDs {
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"})
continue
}
// Authorization check
if requester.Role == domain.RoleTenantAdmin {
userComp := strings.ToLower(extractTraitString(identity.Traits, "companyCode"))
if !manageableSlugs[userComp] {
results = append(results, map[string]any{"id": id, "success": false, "message": "forbidden: user belongs to another tenant"})
continue
}
}
// Prepare updates
traits := identity.Traits
if req.Role != nil {
traits["role"] = *req.Role
}
state := identity.State
if req.Status != nil {
if *req.Status == "active" {
state = "active"
} else {
state = "inactive"
}
}
_, err = h.KratosAdmin.UpdateIdentity(c.Context(), id, traits, state)
if err != nil {
results = append(results, map[string]any{"id": id, "success": false, "message": err.Error()})
continue
}
// Sync to local DB
if h.UserRepo != nil {
localUser := h.mapToLocalUser(*identity)
if req.Role != nil {
localUser.Role = *req.Role
}
if req.Status != nil {
localUser.Status = *req.Status
}
_ = h.UserRepo.Update(c.Context(), localUser)
}
results = append(results, map[string]any{"id": id, "success": true})
}
return c.JSON(fiber.Map{"results": results})
}
func (h *UserHandler) BulkDeleteUsers(c *fiber.Ctx) error {
var req struct {
UserIDs []string `json:"userIds"`
}
if err := c.BodyParser(&req); err != nil {
return errorJSON(c, fiber.StatusBadRequest, "invalid request body")
}
if len(req.UserIDs) == 0 {
return errorJSON(c, fiber.StatusBadRequest, "no user IDs provided")
}
requester, _ := c.Locals("user_profile").(*domain.UserProfileResponse)
if requester == nil {
return errorJSON(c, fiber.StatusUnauthorized, "unauthorized")
}
manageableSlugs := make(map[string]bool)
if requester.Role == domain.RoleTenantAdmin {
for _, t := range requester.ManageableTenants {
manageableSlugs[strings.ToLower(t.Slug)] = true
}
if requester.CompanyCode != "" {
manageableSlugs[strings.ToLower(requester.CompanyCode)] = true
}
}
results := make([]map[string]any, 0, len(req.UserIDs))
for _, id := range req.UserIDs {
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"})
continue
}
// Authorization check
if requester.Role == domain.RoleTenantAdmin {
userComp := strings.ToLower(extractTraitString(identity.Traits, "companyCode"))
if !manageableSlugs[userComp] {
results = append(results, map[string]any{"id": id, "success": false, "message": "forbidden"})
continue
}
}
err = h.KratosAdmin.DeleteIdentity(c.Context(), id)
if err != nil {
results = append(results, map[string]any{"id": id, "success": false, "message": err.Error()})
continue
}
// Local DB Sync
if h.UserRepo != nil {
_ = h.UserRepo.Delete(c.Context(), id)
}
results = append(results, map[string]any{"id": id, "success": true})
}
return c.JSON(fiber.Map{"results": results})
}
func (h *UserHandler) UpdateUser(c *fiber.Ctx) error {
if h.KratosAdmin == nil {
return errorJSON(c, fiber.StatusServiceUnavailable, "identity provider not available")