Added Krank Button, LDAP Sync auth

This commit is contained in:
Carsten Graf
2026-01-23 10:32:20 +01:00
parent 743a103465
commit f7b1322ae6
4 changed files with 291 additions and 73 deletions

170
server.js
View File

@@ -139,56 +139,101 @@ app.get('/login', (req, res) => {
app.post('/login', (req, res) => {
const { username, password } = req.body;
db.get('SELECT * FROM users WHERE username = ?', [username], (err, user) => {
if (err || !user) {
return res.render('login', { error: 'Ungültiger Benutzername oder Passwort' });
// Prüfe ob LDAP aktiviert ist
LDAPService.getConfig((err, ldapConfig) => {
if (err) {
console.error('Fehler beim Abrufen der LDAP-Konfiguration:', err);
}
if (bcrypt.compareSync(password, user.password)) {
// Rollen als JSON-Array parsen
let roles = [];
try {
roles = JSON.parse(user.role);
if (!Array.isArray(roles)) {
// Fallback: Falls kein Array, erstelle Array mit vorhandener Rolle
roles = [user.role];
const isLDAPEnabled = ldapConfig && ldapConfig.enabled === 1;
// Wenn LDAP aktiviert ist, authentifiziere gegen LDAP
if (isLDAPEnabled) {
LDAPService.authenticate(username, password, (authErr, authSuccess) => {
if (authErr || !authSuccess) {
// LDAP-Authentifizierung fehlgeschlagen - prüfe lokale Datenbank als Fallback
db.get('SELECT * FROM users WHERE username = ?', [username], (err, user) => {
if (err || !user) {
return res.render('login', { error: 'Ungültiger Benutzername oder Passwort' });
}
// Versuche lokale Authentifizierung
if (bcrypt.compareSync(password, user.password)) {
handleSuccessfulLogin(req, res, user);
} else {
res.render('login', { error: 'Ungültiger Benutzername oder Passwort' });
}
});
} else {
// LDAP-Authentifizierung erfolgreich - hole Benutzer aus Datenbank
db.get('SELECT * FROM users WHERE username = ?', [username], (err, user) => {
if (err || !user) {
return res.render('login', { error: 'Benutzer nicht in der Datenbank gefunden. Bitte führen Sie eine LDAP-Synchronisation durch.' });
}
handleSuccessfulLogin(req, res, user);
});
}
} catch (e) {
// Fallback: Falls kein JSON, erstelle Array mit vorhandener Rolle
roles = [user.role || 'mitarbeiter'];
}
// Standard-Rolle bestimmen: Immer "mitarbeiter" wenn vorhanden, sonst höchste Priorität
let defaultRole;
if (roles.includes('mitarbeiter')) {
defaultRole = 'mitarbeiter';
} else {
defaultRole = getDefaultRole(roles);
}
req.session.userId = user.id;
req.session.username = user.username;
req.session.roles = roles;
req.session.currentRole = defaultRole;
req.session.firstname = user.firstname;
req.session.lastname = user.lastname;
// Redirect: Immer zu Dashboard wenn Mitarbeiter-Rolle vorhanden, sonst basierend auf Standard-Rolle
if (roles.includes('mitarbeiter')) {
res.redirect('/dashboard');
} else if (defaultRole === 'admin') {
res.redirect('/admin');
} else if (defaultRole === 'verwaltung') {
res.redirect('/verwaltung');
} else {
res.redirect('/dashboard');
}
});
} else {
res.render('login', { error: 'Ungültiger Benutzername oder Passwort' });
// LDAP nicht aktiviert - verwende lokale Authentifizierung
db.get('SELECT * FROM users WHERE username = ?', [username], (err, user) => {
if (err || !user) {
return res.render('login', { error: 'Ungültiger Benutzername oder Passwort' });
}
if (bcrypt.compareSync(password, user.password)) {
handleSuccessfulLogin(req, res, user);
} else {
res.render('login', { error: 'Ungültiger Benutzername oder Passwort' });
}
});
}
});
});
// Helper-Funktion für erfolgreiche Anmeldung
function handleSuccessfulLogin(req, res, user) {
// Rollen als JSON-Array parsen
let roles = [];
try {
roles = JSON.parse(user.role);
if (!Array.isArray(roles)) {
// Fallback: Falls kein Array, erstelle Array mit vorhandener Rolle
roles = [user.role];
}
} catch (e) {
// Fallback: Falls kein JSON, erstelle Array mit vorhandener Rolle
roles = [user.role || 'mitarbeiter'];
}
// Standard-Rolle bestimmen: Immer "mitarbeiter" wenn vorhanden, sonst höchste Priorität
let defaultRole;
if (roles.includes('mitarbeiter')) {
defaultRole = 'mitarbeiter';
} else {
defaultRole = getDefaultRole(roles);
}
req.session.userId = user.id;
req.session.username = user.username;
req.session.roles = roles;
req.session.currentRole = defaultRole;
req.session.firstname = user.firstname;
req.session.lastname = user.lastname;
// Redirect: Immer zu Dashboard wenn Mitarbeiter-Rolle vorhanden, sonst basierend auf Standard-Rolle
if (roles.includes('mitarbeiter')) {
res.redirect('/dashboard');
} else if (defaultRole === 'admin') {
res.redirect('/admin');
} else if (defaultRole === 'verwaltung') {
res.redirect('/verwaltung');
} else {
res.redirect('/dashboard');
}
}
// Logout
app.get('/logout', (req, res) => {
req.session.destroy();
@@ -810,7 +855,7 @@ app.post('/api/timesheet/save', requireAuth, (req, res) => {
activity3_desc, activity3_hours, activity3_project_number,
activity4_desc, activity4_hours, activity4_project_number,
activity5_desc, activity5_hours, activity5_project_number,
overtime_taken_hours, vacation_type
overtime_taken_hours, vacation_type, sick_status
} = req.body;
const userId = req.session.userId;
@@ -818,11 +863,21 @@ app.post('/api/timesheet/save', requireAuth, (req, res) => {
const normalizedEndTime = (end_time && typeof end_time === 'string' && end_time.trim() !== '') ? end_time.trim() : (end_time || null);
const normalizedStartTime = (start_time && typeof start_time === 'string' && start_time.trim() !== '') ? start_time.trim() : (start_time || null);
// Normalisiere sick_status: Boolean oder 1/0 zu Boolean
const isSick = sick_status === true || sick_status === 1 || sick_status === 'true' || sick_status === '1';
// Gesamtstunden berechnen (aus Start- und Endzeit, nicht aus Tätigkeiten)
// Wenn ganzer Tag Urlaub, dann zählt dieser als 8 Stunden normale Arbeitszeit
// Wenn ganzer Tag Urlaub oder Krank, dann zählt dieser als 8 Stunden normale Arbeitszeit
let total_hours = 0;
let finalActivity1Desc = activity1_desc;
let finalActivity1Hours = parseFloat(activity1_hours) || 0;
if (vacation_type === 'full') {
total_hours = 8; // Ganzer Tag Urlaub = 8 Stunden normale Arbeitszeit
} else if (isSick) {
total_hours = 8; // Krank = 8 Stunden normale Arbeitszeit
finalActivity1Desc = 'Krank';
finalActivity1Hours = 8;
} else if (normalizedStartTime && normalizedEndTime) {
const start = new Date(`2000-01-01T${normalizedStartTime}`);
const end = new Date(`2000-01-01T${normalizedEndTime}`);
@@ -844,18 +899,19 @@ app.post('/api/timesheet/save', requireAuth, (req, res) => {
activity3_desc = ?, activity3_hours = ?, activity3_project_number = ?,
activity4_desc = ?, activity4_hours = ?, activity4_project_number = ?,
activity5_desc = ?, activity5_hours = ?, activity5_project_number = ?,
overtime_taken_hours = ?, vacation_type = ?,
overtime_taken_hours = ?, vacation_type = ?, sick_status = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?`,
[
normalizedStartTime, normalizedEndTime, break_minutes, total_hours, notes,
activity1_desc || null, parseFloat(activity1_hours) || 0, activity1_project_number || null,
finalActivity1Desc || null, finalActivity1Hours, activity1_project_number || null,
activity2_desc || null, parseFloat(activity2_hours) || 0, activity2_project_number || null,
activity3_desc || null, parseFloat(activity3_hours) || 0, activity3_project_number || null,
activity4_desc || null, parseFloat(activity4_hours) || 0, activity4_project_number || null,
activity5_desc || null, parseFloat(activity5_hours) || 0, activity5_project_number || null,
overtime_taken_hours ? parseFloat(overtime_taken_hours) : null,
vacation_type || null,
isSick ? 1 : 0,
row.id
],
(err) => {
@@ -874,17 +930,18 @@ app.post('/api/timesheet/save', requireAuth, (req, res) => {
activity3_desc, activity3_hours, activity3_project_number,
activity4_desc, activity4_hours, activity4_project_number,
activity5_desc, activity5_hours, activity5_project_number,
overtime_taken_hours, vacation_type)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
overtime_taken_hours, vacation_type, sick_status)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
userId, date, normalizedStartTime, normalizedEndTime, break_minutes, total_hours, notes,
activity1_desc || null, parseFloat(activity1_hours) || 0, activity1_project_number || null,
finalActivity1Desc || null, finalActivity1Hours, activity1_project_number || null,
activity2_desc || null, parseFloat(activity2_hours) || 0, activity2_project_number || null,
activity3_desc || null, parseFloat(activity3_hours) || 0, activity3_project_number || null,
activity4_desc || null, parseFloat(activity4_hours) || 0, activity4_project_number || null,
activity5_desc || null, parseFloat(activity5_hours) || 0, activity5_project_number || null,
overtime_taken_hours ? parseFloat(overtime_taken_hours) : null,
vacation_type || null
vacation_type || null,
isSick ? 1 : 0
],
(err) => {
if (err) {
@@ -1049,10 +1106,10 @@ app.post('/api/timesheet/submit', requireAuth, (req, res) => {
const { week_start, week_end, version_reason } = req.body;
const userId = req.session.userId;
// Validierung: Prüfen ob alle 7 Tage der Woche ausgefüllt sind
db.all(`SELECT id, date, start_time, end_time, vacation_type, updated_at FROM timesheet_entries
WHERE user_id = ? AND date >= ? AND date <= ?
ORDER BY date, updated_at DESC, id DESC`,
// Validierung: Prüfen ob alle 7 Tage der Woche ausgefüllt sind
db.all(`SELECT id, date, start_time, end_time, vacation_type, sick_status, updated_at FROM timesheet_entries
WHERE user_id = ? AND date >= ? AND date <= ?
ORDER BY date, updated_at DESC, id DESC`,
[userId, week_start, week_end],
(err, entries) => {
if (err) {
@@ -1097,8 +1154,9 @@ app.post('/api/timesheet/submit', requireAuth, (req, res) => {
const dateStr = `${year}-${month}-${day}`;
const entry = entriesByDate[dateStr];
// Wenn ganztägiger Urlaub, dann ist der Tag als ausgefüllt zu betrachten
if (entry && entry.vacation_type === 'full') {
// Wenn ganztägiger Urlaub oder Krank, dann ist der Tag als ausgefüllt zu betrachten
const isSick = entry && (entry.sick_status === 1 || entry.sick_status === true);
if (entry && (entry.vacation_type === 'full' || isSick)) {
continue; // Tag ist ausgefüllt
}