Added access rights feature + read access check
This commit is contained in:
parent
09b4d37f4c
commit
a05560e9fc
@ -24,10 +24,14 @@
|
|||||||
- [x] Images
|
- [x] Images
|
||||||
- [ ] Files/Documents
|
- [ ] Files/Documents
|
||||||
- [x] Authentication
|
- [x] Authentication
|
||||||
|
- [x] Strategies
|
||||||
- [x] Local
|
- [x] Local
|
||||||
- [x] Microsoft Account
|
- [x] Microsoft Account
|
||||||
- [x] Google ID
|
- [x] Google ID
|
||||||
- [x] Facebook
|
- [x] Facebook
|
||||||
|
- [x] Access Rights
|
||||||
|
- [x] View
|
||||||
|
- [ ] Edit / Create
|
||||||
- [x] Background Agent (git sync, cache purge, etc.)
|
- [x] Background Agent (git sync, cache purge, etc.)
|
||||||
- [x] Caching
|
- [x] Caching
|
||||||
- [x] Create Entry
|
- [x] Create Entry
|
||||||
|
File diff suppressed because one or more lines are too long
@ -131,7 +131,13 @@ module.exports = function(passport, appconfig) {
|
|||||||
provider: 'local',
|
provider: 'local',
|
||||||
email: appconfig.admin,
|
email: appconfig.admin,
|
||||||
name: "Administrator",
|
name: "Administrator",
|
||||||
password: pwd
|
password: pwd,
|
||||||
|
rights: [{
|
||||||
|
role: 'admin',
|
||||||
|
path: '/',
|
||||||
|
exact: false,
|
||||||
|
deny: false
|
||||||
|
}]
|
||||||
});
|
});
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
winston.info('[' + PROCNAME + '][AUTH] Administrator account created successfully!');
|
winston.info('[' + PROCNAME + '][AUTH] Administrator account created successfully!');
|
||||||
|
57
libs/rights.js
Normal file
57
libs/rights.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rights
|
||||||
|
*/
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
|
||||||
|
check(req, role) {
|
||||||
|
|
||||||
|
let rt = [];
|
||||||
|
let p = _.chain(req.originalUrl).toLower().trim().value();
|
||||||
|
|
||||||
|
// Load User Rights
|
||||||
|
|
||||||
|
if(_.isArray(req.user.rights)) {
|
||||||
|
rt = req.user.rights;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is admin?
|
||||||
|
|
||||||
|
if(_.find(rt, { role: 'admin' })) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check specific role on path
|
||||||
|
|
||||||
|
let filteredRights = _.filter(rt, (r) => {
|
||||||
|
if(r.role === role || (r.role === 'write' && role === 'read')) {
|
||||||
|
if((!r.exact && _.startsWith(p, r.path)) || (r.exact && p === r.path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check for deny scenario
|
||||||
|
|
||||||
|
let isValid = false;
|
||||||
|
|
||||||
|
if(filteredRights.length > 1) {
|
||||||
|
isValid = !_.chain(filteredRights).sortBy((r) => {
|
||||||
|
return r.path.length + ((r.deny) ? 0.5 : 0);
|
||||||
|
}).last().get('deny').value();
|
||||||
|
} else if(filteredRights.length == 1 && filteredRights[0].deny === false) {
|
||||||
|
isValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deny by default
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
@ -19,6 +19,12 @@ module.exports = (req, res, next) => {
|
|||||||
return res.redirect('/login');
|
return res.redirect('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check permissions
|
||||||
|
|
||||||
|
if(!rights.check(req, 'read')) {
|
||||||
|
return res.render('error-forbidden');
|
||||||
|
}
|
||||||
|
|
||||||
// Set i18n locale
|
// Set i18n locale
|
||||||
|
|
||||||
req.i18n.changeLanguage(req.user.lang);
|
req.i18n.changeLanguage(req.user.lang);
|
||||||
|
@ -36,7 +36,10 @@ var userSchema = modb.Schema({
|
|||||||
},
|
},
|
||||||
|
|
||||||
rights: [{
|
rights: [{
|
||||||
type: String
|
role: String,
|
||||||
|
path: String,
|
||||||
|
exact: Boolean,
|
||||||
|
deny: Boolean
|
||||||
}]
|
}]
|
||||||
|
|
||||||
},
|
},
|
||||||
|
10
package.json
10
package.json
@ -37,7 +37,7 @@
|
|||||||
"bluebird": "^3.4.6",
|
"bluebird": "^3.4.6",
|
||||||
"body-parser": "^1.15.2",
|
"body-parser": "^1.15.2",
|
||||||
"cheerio": "^0.22.0",
|
"cheerio": "^0.22.0",
|
||||||
"child-process-promise": "^2.1.3",
|
"child-process-promise": "^2.2.0",
|
||||||
"chokidar": "^1.6.0",
|
"chokidar": "^1.6.0",
|
||||||
"compression": "^1.6.2",
|
"compression": "^1.6.2",
|
||||||
"connect-flash": "^0.1.1",
|
"connect-flash": "^0.1.1",
|
||||||
@ -46,8 +46,8 @@
|
|||||||
"cron": "^1.1.1",
|
"cron": "^1.1.1",
|
||||||
"express": "^4.14.0",
|
"express": "^4.14.0",
|
||||||
"express-brute": "^1.0.0",
|
"express-brute": "^1.0.0",
|
||||||
"express-brute-mongoose": "0.0.6",
|
"express-brute-mongoose": "0.0.7",
|
||||||
"express-session": "^1.14.1",
|
"express-session": "^1.14.2",
|
||||||
"farmhash": "^1.2.1",
|
"farmhash": "^1.2.1",
|
||||||
"file-type": "^3.8.0",
|
"file-type": "^3.8.0",
|
||||||
"filesize.js": "^1.0.2",
|
"filesize.js": "^1.0.2",
|
||||||
@ -58,7 +58,7 @@
|
|||||||
"i18next-express-middleware": "^1.0.2",
|
"i18next-express-middleware": "^1.0.2",
|
||||||
"i18next-node-fs-backend": "^0.1.2",
|
"i18next-node-fs-backend": "^0.1.2",
|
||||||
"js-yaml": "^3.6.1",
|
"js-yaml": "^3.6.1",
|
||||||
"lodash": "^4.16.4",
|
"lodash": "^4.16.5",
|
||||||
"markdown-it": "^8.0.0",
|
"markdown-it": "^8.0.0",
|
||||||
"markdown-it-abbr": "^1.0.4",
|
"markdown-it-abbr": "^1.0.4",
|
||||||
"markdown-it-anchor": "^2.5.0",
|
"markdown-it-anchor": "^2.5.0",
|
||||||
@ -77,7 +77,7 @@
|
|||||||
"passport-google-oauth20": "^1.0.0",
|
"passport-google-oauth20": "^1.0.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"passport-windowslive": "^1.0.2",
|
"passport-windowslive": "^1.0.2",
|
||||||
"passport.socketio": "^3.6.2",
|
"passport.socketio": "^3.7.0",
|
||||||
"pug": "^2.0.0-beta6",
|
"pug": "^2.0.0-beta6",
|
||||||
"read-chunk": "^2.0.0",
|
"read-chunk": "^2.0.0",
|
||||||
"remove-markdown": "^0.1.0",
|
"remove-markdown": "^0.1.0",
|
||||||
|
@ -28,6 +28,7 @@ global.git = require('./libs/git').init(appconfig, false);
|
|||||||
global.lang = require('i18next');
|
global.lang = require('i18next');
|
||||||
global.mark = require('./libs/markdown');
|
global.mark = require('./libs/markdown');
|
||||||
global.upl = require('./libs/uploads').init(appconfig);
|
global.upl = require('./libs/uploads').init(appconfig);
|
||||||
|
global.rights = require('./libs/rights');
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Load modules
|
// Load modules
|
||||||
@ -217,9 +218,6 @@ io.use(passportSocketIo.authorize({
|
|||||||
accept();
|
accept();
|
||||||
},
|
},
|
||||||
fail: (data, message, error, accept) => {
|
fail: (data, message, error, accept) => {
|
||||||
if(error) {
|
|
||||||
throw new Error(message);
|
|
||||||
}
|
|
||||||
return accept(new Error(message));
|
return accept(new Error(message));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
31
views/error-forbidden.pug
Normal file
31
views/error-forbidden.pug
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
doctype html
|
||||||
|
html
|
||||||
|
head
|
||||||
|
meta(http-equiv='X-UA-Compatible', content='IE=edge')
|
||||||
|
meta(charset='UTF-8')
|
||||||
|
meta(name='viewport', content='width=device-width, initial-scale=1')
|
||||||
|
meta(name='theme-color', content='#009688')
|
||||||
|
meta(name='msapplication-TileColor', content='#009688')
|
||||||
|
meta(name='msapplication-TileImage', content='/favicons/ms-icon-144x144.png')
|
||||||
|
title= appconfig.title
|
||||||
|
|
||||||
|
// Favicon
|
||||||
|
each favsize in [57, 60, 72, 76, 114, 120, 144, 152, 180]
|
||||||
|
link(rel='apple-touch-icon', sizes=favsize + 'x' + favsize, href='/favicons/apple-icon-' + favsize + 'x' + favsize + '.png')
|
||||||
|
link(rel='icon', type='image/png', sizes='192x192', href='/favicons/android-icon-192x192.png')
|
||||||
|
each favsize in [32, 96, 16]
|
||||||
|
link(rel='icon', type='image/png', sizes=favsize + 'x' + favsize, href='/favicons/favicon-' + favsize + 'x' + favsize + '.png')
|
||||||
|
link(rel='manifest', href='/manifest.json')
|
||||||
|
|
||||||
|
// CSS
|
||||||
|
link(type='text/css', rel='stylesheet', href='/css/libs.css')
|
||||||
|
link(type='text/css', rel='stylesheet', href='/css/app.css')
|
||||||
|
|
||||||
|
body(class='server-error')
|
||||||
|
section.hero.is-danger.is-fullheight
|
||||||
|
.hero-body
|
||||||
|
.container
|
||||||
|
a(href='/'): img(src='/favicons/android-icon-96x96.png')
|
||||||
|
h1.title(style={ 'margin-top': '30px'}) Forbidden
|
||||||
|
h2.subtitle(style={ 'margin-bottom': '50px'}) Sorry, you don't have the necessary permissions to access this page.
|
||||||
|
a.button.is-dark.is-inverted(href='/') Go Home
|
Loading…
Reference in New Issue
Block a user