From bdb6457d1d20a4fd7ac342796a035ffcdf65b78d Mon Sep 17 00:00:00 2001 From: azures04 Date: Tue, 23 Dec 2025 16:17:40 +0100 Subject: [PATCH] Add session repository and profile signing utility Introduces sessionRepository.js with functions for managing client and server sessions, blocked servers, skins, capes, and profile actions. Adds signProfileData to utils.js for signing profile data, and updates README project name. --- README.md | 2 +- modules/utils.js | 18 +++- repositories/sessionRepository.js | 165 ++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 repositories/sessionRepository.js diff --git a/README.md b/README.md index 8c25edd..325827a 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# Modun \ No newline at end of file +# Yggdrasil \ No newline at end of file diff --git a/modules/utils.js b/modules/utils.js index 07a8dcd..0e81c13 100644 --- a/modules/utils.js +++ b/modules/utils.js @@ -1,6 +1,8 @@ const path = require("node:path") const Logger = require("./logger") const logger = Logger.createLogger(path.join(__dirname, "..")) +const certificatesManager = require("./certificatesManager") +const serverKeys = certificatesManager.getKeys() async function getRegistrationCountryFromIp(ipAddress) { const apiUrl = `https://ip-api.com/json/${ipAddress}?fields=countryCode` @@ -24,6 +26,20 @@ async function getRegistrationCountryFromIp(ipAddress) { } } +function signProfileData(dataBase64) { + try { + const privateKey = serverKeys.playerCertificateKeys.private + const signer = crypto.createSign("SHA1") + signer.update(dataBase64) + signer.end() + return signer.sign(privateKey, "base64") + } catch (err) { + console.error("Signing failed:", err) + return null + } +} + module.exports = { - getRegistrationCountryFromIp + getRegistrationCountryFromIp, + signProfileData } \ No newline at end of file diff --git a/repositories/sessionRepository.js b/repositories/sessionRepository.js new file mode 100644 index 0000000..bae91a4 --- /dev/null +++ b/repositories/sessionRepository.js @@ -0,0 +1,165 @@ +const path = require("node:path") +const Logger = require("../modules/logger") +const logger = Logger.createLogger(path.join(__dirname, "..")) +const database = require("../modules/database") +const { DefaultError } = require("../errors/errors") + +async function insertLegacyClientSessions(sessionId, uuid) { + try { + await database.query(`DELETE FROM legacyClientSessions WHERE uuid = ?`, [uuid]) + + const sql = `INSERT INTO legacyClientSessions (sessionId, uuid) VALUES (?, ?)` + const result = await database.query(sql, [sessionId, uuid]) + + if (result.affectedRows > 0) { + return { code: 200, sessionId, uuid } + } else { + throw new DefaultError(500, "Internal Server Error", "Unknown DB Error") + } + } catch (error) { + if (error instanceof DefaultError) throw error + logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) + throw new DefaultError(500, "Internal Server Error", "Please contact an administrator.") + } +} + +async function validateLegacyClientSession(sessionId, uuid) { + try { + const sql = `SELECT * FROM legacyClientSessions WHERE sessionId = ? AND uuid = ?` + const rows = await database.query(sql, [sessionId, uuid]) + + const session = rows[0] + if (session) { + return { + code: 200, + message: "Client session valid." + } + } else { + return { + code: 404, + message: "Client session not found for this accessToken/clientToken combination" + } + } + } catch (error) { + logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) + throw new DefaultError(500, "Internal Server Error", "Please contact an administrator.") + } +} + +async function getBlockedServers() { + try { + const sql = `SELECT * FROM blockedServers` + const blockedServers = await database.query(sql) + + return { + code: 200, + blockedServers: blockedServers.map(bannedServer => ({ sha1: bannedServer.hashedIp })) + } + } catch (error) { + logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) + throw new DefaultError(500, "Internal Server Error", "Please contact an administrator.") + } +} + +async function getActiveSkin(uuid) { + try { + const sql = ` + SELECT t.url, ps.variant + FROM playersSkins ps + JOIN textures t ON ps.assetHash = t.hash + WHERE ps.playerUuid = ? AND ps.isSelected = 1 + ` + const rows = await database.query(sql, [uuid]) + const skin = rows[0] + + return { code: 200, data: skin || null } + } catch (error) { + logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) + throw new DefaultError(500, "Internal Server Error", "Please contact an administrator.") + } +} + +async function getActiveCape(uuid) { + try { + const sql = ` + SELECT t.url + FROM playersCapes pc + JOIN textures t ON pc.assetHash = t.hash + WHERE pc.playerUuid = ? AND pc.isSelected = 1 + ` + const rows = await database.query(sql, [uuid]) + const cape = rows[0] + + return { code: 200, data: cape || null } + } catch (error) { + logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) + throw new DefaultError(500, "Internal Server Error", "Please contact an administrator.") + } +} + +async function getProfileActionsList(uuid) { + try { + const cleanUuid = uuid.replace(/-/g, "") + const sql = "SELECT action FROM playerProfileActions WHERE uuid = ?" + const rows = await database.query(sql, [cleanUuid]) + + const actions = rows.map(row => row.action) + + return { code: 200, data: actions } + } catch (error) { + logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) + throw new DefaultError(500, "Internal Server Error", "Please contact an administrator.") + } +} + +async function saveServerSession(uuid, accessToken, serverId, ip) { + try { + const sql = ` + INSERT INTO serverSessions (uuid, accessToken, serverId, ip, createdAt) + VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP) + ON DUPLICATE KEY UPDATE + accessToken = VALUES(accessToken), + serverId = VALUES(serverId), + ip = VALUES(ip), + createdAt = CURRENT_TIMESTAMP + ` + const result = await database.query(sql, [uuid, accessToken, serverId, ip]) + + return { code: 200, success: result.affectedRows > 0 } + } catch (error) { + logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) + throw new DefaultError(500, "Internal Server Error", "Please contact an administrator.") + } +} + +async function getServerSession(uuid, serverId) { + try { + const sql = ` + SELECT ip + FROM serverSessions + WHERE uuid = ? AND serverId = ? + ` + const rows = await database.query(sql, [uuid, serverId]) + const session = rows[0] + + if (!session) { + return { code: 404, valid: false } + } + + return { code: 200, valid: true, ip: session.ip } + } catch (error) { + logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) + throw new DefaultError(500, "Internal Server Error", "Please contact an administrator.") + } +} + +module.exports = { + insertLegacyClientSessions, + validateLegacyClientSession, + getBlockedServers, + getActiveSkin, + getActiveCape, + getProfileActionsList, + saveServerSession, + getServerSession +} \ No newline at end of file