diff --git a/server/models/User.js b/server/models/User.js index 63d8014..7967dc7 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -1,209 +1,211 @@ -const crypto = require('crypto') +const crypto = require("crypto"); -const Note = require('@models/Note') +const Note = require("@models/Note"); -const db = require('@config/database') -const Auth = require('@helpers/Auth') -const cs = require('@helpers/CryptoString') -const speakeasy = require('speakeasy') +const db = require("@config/database"); +const Auth = require("@helpers/Auth"); +const cs = require("@helpers/CryptoString"); +const speakeasy = require("speakeasy"); -let User = module.exports = {} +let User = (module.exports = {}); -const version = '3.8.0' +const version = "4.0.0"; // 3.7.3 - diff/patch update //Login a user, if that user does not exist create them //Issues login token User.login = (username, password, authToken = null) => { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { + const lowerName = username.toLowerCase(); - const lowerName = username.toLowerCase() + let statusObject = { + success: false, + token: null, + userId: null, + verificationRequired: false, + message: "Incorrect Username or Password", + }; - let statusObject = { - success: false, - token: null, - userId: null, - verificationRequired: false, - message: 'Incorrect Username or Password' - } + db.promise() + .query("SELECT * FROM user WHERE username = ? LIMIT 1", [lowerName]) + .then((rows, fields) => { + // + // Login User + // + if (rows[0].length == 1) { + //Pull out user data from database results + const lookedUpUser = rows[0][0]; - db.promise() - .query('SELECT * FROM user WHERE username = ? LIMIT 1', [lowerName]) - .then((rows, fields) => { + //Verify Token if set + const tokenValidates = speakeasy.totp.verify({ + secret: lookedUpUser["two_fa_secret"], + encoding: "base32", + token: authToken, + window: 2, + }); - // - // Login User - // - if(rows[0].length == 1){ + if (lookedUpUser.two_fa_enabled == 1 && !authToken) { + statusObject["verificationRequired"] = true; + statusObject["message"] = "2FA authentication required."; - //Pull out user data from database results - const lookedUpUser = rows[0][0] + return resolve(statusObject); + } - //Verify Token if set - const tokenValidates = speakeasy.totp.verify({ - 'secret': lookedUpUser['two_fa_secret'], - 'encoding': 'base32', - 'token': authToken, - 'window': 2 - }) + if (lookedUpUser.two_fa_enabled == 1 && !tokenValidates) { + statusObject["verificationRequired"] = true; + statusObject["message"] = "Invalid Authorization Token."; - if(lookedUpUser.two_fa_enabled == 1 && !authToken){ + return resolve(statusObject); + } - statusObject['verificationRequired'] = true - statusObject['message'] = '2FA authentication required.' + if ( + lookedUpUser.two_fa_enabled == 0 || + (lookedUpUser.two_fa_enabled == 1 && tokenValidates) + ) { + //hash the password and check for a match - return resolve(statusObject) - } + const salt = Buffer.from(lookedUpUser.salt, "binary"); + crypto.pbkdf2( + password, + salt, + lookedUpUser.iterations, + 512, + "sha512", + function (err, delivered_key) { + if (delivered_key.toString("hex") === lookedUpUser.password) { + User.generateMasterKey(lookedUpUser.id, password) + .then((result) => + User.getMasterKey(lookedUpUser.id, password), + ) + .then((masterKey) => { + User.generateKeypair(lookedUpUser.id, masterKey).then( + ({ publicKey, privateKey }) => { + //Passback a json web token + Auth.createToken(lookedUpUser.id, masterKey).then( + (token) => { + statusObject["token"] = token; + statusObject["userId"] = lookedUpUser.id; + statusObject["success"] = true; - if(lookedUpUser.two_fa_enabled == 1 && !tokenValidates){ + return resolve(statusObject); + }, + ); + }, + ); + }); + } else { + return resolve(statusObject); + } + }, + ); + } + } else { + //If user is not found, say two factor authentication is required + statusObject["verificationRequired"] = true; + statusObject["message"] = "2FA authentication required."; - statusObject['verificationRequired'] = true - statusObject['message'] = 'Invalid Authorization Token.' + //Show fake auth token message + if (authToken) { + statusObject["message"] = "Invalid Authorization Token."; + } - return resolve(statusObject) - } - - if(lookedUpUser.two_fa_enabled == 0 || (lookedUpUser.two_fa_enabled == 1 && tokenValidates) ){ - - //hash the password and check for a match - - const salt = Buffer.from(lookedUpUser.salt, 'binary') - crypto.pbkdf2(password, salt, lookedUpUser.iterations, 512, 'sha512', function(err, delivered_key){ - if(delivered_key.toString('hex') === lookedUpUser.password){ - - User.generateMasterKey(lookedUpUser.id, password) - .then( result => User.getMasterKey(lookedUpUser.id, password)) - .then(masterKey => { - - User.generateKeypair(lookedUpUser.id, masterKey) - .then(({publicKey, privateKey}) => { - - //Passback a json web token - Auth.createToken(lookedUpUser.id, masterKey) - .then(token => { - - statusObject['token'] = token - statusObject['userId'] = lookedUpUser.id - statusObject['success'] = true - - return resolve(statusObject) - }) - }) - }) - - } else { - return resolve(statusObject) - } - }) - } - - } else { - - //If user is not found, say two factor authentication is required - statusObject['verificationRequired'] = true - statusObject['message'] = '2FA authentication required.' - - //Show fake auth token message - if(authToken){ - statusObject['message'] = 'Invalid Authorization Token.' - } - - return resolve(statusObject) - } - }) - .catch(console.log) - - }) -} + return resolve(statusObject); + } + }) + .catch(console.log); + }); +}; //Create user account //Issues login token User.register = (username, password) => { + //For some reason, username won't get into the promise. But password will @TODO figure this out + const lowerName = username.toLowerCase().trim(); - //For some reason, username won't get into the promise. But password will @TODO figure this out - const lowerName = username.toLowerCase().trim() + return new Promise((resolve, reject) => { + db.promise() + .query("SELECT * FROM user WHERE username = ? LIMIT 1", [lowerName]) + .then((rows, fields) => { + if (rows[0].length === 0) { + //No users returned, create new one. Start with hashing password - return new Promise((resolve, reject) => { + //Params for hash function + let shasum = crypto.createHash("sha512"); //Prepare Hash + const ran = parseInt(Date.now()); //Get current time in miliseconds + const semiRandomInt = Math.floor(Math.random() * 11); //Grab a random number + const otherRandomInt = + (ran * semiRandomInt + ran) * semiRandomInt - ran; //Mix things up a bit + shasum.update("" + otherRandomInt); //Update Hasd - db.promise() - .query('SELECT * FROM user WHERE username = ? LIMIT 1', [lowerName]) - .then((rows, fields) => { + const saltString = shasum.digest("hex"); + const salt = Buffer.from(saltString, "binary"); //Generate Salt hash + const iterations = 25000; - if(rows[0].length === 0){ //No users returned, create new one. Start with hashing password + crypto.pbkdf2( + password, + salt, + iterations, + 512, + "sha512", + function (err, delivered_key) { + //Create new user object with freshly salted password + var currentDate = new Date() + .toISOString() + .slice(0, 19) + .replace("T", " "); + var new_user = { + username: lowerName, + password: delivered_key.toString("hex"), + salt: salt, + iterations: iterations, + last_login: currentDate, + created: currentDate, + }; - //Params for hash function - let shasum = crypto.createHash('sha512') //Prepare Hash - const ran = parseInt(Date.now()) //Get current time in miliseconds - const semiRandomInt = Math.floor(Math.random()*11) //Grab a random number - const otherRandomInt = (ran*semiRandomInt+ran)*semiRandomInt-ran //Mix things up a bit - shasum.update(''+otherRandomInt) //Update Hasd + let userId = null; + let newMasterKey = null; - const saltString = shasum.digest('hex') - const salt = Buffer.from(saltString, 'binary') //Generate Salt hash - const iterations = 25000 - - crypto.pbkdf2(password, salt, iterations, 512, 'sha512', function(err, delivered_key) { - - //Create new user object with freshly salted password - var currentDate = new Date().toISOString().slice(0, 19).replace('T', ' '); - var new_user = { - username: lowerName, - password: delivered_key.toString('hex'), - salt: salt, - iterations: iterations, - last_login: currentDate, - created: currentDate - }; - - let userId = null - let newMasterKey = null - - db.promise() - .query('INSERT INTO user SET ?', new_user) - .then((rows, fields) => { - - userId = rows[0].insertId - return User.generateMasterKey(userId, password) - }) - .then( result => { - - return User.getMasterKey(userId, password) - }) - .then(masterKey => { - newMasterKey = masterKey - return User.generateKeypair(userId, newMasterKey) - }) - .then(({publicKey, privateKey}) => { - - return Auth.createToken(userId, newMasterKey) - }) - .then(token => { - - return resolve({token, userId}) - }) - .catch(console.log) - }) - } else { - return reject('Username already in use.') - }//END user create - }) - .catch(console.log) - - - }) -} + db.promise() + .query("INSERT INTO user SET ?", new_user) + .then((rows, fields) => { + userId = rows[0].insertId; + return User.generateMasterKey(userId, password); + }) + .then((result) => { + return User.getMasterKey(userId, password); + }) + .then((masterKey) => { + newMasterKey = masterKey; + return User.generateKeypair(userId, newMasterKey); + }) + .then(({ publicKey, privateKey }) => { + return Auth.createToken(userId, newMasterKey); + }) + .then((token) => { + return resolve({ token, userId }); + }) + .catch(console.log); + }, + ); + } else { + return reject("Username already in use."); + } //END user create + }) + .catch(console.log); + }); +}; //Counts notes, pinned notes, archived notes, shared notes, unread notes, total files and types User.getCounts = (userId, extendedOptions) => { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { + let countTotals = { + tags: {}, + }; + // const userHash = cs.hash(String(userId)).toString('base64') - let countTotals = { - tags: {} - } - // const userHash = cs.hash(String(userId)).toString('base64') - - db.promise().query( - `SELECT + db.promise() + .query( + `SELECT SUM(archived = 1 && share_user_id IS NULL && trashed = 0) AS archivedNotes, SUM(trashed = 1) AS trashedNotes, SUM(share_user_id IS NULL && trashed = 0 AND quick_note < 2) AS totalNotes, @@ -211,44 +213,51 @@ User.getCounts = (userId, extendedOptions) => { SUM(share_user_id != ? && trashed = 0) AS sharedToNotes FROM note LEFT JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id) - WHERE user_id = ?`, [userId, userId, userId, userId]) - .then( (rows, fields) => { + WHERE user_id = ?`, + [userId, userId, userId, userId], + ) + .then((rows, fields) => { + Object.assign(countTotals, rows[0][0]); //combine results + // + // @TODO - Figured out if this is useful + // We want, notes shared with user and note user has shared + // + return db.promise().query( + `SELECT count(id) AS sharedFromNotes + FROM note WHERE shared = 2 AND user_id = ? AND trashed = 0`, + [userId], + ); + }) + .then((rows, fields) => { + Object.assign(countTotals, rows[0][0]); //combine results - Object.assign(countTotals, rows[0][0]) //combine results - // - // @TODO - Figured out if this is useful - // We want, notes shared with user and note user has shared - // - return db.promise().query( - `SELECT count(id) AS sharedFromNotes - FROM note WHERE shared = 2 AND user_id = ? AND trashed = 0`, [userId] - ) - }) - .then( (rows, fields) => { - - Object.assign(countTotals, rows[0][0]) //combine results - - return db.promise().query( - `SELECT + return db.promise().query( + `SELECT SUM(attachment_type = 1) as linkFiles, SUM(attachment_type != 1) as otherFiles, COUNT(id) as totalFiles FROM attachment WHERE visible = 1 AND user_id = ? - `, [userId] - ) - }).then( (rows, fields) => { + `, + [userId], + ); + }) + .then((rows, fields) => { + Object.assign(countTotals, rows[0][0]); //combine results - Object.assign(countTotals, rows[0][0]) //combine results + return db + .promise() + .query( + "SELECT id AS quickNote FROM note WHERE quick_note = 1 AND user_id = ?", + [userId], + ); + }) + .then((rows, fields) => { + Object.assign(countTotals, rows[0][0]); //combine results - return db.promise().query('SELECT id AS quickNote FROM note WHERE quick_note = 1 AND user_id = ?', [userId]) - - }).then( (rows, fields) => { - - Object.assign(countTotals, rows[0][0]) //combine results - - //Count usages of user tags, sort by most popular - return db.promise().query(` + //Count usages of user tags, sort by most popular + return db.promise().query( + ` SELECT tag.text, COUNT(tag_id) AS uses, tag.id FROM note_tag @@ -257,337 +266,345 @@ User.getCounts = (userId, extendedOptions) => { GROUP BY tag_id ORDER BY uses DESC LIMIT 16 - `, [userId]) + `, + [userId], + ); + }) + .then((rows, fields) => { + //Convert everything to an int or 0 + Object.keys(countTotals).forEach((key) => { + const count = parseInt(countTotals[key]); + countTotals[key] = count ? count : 0; + }); - }).then( (rows, fields) => { + //Build out tags object + let tagsObject = {}; + rows[0].forEach((tagRow) => { + tagsObject[tagRow["text"]] = { id: tagRow.id, uses: tagRow.uses }; + }); - + //Assign after counts are updated + countTotals["tags"] = tagsObject; - //Convert everything to an int or 0 - Object.keys(countTotals).forEach( key => { - const count = parseInt(countTotals[key]) - countTotals[key] = count ? count : 0 - }) + countTotals["currentVersion"] = version; - //Build out tags object - let tagsObject = {} - rows[0].forEach(tagRow => { - tagsObject[tagRow['text']] = {'id':tagRow.id, 'uses':tagRow.uses} - }) - - //Assign after counts are updated - countTotals['tags'] = tagsObject - - countTotals['currentVersion'] = version - - // Allow for extended options set on page load - if(extendedOptions){ - - db.promise().query( - `SELECT updated FROM note + // Allow for extended options set on page load + if (extendedOptions) { + db.promise() + .query( + `SELECT updated FROM note JOIN note_raw_text ON note_raw_text.id = note.note_raw_text_id WHERE note.quick_note = 2 - AND user_id = ?`, [userId]) - .then( (rows, fields) => { + AND user_id = ?`, + [userId], + ) + .then((rows, fields) => { + if (rows[0][0] && rows[0][0].updated) { + const lastOpened = rows[0][0].updated; + const timeDiff = Math.round((+new Date() - lastOpened) / 1000); + const hoursInSeconds = 12 * 60 * 60; //12 hours - + // Show metric tracking button if its been 12 hours since last entry + if (lastOpened && timeDiff > hoursInSeconds) { + countTotals["showTrackMetricsButton"] = true; + } + } - if(rows[0][0] && rows[0][0].updated){ - const lastOpened = rows[0][0].updated - const timeDiff = Math.round(((+new Date) - (lastOpened))/1000) - const hoursInSeconds = (12 * 60 * 60) //12 hours - - // Show metric tracking button if its been 12 hours since last entry - if(lastOpened && timeDiff > hoursInSeconds){ - countTotals['showTrackMetricsButton'] = true - } - } - - - resolve(countTotals) - }) - - - } else { - resolve(countTotals) - } - - }) - - }) -} + resolve(countTotals); + }); + } else { + resolve(countTotals); + } + }); + }); +}; //Log out user by deleting login token for that active session User.logout = (sessionId) => { - console.log('Terminate Session -> ', sessionId) - return db.promise().query('DELETE FROM user_active_session WHERE (session_id = ?)', [sessionId]) -} + console.log("Terminate Session -> ", sessionId); + return db + .promise() + .query("DELETE FROM user_active_session WHERE (session_id = ?)", [ + sessionId, + ]); +}; User.generateMasterKey = (userId, password) => { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { + if (!userId || !password) { + reject("Need userId and password to generate key"); + } - if(!userId || !password){ - reject('Need userId and password to generate key') - } + db.promise() + .query("SELECT count(id) as total FROM user_key WHERE user_id = ?", [ + userId, + ]) + .then((rows, fields) => { + //Entry already exists, you good. + if (rows[0][0]["total"] > 0) { + return resolve(true); + // throw new Error('User Encryption key already exists') + } else { + // Generate user key, its big and random + const masterPassword = cs.createSmallSalt(); - db.promise() - .query('SELECT count(id) as total FROM user_key WHERE user_id = ?', [userId]) - .then((rows, fields) => { + //Generate a salt because it wants it + const salt = cs.createSmallSalt(); - //Entry already exists, you good. - if(rows[0][0]['total'] > 0){ - return resolve(true) - // throw new Error('User Encryption key already exists') - } else { - // Generate user key, its big and random - const masterPassword = cs.createSmallSalt() + // Encrypt master password + const encryptedMasterPassword = cs.encrypt( + password, + salt, + masterPassword, + ); - //Generate a salt because it wants it - const salt = cs.createSmallSalt() + const created = Math.round(+new Date() / 1000); - // Encrypt master password - const encryptedMasterPassword = cs.encrypt(password, salt, masterPassword) - - const created = Math.round((+new Date)/1000) - - db.promise() - .query( - 'INSERT INTO user_key (`user_id`, `salt`, `key`, `created`) VALUES (?, ?, ?, ?);', - [userId, salt, encryptedMasterPassword, created] - ) - .then(results => { - return resolve(true) - }) - } - - }) - .catch(error => { - console.log('Create Master Password Error') - console.log(error) - }) - - }) -} + db.promise() + .query( + "INSERT INTO user_key (`user_id`, `salt`, `key`, `created`) VALUES (?, ?, ?, ?);", + [userId, salt, encryptedMasterPassword, created], + ) + .then((results) => { + return resolve(true); + }); + } + }) + .catch((error) => { + console.log("Create Master Password Error"); + console.log(error); + }); + }); +}; User.getMasterKey = (userId, password) => { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { + if (!userId || !password) { + reject("Need userId and password to fetch key"); + } - if(!userId || !password){ - reject('Need userId and password to fetch key') - } + db.promise() + .query("SELECT * FROM user_key WHERE user_id = ? LIMIT 1", [userId]) + .then((rows, fields) => { + const row = rows[0][0]; - db.promise().query('SELECT * FROM user_key WHERE user_id = ? LIMIT 1', [userId]) - .then((rows, fields) => { + if (!rows[0] || rows[0].length == 0 || rows[0][0] == undefined) { + return reject("Row or salt or something not set"); + } - const row = rows[0][0] + const masterKey = cs.decrypt(password, row["salt"], row["key"]); - if(!rows[0] || rows[0].length == 0 || rows[0][0] == undefined){ - return reject('Row or salt or something not set') - } + if (masterKey == null) { + return reject("Unable to decrypt key"); + } - const masterKey = cs.decrypt(password, row['salt'], row['key']) - - if(masterKey == null){ - return reject('Unable to decrypt key') - } - - return resolve(masterKey) - - }) - - }) -} + return resolve(masterKey); + }); + }); +}; User.generateKeypair = (userId, masterKey) => { + let publicKey = null; + let privateKey = null; - let publicKey = null - let privateKey = null + return new Promise((resolve, reject) => { + db.promise() + .query("SELECT * FROM user_key WHERE user_id = ?", [userId]) + .then((rows, fields) => { + const row = rows[0][0]; - return new Promise((resolve, reject) => { - db.promise().query('SELECT * FROM user_key WHERE user_id = ?', [userId]) - .then((rows, fields) => { + const salt = row["salt"]; + publicKey = row["public_key"]; + privateKey = row["private_key_encrypted"]; - const row = rows[0][0] + if (row["public_key"] == null) { + const keyPair = crypto.generateKeyPairSync("rsa", { + modulusLength: 1024, + publicKeyEncoding: { + type: "spki", + format: "pem", + }, + privateKeyEncoding: { + type: "pkcs8", + format: "pem", + }, + }); - const salt = row['salt'] - publicKey = row['public_key'] - privateKey = row['private_key_encrypted'] + publicKey = keyPair.publicKey; + privateKey = keyPair.privateKey; + const privateKeyEncrypted = cs.encrypt(masterKey, salt, privateKey); - if(row['public_key'] == null){ - const keyPair = crypto.generateKeyPairSync('rsa', { - modulusLength: 1024, - publicKeyEncoding: { - type: 'spki', - format: 'pem' - }, - privateKeyEncoding: { - type: 'pkcs8', - format: 'pem' - } - }) + db.promise() + .query( + "UPDATE user_key SET `public_key` = ?, `private_key_encrypted` = ? WHERE user_id = ?;", + [publicKey, privateKeyEncrypted, userId], + ) + .then((rows, fields) => { + return resolve({ publicKey, privateKey }); + }); + } else { + //Decrypt private key + privateKey = cs.decrypt(masterKey, salt, privateKey); - publicKey = keyPair.publicKey - privateKey = keyPair.privateKey - const privateKeyEncrypted = cs.encrypt(masterKey, salt, privateKey) - - db.promise() - .query( - 'UPDATE user_key SET `public_key` = ?, `private_key_encrypted` = ? WHERE user_id = ?;', - [publicKey, privateKeyEncrypted, userId] - ) - .then((rows, fields)=>{ - - return resolve({publicKey, privateKey}) - - }) - - } else { - - //Decrypt private key - privateKey = cs.decrypt(masterKey, salt, privateKey) - - return resolve({publicKey, privateKey}) - } - }) - }) -} + return resolve({ publicKey, privateKey }); + } + }); + }); +}; User.getPublicKey = (userId) => { - return new Promise((resolve, reject) => { - db.promise().query('SELECT public_key FROM user_key WHERE user_id = ?', [userId]) - .then((rows, fields) => { - - const row = rows[0][0] - return resolve(row['public_key']) - - }) - }) -} + return new Promise((resolve, reject) => { + db.promise() + .query("SELECT public_key FROM user_key WHERE user_id = ?", [userId]) + .then((rows, fields) => { + const row = rows[0][0]; + return resolve(row["public_key"]); + }); + }); +}; User.getPrivateKey = (userId, masterKey) => { - return new Promise((resolve, reject) => { - db.promise().query('SELECT salt, private_key_encrypted FROM user_key WHERE user_id = ?', [userId]) - .then((rows, fields) => { + return new Promise((resolve, reject) => { + db.promise() + .query( + "SELECT salt, private_key_encrypted FROM user_key WHERE user_id = ?", + [userId], + ) + .then((rows, fields) => { + const row = rows[0][0]; - const row = rows[0][0] + const salt = row["salt"]; + privateKey = row["private_key_encrypted"]; - const salt = row['salt'] - privateKey = row['private_key_encrypted'] + //Decrypt private key + privateKey = cs.decrypt(masterKey, salt, privateKey); - //Decrypt private key - privateKey = cs.decrypt(masterKey, salt, privateKey) - - return resolve(privateKey) - }) - }) -} + return resolve(privateKey); + }); + }); +}; User.getByUserName = (username) => { - return new Promise((resolve, reject) => { - db.promise().query('SELECT * FROM user WHERE username = ? LIMIT 1', [username.toLowerCase()]) - .then((rows, fields) => { - resolve(rows[0][0]) - }) - }) -} + return new Promise((resolve, reject) => { + db.promise() + .query("SELECT * FROM user WHERE username = ? LIMIT 1", [ + username.toLowerCase(), + ]) + .then((rows, fields) => { + resolve(rows[0][0]); + }); + }); +}; User.changePassword = (userId, oldPass, newPass) => { - return new Promise((resolve, reject) => { - User.getMasterKey(userId, oldPass) - .then(masterKey => { - User.getPrivateKey(userId, masterKey) - .then(privateKey => { - //If success, user has correct password + return new Promise((resolve, reject) => { + User.getMasterKey(userId, oldPass) + .then((masterKey) => { + User.getPrivateKey(userId, masterKey).then((privateKey) => { + //If success, user has correct password - // Generate new master pass, encrypt with new password - // const masterPassword = cs.createSmallSalt() - const salt = cs.createSmallSalt() - const encryptedMasterPassword = cs.encrypt(newPass, salt, masterKey) - const encryptedPrivateKey = cs.encrypt(masterKey, salt, privateKey) + // Generate new master pass, encrypt with new password + // const masterPassword = cs.createSmallSalt() + const salt = cs.createSmallSalt(); + const encryptedMasterPassword = cs.encrypt(newPass, salt, masterKey); + const encryptedPrivateKey = cs.encrypt(masterKey, salt, privateKey); - db.promise() - .query( - 'UPDATE user_key SET salt = ?, `key` = ?, private_key_encrypted = ? WHERE user_id = ? LIMIT 1', - [salt, encryptedMasterPassword, encryptedPrivateKey, userId] - ).then((r,f) => { - //Create login using password - let shasum = crypto.createHash('sha512') //Prepare Hash - const saltString = shasum.digest('hex') - const passwordSalt = Buffer.from(saltString, 'binary') //Generate Salt hash - const iterations = 25000 + db.promise() + .query( + "UPDATE user_key SET salt = ?, `key` = ?, private_key_encrypted = ? WHERE user_id = ? LIMIT 1", + [salt, encryptedMasterPassword, encryptedPrivateKey, userId], + ) + .then((r, f) => { + //Create login using password + let shasum = crypto.createHash("sha512"); //Prepare Hash + const saltString = shasum.digest("hex"); + const passwordSalt = Buffer.from(saltString, "binary"); //Generate Salt hash + const iterations = 25000; - crypto.pbkdf2(newPass, passwordSalt, iterations, 512, 'sha512', function(err, delivered_key) { + crypto.pbkdf2( + newPass, + passwordSalt, + iterations, + 512, + "sha512", + function (err, delivered_key) { + const deliveredPass = delivered_key.toString("hex"); - const deliveredPass = delivered_key.toString('hex') - - db.promise().query('UPDATE user SET password = ?, salt = ? WHERE id = ? LIMIT 1', [deliveredPass, passwordSalt, userId]) - .then((r,f) => { - return resolve(true) - }) - - }) - }) - - }) - - }) - .catch(error => { - resolve(false) - }) - }) -} + db.promise() + .query( + "UPDATE user SET password = ?, salt = ? WHERE id = ? LIMIT 1", + [deliveredPass, passwordSalt, userId], + ) + .then((r, f) => { + return resolve(true); + }); + }, + ); + }); + }); + }) + .catch((error) => { + resolve(false); + }); + }); +}; User.revokeActiveSessions = (userId, sessionId) => { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { + const userHash = cs.hash(String(userId)).toString("base64"); - const userHash = cs.hash(String(userId)).toString('base64') - - db.promise().query('DELETE FROM user_active_session WHERE user_hash = ? AND session_id != ?', [userHash, sessionId]) - .then((r,f) => { - - resolve(true) - }) - - }) -} + db.promise() + .query( + "DELETE FROM user_active_session WHERE user_hash = ? AND session_id != ?", + [userHash, sessionId], + ) + .then((r, f) => { + resolve(true); + }); + }); +}; User.deleteUser = (userId, password) => { + if (!userId || !password) { + return new Promise((resolve, reject) => { + return resolve("Missing User ID or Password. No Action Taken."); + }); + } - if(!userId || !password){ - return new Promise((resolve, reject) => { - return resolve('Missing User ID or Password. No Action Taken.') - }) - } + //Verify user is correct by decryptig master key with password - //Verify user is correct by decryptig master key with password - - let deletePromises = [] + let deletePromises = []; - //Delete all notes and raw text - let noteDelete = db.promise().query(` + //Delete all notes and raw text + let noteDelete = db.promise().query( + ` DELETE note, note_raw_text FROM note JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id) WHERE note.user_id = ? - `,[userId]) - deletePromises.push(noteDelete) + `, + [userId], + ); + deletePromises.push(noteDelete); - //Delete user entry - let userDelete = db.promise().query(` + //Delete user entry + let userDelete = db.promise().query( + ` DELETE FROM user WHERE id = ? - `,[userId]) - deletePromises.push(userDelete) + `, + [userId], + ); + deletePromises.push(userDelete); - //Delete user_key, encrypted search index - let tables = ['user_key', 'user_encrypted_search_index'] - tables.forEach(tableName => { + //Delete user_key, encrypted search index + let tables = ["user_key", "user_encrypted_search_index"]; + tables.forEach((tableName) => { + const query = `DELETE FROM ${tableName} WHERE user_id = ?`; + const deleteQuery = db.promise().query(query, [userId]); + deletePromises.push(deleteQuery); + }); - const query = `DELETE FROM ${tableName} WHERE user_id = ?` - const deleteQuery = db.promise().query(query, [userId]) - deletePromises.push(deleteQuery) - }) + //Remove all note attachments and files - //Remove all note attachments and files - - return Promise.all(deletePromises) -} \ No newline at end of file + return Promise.all(deletePromises); +};