rights system
This commit is contained in:
@@ -16,8 +16,8 @@ router.get('/:id', authenticate, atemschutzController.getOne.bind(atem
|
||||
|
||||
// ── Write — gruppenfuehrer+ ─────────────────────────────────────────────────
|
||||
|
||||
router.post('/', authenticate, requirePermission('atemschutz:write'), atemschutzController.create.bind(atemschutzController));
|
||||
router.patch('/:id', authenticate, requirePermission('atemschutz:write'), atemschutzController.update.bind(atemschutzController));
|
||||
router.post('/', authenticate, requirePermission('atemschutz:create'), atemschutzController.create.bind(atemschutzController));
|
||||
router.patch('/:id', authenticate, requirePermission('atemschutz:create'), atemschutzController.update.bind(atemschutzController));
|
||||
|
||||
// ── Delete — kommandant+ ────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -19,14 +19,14 @@ router.get('/calendar-token', authenticate, bookingController.getCalendarToken.b
|
||||
// ── Write operations ──────────────────────────────────────────────────────────
|
||||
|
||||
router.post('/', authenticate, bookingController.create.bind(bookingController));
|
||||
router.patch('/:id', authenticate, requirePermission('bookings:write'), bookingController.update.bind(bookingController));
|
||||
router.patch('/:id', authenticate, requirePermission('kalender:edit_bookings'), bookingController.update.bind(bookingController));
|
||||
|
||||
// Soft-cancel (sets abgesagt=TRUE) — creator or bookings:write
|
||||
router.delete('/:id', authenticate, bookingController.cancel.bind(bookingController));
|
||||
router.patch('/:id/cancel', authenticate, bookingController.cancel.bind(bookingController));
|
||||
|
||||
// Hard-delete (admin only)
|
||||
router.delete('/:id/force', authenticate, requirePermission('bookings:delete'), bookingController.hardDelete.bind(bookingController));
|
||||
router.delete('/:id/force', authenticate, requirePermission('kalender:delete_bookings'), bookingController.hardDelete.bind(bookingController));
|
||||
|
||||
// ── Single booking read — after specific routes to avoid path conflicts ───────
|
||||
|
||||
|
||||
@@ -17,13 +17,13 @@ router.get('/:id', authenticate, equipmentController.getEquipmen
|
||||
|
||||
// ── Write — gruppenfuehrer+ ────────────────────────────────────────────────
|
||||
|
||||
router.post('/', authenticate, requirePermission('equipment:write'), equipmentController.createEquipment.bind(equipmentController));
|
||||
router.patch('/:id', authenticate, requirePermission('equipment:write'), equipmentController.updateEquipment.bind(equipmentController));
|
||||
router.patch('/:id/status', authenticate, requirePermission('equipment:write'), equipmentController.updateStatus.bind(equipmentController));
|
||||
router.post('/:id/wartung', authenticate, requirePermission('equipment:write'), equipmentController.addWartung.bind(equipmentController));
|
||||
router.post('/', authenticate, requirePermission('ausruestung:create'), equipmentController.createEquipment.bind(equipmentController));
|
||||
router.patch('/:id', authenticate, requirePermission('ausruestung:create'), equipmentController.updateEquipment.bind(equipmentController));
|
||||
router.patch('/:id/status', authenticate, requirePermission('ausruestung:create'), equipmentController.updateStatus.bind(equipmentController));
|
||||
router.post('/:id/wartung', authenticate, requirePermission('ausruestung:create'), equipmentController.addWartung.bind(equipmentController));
|
||||
|
||||
// ── Delete — admin only ──────────────────────────────────────────────────────
|
||||
|
||||
router.delete('/:id', authenticate, requirePermission('equipment:delete'), equipmentController.deleteEquipment.bind(equipmentController));
|
||||
router.delete('/:id', authenticate, requirePermission('ausruestung:delete'), equipmentController.deleteEquipment.bind(equipmentController));
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -22,7 +22,7 @@ router.get('/kategorien', authenticate, eventsController.listKategorien.bind(eve
|
||||
router.post(
|
||||
'/kategorien',
|
||||
authenticate,
|
||||
requirePermission('events:categories'),
|
||||
requirePermission('kalender:manage_categories'),
|
||||
eventsController.createKategorie.bind(eventsController)
|
||||
);
|
||||
|
||||
@@ -33,7 +33,7 @@ router.post(
|
||||
router.patch(
|
||||
'/kategorien/:id',
|
||||
authenticate,
|
||||
requirePermission('events:categories'),
|
||||
requirePermission('kalender:manage_categories'),
|
||||
eventsController.updateKategorie.bind(eventsController)
|
||||
);
|
||||
|
||||
@@ -44,7 +44,7 @@ router.patch(
|
||||
router.delete(
|
||||
'/kategorien/:id',
|
||||
authenticate,
|
||||
requirePermission('events:categories'),
|
||||
requirePermission('kalender:manage_categories'),
|
||||
eventsController.deleteKategorie.bind(eventsController)
|
||||
);
|
||||
|
||||
@@ -118,7 +118,7 @@ router.get(
|
||||
router.post(
|
||||
'/import',
|
||||
authenticate,
|
||||
requirePermission('events:write'),
|
||||
requirePermission('kalender:create_events'),
|
||||
eventsController.importEvents.bind(eventsController)
|
||||
);
|
||||
|
||||
@@ -129,7 +129,7 @@ router.post(
|
||||
router.post(
|
||||
'/',
|
||||
authenticate,
|
||||
requirePermission('events:write'),
|
||||
requirePermission('kalender:create_events'),
|
||||
eventsController.createEvent.bind(eventsController)
|
||||
);
|
||||
|
||||
@@ -146,7 +146,7 @@ router.get('/:id', authenticate, eventsController.getById.bind(eventsController)
|
||||
router.patch(
|
||||
'/:id',
|
||||
authenticate,
|
||||
requirePermission('events:write'),
|
||||
requirePermission('kalender:create_events'),
|
||||
eventsController.updateEvent.bind(eventsController)
|
||||
);
|
||||
|
||||
@@ -157,7 +157,7 @@ router.patch(
|
||||
router.delete(
|
||||
'/:id',
|
||||
authenticate,
|
||||
requirePermission('events:write'),
|
||||
requirePermission('kalender:create_events'),
|
||||
eventsController.cancelEvent.bind(eventsController)
|
||||
);
|
||||
|
||||
@@ -168,7 +168,7 @@ router.delete(
|
||||
router.post(
|
||||
'/:id/delete',
|
||||
authenticate,
|
||||
requirePermission('events:write'),
|
||||
requirePermission('kalender:create_events'),
|
||||
eventsController.deleteEvent.bind(eventsController)
|
||||
);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ router.use(authenticate);
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
requirePermission('incidents:read'),
|
||||
requirePermission('einsaetze:view'),
|
||||
incidentController.listIncidents.bind(incidentController)
|
||||
);
|
||||
|
||||
@@ -31,7 +31,7 @@ router.get(
|
||||
*/
|
||||
router.get(
|
||||
'/stats',
|
||||
requirePermission('incidents:read'),
|
||||
requirePermission('einsaetze:view'),
|
||||
incidentController.getStats.bind(incidentController)
|
||||
);
|
||||
|
||||
@@ -42,7 +42,7 @@ router.get(
|
||||
*/
|
||||
router.post(
|
||||
'/refresh-stats',
|
||||
requirePermission('incidents:delete'), // kommandant+ (repurposing delete permission for admin ops)
|
||||
requirePermission('einsaetze:delete'),
|
||||
incidentController.refreshStats.bind(incidentController)
|
||||
);
|
||||
|
||||
@@ -53,7 +53,7 @@ router.post(
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
requirePermission('incidents:read'),
|
||||
requirePermission('einsaetze:view'),
|
||||
incidentController.getIncident.bind(incidentController)
|
||||
);
|
||||
|
||||
@@ -64,7 +64,7 @@ router.get(
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
requirePermission('incidents:write'),
|
||||
requirePermission('einsaetze:create'),
|
||||
incidentController.createIncident.bind(incidentController)
|
||||
);
|
||||
|
||||
@@ -75,7 +75,7 @@ router.post(
|
||||
*/
|
||||
router.patch(
|
||||
'/:id',
|
||||
requirePermission('incidents:write'),
|
||||
requirePermission('einsaetze:create'),
|
||||
incidentController.updateIncident.bind(incidentController)
|
||||
);
|
||||
|
||||
@@ -86,7 +86,7 @@ router.patch(
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
requirePermission('incidents:delete'),
|
||||
requirePermission('einsaetze:delete'),
|
||||
incidentController.deleteIncident.bind(incidentController)
|
||||
);
|
||||
|
||||
@@ -98,7 +98,7 @@ router.delete(
|
||||
*/
|
||||
router.post(
|
||||
'/:id/personnel',
|
||||
requirePermission('incidents:manage_personnel'),
|
||||
requirePermission('einsaetze:manage_personnel'),
|
||||
incidentController.assignPersonnel.bind(incidentController)
|
||||
);
|
||||
|
||||
@@ -109,7 +109,7 @@ router.post(
|
||||
*/
|
||||
router.delete(
|
||||
'/:id/personnel/:userId',
|
||||
requirePermission('incidents:manage_personnel'),
|
||||
requirePermission('einsaetze:manage_personnel'),
|
||||
incidentController.removePersonnel.bind(incidentController)
|
||||
);
|
||||
|
||||
@@ -121,7 +121,7 @@ router.delete(
|
||||
*/
|
||||
router.post(
|
||||
'/:id/vehicles',
|
||||
requirePermission('incidents:manage_personnel'),
|
||||
requirePermission('einsaetze:manage_personnel'),
|
||||
incidentController.assignVehicle.bind(incidentController)
|
||||
);
|
||||
|
||||
@@ -132,7 +132,7 @@ router.post(
|
||||
*/
|
||||
router.delete(
|
||||
'/:id/vehicles/:fahrzeugId',
|
||||
requirePermission('incidents:manage_personnel'),
|
||||
requirePermission('einsaetze:manage_personnel'),
|
||||
incidentController.removeVehicle.bind(incidentController)
|
||||
);
|
||||
|
||||
|
||||
@@ -17,49 +17,49 @@ router.use(authenticate);
|
||||
// "stats" as a userId parameter.
|
||||
router.get(
|
||||
'/stats',
|
||||
requirePermission('members:read'),
|
||||
requirePermission('mitglieder:view'),
|
||||
memberController.getMemberStats.bind(memberController)
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
requirePermission('members:read'),
|
||||
requirePermission('mitglieder:view'),
|
||||
memberController.getMembers.bind(memberController)
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/:userId',
|
||||
requirePermission('members:read'),
|
||||
requirePermission('mitglieder:view'),
|
||||
memberController.getMemberById.bind(memberController)
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/:userId/profile',
|
||||
requirePermission('members:write'),
|
||||
requirePermission('mitglieder:edit'),
|
||||
memberController.createMemberProfile.bind(memberController)
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/:userId/befoerderungen',
|
||||
requirePermission('members:read'),
|
||||
requirePermission('mitglieder:view'),
|
||||
memberController.getBefoerderungen.bind(memberController)
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/:userId/untersuchungen',
|
||||
requirePermission('members:read'),
|
||||
requirePermission('mitglieder:view'),
|
||||
memberController.getUntersuchungen.bind(memberController)
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/:userId/fahrgenehmigungen',
|
||||
requirePermission('members:read'),
|
||||
requirePermission('mitglieder:view'),
|
||||
memberController.getFahrgenehmigungen.bind(memberController)
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/:userId/ausbildungen',
|
||||
requirePermission('members:read'),
|
||||
requirePermission('mitglieder:view'),
|
||||
memberController.getAusbildungen.bind(memberController)
|
||||
);
|
||||
|
||||
@@ -76,7 +76,7 @@ const requireOwnerOrWrite = (req: Request, res: Response, next: NextFunction): v
|
||||
return;
|
||||
}
|
||||
// Not the owner — must have members:write permission
|
||||
requirePermission('members:write')(req, res, next);
|
||||
requirePermission('mitglieder:edit')(req, res, next);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
17
backend/src/routes/permission.routes.ts
Normal file
17
backend/src/routes/permission.routes.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Router } from 'express';
|
||||
import permissionController from '../controllers/permission.controller';
|
||||
import { authenticate } from '../middleware/auth.middleware';
|
||||
import { requirePermission } from '../middleware/rbac.middleware';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// ── User-facing (any authenticated user) ──────────────────────────────────
|
||||
router.get('/me', authenticate, permissionController.getMyPermissions.bind(permissionController));
|
||||
|
||||
// ── Admin-only routes ─────────────────────────────────────────────────────
|
||||
router.get('/admin/matrix', authenticate, requirePermission('admin:access'), permissionController.getMatrix.bind(permissionController));
|
||||
router.get('/admin/groups', authenticate, requirePermission('admin:access'), permissionController.getGroups.bind(permissionController));
|
||||
router.put('/admin/group/:groupName', authenticate, requirePermission('admin:access'), permissionController.setGroupPermissions.bind(permissionController));
|
||||
router.put('/admin/maintenance/:featureGroupId', authenticate, requirePermission('admin:access'), permissionController.setMaintenanceFlag.bind(permissionController));
|
||||
|
||||
export default router;
|
||||
@@ -1,14 +1,15 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import trainingController from '../controllers/training.controller';
|
||||
import { authenticate, optionalAuth } from '../middleware/auth.middleware';
|
||||
import { requirePermission, getUserRole } from '../middleware/rbac.middleware';
|
||||
import { requirePermission } from '../middleware/rbac.middleware';
|
||||
import { permissionService } from '../services/permission.service';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// injectTeilnahmenFlag
|
||||
//
|
||||
// Sets req.canSeeTeilnahmen = true for Gruppenführer and above.
|
||||
// Sets req.canSeeTeilnahmen = true for users with kalender:mark_attendance.
|
||||
// Regular Mitglieder see only attendance counts; officers see the full list.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -19,12 +20,10 @@ async function injectTeilnahmenFlag(
|
||||
): Promise<void> {
|
||||
try {
|
||||
if (req.user) {
|
||||
const role = await getUserRole(req.user.id);
|
||||
const ROLE_ORDER: Record<string, number> = {
|
||||
bewerber: -1, mitglied: 0, gruppenfuehrer: 1, kommandant: 2, admin: 3,
|
||||
};
|
||||
const groups: string[] = req.user?.groups ?? [];
|
||||
(req as any).canSeeTeilnahmen =
|
||||
(ROLE_ORDER[role] ?? 0) >= ROLE_ORDER.gruppenfuehrer;
|
||||
groups.includes('dashboard_admin') ||
|
||||
permissionService.hasPermission(groups, 'kalender:mark_attendance');
|
||||
}
|
||||
} catch (_err) {
|
||||
// Non-fatal — default to restricted view
|
||||
@@ -68,12 +67,12 @@ router.get('/calendar-token', authenticate, trainingController.getCalendarToken)
|
||||
/**
|
||||
* GET /api/training/stats?year=<YYYY>
|
||||
* Annual participation stats.
|
||||
* Requires Kommandant or above (requirePermission('reports:read')).
|
||||
* Requires Kommandant or above (requirePermission('kalender:view_reports')).
|
||||
*/
|
||||
router.get(
|
||||
'/stats',
|
||||
authenticate,
|
||||
requirePermission('reports:read'),
|
||||
requirePermission('kalender:view_reports'),
|
||||
trainingController.getStats
|
||||
);
|
||||
|
||||
@@ -92,12 +91,12 @@ router.get(
|
||||
/**
|
||||
* POST /api/training
|
||||
* Create a new training event.
|
||||
* Requires Gruppenführer or above (requirePermission('training:write')).
|
||||
* Requires Gruppenführer or above (requirePermission('kalender:create_training')).
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
authenticate,
|
||||
requirePermission('training:write'),
|
||||
requirePermission('kalender:create_training'),
|
||||
trainingController.createEvent
|
||||
);
|
||||
|
||||
@@ -109,7 +108,7 @@ router.post(
|
||||
router.patch(
|
||||
'/:id',
|
||||
authenticate,
|
||||
requirePermission('training:write'),
|
||||
requirePermission('kalender:create_training'),
|
||||
trainingController.updateEvent
|
||||
);
|
||||
|
||||
@@ -121,7 +120,7 @@ router.patch(
|
||||
router.delete(
|
||||
'/:id',
|
||||
authenticate,
|
||||
requirePermission('training:cancel'),
|
||||
requirePermission('kalender:cancel_training'),
|
||||
trainingController.cancelEvent
|
||||
);
|
||||
|
||||
@@ -142,7 +141,7 @@ router.patch(
|
||||
router.post(
|
||||
'/:id/attendance/mark',
|
||||
authenticate,
|
||||
requirePermission('training:mark_attendance'),
|
||||
requirePermission('kalender:mark_attendance'),
|
||||
trainingController.markAttendance
|
||||
);
|
||||
|
||||
|
||||
@@ -10,19 +10,19 @@ const router = Router();
|
||||
router.get('/', authenticate, vehicleController.listVehicles.bind(vehicleController));
|
||||
router.get('/stats', authenticate, vehicleController.getStats.bind(vehicleController));
|
||||
router.get('/alerts', authenticate, vehicleController.getAlerts.bind(vehicleController));
|
||||
router.get('/alerts/export', authenticate, requirePermission('vehicles:read'), vehicleController.exportAlerts.bind(vehicleController));
|
||||
router.get('/alerts/export', authenticate, requirePermission('fahrzeuge:view'), vehicleController.exportAlerts.bind(vehicleController));
|
||||
router.get('/:id', authenticate, vehicleController.getVehicle.bind(vehicleController));
|
||||
router.get('/:id/wartung', authenticate, vehicleController.getWartung.bind(vehicleController));
|
||||
|
||||
// ── Write — kommandant+ ──────────────────────────────────────────────────────
|
||||
|
||||
router.post('/', authenticate, requirePermission('vehicles:write'), vehicleController.createVehicle.bind(vehicleController));
|
||||
router.patch('/:id', authenticate, requirePermission('vehicles:write'), vehicleController.updateVehicle.bind(vehicleController));
|
||||
router.delete('/:id', authenticate, requirePermission('vehicles:delete'), vehicleController.deleteVehicle.bind(vehicleController));
|
||||
router.post('/', authenticate, requirePermission('fahrzeuge:create'), vehicleController.createVehicle.bind(vehicleController));
|
||||
router.patch('/:id', authenticate, requirePermission('fahrzeuge:create'), vehicleController.updateVehicle.bind(vehicleController));
|
||||
router.delete('/:id', authenticate, requirePermission('fahrzeuge:delete'), vehicleController.deleteVehicle.bind(vehicleController));
|
||||
|
||||
// ── Status + maintenance log — gruppenfuehrer+ ──────────────────────────────
|
||||
|
||||
router.patch('/:id/status', authenticate, requirePermission('vehicles:status'), vehicleController.updateVehicleStatus.bind(vehicleController));
|
||||
router.post('/:id/wartung', authenticate, requirePermission('vehicles:status'), vehicleController.addWartung.bind(vehicleController));
|
||||
router.patch('/:id/status', authenticate, requirePermission('fahrzeuge:change_status'), vehicleController.updateVehicleStatus.bind(vehicleController));
|
||||
router.post('/:id/wartung', authenticate, requirePermission('fahrzeuge:change_status'), vehicleController.addWartung.bind(vehicleController));
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user