Skip to content

Commit

Permalink
Change user password implemented. Closing #15.
Browse files Browse the repository at this point in the history
Also progress #3 and temporary disabling manifest.json (progress #9)
  • Loading branch information
kucingbasah737 committed Dec 5, 2023
1 parent b745604 commit 9488407
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 46 deletions.
22 changes: 14 additions & 8 deletions lib/webserver/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ const nunjucks = require('nunjucks');
const uniqid = require('uniqid');
const { default: RedisStore } = require('connect-redis');
const session = require('express-session');
const flash = require('express-flash');
const redis = require('redis');
const cookieParser = require('cookie-parser');
const dayjs = require('dayjs');
const relativeTime = require('dayjs/plugin/relativeTime');
const { IP, IPv4 } = require('ip-toolkit');
Expand Down Expand Up @@ -53,16 +53,14 @@ module.exports = async () => {

const app = express();

app.use(cookieParser());
const sessionSecret = process.env.WEB_SESSION_SECRET || uniqid();

app.locals.appName = process.env.APP_NAME || 'REDIRECTOR';
app.locals.appVersion = global.appVersion;
app.locals.mainPage = '/dashboard';
app.locals.template = 'template.bootstrap-dashboard-example.html.njk';
app.locals.navbar = navbar;

await prepareLocalPublic();

if (WEB_USE_CF_CONNECTING_IP) {
logger.verbose(`${MODULE_NAME} 1A4F8EE0: Will use cf-connecting-ip as requester ip`);
}
Expand All @@ -88,14 +86,15 @@ module.exports = async () => {
store: redisStore,
resave: false,
saveUninitialized: false,
secret: process.env.WEB_SESSION_SECRET || uniqid(),
secret: sessionSecret,
cookie: {
maxAge: 7 * 24 * 3600 * 1000,
},
}));
// end of sessions

