Added support for Datatables

Added support for ajax-based server side validation (useful for validation of emails, duplicate usernames, etc.)
User form more or less ready in the basic version (i.e. without permission management)
This commit is contained in:
Tomas Bures 2017-06-21 02:14:14 +02:00
parent f776170854
commit c81f5544e6
26 changed files with 1097 additions and 167 deletions

View file

@ -1,6 +1,6 @@
'use strict';
let users = require('../lib/models/users');
let users = require('../lib/models/users-legacy');
let lists = require('../lib/models/lists');
let fields = require('../lib/models/fields');
let blacklist = require('../lib/models/blacklist');

View file

@ -3,7 +3,7 @@
const passport = require('../lib/passport');
const router = require('../lib/router-async').create();
const _ = require('../lib/translate')._;
const namespaces = require('../lib/models/namespaces');
const namespaces = require('../models/namespaces');
const interoperableErrors = require('../shared/interoperable-errors');
router.all('/rest/*', (req, res, next) => {

147
routes/users-legacy.js Normal file
View file

@ -0,0 +1,147 @@
'use strict';
let passport = require('../lib/passport');
let express = require('express');
let router = new express.Router();
let users = require('../lib/models/users-legacy');
let fields = require('../lib/models/fields');
let settings = require('../lib/models/settings');
let _ = require('../lib/translate')._;
router.get('/logout', (req, res) => passport.logout(req, res));
router.post('/login', passport.parseForm, (req, res, next) => passport.login(req, res, next));
router.get('/login', (req, res) => {
res.render('users/login', {
next: req.query.next
});
});
router.get('/forgot', passport.csrfProtection, (req, res) => {
res.render('users/forgot', {
csrfToken: req.csrfToken()
});
});
router.post('/forgot', passport.parseForm, passport.csrfProtection, (req, res) => {
users.sendReset(req.body.username, err => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/users/forgot');
} else {
req.flash('success', _('An email with password reset instructions has been sent to your email address, if it exists on our system.'));
}
return res.redirect('/users/login');
});
});
router.get('/reset', passport.csrfProtection, (req, res) => {
users.checkResetToken(req.query.username, req.query.token, (err, status) => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/users/login');
}
if (!status) {
req.flash('danger', _('Unknown or expired reset token'));
return res.redirect('/users/login');
}
res.render('users/reset', {
csrfToken: req.csrfToken(),
username: req.query.username,
resetToken: req.query.token
});
});
});
router.post('/reset', passport.parseForm, passport.csrfProtection, (req, res) => {
users.resetPassword(req.body, (err, status) => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/users/reset?username=' + encodeURIComponent(req.body.username) + '&token=' + encodeURIComponent(req.body['reset-token']));
} else if (!status) {
req.flash('danger', _('Unknown or expired reset token'));
} else {
req.flash('success', _('Your password has been changed successfully'));
}
return res.redirect('/users/login');
});
});
router.all('/api', (req, res, next) => {
if (!req.user) {
req.flash('danger', _('Need to be logged in to access restricted content'));
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
}
next();
});
router.get('/api', passport.csrfProtection, (req, res, next) => {
users.get(req.user.id, (err, user) => {
if (err) {
return next(err);
}
if (!user) {
return next(new Error(_('User data not found')));
}
settings.list(['serviceUrl'], (err, configItems) => {
if (err) {
return next(err);
}
user.serviceUrl = configItems.serviceUrl;
user.csrfToken = req.csrfToken();
user.allowedTypes = Object.keys(fields.types).map(key => ({
type: key,
description: fields.types[key]
}));
res.render('users/api', user);
});
});
});
router.post('/api/reset-token', passport.parseForm, passport.csrfProtection, (req, res) => {
users.resetToken(Number(req.user.id), (err, success) => {
if (err) {
req.flash('danger', err.message || err);
} else if (success) {
req.flash('success', _('Access token updated'));
} else {
req.flash('info', _('Access token not updated'));
}
return res.redirect('/users/api');
});
});
router.all('/account', (req, res, next) => {
if (!req.user) {
req.flash('danger', _('Need to be logged in to access restricted content'));
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
}
next();
});
router.get('/account', passport.csrfProtection, (req, res) => {
let data = {
csrfToken: req.csrfToken(),
email: req.user.email
};
res.render('users/account', data);
});
router.post('/account', passport.parseForm, passport.csrfProtection, (req, res) => {
users.update(Number(req.user.id), req.body, (err, success) => {
if (err) {
req.flash('danger', err.message || err);
} else if (success) {
req.flash('success', _('Account information updated'));
} else {
req.flash('info', _('Account information not updated'));
}
return res.redirect('/users/account');
});
});
module.exports = router;

View file

