// // All actions in noteController.js // const db = require('@config/database') const Note = require('@models/Note') let ShareNote = module.exports = {} const crypto = require('crypto') const cs = require('@helpers/CryptoString') ShareNote.addUserToSharedNote = (userId, noteId, shareUserId, masterKey) => { return new Promise((resolve, reject) => { const Note = require('@models/Note') const User = require('@models/User') //generate new random salts and password let sharedNoteMasterKey = null let encryptedSharedKey = null //new key for note encrypted with shared users pubic key //Current note object let noteObject = null let publicKey = null db.promise().query('SELECT id FROM user WHERE id = ?', [shareUserId]) .then((rows, fields) => { if(rows[0].length == 0){ throw new Error('User Does Not Exist') } return ShareNote.migrateToShared(userId, noteId, masterKey) }) .then(({note, sharedNoteKey})=> { sharedNoteMasterKey = sharedNoteKey noteObject = note return db.promise() .query('SELECT id FROM note WHERE user_id = ? AND note_raw_text_id = ?', [shareUserId, note.rawTextId]) }) .then((rows, fields) => { if(rows[0].length >= 1){ throw new Error('User Already has this note shared with them') } return User.getPublicKey(shareUserId) }) .then(shareUserPublicKey => { //New Encrypted snippet, using new shared password const newSnippetSalt = cs.createSmallSalt() const snippet = JSON.stringify([noteObject.title, noteObject.text.substring(0, 500)]) const encryptedSnippet = cs.encrypt(sharedNoteMasterKey, newSnippetSalt, snippet) //Encrypt shared password for this user const encryptedSharedKey = crypto.publicEncrypt(shareUserPublicKey, Buffer.from(sharedNoteMasterKey, 'utf8')).toString('base64') //Insert new note for shared user return db.promise().query(` INSERT INTO note (user_id, note_raw_text_id, created, color, share_user_id, snippet, snippet_salt, encrypted_share_password_key) VALUES (?,?,?,?,?,?,?,?); `, [shareUserId, noteObject.rawTextId, noteObject.created, noteObject.color, userId, encryptedSnippet, newSnippetSalt, encryptedSharedKey]) }) .then((rows, fields) => { const sharedUserNoteId = rows[0]['insertId'] //Emit update count event to user shared with - so they see the note in real time SocketIo.to(shareUserId).emit('update_counts') let success = true return resolve({success, shareUserId, sharedUserNoteId}) }) .catch(error => { console.log('Shared Note Error') console.log(error) }) }) } ShareNote.removeUserFromSharedNote = (userId, noteId, shareNoteUserId, masterKey) => { return new Promise((resolve, reject) => { let rawTextId = null let removeUserId = null db.promise() .query('SELECT note_raw_text_id, user_id FROM note WHERE id = ? AND share_user_id = ?', [shareNoteUserId, userId]) .then( (rows, fields) => { rawTextId = rows[0][0]['note_raw_text_id'] removeUserId = rows[0][0]['user_id'] //Delete note entry for other user - remove users access //@TODO - this won't remove the note from their search index, it needs to return Note.delete(removeUserId, shareNoteUserId) }) .then(results => { return db.promise().query('SELECT count(*) as count FROM note WHERE note_raw_text_id = ?', [rawTextId]) }) .then((rows, fields) => { //Convert back to normal note if there is only one person with this note if(rows[0][0]['count'] == 1){ return ShareNote.migrateToNormal(userId, noteId, masterKey) .then(results => { resolve(true) }) } else { //Keep note shared return resolve(true) } }) }) } //Encrypt note with private shared key ShareNote.migrateToShared = (userId, noteId, masterKey) => { const User = require('@models/User') return new Promise((resolve, reject) => { //generate new random password const sharedNoteMasterKey = cs.createSmallSalt() let userPublicKey = null let userPrivateKey = null let note = null User.generateKeypair(userId, masterKey) .then( ({publicKey, privateKey}) => { //Get users public key userPublicKey = publicKey userPrivateKey = privateKey return Note.get(userId, noteId, masterKey) }) .then(noteObject => { note = noteObject if(note.shared == 2){ //Note is already shared, decrypt sharedKey let sharedNoteKey = null const encryptedShareKey = note.encrypted_share_password_key if(encryptedShareKey != null){ sharedNoteKey = crypto.privateDecrypt(userPrivateKey, Buffer.from(encryptedShareKey, 'base64') ) } return resolve({note, sharedNoteKey}) } else { // // Update raw_text to have a shared password, encrypt text with this password // const sharedNoteSalt = cs.createSmallSalt() //Encrypt note text with new password const textObject = JSON.stringify([note.title, note.text]) const encryptedText = cs.encrypt(sharedNoteMasterKey, sharedNoteSalt, textObject) //Update note raw text with new data db.promise() .query("UPDATE `application`.`note_raw_text` SET `text` = ?, `salt` = ? WHERE (`id` = ?)", [encryptedText, sharedNoteSalt, note.rawTextId]) .then((rows, fields) => { // // Update snippet using new shared password // + Save shared password (encrypted with public key) // const sharedNoteSnippetSalt = cs.createSmallSalt() const snippet = JSON.stringify([note.title, note.text.substring(0, 500)]) const encryptedSnippet = cs.encrypt(sharedNoteMasterKey, sharedNoteSnippetSalt, snippet) //Encrypt shared password for this user const encryptedSharedKey = crypto.publicEncrypt(userPublicKey, Buffer.from(sharedNoteMasterKey, 'utf8')).toString('base64') //Update note snippet for current user with public key encoded snippet return db.promise().query('UPDATE note SET snippet = ?, snippet_salt = ?, encrypted_share_password_key = ?, shared = 2 WHERE id = ? AND user_id = ?', [encryptedSnippet, sharedNoteSnippetSalt, encryptedSharedKey, noteId, userId]) }) .then((rows, fields) => { return resolve({note, 'sharedNoteKey':sharedNoteMasterKey}) }) } }) }) } //Remove private shared key, encrypt note with users master password ShareNote.migrateToNormal = (userId, noteId, masterKey) => { return new Promise((resolve, reject) => { Note.get(userId, noteId, masterKey) .then(noteObject => { const salt = cs.createSmallSalt() const snippetSalt = cs.createSmallSalt() const snippetObj = JSON.stringify([noteObject.title, noteObject.text.substring(0, 500)]) const snippet = cs.encrypt(masterKey, snippetSalt, snippetObj) const textObject = JSON.stringify([noteObject.title, noteObject.text]) const encryptedText = cs.encrypt(masterKey, salt, textObject) db.promise() .query(`UPDATE note SET snippet = ?, snippet_salt = ?, encrypted_share_password_key = ?, shared = 0 WHERE id = ? AND user_id = ?`, [snippet, snippetSalt, null, noteId, userId]) .then((r,f) => { db.promise() .query('UPDATE note_raw_text SET text = ?, salt = ? WHERE id = ?', [encryptedText, salt, noteObject.rawTextId]) .then(() => { return resolve(true) }) }) }) }) } ShareNote.decryptSharedKey = (userId, noteId, masterKey) => { return new Promise((resolve, reject) => { let userPrivateKey = null const User = require('@models/User') User.generateKeypair(userId, masterKey) .then( ({publicKey, privateKey}) => { userPrivateKey = privateKey return Note.get(userId, noteId, masterKey) }) .then(noteObject => { //Shared notes use encrypted key - decrypt key then return it. const encryptedShareKey = noteObject.encrypted_share_password_key if(encryptedShareKey != null && userPrivateKey != null){ const currentNoteKey = crypto.privateDecrypt(userPrivateKey, Buffer.from(encryptedShareKey, 'base64') ) return resolve(currentNoteKey) } else { return resolve(null) } }) }) } // Get users who see a shared note ShareNote.getShareInfo = (userId, noteId, rawTextId) => { return new Promise((resolve, reject) => { let shareUsers = [] let shareStatus = 0 db.promise() .query(` SELECT username, note.id as noteId FROM note JOIN user ON (user.id = note.user_id) WHERE note_raw_text_id = ? AND share_user_id = ? AND user_id != ? `, [rawTextId, userId, userId]) .then((rows, fields) => { //Return a list of user names shareUsers = rows[0] return db.promise().query('SELECT shared FROM note WHERE id = ? AND user_id = ?', [noteId, userId]) }) .then((rows, fields) => { shareStatus = rows[0][0]['shared'] return resolve({ shareStatus, shareUsers }) }) }) }