1
0
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:
2026-04-13 11:56:35 +09:00
parent 0cd43f0aea
commit 010719eee9
2 changed files with 67 additions and 11 deletions

View File

@@ -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)

View File

@@ -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 {