303 lines
8.8 KiB
JavaScript
Executable File
303 lines
8.8 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
||
/**
|
||
* Script zum Setzen des Admin-Passworts
|
||
*
|
||
* Verwendung:
|
||
* node scripts/set-admin-password.js <neues-passwort>
|
||
*
|
||
* Oder interaktiv:
|
||
* node scripts/set-admin-password.js
|
||
*/
|
||
|
||
import fs from 'fs/promises'
|
||
import path from 'path'
|
||
import { fileURLToPath } from 'url'
|
||
import bcrypt from 'bcryptjs'
|
||
import { decryptObject, encryptObject } from '../server/utils/encryption.js'
|
||
import dotenv from 'dotenv'
|
||
import readline from 'readline'
|
||
|
||
const __filename = fileURLToPath(import.meta.url)
|
||
const __dirname = path.dirname(__filename)
|
||
|
||
// Lade .env Datei
|
||
dotenv.config({ path: path.join(__dirname, '..', '.env') })
|
||
|
||
const ADMIN_EMAIL = 'admin@harheimertc.de'
|
||
|
||
// Pfade bestimmen
|
||
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
|
||
// filename is always a hardcoded constant (e.g., 'users.json'), never user input
|
||
function getDataPath(filename) {
|
||
const cwd = process.cwd()
|
||
if (cwd.endsWith('.output')) {
|
||
return path.join(cwd, '../server/data', filename)
|
||
}
|
||
return path.join(cwd, 'server/data', filename)
|
||
}
|
||
|
||
const USERS_FILE = getDataPath('users.json')
|
||
|
||
// Get encryption key from environment
|
||
function getEncryptionKey() {
|
||
return process.env.ENCRYPTION_KEY || 'local_development_encryption_key_change_in_production'
|
||
}
|
||
|
||
// Prüft ob Daten verschlüsselt sind
|
||
function isEncrypted(data) {
|
||
try {
|
||
const parsed = JSON.parse(data.trim())
|
||
if (Array.isArray(parsed)) {
|
||
return false
|
||
}
|
||
if (typeof parsed === 'object' && parsed !== null && !parsed.encryptedData) {
|
||
return false
|
||
}
|
||
return false
|
||
} catch (e) {
|
||
return true
|
||
}
|
||
}
|
||
|
||
// Liest Benutzer aus Datei
|
||
async function readUsers() {
|
||
try {
|
||
const data = await fs.readFile(USERS_FILE, 'utf-8')
|
||
const encrypted = isEncrypted(data)
|
||
|
||
if (encrypted) {
|
||
const encryptionKey = getEncryptionKey()
|
||
try {
|
||
return decryptObject(data, encryptionKey)
|
||
} catch (decryptError) {
|
||
console.error('Fehler beim Entschlüsseln der Benutzerdaten:', decryptError.message)
|
||
throw new Error('Konnte Benutzerdaten nicht entschlüsseln. Bitte prüfe ENCRYPTION_KEY in .env')
|
||
}
|
||
} else {
|
||
return JSON.parse(data)
|
||
}
|
||
} catch (error) {
|
||
if (error.code === 'ENOENT') {
|
||
return []
|
||
}
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// Schreibt Benutzer in Datei (immer verschlüsselt)
|
||
async function writeUsers(users) {
|
||
try {
|
||
const encryptionKey = getEncryptionKey()
|
||
const encryptedData = encryptObject(users, encryptionKey)
|
||
await fs.writeFile(USERS_FILE, encryptedData, 'utf-8')
|
||
return true
|
||
} catch (error) {
|
||
console.error('Fehler beim Schreiben der Benutzerdaten:', error)
|
||
return false
|
||
}
|
||
}
|
||
|
||
// Hash-Passwort generieren
|
||
async function hashPassword(password) {
|
||
const salt = await bcrypt.genSalt(10)
|
||
return await bcrypt.hash(password, salt)
|
||
}
|
||
|
||
// Fragt nach Passwort (wenn nicht als Argument übergeben)
|
||
function askPassword() {
|
||
return new Promise((resolve) => {
|
||
const rl = readline.createInterface({
|
||
input: process.stdin,
|
||
output: process.stdout
|
||
})
|
||
|
||
rl.question('Neues Passwort für admin@harheimertc.de: ', (password) => {
|
||
rl.close()
|
||
resolve(password)
|
||
})
|
||
})
|
||
}
|
||
|
||
// Fragt nach Bestätigung
|
||
function askConfirmation(question) {
|
||
return new Promise((resolve) => {
|
||
const rl = readline.createInterface({
|
||
input: process.stdin,
|
||
output: process.stdout
|
||
})
|
||
|
||
rl.question(`${question} (j/n): `, (answer) => {
|
||
rl.close()
|
||
resolve(answer.toLowerCase() === 'j' || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'ja' || answer.toLowerCase() === 'yes')
|
||
})
|
||
})
|
||
}
|
||
|
||
// Erstellt ein Backup der users.json
|
||
async function createBackup() {
|
||
try {
|
||
await fs.access(USERS_FILE)
|
||
const backupDir = path.join(__dirname, '..', 'backups', `users-${Date.now()}`)
|
||
await fs.mkdir(backupDir, { recursive: true })
|
||
const backupPath = path.join(backupDir, 'users.json')
|
||
await fs.copyFile(USERS_FILE, backupPath)
|
||
console.log(`📦 Backup erstellt: ${backupPath}`)
|
||
return backupPath
|
||
} catch (error) {
|
||
if (error.code === 'ENOENT') {
|
||
console.log('ℹ️ users.json existiert nicht, kein Backup nötig')
|
||
return null
|
||
}
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// Erstellt eine neue users.json mit Admin-User
|
||
async function createNewUsersFile(password) {
|
||
const hashedPassword = await hashPassword(password)
|
||
|
||
const adminUser = {
|
||
id: Date.now().toString(),
|
||
email: ADMIN_EMAIL,
|
||
password: hashedPassword,
|
||
name: 'Administrator',
|
||
role: 'admin',
|
||
active: true,
|
||
created: new Date().toISOString(),
|
||
lastLogin: null
|
||
}
|
||
|
||
return [adminUser]
|
||
}
|
||
|
||
// Hauptfunktion
|
||
async function main() {
|
||
console.log('🔐 Admin-Passwort setzen\n')
|
||
|
||
// Prüfe ob --recreate Flag gesetzt ist
|
||
const recreateFlag = process.argv.includes('--recreate') || process.argv.includes('-r')
|
||
|
||
// Passwort aus Kommandozeilenargumenten oder interaktiv abfragen
|
||
// Filtere Flags heraus und nimm das erste verbleibende Argument als Passwort
|
||
const args = process.argv.slice(2).filter(arg => !arg.startsWith('--') && !arg.startsWith('-'))
|
||
let newPassword = args.length > 0 ? args[0] : null
|
||
|
||
if (!newPassword) {
|
||
newPassword = await askPassword()
|
||
}
|
||
|
||
if (!newPassword || newPassword.trim().length === 0) {
|
||
console.error('❌ FEHLER: Passwort darf nicht leer sein!')
|
||
process.exit(1)
|
||
}
|
||
|
||
if (newPassword.length < 8) {
|
||
console.error('❌ FEHLER: Passwort muss mindestens 8 Zeichen lang sein!')
|
||
process.exit(1)
|
||
}
|
||
|
||
try {
|
||
// Benutzer laden
|
||
console.log('📖 Lade Benutzerdaten...')
|
||
let users
|
||
let shouldRecreate = recreateFlag
|
||
|
||
try {
|
||
users = await readUsers()
|
||
} catch (error) {
|
||
if (error.message.includes('entschlüsseln') || error.message.includes('Entschlüsselung')) {
|
||
console.error('\n⚠️ WARNUNG: Konnte Benutzerdaten nicht entschlüsseln!')
|
||
console.error(' Dies bedeutet, dass der ENCRYPTION_KEY nicht mit dem verwendeten Schlüssel übereinstimmt.\n')
|
||
|
||
if (!shouldRecreate) {
|
||
const confirmed = await askConfirmation('Möchten Sie die users.json Datei neu erstellen? (Alte Datei wird als Backup gespeichert)')
|
||
if (!confirmed) {
|
||
console.log('\n❌ Abgebrochen. Bitte setzen Sie ENCRYPTION_KEY in der .env Datei auf den korrekten Wert.')
|
||
process.exit(1)
|
||
}
|
||
shouldRecreate = true
|
||
}
|
||
|
||
// Backup erstellen
|
||
await createBackup()
|
||
|
||
// Neue Datei erstellen
|
||
console.log('\n🆕 Erstelle neue users.json Datei...')
|
||
users = await createNewUsersFile(newPassword)
|
||
|
||
// Direkt speichern
|
||
console.log('💾 Speichere neue Benutzerdaten...')
|
||
const success = await writeUsers(users)
|
||
|
||
if (success) {
|
||
console.log('\n✅ Neue users.json Datei erfolgreich erstellt!')
|
||
console.log(`📧 E-Mail: ${ADMIN_EMAIL}`)
|
||
console.log(`👤 Rolle: admin`)
|
||
console.log(`✅ Status: Aktiv`)
|
||
console.log(`🔐 Passwort: Gesetzt`)
|
||
} else {
|
||
console.error('\n❌ FEHLER: Konnte Benutzerdaten nicht speichern!')
|
||
process.exit(1)
|
||
}
|
||
|
||
return
|
||
} else {
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// Admin-User finden oder erstellen
|
||
let adminUser = users.find(u => u.email.toLowerCase() === ADMIN_EMAIL.toLowerCase())
|
||
|
||
if (!adminUser) {
|
||
console.log(`ℹ️ Admin-User (${ADMIN_EMAIL}) nicht gefunden, erstelle neuen Benutzer...`)
|
||
adminUser = {
|
||
id: Date.now().toString(),
|
||
email: ADMIN_EMAIL,
|
||
name: 'Administrator',
|
||
role: 'admin',
|
||
active: true,
|
||
created: new Date().toISOString(),
|
||
lastLogin: null
|
||
}
|
||
users.push(adminUser)
|
||
} else {
|
||
console.log(`✅ Admin-User gefunden: ${adminUser.name || ADMIN_EMAIL}`)
|
||
}
|
||
|
||
// Passwort hashen
|
||
console.log('🔐 Hashe Passwort...')
|
||
const hashedPassword = await hashPassword(newPassword)
|
||
|
||
// Passwort setzen
|
||
adminUser.password = hashedPassword
|
||
adminUser.updated = new Date().toISOString()
|
||
|
||
// Benutzer speichern
|
||
console.log('💾 Speichere Benutzerdaten...')
|
||
const success = await writeUsers(users)
|
||
|
||
if (success) {
|
||
console.log('\n✅ Passwort erfolgreich gesetzt!')
|
||
console.log(`📧 E-Mail: ${ADMIN_EMAIL}`)
|
||
console.log(`👤 Rolle: ${adminUser.role}`)
|
||
console.log(`✅ Status: ${adminUser.active ? 'Aktiv' : 'Inaktiv'}`)
|
||
} else {
|
||
console.error('\n❌ FEHLER: Konnte Benutzerdaten nicht speichern!')
|
||
process.exit(1)
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('\n❌ FEHLER:', error.message)
|
||
console.error(error.stack)
|
||
process.exit(1)
|
||
}
|
||
}
|
||
|
||
// Script ausführen
|
||
main().catch(error => {
|
||
console.error('Unerwarteter Fehler:', error)
|
||
process.exit(1)
|
||
})
|
||
|