Add password change routes to backend and frontend; update routing and UI components for password management

This commit is contained in:
Torsten Schulz (local)
2025-10-17 23:13:33 +02:00
parent b2cef4d306
commit 4fe6b27b8f
8 changed files with 430 additions and 0 deletions

71
backend/set-password.js Normal file
View File

@@ -0,0 +1,71 @@
/**
* Script zum Setzen eines neuen Passworts für einen User
* Verwendung: node set-password.js <user_id> <neues_passwort>
*/
require('dotenv').config();
const bcrypt = require('bcrypt');
const database = require('./src/config/database');
async function setPassword(userId, newPassword) {
try {
// Datenbank initialisieren
await database.initialize();
const { User, AuthInfo } = database.getModels();
// Hole User
const user = await User.findByPk(userId, {
include: [
{
model: AuthInfo,
as: 'authInfo',
required: true
}
]
});
if (!user || !user.authInfo) {
console.error(`❌ User ${userId} oder AuthInfo nicht gefunden`);
process.exit(1);
}
// Hash Passwort
console.log('🔐 Hashe Passwort...');
const hashedPassword = await bcrypt.hash(newPassword, 10);
// Aktualisiere Passwort
user.authInfo.password_hash = hashedPassword;
await user.authInfo.save();
console.log(`✅ Passwort für User ${userId} (${user.full_name}) erfolgreich gesetzt!`);
console.log(` Email: ${user.authInfo.email}`);
// Schließe Datenbankverbindung
await database.close();
process.exit(0);
} catch (error) {
console.error('❌ Fehler beim Setzen des Passworts:', error.message);
await database.close();
process.exit(1);
}
}
// Argumente prüfen
const userId = parseInt(process.argv[2]);
const newPassword = process.argv[3];
if (!userId || !newPassword) {
console.log('Verwendung: node set-password.js <user_id> <neues_passwort>');
console.log('Beispiel: node set-password.js 1 MeinNeuesPasswort123');
process.exit(1);
}
if (newPassword.length < 6) {
console.error('❌ Passwort muss mindestens 6 Zeichen lang sein');
process.exit(1);
}
// Passwort setzen
setPassword(userId, newPassword);

View File

@@ -0,0 +1,47 @@
const PasswordService = require('../services/PasswordService');
/**
* Controller für Passwort-Verwaltung
* Verarbeitet HTTP-Requests und delegiert an PasswordService
*/
class PasswordController {
/**
* Ändert das Passwort
*/
async changePassword(req, res) {
try {
const userId = req.user?.id || 1;
const { oldPassword, newPassword, confirmPassword } = req.body;
if (!oldPassword || !newPassword || !confirmPassword) {
return res.status(400).json({
message: 'Alle Felder sind erforderlich'
});
}
if (newPassword !== confirmPassword) {
return res.status(400).json({
message: 'Die Passwörter stimmen nicht überein'
});
}
await PasswordService.changePassword(userId, oldPassword, newPassword);
res.json({ message: 'Passwort erfolgreich geändert' });
} catch (error) {
console.error('Fehler beim Ändern des Passworts:', error);
// Spezifische Fehlermeldungen
if (error.message.includes('alte Passwort')) {
return res.status(401).json({ message: error.message });
}
res.status(500).json({
message: error.message || 'Fehler beim Ändern des Passworts'
});
}
}
}
module.exports = new PasswordController();

View File

@@ -94,6 +94,10 @@ app.use('/api/holidays', authenticateToken, holidaysRouter);
const profileRouter = require('./routes/profile');
app.use('/api/profile', authenticateToken, profileRouter);
// Password routes (geschützt) - MIT ID-Hashing
const passwordRouter = require('./routes/password');
app.use('/api/password', authenticateToken, passwordRouter);
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);

View File

@@ -0,0 +1,13 @@
const express = require('express');
const router = express.Router();
const PasswordController = require('../controllers/PasswordController');
/**
* Routen für Passwort-Verwaltung
*/
// PUT /api/password - Passwort ändern
router.put('/', PasswordController.changePassword.bind(PasswordController));
module.exports = router;

View File

@@ -0,0 +1,68 @@
const database = require('../config/database');
const bcrypt = require('bcrypt');
/**
* Service-Klasse für Passwort-Verwaltung
*/
class PasswordService {
/**
* Ändert das Passwort eines Benutzers
* @param {number} userId - Benutzer-ID
* @param {string} oldPassword - Altes Passwort
* @param {string} newPassword - Neues Passwort
* @returns {Promise<void>}
*/
async changePassword(userId, oldPassword, newPassword) {
const { User, AuthInfo } = database.getModels();
// Hole User und AuthInfo
const user = await User.findByPk(userId, {
include: [
{
model: AuthInfo,
as: 'authInfo',
required: true
}
]
});
if (!user || !user.authInfo) {
throw new Error('Benutzer oder Login-Informationen nicht gefunden');
}
const authInfo = user.authInfo;
// Prüfe ob Passwort gesetzt ist
if (!authInfo.password_hash) {
throw new Error('Für diesen Account ist kein Passwort gesetzt. Möglicherweise verwenden Sie OAuth-Login.');
}
// Prüfe altes Passwort
const isValidPassword = await bcrypt.compare(oldPassword, authInfo.password_hash);
if (!isValidPassword) {
throw new Error('Das alte Passwort ist nicht korrekt');
}
// Prüfe neues Passwort
if (!newPassword || newPassword.length < 6) {
throw new Error('Das neue Passwort muss mindestens 6 Zeichen lang sein');
}
if (newPassword === oldPassword) {
throw new Error('Das neue Passwort darf nicht mit dem alten übereinstimmen');
}
// Hash neues Passwort
const hashedPassword = await bcrypt.hash(newPassword, 10);
// Aktualisiere Passwort
authInfo.password_hash = hashedPassword;
await authInfo.save();
console.log(`Passwort für User ${userId} erfolgreich geändert`);
}
}
module.exports = new PasswordService();