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
This commit is contained in:
michaelschal committed 2025-08-12 08:50:23 +00:00
1 parent 5fe9763763
commit 5ea3481dc0
7 files changed
+263 -45

No files matched your search

+2 -2
View File
@@ -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",
@@ -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 (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="sm:max-w-md" aria-describedby="quick-create-modal-description">
@@ -25,33 +32,43 @@ export default function QuickCreateMenu({
</p>
</DialogHeader>
<div className="space-y-3">
<Button
variant="outline"
className="w-full justify-start space-x-3 p-4 h-auto hover:bg-blue-50 hover:border-blue-300"
onClick={onCreateOrder}
>
<div className="w-10 h-10 bg-primary rounded-lg flex items-center justify-center">
<Package className="w-5 h-5 text-white" />
</div>
<div className="text-left">
<p className="font-medium text-gray-900">Nouvelle Commande</p>
<p className="text-sm text-gray-600">Créer une commande fournisseur</p>
</div>
</Button>
{canCreateOrders && (
<Button
variant="outline"
className="w-full justify-start space-x-3 p-4 h-auto hover:bg-blue-50 hover:border-blue-300"
onClick={onCreateOrder}
>
<div className="w-10 h-10 bg-primary rounded-lg flex items-center justify-center">
<Package className="w-5 h-5 text-white" />
</div>
<div className="text-left">
<p className="font-medium text-gray-900">Nouvelle Commande</p>
<p className="text-sm text-gray-600">Créer une commande fournisseur</p>
</div>
</Button>
)}
<Button
variant="outline"
className="w-full justify-start space-x-3 p-4 h-auto hover:bg-green-50 hover:border-green-300"
onClick={onCreateDelivery}
>
<div className="w-10 h-10 bg-secondary rounded-lg flex items-center justify-center">
<Truck className="w-5 h-5 text-white" />
{canCreateDeliveries && (
<Button
variant="outline"
className="w-full justify-start space-x-3 p-4 h-auto hover:bg-green-50 hover:border-green-300"
onClick={onCreateDelivery}
>
<div className="w-10 h-10 bg-secondary rounded-lg flex items-center justify-center">
<Truck className="w-5 h-5 text-white" />
</div>
<div className="text-left">
<p className="font-medium text-gray-900">Nouvelle Livraison</p>
<p className="text-sm text-gray-600">Planifier une livraison</p>
</div>
</Button>
)}
{!canCreateOrders && !canCreateDeliveries && (
<div className="text-center py-8 text-gray-500">
<p>Vous n'avez pas les permissions nécessaires pour créer des commandes ou livraisons.</p>
</div>
<div className="text-left">
<p className="font-medium text-gray-900">Nouvelle Livraison</p>
<p className="text-sm text-gray-600">Planifier une livraison</p>
</div>
</Button>
)}
</div>
</DialogContent>
</Dialog>
+10 -8
View File
@@ -14,6 +14,7 @@ import { apiRequest } from "@/lib/queryClient";
import { useToast } from "@/hooks/use-toast";
import { format, startOfMonth, endOfMonth } from "date-fns";
import { fr } from "date-fns/locale";
import { hasPermission } from "@/lib/permissions";
export default function Calendar() {
const { user } = useAuthUnified();
@@ -250,14 +251,15 @@ export default function Calendar() {
</Button>
</div>
<div className="flex items-center space-x-2">
<Button
onClick={() => setShowQuickCreate(true)}
className="bg-accent hover:bg-orange-600 text-white"
>
<Plus className="w-4 h-4 mr-2" />
Nouveau
</Button>
{(hasPermission(user?.role || '', 'orders', 'create') || hasPermission(user?.role || '', 'deliveries', 'create')) && (
<Button
onClick={() => setShowQuickCreate(true)}
className="bg-accent hover:bg-orange-600 text-white"
>
<Plus className="w-4 h-4 mr-2" />
Nouveau
</Button>
)}
</div>
</div>
</div>
+48 -1
View File
@@ -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
- **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
+5 -5
View File
@@ -188,7 +188,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
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<Server> {
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<Server> {
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<Server> {
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" });
}
+4 -4
View File
@@ -32,20 +32,20 @@ const PERMISSIONS: Record<Module, Record<Role, Permission[]>> = {
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
+152
View File
@@ -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 };