Änderung: Erweiterung der Benutzerkontoeinstellungen und Verbesserung der E-Mail-Verschlüsselung

Änderungen:
- Implementierung von neuen Methoden `getAccountSettings` und `setAccountSettings` im `SettingsService`, um Benutzerkontoeinstellungen zu verwalten.
- Anpassung der E-Mail-Verschlüsselung im `User`-Modell zur Verwendung von Buffer für die Speicherung und zur Verbesserung der Fehlerbehandlung bei der Entschlüsselung.
- Hinzufügung eines neuen `immutable`-Feldes im `UserParamType`-Modell, um unveränderliche Einstellungen zu kennzeichnen.
- Anpassungen in den Frontend-Komponenten zur Berücksichtigung von unveränderlichen Feldern und zur Verbesserung der Benutzeroberfläche.

Diese Anpassungen verbessern die Sicherheit der Benutzerdaten und erweitern die Funktionalität der Kontoeinstellungen.
This commit is contained in:
Torsten Schulz (local)
2025-09-15 11:48:00 +02:00
parent eedb1aa7d5
commit d6bfe50b4e
18 changed files with 355 additions and 28 deletions

View File

@@ -11,13 +11,32 @@ const User = sequelize.define('user', {
set(value) {
if (value) {
const encrypted = encrypt(value);
this.setDataValue('email', encrypted);
// Konvertiere Hex-String zu Buffer für die Speicherung
const buffer = Buffer.from(encrypted, 'hex');
this.setDataValue('email', buffer);
}
},
get() {
const encrypted = this.getDataValue('email');
if (encrypted) {
return decrypt(encrypted);
try {
// Konvertiere Buffer zu String für die Entschlüsselung
const encryptedString = encrypted.toString('hex');
const decrypted = decrypt(encryptedString);
if (decrypted) {
return decrypted;
}
} catch (error) {
console.warn('Email decryption failed, treating as plain text:', error.message);
}
// Fallback: Versuche es als Klartext zu lesen
try {
return encrypted.toString('utf8');
} catch (error) {
console.warn('Email could not be read as plain text:', error.message);
return null;
}
}
return null;
}

View File

@@ -35,6 +35,11 @@ const UserParamType = sequelize.define('user_param_type', {
unit: {
type: DataTypes.STRING,
allowNull: true
},
immutable: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
}
}, {
tableName: 'user_param',

View File

@@ -8,6 +8,7 @@ const settingsController = new SettingsController();
router.post('/filter', authenticate, settingsController.filterSettings.bind(settingsController));
router.post('/update', authenticate, settingsController.updateSetting.bind(settingsController));
router.post('/account', authenticate, settingsController.getAccountSettings.bind(settingsController));
router.post('/set-account', authenticate, settingsController.setAccountSettings.bind(settingsController));
router.post('/getparamvalues', settingsController.getTypeParamValues.bind(settingsController));
router.post('/getparamvalueid', settingsController.getTypeParamValueId.bind(settingsController));
router.post('/getparamvalue/:id', settingsController.getTypeParamValue.bind(settingsController));

View File

@@ -103,6 +103,7 @@ class SettingsService extends BaseService{
gender: field.gender,
datatype: field.datatype,
unit: field.unit,
immutable: field.immutable,
value: field.user_params.length > 0 ? field.user_params[0].value : null,
options: options.map(opt => ({ id: opt.id, value: opt.value })),
visibility
@@ -117,6 +118,19 @@ class SettingsService extends BaseService{
if (!paramType) {
throw new Error('Parameter type not found');
}
// Prüfe ob das Feld unveränderlich ist
if (paramType.immutable) {
const userParam = await UserParam.findOne({
where: { userId: user.id, paramTypeId: settingId }
});
// Wenn bereits ein Wert existiert, ist das Feld unveränderlich
if (userParam && userParam.value) {
throw new Error('This field cannot be changed. Please contact support for modifications.');
}
}
const userParam = await UserParam.findOne({
where: { userId: user.id, paramTypeId: settingId }
});
@@ -257,6 +271,81 @@ class SettingsService extends BaseService{
}
}
async getAccountSettings(hashedUserId) {
try {
const user = await this.getUserByHashedId(hashedUserId);
if (!user) {
throw new Error('User not found');
}
// Die Email wird automatisch durch den Getter entschlüsselt
// Falls die Entschlüsselung fehlschlägt, verwende null
let email = null;
try {
email = user.email; // Getter entschlüsselt automatisch
} catch (decryptError) {
console.warn('Email decryption failed, using null:', decryptError.message);
email = null;
}
return {
username: user.username,
email: email,
showinsearch: user.searchable
};
} catch (error) {
console.error('Error getting account settings:', error);
throw error;
}
}
async setAccountSettings({ userId, settings }) {
try {
const user = await this.getUserByHashedId(userId);
if (!user) {
throw new Error('User not found');
}
// Update username if provided
if (settings.username !== undefined) {
await user.update({ username: settings.username });
}
// Update email if provided
if (settings.email !== undefined) {
await user.update({ email: settings.email });
}
// Update searchable flag if provided
if (settings.showinsearch !== undefined) {
await user.update({ searchable: settings.showinsearch });
}
// Update password if provided and not empty
if (settings.newpassword && settings.newpassword.trim() !== '') {
if (!settings.oldpassword || settings.oldpassword.trim() === '') {
throw new Error('Old password is required to change password');
}
// Verify old password
const bcrypt = await import('bcrypt');
const match = await bcrypt.compare(settings.oldpassword, user.password);
if (!match) {
throw new Error('Old password is incorrect');
}
// Hash new password
const hashedPassword = await bcrypt.hash(settings.newpassword, 10);
await user.update({ password: hashedPassword });
}
return { success: true };
} catch (error) {
console.error('Error setting account settings:', error);
throw error;
}
}
async getVisibilities() {
return UserParamVisibilityType.findAll();
}

View File

@@ -24,7 +24,7 @@ const initializeTypes = async () => {
};
const userParams = {
language: { type: 'singleselect', setting: 'personal' },
birthdate: { type: 'date', setting: 'personal' },
birthdate: { type: 'date', setting: 'personal', immutable: true },
zip: { type: 'string', setting: 'personal' },
town: { type: 'string', setting: 'personal' },
bodyheight: { type: 'float', setting: 'view', unit: 'cm' },
@@ -54,6 +54,7 @@ const initializeTypes = async () => {
if (item.minAge) createItem.minAge = item.minAge;
if (item.gender) createItem.gender = item.gender;
if (item.unit) createItem.unit = item.unit;
if (item.immutable) createItem.immutable = item.immutable;
await UserParamType.findOrCreate({
where: { description: key },
defaults: createItem