Skip to content

Commit

Permalink
Merge 53583b0 into 4cfb42d
Browse files Browse the repository at this point in the history
  • Loading branch information
Nziranziza committed Mar 14, 2019
2 parents 4cfb42d + 53583b0 commit 8932198
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 3 deletions.
162 changes: 162 additions & 0 deletions __tests__/routes/accessControl.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import request from 'supertest';
import bcrypt from 'bcrypt';
import app from '../../app';
import { User } from '../../database/models';
import { urlPrefix } from '../mocks/variables.json';
import { signupUser, signupUser2 } from '../mocks/db.json';

let superAdmin;
let user1;
let user2;
let admin;

describe('RBAC', () => {
beforeAll(async () => {
const encryptedPassword = bcrypt.hashSync('123456', 10);
await User.create({
username: 'superAdmin',
userType: 'super-admin',
email: 'superadmin@author.haven',
password: encryptedPassword,
confirmed: 'confirmed'
});
await User.create({
...signupUser,
confirmed: 'confirmed',
password: encryptedPassword
});
await User.create({
...signupUser2,
confirmed: 'confirmed',
password: encryptedPassword
});
await User.create({
username: 'admin',
userType: 'admin',
email: 'admin@author.haven',
password: encryptedPassword,
confirmed: 'confirmed'
});
const res1 = await request(app)
.post(`${urlPrefix}/users/login`)
.send({
user: {
username: 'superadmin@author.haven',
password: '123456'
}
});
superAdmin = res1.body.user;
const res2 = await request(app)
.post(`${urlPrefix}/users/login`)
.send({
user: {
username: signupUser.email,
password: '123456'
}
});
user1 = res2.body.user;
const res3 = await request(app)
.post(`${urlPrefix}/users/login`)
.send({
user: {
username: signupUser2.email,
password: '123456'
}
});
user2 = res3.body.user;
const res4 = await request(app)
.post(`${urlPrefix}/users/login`)
.send({
user: {
username: 'admin@author.haven',
password: '123456'
}
});
admin = res4.body.user;
});

afterAll(async () => {
await User.destroy({ where: { id: superAdmin.id } });
await User.destroy({ where: { id: user1.id } });
await User.destroy({ where: { id: user2.id } });
await User.destroy({ where: { id: admin.id } });
});

test('should not grant access if user not admin', async () => {
const res = await request(app)
.put(`${urlPrefix}/users/${user1.username}/grant`)
.set('authorization', user1.token)
.send({ role: 'admin' });

expect(res.status).toBe(401);
expect(res.body.status).toBe(401);
expect(res.body.message).toBe('Not authorized');
});

test('should not grant access with invalid input', async () => {
const res = await request(app)
.put(`${urlPrefix}/users/${user1.username}/grant`)
.set('authorization', superAdmin.token)
.send({ role: 'dhfjs' });

expect(res.status).toBe(400);
expect(res.body.message).toBe('Bad Request');
});

test('should grant access', async () => {
const res = await request(app)
.put(`${urlPrefix}/users/${user1.username}/grant`)
.set('authorization', superAdmin.token)
.send({ role: 'admin' });

expect(res.status).toBe(200);
expect(res.body.status).toBe(200);
expect(res.body.message).toBe('admin role granted');
expect(res.body.user.userType).toBe('admin');
});

test('should inform a user in case role is already granted', async () => {
const res = await request(app)
.put(`${urlPrefix}/users/${user1.username}/grant`)
.set('authorization', superAdmin.token)
.send({ role: 'admin' });

expect(res.status).toBe(409);
expect(res.body.status).toBe(409);
expect(res.body.message).toBe('already an admin');
});

test('should inform a user in case role is already granted', async () => {
const res = await request(app)
.put(`${urlPrefix}/users/${user2.username}/grant`)
.set('authorization', superAdmin.token)
.send({ role: 'user' });

expect(res.status).toBe(409);
expect(res.body.status).toBe(409);
expect(res.body.message).toBe('already a user');
});

test('should not grant access if user does not exist', async () => {
const fakeName = 'rtvdr';
const res = await request(app)
.put(`${urlPrefix}/users/${fakeName}/grant`)
.set('authorization', superAdmin.token)
.send({ role: 'admin' });

expect(res.status).toBe(404);
expect(res.body.status).toBe(404);
expect(res.body.message).toBe('User not found');
});

test('should not grant super-admin when you are an admin', async () => {
const res = await request(app)
.put(`${urlPrefix}/users/${user2.username}/grant`)
.set('authorization', admin.token)
.send({ role: 'super-admin' });

expect(res.status).toBe(401);
expect(res.body.status).toBe(401);
expect(res.body.message).toBe('Not authorized');
});
});
46 changes: 46 additions & 0 deletions controllers/GrantRoleController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { User } from '../database/models';

