Rechtemanagement erweitert

This commit is contained in:
klaas 2025-03-17 17:05:27 +01:00
parent 85f07108c1
commit 673e33aaf7
7 changed files with 179 additions and 39 deletions

27
app.js
View File

@ -45,9 +45,30 @@ const memberRoutes = require("./routes/member");
const trainingRoutes = require("./routes/training");
const anwesendRoutes = require("./routes/anwesend");
app.use("/user", userRoutes);
app.use("/spiele", spieleRoutes);
app.use("/feature", featureRoutes);
app.use(
"/:abteilung?/user",
(req, res, next) => {
req.abteilung = req.params.abteilung; // `abteilung` in req speichern
next();
},
userRoutes
);
app.use(
"/:abteilung?/spiele",
(req, res, next) => {
req.abteilung = req.params.abteilung; // `abteilung` in req speichern
next();
},
spieleRoutes
);
app.use(
"/:abteilung?/feature",
(req, res, next) => {
req.abteilung = req.params.abteilung; // `abteilung` in req speichern
next();
},
featureRoutes
);
app.use(
"/:abteilung?/member",
(req, res, next) => {

View File

@ -20,6 +20,36 @@ const requireAuth = (req, res, next) => {
}
};
function requireRole(minRole) {
return (req, res, next) => {
const user = req.user; // Aus decoded JWT
const abteilung = req.abteilung;
if (!user || !user.roles || !abteilung) {
return res
.status(403)
.json({ error: "Keine Berechtigung (keine Abteilung gesetzt)" });
}
const role = user.roles[abteilung];
if (!role) {
return res
.status(403)
.json({ error: "Keine Rechte für diese Abteilung" });
}
console.log(role);
console.log(minRole);
console.log(role <= minRole);
if (role > minRole) {
// z.B. 1: Admin, 2: ÜL, etc.
return res.status(403).json({ error: "Nicht genügend Rechte" });
}
next();
};
}
const requireAdmin = async (req, res, next) => {
try {
const result = await pool.query("SELECT role FROM users WHERE id = $1", [
@ -37,4 +67,4 @@ const requireAdmin = async (req, res, next) => {
}
};
module.exports = { requireAuth, requireAdmin };
module.exports = { requireAuth, requireRole, requireAdmin };

View File

@ -1,11 +1,15 @@
const express = require("express");
const pool = require("../db"); // PostgreSQL-Datenbankverbindung
const { requireAuth, requireAdmin } = require("../middleware/auth"); // Auth-Middleware
const {
requireAuth,
requireRole,
requireAdmin,
} = require("../middleware/auth"); // Auth-Middleware
const router = express.Router();
// **1. Alle Anwesenheiten eines Trainings abrufen**
router.get("/:id", requireAuth, async (req, res) => {
router.get("/:id", requireAuth, requireRole(3), async (req, res) => {
const { id } = req.params; // Training-ID
const abteilung = req.abteilung; // Abteilung aus Middleware
@ -34,7 +38,7 @@ router.get("/:id", requireAuth, async (req, res) => {
});
// **2. Anwesenheit für ein Training setzen**
router.post("/:id", requireAuth, async (req, res) => {
router.post("/:id", requireAuth, requireRole(3), async (req, res) => {
const { id } = req.params; // Training-ID
const { inriege, anw } = req.body; // Arrays mit IDs

View File

@ -1,20 +1,25 @@
const express = require("express");
const pool = require("../db"); // PostgreSQL-Datenbankverbindung
const { requireAuth, requireAdmin } = require("../middleware/auth"); // Auth-Middleware
const {
requireAuth,
requireRole,
requireAdmin,
} = require("../middleware/auth"); // Auth-Middleware
const router = express.Router();
router.get("/:id?", requireAuth, async (req, res) => {
router.get("/:id?", requireAuth, requireRole(3), async (req, res) => {
const { id } = req.params; // Mitglieds-ID (optional)
const abteilung = req.abteilung; // Abteilung aus Middleware
console.log(abteilung);
try {
let query;
let values = [];
if (id) {
if (abteilung) {
// **Einzelnes Mitglied mit aktueller Riege abrufen**
console.log("**Einzelnes Mitglied mit aktueller Riege abrufen**");
query = `
SELECT
m.*,
@ -32,12 +37,12 @@ router.get("/:id?", requireAuth, async (req, res) => {
`;
values = [id, abteilung];
} else {
// **Einzelnes Mitglied ohne Riege abrufen**
console.log(" **Einzelnes Mitglied ohne Riege abrufen**");
query = `SELECT * FROM members WHERE id = $1`;
values = [id];
}
} else if (abteilung) {
// **Alle Mitglieder einer Abteilung abrufen**
console.log("**Alle Mitglieder einer Abteilung abrufen**");
query = `
SELECT
m.*,
@ -51,11 +56,11 @@ router.get("/:id?", requireAuth, async (req, res) => {
AND riegenzuordnung.bis IS NULL
LEFT JOIN riegen r
ON riegenzuordnung.fid_riege = r.id
WHERE r.fid_abteilung = $1 OR r.fid_abteilung IS NULL
WHERE r.fid_abteilung = $1
`;
values = [abteilung];
} else {
// **Alle Mitglieder abrufen**
console.log("**Alle Mitglieder abrufen**");
query = `SELECT * FROM mitglieder`;
}
@ -73,7 +78,7 @@ router.get("/:id?", requireAuth, async (req, res) => {
});
// **2. Mitglied anlegen oder aktualisieren (Nur für Admins)**
router.put("/:id?", requireAuth, requireAdmin, async (req, res) => {
router.put("/:id?", requireAuth, requireRole(3), async (req, res) => {
const { id } = req.params;
const {
vorname,
@ -174,7 +179,7 @@ router.put("/:id?", requireAuth, requireAdmin, async (req, res) => {
});
// **3. Mitglied löschen (Nur für Admins)**
router.delete("/:id", requireAuth, requireAdmin, async (req, res) => {
router.delete("/:id", requireAuth, requireRole(2), async (req, res) => {
const { id } = req.params;
try {
@ -197,7 +202,7 @@ router.delete("/:id", requireAuth, requireAdmin, async (req, res) => {
});
// **1. notfallnummern abrufen (Falls ID gegeben: Nur die Nummern eines Mitglieds)**
router.get("/phone/:id", requireAuth, async (req, res) => {
router.get("/phone/:id", requireAuth, requireRole(3), async (req, res) => {
const { id } = req.params;
try {
@ -220,7 +225,7 @@ router.get("/phone/:id", requireAuth, async (req, res) => {
});
// **2. Telefonnummer anlegen oder aktualisieren (Nur für Admins)**
router.put("/phone/:id?", requireAuth, requireAdmin, async (req, res) => {
router.put("/phone/:id?", requireAuth, requireRole(3), async (req, res) => {
const { id } = req.params;
const { fid_teilnehmer, name, nummer, verbindung, stand } = req.body;
@ -285,7 +290,7 @@ router.put("/phone/:id?", requireAuth, requireAdmin, async (req, res) => {
});
// **3. Telefonnummer löschen (Nur für Admins)**
router.delete("/phone/:id", requireAuth, requireAdmin, async (req, res) => {
router.delete("/phone/:id", requireAuth, requireRole(2), async (req, res) => {
const { id } = req.params;
try {
@ -307,7 +312,7 @@ router.delete("/phone/:id", requireAuth, requireAdmin, async (req, res) => {
}
});
router.post("/riege/:id", requireAuth, requireAdmin, async (req, res) => {
router.post("/riege/:id", requireAuth, requireRole(2), async (req, res) => {
const { id } = req.params;
const abteilung = req.abteilung;
const { neue_riege } = req.body;

View File

@ -61,7 +61,7 @@ router.delete("/:id", requireAuth, requireAdmin, async (req, res) => {
}
});
router.put("/:id?", requireAuth, requireAdmin, async (req, res) => {
router.put("/:id?", requireAuth, async (req, res) => {
const { id } = req.params;
const { name, material, regeln, variationen, dauer, type } = req.body;
@ -115,5 +115,4 @@ router.put("/:id?", requireAuth, requireAdmin, async (req, res) => {
}
});
module.exports = router;

View File

@ -1,6 +1,6 @@
const express = require("express");
const pool = require("../db"); // PostgreSQL-Datenbankverbindung
const { requireAuth, requireAdmin } = require("../middleware/auth"); // Auth-Middleware
const { requireAuth, requireRole } = require("../middleware/auth"); // Auth-Middleware
const router = express.Router();
@ -55,7 +55,7 @@ router.get("/:year?/:kw?", requireAuth, async (req, res) => {
}
});
router.post("/leiten/:id", requireAuth, async (req, res) => {
router.post("/leiten/:id", requireAuth, requireRole(3), async (req, res) => {
const { id } = req.params; // Trainings-ID
const abteilung = req.abteilung; // Abteilung aus Middleware
const { fid_helfer, fid_spiel, typ, action } = req.body; // Daten aus POST-Body
@ -122,7 +122,7 @@ router.post("/leiten/:id", requireAuth, async (req, res) => {
}
});
router.post("/new/:jahr/:kw", requireAuth, requireAdmin, async (req, res) => {
router.post("/new/:jahr/:kw", requireAuth, requireRole(2), async (req, res) => {
const { jahr, kw } = req.params;
const abteilung = req.abteilung; // Abteilung aus Middleware

View File

@ -4,7 +4,11 @@ const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const crypto = require("crypto");
const nodemailer = require("nodemailer");
const { requireAuth, requireAdmin } = require("../middleware/auth"); // Falls Middleware in einer extra Datei liegt
const {
requireAuth,
requireRole,
requireAdmin,
} = require("../middleware/auth"); // Falls Middleware in einer extra Datei liegt
const { transporter, sendActivationEmail } = require("../middleware/mail");
const router = express.Router();
@ -47,7 +51,7 @@ router.post("/login", async (req, res) => {
try {
const userResult = await pool.query(
"SELECT *, CASE WHEN admin_temp IS NOT NULL AND (now() - admin_temp) > interval '22 hours' THEN 'expired' ELSE 'valid' END AS admin_status FROM users WHERE username = $1",
"SELECT * FROM users WHERE username = $1",
[username]
);
if (userResult.rows.length > 0) {
@ -56,20 +60,24 @@ router.post("/login", async (req, res) => {
const match = await bcrypt.compare(password, user.password);
if (match) {
if (user.is_active) {
const rightsResult = await pool.query(
`SELECT fid_abteilung, fid_role FROM userrights WHERE fid_user = $1`,
[user.id]
);
// Rollen-Map erstellen:
const roles = {};
rightsResult.rows.forEach((row) => {
roles[row.fid_abteilung] = row.fid_role;
});
const token = jwt.sign(
{ id: user.id, username: user.username, role: user.role },
{ id: user.id, username: user.username, roles },
process.env.JWT_SECRET,
{
expiresIn: "24h",
expiresIn: "24d",
}
);
if (user.admin_status === "expired") {
await pool.query(
"UPDATE users SET role = $1, admin_temp = NULL WHERE id = $2",
["user", user.id]
);
}
res.json({ token });
res.json({ token, roles });
} else {
return res.status(401).json({ error: "Auf Freischlatung warten" });
}
@ -209,10 +217,40 @@ router.post("/update", requireAuth, async (req, res) => {
// Liste der User mit Rollen und is_active
router.get("/admin", requireAuth, requireAdmin, async (req, res) => {
try {
const result = await pool.query(
"SELECT id, username, email, role, is_active FROM users ORDER BY is_active ASC, role DESC"
// 1⃣ Alle Benutzer abfragen:
const usersResult = await pool.query(
"SELECT id, username, email, is_active FROM users ORDER BY is_active ASC"
);
res.json(result.rows);
const users = usersResult.rows;
// 2⃣ Alle Rollen pro User abfragen:
const rolesResult = await pool.query(
`SELECT ur.fid_user, ur.fid_abteilung, ur.fid_role, r.role
FROM userrights ur
JOIN userroles r ON ur.fid_role = r.id`
);
// 3⃣ Rollen für Benutzer gruppieren:
const userRolesMap = {};
rolesResult.rows.forEach((row) => {
if (!userRolesMap[row.fid_user]) {
userRolesMap[row.fid_user] = [];
}
userRolesMap[row.fid_user].push({
abteilung: row.fid_abteilung,
role_id: row.fid_role,
role_name: row.role,
});
});
// 4⃣ Benutzer + Rollen zusammenführen:
const enrichedUsers = users.map((user) => ({
...user,
roles: userRolesMap[user.id] || [],
}));
res.json(enrichedUsers);
} catch (err) {
console.error(err);
res.status(500).json({ error: "Interner Serverfehler" });
@ -258,6 +296,49 @@ router.put("/activate/:id", requireAuth, requireAdmin, async (req, res) => {
}
});
router.put(
"/rights/:userId/:role",
requireAuth,
requireRole(2),
async (req, res) => {
const { userId, role } = req.params;
const abteilung = req.abteilung; // Abteilung aus Middleware
if (!abteilung || !role) {
return res
.status(400)
.json({ error: "Abteilung und Rolle müssen angegeben werden" });
}
try {
// Prüfen, ob bereits ein Recht für User & Abteilung existiert
const existing = await pool.query(
`SELECT id FROM userrights WHERE fid_user = $1 AND fid_abteilung = $2`,
[userId, abteilung]
);
if (existing.rows.length > 0) {
// Eintrag existiert → UPDATE
await pool.query(`UPDATE userrights SET fid_role = $1 WHERE id = $2`, [
role,
existing.rows[0].id,
]);
} else {
// Kein Eintrag → INSERT
await pool.query(
`INSERT INTO userrights (fid_user, fid_abteilung, fid_role) VALUES ($1, $2, $3)`,
[userId, abteilung, role]
);
}
res.json({ message: "Rolle erfolgreich vergeben/geändert" });
} catch (err) {
console.error(err);
res.status(500).json({ error: "Interner Serverfehler" });
}
}
);
router.delete("/:id", requireAuth, requireAdmin, async (req, res) => {
const { id } = req.params;