Files
dashboard/backend/src/routes/member.routes.ts
Matthias Hochmeister 515f14956e rights system
2026-03-23 10:50:52 +01:00

93 lines
2.7 KiB
TypeScript

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;