瀏覽代碼

Added access rights feature + read access check

NGPixel 8 年之前
父節點
當前提交
a05560e9fc
共有 9 個文件被更改,包括 119 次插入14 次删除
  1. 8 4
      README.md
  2. 0 0
      assets/js/libs.js
  3. 7 1
      libs/auth.js
  4. 57 0
      libs/rights.js
  5. 6 0
      middlewares/auth.js
  6. 4 1
      models/user.js
  7. 5 5
      package.json
  8. 1 3
      server.js
  9. 31 0
      views/error-forbidden.pug

+ 8 - 4
README.md

@@ -24,10 +24,14 @@
 	- [x] Images
 	- [ ] Files/Documents
 - [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] Caching
 - [x] Create Entry

文件差異過大導致無法顯示
+ 0 - 0
assets/js/libs.js


+ 7 - 1
libs/auth.js

@@ -131,7 +131,13 @@ module.exports = function(passport, appconfig) {
 						provider: 'local',
 						email: appconfig.admin,
 						name: "Administrator",
-						password: pwd
+						password: pwd,
+						rights: [{
+							role: 'admin',
+							path: '/',
+							exact: false,
+							deny: false
+						}]
 					});
 				}).then(() => {
 					winston.info('[' + PROCNAME + '][AUTH] Administrator account created successfully!');

+ 57 - 0
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 - 0
middlewares/auth.js

@@ -19,6 +19,12 @@ module.exports = (req, res, next) => {
 		return res.redirect('/login');
 	}
 
+	// Check permissions
+
+	if(!rights.check(req, 'read')) {
+		return res.render('error-forbidden');
+	}
+
 	// Set i18n locale
 
 	req.i18n.changeLanguage(req.user.lang);

+ 4 - 1
models/user.js

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

+ 5 - 5
package.json

@@ -37,7 +37,7 @@
     "bluebird": "^3.4.6",
     "body-parser": "^1.15.2",
     "cheerio": "^0.22.0",
-    "child-process-promise": "^2.1.3",
+    "child-process-promise": "^2.2.0",
     "chokidar": "^1.6.0",
     "compression": "^1.6.2",
     "connect-flash": "^0.1.1",
@@ -46,8 +46,8 @@
     "cron": "^1.1.1",
     "express": "^4.14.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",
     "file-type": "^3.8.0",
     "filesize.js": "^1.0.2",
@@ -58,7 +58,7 @@
     "i18next-express-middleware": "^1.0.2",
     "i18next-node-fs-backend": "^0.1.2",
     "js-yaml": "^3.6.1",
-    "lodash": "^4.16.4",
+    "lodash": "^4.16.5",
     "markdown-it": "^8.0.0",
     "markdown-it-abbr": "^1.0.4",
     "markdown-it-anchor": "^2.5.0",
@@ -77,7 +77,7 @@
     "passport-google-oauth20": "^1.0.0",
     "passport-local": "^1.0.0",
     "passport-windowslive": "^1.0.2",
-    "passport.socketio": "^3.6.2",
+    "passport.socketio": "^3.7.0",
     "pug": "^2.0.0-beta6",
     "read-chunk": "^2.0.0",
     "remove-markdown": "^0.1.0",

+ 1 - 3
server.js

@@ -28,6 +28,7 @@ global.git = require('./libs/git').init(appconfig, false);
 global.lang = require('i18next');
 global.mark = require('./libs/markdown');
 global.upl = require('./libs/uploads').init(appconfig);
+global.rights = require('./libs/rights');
 
 // ----------------------------------------
 // Load modules
@@ -217,9 +218,6 @@ io.use(passportSocketIo.authorize({
     accept();
   },
   fail: (data, message, error, accept) => {
-    if(error) {
-      throw new Error(message);
-    }
     return accept(new Error(message));
   }
 }));

+ 31 - 0
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

部分文件因文件數量過多而無法顯示