forked from actualbudget/actual-server
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp-account.js
115 lines (90 loc) · 3.07 KB
/
app-account.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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() {
// eslint-disable-previous-line @typescript-eslint/no-empty-function
}
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;