forked from baron/baron-sso
feat(backend): allow regular users and tenant admins to list their full tenant trees
Changes the /v1/admin/tenants endpoint to be accessible by all authenticated users (requireAnyUser). In the handler, it dynamically resolves the user's affiliations and filters the response to return the complete hierarchical tree (root, parent, child, sibling nodes) for any tenant they belong to.
This commit is contained in:
@@ -609,10 +609,11 @@ func main() {
|
||||
KetoService: ketoService,
|
||||
})
|
||||
|
||||
admin.Get("/check", adminHandler.CheckAuth) // 기본 Admin 체크는 requireAdmin 없이 ApiKeyAuth로만 보호될 수 있음 (또는 추가 가능) admin.Get("/stats", requireSuperAdmin, adminHandler.GetSystemStats)
|
||||
admin.Get("/check", adminHandler.CheckAuth) // 기본 Admin 체크는 requireAdmin 없이 ApiKeyAuth로만 보호될 수 있음 (또는 추가 가능)
|
||||
admin.Get("/stats", requireSuperAdmin, adminHandler.GetSystemStats)
|
||||
|
||||
// Tenant Management (Mixed roles, handler filters results)
|
||||
admin.Get("/tenants", requireAdmin, tenantHandler.ListTenants)
|
||||
admin.Get("/tenants", requireAnyUser, tenantHandler.ListTenants)
|
||||
admin.Post("/tenants", requireSuperAdmin, tenantHandler.CreateTenant)
|
||||
admin.Delete("/tenants/bulk", requireSuperAdmin, tenantHandler.DeleteTenantsBulk)
|
||||
admin.Post("/tenants/:id/approve", requireSuperAdmin, tenantHandler.ApproveTenant)
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"baron-sso-backend/internal/service"
|
||||
"baron-sso-backend/internal/utils"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -114,16 +113,72 @@ func (h *TenantHandler) ListTenants(c *fiber.Ctx) error {
|
||||
var err error
|
||||
|
||||
profile, _ := c.Locals("user_profile").(*domain.UserProfileResponse)
|
||||
role := ""
|
||||
if profile != nil {
|
||||
role = domain.NormalizeRole(profile.Role)
|
||||
}
|
||||
|
||||
// If Tenant Admin, only list manageable tenants
|
||||
if profile != nil && domain.NormalizeRole(profile.Role) == domain.RoleTenantAdmin {
|
||||
slog.Info("Listing manageable tenants for tenant admin", "userID", profile.ID)
|
||||
tenants, err = h.Service.ListManageableTenants(c.Context(), profile.ID)
|
||||
if role != domain.RoleSuperAdmin {
|
||||
// Not a super admin: Only return the entire tree(s) of the tenants they belong to
|
||||
allTenants, _, err := h.Service.ListTenants(c.Context(), 10000, 0, "")
|
||||
if err != nil {
|
||||
return errorJSON(c, fiber.StatusInternalServerError, "failed to list manageable tenants: "+err.Error())
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
if profile != nil {
|
||||
baseTenantIDs := []string{}
|
||||
for _, t := range profile.ManageableTenants {
|
||||
baseTenantIDs = append(baseTenantIDs, t.ID)
|
||||
}
|
||||
for _, t := range profile.JoinedTenants {
|
||||
baseTenantIDs = append(baseTenantIDs, t.ID)
|
||||
}
|
||||
if profile.TenantID != nil {
|
||||
baseTenantIDs = append(baseTenantIDs, *profile.TenantID)
|
||||
}
|
||||
|
||||
// Try to find by companyCode if needed
|
||||
if profile.CompanyCode != "" {
|
||||
for _, t := range allTenants {
|
||||
if strings.EqualFold(t.Slug, profile.CompanyCode) {
|
||||
baseTenantIDs = append(baseTenantIDs, t.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parentMap := make(map[string]string)
|
||||
for _, t := range allTenants {
|
||||
if t.ParentID != nil {
|
||||
parentMap[t.ID] = *t.ParentID
|
||||
}
|
||||
}
|
||||
|
||||
findRoot := func(id string) string {
|
||||
curr := id
|
||||
for {
|
||||
p, exists := parentMap[curr]
|
||||
if !exists || p == "" {
|
||||
break
|
||||
}
|
||||
curr = p
|
||||
}
|
||||
return curr
|
||||
}
|
||||
|
||||
roots := make(map[string]bool)
|
||||
for _, id := range baseTenantIDs {
|
||||
roots[findRoot(id)] = true
|
||||
}
|
||||
|
||||
// Filter tenants that belong to the same tree family
|
||||
for _, t := range allTenants {
|
||||
if roots[findRoot(t.ID)] {
|
||||
tenants = append(tenants, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
total = int64(len(tenants))
|
||||
// Apply basic pagination if needed (optional for usually small number of manageable tenants)
|
||||
if offset < len(tenants) {
|
||||
end := offset + limit
|
||||
if end > len(tenants) {
|
||||
@@ -751,8 +806,8 @@ func (h *TenantHandler) DeleteTenantsBulk(c *fiber.Ctx) error {
|
||||
return errorJSON(c, fiber.StatusBadRequest, "no IDs provided")
|
||||
}
|
||||
|
||||
// Permission check: Super Admin can delete anything.
|
||||
// Tenant Admin should theoretically only delete manageable sub-tenants,
|
||||
// Permission check: Super Admin can delete anything.
|
||||
// Tenant Admin should theoretically only delete manageable sub-tenants,
|
||||
// but currently bulk delete is intended for Super Admin.
|
||||
profile, _ := c.Locals("user_profile").(*domain.UserProfileResponse)
|
||||
if profile == nil || domain.NormalizeRole(profile.Role) != domain.RoleSuperAdmin {
|
||||
|
||||
Reference in New Issue
Block a user