Browse Source

Added access rights feature + read access check

pull/33/head
NGPixel 8 years ago
parent
commit
a05560e9fc
9 changed files with 122 additions and 17 deletions
  1. 12
      README.md
  2. 6
      assets/js/libs.js
  3. 8
      libs/auth.js
  4. 57
      libs/rights.js
  5. 6
      middlewares/auth.js
  6. 5
      models/user.js
  7. 10
      package.json
  8. 4
      server.js
  9. 31
      views/error-forbidden.pug

12
README.md

@ -24,10 +24,14 @@
- [x] Images - [x] Images
- [ ] Files/Documents - [ ] Files/Documents
- [x] Authentication - [x] Authentication
- [x] Local
- [x] Microsoft Account
- [x] Google ID
- [x] Facebook
- [x] Strategies
- [x] Local
- [x] Microsoft Account
- [x] Google ID
- [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

6
assets/js/libs.js
File diff suppressed because it is too large
View File

8
libs/auth.js

@ -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

@ -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;
}
};

6
middlewares/auth.js

@ -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);

5
models/user.js

@ -36,7 +36,10 @@ var userSchema = modb.Schema({
}, },
rights: [{ rights: [{
type: String
role: String,
path: String,
exact: Boolean,
deny: Boolean
}] }]
}, },

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-session": "^1.14.1",
"express-brute-mongoose": "0.0.7",
"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",

4
server.js

@ -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

@ -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…
Cancel
Save