Refactor authentication and data handling in API; implement encryption for user and member data storage. Update relevant components to utilize new encryption methods, ensuring secure data management across the application. Enhance error handling and streamline data writing processes for improved reliability.

This commit is contained in:
Torsten Schulz (local)
2025-11-05 13:49:47 +01:00
parent bc2f59bd1a
commit dd4691b462
58 changed files with 592 additions and 366 deletions

View File

@@ -2,6 +2,7 @@ import bcrypt from 'bcryptjs'
import jwt from 'jsonwebtoken'
import { promises as fs } from 'fs'
import path from 'path'
import { encryptObject, decryptObject } from './encryption.js'
const JWT_SECRET = process.env.JWT_SECRET || 'harheimertc-secret-key-change-in-production'
@@ -21,21 +22,75 @@ const getDataPath = (filename) => {
const USERS_FILE = getDataPath('users.json')
const SESSIONS_FILE = getDataPath('sessions.json')
// Read users from file
// Get encryption key from environment
function getEncryptionKey() {
return process.env.ENCRYPTION_KEY || 'default-key-change-in-production'
}
// Check if data is encrypted by trying to parse as JSON first
function isEncrypted(data) {
try {
const parsed = JSON.parse(data.trim())
if (Array.isArray(parsed)) {
return false // Unencrypted array
}
if (typeof parsed === 'object' && parsed !== null && !parsed.encryptedData) {
return false
}
return false
} catch (e) {
// JSON parsing failed - likely encrypted base64
return true
}
}
// Read users from file (with encryption support and migration)
export async function readUsers() {
try {
const data = await fs.readFile(USERS_FILE, 'utf-8')
return JSON.parse(data)
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)
try {
const plainData = JSON.parse(data)
console.warn('Entschlüsselung fehlgeschlagen, versuche als unverschlüsseltes Format zu lesen')
return plainData
} catch (parseError) {
console.error('Konnte Benutzerdaten weder entschlüsseln noch als JSON lesen')
return []
}
}
} else {
// Plain JSON - migrate to encrypted format
const users = JSON.parse(data)
console.log('Migriere unverschlüsselte Benutzerdaten zu verschlüsselter Speicherung...')
// Write back encrypted
await writeUsers(users)
return users
}
} catch (error) {
if (error.code === 'ENOENT') {
return []
}
console.error('Fehler beim Lesen der Benutzerdaten:', error)
return []
}
}
// Write users to file
// Write users to file (always encrypted)
export async function writeUsers(users) {
try {
await fs.writeFile(USERS_FILE, JSON.stringify(users, null, 2), 'utf-8')
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)

View File

@@ -1,6 +1,7 @@
import { promises as fs } from 'fs'
import path from 'path'
import { randomUUID } from 'crypto'
import { encrypt, decrypt, encryptObject, decryptObject } from './encryption.js'
// Handle both dev and production paths
const getDataPath = (filename) => {
@@ -17,11 +18,66 @@ const getDataPath = (filename) => {
const MEMBERS_FILE = getDataPath('members.json')
// Read manual members from file
// Get encryption key from environment or config
function getEncryptionKey() {
return process.env.ENCRYPTION_KEY || 'default-key-change-in-production'
}
// Check if data is encrypted by trying to parse as JSON first
function isEncrypted(data) {
try {
// Try to parse as JSON - if successful and looks like member data, it's unencrypted
const parsed = JSON.parse(data.trim())
// If it's an array (members list) or object with member-like structure, it's unencrypted
if (Array.isArray(parsed)) {
return false // Unencrypted array
}
// If it's an object but not encrypted format, it's unencrypted
if (typeof parsed === 'object' && parsed !== null && !parsed.encryptedData) {
return false
}
return false
} catch (e) {
// JSON parsing failed - likely encrypted base64
return true
}
}
// Read manual members from file (with encryption support and migration)
export async function readMembers() {
try {
const data = await fs.readFile(MEMBERS_FILE, 'utf-8')
return JSON.parse(data)
// Check if data is encrypted or plain JSON
const encrypted = isEncrypted(data)
if (encrypted) {
// Decrypt and parse
const encryptionKey = getEncryptionKey()
try {
return decryptObject(data, encryptionKey)
} catch (decryptError) {
console.error('Fehler beim Entschlüsseln der Mitgliederdaten:', decryptError)
// Fallback: try to read as plain JSON (migration scenario)
try {
const plainData = JSON.parse(data)
console.warn('Entschlüsselung fehlgeschlagen, versuche als unverschlüsseltes Format zu lesen')
return plainData
} catch (parseError) {
console.error('Konnte Mitgliederdaten weder entschlüsseln noch als JSON lesen')
return []
}
}
} else {
// Plain JSON - migrate to encrypted format
const members = JSON.parse(data)
console.log('Migriere unverschlüsselte Mitgliederdaten zu verschlüsselter Speicherung...')
// Write back encrypted
await writeMembers(members)
return members
}
} catch (error) {
if (error.code === 'ENOENT') {
return []
@@ -31,10 +87,12 @@ export async function readMembers() {
}
}
// Write manual members to file
// Write manual members to file (always encrypted)
export async function writeMembers(members) {
try {
await fs.writeFile(MEMBERS_FILE, JSON.stringify(members, null, 2), 'utf-8')
const encryptionKey = getEncryptionKey()
const encryptedData = encryptObject(members, encryptionKey)
await fs.writeFile(MEMBERS_FILE, encryptedData, 'utf-8')
return true
} catch (error) {
console.error('Fehler beim Schreiben der Mitgliederdaten:', error)