Commit aba86181 by tdgiang

Fix critical/important findings from final whole-branch review

parent 5342bbfe
...@@ -206,7 +206,7 @@ exports.listTransactions = function (req, res) { ...@@ -206,7 +206,7 @@ exports.listTransactions = function (req, res) {
if (!isAdmin) { if (!isAdmin) {
filter.createdByUsername = req.session.username; filter.createdByUsername = req.session.username;
} else if (req.query.staff) { } else if (req.query.staff) {
filter.createdByUsername = req.query.staff; filter.createdByUsername = String(req.query.staff);
} }
function render(staffOptions) { function render(staffOptions) {
......
...@@ -15,21 +15,26 @@ module.exports = function run(config, callback) { ...@@ -15,21 +15,26 @@ module.exports = function run(config, callback) {
console.error("adminBootstrap: no admin exists and ADMIN_USER/ADMIN_PASSWORD not set - cannot seed"); console.error("adminBootstrap: no admin exists and ADMIN_USER/ADMIN_PASSWORD not set - cannot seed");
return callback(null); return callback(null);
} }
AdminUser.create( passwordHash.hash(config.admin.password, function (hashErr, hashed) {
{ if (hashErr) {
username: String(config.admin.user).trim().toLowerCase(), return callback(hashErr);
passwordHash: passwordHash.hash(config.admin.password),
role: "admin",
active: true,
},
function (createErr, admin) {
if (createErr) {
return callback(createErr);
}
console.log("adminBootstrap: seeded first admin account:", admin.username);
return backfillTransactions(admin.username, callback);
} }
); AdminUser.create(
{
username: String(config.admin.user).trim().toLowerCase(),
passwordHash: hashed,
role: "admin",
active: true,
},
function (createErr, admin) {
if (createErr) {
return callback(createErr);
}
console.log("adminBootstrap: seeded first admin account:", admin.username);
return backfillTransactions(admin.username, callback);
}
);
});
}); });
}; };
......
"use strict"; "use strict";
var bcrypt = require("bcryptjs"); var bcrypt = require("bcryptjs");
function hash(plain) { function hash(plain, callback) {
return bcrypt.hashSync(plain, 10); bcrypt.hash(plain, 10, callback);
} }
function compare(plain, hashed) { function compare(plain, hashed, callback) {
return bcrypt.compareSync(plain, hashed); bcrypt.compare(plain, hashed, callback);
} }
module.exports = { module.exports = {
......
"use strict"; "use strict";
var AdminUser = require("../models/AdminUser");
module.exports = function requireLogin(req, res, next) { module.exports = function requireLogin(req, res, next) {
if (req.session && req.session.userId) { if (!req.session || !req.session.userId) {
return next(); if (req.is("json")) {
} return res.status(401).json({ code: "99", data: "LOGIN_REQUIRED" });
if (req.is("json")) { }
return res.status(401).json({ code: "99", data: "LOGIN_REQUIRED" }); return res.redirect("/admin/login");
} }
return res.redirect("/admin/login");
AdminUser.findById(req.session.userId, function (err, user) {
if (err) {
console.error("requireLogin: DB error:", err.message);
if (req.is("json")) {
return res.status(503).json({ code: "99", data: "SERVICE_UNAVAILABLE" });
}
return res.status(503).send("Hệ thống đang bận, vui lòng thử lại sau.");
}
if (!user || !user.active) {
return req.session.destroy(function () {
if (req.is("json")) {
return res.status(401).json({ code: "99", data: "LOGIN_REQUIRED" });
}
return res.redirect("/admin/login");
});
}
// Keep the session's cached role/username in sync with the DB on every request,
// in case an admin changed either after this session was created.
req.session.role = user.role;
req.session.username = user.username;
return next();
});
}; };
...@@ -100,6 +100,9 @@ ...@@ -100,6 +100,9 @@
{% if error == "MISSING_FIELDS" %}<div class="alert alert-error">Vui lòng nhập đủ thông tin.</div>{% endif %} {% if error == "MISSING_FIELDS" %}<div class="alert alert-error">Vui lòng nhập đủ thông tin.</div>{% endif %}
{% if error == "DUPLICATE_USERNAME" %}<div class="alert alert-error">Tên đăng nhập đã tồn tại.</div>{% endif %} {% if error == "DUPLICATE_USERNAME" %}<div class="alert alert-error">Tên đăng nhập đã tồn tại.</div>{% endif %}
{% if error == "DB_ERROR" %}<div class="alert alert-error">Lỗi cơ sở dữ liệu, vui lòng thử lại.</div>{% endif %} {% if error == "DB_ERROR" %}<div class="alert alert-error">Lỗi cơ sở dữ liệu, vui lòng thử lại.</div>{% endif %}
{% if error == "PASSWORD_TOO_SHORT" %}<div class="alert alert-error">Mật khẩu phải có ít nhất 8 ký tự.</div>{% endif %}
{% if error == "CANNOT_LOCK_SELF" %}<div class="alert alert-error">Bạn không thể khoá tài khoản của chính mình.</div>{% endif %}
{% if error == "LAST_ADMIN" %}<div class="alert alert-error">Không thể khoá — đây là tài khoản admin đang hoạt động cuối cùng.</div>{% endif %}
<div class="card"> <div class="card">
<h2>Tạo tài khoản nhân viên mới</h2> <h2>Tạo tài khoản nhân viên mới</h2>
...@@ -111,7 +114,7 @@ ...@@ -111,7 +114,7 @@
</div> </div>
<div class="form-field"> <div class="form-field">
<label for="password">Mật khẩu</label> <label for="password">Mật khẩu</label>
<input type="password" id="password" name="password" required> <input type="password" id="password" name="password" required minlength="8">
</div> </div>
<div class="form-field" style="max-width:140px;"> <div class="form-field" style="max-width:140px;">
<label for="role">Vai trò</label> <label for="role">Vai trò</label>
...@@ -144,11 +147,15 @@ ...@@ -144,11 +147,15 @@
<td><span class="pill pill-{% if u.active %}active{% else %}locked{% endif %}">{% if u.active %}Hoạt động{% else %}Đã khoá{% endif %}</span></td> <td><span class="pill pill-{% if u.active %}active{% else %}locked{% endif %}">{% if u.active %}Hoạt động{% else %}Đã khoá{% endif %}</span></td>
<td> <td>
<div class="row-actions"> <div class="row-actions">
{% if u.username == currentUsername %}
<span style="font-size:12.5px;color:var(--color-text-muted);">Tài khoản của bạn</span>
{% else %}
<form method="POST" action="/admin/accounts/{{u._id}}/toggle"> <form method="POST" action="/admin/accounts/{{u._id}}/toggle">
<button type="submit" class="secondary">{% if u.active %}Khoá{% else %}Mở khoá{% endif %}</button> <button type="submit" class="secondary">{% if u.active %}Khoá{% else %}Mở khoá{% endif %}</button>
</form> </form>
{% endif %}
<form method="POST" action="/admin/accounts/{{u._id}}/reset-password"> <form method="POST" action="/admin/accounts/{{u._id}}/reset-password">
<input type="password" name="newPassword" placeholder="Mật khẩu mới" required> <input type="password" name="newPassword" placeholder="Mật khẩu mới" required minlength="8">
<button type="submit" class="secondary">Đặt lại</button> <button type="submit" class="secondary">Đặt lại</button>
</form> </form>
</div> </div>
......
...@@ -71,9 +71,9 @@ ...@@ -71,9 +71,9 @@
</tbody> </tbody>
</table> </table>
<p class="pager"> <p class="pager">
{% if hasPrev %}<a href="/admin/transactions?page={{prevPage}}">&laquo; Trang trước</a>{% endif %} {% if hasPrev %}<a href="/admin/transactions?page={{prevPage}}{% if selectedStaff %}&staff={{selectedStaff}}{% endif %}">&laquo; Trang trước</a>{% endif %}
Trang {{page}} Trang {{page}}
{% if hasNext %}<a href="/admin/transactions?page={{nextPage}}">Trang sau &raquo;</a>{% endif %} {% if hasNext %}<a href="/admin/transactions?page={{nextPage}}{% if selectedStaff %}&staff={{selectedStaff}}{% endif %}">Trang sau &raquo;</a>{% endif %}
</p> </p>
</body> </body>
</html> </html>
...@@ -100,6 +100,7 @@ module.exports = function() { ...@@ -100,6 +100,7 @@ module.exports = function() {
cookie: { cookie: {
secure: cookieSecure, secure: cookieSecure,
httpOnly: true, httpOnly: true,
sameSite: 'lax',
maxAge: config.session_max_age maxAge: config.session_max_age
}, },
// store: new mongoStore({ // store: new mongoStore({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment