notherbase-fs 4.1.4 → 4.2.1
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/controllers/user.js +116 -8
- package/models/send-mail.js +1 -14
- package/notherbase-fs.js +20 -3
- package/package.json +1 -1
- package/public/js/base.js +12 -0
- package/test/the-front/check/_preprocess/saved.js +1 -1
- package/test/the-front/index.ejs +15 -0
package/controllers/user.js
CHANGED
|
@@ -11,6 +11,8 @@ export default class User {
|
|
|
11
11
|
|
|
12
12
|
this.router.post("/logout", this.logout);
|
|
13
13
|
this.router.post("/changePassword", this.changePassword);
|
|
14
|
+
this.router.post("/sendOTP", this.sendOneTimePassword);
|
|
15
|
+
this.router.post("/changeEmail", this.changeEmail);
|
|
14
16
|
this.router.post("/register", this.register);
|
|
15
17
|
this.router.post("/login", this.login);
|
|
16
18
|
this.router.post("/deletePermanently", this.deletePermanently);
|
|
@@ -20,6 +22,7 @@ export default class User {
|
|
|
20
22
|
this.router.post("/downloadData", this.downloadData);
|
|
21
23
|
this.router.post("/deleteAlldata", this.deleteAlldata);
|
|
22
24
|
this.router.post("/importData", this.importData);
|
|
25
|
+
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
/**
|
|
@@ -28,9 +31,13 @@ export default class User {
|
|
|
28
31
|
* @param {Object} res An Express.js response.
|
|
29
32
|
*/
|
|
30
33
|
logout = async (req, res) => {
|
|
31
|
-
|
|
34
|
+
if (loginCheck(req, res)) {
|
|
35
|
+
delete req.user.memory?.data?.sessions[req.session.id];
|
|
36
|
+
await req.user.commit();
|
|
37
|
+
await req.session?.destroy();
|
|
32
38
|
|
|
33
|
-
|
|
39
|
+
success(res, "Logged out.");
|
|
40
|
+
}
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
/**
|
|
@@ -65,6 +72,86 @@ export default class User {
|
|
|
65
72
|
}
|
|
66
73
|
}
|
|
67
74
|
|
|
75
|
+
validatePassword = async (req, password, user) => {
|
|
76
|
+
if (password && user?.memory?.data?.otp) {
|
|
77
|
+
if (password == user.memory.data.otp.code) {
|
|
78
|
+
if (Date.now() < user.memory.data.otp.expires) {
|
|
79
|
+
user.memory.data.otp.expires = 0;
|
|
80
|
+
await user.commit();
|
|
81
|
+
return "Authenticated.";
|
|
82
|
+
}
|
|
83
|
+
else return "One-time password expired.";
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
let passResult = await bcrypt.compare(req.body.password, user.memory.data.password);
|
|
87
|
+
if (passResult) return "Authenticated.";
|
|
88
|
+
else return "Password doesn't match the username.";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else return "Password error.";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Change a user's email.
|
|
96
|
+
* @param {Object} req An Express.js request.
|
|
97
|
+
* @param {Object} res An Express.js response.
|
|
98
|
+
*/
|
|
99
|
+
changeEmail = async (req, res) => {
|
|
100
|
+
if (loginCheck(req, res)) {
|
|
101
|
+
let spirit = await req.db.Spirit.recallOne("user", null, { username: req.session.currentUser });
|
|
102
|
+
|
|
103
|
+
if (check(res, spirit, "User not found!") &&
|
|
104
|
+
check(res, req.body.email, "New email must be provided."))
|
|
105
|
+
{
|
|
106
|
+
let result = await this.validatePassword(req, req.body.password, spirit);
|
|
107
|
+
if (result == "Authenticated.") {
|
|
108
|
+
let other = await req.db.Spirit.recallOne("user", null, { email: req.body.email });
|
|
109
|
+
|
|
110
|
+
if (check(res, !other, "Email already in use!")) {
|
|
111
|
+
spirit.addBackup({
|
|
112
|
+
...spirit.memory.data,
|
|
113
|
+
email: req.body.email
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
await spirit.commit();
|
|
117
|
+
|
|
118
|
+
success(res, "Email changed successfully!");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else fail(res, result);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Send a one-time password to the user's email.
|
|
128
|
+
* @param {Object} req An Express.js request.
|
|
129
|
+
* @param {Object} res An Express.js response.
|
|
130
|
+
*/
|
|
131
|
+
sendOneTimePassword = async (req, res) => {
|
|
132
|
+
if (loginCheck(req, res)) {
|
|
133
|
+
let spirit = await req.db.Spirit.recallOne("user", null, { username: req.session.currentUser });
|
|
134
|
+
|
|
135
|
+
if (check(res, spirit, "User not found!")) {
|
|
136
|
+
let otp = Math.floor(100000 + Math.random() * 900000);
|
|
137
|
+
spirit.memory.data.otp = {
|
|
138
|
+
code: otp,
|
|
139
|
+
expires: Date.now() + 1000 * 60 * 15
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
await spirit.commit();
|
|
143
|
+
|
|
144
|
+
await req.db.SendMail.send(spirit.memory.data.email, 'One Time Password for NotherBase',
|
|
145
|
+
`<h1>Your One-Time Password:<h1>
|
|
146
|
+
<h2>${otp}<h2>
|
|
147
|
+
<p>Visit <a href="https://www.notherbase.com/the-front/keeper">notherbase.com/the-front/keeper</a> to use your one-time password.</p>
|
|
148
|
+
<p>This one-time password expires in 15 minutes.<p>`);
|
|
149
|
+
|
|
150
|
+
success(res, "One-time password sent.");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
68
155
|
/**
|
|
69
156
|
* Register a new user account.
|
|
70
157
|
* @param {Object} req An Express.js request.
|
|
@@ -84,7 +171,13 @@ export default class User {
|
|
|
84
171
|
username: req.body.username,
|
|
85
172
|
password: hash,
|
|
86
173
|
authLevels: [ "Basic" ],
|
|
87
|
-
view: "compact"
|
|
174
|
+
view: "compact",
|
|
175
|
+
email: "",
|
|
176
|
+
otp: {
|
|
177
|
+
code: "",
|
|
178
|
+
expires: 0
|
|
179
|
+
},
|
|
180
|
+
sessions: []
|
|
88
181
|
});
|
|
89
182
|
|
|
90
183
|
success(res, "Registration successful!");
|
|
@@ -99,15 +192,30 @@ export default class User {
|
|
|
99
192
|
*/
|
|
100
193
|
login = async (req, res) => {
|
|
101
194
|
let spirit = await req.db.Spirit.recallOne("user", null, { username: req.body.username });
|
|
102
|
-
|
|
103
195
|
if (check(res, spirit, "User not found.")) {
|
|
104
|
-
|
|
196
|
+
spirit.memory.data = {
|
|
197
|
+
username: "",
|
|
198
|
+
password: "",
|
|
199
|
+
authLevels: [ "Basic" ],
|
|
200
|
+
view: "compact",
|
|
201
|
+
email: "",
|
|
202
|
+
otp: {
|
|
203
|
+
code: "",
|
|
204
|
+
expires: 0
|
|
205
|
+
},
|
|
206
|
+
sessions: {},
|
|
207
|
+
...spirit.memory.data
|
|
208
|
+
}
|
|
209
|
+
await spirit.commit();
|
|
105
210
|
|
|
106
|
-
|
|
211
|
+
let result = await this.validatePassword(req, req.body.password, spirit);
|
|
212
|
+
if (result === "Authenticated.") {
|
|
107
213
|
req.session.currentUser = req.body.username;
|
|
108
|
-
|
|
109
|
-
|
|
214
|
+
spirit.memory.data.sessions[req.session.id] = Date.now() + 1000 * 60 * 60 * 24 * 28; // 28 days
|
|
215
|
+
await spirit.commit();
|
|
216
|
+
success(res, "Login successful!", req.body.username);
|
|
110
217
|
}
|
|
218
|
+
else fail(res, result);
|
|
111
219
|
}
|
|
112
220
|
}
|
|
113
221
|
|
package/models/send-mail.js
CHANGED
|
@@ -7,19 +7,6 @@ const OAuth2 = google.auth.OAuth2;
|
|
|
7
7
|
const OAuth2Client = new OAuth2(process.env.CLIENTID, process.env.CLIENTSECRET);
|
|
8
8
|
OAuth2Client.setCredentials({ refresh_token: process.env.CLIENTREFRESH });
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Sends an email with a password reset code. WIP
|
|
13
|
-
* @param {String} toEmail Email address to send to.
|
|
14
|
-
* @param {Number} resetToken Token to reset by.
|
|
15
|
-
*/
|
|
16
|
-
const passwordReset = async (toEmail, resetToken) => {
|
|
17
|
-
return await send(toEmail, 'Password Reset for NotherBase',
|
|
18
|
-
`<h1>Your One-Time Password Reset Code:<h1>
|
|
19
|
-
<h2>${resetToken}<h2>
|
|
20
|
-
<p>Visit <a href="https://www.notherbase.com/the-front/keeper">notherbase.com/the-front/keeper</a> to finish changing your password.</p>`);
|
|
21
|
-
};
|
|
22
|
-
|
|
23
10
|
/**
|
|
24
11
|
* Sends an email. WIP
|
|
25
12
|
* @param {String} toEmail Email Address to send to.
|
|
@@ -56,4 +43,4 @@ const send = async (toEmail, subject, html, name = "NotherBase") => {
|
|
|
56
43
|
return sent;
|
|
57
44
|
}
|
|
58
45
|
|
|
59
|
-
export default {
|
|
46
|
+
export default { send };
|
package/notherbase-fs.js
CHANGED
|
@@ -81,8 +81,8 @@ class NotherBaseFS {
|
|
|
81
81
|
//enable cookies
|
|
82
82
|
this.app.use((req, res, next) => {
|
|
83
83
|
this.bases[req.hosting].session(req, res, next);
|
|
84
|
-
});
|
|
85
|
-
|
|
84
|
+
});
|
|
85
|
+
|
|
86
86
|
// allows us to get static files like css
|
|
87
87
|
this.app.use((req, res, next) => {
|
|
88
88
|
this.bases[req.hosting].static(req, res, next);
|
|
@@ -90,15 +90,32 @@ class NotherBaseFS {
|
|
|
90
90
|
this.app.use(express.static(`${__dirname}/public`));
|
|
91
91
|
|
|
92
92
|
//provide database access and etc to use in routes
|
|
93
|
-
this.app.use((req, res, next) => {
|
|
93
|
+
this.app.use(async (req, res, next) => {
|
|
94
94
|
req.globals = globals;
|
|
95
95
|
req.db = Models;
|
|
96
|
+
req.user = req.session?.currentUser ? await req.db.Spirit.recallOne("user", null, { username: req.session.currentUser }) : null;
|
|
96
97
|
req.lock = false;
|
|
97
98
|
// enables sessions only if the protocol is https and we are in production, or if we are in development
|
|
98
99
|
req.sessionsEnabled = (process.env.PRODUCTION == "true" && req.secure) || process.env.PRODUCTION == "false";
|
|
99
100
|
next();
|
|
100
101
|
});
|
|
101
102
|
|
|
103
|
+
//destroy session if it is not authorized
|
|
104
|
+
this.app.use(async (req, res, next) => {
|
|
105
|
+
if (req.session.currentUser) {
|
|
106
|
+
if (req.user?.memory?.data?.sessions?.[req.session.id]) {
|
|
107
|
+
if (req.user.memory.data.sessions[req.session.id] < Date.now()) {
|
|
108
|
+
req.session.regenerate(() => {});
|
|
109
|
+
delete req.user.memory.data.sessions[req.session.id];
|
|
110
|
+
await req.user.commit();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else req.session.regenerate(() => {});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
next();
|
|
117
|
+
});
|
|
118
|
+
|
|
102
119
|
//spirit world routes
|
|
103
120
|
this.app.use("/s", this.spiritWorld.router);
|
|
104
121
|
|
package/package.json
CHANGED
package/public/js/base.js
CHANGED
|
@@ -220,4 +220,16 @@ class Base {
|
|
|
220
220
|
let response = await Base.commune("importData", { password, data: text });
|
|
221
221
|
return response;
|
|
222
222
|
}
|
|
223
|
+
|
|
224
|
+
sendOTP = async (email) => {
|
|
225
|
+
let response = await Base.commune("sendOTP");
|
|
226
|
+
|
|
227
|
+
return response;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
changeEmail = async (password, email) => {
|
|
231
|
+
let response = await Base.commune("changeEmail", { password, email });
|
|
232
|
+
|
|
233
|
+
return response;
|
|
234
|
+
}
|
|
223
235
|
}
|
package/test/the-front/index.ejs
CHANGED
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
<input type="file" id="fileInput">
|
|
26
26
|
<!-- password input -->
|
|
27
27
|
<input type="test" id="password" placeholder="pass">
|
|
28
|
+
<button onclick="sendOTP()">Get OTP</button>
|
|
29
|
+
<input type="text" id="email">
|
|
30
|
+
<button onclick="changeEmail()">Change Email</button>
|
|
28
31
|
|
|
29
32
|
<hr>
|
|
30
33
|
|
|
@@ -114,4 +117,16 @@
|
|
|
114
117
|
let res = await base.importData(pass, file);
|
|
115
118
|
console.log(res.message);
|
|
116
119
|
}
|
|
120
|
+
|
|
121
|
+
let sendOTP = async () => {
|
|
122
|
+
let res = await base.sendOTP();
|
|
123
|
+
console.log(res.message);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let changeEmail = async () => {
|
|
127
|
+
let pass = $('#password').val();
|
|
128
|
+
let email = $('#email').val();
|
|
129
|
+
let res = await base.changeEmail(pass, email);
|
|
130
|
+
console.log(res.message);
|
|
131
|
+
}
|
|
117
132
|
</script>
|