mbkauthe 1.0.3 → 1.0.4
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/index.js +5 -5
- package/lib/auth.js +13 -0
- package/lib/main.js +327 -3
- package/lib/pool.js +27 -0
- package/lib/validateSessionAndRole.js +152 -0
- package/package.json +3 -2
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import router from "./lib/main.js";
|
|
2
1
|
import dotenv from "dotenv";
|
|
3
|
-
import Joi from "joi";
|
|
2
|
+
import Joi from "joi";
|
|
3
|
+
import router from "./lib/main.js";
|
|
4
4
|
dotenv.config();
|
|
5
5
|
|
|
6
6
|
const envSchema = Joi.object({
|
|
@@ -17,7 +17,7 @@ const { error } = envSchema.validate(process.env);
|
|
|
17
17
|
if (error) {
|
|
18
18
|
throw new Error(`Environment variable validation error: ${error.message}`);
|
|
19
19
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
export { validateSession, checkRolePermission, validateSessionAndRole, getUserData } from "./lib/validateSessionAndRole.js";
|
|
21
|
+
export { authenticate } from "./lib/auth.js";
|
|
22
|
+
export { dblogin } from "./lib/pool.js";
|
|
23
23
|
export default router;
|
package/lib/auth.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const authenticate = (authentication) => {
|
|
2
|
+
return (req, res, next) => {
|
|
3
|
+
const token = req.headers["authorization"];
|
|
4
|
+
console.log(`Received token: ${token}`);
|
|
5
|
+
if (token === authentication) {
|
|
6
|
+
console.log("Authentication successful");
|
|
7
|
+
next();
|
|
8
|
+
} else {
|
|
9
|
+
console.log("Authentication failed");
|
|
10
|
+
res.status(401).send("Unauthorized");
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
};
|
package/lib/main.js
CHANGED
|
@@ -1,9 +1,333 @@
|
|
|
1
|
-
import express from
|
|
1
|
+
import express from "express";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import session from "express-session";
|
|
4
|
+
import pgSession from "connect-pg-simple";
|
|
5
|
+
const PgSession = pgSession(session);
|
|
6
|
+
import dotenv from "dotenv";
|
|
7
|
+
import { dblogin } from "./pool.js";
|
|
8
|
+
import { authenticate } from "./auth.js";
|
|
9
|
+
import fetch from 'node-fetch';
|
|
10
|
+
import cookieParser from "cookie-parser"; // Import cookie-parser
|
|
2
11
|
|
|
12
|
+
dotenv.config();
|
|
3
13
|
const router = express.Router();
|
|
14
|
+
let COOKIE_EXPIRE_TIME = 2 * 24 * 60 * 60 * 1000; //2 days
|
|
4
15
|
|
|
5
|
-
|
|
6
|
-
|
|
16
|
+
try {
|
|
17
|
+
const parsedExpireTime = parseInt(process.env.COOKIE_EXPIRE_TIME, 10);
|
|
18
|
+
if (!isNaN(parsedExpireTime) && parsedExpireTime > 0) {
|
|
19
|
+
COOKIE_EXPIRE_TIME = parsedExpireTime * 24 * 60 * 60 * 1000; // Convert days to milliseconds
|
|
20
|
+
} else {
|
|
21
|
+
console.warn("Invalid COOKIE_EXPIRE_TIME in environment variables, using default value");
|
|
22
|
+
}
|
|
23
|
+
WriteConsoleLogs(`Cookie expiration time set to ${COOKIE_EXPIRE_TIME} days for deployed environment`);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
WriteConsoleLogs("Error parsing COOKIE_EXPIRE_TIME:", error);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function WriteConsoleLogs(message) {
|
|
29
|
+
const appName = process.env.AppName;
|
|
30
|
+
try {
|
|
31
|
+
const query = `
|
|
32
|
+
INSERT INTO mbkauthlogs (app_name, message)
|
|
33
|
+
VALUES ($1, $2)
|
|
34
|
+
`;
|
|
35
|
+
await dblogin.query(query, [appName, message]);
|
|
36
|
+
console.log(`Logged message: ${message}`);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error("Error logging message:", error.message);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
router.use(express.json());
|
|
43
|
+
router.use(express.urlencoded({ extended: true }));
|
|
44
|
+
|
|
45
|
+
router.use(
|
|
46
|
+
session({
|
|
47
|
+
store: new PgSession({
|
|
48
|
+
pool: dblogin, // Connection pool
|
|
49
|
+
tableName: "session", // Use another table-name than the default "session" one
|
|
50
|
+
}),
|
|
51
|
+
secret: process.env.SESSION_SECRET_KEY, // Replace with your secret key
|
|
52
|
+
resave: false,
|
|
53
|
+
saveUninitialized: false,
|
|
54
|
+
cookie: {
|
|
55
|
+
maxAge: COOKIE_EXPIRE_TIME,
|
|
56
|
+
DOMAIN: process.env.IS_DEPLOYED === 'true' ? `.${process.env.DOMAIN}` : undefined, // Use root DOMAIN for subDOMAIN sharing
|
|
57
|
+
httpOnly: true,
|
|
58
|
+
secure: process.env.IS_DEPLOYED === 'true', // Use secure cookies in production
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
router.use(cookieParser()); // Use cookie-parser middleware
|
|
66
|
+
|
|
67
|
+
router.use((req, res, next) => {
|
|
68
|
+
if (req.session && req.session.user) {
|
|
69
|
+
const userAgent = req.headers["user-agent"];
|
|
70
|
+
const userIp =
|
|
71
|
+
req.headers["x-forwarded-for"] || req.connection.remoteAddress;
|
|
72
|
+
const formattedIp = userIp === "::1" ? "127.0.0.1" : userIp;
|
|
73
|
+
|
|
74
|
+
req.session.otherInfo = {
|
|
75
|
+
ip: formattedIp,
|
|
76
|
+
browser: userAgent,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
next();
|
|
80
|
+
} else {
|
|
81
|
+
next();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Save the username in a cookie, the cookie user name is use
|
|
86
|
+
// for displaying user name in profile menu. This cookie is not use anyelse where.
|
|
87
|
+
// So it is safe to use.
|
|
88
|
+
router.use(async (req, res, next) => {
|
|
89
|
+
if (req.session && req.session.user) {
|
|
90
|
+
try {
|
|
91
|
+
|
|
92
|
+
res.cookie("username", req.session.user.username, {
|
|
93
|
+
maxAge: COOKIE_EXPIRE_TIME,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const query = `SELECT "Role" FROM "Users" WHERE "UserName" = $1`;
|
|
97
|
+
const result = await dblogin.query(query, [req.session.user.username]);
|
|
98
|
+
|
|
99
|
+
if (result.rows.length > 0) {
|
|
100
|
+
req.session.user.role = result.rows[0].Role;
|
|
101
|
+
res.cookie("userRole", req.session.user.role, {
|
|
102
|
+
maxAge: COOKIE_EXPIRE_TIME,
|
|
103
|
+
});
|
|
104
|
+
} else {
|
|
105
|
+
req.session.user.role = null;
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {
|
|
108
|
+
WriteConsoleLogs("Error fetching user role:", error.message);
|
|
109
|
+
req.session.user.role = null; // Fallback to null role
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
next();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
router.use(async (req, res, next) => {
|
|
116
|
+
// Check for sessionId cookie if session is not initialized
|
|
117
|
+
if (!req.session.user && req.cookies && req.cookies.sessionId) {
|
|
118
|
+
WriteConsoleLogs("Restoring session from sessionId cookie"); // Log session restoration
|
|
119
|
+
const sessionId = req.cookies.sessionId;
|
|
120
|
+
const query = `SELECT * FROM "Users" WHERE "SessionId" = $1`;
|
|
121
|
+
const result = await dblogin.query(query, [sessionId]);
|
|
122
|
+
|
|
123
|
+
if (result.rows.length > 0) {
|
|
124
|
+
const user = result.rows[0];
|
|
125
|
+
req.session.user = {
|
|
126
|
+
id: user.id,
|
|
127
|
+
username: user.UserName,
|
|
128
|
+
sessionId,
|
|
129
|
+
};
|
|
130
|
+
WriteConsoleLogs(`Session restored for user: ${user.UserName}`); // Log successful session restoration
|
|
131
|
+
} else {
|
|
132
|
+
console.warn("No matching session found for sessionId"); // Log if no session is found
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
next();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
//Invoke-RestMethod -Uri http://localhost:3030/terminateAllSessions -Method POST
|
|
139
|
+
// Terminate all sessions route
|
|
140
|
+
router.post("/mbkauthe/api/terminateAllSessions", authenticate(process.env.Main_SECRET_TOKEN), async (req, res) => {
|
|
141
|
+
try {
|
|
142
|
+
await dblogin.query(`UPDATE "Users" SET "SessionId" = NULL`);
|
|
143
|
+
|
|
144
|
+
// Clear the session table
|
|
145
|
+
await dblogin.query('DELETE FROM "session"');
|
|
146
|
+
|
|
147
|
+
// Destroy all sessions on the server
|
|
148
|
+
req.session.destroy((err) => {
|
|
149
|
+
if (err) {
|
|
150
|
+
WriteConsoleLogs("Error destroying session:", err);
|
|
151
|
+
return res
|
|
152
|
+
.status(500)
|
|
153
|
+
.json({ success: false, message: "Failed to terminate sessions" });
|
|
154
|
+
}
|
|
155
|
+
WriteConsoleLogs("All sessions terminated successfully");
|
|
156
|
+
res.status(200).json({
|
|
157
|
+
success: true,
|
|
158
|
+
message: "All sessions terminated successfully",
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
} catch (err) {
|
|
162
|
+
WriteConsoleLogs("Database query error during session termination:", err);
|
|
163
|
+
res
|
|
164
|
+
.status(500)
|
|
165
|
+
.json({ success: false, message: "Internal Server Error" });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
router.post("/mbkauthe/api/login", async (req, res) => {
|
|
171
|
+
WriteConsoleLogs("Login request received"); // Log when login is initiated
|
|
172
|
+
|
|
173
|
+
const { username, password, token, recaptcha } = req.body;
|
|
174
|
+
WriteConsoleLogs(`Login attempt for username: ${username}`); // Log username
|
|
175
|
+
|
|
176
|
+
const secretKey = process.env.RECAPTCHA_SECRET_KEY;
|
|
177
|
+
const verificationUrl = `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${recaptcha}`;
|
|
178
|
+
|
|
179
|
+
// Bypass recaptcha for specific users
|
|
180
|
+
if (username !== "ibnekhalid" && username !== "maaz.waheed" && username !== "support") {
|
|
181
|
+
try {
|
|
182
|
+
const response = await fetch(verificationUrl, { method: 'POST' });
|
|
183
|
+
const body = await response.json();
|
|
184
|
+
WriteConsoleLogs("reCAPTCHA verification response:", body); // Log reCAPTCHA response
|
|
185
|
+
|
|
186
|
+
if (!body.success) {
|
|
187
|
+
WriteConsoleLogs("Failed reCAPTCHA verification");
|
|
188
|
+
return res.status(400).json({ success: false, message: "Failed reCAPTCHA verification" });
|
|
189
|
+
}
|
|
190
|
+
} catch (err) {
|
|
191
|
+
WriteConsoleLogs("Error during reCAPTCHA verification:", err);
|
|
192
|
+
return res.status(500).json({ success: false, message: "Internal Server Error" });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!username || !password) {
|
|
197
|
+
WriteConsoleLogs("Missing username or password");
|
|
198
|
+
return res.status(400).json({
|
|
199
|
+
success: false,
|
|
200
|
+
message: "Username and password are required",
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
WriteConsoleLogs("RECAPTCHA_SECRET_KEY:", process.env.RECAPTCHA_SECRET_KEY); // Log reCAPTCHA secret key
|
|
205
|
+
WriteConsoleLogs("SESSION_SECRET_KEY:", process.env.SESSION_SECRET_KEY); // Log reCAPTCHA secret key
|
|
206
|
+
WriteConsoleLogs("LOGIN_DB:", process.env.LOGIN_DB); // Log reCAPTCHA secret key
|
|
207
|
+
WriteConsoleLogs("COOKIE_EXPIRE_TIME:", process.env.COOKIE_EXPIRE_TIME); // Log reCAPTCHA secret key
|
|
208
|
+
WriteConsoleLogs("DOMAIN:", process.env.DOMAIN); // Log reCAPTCHA secret key
|
|
209
|
+
WriteConsoleLogs("IS_DEPLOYED:", process.env.IS_DEPLOYED); // Log reCAPTCHA secret key
|
|
210
|
+
WriteConsoleLogs("MBKAUTH_TWO_FA_ENABLE:", process.env.MBKAUTH_TWO_FA_ENABLE); // Log reCAPTCHA secret key
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
// Query to check if the username exists
|
|
214
|
+
const userQuery = `SELECT * FROM "Users" WHERE "UserName" = $1`;
|
|
215
|
+
const userResult = await dblogin.query(userQuery, [username]);
|
|
216
|
+
WriteConsoleLogs("User query result:", userResult.rows); // Log user query result
|
|
217
|
+
|
|
218
|
+
if (userResult.rows.length === 0) {
|
|
219
|
+
WriteConsoleLogs(`Username does not exist: ${username}`);
|
|
220
|
+
return res.status(404).json({ success: false, message: "Username does not exist" });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const user = userResult.rows[0];
|
|
224
|
+
|
|
225
|
+
// Check if the password matches
|
|
226
|
+
if (user.Password !== password) {
|
|
227
|
+
WriteConsoleLogs(`Incorrect password for username: ${username}`);
|
|
228
|
+
return res.status(401).json({ success: false, message: "Incorrect password" });
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Check if the account is inactive
|
|
232
|
+
if (!user.Active) {
|
|
233
|
+
WriteConsoleLogs(`Inactive account for username: ${username}`);
|
|
234
|
+
return res.status(403).json({ success: false, message: "Account is inactive" });
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if ((process.env.MBKAUTH_TWO_FA_ENABLE || "").toLocaleLowerCase() === "true") {
|
|
238
|
+
let sharedSecret;
|
|
239
|
+
const query = `SELECT "TwoFAStatus", "TwoFASecret" FROM "TwoFA" WHERE "UserName" = $1`;
|
|
240
|
+
const twoFAResult = await dblogin.query(query, [username]);
|
|
241
|
+
WriteConsoleLogs("TwoFA query result:", twoFAResult.rows); // Log TwoFA query result
|
|
242
|
+
|
|
243
|
+
sharedSecret = twoFAResult.rows[0]?.TwoFASecret;
|
|
244
|
+
if (twoFAResult.rows.length > 0 && twoFAResult.rows[0].TwoFAStatus && !token) {
|
|
245
|
+
WriteConsoleLogs("2FA code required but not provided");
|
|
246
|
+
return res.status(401).json({ success: false, message: "Please Enter 2FA code" });
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (token && twoFAResult.rows[0]?.TwoFAStatus) {
|
|
250
|
+
const tokenValidates = speakeasy.totp.verify({
|
|
251
|
+
secret: sharedSecret,
|
|
252
|
+
encoding: "base32",
|
|
253
|
+
token: token,
|
|
254
|
+
window: 1, // Allows a margin for clock drift, optional
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
if (!tokenValidates) {
|
|
258
|
+
WriteConsoleLogs(`Invalid 2FA code for username: ${username}`);
|
|
259
|
+
return res.status(401).json({ success: false, message: "Invalid 2FA code" });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Generate session ID
|
|
265
|
+
const sessionId = crypto.randomBytes(256).toString("hex");
|
|
266
|
+
WriteConsoleLogs(`Generated session ID for username: ${username}`); // Log session ID
|
|
267
|
+
|
|
268
|
+
await dblogin.query(`UPDATE "Users" SET "SessionId" = $1 WHERE "id" = $2`, [
|
|
269
|
+
sessionId,
|
|
270
|
+
user.id,
|
|
271
|
+
]);
|
|
272
|
+
|
|
273
|
+
// Store session ID in session
|
|
274
|
+
req.session.user = {
|
|
275
|
+
id: user.id,
|
|
276
|
+
username: user.UserName,
|
|
277
|
+
sessionId,
|
|
278
|
+
};
|
|
279
|
+
WriteConsoleLogs(`Session stored for user: ${user.UserName}, sessionId: ${sessionId}`); // Log session storage
|
|
280
|
+
|
|
281
|
+
// Set a cookie accessible across subDOMAINs
|
|
282
|
+
res.cookie("sessionId", sessionId, {
|
|
283
|
+
maxAge: COOKIE_EXPIRE_TIME,
|
|
284
|
+
DOMAIN: process.env.IS_DEPLOYED === 'true' ? `.${process.env.DOMAIN}` : undefined, // Use DOMAIN only in production
|
|
285
|
+
httpOnly: true,
|
|
286
|
+
secure: process.env.IS_DEPLOYED === 'true', // Use secure cookies in production
|
|
287
|
+
});
|
|
288
|
+
WriteConsoleLogs(`Cookie set for user: ${user.UserName}, sessionId: ${sessionId}`); // Log cookie setting
|
|
289
|
+
|
|
290
|
+
WriteConsoleLogs(`User "${username}" logged in successfully`);
|
|
291
|
+
res.status(200).json({
|
|
292
|
+
success: true,
|
|
293
|
+
message: "Login successful",
|
|
294
|
+
sessionId,
|
|
295
|
+
});
|
|
296
|
+
} catch (err) {
|
|
297
|
+
WriteConsoleLogs("Error during login process:", err);
|
|
298
|
+
res.status(500).json({ success: false, message: "Internal Server Error" });
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
router.post("/mbkauthe/api/logout", async (req, res) => {
|
|
303
|
+
if (req.session.user) {
|
|
304
|
+
try {
|
|
305
|
+
const { id, username } = req.session.user;
|
|
306
|
+
const query = `SELECT "Active" FROM "Users" WHERE "id" = $1`;
|
|
307
|
+
const result = await dblogin.query(query, [id]);
|
|
308
|
+
|
|
309
|
+
if (result.rows.length > 0 && !result.rows[0].Active) {
|
|
310
|
+
WriteConsoleLogs("Account is inactive during logout");
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
req.session.destroy((err) => {
|
|
314
|
+
if (err) {
|
|
315
|
+
WriteConsoleLogs("Error destroying session:", err);
|
|
316
|
+
return res.status(500).json({ success: false, message: "Logout failed" });
|
|
317
|
+
}
|
|
318
|
+
// Clear both session cookies
|
|
319
|
+
res.clearCookie("connect.sid");
|
|
320
|
+
res.clearCookie("sessionId"); // Clear the sessionId cookie used for restoration
|
|
321
|
+
WriteConsoleLogs(`User "${username}" logged out successfully`);
|
|
322
|
+
res.status(200).json({ success: true, message: "Logout successful" });
|
|
323
|
+
});
|
|
324
|
+
} catch (err) {
|
|
325
|
+
WriteConsoleLogs("Database query error during logout:", err);
|
|
326
|
+
res.status(500).json({ success: false, message: "Internal Server Error" });
|
|
327
|
+
}
|
|
328
|
+
} else {
|
|
329
|
+
res.status(400).json({ success: false, message: "Not logged in" });
|
|
330
|
+
}
|
|
7
331
|
});
|
|
8
332
|
|
|
9
333
|
export default router;
|
package/lib/pool.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import pkg from "pg";
|
|
2
|
+
const { Pool } = pkg;
|
|
3
|
+
import dotenv from "dotenv";
|
|
4
|
+
|
|
5
|
+
dotenv.config();
|
|
6
|
+
|
|
7
|
+
// PostgreSQL connection pool for pool
|
|
8
|
+
const poolConfig = {
|
|
9
|
+
connectionString: process.env.LOGIN_DB,
|
|
10
|
+
ssl: {
|
|
11
|
+
rejectUnauthorized: true,
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const dblogin = new Pool(poolConfig);
|
|
17
|
+
|
|
18
|
+
// Test connection for pool
|
|
19
|
+
(async () => {
|
|
20
|
+
try {
|
|
21
|
+
const client = await dblogin.connect();
|
|
22
|
+
console.log("Connected to PostgreSQL database (pool)!");
|
|
23
|
+
client.release();
|
|
24
|
+
} catch (err) {
|
|
25
|
+
console.error("Database connection error (pool):", err);
|
|
26
|
+
}
|
|
27
|
+
})();
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { dblogin } from "./pool.js";
|
|
2
|
+
|
|
3
|
+
async function validateSession(req, res, next) {
|
|
4
|
+
if (!req.session.user) {
|
|
5
|
+
return res.render("templates/Error/NotLoggedIn.handlebars", {
|
|
6
|
+
currentUrl: req.originalUrl,
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const { id, sessionId } = req.session.user;
|
|
12
|
+
const query = `SELECT "SessionId", "Active" FROM "Users" WHERE "id" = $1`;
|
|
13
|
+
const result = await dblogin.query(query, [id]);
|
|
14
|
+
|
|
15
|
+
// Check if user exists and session ID matches
|
|
16
|
+
if (result.rows.length === 0 || result.rows[0].SessionId !== sessionId) {
|
|
17
|
+
console.log(
|
|
18
|
+
`Session invalidated for user \"${req.session.user.username}\"`
|
|
19
|
+
);
|
|
20
|
+
req.session.destroy();
|
|
21
|
+
// ...existing code...
|
|
22
|
+
return res.render("templates/Error/SessionExpire.handlebars", {
|
|
23
|
+
currentUrl: req.originalUrl,
|
|
24
|
+
});
|
|
25
|
+
// ...existing code...
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check if the user account is inactive
|
|
29
|
+
if (!result.rows[0].Active) {
|
|
30
|
+
console.log(
|
|
31
|
+
`Account is inactive for user \"${req.session.user.username}\"`
|
|
32
|
+
);
|
|
33
|
+
req.session.destroy();
|
|
34
|
+
res.clearCookie("connect.sid");
|
|
35
|
+
return res.render("templates/Error/AccountInactive.handlebars", {
|
|
36
|
+
currentUrl: req.originalUrl,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
next(); // Proceed if everything is valid
|
|
41
|
+
} catch (err) {
|
|
42
|
+
console.error("Session validation error:", err);
|
|
43
|
+
res.status(500).json({ success: false, message: "Internal Server Error" });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const checkRolePermission = (requiredRole) => {
|
|
48
|
+
return async (req, res, next) => {
|
|
49
|
+
try {
|
|
50
|
+
if (!req.session || !req.session.user || !req.session.user.id) {
|
|
51
|
+
console.log("User not authenticated");
|
|
52
|
+
console.log(req.session);
|
|
53
|
+
return res.render("templates/Error/NotLoggedIn.handlebars", {
|
|
54
|
+
currentUrl: req.originalUrl,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (requiredRole === "Any" || requiredRole === "any") {
|
|
59
|
+
return next();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const userId = req.session.user.id;
|
|
63
|
+
|
|
64
|
+
const query = `SELECT "Role" FROM "Users" WHERE "id" = $1`;
|
|
65
|
+
const result = await dblogin.query(query, [userId]);
|
|
66
|
+
|
|
67
|
+
if (result.rows.length === 0) {
|
|
68
|
+
return res
|
|
69
|
+
.status(401)
|
|
70
|
+
.json({ success: false, message: "User not found" });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const userRole = result.rows[0].Role;
|
|
74
|
+
if (userRole !== requiredRole) {
|
|
75
|
+
return res.render("templates/Error/AccessDenied.handlebars", {
|
|
76
|
+
currentRole: userRole,
|
|
77
|
+
requiredRole: requiredRole,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
next();
|
|
82
|
+
} catch (err) {
|
|
83
|
+
console.error("Permission check error:", err);
|
|
84
|
+
res
|
|
85
|
+
.status(500)
|
|
86
|
+
.json({ success: false, message: "Internal Server Error" });
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const validateSessionAndRole = (requiredRole) => {
|
|
92
|
+
return async (req, res, next) => {
|
|
93
|
+
await validateSession(req, res, async () => {
|
|
94
|
+
await checkRolePermission(requiredRole)(req, res, next);
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
async function getUserData(UserName, parameters) {
|
|
100
|
+
try {
|
|
101
|
+
if (!parameters || parameters.length === 0) {
|
|
102
|
+
throw new Error("Parameters are required to fetch user data");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Dynamically select fields from Users table based on `parameters`
|
|
106
|
+
const userFields = [
|
|
107
|
+
"Password", "UserName", "Role", "Active", "GuestRole", "HaveMailAccount",
|
|
108
|
+
];
|
|
109
|
+
const profileFields = [
|
|
110
|
+
"FullName", "email", "Image", "ProjectLinks", "SocialAccounts", "Bio",
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
let userParameters = [];
|
|
114
|
+
let profileParameters = [];
|
|
115
|
+
|
|
116
|
+
if (parameters === "profiledata") {
|
|
117
|
+
userParameters = userFields.filter(field => field !== "Password");
|
|
118
|
+
profileParameters = profileFields;
|
|
119
|
+
} else {
|
|
120
|
+
userParameters = userFields.filter(field => parameters.includes(field));
|
|
121
|
+
profileParameters = profileFields.filter(field => parameters.includes(field));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Prepare queries based on required fields
|
|
125
|
+
let userResult = {};
|
|
126
|
+
if (userParameters.length > 0) {
|
|
127
|
+
const userQuery = `SELECT ${userParameters.map(field => `"${field}"`).join(", ")}
|
|
128
|
+
FROM "Users" WHERE "UserName" = $1`;
|
|
129
|
+
const userQueryResult = await dblogin.query(userQuery, [UserName]);
|
|
130
|
+
if (userQueryResult.rows.length === 0) return { error: "User not found" };
|
|
131
|
+
userResult = userQueryResult.rows[0];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let profileResult = {};
|
|
135
|
+
if (profileParameters.length > 0) {
|
|
136
|
+
const profileQuery = `SELECT ${profileParameters.map(field => `"${field}"`).join(", ")}
|
|
137
|
+
FROM profiledata WHERE "UserName" = $1`;
|
|
138
|
+
const profileQueryResult = await dblogin.query(profileQuery, [UserName]);
|
|
139
|
+
if (profileQueryResult.rows.length === 0) return { error: "Profile data not found" };
|
|
140
|
+
profileResult = profileQueryResult.rows[0];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Combine results
|
|
144
|
+
const combinedResult = { ...userResult, ...profileResult };
|
|
145
|
+
return combinedResult;
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.error("Error fetching user data:", err.message);
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export { validateSession, checkRolePermission, validateSessionAndRole, getUserData };
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mbkauthe",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "MBKTechStudio's reusable authentication system for Node.js applications.",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"scripts": {
|
|
7
|
-
"test": "
|
|
8
|
+
"test": "node index.js"
|
|
8
9
|
},
|
|
9
10
|
"repository": {
|
|
10
11
|
"type": "git",
|