diff --git a/client/src/App.vue b/client/src/App.vue index fdc8225..5bb9986 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -1,9 +1,22 @@ \ No newline at end of file diff --git a/client/src/router/index.js b/client/src/router/index.js index e29fe14..c9bd101 100644 --- a/client/src/router/index.js +++ b/client/src/router/index.js @@ -6,6 +6,7 @@ import Router from 'vue-router' const HomePage = () => import(/* webpackChunkName: "HomePage" */ '@/pages/HomePage') const LoginPage = () => import(/* webpackChunkName: "LoginPage" */ '@/pages/LoginPage') const HelpPage = () => import(/* webpackChunkName: "HelpPage" */ '@/pages/HelpPage') +const TermsPage = () => import(/* webpackChunkName: "TermsPage" */ '@/pages/TermsPage') const SettingsPage = () => import(/* webpackChunkName: "SettingsPage" */ '@/pages/SettingsPage') const SharePage = () => import(/* webpackChunkName: "SharePage" */ '@/pages/SharePage') const NotesPage = () => import(/* webpackChunkName: "NotesPage" */ '@/pages/NotesPage') @@ -53,7 +54,13 @@ export default new Router({ meta: {title:'Help'}, component: HelpPage }, - { + { + path: '/terms', + name: 'Terms', + meta: {title:'Terms'}, + component: TermsPage + }, + { path: '/settings', name: 'Settings', meta: {title:'Settings'}, diff --git a/server/index.js b/server/index.js index be38d0b..3975079 100644 --- a/server/index.js +++ b/server/index.js @@ -21,17 +21,17 @@ const port = 3000 // // Request Rate Limiter // -const rateLimit = require('express-rate-limit'); +const rateLimit = require('express-rate-limit') +//Limiter for the entire app const limiter = rateLimit({ - windowMs: 10 * 60 * 1000, // minutes - max: 1000 // limit each IP to 100 requests per windowMs -}); + windowMs: 10 * 60 * 1000, // 10 minutes + max: 1000 // limit each IP to 1000 requests per windowMs +}) // apply to all requests app.use(limiter); - var http = require('http').createServer(app); var io = require('socket.io')(http, { path:'/socket' @@ -222,6 +222,7 @@ app.use(express.json({limit: '5mb'})) app.use(function(req, res, next){ //Always null out master key, never allow it set from outside + req.headers.userId = null req.headers.masterKey = null req.headers.sessionId = null @@ -243,10 +244,7 @@ app.use(function(req, res, next){ }) .catch(error => { - console.log(error) - - res.statusMessage = error //Throw 400 error if token is bad - res.status(400).end() + next('Unauthorized') }) } else { next() //No token. Move along. @@ -260,13 +258,12 @@ let UserTest = require('@models/User') let NoteTest = require('@models/Note') let AuthTest = require('@helpers/Auth') -Auth.testTwoFactor() - Auth.test() -UserTest.keyPairTest('genMan23', '1', printResults) +UserTest.keyPairTest('genMan25', '1', printResults) .then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey, printResults)) .then( message => { if(printResults) console.log(message) + Auth.testTwoFactor() }) @@ -303,4 +300,16 @@ app.use('/api/quick-note', quickNote) //Output running status app.listen(port, () => { // console.log(`Listening on port ${port}!`) +}) + +// +//Error handlers +// +//Default error handler just say unauthorized for everything +app.use(function (err, req, res, next) { + if (err) { + res.status(401).send('Unauthorized') + return + } + next() }) \ No newline at end of file diff --git a/server/models/Note.js b/server/models/Note.js index 0da9cf5..02bdeb3 100644 --- a/server/models/Note.js +++ b/server/models/Note.js @@ -449,8 +449,7 @@ Note.update = (userId, noteId, noteText, noteTitle, color, pinned, archived, has //Shared notes use encrypted key - decrypt key then decrypt note const encryptedShareKey = rows[0][0].encrypted_share_password_key if(encryptedShareKey != null){ - masterKey = crypto.privateDecrypt(userPrivateKey, - Buffer.from(encryptedShareKey, 'base64') ) + masterKey = crypto.privateDecrypt(userPrivateKey, Buffer.from(encryptedShareKey, 'base64') ) } let encryptedNoteText = '' @@ -475,10 +474,14 @@ Note.update = (userId, noteId, noteText, noteTitle, color, pinned, archived, has for (var i = 0; i < rows[0].length; i++) { const otherNote = rows[0][i] //Re-encrypt for other user - const updatedSnippet = cs.encrypt(masterKey, otherNote.snippet_salt, snippet) + let updatedSnippet = '' //Default to no snippet + if(noteText.length > 500){ + updatedSnippet = cs.encrypt(masterKey, otherNote.snippet_salt, snippet) + } db.promise().query('UPDATE note SET snippet = ? WHERE id = ?', [updatedSnippet, otherNote.id]) - - SocketIo.to(otherNote['user_id']).emit('new_note_text_saved', {'noteId':otherNote.id, hash}) + .then((rows, fields) => { + SocketIo.to(otherNote['user_id']).emit('new_note_text_saved', {'noteId':otherNote.id, hash}) + }) } }) @@ -489,10 +492,13 @@ Note.update = (userId, noteId, noteText, noteTitle, color, pinned, archived, has }) .then( (rows, fields) => { + //Set openend time to a minute ago + const theFuture = Math.round((+new Date)/1000) + 10 + //Update other note attributes return db.promise() - .query('UPDATE note SET pinned = ?, archived = ?, color = ?, snippet = ?, indexed = 0 WHERE id = ? AND user_id = ? LIMIT 1', - [pinned, archived, color, noteSnippet, noteId, userId]) + .query('UPDATE note SET pinned = ?, archived = ?, color = ?, snippet = ?, indexed = 0, opened = ? WHERE id = ? AND user_id = ? LIMIT 1', + [pinned, archived, color, noteSnippet, theFuture, noteId, userId]) }) .then((rows, fields) => { @@ -750,7 +756,6 @@ Note.get = (userId, noteId, masterKey) => { }) .then((rows, fields) => { - const nowTime = Math.round((+new Date)/1000) let noteLockedOut = false let noteData = rows[0][0] // const rawTextId = noteData['rawTextId'] @@ -776,6 +781,7 @@ Note.get = (userId, noteId, masterKey) => { noteData.title = textObject[0] noteData.text = textObject[1] + const nowTime = Math.round((+new Date)/1000) db.promise().query(`UPDATE note SET opened = ? WHERE (id = ?)`, [nowTime, noteId]) //Return note data @@ -1122,7 +1128,7 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => { } } } catch(err) { - console.log('Error opening note id -> ', note.id) + console.log('Error opening note id -> '+note.id+' for userId -> '+userId) console.log(err) } diff --git a/server/models/User.js b/server/models/User.js index 9f13c92..9de7639 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -9,7 +9,7 @@ const speakeasy = require('speakeasy') let User = module.exports = {} -const version = '3.1.5' +const version = '3.1.6' //Login a user, if that user does not exist create them //Issues login token @@ -498,6 +498,7 @@ User.deleteUser = (userId, password) => { let deletePromises = [] + //Delete all notes and raw text let noteDelete = db.promise().query(` DELETE note, note_raw_text FROM note @@ -506,12 +507,14 @@ User.deleteUser = (userId, password) => { `,[userId]) deletePromises.push(noteDelete) + //Delete user entry let userDelete = db.promise().query(` DELETE FROM user WHERE id = ? `,[userId]) deletePromises.push(userDelete) - let tables = ['user_key', 'user_encrypted_search_index', 'attachment'] + //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 = ?` @@ -519,6 +522,8 @@ User.deleteUser = (userId, password) => { deletePromises.push(deleteQuery) }) + //Remove all note attachments and files + return Promise.all(deletePromises) } diff --git a/server/routes/attachmentController.js b/server/routes/attachmentController.js index cb1c297..4230460 100644 --- a/server/routes/attachmentController.js +++ b/server/routes/attachmentController.js @@ -6,16 +6,23 @@ let router = express.Router() let Attachment = require('@models/Attachment') let Note = require('@models/Note') + let userId = null +let masterKey = null // middleware that is specific to this router router.use(function setUserId (req, res, next) { - if(userId = req.headers.userId){ + + //Session key is required to continue + if(!req.headers.sessionId){ + next('Unauthorized') + } + + if(req.headers.userId){ userId = req.headers.userId masterKey = req.headers.masterKey + next() } - - next() }) router.post('/search', function (req, res) { diff --git a/server/routes/noteController.js b/server/routes/noteController.js index b458e92..2ee7f20 100644 --- a/server/routes/noteController.js +++ b/server/routes/noteController.js @@ -10,12 +10,17 @@ let masterKey = null // middleware that is specific to this router router.use(function setUserId (req, res, next) { + + //Session key is required to continue + if(!req.headers.sessionId){ + next('Unauthorized') + } + if(req.headers.userId){ userId = req.headers.userId masterKey = req.headers.masterKey + next() } - - next() }) // diff --git a/server/routes/publicController.js b/server/routes/publicController.js index 65d0ddc..0a35bbc 100644 --- a/server/routes/publicController.js +++ b/server/routes/publicController.js @@ -1,17 +1,60 @@ var express = require('express') var router = express.Router() +const rateLimit = require('express-rate-limit') + +const Note = require('@models/Note') +const User = require('@models/User') + + -let Note = require('@models/Note') // // Public Note action // -router.post('/opensharednote', function (req, res) { +const sharedNoteLimiter = rateLimit({ + windowMs: 30 * 60 * 1000, //30 min window + max: 50, // start blocking after 50 requests + message:'Unable to open that many shared notes' +}) +router.post('/opensharednote', sharedNoteLimiter, function (req, res) { Note.getShared(req.body.noteId, req.body.sharedKey) .then(results => res.send(results)) }) +// +// Login User +// +const loginLimiter = rateLimit({ + windowMs: 30 * 60 * 1000, // 30 min window + max: 25, // start blocking after 25 requests + message:'Please try to login again later' +}) +router.post('/login', loginLimiter, function (req, res) { + + User.login(req.body.username, req.body.password, req.body.authToken) + .then( returnData => { + + res.send(returnData) + }) +}) + +// +// Register User +// +const registerLimiter = rateLimit({ + windowMs: 60 * 60 * 1000, // 1 hour window + max: 5, // start blocking after 5 requests + message:'Please try again to create an acount in an hour' +}) +router.post('/register', registerLimiter, function (req, res) { + + User.register(req.body.username, req.body.password) + .then( returnData => { + + res.send(returnData) + }) +}) diff --git a/server/routes/quicknoteController.js b/server/routes/quicknoteController.js index 59f230d..1e79a75 100644 --- a/server/routes/quicknoteController.js +++ b/server/routes/quicknoteController.js @@ -6,13 +6,19 @@ let QuickNote = require('@models/QuickNote'); let userId = null let masterKey = null +// middleware that is specific to this router router.use(function setUserId (req, res, next) { + + //Session key is required to continue + if(!req.headers.sessionId){ + next('Unauthorized') + } + if(req.headers.userId){ userId = req.headers.userId masterKey = req.headers.masterKey + next() } - - next() }) //Get quick note text diff --git a/server/routes/tagController.js b/server/routes/tagController.js index 640f972..5e444d1 100644 --- a/server/routes/tagController.js +++ b/server/routes/tagController.js @@ -1,16 +1,24 @@ var express = require('express') var router = express.Router() -let Tags = require('@models/Tag'); +let Tags = require('@models/Tag') + let userId = null +let masterKey = null // middleware that is specific to this router router.use(function setUserId (req, res, next) { - if(userId = req.headers.userId){ - userId = req.headers.userId + + //Session key is required to continue + if(!req.headers.sessionId){ + next('Unauthorized') + } + + if(req.headers.userId){ + userId = req.headers.userId + masterKey = req.headers.masterKey + next() } - - next() }) //Get the latest notes the user has created diff --git a/server/routes/userController.js b/server/routes/userController.js index aa8082a..8c0d856 100644 --- a/server/routes/userController.js +++ b/server/routes/userController.js @@ -5,20 +5,24 @@ const User = require('@models/User') const Auth = require('@helpers/Auth') const cs = require('@helpers/CryptoString') +let userId = null +let masterKey = null + // middleware that is specific to this router -router.use(function timeLog (req, res, next) { - // console.log('Time: ', Date.now()) - next() +router.use(function setUserId (req, res, next) { + + //Session key is required to continue + if(!req.headers.sessionId){ + next('Unauthorized') + } + + if(req.headers.userId){ + userId = req.headers.userId + masterKey = req.headers.masterKey + next() + } }) -// Login User -router.post('/login', function (req, res) { - - User.login(req.body.username, req.body.password, req.body.authToken) - .then( returnData => { - res.send(returnData) - }) -}) // Logout User router.post('/logout', function (req, res) { @@ -28,19 +32,6 @@ router.post('/logout', function (req, res) { }) }) -// Register User -router.post('/register', function (req, res) { - - User.register(req.body.username, req.body.password) - .then( returnData => { - - res.send(returnData) - }) - .catch(e => { - res.send(false) - }) -}) - // change password router.post('/changepassword', function (req, res) {