Skip to content

Commit

Permalink
BE #1 & BE #2 Move Registration and Login code to express generated p…
Browse files Browse the repository at this point in the history
…roject
  • Loading branch information
Satyajit Dey committed May 14, 2020
1 parent 49d2e88 commit 50028e2
Show file tree
Hide file tree
Showing 16 changed files with 3,250 additions and 197 deletions.
51 changes: 26 additions & 25 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
var debug = require('debug')('cms-backend-api:*');
var express = require('express');
var logger = require('morgan');
var mongoose=require('mongoose');
const express = require('express');
const logger = require('morgan');
const bodyParser = require('body-parser');
const UsersRouter = require('./routes/users.route');
const AuthRouter = require('./routes/auth.route');

//custom modules
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

//connect to database
var connect = mongoose.connect(process.env.DB_URL,{
autoIndex: false,
useNewUrlParser: true,
useUnifiedTopology: true
});

connect.then((db) => {
debug('Connected to database ' + process.env.DB_URL);
}, (err) => { console.log(err); });



var app = express();
const app = express();

//middleware
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

//routes
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Allow-Methods', 'GET,HEAD,PUT,PATCH,POST,DELETE');
res.header('Access-Control-Expose-Headers', 'Content-Length');
res.header('Access-Control-Allow-Headers', 'Accept, Authorization, Content-Type, X-Requested-With, Range');
if (req.method === 'OPTIONS') {
return res.send(200);
} else {
return next();
}
});

app.use(bodyParser.json());
UsersRouter.routesConfig(app);
AuthRouter.routesConfig(app);

app.get(process.env.API_ENPOINT_BASE, (req, res) => {
res.send(`CMS API v1.`)
});

module.exports = app;
20 changes: 10 additions & 10 deletions bin/www
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@
const dotenv = require('dotenv');
dotenv.config();

var app = require('../app');
var debug = require('debug')('cms-backend-api:*');
var http = require('http');
const app = require('../app');
const debug = require('debug')('cms-backend-api:*');
const http = require('http');

/**
* Get port from environment and store in Express.
*/

var port = normalizePort(process.env.PORT || '3000');
const port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
* Create HTTP server.
*/

var server = http.createServer(app);
const server = http.createServer(app);

/**
* Listen on provided port, on all network interfaces.
Expand All @@ -36,7 +36,7 @@ server.on('listening', onListening);
*/

