Implement a new hardcoded permissions system for enhanced user access control

This commit replaces the previous dynamic, database-driven permissions system with a hardcoded, role-based access control (RBAC) structure. New files in `client/src/lib/permissions.ts` and `server/permissions.ts` define permissions per module and role, simplifying management and improving performance. Database tables related to roles, permissions, and user roles have been removed, and associated storage interface methods and routes have been deprecated or updated to reflect the new architecture. The `replit.md` file has also been updated to document these significant changes.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: a8a78c07-e900-425c-a577-5b4c5894379d
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/a8a78c07-e900-425c-a577-5b4c5894379d/7SnSmh4
This commit is contained in:
michaelschal committed 2025-08-11 19:11:11 +00:00
1 parent 70da3e9f5b
commit df479800e3
9 files changed
+441 -873

No files matched your search

-179
View File
@@ -1,179 +0,0 @@
import { storage } from "./storage";
export async function initRolesAndPermissions() {
console.log("Initializing roles and permissions...");
try {
// Check if roles already exist
const existingRoles = await storage.getRoles();
if (existingRoles.length > 0) {
console.log("Roles already exist, skipping initialization");
return;
}
// Create default roles
const adminRole = await storage.createRole({
name: "admin",
displayName: "Administrateur",
description: "Accès complet à toutes les fonctionnalités du système",
color: "#dc2626",
isSystem: true,
isActive: true,
});
const managerRole = await storage.createRole({
name: "manager",
displayName: "Manager",
description: "Accès à la gestion des commandes, livraisons et fournisseurs",
color: "#2563eb",
isSystem: true,
isActive: true,
});
const employeeRole = await storage.createRole({
name: "employee",
displayName: "Employé",
description: "Accès en lecture aux données et publicités",
color: "#16a34a",
isSystem: true,
isActive: true,
});
const directeurRole = await storage.createRole({
name: "directeur",
displayName: "Directeur",
description: "Supervision générale et gestion stratégique",
color: "#7c3aed",
isSystem: true,
isActive: true,
});
console.log("Created default roles:", { adminRole, managerRole, employeeRole, directeurRole });
// Create default permissions
const permissions = [
// Dashboard permissions
{ category: "tableau_de_bord", name: "dashboard_read", displayName: "Voir tableau de bord", description: "Accès en lecture au tableau de bord", action: "read", resource: "dashboard", isSystem: true },
// Groups/Stores permissions
{ category: "magasins", name: "groups_read", displayName: "Voir magasins", description: "Accès en lecture aux magasins", action: "read", resource: "groups", isSystem: true },
{ category: "magasins", name: "groups_create", displayName: "Créer magasins", description: "Création de nouveaux magasins", action: "create", resource: "groups", isSystem: true },
{ category: "magasins", name: "groups_update", displayName: "Modifier magasins", description: "Modification des magasins existants", action: "update", resource: "groups", isSystem: true },
{ category: "magasins", name: "groups_delete", displayName: "Supprimer magasins", description: "Suppression de magasins", action: "delete", resource: "groups", isSystem: true },
// Suppliers permissions
{ category: "fournisseurs", name: "suppliers_read", displayName: "Voir fournisseurs", description: "Accès en lecture aux fournisseurs", action: "read", resource: "suppliers", isSystem: true },
{ category: "fournisseurs", name: "suppliers_create", displayName: "Créer fournisseurs", description: "Création de nouveaux fournisseurs", action: "create", resource: "suppliers", isSystem: true },
{ category: "fournisseurs", name: "suppliers_update", displayName: "Modifier fournisseurs", description: "Modification des fournisseurs existants", action: "update", resource: "suppliers", isSystem: true },
{ category: "fournisseurs", name: "suppliers_delete", displayName: "Supprimer fournisseurs", description: "Suppression de fournisseurs", action: "delete", resource: "fournisseurs", isSystem: true },
// Orders permissions
{ category: "commandes", name: "orders_read", displayName: "Voir commandes", description: "Accès en lecture aux commandes", action: "read", resource: "orders", isSystem: true },
{ category: "commandes", name: "orders_create", displayName: "Créer commandes", description: "Création de nouvelles commandes", action: "create", resource: "orders", isSystem: true },
{ category: "commandes", name: "orders_update", displayName: "Modifier commandes", description: "Modification des commandes existantes", action: "update", resource: "orders", isSystem: true },
{ category: "commandes", name: "orders_delete", displayName: "Supprimer commandes", description: "Suppression de commandes", action: "delete", resource: "orders", isSystem: true },
// Deliveries permissions
{ category: "livraisons", name: "deliveries_read", displayName: "Voir livraisons", description: "Accès en lecture aux livraisons", action: "read", resource: "deliveries", isSystem: true },
{ category: "livraisons", name: "deliveries_create", displayName: "Créer livraisons", description: "Création de nouvelles livraisons", action: "create", resource: "deliveries", isSystem: true },
{ category: "livraisons", name: "deliveries_update", displayName: "Modifier livraisons", description: "Modification des livraisons existantes", action: "update", resource: "deliveries", isSystem: true },
{ category: "livraisons", name: "deliveries_delete", displayName: "Supprimer livraisons", description: "Suppression de livraisons", action: "delete", resource: "deliveries", isSystem: true },
{ category: "livraisons", name: "deliveries_validate", displayName: "Valider livraisons", description: "Validation des livraisons avec BL", action: "validate", resource: "deliveries", isSystem: true },
// Calendar permissions
{ category: "calendrier", name: "calendar_read", displayName: "Voir calendrier", description: "Accès en lecture au calendrier", action: "read", resource: "calendar", isSystem: true },
// BL Reconciliation permissions
{ category: "rapprochement", name: "reconciliation_read", displayName: "Voir rapprochement", description: "Accès en lecture au rapprochement BL/Factures", action: "read", resource: "reconciliation", isSystem: true },
{ category: "rapprochement", name: "reconciliation_update", displayName: "Modifier rapprochement", description: "Modification des données de rapprochement", action: "update", resource: "reconciliation", isSystem: true },
// Publicities permissions
{ category: "publicites", name: "publicities_read", displayName: "Voir publicités", description: "Accès en lecture aux publicités", action: "read", resource: "publicities", isSystem: true },
{ category: "publicites", name: "publicities_create", displayName: "Créer publicités", description: "Création de nouvelles publicités", action: "create", resource: "publicities", isSystem: true },
{ category: "publicites", name: "publicities_update", displayName: "Modifier publicités", description: "Modification des publicités existantes", action: "update", resource: "publicities", isSystem: true },
{ category: "publicites", name: "publicities_delete", displayName: "Supprimer publicités", description: "Suppression de publicités", action: "delete", resource: "publicities", isSystem: true },
// Customer Orders permissions
{ category: "commandes_clients", name: "customer_orders_read", displayName: "Voir commandes clients", description: "Accès en lecture aux commandes clients", action: "read", resource: "customer_orders", isSystem: true },
{ category: "commandes_clients", name: "customer_orders_create", displayName: "Créer commandes clients", description: "Création de nouvelles commandes clients", action: "create", resource: "customer_orders", isSystem: true },
{ category: "commandes_clients", name: "customer_orders_update", displayName: "Modifier commandes clients", description: "Modification des commandes clients existantes", action: "update", resource: "customer_orders", isSystem: true },
{ category: "commandes_clients", name: "customer_orders_delete", displayName: "Supprimer commandes clients", description: "Suppression de commandes clients", action: "delete", resource: "customer_orders", isSystem: true },
{ category: "commandes_clients", name: "customer_orders_print", displayName: "Imprimer étiquettes", description: "Impression d'étiquettes de commandes clients", action: "print", resource: "customer_orders", isSystem: true },
{ category: "commandes_clients", name: "customer_orders_notify", displayName: "Notifier clients", description: "Envoi de notifications aux clients", action: "notify", resource: "customer_orders", isSystem: true },
// DLC Management permissions
{ category: "gestion_dlc", name: "dlc_read", displayName: "Voir produits DLC", description: "Accès en lecture aux produits avec date limite de consommation", action: "read", resource: "dlc", isSystem: true },
{ category: "gestion_dlc", name: "dlc_create", displayName: "Créer produits DLC", description: "Création de nouveaux produits DLC", action: "create", resource: "dlc", isSystem: true },
{ category: "gestion_dlc", name: "dlc_update", displayName: "Modifier produits DLC", description: "Modification des produits DLC existants", action: "update", resource: "dlc", isSystem: true },
{ category: "gestion_dlc", name: "dlc_delete", displayName: "Supprimer produits DLC", description: "Suppression de produits DLC", action: "delete", resource: "dlc", isSystem: true },
{ category: "gestion_dlc", name: "dlc_validate", displayName: "Valider produits DLC", description: "Validation et vérification des produits DLC", action: "validate", resource: "dlc", isSystem: true },
{ category: "gestion_dlc", name: "dlc_print", displayName: "Imprimer étiquettes DLC", description: "Impression d'étiquettes pour produits DLC", action: "print", resource: "dlc", isSystem: true },
{ category: "gestion_dlc", name: "dlc_stats", displayName: "Voir statistiques DLC", description: "Accès aux statistiques des produits DLC", action: "stats", resource: "dlc", isSystem: true },
// Task Management permissions
{ category: "gestion_taches", name: "tasks_read", displayName: "Voir tâches", description: "Accès en lecture aux tâches", action: "read", resource: "tasks", isSystem: true },
{ category: "gestion_taches", name: "tasks_create", displayName: "Créer tâches", description: "Création de nouvelles tâches", action: "create", resource: "tasks", isSystem: true },
{ category: "gestion_taches", name: "tasks_update", displayName: "Modifier tâches", description: "Modification des tâches existantes", action: "update", resource: "tasks", isSystem: true },
{ category: "gestion_taches", name: "tasks_delete", displayName: "Supprimer tâches", description: "Suppression de tâches", action: "delete", resource: "tasks", isSystem: true },
{ category: "gestion_taches", name: "tasks_assign", displayName: "Assigner tâches", description: "Attribution de tâches aux utilisateurs", action: "assign", resource: "tasks", isSystem: true },
// Users permissions
{ category: "utilisateurs", name: "users_read", displayName: "Voir utilisateurs", description: "Accès en lecture aux utilisateurs", action: "read", resource: "users", isSystem: true },
{ category: "utilisateurs", name: "users_create", displayName: "Créer utilisateurs", description: "Création de nouveaux utilisateurs", action: "create", resource: "users", isSystem: true },
{ category: "utilisateurs", name: "users_update", displayName: "Modifier utilisateurs", description: "Modification des utilisateurs existants", action: "update", resource: "users", isSystem: true },
{ category: "utilisateurs", name: "users_delete", displayName: "Supprimer utilisateurs", description: "Suppression d'utilisateurs", action: "delete", resource: "users", isSystem: true },
// Role Management permissions
{ category: "gestion_roles", name: "roles_read", displayName: "Voir rôles", description: "Accès en lecture aux rôles", action: "read", resource: "roles", isSystem: true },
{ category: "gestion_roles", name: "roles_create", displayName: "Créer rôles", description: "Création de nouveaux rôles", action: "create", resource: "roles", isSystem: true },
{ category: "gestion_roles", name: "roles_update", displayName: "Modifier rôles", description: "Modification des rôles existants", action: "update", resource: "roles", isSystem: true },
{ category: "gestion_roles", name: "roles_delete", displayName: "Supprimer rôles", description: "Suppression de rôles", action: "delete", resource: "roles", isSystem: true },
{ category: "gestion_roles", name: "roles_assign", displayName: "Assigner rôles", description: "Attribution de rôles aux utilisateurs", action: "assign", resource: "roles", isSystem: true },
// System administration permissions
{ category: "administration", name: "system_admin", displayName: "Administration système", description: "Accès complet à l'administration du système", action: "admin", resource: "system", isSystem: true },
{ category: "administration", name: "nocodb_config", displayName: "Configuration NocoDB", description: "Gestion de la configuration NocoDB", action: "config", resource: "nocodb", isSystem: true },
];
// Create all permissions
const createdPermissions = [];
for (const permData of permissions) {
const permission = await storage.createPermission(permData);
createdPermissions.push(permission);
}
console.log(`Created ${createdPermissions.length} permissions`);
// Assign permissions to roles
// Super Admin gets all permissions
const allPermissionIds = createdPermissions.map(p => p.id);
await storage.setRolePermissions(adminRole.id, allPermissionIds);
console.log(`Assigned all permissions to Super Admin role`);
// Manager gets most permissions except system admin and role management, but includes DLC validate
const managerPermissions = createdPermissions.filter(p =>
!p.name.startsWith('roles_') &&
!p.name.startsWith('system_') &&
p.name !== 'users_delete'
).map(p => p.id);
await storage.setRolePermissions(managerRole.id, managerPermissions);
console.log(`Assigned ${managerPermissions.length} permissions to Manager role`);
// Employee gets basic read permissions, customer orders and DLC read/create/update
const employeePermissions = createdPermissions.filter(p =>
p.action === 'read' ||
p.name.startsWith('customer_orders_') ||
p.name.startsWith('dlc_') && (p.action === 'read' || p.action === 'create' || p.action === 'update') ||
(p.category === 'livraisons' && (p.action === 'create' || p.action === 'update'))
).map(p => p.id);
await storage.setRolePermissions(employeeRole.id, employeePermissions);
console.log(`Assigned ${employeePermissions.length} permissions to Employee role`);
console.log("✅ Roles and permissions initialization completed successfully");
} catch (error) {
console.error("❌ Error initializing roles and permissions:", error);
throw error;
}
}
+117
View File
@@ -0,0 +1,117 @@
// Middleware de permissions hardcodées
// Remplace l'ancien système flexible basé sur la base de données
import type { Request, Response, NextFunction } from 'express';
import { hasPermission, hasModuleAccess } from '@shared/permissions';
import type { Permission, Module } from '@shared/permissions';
// Interface pour les requêtes authentifiées
interface AuthRequest extends Request {
user: {
id: string;
role: string;
claims?: { sub: string };
};
}
// Middleware pour vérifier l'accès à un module
export function requireModuleAccess(module: Module) {
return (req: AuthRequest, res: Response, next: NextFunction) => {
const userRole = req.user?.role;
if (!userRole) {
return res.status(401).json({ message: "Utilisateur non authentifié" });
}
if (!hasModuleAccess(userRole, module)) {
return res.status(403).json({
message: `Accès refusé au module ${module}`,
required: "Permission d'accès au module",
userRole
});
}
next();
};
}
// Middleware pour vérifier une permission spécifique
export function requirePermission(module: Module, action: Permission) {
return (req: AuthRequest, res: Response, next: NextFunction) => {
const userRole = req.user?.role;
if (!userRole) {
return res.status(401).json({ message: "Utilisateur non authentifié" });
}
if (!hasPermission(userRole, module, action)) {
return res.status(403).json({
message: `Permission refusée: ${action} sur ${module}`,
required: `${action} permission on ${module}`,
userRole
});
}
next();
};
}
// Middleware combiné: accès module + permission
export function requireModulePermission(module: Module, action: Permission) {
return (req: AuthRequest, res: Response, next: NextFunction) => {
const userRole = req.user?.role;
if (!userRole) {
return res.status(401).json({ message: "Utilisateur non authentifié" });
}
// Vérifier l'accès au module ET la permission
if (!hasModuleAccess(userRole, module) || !hasPermission(userRole, module, action)) {
return res.status(403).json({
message: `Accès refusé: ${action} sur module ${module}`,
required: `${action} permission on ${module} module`,
userRole
});
}
next();
};
}
// Helper: vérifie si l'utilisateur est admin
export function requireAdmin(req: AuthRequest, res: Response, next: NextFunction) {
const userRole = req.user?.role;
if (!userRole) {
return res.status(401).json({ message: "Utilisateur non authentifié" });
}
if (userRole !== 'admin') {
return res.status(403).json({
message: "Accès réservé aux administrateurs",
required: "admin role",
userRole
});
}
next();
}
// Helper: vérifie si l'utilisateur est admin ou directeur
export function requireAdminOrDirecteur(req: AuthRequest, res: Response, next: NextFunction) {
const userRole = req.user?.role;
if (!userRole) {
return res.status(401).json({ message: "Utilisateur non authentifié" });
}
if (!['admin', 'directeur'].includes(userRole)) {
return res.status(403).json({
message: "Accès réservé aux administrateurs et directeurs",
required: "admin or directeur role",
userRole
});
}
next();
}
+4 -528
View File
@@ -2,6 +2,7 @@ import type { Express } from "express";
import { createServer, type Server } from "http";
import { storage } from "./storage";
import { setupLocalAuth, requireAuth } from "./localAuth";
import { requireModulePermission, requireAdmin } from "./permissions";
console.log('🔍 Using development storage and authentication');
@@ -19,9 +20,6 @@ import {
insertCustomerOrderSchema,
insertDlcProductSchema,
insertDlcProductFrontendSchema,
insertRoleSchema,
insertPermissionSchema,
insertUserRoleSchema,
insertTaskSchema
} from "@shared/schema";
import { z } from "zod";
@@ -853,9 +851,9 @@ export async function registerRoutes(app: Express): Promise<Server> {
return res.status(403).json({ message: "Access denied" });
}
// Get all users with roles and groups for admin
const usersWithRoles = await storage.getUsersWithRolesAndGroups();
const safeUsers = Array.isArray(usersWithRoles) ? usersWithRoles : [];
// Get all users with groups for admin
const allUsers = await storage.getUsers();
const safeUsers = Array.isArray(allUsers) ? allUsers : [];
console.log('🔐 API /api/users - Returning:', { isArray: Array.isArray(safeUsers), length: safeUsers.length });
res.json(safeUsers);
@@ -1340,129 +1338,14 @@ export async function registerRoutes(app: Express): Promise<Server> {
// Role Management API Routes
// Get all roles
app.get('/api/roles', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Access denied" });
}
const roles = await storage.getRoles();
res.json(Array.isArray(roles) ? roles : []);
} catch (error) {
console.error("Error fetching roles:", error);
res.status(500).json([]);
}
});
// Get all permissions
app.get('/api/permissions', isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims ? req.user.claims.sub : req.user.id;
console.log(`🔍 ${isProduction ? 'PRODUCTION' : 'DEV'} Permissions API - User ID:`, userId);
const user = await storage.getUserWithGroups(userId);
console.log(`👤 ${isProduction ? 'PRODUCTION' : 'DEV'} Permissions API - User found:`, user ? user.role : 'NOT FOUND');
if (!user || user.role !== 'admin') {
console.log(`❌ ${isProduction ? 'PRODUCTION' : 'DEV'} Permissions API - Access denied, user role:`, user?.role);
return res.status(403).json({ message: "Accès refusé - droits administrateur requis" });
}
console.log(`🔍 ${isProduction ? 'PRODUCTION' : 'DEV'} Fetching all permissions...`);
const permissions = await storage.getPermissions();
console.log(`📝 ${isProduction ? 'PRODUCTION' : 'DEV'} Permissions fetched:`, permissions.length, "items");
console.log(`🏷️ ${isProduction ? 'PRODUCTION' : 'DEV'} Categories found:`, [...new Set(permissions.map(p => p.category))]);
console.log(`🔧 ${isProduction ? 'PRODUCTION' : 'DEV'} DLC permissions:`, permissions.filter(p => p.category === 'gestion_dlc').map(p => p.name));
// 🎯 VÉRIFICATION SPÉCIFIQUE PERMISSIONS TÂCHES
const taskPermissions = permissions.filter(p => p.category === 'gestion_taches');
console.log(`📋 ${isProduction ? 'PRODUCTION' : 'DEV'} Task permissions found:`, taskPermissions.length);
if (taskPermissions.length > 0) {
console.log(`📋 ${isProduction ? 'PRODUCTION' : 'DEV'} Task permissions details:`);
taskPermissions.forEach(p => {
console.log(` - ID: ${p.id}, Name: ${p.name}, DisplayName: "${p.displayName}", Category: ${p.category}`);
});
} else {
console.log(`❌ ${isProduction ? 'PRODUCTION' : 'DEV'} NO TASK PERMISSIONS FOUND - This explains the frontend issue!`);
}
res.json(Array.isArray(permissions) ? permissions : []);
} catch (error) {
console.error("❌ Error fetching permissions:", error);
res.status(500).json([]);
}
});
// Get permissions for a specific role
app.get('/api/roles/:id/permissions', isAuthenticated, async (req: any, res) => {
console.log("🚀 ROLE PERMISSIONS ROUTE CALLED - Starting");
try {
const userId = req.user.claims ? req.user.claims.sub : req.user.id;
console.log("🔍 ROLE PERMISSIONS - User ID:", userId);
const user = await storage.getUserWithGroups(userId);
console.log("👤 ROLE PERMISSIONS - User found:", user ? `${user.role} (${user.username})` : 'NOT FOUND');
if (!user || user.role !== 'admin') {
console.log("❌ ROLE PERMISSIONS - Access denied, user role:", user?.role);
return res.status(403).json({ message: "Access denied" });
}
const roleId = parseInt(req.params.id);
console.log("🔍 ROLE PERMISSIONS - Fetching for role ID:", roleId);
console.log("🔍 ROLE PERMISSIONS - Storage type:", storage.constructor.name);
const rolePermissions = await storage.getRolePermissions(roleId);
console.log("📝 ROLE PERMISSIONS - Fetched:", rolePermissions?.length || 0, "items");
if (rolePermissions && rolePermissions.length > 0) {
console.log("🏷️ ROLE PERMISSIONS - Sample:", rolePermissions.slice(0, 2));
console.log("🔍 ROLE PERMISSIONS - Full structure sample:", JSON.stringify(rolePermissions[0], null, 2));
// Debug spécifique pour les tâches
const taskPermissions = rolePermissions.filter(rp => rp.permission && rp.permission.category === 'gestion_taches');
console.log("🎯 ROLE PERMISSIONS - Task permissions:", taskPermissions.length);
taskPermissions.forEach(tp => {
console.log(` - Task permission: ${tp.permission.name} (${tp.permission.displayName})`);
});
} else {
console.log("⚠️ ROLE PERMISSIONS - No permissions found for role", roleId);
}
res.json(rolePermissions || []);
} catch (error) {
console.error("❌ ROLE PERMISSIONS - Error:", error);
res.status(500).json({ message: "Failed to fetch role permissions" });
}
});
// Set permissions for a role
app.post('/api/roles/:id/permissions', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Access denied" });
}
const roleId = parseInt(req.params.id);
const { permissionIds } = req.body;
if (!Array.isArray(permissionIds)) {
return res.status(400).json({ message: "permissionIds must be an array" });
}
await storage.setRolePermissions(roleId, permissionIds);
console.log(`✅ Permissions updated for role ${roleId}:`, permissionIds);
res.json({ message: "Permissions updated successfully" });
} catch (error) {
console.error("Error setting role permissions:", error);
res.status(500).json({ message: "Failed to update permissions" });
}
});
// Set roles for a user
app.post('/api/users/:userId/roles', isAuthenticated, async (req: any, res) => {
@@ -1672,208 +1555,19 @@ export async function registerRoutes(app: Express): Promise<Server> {
// ===== ROLE MANAGEMENT ROUTES =====
// Roles routes
app.get('/api/roles', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Insufficient permissions" });
}
const roles = await storage.getRoles();
res.json(roles);
} catch (error) {
console.error("Error fetching roles:", error);
res.status(500).json({ message: "Failed to fetch roles" });
}
});
app.get('/api/roles/:id', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Insufficient permissions" });
}
const id = parseInt(req.params.id);
const role = await storage.getRoleWithPermissions(id);
if (!role) {
return res.status(404).json({ message: "Role not found" });
}
res.json(role);
} catch (error) {
console.error("Error fetching role:", error);
res.status(500).json({ message: "Failed to fetch role" });
}
});
app.post('/api/roles', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Insufficient permissions" });
}
const data = insertRoleSchema.parse(req.body);
const role = await storage.createRole(data);
res.json(role);
} catch (error) {
console.error("Error creating role:", error);
res.status(500).json({ message: "Failed to create role" });
}
});
app.put('/api/roles/:id', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Insufficient permissions" });
}
const id = parseInt(req.params.id);
const data = insertRoleSchema.partial().parse(req.body);
const role = await storage.updateRole(id, data);
res.json(role);
} catch (error) {
console.error("Error updating role:", error);
res.status(500).json({ message: "Failed to update role" });
}
});
app.delete('/api/roles/:id', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Insufficient permissions" });
}
const id = parseInt(req.params.id);
// Check if role is system role
const role = await storage.getRole(id);
if (role?.isSystem) {
return res.status(400).json({ message: "Cannot delete system role" });
}
await storage.deleteRole(id);
res.json({ message: "Role deleted successfully" });
} catch (error) {
console.error("Error deleting role:", error);
res.status(500).json({ message: "Failed to delete role" });
}
});
app.get('/api/permissions/category/:category', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Insufficient permissions" });
}
const category = req.params.category;
const permissions = await storage.getPermissionsByCategory(category);
res.json(permissions);
} catch (error) {
console.error("Error fetching permissions by category:", error);
res.status(500).json({ message: "Failed to fetch permissions" });
}
});
app.post('/api/permissions', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Insufficient permissions" });
}
const data = insertPermissionSchema.parse(req.body);
const permission = await storage.createPermission(data);
res.json(permission);
} catch (error) {
console.error("Error creating permission:", error);
res.status(500).json({ message: "Failed to create permission" });
}
});
app.put('/api/permissions/:id', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Insufficient permissions" });
}
const id = parseInt(req.params.id);
const data = insertPermissionSchema.partial().parse(req.body);
const permission = await storage.updatePermission(id, data);
res.json(permission);
} catch (error) {
console.error("Error updating permission:", error);
res.status(500).json({ message: "Failed to update permission" });
}
});
app.delete('/api/permissions/:id', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Insufficient permissions" });
}
const id = parseInt(req.params.id);
// Check if permission is system permission
const permission = await storage.getPermission(id);
if (permission?.isSystem) {
return res.status(400).json({ message: "Cannot delete system permission" });
}
await storage.deletePermission(id);
res.json({ message: "Permission deleted successfully" });
} catch (error) {
console.error("Error deleting permission:", error);
res.status(500).json({ message: "Failed to delete permission" });
}
});
// Role-Permission association routes
app.get('/api/roles/:roleId/permissions', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Insufficient permissions" });
}
const roleId = parseInt(req.params.roleId);
const rolePermissions = await storage.getRolePermissions(roleId);
res.json(rolePermissions);
} catch (error) {
console.error("Error fetching role permissions:", error);
res.status(500).json({ message: "Failed to fetch role permissions" });
}
});
app.put('/api/roles/:roleId/permissions', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Insufficient permissions" });
}
const roleId = parseInt(req.params.roleId);
const { permissionIds } = req.body;
if (!Array.isArray(permissionIds)) {
return res.status(400).json({ message: "permissionIds must be an array" });
}
await storage.setRolePermissions(roleId, permissionIds);
res.json({ message: "Role permissions updated successfully" });
} catch (error) {
console.error("Error setting role permissions:", error);
res.status(500).json({ message: "Failed to set role permissions" });
}
});
// User-Role association routes
app.get('/api/users/:userId/roles', isAuthenticated, async (req: any, res) => {
@@ -2442,23 +2136,6 @@ export async function registerRoutes(app: Express): Promise<Server> {
});
// Debug endpoint to check permissions without auth (temporary)
app.get('/api/debug/permissions-status', async (req: any, res) => {
try {
const permissions = await storage.getPermissions();
const taskPermissions = permissions.filter(p => p.category === 'gestion_taches');
const samplePermissions = permissions.slice(0, 5);
res.json({
total: permissions.length,
taskPermissions: taskPermissions,
samplePermissions: samplePermissions,
categories: [...new Set(permissions.map(p => p.category))].sort(),
timestamp: new Date().toISOString()
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Debug endpoint to check current user auth status
app.get('/api/debug/auth-status', isAuthenticated, async (req: any, res) => {
@@ -2486,34 +2163,6 @@ export async function registerRoutes(app: Express): Promise<Server> {
});
// Debug endpoint to check task permissions specifically
app.get('/api/debug/task-permissions', isAuthenticated, async (req: any, res) => {
try {
console.log('🔍 DEV TASK PERMISSIONS DEBUG');
const permissions = await storage.getPermissions();
const taskPermissions = permissions.filter(p => p.category === 'gestion_taches');
console.log('📋 DEV Task permissions found:', taskPermissions.length);
taskPermissions.forEach(p => {
console.log(` - ID: ${p.id}, Name: ${p.name}, DisplayName: "${p.displayName}", Category: ${p.category}`);
});
res.json({
environment: 'development',
storage: taskPermissions,
allCategories: [...new Set(permissions.map(p => p.category))].sort(),
summary: {
total_permissions: permissions.length,
task_permissions_count: taskPermissions.length,
has_gestion_taches: taskPermissions.length > 0,
timestamp: new Date().toISOString()
}
});
} catch (error) {
console.error('❌ Task permissions debug error:', error);
res.status(500).json({ message: "Debug failed", error: error.message });
}
});
// Test CRITIQUE - Comparaison des storages pour permissions tâches
app.get('/api/debug/compare-storages', isAuthenticated, async (req: any, res) => {
@@ -2521,12 +2170,10 @@ export async function registerRoutes(app: Express): Promise<Server> {
console.log('🔍 CRITICAL TEST - Comparing dev vs prod storage for task permissions');
// Test développement
const devPermissions = await devStorage.getPermissions();
const devTaskPerms = devPermissions.filter(p => p.category === 'gestion_taches');
console.log('📋 DEV storage - Task permissions:', devTaskPerms.length);
// Test production
const prodPermissions = await prodStorage.getPermissions();
const prodTaskPerms = prodPermissions.filter(p => p.category === 'gestion_taches');
console.log('📋 PROD storage - Task permissions:', prodTaskPerms.length);
@@ -2553,181 +2200,10 @@ export async function registerRoutes(app: Express): Promise<Server> {
});
// ENDPOINT TEMPORAIRE - Test production storage avec gestion d'erreur détaillée
app.get('/api/debug/test-production-permissions', isAuthenticated, async (req: any, res) => {
console.log('🚨 STARTING PRODUCTION STORAGE TEST');
try {
// Test basique de connexion d'abord
console.log('🔍 Testing production storage connection...');
if (!prodStorage) {
throw new Error('Production storage is not available');
}
console.log('🔍 Production storage object exists, testing getPermissions...');
const productionPermissions = await prodStorage.getPermissions();
console.log('✅ Production storage getPermissions() succeeded:', productionPermissions.length, 'permissions');
const taskPermissions = productionPermissions.filter(p => p.category === 'gestion_taches');
console.log('🏭 PRODUCTION STORAGE - Task permissions found:', taskPermissions.length);
if (taskPermissions.length > 0) {
console.log('📋 Production task permissions details:');
taskPermissions.forEach(p => {
console.log(` - ID: ${p.id}, Name: ${p.name}, DisplayName: "${p.displayName}", Category: ${p.category}`);
});
} else {
console.log('❌ NO TASK PERMISSIONS found in production storage!');
}
res.json({
success: true,
environment: 'forced_production_storage',
total_permissions: productionPermissions.length,
task_permissions_count: taskPermissions.length,
task_permissions: taskPermissions,
all_categories: [...new Set(productionPermissions.map(p => p.category))].sort(),
has_gestion_taches: taskPermissions.length > 0,
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('❌ CRITICAL ERROR in production storage test:', error);
console.error('❌ Error type:', typeof error);
console.error('❌ Error message:', error?.message);
console.error('❌ Error stack:', error?.stack);
res.status(500).json({
success: false,
message: "Production storage test failed",
error: error?.message || 'Unknown error',
errorType: typeof error,
timestamp: new Date().toISOString()
});
}
});
// Debug/Fix endpoint for production permission issues
app.post('/api/debug/fix-production-permissions', isAuthenticated, async (req: any, res) => {
try {
console.log('🔧 FIXING PRODUCTION PERMISSIONS...');
// Only allow admin users to run fixes
const userId = req.user.claims ? req.user.claims.sub : req.user.id;
const user = await storage.getUserWithGroups(userId);
if (user?.role !== 'admin') {
return res.status(403).json({ message: "Admin access required" });
}
const fixes = [];
// 1. Fix missing display names for permissions
console.log('📝 Fixing permission display names...');
const permissions = await storage.getPermissions();
let fixedDisplayNames = 0;
for (const permission of permissions) {
if (!permission.displayName || permission.displayName === permission.name) {
// Create proper French display names based on name patterns
let displayName = permission.name;
// Pattern-based translations
if (permission.name.includes('_read')) displayName = displayName.replace('_read', ' - Voir');
if (permission.name.includes('_create')) displayName = displayName.replace('_create', ' - Créer');
if (permission.name.includes('_update')) displayName = displayName.replace('_update', ' - Modifier');
if (permission.name.includes('_delete')) displayName = displayName.replace('_delete', ' - Supprimer');
if (permission.name.includes('_validate')) displayName = displayName.replace('_validate', ' - Valider');
if (permission.name.includes('_print')) displayName = displayName.replace('_print', ' - Imprimer');
if (permission.name.includes('_assign')) displayName = displayName.replace('_assign', ' - Assigner');
if (permission.name.includes('_notify')) displayName = displayName.replace('_notify', ' - Notifier');
if (permission.name.includes('_stats')) displayName = displayName.replace('_stats', ' - Statistiques');
// Resource-based translations
displayName = displayName
.replace('dashboard', 'tableau de bord')
.replace('groups', 'magasins')
.replace('suppliers', 'fournisseurs')
.replace('orders', 'commandes')
.replace('deliveries', 'livraisons')
.replace('publicities', 'publicités')
.replace('customer_orders', 'commandes clients')
.replace('tasks', 'tâches')
.replace('users', 'utilisateurs')
.replace('roles', 'rôles')
.replace('permissions', 'permissions')
.replace('dlc', 'produits DLC')
.replace('calendar', 'calendrier')
.replace('reconciliation', 'rapprochement')
.replace('bl_reconciliation', 'rapprochement BL')
.replace('nocodb_config', 'config NocoDB')
.replace('system', 'système');
// Capitalize first letter
displayName = displayName.charAt(0).toUpperCase() + displayName.slice(1);
try {
await storage.updatePermission(permission.id, {
...permission,
displayName: displayName
});
fixedDisplayNames++;
} catch (updateError) {
console.error(`Error updating permission ${permission.id}:`, updateError);
}
}
}
fixes.push(`Fixed ${fixedDisplayNames} permission display names`);
// 2. Ensure missing task permissions exist
console.log('🎯 Checking task management permissions...');
const taskPermissions = [
{ category: "gestion_taches", name: "tasks_read", displayName: "Voir tâches", description: "Accès en lecture aux tâches", action: "read", resource: "tasks" },
{ category: "gestion_taches", name: "tasks_create", displayName: "Créer tâches", description: "Création de nouvelles tâches", action: "create", resource: "tasks" },
{ category: "gestion_taches", name: "tasks_update", displayName: "Modifier tâches", description: "Modification des tâches existantes", action: "update", resource: "tasks" },
{ category: "gestion_taches", name: "tasks_delete", displayName: "Supprimer tâches", description: "Suppression de tâches", action: "delete", resource: "tasks" },
{ category: "gestion_taches", name: "tasks_assign", displayName: "Assigner tâches", description: "Attribution de tâches aux utilisateurs", action: "assign", resource: "tasks" }
];
let createdTaskPermissions = 0;
for (const taskPerm of taskPermissions) {
const existing = permissions.find(p => p.name === taskPerm.name);
if (!existing) {
try {
await storage.createPermission({
...taskPerm,
isSystem: true
});
createdTaskPermissions++;
} catch (createError) {
console.error(`Error creating task permission ${taskPerm.name}:`, createError);
}
}
}
if (createdTaskPermissions > 0) {
fixes.push(`Created ${createdTaskPermissions} missing task permissions`);
}
console.log('✅ Production permissions fix completed');
res.json({
success: true,
message: 'Production permission fixes applied successfully',
fixes: fixes,
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('❌ Error fixing production permissions:', error);
res.status(500).json({
success: false,
message: 'Failed to fix production permissions',
error: error.message
});
}
});
const httpServer = createServer(app);
return httpServer;
+1 -47
View File
@@ -7,10 +7,6 @@ import {
userGroups,
publicities,
publicityParticipations,
roles,
permissions,
rolePermissions,
userRoles,
customerOrders,
dlcProducts,
tasks,
@@ -34,16 +30,6 @@ import {
type PublicityParticipation,
type InsertPublicityParticipation,
type PublicityWithRelations,
type Role,
type InsertRole,
type Permission,
type InsertPermission,
type RolePermission,
type InsertRolePermission,
type UserRole,
type InsertUserRole,
type RoleWithPermissions,
type UserWithRoles,
nocodbConfig,
type NocodbConfig,
type InsertNocodbConfig,
@@ -69,8 +55,7 @@ export interface IStorage {
upsertUser(user: UpsertUser): Promise<User>;
getUserWithGroups(id: string): Promise<UserWithGroups | undefined>;
getUsers(): Promise<User[]>;
getUsersWithRoles(): Promise<UserWithRoles[]>;
getUsersWithRolesAndGroups(): Promise<(UserWithRoles & { userGroups: any[] })[]>;
createUser(user: UpsertUser): Promise<User>;
updateUser(id: string, user: Partial<UpsertUser>): Promise<User>;
deleteUser(id: string): Promise<void>;
@@ -156,39 +141,8 @@ export interface IStorage {
validateDlcProduct(id: number, validatedBy: string): Promise<DlcProductFrontend>;
getDlcStats(groupIds?: number[]): Promise<{ active: number; expiringSoon: number; expired: number; }>;
// Role management operations
getRoles(): Promise<Role[]>;
getRole(id: number): Promise<Role | undefined>;
createRole(role: InsertRole): Promise<Role>;
updateRole(id: number, role: Partial<InsertRole>): Promise<Role>;
deleteRole(id: number): Promise<void>;
getRoleWithPermissions(id: number): Promise<RoleWithPermissions | undefined>;
// Permission management operations
getPermissions(): Promise<Permission[]>;
getPermission(id: number): Promise<Permission | undefined>;
createPermission(permission: InsertPermission): Promise<Permission>;
updatePermission(id: number, permission: Partial<InsertPermission>): Promise<Permission>;
deletePermission(id: number): Promise<void>;
getPermissionsByCategory(category: string): Promise<Permission[]>;
// Role-Permission association operations
getRolePermissions(roleId: number): Promise<RolePermission[]>;
setRolePermissions(roleId: number, permissionIds: number[]): Promise<void>;
addPermissionToRole(roleId: number, permissionId: number): Promise<RolePermission>;
removePermissionFromRole(roleId: number, permissionId: number): Promise<void>;
// User-Role association operations
getUserRoles(userId: string): Promise<UserRole[]>;
setUserRoles(userId: string, roleIds: number[], assignedBy: string): Promise<void>;
addRoleToUser(userId: string, roleId: number, assignedBy: string): Promise<UserRole>;
removeRoleFromUser(userId: string, roleId: number): Promise<void>;
getUserWithRoles(userId: string): Promise<UserWithRoles | undefined>;
// Permission checking operations
userHasPermission(userId: string, permissionName: string): Promise<boolean>;
userHasRole(userId: string, roleName: string): Promise<boolean>;
getUserEffectivePermissions(userId: string): Promise<Permission[]>;
// Task operations
getTasks(groupIds?: number[]): Promise<any[]>;