En-/decryption fixed
This commit is contained in:
@@ -3,7 +3,7 @@ import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import chatRouter from './routers/chatRouter.js';
|
||||
import authRouter from './routers/authRouter.js';
|
||||
import navigationRouter from './routers/navigationRouter.js'
|
||||
import navigationRouter from './routers/navigationRouter.js';
|
||||
import settingsRouter from './routers/settingsRouter.js';
|
||||
import cors from 'cors';
|
||||
|
||||
@@ -11,8 +11,15 @@ const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(cors());
|
||||
|
||||
const corsOptions = {
|
||||
origin: '*',
|
||||
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
preflightContinue: false,
|
||||
optionsSuccessStatus: 204
|
||||
};
|
||||
|
||||
app.use(cors(corsOptions));
|
||||
app.use(express.json()); // To handle JSON request bodies
|
||||
|
||||
app.use('/api/chat', chatRouter);
|
||||
|
||||
@@ -7,6 +7,7 @@ export const register = async (req, res) => {
|
||||
const result = await userService.registerUser({ email, username, password, language });
|
||||
res.status(201).json(result);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,20 +8,23 @@ const menuStructure = {
|
||||
home: {
|
||||
visible: ["all"],
|
||||
children: {},
|
||||
path: "/"
|
||||
path: "/",
|
||||
icon: "logo_mono.png"
|
||||
},
|
||||
friends: {
|
||||
visible: ["all"],
|
||||
children: {
|
||||
manageFriends : {
|
||||
visible: ["all"],
|
||||
path: "/socialnetwork/friends"
|
||||
path: "/socialnetwork/friends",
|
||||
icon: "friends24.png"
|
||||
}
|
||||
},
|
||||
showLoggedinFriends: 1
|
||||
},
|
||||
socialnetwork: {
|
||||
visible: ["all"],
|
||||
icon: "socialnetwork.png",
|
||||
children: {
|
||||
guestbook: {
|
||||
visible: ["all"],
|
||||
@@ -56,6 +59,7 @@ const menuStructure = {
|
||||
},
|
||||
chats: {
|
||||
visible: ["over12"],
|
||||
icon: "chat.png",
|
||||
children: {
|
||||
multiChat: {
|
||||
visible: ["over12"],
|
||||
@@ -69,6 +73,7 @@ const menuStructure = {
|
||||
},
|
||||
falukant: {
|
||||
visible: ["all"],
|
||||
icon: "falukant24.png",
|
||||
children: {
|
||||
create: {
|
||||
visible: ["nofalukantaccount"],
|
||||
@@ -130,9 +135,11 @@ const menuStructure = {
|
||||
},
|
||||
minigames: {
|
||||
visible: ["all"],
|
||||
icon: "minigames24.png",
|
||||
},
|
||||
settings: {
|
||||
visible: ["all"],
|
||||
icon: "settings24.png",
|
||||
children: {
|
||||
homepage: {
|
||||
visible: ["all"],
|
||||
@@ -156,7 +163,7 @@ const menuStructure = {
|
||||
},
|
||||
sexuality: {
|
||||
visible: ["over14"],
|
||||
path: "/setting/sexuality"
|
||||
path: "/settings/sexuality"
|
||||
},
|
||||
notifications: {
|
||||
visible: ["all"],
|
||||
|
||||
@@ -3,10 +3,40 @@ import SettingsType from '../models/type/settings.js';
|
||||
import UserParam from '../models/community/user_param.js';
|
||||
import User from '../models/community/user.js';
|
||||
import UserParamValue from '../models/type/user_param_value.js';
|
||||
import { calculateAge } from '../utils/userdata.js';
|
||||
import { DataTypes, Op } from 'sequelize';
|
||||
import { decrypt } from '../utils/encryption.js';
|
||||
|
||||
export const filterSettings = async (req, res) => {
|
||||
const { userid, type } = req.body;
|
||||
try {
|
||||
const user = await User.findOne({ where: { hashedId: userid } });
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
const userParams = await UserParam.findAll({
|
||||
where: { userId: user.id },
|
||||
include: [
|
||||
{
|
||||
model: UserParamType,
|
||||
as: 'paramType'
|
||||
}
|
||||
]
|
||||
});
|
||||
let birthdate = null;
|
||||
let gender = null;
|
||||
|
||||
for (const param of userParams) {
|
||||
console.log(param.paramType.description);
|
||||
if (param.paramType.description === 'birthdate') {
|
||||
birthdate = param.value;
|
||||
}
|
||||
if (param.paramType.description === 'gender') {
|
||||
const genderResult = await UserParamValue.findOne({ where: { id: param.value } });
|
||||
gender = genderResult.dataValues.value;
|
||||
}
|
||||
}
|
||||
const age = birthdate ? calculateAge(birthdate) : null;
|
||||
const fields = await UserParamType.findAll({
|
||||
include: [
|
||||
{
|
||||
@@ -26,9 +56,14 @@ export const filterSettings = async (req, res) => {
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
where: {
|
||||
[Op.and]: [
|
||||
{ minAge: { [Op.or]: [null, { [Op.lte]: age }] } },
|
||||
{ gender: { [Op.or]: [null, gender] } }
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const responseFields = await Promise.all(fields.map(async (field) => {
|
||||
const options = ['singleselect', 'multiselect'].includes(field.datatype) ? await UserParamValue.findAll({
|
||||
where: { userParamTypeId: field.id }
|
||||
@@ -44,11 +79,10 @@ export const filterSettings = async (req, res) => {
|
||||
options: options.map(opt => ({ id: opt.id, value: opt.value }))
|
||||
};
|
||||
}));
|
||||
|
||||
res.status(200).json(responseFields);
|
||||
} catch (error) {
|
||||
console.error('Error fetching settings:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
console.error('Error filtering settings:', error);
|
||||
res.status(500).json({ error: 'An error occurred while filtering the settings' });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -108,4 +142,33 @@ export const getTypeParamValue = async(req, res) => {
|
||||
return;
|
||||
}
|
||||
res.status(200).json({ paramValueId: userParamValueObject.value });
|
||||
};
|
||||
};
|
||||
|
||||
export const getAccountSettings = async (req, res) => {
|
||||
try {
|
||||
const user = await User.findOne({ where: { hashedId: req.body.userId } });
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
const email = user.email;
|
||||
res.status(200).json({ username: user.username, email, showinsearch: user.searchable });
|
||||
} catch (error) {
|
||||
console.error('Error retrieving account settings:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
};
|
||||
|
||||
export const setAccountSettings = async(req, res) => {
|
||||
const { userid: userId, username, email, searchable, oldpassword, newpassword, newpasswordrepeat } = req.body;
|
||||
const user = await User.findOne({ where: { hashedId: userId }});
|
||||
if (!user) {
|
||||
res.status(404).json({ error: 'User not found' });
|
||||
return;
|
||||
}
|
||||
user.searchable = searchable;
|
||||
if (user.password !== oldpassword) {
|
||||
res.status(401).json({error: 'Wrong password'});
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
16
backend/middleware/authMiddleware.js
Normal file
16
backend/middleware/authMiddleware.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import User from '../models/community/user.js';
|
||||
|
||||
export const authenticate = async (req, res, next) => {
|
||||
const userId = req.headers.userid;
|
||||
const authCode = req.headers.authcode;
|
||||
if (!userId || !authCode) {
|
||||
return res.status(401).json({ error: 'Unauthorized: Missing credentials' });
|
||||
}
|
||||
|
||||
const user = await User.findOne({ where: { hashedId: userId, authCode } });
|
||||
if (!user) {
|
||||
return res.status(401).json({ error: 'Unauthorized: Invalid credentials' });
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.sequelize.transaction(async (transaction) => {
|
||||
await queryInterface.addColumn(
|
||||
'user',
|
||||
'email_temp',
|
||||
{
|
||||
type: Sequelize.BLOB,
|
||||
},
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
await queryInterface.sequelize.query(
|
||||
'UPDATE "community"."user" SET "email_temp" = "email"::bytea',
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
await queryInterface.removeColumn('user', 'email', { transaction });
|
||||
|
||||
await queryInterface.renameColumn('user', 'email_temp', 'email', { transaction });
|
||||
|
||||
await queryInterface.changeColumn(
|
||||
'user',
|
||||
'email',
|
||||
{
|
||||
type: Sequelize.BLOB,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
},
|
||||
{ transaction }
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
// Rollback code if needed
|
||||
},
|
||||
};
|
||||
@@ -1,25 +1,18 @@
|
||||
import { sequelize } from '../../utils/sequelize.js';
|
||||
import { DataTypes } from 'sequelize';
|
||||
import bcrypt from 'bcrypt';
|
||||
import { encrypt, generateIv } from '../../utils/encryption.js';
|
||||
import { encrypt, decrypt } from '../../utils/encryption.js';
|
||||
import crypto from 'crypto';
|
||||
|
||||
const User = sequelize.define('user', {
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
type: DataTypes.BLOB, // Verwende BLOB, um die E-Mail als bytea zu speichern
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
set(value) {
|
||||
if (value) {
|
||||
const iv = generateIv();
|
||||
this.setDataValue('iv', iv.toString('hex'));
|
||||
this.setDataValue('email', encrypt(value, iv));
|
||||
this.setDataValue('email', Buffer.from(encrypt(value), 'hex'));
|
||||
}
|
||||
}
|
||||
},
|
||||
iv: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
},
|
||||
username: {
|
||||
type: DataTypes.STRING,
|
||||
@@ -46,6 +39,14 @@ const User = sequelize.define('user', {
|
||||
hashedId: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
searchable: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true
|
||||
},
|
||||
authCode: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
}
|
||||
}, {
|
||||
tableName: 'user',
|
||||
@@ -57,6 +58,11 @@ const User = sequelize.define('user', {
|
||||
user.hashedId = hashedId;
|
||||
await user.save();
|
||||
}
|
||||
},
|
||||
getterMethods: {
|
||||
email() {
|
||||
return decrypt(this.getDataValue('email').toString('hex'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -63,13 +63,14 @@ const UserParam = sequelize.define('user_param', {
|
||||
|
||||
UserParam.upsertParam = async function (userId, paramTypeId, value) {
|
||||
try {
|
||||
const val = value !== null && value !== undefined ? value.toString() : '';
|
||||
const [userParam, created] = await UserParam.findOrCreate({
|
||||
where: { userId, paramTypeId },
|
||||
defaults: { value }
|
||||
defaults: { value: val }
|
||||
});
|
||||
|
||||
if (!created) {
|
||||
userParam.value = value;
|
||||
userParam.value = value !== null && value !== undefined ? value.toString() : '';
|
||||
await userParam.save();
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Router } from 'express';
|
||||
import { menu } from '../controllers/navigationController.js';
|
||||
import { authenticate } from '../middleware/authMiddleware.js';
|
||||
|
||||
const router = Router();
|
||||
router.use(authenticate);
|
||||
|
||||
router.get('/:userid', menu);
|
||||
|
||||
export default router;
|
||||
@@ -1,11 +1,15 @@
|
||||
import { Router } from 'express';
|
||||
import { filterSettings, updateSetting, getTypeParamValueId, getTypeParamValues, getTypeParamValue } from '../controllers/settingsController.js';
|
||||
import { filterSettings, updateSetting, getTypeParamValueId, getTypeParamValues, getTypeParamValue, getAccountSettings } from '../controllers/settingsController.js';
|
||||
import { authenticate } from '../middleware/authMiddleware.js';
|
||||
|
||||
const settingsRouter = Router();
|
||||
settingsRouter.post('/filter', filterSettings);
|
||||
settingsRouter.post('/update', updateSetting);
|
||||
settingsRouter.post('/getparamvalueid', getTypeParamValueId);
|
||||
settingsRouter.post('/getparamvalues', getTypeParamValues);
|
||||
settingsRouter.post('/getparamvalue/:id', getTypeParamValue);
|
||||
const router = Router();
|
||||
|
||||
export default settingsRouter;
|
||||
router.post('/filter', authenticate, filterSettings);
|
||||
router.post('/update', authenticate, updateSetting);
|
||||
router.post('/account', authenticate, getAccountSettings);
|
||||
|
||||
router.post('/getparamvalues', getTypeParamValues);
|
||||
router.post('/getparamvalueid', getTypeParamValueId);
|
||||
router.post('/getparamvalue/:id', getTypeParamValue);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -1,51 +1,53 @@
|
||||
import bcrypt from 'bcrypt';
|
||||
import crypto from 'crypto';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import User from '../models/community/user.js';
|
||||
import UserParam from '../models/community/user_param.js';
|
||||
import UserParamType from '../models/type/user_param.js';
|
||||
import { sendAccountActivationEmail, sendPasswordResetEmail } from './emailService.js';
|
||||
import { sequelize } from '../utils/sequelize.js';
|
||||
import { encrypt, generateIv } from '../utils/encryption.js';
|
||||
|
||||
const saltRounds = 10;
|
||||
|
||||
export const registerUser = async ({ email, username, password, language }) => {
|
||||
const iv = generateIv();
|
||||
const encryptedEmail = encrypt(email, iv);
|
||||
const encryptionKey = process.env.SECRET_KEY;
|
||||
const results = await sequelize.query(
|
||||
`SELECT * FROM "community"."user" WHERE "email" = :email`,
|
||||
`SELECT * FROM community.user WHERE pgp_sym_decrypt(email::bytea, :key) = :email`,
|
||||
{
|
||||
replacements: { key: process.env.SECRET_KEY, email: encryptedEmail },
|
||||
replacements: { key: encryptionKey, email },
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
}
|
||||
);
|
||||
if (results.length && results.length > 0) {
|
||||
throw new Error('Email already in use');
|
||||
if (results.length > 0) {
|
||||
throw new Error('emailinuse');
|
||||
}
|
||||
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
||||
const resetToken = uuidv4();
|
||||
const user = await User.create({
|
||||
email: encryptedEmail,
|
||||
iv: iv.toString('hex'),
|
||||
email: email,
|
||||
username,
|
||||
password: hashedPassword,
|
||||
resetToken: resetToken,
|
||||
active: false,
|
||||
registration_date: new Date()
|
||||
});
|
||||
|
||||
const languageType = await UserParamType.findOne({ where: { description: 'language' } });
|
||||
if (!languageType) {
|
||||
throw new Error('Language type not found');
|
||||
throw new Error('languagenotfound');
|
||||
}
|
||||
await UserParam.create({
|
||||
|
||||
const languageParam = await UserParam.create({
|
||||
userId: user.id,
|
||||
paramTypeId: languageType.id,
|
||||
value: language
|
||||
});
|
||||
|
||||
const activationLink = `${process.env.FRONTEND_URL}/activate?token=${resetToken}`;
|
||||
await sendAccountActivationEmail(email, activationLink, username, resetToken, language);
|
||||
const authCode = crypto.randomBytes(20).toString('hex');
|
||||
|
||||
return { id: user.hashedId, username: user.username, active: user.active };
|
||||
return { id: user.hashedId, username: user.username, active: user.active, param: [languageParam], authCode };
|
||||
};
|
||||
|
||||
export const loginUser = async ({ username, password }) => {
|
||||
@@ -57,6 +59,9 @@ export const loginUser = async ({ username, password }) => {
|
||||
if (!match) {
|
||||
throw new Error('credentialsinvalid');
|
||||
}
|
||||
const authCode = crypto.randomBytes(20).toString('hex');
|
||||
user.authCode = authCode;
|
||||
await user.save();
|
||||
const params = await UserParam.findAll({
|
||||
where: {
|
||||
userId: user.id
|
||||
@@ -69,9 +74,16 @@ export const loginUser = async ({ username, password }) => {
|
||||
}
|
||||
}
|
||||
});
|
||||
const mappedParams = params.map(param => {
|
||||
return { 'name': param.paramType.description, 'value': param.value }; });
|
||||
return { id: user.hashedId, username: user.username, active: user.active, param: mappedParams };
|
||||
const mappedParams = params.map(param => {
|
||||
return { 'name': param.paramType.description, 'value': param.value };
|
||||
});
|
||||
return {
|
||||
id: user.hashedId,
|
||||
username: user.username,
|
||||
active: user.active,
|
||||
param: mappedParams,
|
||||
authCode
|
||||
};
|
||||
};
|
||||
|
||||
export const handleForgotPassword = async ({ email }) => {
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import crypto from 'crypto';
|
||||
|
||||
const algorithm = 'aes-256-ctr';
|
||||
const secretKey = process.env.SECRET_KEY;
|
||||
const algorithm = 'aes-256-ecb'; // Verwende ECB-Modus, der keinen IV benötigt
|
||||
const key = crypto.scryptSync(process.env.SECRET_KEY, 'salt', 32); // Der Schlüssel sollte eine Länge von 32 Bytes haben
|
||||
|
||||
export const encrypt = (text) => {
|
||||
const cipher = crypto.createCipheriv(algorithm, secretKey, Buffer.alloc(16, 0));
|
||||
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
|
||||
return encrypted.toString('hex');
|
||||
const cipher = crypto.createCipheriv(algorithm, key, null); // Kein IV verwendet
|
||||
let encrypted = cipher.update(text, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
return encrypted;
|
||||
};
|
||||
|
||||
export const decrypt = (hash) => {
|
||||
const decipher = crypto.createDecipheriv(algorithm, secretKey, Buffer.alloc(16, 0));
|
||||
const decrpyted = Buffer.concat([decipher.update(Buffer.from(hash, 'hex')), decipher.final()]);
|
||||
return decrpyted.toString();
|
||||
export const decrypt = (text) => {
|
||||
const decipher = crypto.createDecipheriv(algorithm, key, null); // Kein IV verwendet
|
||||
let decrypted = decipher.update(text, 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
return decrypted;
|
||||
};
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import crypto from 'crypto';
|
||||
|
||||
const algorithm = 'aes-256-cbc';
|
||||
const secretKey = process.env.SECRET_KEY;
|
||||
const algorithm = 'aes-256-ecb';
|
||||
const key = crypto.scryptSync(process.env.SECRET_KEY, 'salt', 32);
|
||||
|
||||
export const generateIv = () => {
|
||||
return crypto.randomBytes(16);
|
||||
};
|
||||
|
||||
export const encrypt = (text, iv) => {
|
||||
const cipher = crypto.createCipheriv(algorithm, Buffer.from(secretKey, 'utf-8'), iv);
|
||||
export const encrypt = (text) => {
|
||||
const cipher = crypto.createCipheriv(algorithm, key, null);
|
||||
let encrypted = cipher.update(text, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
return encrypted;
|
||||
};
|
||||
|
||||
export const decrypt = (text, iv) => {
|
||||
const decipher = crypto.createDecipheriv(algorithm, Buffer.from(secretKey, 'utf-8'), iv);
|
||||
export const decrypt = (text) => {
|
||||
const decipher = crypto.createDecipheriv(algorithm, key, null);
|
||||
let decrypted = decipher.update(text, 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
return decrypted;
|
||||
|
||||
@@ -35,8 +35,8 @@ const initializeTypes = async () => {
|
||||
sexualpreference: { type: 'singleselect', 'setting': 'sexuality', minAge: 14 },
|
||||
gender: { type: 'singleselect', setting: 'personal' },
|
||||
pubichair: { type: 'singleselect', setting: 'sexuality', minAge: 14 },
|
||||
penislenght: { type: 'int', setting: 'sexuality', minAge: 14, gender: 'm' },
|
||||
brasize: { type: 'string', setting: 'sexuality', minAge: 14, gender: 'f' }
|
||||
penislenght: { type: 'int', setting: 'sexuality', minAge: 14, gender: 'male' },
|
||||
brasize: { type: 'string', setting: 'sexuality', minAge: 14, gender: 'female' }
|
||||
};
|
||||
Object.keys(userParams).forEach(async(key) => {
|
||||
const item = userParams[key];
|
||||
@@ -56,6 +56,8 @@ const initializeTypes = async () => {
|
||||
hairlength: ['short', 'medium', 'long', 'bald', 'other'],
|
||||
skincolor: ['light', 'medium', 'dark', 'other'],
|
||||
freckles: ['much', 'medium', 'less', 'none'],
|
||||
sexualpreference: ['straight', 'gay', 'bi', 'pan', 'asexual'],
|
||||
pubichair: ['none', 'short', 'medium', 'long', 'hairy', 'waxed', 'landingstrip', 'bikinizone', 'other'],
|
||||
};
|
||||
Object.keys(valuesList).forEach(async(key) => {
|
||||
const values = valuesList[key];
|
||||
|
||||
10
backend/utils/userdata.js
Normal file
10
backend/utils/userdata.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export const calculateAge = (birthdate) => {
|
||||
const today = new Date();
|
||||
const birthDate = new Date(birthdate);
|
||||
let age = today.getFullYear() - birthDate.getFullYear();
|
||||
const monthDiff = today.getMonth() - birthDate.getMonth();
|
||||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
|
||||
age--;
|
||||
}
|
||||
return age;
|
||||
};
|
||||
Reference in New Issue
Block a user