Skip to content

Commit

Permalink
feat: added jwt authorization with new role system based on numeric v…
Browse files Browse the repository at this point in the history
…alues

Signed-off-by: Varij Kapil <[email protected]>
  • Loading branch information
varijkapil13 committed Jan 21, 2019
1 parent 7e116e6 commit 79b24f9
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 11 deletions.
1 change: 0 additions & 1 deletion node_modules/rxjs/_esm2015/internal/util/identity.js.map

This file was deleted.

5 changes: 4 additions & 1 deletion server/auth/authorization.controller.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import * as jwt from 'jsonwebtoken';
import crypto from 'crypto';
const jwtSecret = 'aJWTSecret13121992for13121992jwtTokenInTheRequest13121992';
import configuration from '../common/env.config';

const {jwtSecret} = configuration;
// created using the following tutorial : https://www.toptal.com/nodejs/secure-rest-api-in-nodejs
class AuthorizationController {
static login(req, res) {
try {
Expand Down
26 changes: 26 additions & 0 deletions server/auth/authorization.validation.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as jwt from 'jsonwebtoken';
import configuration from '../common/env.config';

const {jwtSecret} = configuration;

// created using the following tutorial : https://www.toptal.com/nodejs/secure-rest-api-in-nodejs

export default class AuthorizationValidationController {
static validJWTNeeded(req, res, next) {
if (req.headers['authorization']) {
try {
const authorization = req.headers['authorization'].split(' ');
if (authorization[0] !== 'Bearer') {
return res.status(401).send();
} else {
req.jwt = jwt.verify(authorization[1], jwtSecret);
return next();
}
} catch (err) {
return res.status(403).send();
}
} else {
return res.status(401).send();
}
}
}
23 changes: 23 additions & 0 deletions server/auth/permission.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// https://www.toptal.com/nodejs/secure-rest-api-in-nodejs

/*We can use the bitwise AND operator (bitmasking) to control the permissions.
If we set each required permission as a power of 2, we can treat each bit of the
32bit integer as a single permission.
An admin can then have all permissions by setting their permission value to 2147483647.
That user could then have access to any route.
As another example, a user whose permission value was set to 7 would have permissions to
the roles marked with bits for values 1, 2, and 4 (two to the power of 0, 1, and 2).*/

export default class PermissionController {
static minimumPermissionRequired(requiredPermissionLevel) {
return (req, res, next) => {
const userPermissionLevel = parseInt(req.jwt.roles);
if (userPermissionLevel & requiredPermissionLevel) {
return next();
} else {
return res.status(403).send();
}
};
}
}
11 changes: 11 additions & 0 deletions server/common/env.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const configuration = {
jwtSecret: '',
jwtExpirationInSeconds: 36000,
permissions: {
USER: 1,
MANAGER: 4,
ADMIN: 2048
}
};

export default configuration;
35 changes: 35 additions & 0 deletions server/common/role.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import config from './env.config';

export default class RoleController {
static convertToInteger(roleName) {
if (this.__isAdmin(roleName)) {
return config.permissions.ADMIN;
}
if (this.__isManager(roleName)) {
return config.permissions.MANAGER;
}

return config.permissions.USER;
}

static __isAdmin(role) {
const lowerCaseRole = role.toLowerCase();
return lowerCaseRole === 'admin' || lowerCaseRole === 'administrator' || lowerCaseRole === 'superuser';
}

static __isManager(role) {
const lowerCaseRole = role.toLowerCase();
return (
lowerCaseRole === 'manager' ||
lowerCaseRole === 'manage' ||
lowerCaseRole === 'hr' ||
lowerCaseRole === 'human resource' ||
lowerCaseRole === 'resource' ||
lowerCaseRole === 'ceo' ||
lowerCaseRole === 'management' ||
lowerCaseRole === 'cto' ||
lowerCaseRole === 'coo' ||
lowerCaseRole === 'finance'
);
}
}
13 changes: 7 additions & 6 deletions server/controllers/user.controller.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import model from '../models';
import crypto from 'crypto';
import RoleController from '../common/role.controller';

const {User, Avatar} = model;

class UserController {
static signUpWithAvatar(req, res) {
const {email, password, roles} = req.body;
const {password, roles} = req.body;
// hash the password before saving in the database
const salt = crypto.randomBytes(16).toString('base64');
const hash = crypto
Expand All @@ -17,19 +18,19 @@ class UserController {
const avatarId = req.params.avatarId;
User.findAll({
where: {avatarId},
attributes: {include: ['id', 'email', 'avatarId', 'createdAt']}
attributes: {include: ['id', 'email', 'avatarId', 'createdAt'], exclude: ['password']}
}).then(user => {
if (user) {
if (user.length > 0) {
return res.status(400).send({message: 'User with avatar id ' + avatarId + ' already exists', user});
} else {
Avatar.findByPk(avatarId)
.then(avatar => {
if (avatar) {
return User.create({
email,
email: avatar.email,
password: hashedPassword,
avatarId,
roles
roles: RoleController.convertToInteger(roles)
})
.then(userData =>
res.status(201).send({
Expand Down Expand Up @@ -71,7 +72,7 @@ class UserController {
email,
password: hashedPassword,
avatarId: avatar.id,
roles
roles: RoleController.convertToInteger(roles)
})
.then(userData =>
res.status(201).send({
Expand Down
1 change: 0 additions & 1 deletion server/routes/user.route.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import express from 'express';
import UserController from '../controllers/user.controller';
import AuthorizationController from '../auth/authorization.controller';

const routes = express.Router();

// sign ip with avatar id
Expand Down
19 changes: 17 additions & 2 deletions server/routes/workday.route.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
import express from 'express';
import multer from 'multer';
import WorkdayController from '../controllers/workday.controller';
import PermissionController from '../auth/permission.controller';
import AuthorizationValidationController from '../auth/authorization.validation.controller';
import configuration from '../common/env.config';

const ADMIN = configuration.permissions.ADMIN;
const MANAGER = configuration.permissions.MANAGER;
const USER = configuration.permissions.USER;

const routes = express.Router();
const upload = multer({storage: multer.memoryStorage()});

routes.post('/:avatarId/upload', upload.single('reportFile'), WorkdayController.importTimelyFile);
routes.post('/:avatarId', WorkdayController.addWorkday);
routes.post('/:avatarId/upload', upload.single('reportFile'), [
AuthorizationValidationController.validJWTNeeded,
PermissionController.minimumPermissionRequired(MANAGER),
WorkdayController.importTimelyFile
]);
routes.post('/:avatarId', [
AuthorizationValidationController.validJWTNeeded,
PermissionController.minimumPermissionRequired(MANAGER),
WorkdayController.addWorkday
]);

export default routes;

0 comments on commit 79b24f9

Please sign in to comment.