// @ts-check const path = require("path"); require("dotenv").config({ path: path.resolve(__dirname, "../../../.env") }); const generator = require("generate-password"); const noDatabaseDbHandler = require("../utils/noDatabaseDbHandler"); const dbHandler = require("../utils/dbHandler"); const handleGrants = require("./handleGrants"); const encrypt = require("../../functions/dsql/encrypt"); const decrypt = require("../../functions/dsql/decrypt"); const defaultMariadbUserHost = process.env.DSQL_DB_HOST || "127.0.0.1"; /** * Refresh Mariadb User Grants * =================================================== * @param {object} params * @param {number | string} [params.userId] * @param {string} [params.mariadbUserHost] * @param {string} [params.mariadbUser] * @param {string | number} [params.sqlUserID] */ async function refreshUsersAndGrants({ userId, mariadbUserHost, mariadbUser, sqlUserID, }) { /** * @description Users * @type {*[] | null} */ // @ts-ignore const users = await dbHandler({ query: `SELECT * FROM users`, }); if (!users?.[0]) { process.exit(); } for (let i = 0; i < users.length; i++) { const user = users[i]; if (!user) continue; if (userId && user.id != userId) continue; try { const { mariadb_user, mariadb_host, mariadb_pass, id } = user; const existingUser = await noDatabaseDbHandler( `SELECT * FROM mysql.user WHERE User = '${mariadb_user}' AND Host = '${mariadb_host}'` ); const existingMariaDBUserArray = userId && sqlUserID ? await dbHandler({ query: `SELECT * FROM mariadb_users WHERE id = ? AND user_id = ?`, values: [sqlUserID, userId], }) : null; /** * @type {import("../../types").MYSQL_mariadb_users_table_def | undefined} */ const activeMariadbUserObject = Array.isArray( existingMariaDBUserArray ) ? existingMariaDBUserArray?.[0] : undefined; const isPrimary = activeMariadbUserObject ? String(activeMariadbUserObject.primary)?.match(/1/) ? true : false : false; const isUserExisting = Boolean(existingUser?.[0]?.User); const isThisPrimaryHost = Boolean( mariadbUserHost == defaultMariadbUserHost ); const dslUsername = `dsql_user_${id}`; const dsqlPassword = activeMariadbUserObject?.password ? activeMariadbUserObject.password : isUserExisting ? mariadb_pass : generator.generate({ length: 16, numbers: true, symbols: true, uppercase: true, exclude: "*#.'`\"", }); const encryptedPassword = activeMariadbUserObject?.password ? activeMariadbUserObject.password : isUserExisting ? mariadb_pass : encrypt({ data: dsqlPassword, encryptionKey: process.env.DSQL_ENCRYPTION_PASSWORD, encryptionSalt: process.env.DSQL_ENCRYPTION_SALT, }); if ( !isUserExisting && !sqlUserID && !isPrimary && !mariadbUserHost && !mariadbUser ) { const createNewUser = await noDatabaseDbHandler( `CREATE USER IF NOT EXISTS '${dslUsername}'@'${defaultMariadbUserHost}' IDENTIFIED BY '${dsqlPassword}' REQUIRE SSL` ); console.log("createNewUser", createNewUser); console.log( `User ${user.id}: ${user.first_name} ${user.last_name} SQL credentials successfully updated.` ); const updateUser = await dbHandler({ query: `UPDATE users SET mariadb_user = ?, mariadb_host = ?, mariadb_pass = ? WHERE id = ?`, values: [ dslUsername, defaultMariadbUserHost, encryptedPassword, user.id, ], }); } if (isPrimary) { const finalHost = mariadbUserHost ? mariadbUserHost : mariadb_host; const updateUser = await dbHandler({ query: `UPDATE users SET mariadb_user = ?, mariadb_host = ?, mariadb_pass = ? WHERE id = ?`, values: [ dslUsername, finalHost, encryptedPassword, user.id, ], }); } ////////////////////////////////////////////// ////////////////////////////////////////////// ////////////////////////////////////////////// /** * @description Handle mariadb_users table */ const existingMariadbPrimaryUser = await dbHandler({ query: `SELECT * FROM mariadb_users WHERE user_id = ? AND \`primary\` = 1`, values: [id], }); const isPrimaryUserExisting = Boolean( Array.isArray(existingMariadbPrimaryUser) && existingMariadbPrimaryUser?.[0]?.user_id ); /** @type {import("./handleGrants").GrantType[]} */ const primaryUserGrants = [ { database: "*", table: "*", privileges: ["ALL"], }, ]; if (!isPrimaryUserExisting) { const insertPrimaryMariadbUser = await dbHandler({ query: `INSERT INTO mariadb_users (user_id, username, password, \`primary\`, grants) VALUES (?, ?, ?, ?, ?)`, values: [ id, dslUsername, encryptedPassword, "1", JSON.stringify(primaryUserGrants), ], }); } ////////////////////////////////////////////// const existingExtraMariadbUsers = await dbHandler({ query: `SELECT * FROM mariadb_users WHERE user_id = ? AND \`primary\` != '1'`, values: [id], }); if (Array.isArray(existingExtraMariadbUsers)) { for (let i = 0; i < existingExtraMariadbUsers.length; i++) { const mariadbUser = existingExtraMariadbUsers[i]; const { user_id, username, host, password, primary, grants, } = mariadbUser; if (mariadbUser && username != mariadbUser) continue; if (mariadbUserHost && host != mariadbUserHost) continue; const decrptedPassword = decrypt({ encryptedString: password, encryptionKey: process.env.DSQL_ENCRYPTION_PASSWORD, encryptionSalt: process.env.DSQL_ENCRYPTION_SALT, }); const existingExtraMariadbUser = await noDatabaseDbHandler( `SELECT * FROM mysql.user WHERE User = '${username}' AND Host = '${host}'` ); const isExtraMariadbUserExisting = Boolean( existingExtraMariadbUser?.[0]?.User ); if (!isExtraMariadbUserExisting) { await noDatabaseDbHandler( `CREATE USER IF NOT EXISTS '${username}'@'${host}' IDENTIFIED BY '${decrptedPassword}' REQUIRE SSL` ); } const isGrantHandled = await handleGrants({ username, host, grants: grants && typeof grants == "string" ? JSON.parse(grants) : [], userId: String(userId), }); if (!isGrantHandled) { console.log( `Error in handling grants for user ${username}@${host}` ); } } } ////////////////////////////////////////////// ////////////////////////////////////////////// ////////////////////////////////////////////// } catch (/** @type {any} */ error) { console.log(`Error in adding SQL user =>`, error.message); } } process.exit(); //////////////////////////////////////// //////////////////////////////////////// //////////////////////////////////////// } /** ****************************************************************************** */ /** ****************************************************************************** */ /** ****************************************************************************** */ /** ****************************************************************************** */ /** ****************************************************************************** */ module.exports = refreshUsersAndGrants;