function normalizePort(val) {
var port = parseInt(val, 10);
let port = parseInt(val, 10);

if (isNaN(port)) {
// named pipe
Expand All @@ -60,7 +60,7 @@ function onError(error) {
throw error;
}

var bind = typeof port === 'string'
let bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;

Expand All @@ -84,9 +84,9 @@ function onError(error) {
*/

function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
let addr = server.address();
let bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
debug(`CMS Engine started and listening port: ${bind}`);
}
21 changes: 21 additions & 0 deletions controllers/auth.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const jwt = require('jsonwebtoken');
const crypto = require('crypto');

exports.login = (req, res) => {
try {
let refreshId = req.body.userId + process.env.AUTH_SECRET;
let salt = crypto.randomBytes(16).toString('base64');
let hash = crypto.createHmac('sha512', salt).update(refreshId).digest("base64");
req.body.refreshKey = salt;
let token = jwt.sign(req.body, process.env.AUTH_SECRET);
let b = new Buffer(hash);
let refresh_token = b.toString('base64');
res.status(201).send({accessToken: token, refreshToken: refresh_token});
} catch (err) {
res.status(500).send({errors: err});
}
};

exports.logout = (req, res) => {
res.status(204).send({accessToken: null, refreshToken: null});
};
56 changes: 56 additions & 0 deletions controllers/users.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const UserModel = require('../models/users.model');
const crypto = require('crypto');

exports.insert = (req, res) => {
let salt = crypto.randomBytes(16).toString('base64');
let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("base64");
req.body.password = salt + "$" + hash;
UserModel.createUser(req.body)
.then((result) => {
res.status(201).send({id: result._id});
});
};

exports.list = (req, res) => {
let limit = req.query.limit && req.query.limit <= 100 ? parseInt(req.query.limit) : 10;
let page = 0;
if (req.query) {
if (req.query.page) {
req.query.page = parseInt(req.query.page);
page = Number.isInteger(req.query.page) ? req.query.page : 0;
}
}

UserModel.list(limit, page)
.then((result) => {
res.status(200).send(result);
})
};

exports.getById = (req, res) => {
UserModel.findById(req.params.userId)
.then((result) => {
res.status(200).send(result);
}).catch(err => res.status(400).send({'message': err.message}));
};

exports.patchById = (req, res) => {
if (req.body.password) {
let salt = crypto.randomBytes(16).toString('base64');
let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("base64");
req.body.password = salt + "$" + hash;
}

UserModel.patchUser(req.params.userId, req.body)
.then((result) => {
res.status(204).send({});
});

};

exports.removeById = (req, res) => {
UserModel.removeById(req.params.userId)
.then((result) => {
res.status(204).send({});
});
};
36 changes: 36 additions & 0 deletions middlewares/auth.permission.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
exports.minimumPermissionLevelRequired = (required_permission_level) => {
return (req, res, next) => {
let user_permission_level = parseInt(req.jwt.permissionLevel);
if (user_permission_level & required_permission_level) {
return next();
} else {
return res.status(403).send();
}
};
};

exports.onlySameUserOrAdminCanDoThisAction = (req, res, next) => {

let user_permission_level = parseInt(req.jwt.permissionLevel);
let userId = req.jwt.userId;
if (req.params && req.params.userId && userId === req.params.userId) {
return next();
} else {
if (user_permission_level & process.env.AUTH_PERMISSION_ADMIN) {
return next();
} else {
return res.status(403).send();
}
}

};

exports.sameUserCantDoThisAction = (req, res, next) => {
let userId = req.jwt.userId;

if (req.params.userId !== userId) {
return next();
} else {
return res.status(400).send();
}
};
59 changes: 59 additions & 0 deletions middlewares/auth.validation.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const {check} = require('express-validator');
const crypto = require('crypto');
const jwt = require('jsonwebtoken');
const UserModel = require('../models/users.model');

exports.authFieldValidationRules = () => {
return [
// email should not be empty
check('email', 'email empty').notEmpty(),
// email must be valid
check('email', 'email is not valid').isEmail().normalizeEmail(),
// password should not be empty
check('password', 'password empty').notEmpty()
]
};

exports.matchEmailAndPassword = (req, res, next) => {
const {email, password} = req.body;
UserModel.findByEmail(email).then(user => {
if (!user) {
return res.status(404).send({errors: `User with email:<${email}> doesn't exist`});
} else {
let passwordFields = user.password.split('$');
let salt = passwordFields[0];
let hash = crypto.createHmac('sha512', salt).update(password).digest("base64");
if (hash === passwordFields[1]) {
req.body = {
userId: user._id,
email: user.email,
permissionLevel: user.permissionLevel,
provider: 'email',
name: `${user.firstName} ${user.lastName}`,
};
return next();
} else {
return res.status(400).send({errors: 'Invalid e-mail or password'});
}
}
});
};

exports.verifyJwtToken = (req, res, next) => {
if (req.headers['authorization']) {
try {
let authorization = req.headers['authorization'].split(' ');
if (authorization[0] !== 'Bearer') {
return res.status(401).send();
} else {
req.jwt = jwt.verify(authorization[1], process.env.AUTH_SECRET);
return next();
}

} catch (err) {
return res.status(403).send();
}
} else {
return res.status(401).send();
}
};
15 changes: 15 additions & 0 deletions middlewares/field.validation.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const {validationResult} = require('express-validator');

exports.validateRules = (req, res, next) => {
const errors = validationResult(req);
if (errors.isEmpty()) {
return next();
}

const extractedErrors = [];
errors.array().map(err => extractedErrors.push({[err.param]: err.msg}));

return res.status(400).json({
errors: extractedErrors,
});
};
53 changes: 53 additions & 0 deletions middlewares/user.validation.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const mongoose = require('../services/db.service').mongoose;
const UserModel = require('../models/users.model');

const {check} = require('express-validator');

exports.registrationFieldValidationRules = () => {
return [
// firstName should not be empty
check('firstName', 'firstName empty').notEmpty(),
// lastName should not be empty
check('lastName', 'lastName empty').notEmpty(),
// email should not be empty
check('email', 'email empty').notEmpty(),
// email must be valid
check('email', 'email is not valid').isEmail().normalizeEmail(),
// password should not be empty
check('password', 'password empty').notEmpty(),
// password must be at least 8 chars long
check('password', 'password must be at least 8 chars long').isLength({min: 8})
]
};

exports.isEmailAlreadyExists = (req, res, next) => {
UserModel.findByEmail(req.body.email)
.then((result) => {
if (result) {
return res.status(409).json({message: 'Email already in use.'});
} else {
return next();
}
}).catch(err => res.status(500).json({errors: err}));
};

exports.verifyUserId = (req, res, next) => {
if (req.params.userId && mongoose.Types.ObjectId.isValid(req.params.userId)) {
return next();
}

return res.sendStatus(404);
};

exports.updatePasswordValidationRules = () => {
return [
check('email', 'email empty').notEmpty(),
// email must be valid
check('email', 'email is not valid').isEmail().normalizeEmail(),
// password should not be empty
check('password', 'password empty').notEmpty(),
// password must be at least 8 chars long
check('password', 'password must be at least 8 chars long').isLength({min: 8})
]
};

Loading

0 comments on commit 50028e2

Please sign in to comment.