diff --git a/package.json b/package.json
index b7a4a33..be44899 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"start": "npm run create && NODE_ENV=staging babel-node ./src/index.js",
"dev": "nodemon --exec babel-node ./src/index.js",
"create": "NODE_ENV=staging sequelize db:migrate",
- "migrate": "sequelize db:migrate:undo:all && sequelize db:migrate",
+ "migrate": "sequelize db:migrate:undo:all && sequelize db:migrate && sequelize db:seed:all",
"seqinit": "sequelize init",
"build": "npm run clear && babel src --out-dir build",
"clear": "rm -rf build",
diff --git a/src/controllers/authController.js b/src/controllers/authController.js
index 5511326..eb865c5 100644
--- a/src/controllers/authController.js
+++ b/src/controllers/authController.js
@@ -24,7 +24,8 @@ export default class AuthController {
const payload = { username: user.username,
id: user.id,
- email: user.email };
+ email: user.email,
+ roles: user.roles };
const token = await generateToken(payload);
return res.status(200).json({ message: 'Logged in successfully',
diff --git a/src/controllers/helpers/validateRoles.js b/src/controllers/helpers/validateRoles.js
new file mode 100644
index 0000000..c3209ef
--- /dev/null
+++ b/src/controllers/helpers/validateRoles.js
@@ -0,0 +1,3 @@
+const validateRoles = data => (data.permissions && data.role && data.tablesAllowed)
+&& data;
+export default validateRoles;
diff --git a/src/controllers/rolesController.js b/src/controllers/rolesController.js
new file mode 100644
index 0000000..8faa9ae
--- /dev/null
+++ b/src/controllers/rolesController.js
@@ -0,0 +1,19 @@
+/* eslint-disable require-jsdoc */
+import model from '../models';
+
+const { Permissions } = model;
+
+class PermitionsController {
+ static async createRole(req, res) {
+ try {
+ const permissions = req.body.permissions.split(',');
+ const tablesAllowed = req.body.tablesAllowed.split(',');
+ const role = await Permissions.create({ role: req.body.role, permissions, tablesAllowed });
+ return role && res.status(201).json({ message: 'Role successfully created' });
+ } catch (error) {
+ return res.status(400).json({ error: 'tableAllowed, role, and permissions are required ' });
+ }
+ }
+}
+
+export default PermitionsController;
diff --git a/src/controllers/userController.js b/src/controllers/userController.js
index 060493d..2ca0be4 100644
--- a/src/controllers/userController.js
+++ b/src/controllers/userController.js
@@ -29,12 +29,14 @@ export default class UserController {
const user = await User.create({ username,
email,
password: hashedPasword,
- verified });
+ verified,
+ roles: ['user'] });
const payload = { username: user.username,
id: user.id,
email: user.email,
- verified: user.verified };
+ verified: user.verified,
+ roles: user.roles };
const token = await generateToken(payload);
const mailSend = await MailSender.sendMail(user.email, user.username, token);
@@ -60,7 +62,8 @@ export default class UserController {
const payload = { username: user.username,
userId: user.id,
- email: user.email };
+ email: user.email,
+ role: user.role };
const token = await generateToken(payload);
// @sends a message to an existing email in our database with the below email template
const message = `
You are receiving this because you (or someone else) requested the reset of your password.
diff --git a/src/middlewares/roleCheck.js b/src/middlewares/roleCheck.js
new file mode 100644
index 0000000..4bb3e05
--- /dev/null
+++ b/src/middlewares/roleCheck.js
@@ -0,0 +1,7 @@
+
+const checkAdmin = (req, res, next) => (
+ req.user.roles.includes('admin')
+ ? next()
+ : res.status(403).json({ message: 'Not allowed to perform the action' }));
+
+export default checkAdmin;
diff --git a/src/migrations/20190624-create-userschema.js b/src/migrations/20190624-create-userschema.js
index 3218574..d6a4fb8 100644
--- a/src/migrations/20190624-create-userschema.js
+++ b/src/migrations/20190624-create-userschema.js
@@ -34,6 +34,6 @@ gender: { type: Sequelize.STRING,
allowNull: true },
provider: { type: Sequelize.STRING,
allowNull: true },
-role: { type: Sequelize.STRING,
+roles: { type: Sequelize.ARRAY(Sequelize.STRING),
allowNull: true }, }),
down: queryInterface => queryInterface.dropTable('User') };
diff --git a/src/migrations/20190703214859-create-user.js b/src/migrations/20190703214859-create-user.js
index 3218574..d6a4fb8 100644
--- a/src/migrations/20190703214859-create-user.js
+++ b/src/migrations/20190703214859-create-user.js
@@ -34,6 +34,6 @@ gender: { type: Sequelize.STRING,
allowNull: true },
provider: { type: Sequelize.STRING,
allowNull: true },
-role: { type: Sequelize.STRING,
+roles: { type: Sequelize.ARRAY(Sequelize.STRING),
allowNull: true }, }),
down: queryInterface => queryInterface.dropTable('User') };
diff --git a/src/migrations/20190725090430-create-permissions.js b/src/migrations/20190725090430-create-permissions.js
new file mode 100644
index 0000000..9df2f88
--- /dev/null
+++ b/src/migrations/20190725090430-create-permissions.js
@@ -0,0 +1,12 @@
+module.exports = { up: (queryInterface, Sequelize) => queryInterface.createTable('Permissions', { id: { allowNull: false,
+ autoIncrement: true,
+ primaryKey: true,
+ type: Sequelize.INTEGER },
+role: { type: Sequelize.STRING },
+permissions: { allowNull: false, type: Sequelize.ARRAY(Sequelize.STRING) },
+tablesAllowed: { allowNull: false, type: Sequelize.ARRAY(Sequelize.STRING) },
+createdAt: { allowNull: false,
+ type: Sequelize.DATE },
+updatedAt: { allowNull: false,
+ type: Sequelize.DATE } }),
+down: (queryInterface, Sequelize) => queryInterface.dropTable('Permissions') };
diff --git a/src/models/Permissions.js b/src/models/Permissions.js
new file mode 100644
index 0000000..b245097
--- /dev/null
+++ b/src/models/Permissions.js
@@ -0,0 +1,11 @@
+
+
+module.exports = (sequelize, DataTypes) => {
+ const Permissions = sequelize.define('Permissions', { role: { type: DataTypes.STRING,
+ allowNull: false },
+ tablesAllowed: { type: DataTypes.ARRAY(DataTypes.STRING),
+ allowNull: false },
+ permissions: { type: DataTypes.ARRAY(DataTypes.STRING),
+ allowNull: false } }, {});
+ return Permissions;
+};
diff --git a/src/models/User.js b/src/models/User.js
index e48b6fc..311787b 100644
--- a/src/models/User.js
+++ b/src/models/User.js
@@ -1,4 +1,4 @@
-export default (sequelize) => {
+export default (sequelize, DataTypes) => {
const User = sequelize.define('User',
{ username: { type: String, unique: true, required: true },
firstName: { allowNull: true, type: String },
@@ -15,7 +15,7 @@ export default (sequelize) => {
dateOfBirth: { allowNull: true, type: Date },
gender: { type: String },
provider: { type: String },
- role: { type: String } },
+ roles: { type: DataTypes.ARRAY(DataTypes.STRING), allowNull: true } },
{ timestamps: true, tableName: 'User' });
User.associate = (models) => {
diff --git a/src/routes/index.js b/src/routes/index.js
index 7a3acb9..f7ab177 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -17,11 +17,13 @@ import socialAPIRoute from './socialAPIRoute';
import optinAndOptOut from './optinAndOptOut';
import bookmarkRoute from './bookmarksRoutes';
import readerStatsRoute from './readerStatsRoute';
+import role from './roleRoutes';
const { verifyToken } = Auth;
const router = express.Router();
+router.use('/api', role);
router.use('/api', bookmarkRoute);
// article routes
@@ -50,4 +52,5 @@ router.patch('/api/users/verification/:userToken', UserController.verifyUser);
router.patch('/api/users/:username', verifyToken, bodyValidation, usernameAvailability, UserProfile.updateProfile);
router.get('/api/users/', verifyToken, UserProfile.getAllUser);
+
export default router;
diff --git a/src/routes/roleRoutes.js b/src/routes/roleRoutes.js
new file mode 100644
index 0000000..8d2ebfa
--- /dev/null
+++ b/src/routes/roleRoutes.js
@@ -0,0 +1,12 @@
+import express from 'express';
+import Auth from '../middlewares/Auth';
+import checkAdmin from '../middlewares/roleCheck';
+import roles from '../controllers/rolesController';
+
+const role = express.Router();
+
+const { verifyToken } = Auth;
+
+role.post('/role', verifyToken, checkAdmin, roles.createRole);
+
+export default role;
diff --git a/src/seeders/20190703161356-users.js b/src/seeders/20190703161356-users.js
index 0e64d2f..94fd3bd 100644
--- a/src/seeders/20190703161356-users.js
+++ b/src/seeders/20190703161356-users.js
@@ -2,9 +2,10 @@ module.exports = { up: queryInterface => queryInterface.bulkInsert('User', [{ us
firstName: 'john',
lastName: 'Doe',
email: 'michael.nzube@andela.com',
- password: 'hashing',
+ password: 'process.env.JAWANS_ADMIN',
following: true,
bio: 'bios',
- image: 'image' }], {}),
+ image: 'image',
+ roles: ['user'] }], {}),
down: queryInterface => queryInterface.bulkDelete('User', null, {}) };
diff --git a/src/seeders/20190725062644-initUsers.js b/src/seeders/20190725062644-initUsers.js
new file mode 100644
index 0000000..87b8fbc
--- /dev/null
+++ b/src/seeders/20190725062644-initUsers.js
@@ -0,0 +1,11 @@
+module.exports = { up: queryInterface => queryInterface.bulkInsert('User', [{ username: 'john',
+ firstName: 'kagabo',
+ lastName: 'prince',
+ email: 'faustin.kagabo@andela.com',
+ password: '$2b$12$ohLYwcyFvN9o/fnRSd4G1.vcdNvt6SDJpiyTpOxiz38Y/wG4hNeza',
+ following: true,
+ bio: 'bios',
+ image: 'image',
+ roles: ['admin'] }], {}),
+
+down: queryInterface => queryInterface.bulkDelete('User', null, {}) };
diff --git a/src/test/mock/users.js b/src/test/mock/users.js
index 5973bca..9c6e19e 100644
--- a/src/test/mock/users.js
+++ b/src/test/mock/users.js
@@ -7,4 +7,7 @@ user2: { firstName: 'james',
lastName: 'bond',
email: 'jamesbond@ahjawans.com',
username: 'james_bond',
- password: 'James@12345', } };
+ password: 'James@12345', },
+userAdmin: { username: 'john',
+ password: 'Kagabo1@',
+ email: 'faustin.kagabo@andela.com' } };
diff --git a/src/test/roleTest.js b/src/test/roleTest.js
new file mode 100644
index 0000000..a779176
--- /dev/null
+++ b/src/test/roleTest.js
@@ -0,0 +1,52 @@
+import chai from 'chai';
+import chaiHttp from 'chai-http';
+import app from '../index';
+import mock from './mock/users';
+
+chai.use(chaiHttp);
+chai.should();
+let tokenGen;
+describe('role', () => {
+ it('admin signin', (done) => {
+ chai.request(app)
+ .post('/api/users/login')
+ .send(mock.userAdmin)
+ .end((req, res) => {
+ // eslint-disable-next-line prefer-destructuring
+ res.should.have.status(200);
+ res.body.data.should.have.property('token');
+ tokenGen = res.body.data.token;
+ done();
+ });
+ });
+
+ it('admin should create a role', (done) => {
+ const role = { tablesAllowed: 'Articles,User',
+ role: 'admin',
+ permissions: 'GET,DELETE' };
+ chai.request(app)
+ .post('/api/role')
+ .set('token', tokenGen)
+ .send(role)
+ .end((req, res) => {
+ res.should.have.status(201);
+ res.body.should.have.property('message');
+ done();
+ });
+ });
+
+ it('should not create a role if user is not an admin', (done) => {
+ const role = { tablesAllowed: 'Articles,User',
+ role: 'admin',
+ permissions: 'GET,DELETE' };
+ chai.request(app)
+ .post('/api/role')
+ .set('token', `${tokenGen}dgfwe`)
+ .send(role)
+ .end((req, res) => {
+ res.should.have.status(401);
+ res.body.should.have.property('error');
+ done();
+ });
+ });
+});
diff --git a/src/test/user.js b/src/test/user.js
index fd60cd2..78c375a 100644
--- a/src/test/user.js
+++ b/src/test/user.js
@@ -354,7 +354,7 @@ describe('User Profile view amend', () => {
chai.request(app)
.patch(`/api/users/${userGen}`)
.set('token', tokenGen)
- .send({ id: 16,
+ .send({ id: 18,
username: 'shaluchandwani',
firstName: 'Shalu',
lastName: 'chandwani',