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

+149
View File
@@ -0,0 +1,149 @@
// Système de permissions hardcodé par module
// Remplace l'ancien système flexible basé sur la base de données
export type Permission = 'view' | 'create' | 'edit' | 'delete' | 'validate';
export type Role = 'admin' | 'directeur' | 'manager' | 'employee';
export type Module =
| 'dashboard'
| 'calendar'
| 'orders'
| 'deliveries'
| 'reconciliation'
| 'publicity'
| 'customer-orders'
| 'dlc'
| 'tasks';
// Définition des permissions par module et par rôle
const PERMISSIONS: Record<Module, Record<Role, Permission[]>> = {
// Tableau de bord - tous les rôles peuvent le voir
dashboard: {
admin: ['view'],
directeur: ['view'],
manager: ['view'],
employee: ['view']
},
// Calendrier - Admin/Directeur tout, Manager tout sauf delete, Employé view
calendar: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit', 'delete'],
manager: ['view', 'create', 'edit'],
employee: ['view']
},
// Commandes - Admin/Directeur tout, Manager tout sauf delete, Employé view
orders: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit', 'delete'],
manager: ['view', 'create', 'edit'],
employee: ['view']
},
// Livraisons - Admin/Directeur tout, Manager tout sauf delete, Employé view
deliveries: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit', 'delete'],
manager: ['view', 'create', 'edit'],
employee: ['view']
},
// Rapprochement - Admin tout, Directeur tout sauf delete, Manager/Employé rien
reconciliation: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit'],
manager: [],
employee: []
},
// Publicité - Admin tout, autres view seulement
publicity: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view'],
manager: ['view'],
employee: ['view']
},
// Commandes client - Admin/Directeur tout, Manager tout sauf delete, Employé view + create
'customer-orders': {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit', 'delete'],
manager: ['view', 'create', 'edit'],
employee: ['view', 'create']
},
// DLC - Admin/Directeur tout, Manager tout sauf delete, Employé create + view
dlc: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit', 'delete'],
manager: ['view', 'create', 'edit'],
employee: ['view', 'create']
},
// Tâches - Admin/Directeur tout, Manager view + validate, Employé view
tasks: {
admin: ['view', 'create', 'edit', 'delete', 'validate'],
directeur: ['view', 'create', 'edit', 'delete', 'validate'],
manager: ['view', 'validate'],
employee: ['view']
}
};
// Hook pour vérifier les permissions
export function usePermissions(userRole?: string) {
const role = userRole?.toLowerCase() as Role;
// Vérifie si l'utilisateur peut accéder à un module
const canAccessModule = (module: Module): boolean => {
if (!role || !PERMISSIONS[module]) return false;
const permissions = PERMISSIONS[module][role] || [];
return permissions.length > 0;
};
// Vérifie si l'utilisateur peut effectuer une action sur un module
const canPerformAction = (module: Module, action: Permission): boolean => {
if (!role || !PERMISSIONS[module]) return false;
const permissions = PERMISSIONS[module][role] || [];
return permissions.includes(action);
};
// Retourne toutes les permissions pour un module
const getModulePermissions = (module: Module): Permission[] => {
if (!role || !PERMISSIONS[module]) return [];
return PERMISSIONS[module][role] || [];
};
// Vérifie si l'utilisateur peut voir le module (a au moins une permission)
const canView = (module: Module): boolean => canPerformAction(module, 'view');
const canCreate = (module: Module): boolean => canPerformAction(module, 'create');
const canEdit = (module: Module): boolean => canPerformAction(module, 'edit');
const canDelete = (module: Module): boolean => canPerformAction(module, 'delete');
const canValidate = (module: Module): boolean => canPerformAction(module, 'validate');
return {
canAccessModule,
canPerformAction,
getModulePermissions,
canView,
canCreate,
canEdit,
canDelete,
canValidate
};
}
// Fonction utilitaire pour les middlewares serveur
export function hasPermission(userRole: string, module: Module, action: Permission): boolean {
const role = userRole?.toLowerCase() as Role;
if (!role || !PERMISSIONS[module]) return false;
const permissions = PERMISSIONS[module][role] || [];
return permissions.includes(action);
}
// Fonction utilitaire pour vérifier l'accès à un module
export function hasModuleAccess(userRole: string, module: Module): boolean {
const role = userRole?.toLowerCase() as Role;
if (!role || !PERMISSIONS[module]) return false;
const permissions = PERMISSIONS[module][role] || [];
return permissions.length > 0;
}
+1
View File
@@ -12,6 +12,7 @@ import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { apiRequest } from "@/lib/queryClient"; import { apiRequest } from "@/lib/queryClient";
import { getRoleTailwindClasses, getRoleDisplayName } from "@/lib/roleUtils"; import { getRoleTailwindClasses, getRoleDisplayName } from "@/lib/roleUtils";
import { usePermissions } from "@/lib/permissions";
import { isUnauthorizedError } from "@/lib/authUtils"; import { isUnauthorizedError } from "@/lib/authUtils";
import { import {
Search, Search,
+19
View File
@@ -79,6 +79,25 @@ Preferred communication style: Simple, everyday language.
## Recent Changes ## Recent Changes
### August 11, 2025 - Permissions System Overhaul & User Management Improvements
#### Hardcoded Permissions System Implementation
- **Migration from Database to Code**: Replaced flexible database-driven permissions with hardcoded permission system for better performance and control
- **New Architecture**:
- Created `client/src/lib/permissions.ts` and `shared/permissions.ts` with role-based module permissions
- Implemented server-side middleware `server/permissions.ts` for route protection
- Removed database tables: `roles`, `permissions`, `rolePermissions`, `userRoles`
- Cleaned up storage interface and routes from old permission system
- **Permission Matrix by Module**:
- **Dashboard**: All roles can view
- **Calendar/Orders/Deliveries**: Admin/Directeur full access, Manager all except delete, Employee view only
- **Reconciliation**: Admin full access, Directeur all except delete, Manager/Employee no access
- **Publicity**: Admin full access, others view only
- **Customer Orders**: Admin/Directeur full access, Manager all except delete, Employee view + create
- **DLC**: Admin/Directeur full access, Manager all except delete, Employee create + view
- **Tasks**: Admin/Directeur full access, Manager view + validate, Employee view only
- **Benefits**: Improved performance, simplified maintenance, no database queries for permission checks
### August 11, 2025 - User Management Improvements ### August 11, 2025 - User Management Improvements
#### User Deletion Fix #### User Deletion Fix
-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 { createServer, type Server } from "http";
import { storage } from "./storage"; import { storage } from "./storage";
import { setupLocalAuth, requireAuth } from "./localAuth"; import { setupLocalAuth, requireAuth } from "./localAuth";
import { requireModulePermission, requireAdmin } from "./permissions";
console.log('🔍 Using development storage and authentication'); console.log('🔍 Using development storage and authentication');
@@ -19,9 +20,6 @@ import {
insertCustomerOrderSchema, insertCustomerOrderSchema,
insertDlcProductSchema, insertDlcProductSchema,
insertDlcProductFrontendSchema, insertDlcProductFrontendSchema,
insertRoleSchema,
insertPermissionSchema,
insertUserRoleSchema,
insertTaskSchema insertTaskSchema
} from "@shared/schema"; } from "@shared/schema";
import { z } from "zod"; import { z } from "zod";
@@ -853,9 +851,9 @@ export async function registerRoutes(app: Express): Promise<Server> {
return res.status(403).json({ message: "Access denied" }); return res.status(403).json({ message: "Access denied" });
} }
// Get all users with roles and groups for admin // Get all users with groups for admin
const usersWithRoles = await storage.getUsersWithRolesAndGroups(); const allUsers = await storage.getUsers();
const safeUsers = Array.isArray(usersWithRoles) ? usersWithRoles : []; const safeUsers = Array.isArray(allUsers) ? allUsers : [];
console.log('🔐 API /api/users - Returning:', { isArray: Array.isArray(safeUsers), length: safeUsers.length }); console.log('🔐 API /api/users - Returning:', { isArray: Array.isArray(safeUsers), length: safeUsers.length });
res.json(safeUsers); res.json(safeUsers);
@@ -1340,129 +1338,14 @@ export async function registerRoutes(app: Express): Promise<Server> {
// Role Management API Routes
// Get all roles // 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 // 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 // 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 // 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 // Set roles for a user
app.post('/api/users/:userId/roles', isAuthenticated, async (req: any, res) => { 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 ===== // ===== ROLE MANAGEMENT ROUTES =====
// Roles 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 // 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 // User-Role association routes
app.get('/api/users/:userId/roles', isAuthenticated, async (req: any, res) => { 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) // 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 // Debug endpoint to check current user auth status
app.get('/api/debug/auth-status', isAuthenticated, async (req: any, res) => { 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 // 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 // Test CRITIQUE - Comparaison des storages pour permissions tâches
app.get('/api/debug/compare-storages', isAuthenticated, async (req: any, res) => { 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'); console.log('🔍 CRITICAL TEST - Comparing dev vs prod storage for task permissions');
// Test développement // Test développement
const devPermissions = await devStorage.getPermissions();
const devTaskPerms = devPermissions.filter(p => p.category === 'gestion_taches'); const devTaskPerms = devPermissions.filter(p => p.category === 'gestion_taches');
console.log('📋 DEV storage - Task permissions:', devTaskPerms.length); console.log('📋 DEV storage - Task permissions:', devTaskPerms.length);
// Test production // Test production
const prodPermissions = await prodStorage.getPermissions();
const prodTaskPerms = prodPermissions.filter(p => p.category === 'gestion_taches'); const prodTaskPerms = prodPermissions.filter(p => p.category === 'gestion_taches');
console.log('📋 PROD storage - Task permissions:', prodTaskPerms.length); 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 // 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 // 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); const httpServer = createServer(app);
return httpServer; return httpServer;
+1 -47
View File
@@ -7,10 +7,6 @@ import {
userGroups, userGroups,
publicities, publicities,
publicityParticipations, publicityParticipations,
roles,
permissions,
rolePermissions,
userRoles,
customerOrders, customerOrders,
dlcProducts, dlcProducts,
tasks, tasks,
@@ -34,16 +30,6 @@ import {
type PublicityParticipation, type PublicityParticipation,
type InsertPublicityParticipation, type InsertPublicityParticipation,
type PublicityWithRelations, type PublicityWithRelations,
type Role,
type InsertRole,
type Permission,
type InsertPermission,
type RolePermission,
type InsertRolePermission,
type UserRole,
type InsertUserRole,
type RoleWithPermissions,
type UserWithRoles,
nocodbConfig, nocodbConfig,
type NocodbConfig, type NocodbConfig,
type InsertNocodbConfig, type InsertNocodbConfig,
@@ -69,8 +55,7 @@ export interface IStorage {
upsertUser(user: UpsertUser): Promise<User>; upsertUser(user: UpsertUser): Promise<User>;
getUserWithGroups(id: string): Promise<UserWithGroups | undefined>; getUserWithGroups(id: string): Promise<UserWithGroups | undefined>;
getUsers(): Promise<User[]>; getUsers(): Promise<User[]>;
getUsersWithRoles(): Promise<UserWithRoles[]>;
getUsersWithRolesAndGroups(): Promise<(UserWithRoles & { userGroups: any[] })[]>;
createUser(user: UpsertUser): Promise<User>; createUser(user: UpsertUser): Promise<User>;
updateUser(id: string, user: Partial<UpsertUser>): Promise<User>; updateUser(id: string, user: Partial<UpsertUser>): Promise<User>;
deleteUser(id: string): Promise<void>; deleteUser(id: string): Promise<void>;
@@ -156,39 +141,8 @@ export interface IStorage {
validateDlcProduct(id: number, validatedBy: string): Promise<DlcProductFrontend>; validateDlcProduct(id: number, validatedBy: string): Promise<DlcProductFrontend>;
getDlcStats(groupIds?: number[]): Promise<{ active: number; expiringSoon: number; expired: number; }>; 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 // Task operations
getTasks(groupIds?: number[]): Promise<any[]>; getTasks(groupIds?: number[]): Promise<any[]>;
+149
View File
@@ -0,0 +1,149 @@
// Système de permissions hardcodé par module
// Remplace l'ancien système flexible basé sur la base de données
export type Permission = 'view' | 'create' | 'edit' | 'delete' | 'validate';
export type Role = 'admin' | 'directeur' | 'manager' | 'employee';
export type Module =
| 'dashboard'
| 'calendar'
| 'orders'
| 'deliveries'
| 'reconciliation'
| 'publicity'
| 'customer-orders'
| 'dlc'
| 'tasks';
// Définition des permissions par module et par rôle
const PERMISSIONS: Record<Module, Record<Role, Permission[]>> = {
// Tableau de bord - tous les rôles peuvent le voir
dashboard: {
admin: ['view'],
directeur: ['view'],
manager: ['view'],
employee: ['view']
},
// Calendrier - Admin/Directeur tout, Manager tout sauf delete, Employé view
calendar: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit', 'delete'],
manager: ['view', 'create', 'edit'],
employee: ['view']
},
// Commandes - Admin/Directeur tout, Manager tout sauf delete, Employé view
orders: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit', 'delete'],
manager: ['view', 'create', 'edit'],
employee: ['view']
},
// Livraisons - Admin/Directeur tout, Manager tout sauf delete, Employé view
deliveries: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit', 'delete'],
manager: ['view', 'create', 'edit'],
employee: ['view']
},
// Rapprochement - Admin tout, Directeur tout sauf delete, Manager/Employé rien
reconciliation: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit'],
manager: [],
employee: []
},
// Publicité - Admin tout, autres view seulement
publicity: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view'],
manager: ['view'],
employee: ['view']
},
// Commandes client - Admin/Directeur tout, Manager tout sauf delete, Employé view + create
'customer-orders': {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit', 'delete'],
manager: ['view', 'create', 'edit'],
employee: ['view', 'create']
},
// DLC - Admin/Directeur tout, Manager tout sauf delete, Employé create + view
dlc: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit', 'delete'],
manager: ['view', 'create', 'edit'],
employee: ['view', 'create']
},
// Tâches - Admin/Directeur tout, Manager view + validate, Employé view
tasks: {
admin: ['view', 'create', 'edit', 'delete', 'validate'],
directeur: ['view', 'create', 'edit', 'delete', 'validate'],
manager: ['view', 'validate'],
employee: ['view']
}
};
// Hook pour vérifier les permissions
export function usePermissions(userRole?: string) {
const role = userRole?.toLowerCase() as Role;
// Vérifie si l'utilisateur peut accéder à un module
const canAccessModule = (module: Module): boolean => {
if (!role || !PERMISSIONS[module]) return false;
const permissions = PERMISSIONS[module][role] || [];
return permissions.length > 0;
};
// Vérifie si l'utilisateur peut effectuer une action sur un module
const canPerformAction = (module: Module, action: Permission): boolean => {
if (!role || !PERMISSIONS[module]) return false;
const permissions = PERMISSIONS[module][role] || [];
return permissions.includes(action);
};
// Retourne toutes les permissions pour un module
const getModulePermissions = (module: Module): Permission[] => {
if (!role || !PERMISSIONS[module]) return [];
return PERMISSIONS[module][role] || [];
};
// Vérifie si l'utilisateur peut voir le module (a au moins une permission)
const canView = (module: Module): boolean => canPerformAction(module, 'view');
const canCreate = (module: Module): boolean => canPerformAction(module, 'create');
const canEdit = (module: Module): boolean => canPerformAction(module, 'edit');
const canDelete = (module: Module): boolean => canPerformAction(module, 'delete');
const canValidate = (module: Module): boolean => canPerformAction(module, 'validate');
return {
canAccessModule,
canPerformAction,
getModulePermissions,
canView,
canCreate,
canEdit,
canDelete,
canValidate
};
}
// Fonction utilitaire pour les middlewares serveur
export function hasPermission(userRole: string, module: Module, action: Permission): boolean {
const role = userRole?.toLowerCase() as Role;
if (!role || !PERMISSIONS[module]) return false;
const permissions = PERMISSIONS[module][role] || [];
return permissions.includes(action);
}
// Fonction utilitaire pour vérifier l'accès à un module
export function hasModuleAccess(userRole: string, module: Module): boolean {
const role = userRole?.toLowerCase() as Role;
if (!role || !PERMISSIONS[module]) return false;
const permissions = PERMISSIONS[module][role] || [];
return permissions.length > 0;
}
+1 -119
View File
@@ -136,40 +136,7 @@ export const publicityParticipations = pgTable("publicity_participations", {
pk: primaryKey({ columns: [table.publicityId, table.groupId] }) pk: primaryKey({ columns: [table.publicityId, table.groupId] })
})); }));
// Roles - Dynamic role management
export const roles = pgTable("roles", {
id: serial("id").primaryKey(),
name: varchar("name").notNull().unique(),
displayName: varchar("display_name").notNull(),
description: text("description"),
color: varchar("color").default("#6b7280"), // Couleur d'affichage
isSystem: boolean("is_system").default(false), // Rôles système non supprimables
isActive: boolean("is_active").default(true),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Permissions - Available permissions in the system
export const permissions = pgTable("permissions", {
id: serial("id").primaryKey(),
name: varchar("name").notNull().unique(),
displayName: varchar("display_name").notNull(),
description: text("description"),
category: varchar("category").notNull(), // dashboard, orders, deliveries, users, etc.
action: varchar("action").notNull(), // read, create, update, delete, validate
resource: varchar("resource").notNull(), // orders, deliveries, users, etc.
isSystem: boolean("is_system").default(true), // Permissions système
createdAt: timestamp("created_at").defaultNow(),
});
// Role Permissions - Many to many relationship
export const rolePermissions = pgTable("role_permissions", {
roleId: integer("role_id").notNull(),
permissionId: integer("permission_id").notNull(),
createdAt: timestamp("created_at").defaultNow(),
}, (table) => ({
pk: primaryKey({ columns: [table.roleId, table.permissionId] })
}));
// NocoDB configuration globale (une seule instance NocoDB partagée) // NocoDB configuration globale (une seule instance NocoDB partagée)
export const nocodbConfig = pgTable("nocodb_config", { export const nocodbConfig = pgTable("nocodb_config", {
@@ -221,15 +188,7 @@ export const customerOrders = pgTable("customer_orders", {
updatedAt: timestamp("updated_at").defaultNow(), updatedAt: timestamp("updated_at").defaultNow(),
}); });
// User Roles - Many to many relationship (supports multiple roles per user)
export const userRoles = pgTable("user_roles", {
userId: varchar("user_id").notNull(),
roleId: integer("role_id").notNull(),
assignedBy: varchar("assigned_by").notNull(), // Who assigned this role
assignedAt: timestamp("assigned_at").defaultNow(),
}, (table) => ({
pk: primaryKey({ columns: [table.userId, table.roleId] })
}));
// DLC Products (Date Limite de Consommation) // DLC Products (Date Limite de Consommation)
export const dlcProducts = pgTable("dlc_products", { export const dlcProducts = pgTable("dlc_products", {
@@ -273,7 +232,6 @@ export const tasks = pgTable("tasks", {
// Relations // Relations
export const usersRelations = relations(users, ({ many }) => ({ export const usersRelations = relations(users, ({ many }) => ({
userGroups: many(userGroups), userGroups: many(userGroups),
userRoles: many(userRoles),
createdOrders: many(orders), createdOrders: many(orders),
createdDeliveries: many(deliveries), createdDeliveries: many(deliveries),
createdPublicities: many(publicities), createdPublicities: many(publicities),
@@ -368,36 +326,7 @@ export const publicityParticipationsRelations = relations(publicityParticipation
}), }),
})); }));
export const rolesRelations = relations(roles, ({ many }) => ({
userRoles: many(userRoles),
rolePermissions: many(rolePermissions),
}));
export const userRolesRelations = relations(userRoles, ({ one }) => ({
user: one(users, {
fields: [userRoles.userId],
references: [users.id],
}),
role: one(roles, {
fields: [userRoles.roleId],
references: [roles.id],
}),
}));
export const permissionsRelations = relations(permissions, ({ many }) => ({
rolePermissions: many(rolePermissions),
}));
export const rolePermissionsRelations = relations(rolePermissions, ({ one }) => ({
role: one(roles, {
fields: [rolePermissions.roleId],
references: [roles.id],
}),
permission: one(permissions, {
fields: [rolePermissions.permissionId],
references: [permissions.id],
}),
}));
export const customerOrdersRelations = relations(customerOrders, ({ one }) => ({ export const customerOrdersRelations = relations(customerOrders, ({ one }) => ({
group: one(groups, { group: one(groups, {
@@ -502,24 +431,7 @@ export const insertPublicityParticipationSchema = createInsertSchema(publicityPa
createdAt: true, createdAt: true,
}); });
export const insertRoleSchema = createInsertSchema(roles).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertPermissionSchema = createInsertSchema(permissions).omit({
id: true,
createdAt: true,
});
export const insertRolePermissionSchema = createInsertSchema(rolePermissions).omit({
createdAt: true,
});
export const insertUserRoleSchema = createInsertSchema(userRoles).omit({
assignedAt: true,
});
export const insertNocodbConfigSchema = createInsertSchema(nocodbConfig).omit({ export const insertNocodbConfigSchema = createInsertSchema(nocodbConfig).omit({
id: true, id: true,
@@ -606,31 +518,6 @@ export type PublicityWithRelations = Publicity & {
participations: (PublicityParticipation & { group: Group })[]; participations: (PublicityParticipation & { group: Group })[];
}; };
export type Role = typeof roles.$inferSelect;
export type InsertRole = z.infer<typeof insertRoleSchema>;
export type Permission = typeof permissions.$inferSelect;
export type InsertPermission = z.infer<typeof insertPermissionSchema>;
export type RolePermission = typeof rolePermissions.$inferSelect;
export type InsertRolePermission = z.infer<typeof insertRolePermissionSchema>;
export type UserRole = typeof userRoles.$inferSelect;
export type InsertUserRole = z.infer<typeof insertUserRoleSchema>;
export type RoleWithPermissions = Role & {
rolePermissions: (RolePermission & { permission: Permission })[];
};
export type UserWithRoles = User & {
userRoles: (UserRole & { role: Role })[];
};
export type PermissionWithActions = Permission & {
action: string;
resource: string;
};
export type NocodbConfig = typeof nocodbConfig.$inferSelect; export type NocodbConfig = typeof nocodbConfig.$inferSelect;
export type InsertNocodbConfig = z.infer<typeof insertNocodbConfigSchema>; export type InsertNocodbConfig = z.infer<typeof insertNocodbConfigSchema>;
@@ -643,11 +530,6 @@ export type CustomerOrderWithRelations = CustomerOrder & {
supplier: Supplier; supplier: Supplier;
}; };
export type UserWithRole = User & {
dynamicRole?: Role | null;
userGroups: (UserGroup & { group: Group })[];
};
export type DlcProduct = typeof dlcProducts.$inferSelect; export type DlcProduct = typeof dlcProducts.$inferSelect;
export type InsertDlcProduct = z.infer<typeof insertDlcProductSchema>; export type InsertDlcProduct = z.infer<typeof insertDlcProductSchema>;