/**
* @description a class to grant roles to users
*/
class grantRoleController {
/**
* @author Daniel
* @param {*} req
* @param {*} res
* @returns {*} object
*/
static async assignRole(req, res) {
const { username } = req.params;
const { currentUser } = req;
const { role } = req.body;
if (currentUser.userType === 'admin' && role === 'super-admin') {
return res.status(401).send({
status: 401,
message: 'Not authorized'
});
}
const user = await User.findOne({ where: { username } });
if (!user) {
return res.status(404).send({
status: 404,
message: 'User not found'
});
}
if (user.userType === role) {
return res.status(409).send({
status: 409,
message: `already ${role === 'admin' ? 'an' : 'a'} ${role}`
});
}
user.update({ userType: role });
const { password, confirmationCode, ...userData } = user.get();
return res.status(200).send({
status: 200,
message: `${role} role granted`,
user: userData
});
}
}

export default grantRoleController;
2 changes: 2 additions & 0 deletions controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import FollowController from './FollowController';
import CommentController from './CommentController';
import RatingController from './RatingController';
import FavoriteCommentController from './FavoriteCommentController';
import GrantRoleController from './GrantRoleController';

export {
AuthController,
Expand All @@ -18,4 +19,5 @@ export {
CommentController,
RatingController,
FavoriteCommentController,
GrantRoleController
};
20 changes: 19 additions & 1 deletion middlewares/verifyJwt.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import passport from 'passport';

const verifyJwt = ({ tokenRequired = true, confirmEmail = false } = {}) => (req, res, next) => {
const verifyJwt = ({ tokenRequired = true, confirmEmail = false, access = ['admin', 'user'] } = {}) => (req, res, next) => {
passport.authenticate('jwt', (err, user, info) => {
if (err) {
return res.status(520).send({ errors: { body: [err.message] } });
Expand All @@ -19,6 +19,24 @@ const verifyJwt = ({ tokenRequired = true, confirmEmail = false } = {}) => (req,
}
req.currentUser = user;
}
if (access.length === 1 && access[0] === 'admin' && user.userType === 'user') {
return res.status(401).send({
status: 401,
message: 'Not authorized'
});
}
if (access.length === 1 && access[0] === 'user' && user.userType === 'admin') {
return res.status(401).send({
status: 401,
message: 'Not authorized'
});
}
if (access.length === 1 && access[0] === 'super-admin' && user.userType !== 'super-admin') {
return res.status(401).send({
status: 401,
message: 'Not authorized'
});
}

return next();
})(req, res, next);
Expand Down
16 changes: 14 additions & 2 deletions routes/api/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ import { celebrate } from 'celebrate';
import passport from 'passport';
import dotenv from 'dotenv';
import { User } from '../../database/models';
import { authValidator } from '../validators';
import { AuthController, UserController, FollowController } from '../../controllers';
import { authValidator, roleValidator } from '../validators';
import {
AuthController,
UserController,
FollowController,
GrantRoleController
} from '../../controllers';
import { verifyJwt } from '../../middlewares';
import { asyncHandler } from '../../helpers';

Expand Down Expand Up @@ -77,4 +82,11 @@ router.post('/:username/follow', verifyJwt(), asyncHandler(FollowController.foll

router.delete('/:username/unfollow', verifyJwt(), asyncHandler(FollowController.unfollow));

router.put(
'/:username/grant',
verifyJwt({ access: ['admin'] }),
celebrate({ body: roleValidator }),
asyncHandler(GrantRoleController.assignRole)
);

export default router;
2 changes: 2 additions & 0 deletions routes/validators/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import profileValidator from './user';
import commentValidator from './comment';
import ratingValidator from './rating';
import profilesValidator from './profile';
import roleValidator from './role';

export {
authValidator,
Expand All @@ -12,4 +13,5 @@ export {
commentValidator,
ratingValidator,
profilesValidator,
roleValidator
};
8 changes: 8 additions & 0 deletions routes/validators/role.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Joi } from 'celebrate';

const role = Joi.object().keys({
role: Joi.string()
.valid('admin', 'super-admin', 'user')
.required()
});
export default role;

0 comments on commit 8932198

Please sign in to comment.