forked from baron/baron-sso
feat: implement bulk user actions and organization tree search with auto-expansion
This commit is contained in:
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user