notherbase-fs 4.5.4 → 4.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/server/user.js +231 -177
package/package.json
CHANGED
package/server/user.js
CHANGED
|
@@ -28,12 +28,17 @@ export default class User {
|
|
|
28
28
|
* @param {Object} res An Express.js response.
|
|
29
29
|
*/
|
|
30
30
|
logout = async (req, res) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
try {
|
|
32
|
+
if (loginCheck(req, res)) {
|
|
33
|
+
delete req.user?.data?.sessions[req.session.id];
|
|
34
|
+
await req.user.commit();
|
|
35
|
+
await req.session?.destroy();
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
success(res, "Logged out.");
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.log(error);
|
|
41
|
+
fail(res, "Server error");
|
|
37
42
|
}
|
|
38
43
|
}
|
|
39
44
|
|
|
@@ -43,49 +48,57 @@ export default class User {
|
|
|
43
48
|
* @param {Object} res An Express.js response.
|
|
44
49
|
*/
|
|
45
50
|
changePassword = async (req, res) => {
|
|
46
|
-
|
|
47
|
-
|
|
51
|
+
try {
|
|
52
|
+
if (loginCheck(req, res)) {
|
|
53
|
+
let spirit = await req.Spirit.findOne({ service: "user", "data.username": req.session.currentUser });
|
|
54
|
+
console.log(spirit);
|
|
55
|
+
|
|
48
56
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
if (check(res, spirit, "User not found!") &&
|
|
58
|
+
check(res, req.body.newPassword === req.body.confirmation, "New password and confirmation must match!") &&
|
|
59
|
+
check(res, req.body.oldPassword != req.body.newPassword, "New password must be different from the old one."))
|
|
60
|
+
{
|
|
61
|
+
let passResult = await bcrypt.compare(req.body.oldPassword, spirit.data.password);
|
|
54
62
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
await spirit.commit();
|
|
65
|
-
|
|
66
|
-
success(res, "Password changed successfully!");
|
|
63
|
+
if (check(res, passResult, "Old password incorrect.")) {
|
|
64
|
+
const salt = await bcrypt.genSalt(10);
|
|
65
|
+
const hash = await bcrypt.hash(req.body.newPassword, salt);
|
|
66
|
+
|
|
67
|
+
spirit.data.password = hash;
|
|
68
|
+
await spirit.commit();
|
|
69
|
+
|
|
70
|
+
success(res, "Password changed successfully!");
|
|
71
|
+
}
|
|
67
72
|
}
|
|
68
73
|
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.log(error);
|
|
76
|
+
fail(res, "Server error");
|
|
69
77
|
}
|
|
70
78
|
}
|
|
71
79
|
|
|
72
80
|
validatePassword = async (req, password, user) => {
|
|
73
|
-
|
|
74
|
-
if (password
|
|
75
|
-
if (
|
|
76
|
-
user.data.otp.expires
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
try {
|
|
82
|
+
if (password && user?.data?.otp) {
|
|
83
|
+
if (password == user.data.otp.code) {
|
|
84
|
+
if (Date.now() < user.data.otp.expires) {
|
|
85
|
+
user.data.otp.expires = 0;
|
|
86
|
+
await user.commit();
|
|
87
|
+
return "Authenticated.";
|
|
88
|
+
}
|
|
89
|
+
else return "One-time password expired.";
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
let passResult = await bcrypt.compare(req.body.password, user.data.password);
|
|
93
|
+
if (passResult) return "Authenticated.";
|
|
94
|
+
else return "Password doesn't match the username.";
|
|
79
95
|
}
|
|
80
|
-
else return "One-time password expired.";
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
let passResult = await bcrypt.compare(req.body.password, user.data.password);
|
|
84
|
-
if (passResult) return "Authenticated.";
|
|
85
|
-
else return "Password doesn't match the username.";
|
|
86
96
|
}
|
|
97
|
+
else return "Password error.";
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.log(error);
|
|
100
|
+
fail(res, "Server error");
|
|
87
101
|
}
|
|
88
|
-
else return "Password error.";
|
|
89
102
|
}
|
|
90
103
|
|
|
91
104
|
/**
|
|
@@ -94,29 +107,30 @@ export default class User {
|
|
|
94
107
|
* @param {Object} res An Express.js response.
|
|
95
108
|
*/
|
|
96
109
|
changeEmail = async (req, res) => {
|
|
97
|
-
|
|
98
|
-
|
|
110
|
+
try {
|
|
111
|
+
if (loginCheck(req, res)) {
|
|
112
|
+
let spirit = await req.Spirit.findOne({ service: "user", "data.username": req.session.currentUser });
|
|
99
113
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
await spirit.commit();
|
|
114
|
-
|
|
115
|
-
success(res, "Email changed successfully!");
|
|
114
|
+
if (check(res, spirit, "User not found!") &&
|
|
115
|
+
check(res, req.body.email, "New email must be provided."))
|
|
116
|
+
{
|
|
117
|
+
let result = await this.validatePassword(req, req.body.password, spirit);
|
|
118
|
+
if (result == "Authenticated.") {
|
|
119
|
+
let other = await req.Spirit.findOne({ service: "user", "data.email": req.body.email });
|
|
120
|
+
|
|
121
|
+
if (check(res, !other, "Email already in use!")) {
|
|
122
|
+
spirit.data.email = req.body.email;
|
|
123
|
+
await spirit.commit();
|
|
124
|
+
|
|
125
|
+
success(res, "Email changed successfully!");
|
|
126
|
+
}
|
|
116
127
|
}
|
|
128
|
+
else fail(res, result);
|
|
117
129
|
}
|
|
118
|
-
else fail(res, result);
|
|
119
130
|
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.log(error);
|
|
133
|
+
fail(res, "Server error");
|
|
120
134
|
}
|
|
121
135
|
}
|
|
122
136
|
|
|
@@ -126,26 +140,31 @@ export default class User {
|
|
|
126
140
|
* @param {Object} res An Express.js response.
|
|
127
141
|
*/
|
|
128
142
|
sendOneTimePassword = async (req, res) => {
|
|
129
|
-
|
|
130
|
-
|
|
143
|
+
try {
|
|
144
|
+
if (loginCheck(req, res)) {
|
|
145
|
+
let spirit = await req.Spirit.findOne({ service: "user", "data.username": req.session.currentUser });
|
|
131
146
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
147
|
+
if (check(res, spirit, "User not found!")) {
|
|
148
|
+
let otp = Math.floor(100000 + Math.random() * 900000);
|
|
149
|
+
spirit.data.otp = {
|
|
150
|
+
code: otp,
|
|
151
|
+
expires: Date.now() + 1000 * 60 * 15
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
await spirit.commit();
|
|
140
155
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
156
|
+
await req.SendMail.send(spirit.data.email, 'One Time Password for NotherBase',
|
|
157
|
+
`<h1>Your One-Time Password:<h1>
|
|
158
|
+
<h2>${otp}<h2>
|
|
159
|
+
<p>Visit <a href="https://www.notherbase.com/the-front/keeper">notherbase.com/the-front/keeper</a> to use your one-time password.</p>
|
|
160
|
+
<p>This one-time password expires in 15 minutes.<p>`);
|
|
161
|
+
|
|
162
|
+
success(res, "One-time password sent.");
|
|
163
|
+
}
|
|
148
164
|
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.log(error);
|
|
167
|
+
fail(res, "Server error");
|
|
149
168
|
}
|
|
150
169
|
}
|
|
151
170
|
|
|
@@ -155,36 +174,41 @@ export default class User {
|
|
|
155
174
|
* @param {Object} res An Express.js response.
|
|
156
175
|
*/
|
|
157
176
|
register = async (req, res) => {
|
|
158
|
-
|
|
159
|
-
check(res, req.body.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
177
|
+
try {
|
|
178
|
+
if (check(res, req.body.password.length > 10, "Password must be >10 characters long.") &&
|
|
179
|
+
check(res, req.body.username.length > 2, "Username too short."))
|
|
180
|
+
{
|
|
181
|
+
let spirit = await req.Spirit.findOne({ service: "user", "data.username": req.body.username });
|
|
182
|
+
|
|
183
|
+
if (check(res, !spirit, "Username already in use!")) {
|
|
184
|
+
const salt = await bcrypt.genSalt(10);
|
|
185
|
+
const hash = await bcrypt.hash(req.body.password, salt);
|
|
166
186
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
187
|
+
spirit = new req.Spirit({
|
|
188
|
+
service:"user",
|
|
189
|
+
_lastUpdate: Date.now(),
|
|
190
|
+
data: {
|
|
191
|
+
username: req.body.username,
|
|
192
|
+
password: hash,
|
|
193
|
+
authLevels: [ "Basic" ],
|
|
194
|
+
view: "compact",
|
|
195
|
+
email: "",
|
|
196
|
+
otp: {
|
|
197
|
+
code: "",
|
|
198
|
+
expires: 0
|
|
199
|
+
},
|
|
200
|
+
sessions: {}
|
|
179
201
|
},
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
success(res, "Registration successful!");
|
|
202
|
+
backups: []
|
|
203
|
+
});
|
|
204
|
+
await spirit.save();
|
|
205
|
+
|
|
206
|
+
success(res, "Registration successful!");
|
|
207
|
+
}
|
|
187
208
|
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.log(error);
|
|
211
|
+
fail(res, "Server error");
|
|
188
212
|
}
|
|
189
213
|
}
|
|
190
214
|
|
|
@@ -194,32 +218,37 @@ export default class User {
|
|
|
194
218
|
* @param {Object} res An Express.js response.
|
|
195
219
|
*/
|
|
196
220
|
login = async (req, res) => {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
spirit.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
let result = await this.validatePassword(req, req.body.password, spirit);
|
|
215
|
-
if (result === "Authenticated.") {
|
|
216
|
-
req.session.currentUser = req.body.username;
|
|
217
|
-
if (typeof spirit.data.sessions !== "object" || Array.isArray(spirit.data.sessions)) spirit.data.sessions = {};
|
|
218
|
-
spirit.data.sessions[req.session.id] = Date.now() + 1000 * 60 * 60 * 24 * 28; // 28 days
|
|
221
|
+
try {
|
|
222
|
+
let spirit = await req.Spirit.findOne({ service: "user", "data.username": req.body.username });
|
|
223
|
+
if (check(res, spirit, "User not found.")) {
|
|
224
|
+
spirit.data = {
|
|
225
|
+
username: "",
|
|
226
|
+
password: "",
|
|
227
|
+
authLevels: [ "Basic" ],
|
|
228
|
+
view: "compact",
|
|
229
|
+
email: "",
|
|
230
|
+
otp: {
|
|
231
|
+
code: "",
|
|
232
|
+
expires: 0
|
|
233
|
+
},
|
|
234
|
+
sessions: {},
|
|
235
|
+
...spirit.data
|
|
236
|
+
}
|
|
219
237
|
await spirit.commit();
|
|
220
|
-
|
|
238
|
+
|
|
239
|
+
let result = await this.validatePassword(req, req.body.password, spirit);
|
|
240
|
+
if (result === "Authenticated.") {
|
|
241
|
+
req.session.currentUser = req.body.username;
|
|
242
|
+
if (typeof spirit.data.sessions !== "object" || Array.isArray(spirit.data.sessions)) spirit.data.sessions = {};
|
|
243
|
+
spirit.data.sessions[req.session.id] = Date.now() + 1000 * 60 * 60 * 24 * 28; // 28 days
|
|
244
|
+
await spirit.commit();
|
|
245
|
+
success(res, "Login successful!", req.body.username);
|
|
246
|
+
}
|
|
247
|
+
else fail(res, result);
|
|
221
248
|
}
|
|
222
|
-
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.log(error);
|
|
251
|
+
fail(res, "Server error");
|
|
223
252
|
}
|
|
224
253
|
}
|
|
225
254
|
|
|
@@ -229,22 +258,27 @@ export default class User {
|
|
|
229
258
|
* @param {Object} res An Express.js response.
|
|
230
259
|
*/
|
|
231
260
|
deletePermanently = async (req, res) => {
|
|
232
|
-
|
|
233
|
-
|
|
261
|
+
try {
|
|
262
|
+
if (loginCheck(req, res)) {
|
|
263
|
+
let spirit = await req.Spirit.findOne({ service: "user", "data.username": req.session.currentUser });
|
|
234
264
|
|
|
235
|
-
|
|
236
|
-
|
|
265
|
+
if (check(res, spirit, "User not found.")) {
|
|
266
|
+
let passResult = await bcrypt.compare(req.body.password, spirit.data.password);
|
|
237
267
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
if (check(res, deleted > 0, "No account deleted")) {
|
|
242
|
-
await req.session.destroy();
|
|
268
|
+
if (check(res, passResult, "Password doesn't match the username.")) {
|
|
269
|
+
let deleted = await req.Spirit.deleteMany({ service: "user", "data.username": req.session.currentUser });
|
|
243
270
|
|
|
244
|
-
|
|
271
|
+
if (check(res, deleted > 0, "No account deleted")) {
|
|
272
|
+
await req.session.destroy();
|
|
273
|
+
|
|
274
|
+
success(res, "Account deleted.");
|
|
275
|
+
}
|
|
245
276
|
}
|
|
246
277
|
}
|
|
247
278
|
}
|
|
279
|
+
} catch (error) {
|
|
280
|
+
console.log(error);
|
|
281
|
+
fail(res, "Server error");
|
|
248
282
|
}
|
|
249
283
|
}
|
|
250
284
|
|
|
@@ -254,81 +288,101 @@ export default class User {
|
|
|
254
288
|
* @param {Object} res An Express.js response.
|
|
255
289
|
*/
|
|
256
290
|
getInfo = async (req, res) => {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
291
|
+
try {
|
|
292
|
+
if (loginCheck(req, res)) {
|
|
293
|
+
let user = await req.Spirit.findOne({ service: "user", "data.username": req.session.currentUser });
|
|
294
|
+
|
|
295
|
+
if (check(res, user, "Account not found!")) {
|
|
296
|
+
success(res, "Info found", {
|
|
297
|
+
username: user.data.username
|
|
298
|
+
});
|
|
299
|
+
}
|
|
264
300
|
}
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.log(error);
|
|
303
|
+
fail(res, "Server error");
|
|
265
304
|
}
|
|
266
305
|
}
|
|
267
306
|
|
|
268
307
|
//download all spirit data belonging to the user
|
|
269
308
|
downloadData = async (req, res) => {
|
|
270
|
-
|
|
271
|
-
|
|
309
|
+
try {
|
|
310
|
+
if (loginCheck(req, res)) {
|
|
311
|
+
let user = await req.Spirit.findOne({ service: "user", "data.username": req.session.currentUser });
|
|
272
312
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
313
|
+
if (check(res, user, "Account not found!")) {
|
|
314
|
+
let data = await req.Spirit.find({ parent: user._id });
|
|
315
|
+
let dataToDownload = data.map(d => d);
|
|
276
316
|
|
|
277
|
-
|
|
317
|
+
successJSON(res, "Data Downloaded", dataToDownload);
|
|
318
|
+
}
|
|
278
319
|
}
|
|
320
|
+
} catch (error) {
|
|
321
|
+
console.log(error);
|
|
322
|
+
fail(res, "Server error");
|
|
279
323
|
}
|
|
280
324
|
}
|
|
281
325
|
|
|
282
326
|
//delete all spirit data belonging to the user
|
|
283
327
|
deleteAlldata = async (req, res) => {
|
|
284
|
-
|
|
285
|
-
|
|
328
|
+
try {
|
|
329
|
+
if (loginCheck(req, res)) {
|
|
330
|
+
let user = await req.Spirit.findOne({ service: "user", "data.username": req.session.currentUser });
|
|
286
331
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
332
|
+
if (check(res, user, "Account not found!")) {
|
|
333
|
+
if (check(res, req.body.password, "Password error.")) {
|
|
334
|
+
let passResult = await bcrypt.compare(req.body.password, user.data.password);
|
|
290
335
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
336
|
+
if (check(res, passResult, "Password doesn't match the username.")) {
|
|
337
|
+
let deleted = await req.Spirit.deleteMany({ parent: user._id });
|
|
338
|
+
if (check(res, deleted > 0, "No data deleted")) {
|
|
339
|
+
success(res, "Data Deleted", deleted);
|
|
340
|
+
}
|
|
295
341
|
}
|
|
296
342
|
}
|
|
297
343
|
}
|
|
298
344
|
}
|
|
345
|
+
} catch (error) {
|
|
346
|
+
console.log(error);
|
|
347
|
+
fail(res, "Server error");
|
|
299
348
|
}
|
|
300
349
|
}
|
|
301
350
|
|
|
302
351
|
// import spirit data from a JSON file
|
|
303
352
|
importData = async (req, res) => {
|
|
304
|
-
|
|
305
|
-
|
|
353
|
+
try {
|
|
354
|
+
if (loginCheck(req, res)) {
|
|
355
|
+
let user = await req.Spirit.findOne({ service: "user", "data.username": req.session.currentUser });
|
|
306
356
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
357
|
+
if (check(res, user, "Account not found!")) {
|
|
358
|
+
if (check(res, req.body.password, "Password error.")) {
|
|
359
|
+
let passResult = await bcrypt.compare(req.body.password, user.data.password);
|
|
310
360
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
361
|
+
if (check(res, passResult, "Password doesn't match the username.")) {
|
|
362
|
+
let data = JSON.parse(req.body.data);
|
|
363
|
+
let imported = 0;
|
|
364
|
+
for (let i = 0; i < data.length; i++) {
|
|
365
|
+
if (data[i].parent != null) {
|
|
366
|
+
let spirit = new req.Spirit({
|
|
367
|
+
service: data[i].service,
|
|
368
|
+
_lastUpdate: data[i]._lastUpdate,
|
|
369
|
+
data: data[i].data,
|
|
370
|
+
parent: user._id,
|
|
371
|
+
backups: data[i].backups || []
|
|
372
|
+
});
|
|
373
|
+
await spirit.save();
|
|
374
|
+
if (spirit) imported++;
|
|
375
|
+
}
|
|
325
376
|
}
|
|
326
|
-
}
|
|
327
377
|
|
|
328
|
-
|
|
378
|
+
success(res, "Data Imported", imported);
|
|
379
|
+
}
|
|
329
380
|
}
|
|
330
381
|
}
|
|
331
382
|
}
|
|
383
|
+
} catch (error) {
|
|
384
|
+
console.log(error);
|
|
385
|
+
fail(res, "Server error");
|
|
332
386
|
}
|
|
333
387
|
}
|
|
334
388
|
}
|