Updated Version

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