diff --git a/backend/src/controllers/cachedEmail.controller.ts b/backend/src/controllers/cachedEmail.controller.ts index 37b5e8fc..84210f23 100644 --- a/backend/src/controllers/cachedEmail.controller.ts +++ b/backend/src/controllers/cachedEmail.controller.ts @@ -260,6 +260,16 @@ export async function getContractFolderCounts(req: AuthRequest, res: Response): export async function syncAccount(req: AuthRequest, res: Response): Promise { try { const stressfreiEmailId = parseInt(req.params.id); + // Mitarbeiter brauchen customers:update (wie früher), Portal-Kunden + // brauchen keine Perm – nur Eigentum am Konto (Owner-Check unten). + // Trennung der Threat-Modelle: Portal-User dürfen IHR eigenes + // Postfach syncen, sollen aber nicht Mitarbeiter-Updates triggern. + const isPortal = !!req.user?.isCustomerPortal; + const hasUpdatePerm = req.user?.permissions?.includes('customers:update') ?? false; + if (!isPortal && !hasUpdatePerm) { + res.status(403).json({ success: false, error: 'Keine Berechtigung' } as ApiResponse); + return; + } if (!(await canAccessStressfreiEmail(req, res, stressfreiEmailId))) return; const fullSync = req.query.full === 'true'; diff --git a/backend/src/routes/cachedEmail.routes.ts b/backend/src/routes/cachedEmail.routes.ts index bc3d9f2d..6d83098d 100644 --- a/backend/src/routes/cachedEmail.routes.ts +++ b/backend/src/routes/cachedEmail.routes.ts @@ -236,10 +236,14 @@ router.delete( // E-Mails für ein Konto synchronisieren // POST /api/stressfrei-emails/:id/sync?full=true +// +// KEIN `requirePermission('customers:update')` hier: Portal-Kunden +// dürfen ihr EIGENES Postfach synchronisieren – sie haben aber nur +// `customers:read`. Der Mitarbeiter-Perm-Check und der Owner-Check +// laufen im Controller. (Pentest 2026-05-30 follow-up.) router.post( '/stressfrei-emails/:id/sync', authenticate, - requirePermission('customers:update'), cachedEmailController.syncAccount ); diff --git a/frontend/src/pages/tasks/TaskList.tsx b/frontend/src/pages/tasks/TaskList.tsx index 9c69cdea..2c6d3564 100644 --- a/frontend/src/pages/tasks/TaskList.tsx +++ b/frontend/src/pages/tasks/TaskList.tsx @@ -660,21 +660,33 @@ function CreateSupportTicketModal({ />
{filteredContracts.length > 0 ? ( - filteredContracts.map((contract) => ( -
setSelectedContractId(contract.id)} - className={`p-3 cursor-pointer border-b last:border-b-0 hover:bg-gray-50 ${ - selectedContractId === contract.id ? 'bg-blue-50 border-blue-200' : '' - }`} - > -
{contract.contractNumber}
-
- {contract.providerName || 'Kein Anbieter'} - {contract.tariffName && ` - ${contract.tariffName}`} + filteredContracts.map((contract) => { + const isSelected = selectedContractId === contract.id; + return ( +
setSelectedContractId(contract.id)} + className={`p-3 cursor-pointer border-b last:border-b-0 transition-colors flex items-center gap-3 ${ + isSelected + ? 'bg-blue-100 border-l-4 border-l-blue-600 pl-2' + : 'hover:bg-gray-50' + }`} + > + {isSelected && ( + + )} +
+
+ {contract.contractNumber} +
+
+ {contract.providerName || 'Kein Anbieter'} + {contract.tariffName && ` - ${contract.tariffName}`} +
+
-
- )) + ); + }) ) : (
Keine Verträge gefunden.