forked from baron/baron-sso
feat: allow regular users to view their own tenant's org chart
Changes the /users endpoint to allow RoleUser access and securely restricts the returned data to only users within their affiliated tenants. Removes the unnecessary back button from the Org Chart view since it's now a top-level nav item.
This commit is contained in:
@@ -599,13 +599,17 @@ func main() {
|
||||
KetoService: ketoService,
|
||||
})
|
||||
requireAdmin := middleware.RequireRole(middleware.RBACConfig{
|
||||
AllowedRoles: []string{domain.RoleSuperAdmin, domain.RoleTenantAdmin},
|
||||
AuthHandler: authHandler,
|
||||
KetoService: ketoService,
|
||||
AllowedRoles: []string{domain.RoleSuperAdmin, domain.RoleTenantAdmin},
|
||||
AuthHandler: authHandler,
|
||||
KetoService: ketoService,
|
||||
})
|
||||
requireAnyUser := middleware.RequireRole(middleware.RBACConfig{
|
||||
AllowedRoles: []string{domain.RoleSuperAdmin, domain.RoleTenantAdmin, domain.RoleRPAdmin, domain.RoleUser},
|
||||
AuthHandler: authHandler,
|
||||
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)
|
||||
@@ -668,9 +672,8 @@ func main() {
|
||||
relyingPartyHandler.Delete)
|
||||
|
||||
// Admin User Management
|
||||
admin.Get("/users", requireAdmin, userHandler.ListUsers)
|
||||
admin.Get("/users/export", userHandler.ExportUsersCSV) // Removed requireAdmin to handle mock role in query param
|
||||
admin.Post("/users", requireAdmin, userHandler.CreateUser)
|
||||
admin.Get("/users", requireAnyUser, userHandler.ListUsers)
|
||||
admin.Get("/users/export", userHandler.ExportUsersCSV) // Removed requireAdmin to handle mock role in query param admin.Post("/users", requireAdmin, userHandler.CreateUser)
|
||||
admin.Post("/users/bulk", requireAdmin, userHandler.BulkCreateUsers)
|
||||
admin.Put("/users/bulk", requireAdmin, userHandler.BulkUpdateUsers)
|
||||
admin.Delete("/users/bulk", requireAdmin, userHandler.BulkDeleteUsers)
|
||||
|
||||
@@ -100,13 +100,17 @@ func (h *UserHandler) ListUsers(c *fiber.Ctx) error {
|
||||
|
||||
// [New] Manageable Tenants Map for efficient lookup
|
||||
manageableSlugs := make(map[string]bool)
|
||||
if requesterRole == domain.RoleTenantAdmin {
|
||||
if requesterRole == domain.RoleTenantAdmin || requesterRole == domain.RoleUser || requesterRole == domain.RoleRPAdmin {
|
||||
profile, _ := c.Locals("user_profile").(*domain.UserProfileResponse)
|
||||
if profile != nil {
|
||||
for _, t := range profile.ManageableTenants {
|
||||
manageableSlugs[strings.ToLower(t.Slug)] = true
|
||||
manageableSlugs[strings.ToLower(t.ID)] = true // Add ID as well
|
||||
}
|
||||
for _, t := range profile.JoinedTenants {
|
||||
manageableSlugs[strings.ToLower(t.Slug)] = true
|
||||
manageableSlugs[strings.ToLower(t.ID)] = true
|
||||
}
|
||||
// Include primary tenant slug if not already there
|
||||
if profile.CompanyCode != "" {
|
||||
manageableSlugs[strings.ToLower(profile.CompanyCode)] = true
|
||||
@@ -137,8 +141,8 @@ func (h *UserHandler) ListUsers(c *fiber.Ctx) error {
|
||||
compCode := strings.ToLower(extractTraitString(identity.Traits, "companyCode"))
|
||||
tID := strings.ToLower(extractTraitString(identity.Traits, "tenant_id"))
|
||||
|
||||
// Tenant Admin filtering
|
||||
if requesterRole == domain.RoleTenantAdmin {
|
||||
// Tenant Admin & Member filtering
|
||||
if requesterRole == domain.RoleTenantAdmin || requesterRole == domain.RoleUser || requesterRole == domain.RoleRPAdmin {
|
||||
if !manageableSlugs[compCode] && !manageableSlugs[tID] {
|
||||
continue
|
||||
}
|
||||
@@ -194,6 +198,15 @@ func (h *UserHandler) ListUsers(c *fiber.Ctx) error {
|
||||
// 2. Fallback to Local DB if Kratos is down
|
||||
slog.Warn("Kratos unavailable, falling back to local DB for user list", "error", err)
|
||||
|
||||
// If requester is not Super Admin, we should technically filter by manageable slugs in DB too.
|
||||
// For simplicity in fallback, if tenantSlug is empty we default to their primary company code.
|
||||
if (requesterRole == domain.RoleTenantAdmin || requesterRole == domain.RoleUser || requesterRole == domain.RoleRPAdmin) && tenantSlug == "" {
|
||||
profile, _ := c.Locals("user_profile").(*domain.UserProfileResponse)
|
||||
if profile != nil && profile.CompanyCode != "" {
|
||||
tenantSlug = profile.CompanyCode
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch from UserRepo
|
||||
users, total, err := h.UserRepo.List(c.Context(), offset, limit, search, tenantSlug)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user