This repository has been archived on 2024-04-29. You can view files and clone it, but cannot push or open issues or pull requests.
node-fftcg/backend/inc/fftcgdb.coffee

108 lines
3 KiB
CoffeeScript

# libraries
bcrypt = (require 'bcrypt')
sqlite3 = (require 'sqlite3').verbose()
# bruteforce countermeasure
saltRounds = 13
FFTCGDB = (filename) ->
@filename = filename
@db = new sqlite3.Database @filename, (err) ->
if err
console.error err.message
@db.run """
CREATE TABLE IF NOT EXISTS users (
login text NOT NULL COLLATE NOCASE,
pwdhash text NOT NULL,
UNIQUE(login)
);
"""
console.log "[FFTCGDB] Connected to '#{@filename}'"
return
FFTCGDB::close = ->
new Promise (resolve, reject) ->
@db.close (err) ->
if err
resolve "[FFTCGDB] Error closing: '#{err.message}'"
else
reject "[FFTCGDB] Closed '#{@filename}'"
FFTCGDB::register = (login, password) ->
that = @
new Promise (resolve, reject) ->
# validate user input
if login == '' or password == ''
# no user name or password given
console.log "[FFTCGDB] reg: user name '#{login}' or password empty"
reject 'invalid'
# hash password
bcrypt.hash password, saltRounds, (err, hash) ->
if err
console.log "[FFTCGDB] reg: hash fail for name '#{login}'"
reject 'hash'
# try creating row in users table
stmt = that.db.prepare 'INSERT INTO users (login, pwdhash) VALUES (?, ?)'
stmt.run [login, hash], (err) ->
if err
console.log "[FFTCGDB] reg: DB fail '#{err.code}' for name '#{login}'"
stmt.finalize()
# reduce attack surface, don't disclose user names
reject 'db' # user already exists
else
console.log "[FFTCGDB] reg: OK '#{login}'"
stmt.finalize()
# registration successful
resolve
uid: @lastID
login: login
FFTCGDB::login = (login, password) ->
that = @
new Promise (resolve, reject) ->
# get users table row
stmt = that.db.prepare 'SELECT rowid, login, pwdhash FROM users WHERE login = ?'
stmt.get [login], (err, row) ->
if err
console.log "[FFTCGDB] login: DB fail '#{err.code}' for name '#{login}'"
stmt.finalize()
reject 'db'
else if not row
# hash the password for timing attack reasons
bcrypt.hash password, saltRounds, (err, hash) ->
console.log "[FFTCGDB] login: nonexistent '#{login}'"
stmt.finalize()
# reduce attack surface, don't disclose user names
reject 'login' # user doesnt exist
else
bcrypt.compare password, row.pwdhash, (err, res) ->
if err
console.log "[FFTCGDB] login: hash fail for name '#{login}'"
reject 'hash'
if res == true
console.log "[FFTCGDB] login: OK '#{row.login}'"
stmt.finalize()
# login successful
resolve
uid: row.rowid
login: row.login
else
console.log "[FFTCGDB] login: wrong password for '#{login}'"
stmt.finalize()
# login failed
reject 'login'
module.exports = FFTCGDB