Skip to content
Snippets Groups Projects
Select Git revision
  • master default protected
1 result

app-account.js

Blame
  • app-account.js 3.00 KiB
    let express = require('express');
    let bcrypt = require('bcrypt');
    let uuid = require('uuid');
    let errorMiddleware = require('./util/error-middleware');
    let { validateUser } = require('./util/validate-user');
    let { getAccountDb } = require('./account-db');
    
    let app = express();
    app.use(errorMiddleware);
    
    function init() {}
    
    function hashPassword(password) {
      return bcrypt.hashSync(password, 12);
    }
    
    // Non-authenticated endpoints:
    //
    // /boostrap (special endpoint for setting up the instance, cant call again)
    // /login
    
    app.get('/needs-bootstrap', (req, res) => {
      let accountDb = getAccountDb();
      let rows = accountDb.all('SELECT * FROM auth');
    
      res.send({
        status: 'ok',
        data: { bootstrapped: rows.length > 0 }
      });
    });
    
    app.post('/bootstrap', (req, res) => {
      let { password } = req.body;
      let accountDb = getAccountDb();
    
      let rows = accountDb.all('SELECT * FROM auth');
      if (rows.length !== 0) {
        res.status(400).send({
          status: 'error',
          reason: 'already-bootstrapped'
        });
        return;
      }
    
      if (password == null || password === '') {
        res.status(400).send({ status: 'error', reason: 'invalid-password' });
        return;
      }
    
      // Hash the password. There's really not a strong need for this
      // since this is a self-hosted instance owned by the user.
      // However, just in case we do it.
      let hashed = hashPassword(password);
      accountDb.mutate('INSERT INTO auth (password) VALUES (?)', [hashed]);
    
      let token = uuid.v4();
      accountDb.mutate('INSERT INTO sessions (token) VALUES (?)', [token]);
    
      res.send({ status: 'ok', data: { token } });
    });
    
    app.post('/login', (req, res) => {
      let { password } = req.body;
      let accountDb = getAccountDb();
    
      let row = accountDb.first('SELECT * FROM auth');
      let confirmed = row && bcrypt.compareSync(password, row.password);
    
      let token = null;
      if (confirmed) {
        // Right now, tokens are permanent and there's just one in the
        // system. In the future this should probably evolve to be a
        // "session" that times out after a long time or something, and
        // maybe each device has a different token
        let row = accountDb.first('SELECT * FROM sessions');
        token = row.token;
      }
    
      res.send({ status: 'ok', data: { token } });
    });
    
    app.post('/change-password', (req, res) => {
      let user = validateUser(req, res);
      if (!user) return;
    
      let accountDb = getAccountDb();
      let { password } = req.body;
    
      if (password == null || password === '') {
        res.send({ status: 'error', reason: 'invalid-password' });
        return;
      }
    
      let hashed = hashPassword(password);
    
      // Note that this doesn't have a WHERE. This table only ever has 1
      // row (maybe that will change in the future? if this this will not work)
      accountDb.mutate('UPDATE auth SET password = ?', [hashed]);
    
      res.send({ status: 'ok', data: {} });
    });
    
    app.get('/validate', (req, res) => {
      let user = validateUser(req, res);
      if (user) {
        res.send({ status: 'ok', data: { validated: true } });
      }
    });
    
    app.use(errorMiddleware);
    
    module.exports.handlers = app;
    module.exports.init = init;