A LOT of fixes

This commit is contained in:
Gu://em_ 2025-05-04 23:14:00 +02:00
parent ca080b3e59
commit 106646f306
13 changed files with 120 additions and 69 deletions

View file

@ -12,5 +12,8 @@
"type": "sqlite" "type": "sqlite"
}, },
"JWT_secret": "HGF7654EGBNKJNBJH6754356788GJHGY" "auth": {
"JWT_secret": "HGF7654EGBNKJNBJH6754356788GJHGY",
"tokenExpiry": "1h"
}
} }

View file

@ -3,10 +3,15 @@ const authService = require("../services/authService");
async function login(req, res) { async function login(req, res) {
try { try {
const token = await authService.login(req.body.username, req.body.password); const username = req.body.username;
const email = req.body.email;
const password = req.body.password
const token = await authService.login(username, email, password);
res.json({ token }); res.json({ token });
} catch (err) { } catch (err) {
handleError(err, res); handleError(err, res);
} }
} }
module.exports = { login };

View file

@ -66,7 +66,7 @@ async function initDatabase() {
creation_date TINYTEXT NOT NULL, creation_date TINYTEXT NOT NULL,
downloads_count INT NOT NULL, downloads_count INT NOT NULL,
FOREIGN KEY (mod) REFERENCES Users(username) FOREIGN KEY (mod) REFERENCES Mods(name)
);`); );`);
// Mods tags // Mods tags

View file

