Added privacy policy
Updated marketing Added some keyboard shortcuts Added settings page Added accent theming Added beta 2FA
This commit is contained in:
1 parent
2ae84ab73e
commit
06b8f0ad6a
29 files changed
+1428
-362
No files matched your search
+85
-28
@@ -5,22 +5,32 @@ const Note = require('@models/Note')
|
||||
const db = require('@config/database')
|
||||
const Auth = require('@helpers/Auth')
|
||||
const cs = require('@helpers/CryptoString')
|
||||
const speakeasy = require('speakeasy')
|
||||
|
||||
let User = module.exports = {}
|
||||
|
||||
const version = '3.0.1'
|
||||
const version = '3.1.3'
|
||||
|
||||
//Login a user, if that user does not exist create them
|
||||
//Issues login token
|
||||
User.login = (username, password) => {
|
||||
User.login = (username, password, authToken = null) => {
|
||||
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'
|
||||
}
|
||||
|
||||
db.promise()
|
||||
.query('SELECT * FROM user WHERE username = ? LIMIT 1', [lowerName])
|
||||
.then((rows, fields) => {
|
||||
|
||||
//
|
||||
// Login User
|
||||
//
|
||||
if(rows[0].length == 1){
|
||||
@@ -28,34 +38,76 @@ User.login = (username, password) => {
|
||||
//Pull out user data from database results
|
||||
const lookedUpUser = rows[0][0]
|
||||
|
||||
//hash the password and check for a match
|
||||
// const salt = new Buffer(lookedUpUser.salt, 'binary')
|
||||
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){
|
||||
//Verify Token if set
|
||||
const tokenValidates = speakeasy.totp.verify({
|
||||
'secret': lookedUpUser['two_fa_secret'],
|
||||
'encoding': 'base32',
|
||||
'token': authToken,
|
||||
'window': 2
|
||||
})
|
||||
|
||||
User.generateMasterKey(lookedUpUser.id, password)
|
||||
.then( result => User.getMasterKey(lookedUpUser.id, password))
|
||||
.then(masterKey => {
|
||||
if(lookedUpUser.two_fa_enabled == 1 && !authToken){
|
||||
|
||||
User.generateKeypair(lookedUpUser.id, masterKey)
|
||||
.then(({publicKey, privateKey}) => {
|
||||
statusObject['verificationRequired'] = true
|
||||
statusObject['message'] = '2FA authentication required.'
|
||||
|
||||
//Passback a json web token
|
||||
Auth.createToken(lookedUpUser.id, masterKey)
|
||||
.then(token => {
|
||||
return resolve({ token: token, userId:lookedUpUser.id })
|
||||
return resolve(statusObject)
|
||||
}
|
||||
|
||||
if(lookedUpUser.two_fa_enabled == 1 && !tokenValidates){
|
||||
|
||||
statusObject['verificationRequired'] = true
|
||||
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 {
|
||||
} else {
|
||||
return resolve(statusObject)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
reject('Password does not match database')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return reject('Incorrect Username or Password')
|
||||
|
||||
//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)
|
||||
@@ -186,6 +238,12 @@ User.getCounts = (userId) => {
|
||||
|
||||
Object.assign(countTotals, rows[0][0]) //combine results
|
||||
|
||||
return db.promise().query('SELECT two_fa_enabled FROM user WHERE id = ?', [userId])
|
||||
|
||||
}).then( (rows, fields) => {
|
||||
|
||||
Object.assign(countTotals, rows[0][0]) //combine results
|
||||
|
||||
//Convert everything to an int or 0
|
||||
Object.keys(countTotals).forEach( key => {
|
||||
const count = parseInt(countTotals[key])
|
||||
@@ -253,13 +311,12 @@ User.generateMasterKey = (userId, password) => {
|
||||
}
|
||||
|
||||
User.getMasterKey = (userId, password) => {
|
||||
|
||||
if(!userId || !password){
|
||||
reject('Need userId and password to fetch key')
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
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) => {
|
||||
|
||||
|
||||
Reference in new issue
Block a user