notherbase-fs 4.1.3 → 4.2.0

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.
@@ -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,7 +31,7 @@ export default class User {
28
31
  * @param {Object} res An Express.js response.
29
32
  */
30
33
  logout = async (req, res) => {
31
- await req.session.destroy();
34
+ await req.session?.destroy();
32
35
 
33
36
  success(res, "Logged out.");
34
37
  }
@@ -65,6 +68,86 @@ export default class User {
65
68
  }
66
69
  }
67
70
 
71
+ validatePassword = async (req, password, user) => {
72
+ if (password && user?.memory?.data?.otp) {
73
+ if (password == user.memory.data.otp.code) {
74
+ if (Date.now() < user.memory.data.otp.expires) {
75
+ user.memory.data.otp.expires = 0;
76
+ await user.commit();
77
+ return "Authenticated.";
78
+ }
79
+ else return "One-time password expired.";
80
+ }
81
+ else {
82
+ let passResult = await bcrypt.compare(req.body.password, user.memory.data.password);
83
+ if (passResult) return "Authenticated.";
84
+ else return "Password doesn't match the username.";
85
+ }
86
+ }
87
+ else return "Password error.";
88
+ }
89
+
90
+ /**
91
+ * Change a user's email.
92
+ * @param {Object} req An Express.js request.
93
+ * @param {Object} res An Express.js response.
94
+ */
95
+ changeEmail = async (req, res) => {
96
+ if (loginCheck(req, res)) {
97
+ let spirit = await req.db.Spirit.recallOne("user", null, { username: req.session.currentUser });
98
+
99
+ if (check(res, spirit, "User not found!") &&
100
+ check(res, req.body.email, "New email must be provided."))
101
+ {
102
+ let result = await this.validatePassword(req, req.body.password, spirit);
103
+ if (result == "Authenticated.") {
104
+ let other = await req.db.Spirit.recallOne("user", null, { email: req.body.email });
105
+
106
+ if (check(res, !other, "Email already in use!")) {
107
+ spirit.addBackup({
108
+ ...spirit.memory.data,
109
+ email: req.body.email
110
+ });
111
+
112
+ await spirit.commit();
113
+
114
+ success(res, "Email changed successfully!");
115
+ }
116
+ }
117
+ else fail(res, result);
118
+ }
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Send a one-time password to the user's email.
124
+ * @param {Object} req An Express.js request.
125
+ * @param {Object} res An Express.js response.
126
+ */
127
+ sendOneTimePassword = async (req, res) => {
128
+ if (loginCheck(req, res)) {
129
+ let spirit = await req.db.Spirit.recallOne("user", null, { username: req.session.currentUser });
130
+
131
+ if (check(res, spirit, "User not found!")) {
132
+ let otp = Math.floor(100000 + Math.random() * 900000);
133
+ spirit.memory.data.otp = {
134
+ code: otp,
135
+ expires: Date.now() + 1000 * 60 * 15
136
+ }
137
+
138
+ await spirit.commit();
139
+
140
+ await req.db.SendMail.send(spirit.memory.data.email, 'One Time Password for NotherBase',
141
+ `<h1>Your One-Time Password:<h1>
142
+ <h2>${otp}<h2>
143
+ <p>Visit <a href="https://www.notherbase.com/the-front/keeper">notherbase.com/the-front/keeper</a> to use your one-time password.</p>
144
+ <p>This one-time password expires in 15 minutes.<p>`);
145
+
146
+ success(res, "One-time password sent.");
147
+ }
148
+ }
149
+ }
150
+
68
151
  /**
69
152
  * Register a new user account.
70
153
  * @param {Object} req An Express.js request.
@@ -84,7 +167,13 @@ export default class User {
84
167
  username: req.body.username,
85
168
  password: hash,
86
169
  authLevels: [ "Basic" ],
87
- view: "compact"
170
+ view: "compact",
171
+ email: "",
172
+ otp: {
173
+ code: "",
174
+ expires: 0
175
+ },
176
+ sessions: []
88
177
  });
89
178
 
90
179
  success(res, "Registration successful!");
@@ -99,15 +188,30 @@ export default class User {
99
188
  */
100
189
  login = async (req, res) => {
101
190
  let spirit = await req.db.Spirit.recallOne("user", null, { username: req.body.username });
102
-
103
191
  if (check(res, spirit, "User not found.")) {
104
- let passResult = await bcrypt.compare(req.body.password, spirit.memory.data.password);
192
+ spirit.memory.data = {
193
+ username: "",
194
+ password: "",
195
+ authLevels: [ "Basic" ],
196
+ view: "compact",
197
+ email: "",
198
+ otp: {
199
+ code: "",
200
+ expires: 0
201
+ },
202
+ sessions: {},
203
+ ...spirit.memory.data
204
+ }
205
+ await spirit.commit();
105
206
 
106
- if (check(res, passResult, "Password doesn't match the username.")) {
207
+ let result = await this.validatePassword(req, req.body.password, spirit);
208
+ if (result === "Authenticated.") {
107
209
  req.session.currentUser = req.body.username;
108
-
109
- success(res, "Logged in.", req.body.username);
210
+ spirit.memory.data.sessions[req.session.id] = Date.now() + 1000 * 60 * 60 * 24 * 28; // 28 days
211
+ await spirit.commit();
212
+ success(res, "Login successful!", req.body.username);
110
213
  }
214
+ else fail(res, result);
111
215
  }
112
216
  }
113
217
 
@@ -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 { passwordReset, send };
46
+ export default { send };
package/notherbase-fs.js CHANGED
@@ -36,7 +36,8 @@ class NotherBaseFS {
36
36
  this.app.use(express.json({
37
37
  extended: true,
38
38
  inflate: true,
39
- type: 'application/x-www-form-urlencoded'
39
+ type: 'application/x-www-form-urlencoded',
40
+ limit: '50mb'
40
41
  }));
41
42
 
42
43
  //be safe, needs to be before session
@@ -80,8 +81,8 @@ class NotherBaseFS {
80
81
  //enable cookies
81
82
  this.app.use((req, res, next) => {
82
83
  this.bases[req.hosting].session(req, res, next);
83
- });
84
-
84
+ });
85
+
85
86
  // allows us to get static files like css
86
87
  this.app.use((req, res, next) => {
87
88
  this.bases[req.hosting].static(req, res, next);
@@ -89,15 +90,32 @@ class NotherBaseFS {
89
90
  this.app.use(express.static(`${__dirname}/public`));
90
91
 
91
92
  //provide database access and etc to use in routes
92
- this.app.use((req, res, next) => {
93
+ this.app.use(async (req, res, next) => {
93
94
  req.globals = globals;
94
95
  req.db = Models;
96
+ req.user = req.session?.currentUser ? await req.db.Spirit.recallOne("user", null, { username: req.session.currentUser }) : null;
95
97
  req.lock = false;
96
98
  // enables sessions only if the protocol is https and we are in production, or if we are in development
97
99
  req.sessionsEnabled = (process.env.PRODUCTION == "true" && req.secure) || process.env.PRODUCTION == "false";
98
100
  next();
99
101
  });
100
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.memory.save();
111
+ }
112
+ }
113
+ else req.session.regenerate(() => {});
114
+ }
115
+
116
+ next();
117
+ });
118
+
101
119
  //spirit world routes
102
120
  this.app.use("/s", this.spiritWorld.router);
103
121
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "notherbase-fs",
3
- "version": "4.1.3",
3
+ "version": "4.2.0",
4
4
  "description": "Functions to help make developing for NotherBase easier.",
5
5
  "exports": "./notherbase-fs.js",
6
6
  "scripts": {
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
  }
@@ -2,7 +2,7 @@ export default async (req, user, io) => {
2
2
  if (user) {
3
3
  let spirit = await req.db.Spirit.recallOne("test-save3", user.memory._id);
4
4
 
5
- return spirit.memory.data.text;
5
+ return spirit?.memory?.data?.text;
6
6
  }
7
7
 
8
8
  return "Not Logged In";
@@ -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
 
@@ -99,12 +102,12 @@
99
102
  let test = new Test();
100
103
 
101
104
  let downloadData = async () => {
102
- let res = base.downloadData();
105
+ let res = await base.downloadData();
103
106
  console.log(res.message);
104
107
  }
105
108
 
106
109
  let deleteData = async () => {
107
- let res = base.deleteData($('#password').val());
110
+ let res = await base.deleteData($('#password').val());
108
111
  console.log(res.message);
109
112
  }
110
113
 
@@ -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>