From d0a4662797991a37e10ea3ed329d03d466667ef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=81=E6=B2=BB=E5=B9=B3?= Date: Sat, 22 Oct 2016 15:27:34 +0800 Subject: [PATCH 1/5] Update user schema --- src/common/constants/Roles.js | 8 ++++++++ src/server/models/User.js | 6 ++++++ 2 files changed, 14 insertions(+) create mode 100644 src/common/constants/Roles.js diff --git a/src/common/constants/Roles.js b/src/common/constants/Roles.js new file mode 100644 index 0000000..13cb996 --- /dev/null +++ b/src/common/constants/Roles.js @@ -0,0 +1,8 @@ +export default { + // end user + USER: 'USER', + // system administrator + ADMIN: 'ADMIN', + // root + ROOT: 'ROOT', +}; diff --git a/src/server/models/User.js b/src/server/models/User.js index 11f77d1..54a3937 100644 --- a/src/server/models/User.js +++ b/src/server/models/User.js @@ -2,6 +2,7 @@ import crypto from 'crypto'; import mongoose from 'mongoose'; import jwt from 'jsonwebtoken'; import configs from '../../../configs/project/server'; +import Roles from '../../common/constants/Roles'; const hashPassword = (rawPassword = '') => { let recursiveLevel = 5; @@ -32,6 +33,11 @@ let UserSchema = new mongoose.Schema({ required: true, set: hashPassword, }, + role: { + type: String, + enum: Object.keys(Roles).map(r => Roles[r]), + default: Roles.USER, + }, avatarURL: String, }, { versionKey: false, From 0d691978c21f31da1b575498faf2836691a79ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=81=E6=B2=BB=E5=B9=B3?= Date: Sat, 22 Oct 2016 15:37:46 +0800 Subject: [PATCH 2/5] Add roleRequired middleware --- src/server/middlewares/roleRequired.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/server/middlewares/roleRequired.js diff --git a/src/server/middlewares/roleRequired.js b/src/server/middlewares/roleRequired.js new file mode 100644 index 0000000..7879885 --- /dev/null +++ b/src/server/middlewares/roleRequired.js @@ -0,0 +1,16 @@ +import Errors from '../../common/constants/Errors'; + +const roleRequired = (requiredRoles) => (req, res, next) => { + if (( + requiredRoles instanceof Array && + requiredRoles.indexOf(req.user.role) >= 0 + ) || ( + req.user.role === requiredRoles + )) { + next(); + } else { + return res.errors([Errors.PERMISSION_DENIED]); + } +}; + +export default roleRequired; From c56104bbe23898d22c5bdfe7b013f9b43c7ae5d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=81=E6=B2=BB=E5=B9=B3?= Date: Sat, 22 Oct 2016 15:58:34 +0800 Subject: [PATCH 3/5] Add admin-role required API User#List --- src/common/api/user.js | 1 + src/server/controllers/user.js | 16 ++++++++++++++++ src/server/models/User.js | 3 +++ src/server/routes/api.js | 7 +++++++ 4 files changed, 27 insertions(+) diff --git a/src/common/api/user.js b/src/common/api/user.js index 584df24..2d64da1 100644 --- a/src/common/api/user.js +++ b/src/common/api/user.js @@ -1,4 +1,5 @@ export default (apiEngine) => ({ + list: ({ page }) => apiEngine.get('/api/users', { params: { page } }), register: (user) => apiEngine.post('/api/users', { data: user }), login: (user) => apiEngine.post('/api/users/login', { data: user }), logout: () => apiEngine.get('/api/users/logout'), diff --git a/src/server/controllers/user.js b/src/server/controllers/user.js index d46992d..5abdfdb 100644 --- a/src/server/controllers/user.js +++ b/src/server/controllers/user.js @@ -3,6 +3,22 @@ import User from '../models/User'; import filterAttribute from '../utils/filterAttribute'; export default { + list(req, res) { + User.paginate({ page: req.query.page }, handleDbError(res)((page) => { + User + .find({}) + .sort({ createdAt: 'desc' }) + .limit(page.limit) + .skip(page.skip) + .exec(handleDbError(res)((users) => { + res.json({ + users: users, + page: page, + }); + })); + })); + }, + create(req, res) { const user = User({ name: req.body.name, diff --git a/src/server/models/User.js b/src/server/models/User.js index 54a3937..c10e233 100644 --- a/src/server/models/User.js +++ b/src/server/models/User.js @@ -3,6 +3,7 @@ import mongoose from 'mongoose'; import jwt from 'jsonwebtoken'; import configs from '../../../configs/project/server'; import Roles from '../../common/constants/Roles'; +import paginatePlugin from './plugins/paginate'; const hashPassword = (rawPassword = '') => { let recursiveLevel = 5; @@ -47,6 +48,8 @@ let UserSchema = new mongoose.Schema({ }, }); +UserSchema.plugin(paginatePlugin); + UserSchema.path('email.value').validate(function(value, cb) { User.findOne({ 'email.value': value }, (err, user) => { cb(!err && !user); diff --git a/src/server/routes/api.js b/src/server/routes/api.js index 1f18ca1..c7d1255 100644 --- a/src/server/routes/api.js +++ b/src/server/routes/api.js @@ -1,6 +1,8 @@ import configs from '../../../configs/project/server'; +import Roles from '../../common/constants/Roles'; import bodyParser from '../middlewares/bodyParser'; import authRequired from '../middlewares/authRequired'; +import roleRequired from '../middlewares/roleRequired'; import fileUpload from '../middlewares/fileUpload'; import userController from '../controllers/user'; import formValidationController from '../controllers/formValidation'; @@ -9,6 +11,11 @@ import todoController from '../controllers/todo'; export default ({ app }) => { // user + app.get('/api/users', + authRequired, + roleRequired([Roles.ADMIN]), + userController.list + ); app.post('/api/users', bodyParser.json, userController.create); app.post('/api/users/login', bodyParser.json, userController.login); app.get('/api/users/logout', userController.logout); From 912b3ec43f136236e77efb367c9e550cb3215b12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=81=E6=B2=BB=E5=B9=B3?= Date: Sat, 22 Oct 2016 17:11:14 +0800 Subject: [PATCH 4/5] Add util composeEnterHooks --- src/common/utils/composeEnterHooks.js | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/common/utils/composeEnterHooks.js diff --git a/src/common/utils/composeEnterHooks.js b/src/common/utils/composeEnterHooks.js new file mode 100644 index 0000000..60138ea --- /dev/null +++ b/src/common/utils/composeEnterHooks.js @@ -0,0 +1,47 @@ +// ref: +// - +// - + +export default { + parallel(...hooks) { + let callbacksRequired = hooks.reduce((totalCallbacks, hook) => { + if (hook.length >= 3) { + totalCallbacks++; + } + return totalCallbacks; + }, 0); + + return function onEnter(nextState, replace, executeTransition) { + let callbacksInvoked = 0; + hooks.forEach((hook) => { + hook.call(this, nextState, replace, () => { + if (++callbacksInvoked === callbacksRequired) { + executeTransition(); + } + }); + }); + if (!callbacksRequired) { + executeTransition(); + } + }; + }, + + series(...hooks) { + return function onEnter(nextState, replace, executeTransition) { + (function executeHooksSynchronously(remainingHooks) { + if (!remainingHooks.length) { + return executeTransition(); + } + let nextHook = remainingHooks[0]; + if (nextHook.length >= 3) { + nextHook.call(this, nextState, replace, () => { + executeHooksSynchronously(remainingHooks.slice(1)); + }); + } else { + nextHook.call(this, nextState, replace); + executeHooksSynchronously(remainingHooks.slice(1)); + } + })(hooks); + }; + }, +}; From 6eb2155d06a3ce395e8c8f9795ab13300d77f6c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=81=E6=B2=BB=E5=B9=B3?= Date: Sat, 22 Oct 2016 18:07:18 +0800 Subject: [PATCH 5/5] Add onEnterHook roleRequired --- src/common/utils/roleRequired.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/common/utils/roleRequired.js diff --git a/src/common/utils/roleRequired.js b/src/common/utils/roleRequired.js new file mode 100644 index 0000000..7354797 --- /dev/null +++ b/src/common/utils/roleRequired.js @@ -0,0 +1,15 @@ +export default (store) => (requiredRoles) => (nextState, replace) => { + let { user } = store.getState().cookies; + user = (user && JSON.parse(user)) || {}; + + if (!(( + requiredRoles instanceof Array && + requiredRoles.indexOf(user.role) >= 0 + ) || ( + user.role === requiredRoles + ))) { + replace({ + pathname: '/', + }); + } +};