'use strict'; const db = require('../db/db'); /** * Factory: returns middleware that loads a resource by req.params[paramKey], * verifies ownership against req.user.id, and attaches the row as req.resource. * * Simple usage (table lookup): * requireOwnership({ table: 'tests', ownerField: 'created_by' }) * * Complex usage (JOIN / aliased field): * requireOwnership({ * fetchFn: id => db.prepare(` * SELECT a.id, COALESCE(c.teacher_id, a.created_by) AS teacher_id * FROM assignments a LEFT JOIN classes c ON c.id = a.class_id WHERE a.id = ? * `).get(id), * ownerField: 'teacher_id', * }) * * Options: * table — DB table name for `SELECT * FROM {table} WHERE id = ?` * fetchFn — (id) => row|undefined (alternative to `table`) * ownerField — row field compared to req.user.id (required) * paramKey — req.params key for the record ID (default: 'id') * adminBypass — admin role always passes (default: true) */ const ALLOWED_TABLES = new Set(['tests','classes','assignments','questions','courses','lessons','files','folders','shop_items','live_sessions','flashcard_decks']); function requireOwnership({ table, fetchFn, ownerField, paramKey = 'id', adminBypass = true }) { if (table && !ALLOWED_TABLES.has(table)) throw new Error(`requireOwnership: unknown table "${table}"`); // Pre-compile statement once at middleware creation (server startup) const stmt = table ? db.prepare(`SELECT * FROM ${table} WHERE id = ?`) : null; const fetch = fetchFn || (id => stmt.get(id)); return (req, res, next) => { const row = fetch(req.params[paramKey]); if (!row) return res.status(404).json({ error: 'Not found' }); if (adminBypass && req.user?.role === 'admin') { req.resource = row; return next(); } if (row[ownerField] !== req.user?.id) { return res.status(403).json({ error: 'Forbidden' }); } req.resource = row; next(); }; } module.exports = { requireOwnership };