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"
},
"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) {
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 });
} catch (err) {
handleError(err, res);
}
}
module.exports = { login };

View file

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

View file

@ -29,11 +29,11 @@ async function authorizeModModification(req) {
// Auth token
await authenticateToken(req);
// 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");
}
const mod_name = req.params.id;
const mod = getModByName(mod_name);
const mod_name = req.params.name;
const mod = await getModByName(mod_name);
if (!mod) {
throw new AppError(404, "No mod was found with this name", "Not found");
}
@ -48,11 +48,11 @@ async function authorizeModpackModification(req) {
// Auth token
await authenticateToken(req);
// 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");
}
const modpack_name = req.params.id;
const modpack = getModpackByName(modpack_name);
const modpack_name = req.params.name;
const modpack = await getModpackByName(modpack_name);
if (!modpack) {
throw new AppError(404, "No mod was found with this name", "Not found");
}
@ -67,17 +67,17 @@ async function authorizeUserModification(req) {
// Auth token
await authenticateToken(req);
// 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");
}
const user_name = req.params.id;
const user = getUserByName(user_name);
const user_name = req.params.name;
const user = await getUserByName(user_name);
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
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
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({
status: err.status,
message: err.message
@ -11,6 +21,7 @@ const handleError = (err, res) => {
}
// Default error
console.error("Error:", err.message);
res.status(500).json({
message: 'Internal server error',
status: 500

View file

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

View file

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

View file

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

View file

@ -1,48 +1,79 @@
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const userModel = require("../models/user");
const AppError = require("../utils/appError");
const cryptoUtils = require("../utils/crypto");
const configManager = require("../utils/configManager");
const validate = require("../utils/validate_legacy");
const JWT_Secret = configManager.getJWTSecret();
async function login(identifier, password) {
async function login(username, email, password) {
// Check for null
if (!username || !password) {
if (!(username || email) || !password) {
throw new AppError(400, "Bad request", "missing credentials");
}
// Get user data
let user;
if (validate.isEmail(identifier)) { // If matches email
user = await userModel.getUserByEmail(username);
let user_get;
if (email) { // If matches email
user_get = await userModel.getUserByEmail(email);
} else
if (validate.isID(identifier)) { // if matches username
user = await userModel.getUserByName(username);
if (username) { // if matches username
user_get = await userModel.getUserByName(username);
} else {
console.debug("Failed finding user, weird...")
throw new AppError(401, "Unauthorized", "Invalid credentials");
}
// 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", "Invalid credentials");
}
// 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");
}
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
const passwords_match = await bcrypt.compare(password, user[0].password);
const passwords_match = await cryptoUtils.passwordsMatch(password, saved_password)
if (!passwords_match) {
// throw new AppError(401, "Unauthorized: Invalid password");
console.debug(password, "differs from", saved_password);
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) {

View file

@ -51,10 +51,10 @@ async function createMod(mod_data, author) {
mod_infos.full_description = await mdToHtml(mod_infos.full_description); // Convert
await sanitizeModData(mod_data); // Sanitize
//TODO
// mod_infos.creation_date = ...
mod_infos.creation_date = 0
// 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 getModByName(name);

View file

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

View file

@ -31,7 +31,8 @@ const default_config = {
},
"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))));
// 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.")
}
@ -75,7 +76,7 @@ async function getConfig() {
}
async function getJWTSecret() {
return config.auth.JWT_Secret || process.env.JWT_Secret;
return config.auth.JWT_secret || process.env.JWT_secret;
}
async function getVersion() {

View file

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