From 5ea3481dc037cd242e849b0cd4290996242fa9d3 Mon Sep 17 00:00:00 2001
From: michaelschal <35957947-michaelschal@users.noreply.replit.com>
Date: Tue, 12 Aug 2025 08:50:23 +0000
Subject: [PATCH] Restrict employee access to orders and deliveries while
fixing supplier visibility
Fixes bugs that prevented employees from viewing supplier lists and restricted their access to order and delivery creation functionalities.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: b163d4c0-de5e-4f4e-a9c0-aed4c7049718
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/b163d4c0-de5e-4f4e-a9c0-aed4c7049718/9RgkyCi
---
client/src/components/Sidebar.tsx | 4 +-
.../src/components/modals/QuickCreateMenu.tsx | 67 +++++---
client/src/pages/Calendar.tsx | 18 ++-
replit.md | 49 +++++-
server/routes.ts | 10 +-
shared/permissions.ts | 8 +-
test-employee-supplier-access.js | 152 ++++++++++++++++++
7 files changed, 263 insertions(+), 45 deletions(-)
create mode 100644 test-employee-supplier-access.js
diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx
index 2249df9..075e16d 100644
--- a/client/src/components/Sidebar.tsx
+++ b/client/src/components/Sidebar.tsx
@@ -81,13 +81,13 @@ export default function Sidebar() {
path: "/orders",
label: "Commandes",
icon: Package,
- roles: ["admin", "directeur", "manager", "employee"]
+ roles: ["admin", "directeur", "manager"]
},
{
path: "/deliveries",
label: "Livraisons",
icon: Truck,
- roles: ["admin", "directeur", "manager", "employee"]
+ roles: ["admin", "directeur", "manager"]
},
{
path: "/bl-reconciliation",
diff --git a/client/src/components/modals/QuickCreateMenu.tsx b/client/src/components/modals/QuickCreateMenu.tsx
index d617ea2..730983a 100644
--- a/client/src/components/modals/QuickCreateMenu.tsx
+++ b/client/src/components/modals/QuickCreateMenu.tsx
@@ -1,6 +1,8 @@
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Package, Truck } from "lucide-react";
+import { useAuthUnified } from "@/hooks/useAuthUnified";
+import { hasPermission } from "@/lib/permissions";
interface QuickCreateMenuProps {
isOpen: boolean;
@@ -15,6 +17,11 @@ export default function QuickCreateMenu({
onCreateOrder,
onCreateDelivery,
}: QuickCreateMenuProps) {
+ const { user } = useAuthUnified();
+
+ const canCreateOrders = hasPermission(user?.role || '', 'orders', 'create');
+ const canCreateDeliveries = hasPermission(user?.role || '', 'deliveries', 'create');
+
return (
-
+ {canCreateOrders && (
+
+ )}
-
-
-
setShowQuickCreate(true)}
- className="bg-accent hover:bg-orange-600 text-white"
- >
-
- Nouveau
-
+ {(hasPermission(user?.role || '', 'orders', 'create') || hasPermission(user?.role || '', 'deliveries', 'create')) && (
+
setShowQuickCreate(true)}
+ className="bg-accent hover:bg-orange-600 text-white"
+ >
+
+ Nouveau
+
+ )}
diff --git a/replit.md b/replit.md
index 3ccbd29..166ac0e 100644
--- a/replit.md
+++ b/replit.md
@@ -162,4 +162,51 @@ Preferred communication style: Simple, everyday language.
- Added automatic order status synchronization in `updateDelivery` method when delivery marked as delivered
- Created `fix-delivery-order-sync.sql` script to correct existing production data inconsistencies
- Improved Calendar date initialization to show current month instead of hardcoded July 2025
-- **Result**: Calendar now properly displays delivery dates and automatically synchronizes order statuses when deliveries are completed
\ No newline at end of file
+- **Result**: Calendar now properly displays delivery dates and automatically synchronizes order statuses when deliveries are completed
+
+### August 12, 2025 - Employee Permissions Restriction & Database Schema Download
+
+#### Employee Role Permissions Update
+- **Issue**: Employee role had excessive access to orders and deliveries modules, but needed restricted calendar access
+- **Root Cause**: Permission system allowed employees full access to create orders/deliveries from calendar and sidebar navigation
+- **Solution Implemented**:
+ - Updated `shared/permissions.ts`: Removed all permissions for employees on 'orders' and 'deliveries' modules
+ - Modified `client/src/components/Sidebar.tsx`: Removed orders and deliveries menu items for employee role
+ - Enhanced `client/src/pages/Calendar.tsx`: Added permission checks to hide "Nouveau" button for employees
+ - Updated `client/src/components/modals/QuickCreateMenu.tsx`: Added role-based filtering for order/delivery creation options
+- **Employee Access Rules**:
+ - **Calendar**: View-only access, cannot create orders or deliveries
+ - **Sidebar**: No access to Orders and Deliveries menu items
+ - **Customer Orders & DLC**: Maintains supplier list access for creating customer orders and DLC entries
+- **Result**: Employees now have appropriate restricted access while maintaining necessary supplier information for their workflows
+
+#### Database Schema Download Feature
+- **Issue**: Database schema scan results were only available in server logs, needed downloadable reports
+- **Solution Implemented**:
+ - Created new API route `/api/debug/download-schema` for production schema downloads
+ - Added comprehensive schema report generation with tables, columns, constraints, and record counts
+ - Enhanced `client/src/pages/DatabaseDebug.tsx`: Added download button that appears after successful scans
+ - Implemented proper file download headers with automatic filename generation
+ - Added admin-only access restriction and production environment validation
+- **Download Report Features**:
+ - Complete database structure with column details and constraints
+ - Record counts per table for data overview
+ - Foreign key relationships mapping
+ - Formatted text file with timestamps and user information
+- **Result**: Administrators can now download comprehensive database schema reports for documentation and analysis
+
+#### Employee Supplier Access Fix
+- **Issue**: Employee role could not access supplier lists in Customer Orders and DLC modules on production server
+- **Root Cause**: API route `/api/suppliers` was restricted to admin and manager roles only, excluding employees and directeurs
+- **Solution Implemented**:
+ - Updated `/api/suppliers` GET route: Added employee and directeur role access for read operations
+ - Updated `/api/suppliers` POST route: Added directeur role access for creation operations
+ - Updated `/api/suppliers` PUT route: Added directeur role access for edit operations
+ - Updated `/api/suppliers` DELETE route: Restricted to admin and directeur only (removed manager access)
+ - Created test script `test-employee-supplier-access.js` for production verification
+- **Access Matrix Updated**:
+ - **GET /api/suppliers**: admin, directeur, manager, employee (all roles can read)
+ - **POST /api/suppliers**: admin, directeur, manager (creation permissions)
+ - **PUT /api/suppliers**: admin, directeur, manager (edit permissions)
+ - **DELETE /api/suppliers**: admin, directeur only (delete permissions)
+- **Result**: Employees can now access supplier lists in Customer Orders and DLC modules while maintaining appropriate write restrictions
\ No newline at end of file
diff --git a/server/routes.ts b/server/routes.ts
index d67611a..0f85bb9 100644
--- a/server/routes.ts
+++ b/server/routes.ts
@@ -188,7 +188,7 @@ export async function registerRoutes(app: Express): Promise {
app.get('/api/suppliers', 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' && user.role !== 'manager')) {
+ if (!user || (user.role !== 'admin' && user.role !== 'manager' && user.role !== 'directeur' && user.role !== 'employee')) {
return res.status(403).json({ message: "Insufficient permissions" });
}
@@ -237,8 +237,8 @@ export async function registerRoutes(app: Express): Promise {
console.log('✅ User found:', { username: user.username, role: user.role });
// Vérifier les permissions
- if (user.role !== 'admin' && user.role !== 'manager') {
- console.error('❌ Insufficient permissions:', { userRole: user.role, required: ['admin', 'manager'] });
+ if (user.role !== 'admin' && user.role !== 'manager' && user.role !== 'directeur') {
+ console.error('❌ Insufficient permissions:', { userRole: user.role, required: ['admin', 'manager', 'directeur'] });
return res.status(403).json({ message: "Insufficient permissions" });
}
@@ -279,7 +279,7 @@ export async function registerRoutes(app: Express): Promise {
app.put('/api/suppliers/: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' && user.role !== 'manager')) {
+ if (!user || (user.role !== 'admin' && user.role !== 'manager' && user.role !== 'directeur')) {
return res.status(403).json({ message: "Insufficient permissions" });
}
@@ -296,7 +296,7 @@ export async function registerRoutes(app: Express): Promise {
app.delete('/api/suppliers/: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' && user.role !== 'manager')) {
+ if (!user || (user.role !== 'admin' && user.role !== 'directeur')) {
return res.status(403).json({ message: "Insufficient permissions" });
}
diff --git a/shared/permissions.ts b/shared/permissions.ts
index 00af99a..0cd843a 100644
--- a/shared/permissions.ts
+++ b/shared/permissions.ts
@@ -32,20 +32,20 @@ const PERMISSIONS: Record> = {
employee: ['view']
},
- // Commandes - Admin/Directeur tout, Manager tout sauf delete, Employé view
+ // Commandes - Admin/Directeur tout, Manager tout sauf delete, Employé aucun accès
orders: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit', 'delete'],
manager: ['view', 'create', 'edit'],
- employee: ['view']
+ employee: []
},
- // Livraisons - Admin/Directeur tout, Manager tout sauf delete, Employé view
+ // Livraisons - Admin/Directeur tout, Manager tout sauf delete, Employé aucun accès
deliveries: {
admin: ['view', 'create', 'edit', 'delete'],
directeur: ['view', 'create', 'edit', 'delete'],
manager: ['view', 'create', 'edit'],
- employee: ['view']
+ employee: []
},
// Rapprochement - Admin tout, Directeur tout sauf delete, Manager/Employé rien
diff --git a/test-employee-supplier-access.js b/test-employee-supplier-access.js
new file mode 100644
index 0000000..569f5db
--- /dev/null
+++ b/test-employee-supplier-access.js
@@ -0,0 +1,152 @@
+#!/usr/bin/env node
+
+/**
+ * Script de test pour vérifier l'accès des employés aux fournisseurs
+ * À exécuter sur le serveur de production pour diagnostiquer les problèmes d'accès
+ */
+
+const https = require('https');
+const fs = require('fs');
+
+// Configuration pour votre serveur de production
+const PRODUCTION_URL = process.env.PRODUCTION_URL || 'https://votre-serveur-production.com';
+const TEST_EMPLOYEE_CREDENTIALS = {
+ username: process.env.TEST_EMPLOYEE_USERNAME || 'test_employee',
+ password: process.env.TEST_EMPLOYEE_PASSWORD || 'password123'
+};
+
+console.log('🧪 === TEST D\'ACCÈS FOURNISSEURS POUR EMPLOYÉS ===\n');
+
+async function makeRequest(path, method = 'GET', data = null, cookies = '') {
+ return new Promise((resolve, reject) => {
+ const url = new URL(path, PRODUCTION_URL);
+ const options = {
+ hostname: url.hostname,
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
+ path: url.pathname + url.search,
+ method: method,
+ headers: {
+ 'Content-Type': 'application/json',
+ 'User-Agent': 'LogiFlow-Test-Script/1.0',
+ ...(cookies ? { 'Cookie': cookies } : {}),
+ ...(data ? { 'Content-Length': Buffer.byteLength(JSON.stringify(data)) } : {})
+ }
+ };
+
+ const req = https.request(options, (res) => {
+ let body = '';
+ res.on('data', (chunk) => body += chunk);
+ res.on('end', () => {
+ try {
+ const parsed = JSON.parse(body);
+ resolve({
+ status: res.statusCode,
+ headers: res.headers,
+ data: parsed
+ });
+ } catch (e) {
+ resolve({
+ status: res.statusCode,
+ headers: res.headers,
+ data: body
+ });
+ }
+ });
+ });
+
+ req.on('error', reject);
+
+ if (data) {
+ req.write(JSON.stringify(data));
+ }
+
+ req.end();
+ });
+}
+
+async function testEmployeeSupplierAccess() {
+ try {
+ console.log('1️⃣ Test de connexion employé...');
+
+ // Étape 1: Se connecter avec un compte employé
+ const loginResponse = await makeRequest('/api/login', 'POST', TEST_EMPLOYEE_CREDENTIALS);
+
+ console.log(` Status: ${loginResponse.status}`);
+ console.log(` Response: ${JSON.stringify(loginResponse.data, null, 2)}\n`);
+
+ if (loginResponse.status !== 200) {
+ console.error('❌ Échec de connexion employé');
+ console.error(' Vérifiez que le compte employé existe et que les identifiants sont corrects');
+ return;
+ }
+
+ // Extraire les cookies de session
+ const setCookieHeaders = loginResponse.headers['set-cookie'] || [];
+ const cookies = setCookieHeaders.join('; ');
+
+ console.log('2️⃣ Test d\'accès aux fournisseurs...');
+
+ // Étape 2: Tester l'accès à la liste des fournisseurs
+ const suppliersResponse = await makeRequest('/api/suppliers', 'GET', null, cookies);
+
+ console.log(` Status: ${suppliersResponse.status}`);
+ console.log(` Response: ${JSON.stringify(suppliersResponse.data, null, 2)}\n`);
+
+ if (suppliersResponse.status === 200) {
+ console.log('✅ SUCCÈS: L\'employé peut accéder aux fournisseurs');
+ console.log(` Nombre de fournisseurs retournés: ${Array.isArray(suppliersResponse.data) ? suppliersResponse.data.length : 'N/A'}`);
+ } else if (suppliersResponse.status === 403) {
+ console.log('❌ ÉCHEC: L\'employé n\'a pas l\'autorisation d\'accéder aux fournisseurs');
+ console.log(' Le problème est confirmé - permissions insuffisantes');
+ } else {
+ console.log(`⚠️ ERREUR INATTENDUE: Status ${suppliersResponse.status}`);
+ }
+
+ console.log('\n3️⃣ Test d\'accès aux fournisseurs DLC...');
+
+ // Étape 3: Tester l'accès aux fournisseurs DLC spécifiquement
+ const dlcSuppliersResponse = await makeRequest('/api/suppliers?dlc=true', 'GET', null, cookies);
+
+ console.log(` Status: ${dlcSuppliersResponse.status}`);
+ console.log(` Response: ${JSON.stringify(dlcSuppliersResponse.data, null, 2)}\n`);
+
+ if (dlcSuppliersResponse.status === 200) {
+ console.log('✅ SUCCÈS: L\'employé peut accéder aux fournisseurs DLC');
+ console.log(` Nombre de fournisseurs DLC retournés: ${Array.isArray(dlcSuppliersResponse.data) ? dlcSuppliersResponse.data.length : 'N/A'}`);
+ } else if (dlcSuppliersResponse.status === 403) {
+ console.log('❌ ÉCHEC: L\'employé n\'a pas l\'autorisation d\'accéder aux fournisseurs DLC');
+ }
+
+ // Résumé final
+ console.log('\n📊 === RÉSUMÉ DU TEST ===');
+ console.log(`Connexion employé: ${loginResponse.status === 200 ? '✅ OK' : '❌ ÉCHEC'}`);
+ console.log(`Accès fournisseurs: ${suppliersResponse.status === 200 ? '✅ OK' : '❌ ÉCHEC'}`);
+ console.log(`Accès fournisseurs DLC: ${dlcSuppliersResponse.status === 200 ? '✅ OK' : '❌ ÉCHEC'}`);
+
+ if (suppliersResponse.status === 200 && dlcSuppliersResponse.status === 200) {
+ console.log('\n🎉 TOUS LES TESTS PASSÉS - L\'accès employé aux fournisseurs fonctionne correctement');
+ } else {
+ console.log('\n⚠️ PROBLÈME DÉTECTÉ - Vérifiez les permissions serveur et la configuration des rôles');
+ }
+
+ } catch (error) {
+ console.error('💥 Erreur lors du test:', error.message);
+ console.error(' Vérifiez que le serveur de production est accessible');
+ }
+}
+
+// Instructions d'utilisation
+console.log('📋 Instructions:');
+console.log('1. Assurez-vous d\'avoir un compte employé de test');
+console.log('2. Configurez les variables d\'environnement si nécessaire:');
+console.log(' - PRODUCTION_URL=https://votre-serveur.com');
+console.log(' - TEST_EMPLOYEE_USERNAME=nom_utilisateur');
+console.log(' - TEST_EMPLOYEE_PASSWORD=mot_de_passe');
+console.log('3. Exécutez: node test-employee-supplier-access.js\n');
+
+// Exécuter le test si le script est appelé directement
+if (require.main === module) {
+ testEmployeeSupplierAccess().catch(console.error);
+}
+
+module.exports = { testEmployeeSupplierAccess };
\ No newline at end of file