app.use((req, res, next) => {
const { uvid } = req.session;

const xid = uniqid();
res.locals.xid = xid;
res.locals.currentHostname = req.hostname;
Expand All @@ -121,20 +120,25 @@ module.exports = async () => {
res.locals.ip = ip;

// unique visitor id
if (!req.cookies.uvid) {
res.locals.uvid = uvid;
if (!uvid) {
res.locals.uvid = uuidv1();

logger.debug(`${MODULE_NAME} 3CEB38EE: New visitor detected, setting uivd cookie`, {
xid,
ip: req.ip,
url: req.originalUrl,
session: req.session,
uvid: res.locals.uvid,
});

req.cookies.uvid = res.locals.uvid;
req.session.uvid = res.locals.uvid;
}

next();
});

await prepareLocalPublic();
app.use(express.static('./node_modules/bootstrap/dist'));
app.use(express.static('./public/'));
app.use(express.static('./public.local/'));
Expand Down Expand Up @@ -165,6 +169,8 @@ module.exports = async () => {

accessLogger(app, 'logs');

app.use(flash());

app.get('/', (req, res) => res.redirect(app.locals.mainPage));

app.use('/dashboard', validateSession, routerDashboard);
Expand Down
55 changes: 53 additions & 2 deletions lib/webserver/routers/dashboard/routers/users/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
const MODULE_NAME = 'WEBSERVER.ROUTERS.DASHBOARD.ROUTERS.USERS';

const express = require('express');
const urlJoin = require('url-join');
const logger = require('../../../../../logger');
const forbidIfNotSuper = require('../../forbid-if-not-super-user');
const getUserList = require('../../../../../get-user-list');
const getUserByEmail = require('../../../../../get-user-by-email');
const getTargetList = require('../../../../../get-target-list');
const updatePassword = require('../../../../../update-password');

const router = express.Router();
module.exports = router;
Expand Down Expand Up @@ -46,10 +48,10 @@ const pageMain = async (req, res) => {
* @param {import('express').Response} res
*/
const pageView = async (req, res) => {
const { xid } = res.locals;
const { xid, currentUser } = res.locals;
const { email } = req.params;

if (email !== req.session.email && !res.locals.currentUser?.super) {
if (email !== req.session.email && !currentUser?.super) {
res.status(403).json({
status: 403,
message: 'You\'re not authorized to access this resource. Get Away!',
Expand Down Expand Up @@ -96,6 +98,55 @@ const pageView = async (req, res) => {
}
};

/**
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
const pageChangePasswordSubmit = async (req, res) => {
const { xid, currentUser } = res.locals;
const {
email: inputEmail,
newPassword,
newPasswordConfirmation,
} = req.body;

if (inputEmail !== currentUser.email && !currentUser.super) {
res.status(403).json({
status: 403,
message: 'No, you\'re not allowed to change other user password. Wanna play with us?',
'trace-id': xid,
});

return;
}

if (newPassword !== newPasswordConfirmation) {
req.flash('warn', 'Passwords didn\'t match. Please correct it and try again.');
res.redirect(urlJoin(req.baseUrl, 'view', inputEmail));
return;
}

try {
await updatePassword(xid, inputEmail, newPassword);

req.flash('info', 'Password has been changed.');
} catch (e) {
const newE = new Error(`${MODULE_NAME} 9EADC4F3: Exception on pageChangePasswordSubmit`);

logger.warn(newE.message, {
xid,
eCode: e.code,
eMessage: e.message,
});

req.flash('warn', 'Something wrong');
} finally {
res.redirect(urlJoin(req.baseUrl, 'view', inputEmail));
}
};

router.use(init);
router.get('/', forbidIfNotSuper({ redirectUrl: '/dashboard/users/view' }), pageMain);
router.get('/view/:email', pageView);
router.post('/change-password', express.urlencoded({ extended: false }), pageChangePasswordSubmit);
2 changes: 1 addition & 1 deletion lib/webserver/routers/dump-request/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const pageMain = (req, res) => {
request: {
clientIp: req.ip,
clientIps: req.ips,
uvid: req.cookies.uvid,
uvid: res.locals.uvid || null,
method: req.method,
host: req.hostname,
url: req.originalUrl,
Expand Down
17 changes: 5 additions & 12 deletions lib/webserver/routers/login/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,19 +163,12 @@ const pageLoginVerification = async (req, res) => {
* @param {import('express').Response} res
*/
const pageLogout = (req, res) => {
const { xid } = res.locals;

req.session.destroy((e) => {
if (e) {
logger.warn(`${MODULE_NAME} ED2C76F7: Exception on destroying session`, {
xid,
eCode: e.code,
eMessage: e.message || e.toString(),
});
}
});
if (req.session.email) {
delete req.session.email;
}

res.redirect(req.app.locals.mainPage);
req.flash('info', 'Please log-in again');
res.redirect('/login');
};

router.get('/', pageLogin);
Expand Down
2 changes: 1 addition & 1 deletion lib/webserver/target-lookup.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ module.exports = async (req, res, next) => {
req.get('user-agent'),
req.get('referer'),
req.headers,
req.cookies?.uvid || null,
req.locals.uvid || null,
);

await incrementHit(xid, target.uuid, hitUuid);
Expand Down
13 changes: 8 additions & 5 deletions lib/webserver/validate-session.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ module.exports = async (req, res, next) => {
const { xid } = res.locals;
const { email } = (req.session || {});

logger.debug(`${MODULE_NAME} 3BEA4EF6: Validating`, {
xid,
session: req.session,
});

if (!email) {
req.session.destroy();
res.redirect('/login');
return;
}
Expand All @@ -35,7 +39,8 @@ module.exports = async (req, res, next) => {
email,
});

req.session.destroy();
delete req.session.email;
req.flash('info', 'Please log-in again');
res.redirect('/login');
return;
}
Expand All @@ -50,7 +55,7 @@ module.exports = async (req, res, next) => {
msg: 'Your user has been disabled!',
};

req.session.destroy();
delete req.session.email;
res.redirect(urlJoin(
'/login',
`?${querystring.stringify(qs)}`,
Expand All @@ -59,7 +64,6 @@ module.exports = async (req, res, next) => {
return;
}

// req.session.super = user.super;
res.locals.currentUser = user;

logger.debug(`${MODULE_NAME} 3BFA490F: Session validated`, {
Expand All @@ -76,7 +80,6 @@ module.exports = async (req, res, next) => {
eMessage: e.message || e.toString(),
});

// req.session.destroy();
res.status(500).json({
status: 500,
message: 'Something wrong',
Expand Down
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"dayjs": "^1.11.10",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-flash": "^0.0.2",
"express-rate-limit": "^7.1.4",
"express-session": "^1.17.3",
"geoip-lite": "^1.4.8",
Expand Down
23 changes: 22 additions & 1 deletion views/template.bootstrap-dashboard-example.html.njk
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
<link rel="apple-touch-icon" href="/vendors/bootstrap-signin/assets/img/favicons/apple-touch-icon.png" sizes="180x180">
<link rel="icon" href="/vendors/bootstrap-signin/assets/img/favicons/favicon-32x32.png" sizes="32x32" type="image/png">
<link rel="icon" href="/vendors/bootstrap-signin/assets/img/favicons/favicon-16x16.png" sizes="16x16" type="image/png">
<link rel="manifest" href="/vendors/bootstrap-signin/assets/img/favicons/manifest.json">
<link rel="mask-icon" href="/vendors/bootstrap-signin/assets/img/favicons/safari-pinned-tab.svg" color="#712cf9">
<link rel="icon" href="/vendors/bootstrap-signin/assets/img/favicons/favicon.ico">
{# <link rel="manifest" href="/vendors/bootstrap-signin/assets/img/favicons/manifest.json" crossorigin="use-credentials"> #}

<meta name="theme-color" content="#712cf9">


Expand Down Expand Up @@ -256,6 +257,26 @@
{% endif %}
</div>

{% if messages.info %}
<div class="alert alert-light" role="alert">
{{ messages.info }}
</div>
{% endif %}

{% if messages.success %}
<div class="alert alert-success" role="alert">
{{ messages.info }}
</div>
{% endif %}

{% if messages.warn %}
<div class="alert alert-danger" role="alert">
{{ messages.warn }}
</div>
{% endif %}



{% block content %}
<!-- no content yet -->
{% endblock %}
Expand Down
32 changes: 21 additions & 11 deletions views/users.modal.change-password.html.njk
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
<div class="modal fade" id="changePasswordModal" tabindex="-1" aria-labelledby="changePasswordModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST"
id="change-password"
action="{{ baseUrl}}/change-password"
>

<div class="modal-header">
<h1 class="modal-title fs-5" id="changePasswordModalLabel">Change Password</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form>
<div class="mb-3">
<label for="recipient-name" class="col-form-label">Recipient:</label>
<input type="text" class="form-control" id="recipient-name">
</div>
<div class="mb-3">
<label for="message-text" class="col-form-label">Message:</label>
<textarea class="form-control" id="message-text"></textarea>
</div>
</form>
<input name="email" id="username" value="{{ user.email }}" autocomplete="username" style="display: none;">

<div class="mb-3">
<label for="password" class="col-form-label">New password:</label>
<input name="newPassword" type="password" class="form-control" id="password" required
autocomplete="new-password"
>
</div>

<div class="mb-3">
<label for="password1" class="col-form-label">Retype new password:</label>
<input name="newPasswordConfirmation" type="password" class="form-control" id="password1" autocomplete="off" required>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Send message</button>
<button type="submit" class="btn btn-primary">Change password</button>
</div>

</form>
</div>
</div>
</div>
Loading

0 comments on commit 9488407

Please sign in to comment.