Added permission validation to non admin users

react_convert
Mark Moffat 2018-02-05 20:51:04 +01:00
parent bf67621a86
commit 4533e23993
9 changed files with 249 additions and 191 deletions

2
app.js
View File

@ -235,7 +235,7 @@ app.use('/', customer);
app.use('/', product);
app.use('/', order);
app.use('/', user);
app.use('/admin', admin);
app.use('/', admin);
app.use('/paypal', paypal);
app.use('/stripe', stripe);
app.use('/authorizenet', authorizenet);

View File

@ -11,7 +11,35 @@ const nodemailer = require('nodemailer');
const escape = require('html-entities').AllHtmlEntities;
let ObjectId = require('mongodb').ObjectID;
const restrictedRoutes = [
{route: '/admin/product/new', response: 'redirect'},
{route: '/admin/product/insert', response: 'redirect'},
{route: '/admin/product/edit/:id', response: 'redirect'},
{route: '/admin/product/update', response: 'redirect'},
{route: '/admin/product/delete/:id', response: 'redirect'},
{route: '/admin/product/published_state', response: 'json'},
{route: '/admin/product/setasmainimage', response: 'json'},
{route: '/admin/product/deleteimage', response: 'json'},
{route: '/admin/order/statusupdate', response: 'json'},
{route: '/admin/settings/update', response: 'json'},
{route: '/admin/settings/option/remove', response: 'json'},
{route: '/admin/settings/pages/new', response: 'redirect'},
{route: '/admin/settings/pages/edit/:page', response: 'redirect'},
{route: '/admin/settings/pages/update', response: 'json'},
{route: '/admin/settings/pages/delete/:page', response: 'redirect'},
{route: '/admin/settings/menu/new', response: 'redirect'},
{route: '/admin/settings/menu/update', response: 'redirect'},
{route: '/admin/settings/menu/delete/:menuid', response: 'redirect'},
{route: '/admin/settings/menu/save_order', response: 'json'},
{route: '/admin/file/upload', response: 'redirect'},
{route: '/admin/file/delete', response: 'json'}
];
// common functions
exports.restrict = (req, res, next) => {
exports.checkLogin(req, res, next);
};
exports.checkLogin = (req, res, next) => {
// if not protecting we check for public pages and don't checkLogin
if(req.session.needsSetup === true){
@ -26,6 +54,26 @@ exports.checkLogin = (req, res, next) => {
res.redirect('/admin/login');
};
// Middleware to check for admin access for certain route
exports.checkAccess = (req, res, next) => {
const routeCheck = _.find(restrictedRoutes, {'route': req.route.path});
// If the user is not an admin and route is restricted, show message and redirect to /admin
if(req.session.isAdmin === 'false' && routeCheck){
if(routeCheck.response === 'redirect'){
req.session.message = 'Unauthorised. Please refer to administrator.';
req.session.messageType = 'danger';
res.redirect('/admin');
return;
}
if(routeCheck.response === 'json'){
res.status(400).json({message: 'Unauthorised. Please refer to administrator.'});
}
}else{
next();
}
};
exports.showCartCloseBtn = (page) => {
let showCartCloseButton = true;
if(page === 'checkout' || page === 'pay'){
@ -64,10 +112,6 @@ exports.addSitemapProducts = (req, res, cb) => {
});
};
exports.restrict = (req, res, next) => {
exports.checkLogin(req, res, next);
};
exports.clearSessionValue = (session, sessionVar) => {
let temp;
if(session){

View File

@ -50,7 +50,7 @@ $(document).ready(function (){
showNotification(msg, 'success');
})
.fail(function(msg){
showNotification(msg.responseText, 'danger');
showNotification(msg.responseJSON.message, 'danger');
});
});
@ -118,10 +118,10 @@ $(document).ready(function (){
data: {id: this.id, state: this.checked}
})
.done(function(msg){
showNotification(msg, 'success');
showNotification(msg.message, 'success');
})
.fail(function(msg){
showNotification(msg.responseText, 'danger');
showNotification(msg.responseJSON.message, 'danger');
});
});
@ -540,7 +540,7 @@ $(document).ready(function (){
showNotification(msg, 'success');
})
.fail(function(msg){
showNotification(msg.responseText, 'danger');
showNotification(msg.responseJSON.message, 'danger');
});
}else{
showNotification('Please enter a permalink to validate', 'danger');

File diff suppressed because one or more lines are too long

View File

@ -10,12 +10,12 @@ const glob = require('glob');
const router = express.Router();
// Admin section
router.get('/', common.restrict, (req, res, next) => {
router.get('/admin', common.restrict, (req, res, next) => {
res.redirect('/admin/orders');
});
// logout
router.get('/logout', (req, res) => {
router.get('/admin/logout', (req, res) => {
req.session.user = null;
req.session.message = null;
req.session.messageType = null;
@ -23,7 +23,7 @@ router.get('/logout', (req, res) => {
});
// login form
router.get('/login', (req, res) => {
router.get('/admin/login', (req, res) => {
let db = req.app.db;
db.users.count({}, (err, userCount) => {
@ -54,7 +54,7 @@ router.get('/login', (req, res) => {
});
// login the user and check the password
router.post('/login_action', (req, res) => {
router.post('/admin/login_action', (req, res) => {
let db = req.app.db;
db.users.findOne({userEmail: req.body.email}, (err, user) => {
@ -92,7 +92,7 @@ router.post('/login_action', (req, res) => {
});
// setup form is shown when there are no users setup in the DB
router.get('/setup', (req, res) => {
router.get('/admin/setup', (req, res) => {
let db = req.app.db;
db.users.count({}, (err, userCount) => {
@ -119,7 +119,7 @@ router.get('/setup', (req, res) => {
});
// insert a user
router.post('/setup_action', (req, res) => {
router.post('/admin/setup_action', (req, res) => {
const db = req.app.db;
let doc = {
@ -156,7 +156,7 @@ router.post('/setup_action', (req, res) => {
});
// settings update
router.get('/settings', common.restrict, (req, res) => {
router.get('/admin/settings', common.restrict, (req, res) => {
res.render('settings', {
title: 'Cart settings',
session: req.session,
@ -172,7 +172,7 @@ router.get('/settings', common.restrict, (req, res) => {
});
// settings update
router.post('/settings/update', common.restrict, (req, res) => {
router.post('/admin/settings/update', common.restrict, common.checkAccess, (req, res) => {
let result = common.updateConfig(req.body);
if(result === true){
res.status(200).json({message: 'Settings successfully updated'});
@ -182,7 +182,7 @@ router.post('/settings/update', common.restrict, (req, res) => {
});
// settings update
router.post('/settings/option/remove', common.restrict, (req, res) => {
router.post('/admin/settings/option/remove', common.restrict, common.checkAccess, (req, res) => {
const db = req.app.db;
db.products.findOne({_id: common.getId(req.body.productId)}, (err, product) => {
if(err){
@ -209,7 +209,7 @@ router.post('/settings/option/remove', common.restrict, (req, res) => {
});
// settings update
router.get('/settings/menu', common.restrict, async (req, res) => {
router.get('/admin/settings/menu', common.restrict, async (req, res) => {
const db = req.app.db;
res.render('settings_menu', {
title: 'Cart menu',
@ -224,7 +224,7 @@ router.get('/settings/menu', common.restrict, async (req, res) => {
});
// settings page list
router.get('/settings/pages', common.restrict, (req, res) => {
router.get('/admin/settings/pages', common.restrict, (req, res) => {
const db = req.app.db;
db.pages.find({}).toArray(async (err, pages) => {
if(err){
@ -246,7 +246,7 @@ router.get('/settings/pages', common.restrict, (req, res) => {
});
// settings pages new
router.get('/settings/pages/new', common.restrict, async (req, res) => {
router.get('/admin/settings/pages/new', common.restrict, common.checkAccess, async (req, res) => {
const db = req.app.db;
res.render('settings_page_edit', {
@ -263,7 +263,7 @@ router.get('/settings/pages/new', common.restrict, async (req, res) => {
});
// settings pages editor
router.get('/settings/pages/edit/:page', common.restrict, (req, res) => {
router.get('/admin/settings/pages/edit/:page', common.restrict, common.checkAccess, (req, res) => {
const db = req.app.db;
db.pages.findOne({_id: common.getId(req.params.page)}, async (err, page) => {
if(err){
@ -299,7 +299,7 @@ router.get('/settings/pages/edit/:page', common.restrict, (req, res) => {
});
// settings update page
router.post('/settings/pages/update', common.restrict, (req, res) => {
router.post('/admin/settings/pages/update', common.restrict, common.checkAccess, (req, res) => {
const db = req.app.db;
let doc = {
@ -339,7 +339,7 @@ router.post('/settings/pages/update', common.restrict, (req, res) => {
});
// settings delete page
router.get('/settings/pages/delete/:page', common.restrict, (req, res) => {
router.get('/admin/settings/pages/delete/:page', common.restrict, common.checkAccess, (req, res) => {
const db = req.app.db;
db.pages.remove({_id: common.getId(req.params.page)}, {}, (err, numRemoved) => {
if(err){
@ -355,7 +355,7 @@ router.get('/settings/pages/delete/:page', common.restrict, (req, res) => {
});
// new menu item
router.post('/settings/menu/new', common.restrict, (req, res) => {
router.post('/admin/settings/menu/new', common.restrict, common.checkAccess, (req, res) => {
let result = common.newMenu(req, res);
if(result === false){
req.session.message = 'Failed creating menu.';
@ -365,7 +365,7 @@ router.post('/settings/menu/new', common.restrict, (req, res) => {
});
// update existing menu item
router.post('/settings/menu/update', common.restrict, (req, res) => {
router.post('/admin/settings/menu/update', common.restrict, common.checkAccess, (req, res) => {
let result = common.updateMenu(req, res);
if(result === false){
req.session.message = 'Failed updating menu.';
@ -375,7 +375,7 @@ router.post('/settings/menu/update', common.restrict, (req, res) => {
});
// delete menu item
router.get('/settings/menu/delete/:menuid', common.restrict, (req, res) => {
router.get('/admin/settings/menu/delete/:menuid', common.restrict, common.checkAccess, (req, res) => {
let result = common.deleteMenu(req, res, req.params.menuid);
if(result === false){
req.session.message = 'Failed deleting menu.';
@ -385,7 +385,7 @@ router.get('/settings/menu/delete/:menuid', common.restrict, (req, res) => {
});
// We call this via a Ajax call to save the order from the sortable list
router.post('/settings/menu/save_order', common.restrict, (req, res) => {
router.post('/admin/settings/menu/save_order', common.restrict, common.checkAccess, (req, res) => {
let result = common.orderMenu(req, res);
if(result === false){
res.status(400).json({message: 'Failed saving menu order'});
@ -395,7 +395,7 @@ router.post('/settings/menu/save_order', common.restrict, (req, res) => {
});
// validate the permalink
router.post('/api/validate_permalink', (req, res) => {
router.post('/admin/api/validate_permalink', (req, res) => {
// if doc id is provided it checks for permalink in any products other that one provided,
// else it just checks for any products with that permalink
const db = req.app.db;
@ -412,18 +412,16 @@ router.post('/api/validate_permalink', (req, res) => {
console.info(err.stack);
}
if(products > 0){
res.writeHead(400, {'Content-Type': 'application/text'});
res.end('Permalink already exists');
res.status(400).json({message: 'Permalink already exists'});
}else{
res.writeHead(200, {'Content-Type': 'application/text'});
res.end('Permalink validated successfully');
res.status(200).json({message: 'Permalink validated successfully'});
}
});
});
// upload the file
let upload = multer({dest: 'public/uploads/'});
router.post('/file/upload', common.restrict, upload.single('upload_file'), (req, res, next) => {
router.post('/admin/file/upload', common.restrict, common.checkAccess, upload.single('upload_file'), (req, res, next) => {
const db = req.app.db;
if(req.file){
@ -479,15 +477,15 @@ router.post('/file/upload', common.restrict, upload.single('upload_file'), (req,
});
// delete a file via ajax request
router.post('/testEmail', common.restrict, (req, res) => {
router.post('/admin/testEmail', common.restrict, (req, res) => {
let config = common.getConfig();
// TODO: Should fix this to properly handle result
common.sendEmail(config.emailAddress, 'expressCart test email', 'Your email settings are working');
res.status(200).json('Test email sent');
res.status(200).json({message: 'Test email sent'});
});
// delete a file via ajax request
router.post('/file/delete', common.restrict, (req, res) => {
router.post('/admin/file/delete', common.restrict, common.checkAccess, (req, res) => {
req.session.message = null;
req.session.messageType = null;
@ -503,7 +501,7 @@ router.post('/file/delete', common.restrict, (req, res) => {
});
});
router.get('/files', common.restrict, (req, res) => {
router.get('/admin/files', common.restrict, (req, res) => {
// loop files in /public/uploads/
glob('public/uploads/**', {nosort: true}, (er, files) => {
// sort array

View File

@ -131,7 +131,7 @@ router.get('/admin/order/delete/:id', common.restrict, (req, res) => {
});
// update order status
router.post('/admin/order/statusupdate', common.restrict, (req, res) => {
router.post('/admin/order/statusupdate', common.restrict, common.checkAccess, (req, res) => {
const db = req.app.db;
db.orders.update({_id: common.getId(req.body.order_id)}, {$set: {orderStatus: req.body.status}}, {multi: false}, (err, numReplaced) => {
if(err){

View File

@ -74,7 +74,7 @@ router.get('/admin/product/new', common.restrict, common.checkAccess, (req, res)
});
// insert new product form action
router.post('/admin/product/insert', common.restrict, (req, res) => {
router.post('/admin/product/insert', common.restrict, common.checkAccess, (req, res) => {
const db = req.app.db;
let doc = {
@ -145,7 +145,7 @@ router.post('/admin/product/insert', common.restrict, (req, res) => {
});
// render the editor
router.get('/admin/product/edit/:id', common.restrict, (req, res) => {
router.get('/admin/product/edit/:id', common.restrict, common.checkAccess, (req, res) => {
const db = req.app.db;
common.getImages(req.params.id, req, res, (images) => {
@ -176,7 +176,7 @@ router.get('/admin/product/edit/:id', common.restrict, (req, res) => {
});
// Update an existing product form action
router.post('/admin/product/update', common.restrict, (req, res) => {
router.post('/admin/product/update', common.restrict, common.checkAccess, (req, res) => {
const db = req.app.db;
db.products.findOne({_id: common.getId(req.body.frmProductId)}, (err, product) => {
@ -256,7 +256,7 @@ router.post('/admin/product/update', common.restrict, (req, res) => {
});
// delete product
router.get('/admin/product/delete/:id', common.restrict, (req, res) => {
router.get('/admin/product/delete/:id', common.restrict, common.checkAccess, (req, res) => {
const db = req.app.db;
// remove the article
@ -283,23 +283,21 @@ router.get('/admin/product/delete/:id', common.restrict, (req, res) => {
});
// update the published state based on an ajax call from the frontend
router.post('/admin/product/published_state', common.restrict, (req, res) => {
router.post('/admin/product/published_state', common.restrict, common.checkAccess, (req, res) => {
const db = req.app.db;
db.products.update({_id: common.getId(req.body.id)}, {$set: {productPublished: req.body.state}}, {multi: false}, (err, numReplaced) => {
if(err){
console.error(colors.red('Failed to update the published state: ' + err));
res.writeHead(400, {'Content-Type': 'application/text'});
res.end('Published state not updated');
res.status(400).json('Published state not updated');
}else{
res.writeHead(200, {'Content-Type': 'application/text'});
res.end('Published state updated');
res.status(200).json('Published state updated');
}
});
});
// set as main product image
router.post('/admin/product/setasmainimage', common.restrict, (req, res) => {
router.post('/admin/product/setasmainimage', common.restrict, common.checkAccess, (req, res) => {
const db = req.app.db;
// update the productImage to the db
@ -313,7 +311,7 @@ router.post('/admin/product/setasmainimage', common.restrict, (req, res) => {
});
// deletes a product image
router.post('/admin/product/deleteimage', common.restrict, (req, res) => {
router.post('/admin/product/deleteimage', common.restrict, common.checkAccess, (req, res) => {
const db = req.app.db;
// get the productImage from the db

View File

@ -2,15 +2,19 @@
<h2>Menu</h2>
<ul class="list-group">
<li class="list-group-item"><strong>Products</strong></li>
{{#ifCond session.isAdmin '===' 'true'}}
<li class="list-group-item"><i class="fa fa-plus-square-o fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/product/new">New</a></li>
{{/ifCond}}
<li class="list-group-item"><i class="fa fa-list fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/products">List</a></li>
<li class="list-group-item"><strong>Orders</strong></li>
<li class="list-group-item"><i class="fa fa-cube fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/orders">List</a></li>
<li class="list-group-item"><strong>Customers</strong></li>
<li class="list-group-item"><i class="fa fa-users fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/customers">List</a></li>
<li class="list-group-item"><strong>Users</strong></li>
{{#ifCond session.isAdmin '===' 'true'}}
<li class="list-group-item"><i class="fa fa-user-plus fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/user/new">New</a></li>
<li class="list-group-item"><i class="fa fa-user fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/users">Edit</a></li>
{{/ifCond}}
<li class="list-group-item"><i class="fa fa-user-circle-o fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/user/edit/{{session.userId}}">My Account</a></li>
<li class="list-group-item"><strong>Settings</strong></li>
<li class="list-group-item"><i class="fa fa-cog fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/settings">General settings</a></li>

View File

@ -3,7 +3,10 @@
<div class="row">
<div class="col-md-10">
<form method="post" id="settingsForm" action="/admin/settings/update" data-toggle="validator">
<h2 class="clearfix">General Settings <div class="pull-right"><button type="submit" id="btnSettingsUpdate" class="btn btn-success">Update</button></h2>
<h2 class="clearfix">General Settings
<div class="pull-right">
<button type="submit" id="btnSettingsUpdate" class="btn btn-success">Update</button>
</h2>
<div class="form-group">
<label>Cart name *</label>
<input type="text" class="form-control" name="cartTitle" value="{{config.cartTitle}}" required>
@ -38,7 +41,8 @@
<div class="form-group">
<label>Free shipping threshold</label>
<input type="text" class="form-control" name="freeShippingAmount" value="{{config.freeShippingAmount}}">
<p class="help-block">Orders over this value will mean the shipped will the FREE. Set to high value if you always want to charge shipping.</p>
<p class="help-block">Orders over this value will mean the shipped will the FREE. Set to high value if you always want to charge
shipping.</p>
</div>
<div class="form-group">
<label>Payment gateway</label>
@ -46,7 +50,8 @@
<option {{selectState 'paypal' config.paymentGateway}} value="paypal">Paypal</option>
<option {{selectState 'stripe' config.paymentGateway}} value="stripe">Stripe</option>
</select>
<p class="help-block">You will also need to configure your payment gateway credentials in the `/config/&lt;gateway_name&gt;.json` file.</p>
<p class="help-block">You will also need to configure your payment gateway credentials in the `/config/&lt;gateway_name&gt;.json`
file.</p>
</div>
<div class="form-group">
<label>Currency symbol</label>
@ -82,9 +87,13 @@
<div class="form-group">
<label>Menu Enabled: </label>
<div class="checkbox">
<label><input class="settingsMenuEnabled" type="checkbox" {{checkedState config.menuEnabled}} id="menuEnabled" name="menuEnabled"></label>
<label>
<input class="settingsMenuEnabled" type="checkbox" {{checkedState config.menuEnabled}} id="menuEnabled"
name="menuEnabled">
</label>
</div>
<p class="help-block">If a menu is set you can set it up <a href="/admin/settings/menu">here</a>.</p>
<p class="help-block">If a menu is set you can set it up
<a href="/admin/settings/menu">here</a>.</p>
</div>
<div class="form-group">
<label>Menu header</label>
@ -108,7 +117,10 @@
<label>Google analytics</label>
<textarea class="form-control" rows="3" id="googleAnalytics" name="googleAnalytics">{{googleAnalytics}}</textarea>
<input type="hidden" id="googleAnalytics_input" name="googleAnalytics_input">
<p class="help-block">Your Google Analytics code. Please also inlude the "script" tags - <a href="https://support.google.com/analytics/answer/1032385?hl=en" target="_blank">Help</a></p>
<p class="help-block">Your Google Analytics code. Please also inlude the "script" tags -
<a href="https://support.google.com/analytics/answer/1032385?hl=en"
target="_blank">Help</a>
</p>
</div>
<div class="form-group">
<label>Custom CSS</label>
@ -126,7 +138,9 @@
<div class="form-group">
<label>Email SMTP secure </label>
<div class="checkbox">
<label><input class="settingsMenuEnabled" type="checkbox" {{checkedState config.emailSecure}} name="emailSecure"></label>
<label>
<input class="settingsMenuEnabled" type="checkbox" {{checkedState config.emailSecure}} name="emailSecure">
</label>
</div>
</div>
<div class="form-group">
@ -142,4 +156,4 @@
</div>
</div>
</div>
</div>
</div>