@ -29,11 +29,11 @@ async function authorizeModModification(req) {
// Auth token // Auth token
await authenticateToken(req); await authenticateToken(req);
// Get mod infos // Get mod infos
if (!req.params || req.params.id) { if (!req.params || !req.params.name) {
throw new AppError(400, "No mod name was scpecified", "Bad request"); throw new AppError(400, "No mod name was scpecified", "Bad request");
} }
const mod_name = req.params.id; const mod_name = req.params.name;
const mod = getModByName(mod_name); const mod = await getModByName(mod_name);
if (!mod) { if (!mod) {
throw new AppError(404, "No mod was found with this name", "Not found"); throw new AppError(404, "No mod was found with this name", "Not found");
} }
@ -48,11 +48,11 @@ async function authorizeModpackModification(req) {
// Auth token // Auth token
await authenticateToken(req); await authenticateToken(req);
// Get mod infos // Get mod infos
if (!req.params || req.params.id) { if (!req.params || !req.params.name) {
throw new AppError(400, "No mod name was scpecified", "Bad request"); throw new AppError(400, "No mod name was scpecified", "Bad request");
} }
const modpack_name = req.params.id; const modpack_name = req.params.name;
const modpack = getModpackByName(modpack_name); const modpack = await getModpackByName(modpack_name);
if (!modpack) { if (!modpack) {
throw new AppError(404, "No mod was found with this name", "Not found"); throw new AppError(404, "No mod was found with this name", "Not found");
} }
@ -67,17 +67,17 @@ async function authorizeUserModification(req) {
// Auth token // Auth token
await authenticateToken(req); await authenticateToken(req);
// Get mod infos // Get mod infos
if (!req.params || req.params.id) { if (!req.params || !req.params.name) {
throw new AppError(400, "No mod name was scpecified", "Bad request"); throw new AppError(400, "No mod name was scpecified", "Bad request");
} }
const user_name = req.params.id; const user_name = req.params.name;
const user = getUserByName(user_name); const user = await getUserByName(user_name);
if (!user) { if (!user) {
throw new AppError(404, "No mod was found with this name", "Not found"); throw new AppError(404, "No user was found with this name", "Not found");
} }
// Authorize // Authorize
if ( user.username != req.token_infos.username) { if ( user.username != req.token_infos.username) {
throw new AppError(401, "Mod author differs from current user", "Unauthorized"); throw new AppError(401, "User to modify differs from current user", "Unauthorized");
} }
} }

View file

@ -4,6 +4,16 @@ const handleError = (err, res) => {
// Send error infos // Send error infos
if (err instanceof AppError) { if (err instanceof AppError) {
// Log
if (err.statusCode == 500) {
console.error("Error:", err.message);
if (err.debugMsg) {
console.debug(" >", err.debugMsg);
}
}
// Response
return res.status(err.statusCode).json({ return res.status(err.statusCode).json({
status: err.status, status: err.status,
message: err.message message: err.message
@ -11,6 +21,7 @@ const handleError = (err, res) => {
} }
// Default error // Default error
console.error("Error:", err.message);
res.status(500).json({ res.status(500).json({
message: 'Internal server error', message: 'Internal server error',
status: 500 status: 500

View file

@ -62,22 +62,27 @@ async function createMod(name, display_name, author, description, mod_infos) {
[name, display_name, author, description]); [name, display_name, author, description]);
// ModInfos table // ModInfos table
await db.prepare(`INSERT INTO ModInfos (mod, full_description, license, links, creation_date) await db.prepare(`INSERT INTO ModInfos (mod, full_description, license, links, creation_date, downloads_count)
VALUES (?, ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, ?)`,
[name, full_description, license.type, links.toString(), creation_date]); [name, full_description, license.type, links.toString(), creation_date, 0]);
// Tags // Tags
const tags_proc = addTags(name, tags, []); if (tags) {
const tags_proc = addTags(name, tags, []);
}
// License // License
if (license_type == "custom") { if (license.type == "custom") {
await db.prepare(`UPDATE ModInfos SET custom_license = ? await db.prepare(`UPDATE ModInfos SET custom_license = ?
WHERE mod = ?`, WHERE mod = ?`,
[license.content, name]); [license.content, name]);
} }
// Await // Await
await tags_proc; if (tags) {
await tags_proc;
}
return; return;
} }

View file

@ -5,8 +5,8 @@ const db = getDatabase();
// --- Get --- // --- Get ---
async function getAllUsers(name) { async function getAllUsers() {
return db.query("SELECT username, display_name, email, profile_picture FROM Users WHERE username = ?", [name]); return db.query("SELECT username, display_name, email, profile_picture FROM Users");
} }
async function getUserByName(name) { async function getUserByName(name) {
@ -35,8 +35,9 @@ async function exists(name) {
async function createUser( username, email, password, displayName, profilePicture, settings ) { async function createUser( username, email, password, displayName, profilePicture, settings ) {
// Create user // Create user
await db.prepare("INSERT INTO Users (username, email, password, display_name, role) \ await db.prepare(`INSERT INTO Users (username, email, password, display_name, role )
VALUES (?, ?, ?, ?, ?)", [username, email, password, displayName, "user"]); VALUES (?, ?, ?, ?, ? )`,
[username, email, password, displayName, "user"]);
// Handle nullable fields // Handle nullable fields
if (profilePicture) { if (profilePicture) {

View file

@ -5,27 +5,21 @@ const router = express.Router();
// List users // List users
router.get("/", async (req,res) => { router.get("/", async (req,res) => {
console.debug("Accessing users list");
controller.listUsers(req,res); controller.listUsers(req,res);
}); });
// Create a user // Create a user
router.post("/", async (req, res) => { router.post("/", async (req, res) => {
console.debug("Creating user ", req.body.name);
controller.createUser(req, res); controller.createUser(req, res);
}) })
// Get user infos // Get user infos
router.get("/:name", async (req,res) => { router.get("/:name", async (req,res) => {
const name = req.params.name;
console.debug("Accessing user " + name)
controller.getUserByName(req, res); controller.getUserByName(req, res);
}); });
// Delete user // Delete user
router.delete("/:name", async (req,res) => { router.delete("/:name", async (req,res) => {
const name = req.params.name;
console.debug("Deleting user " + name)
controller.deleteUser(req, res); controller.deleteUser(req, res);
}); });

View file

@ -1,48 +1,79 @@
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const userModel = require("../models/user"); const userModel = require("../models/user");
const AppError = require("../utils/appError"); const AppError = require("../utils/appError");
const cryptoUtils = require("../utils/crypto");
const configManager = require("../utils/configManager"); const configManager = require("../utils/configManager");
const validate = require("../utils/validate_legacy"); const validate = require("../utils/validate_legacy");
const JWT_Secret = configManager.getJWTSecret(); const JWT_Secret = configManager.getJWTSecret();
async function login(identifier, password) { async function login(username, email, password) {
// Check for null // Check for null
if (!username || !password) { if (!(username || email) || !password) {
throw new AppError(400, "Bad request", "missing credentials"); throw new AppError(400, "Bad request", "missing credentials");
} }
// Get user data // Get user data
let user; let user_get;
if (validate.isEmail(identifier)) { // If matches email if (email) { // If matches email
user = await userModel.getUserByEmail(username); user_get = await userModel.getUserByEmail(email);
} else } else
if (validate.isID(identifier)) { // if matches username if (username) { // if matches username
user = await userModel.getUserByName(username); user_get = await userModel.getUserByName(username);
} else { } else {
console.debug("Failed finding user, weird...")
throw new AppError(401, "Unauthorized", "Invalid credentials"); throw new AppError(401, "Unauthorized", "Invalid credentials");
} }
// Check if user exists // Check if user exists
if (!user || user.length == 0) { if (!user_get || user_get.length == 0) {
// throw new AppError(401, "Unauthorized: No user with this name"); // throw new AppError(401, "Unauthorized: No user with this name");
throw new AppError(401, "Unauthorized", "Invalid credentials"); throw new AppError(401, "Unauthorized", "Invalid credentials");
} }
// Just in case // Just in case
if (user.length > 1) { if (user_get.length > 1) {
throw new AppError(500, "Internal server error", "Found multiple users with this name or email, please contact administration"); throw new AppError(500, "Internal server error", "Found multiple users with this name or email, please contact administration");
} }
const user = user_get[0];
// Get user password
const saved_password_get = await userModel.getUserPassword(user.username);
// Check if retrieved password sucessfully
if (!saved_password_get || saved_password_get.length == 0) {
throw new AppError(500, "Unable to retrieve user password");
}
saved_password = saved_password_get[0].password;
// Check if retrieved password sucessfully again
if (!saved_password) {
throw new AppError(500, "Unable to retrieve user password");
}
// Check if passwords match // Check if passwords match
const passwords_match = await bcrypt.compare(password, user[0].password); const passwords_match = await cryptoUtils.passwordsMatch(password, saved_password)
if (!passwords_match) { if (!passwords_match) {
// throw new AppError(401, "Unauthorized: Invalid password"); // throw new AppError(401, "Unauthorized: Invalid password");
console.debug(password, "differs from", saved_password);
throw new AppError(401, "Unauthorized", "Invalid credentials"); throw new AppError(401, "Unauthorized", "Invalid credentials");
} }
return jwt.sign({ username: user[0].username, role: user[0].role }, await JWT_Secret); const payload = { type: "user",
username: user.username,
email: user.email,
role: user.role };
const token = await cryptoUtils.signToken(payload);
return token;
// // Check if passwords match
// const passwords_match = await bcrypt.compare(password, user[0].password);
// if (!passwords_match) {
// // throw new AppError(401, "Unauthorized: Invalid password");
// console.debug("Password doesn't match")
// throw new AppError(401, "Unauthorized", "Invalid credentials");
// }
// return jwt.sign({ username: user[0].username, role: user[0].role }, await JWT_Secret);
} }
// function authorizeRole(user, roles) { // function authorizeRole(user, roles) {

View file

@ -51,10 +51,10 @@ async function createMod(mod_data, author) {
mod_infos.full_description = await mdToHtml(mod_infos.full_description); // Convert mod_infos.full_description = await mdToHtml(mod_infos.full_description); // Convert
await sanitizeModData(mod_data); // Sanitize await sanitizeModData(mod_data); // Sanitize
//TODO //TODO
// mod_infos.creation_date = ... mod_infos.creation_date = 0
// Write changes to database // Write changes to database
model.createMod(name, display_name, author, description, mod_data); await model.createMod(name, display_name, author, description, mod_infos);
// Return // Return
return getModByName(name); return getModByName(name);

View file

@ -1,30 +1,32 @@
const model = require("../models/user"); const model = require("../models/user");
const AppError = require("../utils/appError"); const AppError = require("../utils/appError");
const cryptoUtils = require("../utils/crypto");
const { validateUserData } = require("../utils/validate_legacy"); const { validateUserData } = require("../utils/validate_legacy");
const { sanitizeUserData } = require("../utils/sanitize"); const { sanitizeUserData } = require("../utils/sanitize");
async function getAllUsers() { async function getAllUsers() {
return model.getAllUsers(); return await model.getAllUsers();
} }
async function getUserByName(name) { async function getUserByName(name) {
return model.getUserByName(name); const res = await model.getUserByName(name);
return res[0];
} }
async function createUser(user_data) { async function createUser(user_data) {
// Check body validity // Check body validity
await validateUserData(user_data); // TODO
// Check authenticity
//TODO no auth provider
// Sanitize // Sanitize
await sanitizeUserData(user_data); // TODO
console.debug("Passed validity checks"); // Gather data
model.createUser(user_data); const { username, email, password, display_name, profile_picture, settings } = user_data
return; const password_hash = await cryptoUtils.hashPassword(password);
await model.createUser(username, email, password_hash, display_name, null, null);
return model.getUserByName(username);
} }
async function deleteUser(name, token_user) { async function deleteUser(name, token_user) {
@ -35,13 +37,10 @@ async function deleteUser(name, token_user) {
throw new AppError(404, "Cannot find user with this name", "Not found"); throw new AppError(404, "Cannot find user with this name", "Not found");
} }
// Check authenticity const res = await model.getUserByName(name);
if (name != token_user) { await model.deleteUser(name);
throw new AppError(401, "", "Unauthorized");
}
model.deleteUser(name); return res;
return;
} }
module.exports = { getAllUsers, getUserByName, createUser, deleteUser }; module.exports = { getAllUsers, getUserByName, createUser, deleteUser };

View file

@ -31,7 +31,8 @@ const default_config = {
}, },
"auth" : { "auth" : {
"JWT_secret": "HGF7654EGBNKJNBJH6754356788GJHGY" "JWT_secret": "HGF7654EGBNKJNBJH6754356788GJHGY",
"tokenExpiry": "1h"
} }
} }
@ -48,7 +49,7 @@ function loadConfig() {
user_config = JSON.parse(fs.readFileSync(path.resolve(path.join(config_folder, config_file_name)))); user_config = JSON.parse(fs.readFileSync(path.resolve(path.join(config_folder, config_file_name))));
// Warns // Warns
if (!user_config.auth || !user_config.auth.jwtSecret) { if (!user_config.auth || !user_config.auth.JWT_secret) {
console.warn("WARNING: No JWT secret provided, using the default one. Please note that using the default secret is a major security risk.") console.warn("WARNING: No JWT secret provided, using the default one. Please note that using the default secret is a major security risk.")
} }
@ -75,7 +76,7 @@ async function getConfig() {
} }
async function getJWTSecret() { async function getJWTSecret() {
return config.auth.JWT_Secret || process.env.JWT_Secret; return config.auth.JWT_secret || process.env.JWT_secret;
} }
async function getVersion() { async function getVersion() {

View file

@ -10,9 +10,10 @@ async function sanitizeText(text) {
} }
async function sanitizeModData(mod_data) { async function sanitizeModData(mod_data) {
mod_data.displayName = await sanitizeText(mod_data.displayName); console.warn("Skipping sanitanization (not implemented)");
mod_data.otherInfos.description = await sanitizeText(mod_data.otherInfos.description); // mod_data.displayName = await sanitizeText(mod_data.displayName);
mod_data.otherInfos.changelogs = await sanitizeText(mod_data.otherInfos.changelogs); // mod_data.otherInfos.description = await sanitizeText(mod_data.otherInfos.description);
// mod_data.otherInfos.changelogs = await sanitizeText(mod_data.otherInfos.changelogs);
} }
module.exports = { sanitizeText, sanitizeModData }; module.exports = { sanitizeText, sanitizeModData };