@ -1,147 +1,102 @@
'use strict';
let passport = require('../lib/passport');
let express = require('express');
let router = new express.Router();
let users = require('../lib/models/users');
let fields = require('../lib/models/fields');
let settings = require('../lib/models/settings');
let _ = require('../lib/translate')._;
const passport = require('../lib/passport');
const router = require('../lib/router-async').create();
const _ = require('../lib/translate')._;
const users = require('../models/users');
const interoperableErrors = require('../shared/interoperable-errors');
const tools = require('../lib/tools-async');
router.get('/logout', (req, res) => passport.logout(req, res));
router.post('/login', passport.parseForm, (req, res, next) => passport.login(req, res, next));
router.get('/login', (req, res) => {
res.render('users/login', {
next: req.query.next
});
});
router.all('/rest/*', (req, res, next) => {
req.needsJSONResponse = true;
router.get('/forgot', passport.csrfProtection, (req, res) => {
res.render('users/forgot', {
csrfToken: req.csrfToken()
});
});
router.post('/forgot', passport.parseForm, passport.csrfProtection, (req, res) => {
users.sendReset(req.body.username, err => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/users/forgot');
} else {
req.flash('success', _('An email with password reset instructions has been sent to your email address, if it exists on our system.'));
}
return res.redirect('/users/login');
});
});
router.get('/reset', passport.csrfProtection, (req, res) => {
users.checkResetToken(req.query.username, req.query.token, (err, status) => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/users/login');
}
if (!status) {
req.flash('danger', _('Unknown or expired reset token'));
return res.redirect('/users/login');
}
res.render('users/reset', {
csrfToken: req.csrfToken(),
username: req.query.username,
resetToken: req.query.token
});
});
});
router.post('/reset', passport.parseForm, passport.csrfProtection, (req, res) => {
users.resetPassword(req.body, (err, status) => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/users/reset?username=' + encodeURIComponent(req.body.username) + '&token=' + encodeURIComponent(req.body['reset-token']));
} else if (!status) {
req.flash('danger', _('Unknown or expired reset token'));
} else {
req.flash('success', _('Your password has been changed successfully'));
}
return res.redirect('/users/login');
});
});
router.all('/api', (req, res, next) => {
if (!req.user) {
req.flash('danger', _('Need to be logged in to access restricted content'));
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
throw new interoperableErrors.NotLoggedInError();
}
next();
});
router.get('/api', passport.csrfProtection, (req, res, next) => {
users.get(req.user.id, (err, user) => {
if (err) {
return next(err);
}
if (!user) {
return next(new Error(_('User data not found')));
}
settings.list(['serviceUrl'], (err, configItems) => {
if (err) {
return next(err);
router.getAsync('/rest/users/:userId', async (req, res) => {
const user = await users.getById(req.params.userId);
return res.json(user);
});
router.postAsync('/rest/users', passport.csrfProtection, async (req, res) => {
await users.create(req.body);
return res.json();
});
router.putAsync('/rest/users/:userId', passport.csrfProtection, async (req, res) => {
const user = req.body;
user.id = parseInt(req.params.userId);
await users.updateWithConsistencyCheck(user);
return res.json();
});
router.deleteAsync('/rest/users/:userId', passport.csrfProtection, async (req, res) => {
await users.remove(req.params.userId);
return res.json();
});
router.postAsync('/rest/validate', async (req, res) => {
const data = {};
if (req.body.username) {
data.username = {};
try {
await users.getByUsername(req.body.username);
data.username.exists = true;
} catch (error) {
if (error instanceof interoperableErrors.NotFoundError) {
data.username.exists = false;
} else {
throw error;
}
user.serviceUrl = configItems.serviceUrl;
user.csrfToken = req.csrfToken();
user.allowedTypes = Object.keys(fields.types).map(key => ({
type: key,
description: fields.types[key]
}));
res.render('users/api', user);
});
});
});
router.post('/api/reset-token', passport.parseForm, passport.csrfProtection, (req, res) => {
users.resetToken(Number(req.user.id), (err, success) => {
if (err) {
req.flash('danger', err.message || err);
} else if (success) {
req.flash('success', _('Access token updated'));
} else {
req.flash('info', _('Access token not updated'));
}
return res.redirect('/users/api');
});
}
if (req.body.email) {
data.email = {};
try {
await tools.validateEmail(req.body.email);
data.email.invalid = false;
} catch (error) {
console.log(error);
data.email.invalid = true;
}
}
return res.json(data);
});
router.all('/account', (req, res, next) => {
router.postAsync('/rest/usersTable', async (req, res) => {
return res.json(await users.listDTAjax(req.body));
});
router.all('/*', (req, res, next) => {
if (!req.user) {
req.flash('danger', _('Need to be logged in to access restricted content'));
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
}
// res.setSelectedMenu('users'); FIXME
next();
});
router.get('/account', passport.csrfProtection, (req, res) => {
let data = {
csrfToken: req.csrfToken(),
email: req.user.email
};
res.render('users/account', data);
});
router.post('/account', passport.parseForm, passport.csrfProtection, (req, res) => {
users.update(Number(req.user.id), req.body, (err, success) => {
if (err) {
req.flash('danger', err.message || err);
} else if (success) {
req.flash('success', _('Account information updated'));
} else {
req.flash('info', _('Account information not updated'));
}
return res.redirect('/users/account');
router.getAsync('/*', passport.csrfProtection, async (req, res) => {
res.render('react-root', {
title: _('Users'),
reactEntryPoint: 'users',
reactCsrfToken: req.csrfToken()
});
});
module.exports = router;