import { Router, Request, Response, NextFunction } from 'express'; import memberController from '../controllers/member.controller'; import { authenticate } from '../middleware/auth.middleware'; import { requirePermission } from '../middleware/rbac.middleware'; const router = Router(); // ---------------------------------------------------------------- // Apply authentication to every route in this router. // requirePermission is applied per-route because PATCH allows the // owner to update their own limited fields even without 'members:write'. // ---------------------------------------------------------------- router.use(authenticate); // IMPORTANT: The static /stats route must be registered BEFORE // the dynamic /:userId route, otherwise Express would match // "stats" as a userId parameter. router.get( '/stats', requirePermission('mitglieder:view_all'), memberController.getMemberStats.bind(memberController) ); router.get( '/', requirePermission('mitglieder:view_all'), memberController.getMembers.bind(memberController) ); router.get( '/:userId', requirePermission('mitglieder:view_all'), memberController.getMemberById.bind(memberController) ); router.post( '/:userId/profile', requirePermission('mitglieder:edit'), memberController.createMemberProfile.bind(memberController) ); router.get( '/:userId/befoerderungen', requirePermission('mitglieder:view_all'), memberController.getBefoerderungen.bind(memberController) ); router.get( '/:userId/untersuchungen', requirePermission('mitglieder:view_all'), memberController.getUntersuchungen.bind(memberController) ); router.get( '/:userId/fahrgenehmigungen', requirePermission('mitglieder:view_all'), memberController.getFahrgenehmigungen.bind(memberController) ); router.get( '/:userId/ausbildungen', requirePermission('mitglieder:view_all'), memberController.getAusbildungen.bind(memberController) ); /** * Inline middleware for PATCH /:userId. * Enforces that the caller is either the profile owner OR holds members:write. * This is the route-level IDOR guard; the controller still applies the * correct Zod schema (full vs. limited fields) based on role. */ const requireOwnerOrWrite = (req: Request, res: Response, next: NextFunction): void => { const isOwner = req.user?.id === req.params.userId; if (isOwner) { next(); return; } // Not the owner — must have members:write permission requirePermission('mitglieder:edit')(req, res, next); }; /** * PATCH /:userId — open to both privileged users AND the profile owner. * Route-level guard rejects all other callers before the controller runs. */ router.patch( '/:userId', requireOwnerOrWrite, memberController.updateMember.bind(memberController) ); export default router;