const fs = require('fs') const SamlStrategy = require('passport-saml').Strategy const dbconn = require('./dbconn') const methods = require('./methods') // pwd encryption const bcrypt = require('bcryptjs') const saltRounds = 10 // forgot pwd const async = require('async') const crypto = require('crypto') const nodemailer = require('nodemailer') module.exports = function (app, config, passport) { // =========== PASSPORT ======= passport.serializeUser(function (user, done) { done(null, user); }); passport.deserializeUser(function (user, done) { done(null, user); }); var samlStrategy = new SamlStrategy({ // URL that goes from the Identity Provider -> Service Provider callbackUrl: config.passport.saml.path, // Base address to call logout requests logoutUrl: config.passport.saml.logoutUrl, entryPoint: config.passport.saml.entryPoint, issuer: config.passport.saml.issuer, identifierFormat: null, // Service Provider private key decryptionPvk: fs.readFileSync(__dirname + '/cert/key.pem', 'utf8'), // Service Provider Certificate privateCert: fs.readFileSync(__dirname + '/cert/key.pem', 'utf8'), // Identity Provider's public key cert: fs.readFileSync(__dirname + '/cert/cert_idp.pem', 'utf8'), validateInResponseTo: false, disableRequestedAuthnContext: true }, function (profile, done) { return done(null, { id: profile.nameID, idFormat: profile.nameIDFormat, email: profile.email, firstName: profile.givenName, lastName: profile.sn }); }); passport.use(samlStrategy); // ============= SAML ============== app.post(config.passport.saml.path, passport.authenticate(config.passport.strategy, { failureRedirect: '/', failureFlash: true }), function (req, res) { res.redirect('/'); } ); // to generate Service Provider's XML metadata app.get('/saml/metadata', function(req, res) { res.type('application/xml'); var spMetadata = samlStrategy.generateServiceProviderMetadata(fs.readFileSync(__dirname + '/cert/cert.pem', 'utf8')); res.status(200).send(spMetadata); } ); // ======== NODEMAILER ==================== var smtpTransport = nodemailer.createTransport({ host: config.mailer.host, secureConnection: config.mailer.secureConnection, port: config.mailer.port, auth: { user: config.mailer.authUser, pass: config.mailer.authPass }, tls: { ciphers: config.mailer.tlsCiphers } }); var mailOptions = { to: "", from: config.mailer.from, subject: "", text: "" }; // ======== APP ROUTES ==================== app.get('/', function (req, res) { res.redirect('/profile') }); app.get('/login', passport.authenticate(config.passport.strategy, { successRedirect: '/', failureRedirect: '/login' }) ); app.get('/logout', function (req, res) { if (req.user == null) { return res.redirect('/'); } req.user.nameID = req.user.id; req.user.nameIDFormat = req.user.idFormat; return samlStrategy.logout(req, function(err, uri) { req.logout(); if ( req.session ) { req.session.destroy((err) => { if(err) { return console.log(err); } }); } return res.redirect(uri); }); }); app.get('/profile', function (req, res) { if (req.isAuthenticated()) { methods.getUserByEmail(req.user.email, function(data, err){ if (!err) { res.render('profile', { user: data, email: req.user.email }); } }) } else { res.redirect('/login'); } }); app.get('/services', function (req, res) { if (req.isAuthenticated()) { async.waterfall([ // get userId by email from userdb function(done) { methods.getUserIdByEmail(req.user.email, function(userId, err) { if (!err) { done(err, userId) } }) }, // get user-project-role from userdb function(userId, done) { methods.getUserProjectRole(userId, function(userProjects, err) { if (!err) { done(err, userProjects) } }) }, // get all projects from projectdb function(userProjects, done) { methods.getAllProjects(function(projectsOverview, err) { if (!err) { done(err, userProjects, projectsOverview) } }) }, // create JSON object of projects and user status for front-end function(userProjects, projectsOverview, done) { var allProjects = [] // JSON object var userProjectId = [] // array of user's project_id for (var i = 0; i < userProjects.length; i++) { userProjectId.push(userProjects[i].project_id) } for (var i = 0; i < projectsOverview.length; i++) { // check if projectId is exist in userProjectId[] var status = "You cannot access this service" if (userProjectId.indexOf(projectsOverview[i].id) > -1) { status = "You can access this service" } // add data to JSON object allProjects.push({ id: projectsOverview[i].id, title: projectsOverview[i].title, summary: projectsOverview[i].onelinesummary, cp: projectsOverview[i].contact_email, userStatus: status }); } // render the page res.render('services', { user: req.user, project: allProjects }); } ]) } else { res.redirect('/login'); } }); app.get('/security', function (req, res) { if (req.isAuthenticated()) { console.log(req.user) res.render('security', { user: req.user // useful for view engine, useless for HTML }); } else { res.redirect('/login'); } }); app.post('/updateProfile', function (req, res) { var userData = { title: req.body.inputTitle, firstname: req.body.inputFirstname, lastname: req.body.inputLastname, email: req.body.inputEmail, organisation: req.body.inputOrganisation, industry: req.body.inputIndustry, speciality: req.body.inputSpeciality, } if (req.isAuthenticated()) { if (userData.email) { dbconn.user.query('UPDATE user SET ? WHERE email = "' +userData.email+'"', userData, function (err, rows, fields) { //if (err) throw err; if (err) { req.flash('error', "Failed"); } else { req.flash('success', 'Profile updated!'); } res.redirect('/profile'); }) } } else { res.redirect('/login'); } }); app.post('/changePwd', function (req, res) { if (req.isAuthenticated()) { var currPwd = req.body.inputCurrPwd var newPwd = req.body.inputNewPwd var retypePwd = req.body.inputConfirm methods.getUserIdByEmail(req.user.email, function(userId, err) { if (!err) { // Load hashed passwd from DB dbconn.user.query('SELECT password FROM credential WHERE user_id='+userId, function (err, rows, fields) { if (err) { res.redirect('/500') throw err } var userPwd = rows[0].password // check if the password is correct bcrypt.compare(currPwd, userPwd, function(err, isMatch) { if (err) { res.redirect('/500') throw err } else if (!isMatch) { req.flash('error', "Sorry, your password was incorrect. Please double-check your password.") res.redirect('/security') } else { if ( newPwd != retypePwd ) { req.flash('error', "Passwords do no match. Please make sure you re-type your new password correctly.") res.redirect('/security') } else { // update password bcrypt.genSalt(saltRounds, function(err, salt) { bcrypt.hash(newPwd, salt, function(err, hash) { var credentialData = { password: hash, user_id: userId } methods.updateCredential(credentialData, function(err){ if (err) { req.flash('error', "Database error: Password cannot be modified.") throw err } else { req.flash('success', "Pasword updated!") console.log('pasword updated!') } res.redirect('/security') }) }); }); } } }) }) } }) } else { res.redirect('/login'); } }); app.get('/forgotPwd', function (req, res) { res.render('forgotPwd', { user: req.user }); }); app.post('/forgotPwd', function(req, res, next) { //methods.currentDate(); var emailAddress = req.body.inputEmail; var emailContent = "Hi there,\n\n"+ "we've received a request to reset your password. However, this email address is not on our database of registered users.\n\n"+ "Thanks,\nM4_LAB Team"; var emailSubject = "Account Access Attempted"; async.waterfall([ function(done) { crypto.randomBytes(20, function(err, buf) { var token = buf.toString('hex'); done(err, token); }); }, function(token, done) { methods.checkUserEmail(emailAddress, function(err, user){ if (user) { console.log("email: user found"); emailSubject = "M4_LAB Password Reset"; emailContent = "Hi User,\n\n"+ "we've received a request to reset your password. If you didn't make the request, just ignore this email.\n\n"+ "Otherwise, you can reset your password using this link: http://" + req.headers.host + "/reset/" + token + "\n" + "This password reset is only valid for 1 hour.\n\n"+ "Thanks,\nM4_LAB Team" var credentialData = { user_id: user.id, resetPasswordToken: token, resetPasswordExpires: Date.now() + 3600000 // 1 hour } methods.updateCredential(credentialData, function(err) { done(err, token, user); }); } else { done(err, null, null); } }); }, function(token, user, done) { mailOptions.to = emailAddress; mailOptions.subject = emailSubject; mailOptions.text = emailContent; smtpTransport.sendMail(mailOptions, function(err) { done(err, 'done'); }); } ], function(err) { if (err) { req.flash('error', 'An error occured. Please try again.'); } else { req.flash('success', 'An e-mail has been sent to ' + emailAddress + ' with further instructions.'); } res.redirect('/forgotPwd'); }); }); app.get('/reset/:token', function(req, res) { methods.checkUserToken(req.params.token, function(err, user){ //console.log(user); if (!user) { req.flash('error', 'Password reset token is invalid or has expired.'); res.redirect('/forgotPwd'); } else { res.render('reset'); } }); }); app.post('/reset/:token', function(req, res) { methods.checkUserToken(req.params.token, function(err, user){ if (user) { // encrypt password bcrypt.genSalt(saltRounds, function(err, salt) { bcrypt.hash(req.body.inputNewPwd, salt, function(err, hash) { var credentialData = { password: hash, user_id: user.user_id } // update password methods.updateCredential(credentialData, function(err){ if (err) { req.flash('error', "Database error: Password cannot be modified.") throw err } else { req.flash('success', "Your pasword has been updated.") console.log('pasword updated!') res.redirect('/login') // todo: send confirmation email } }) }); }); } else { req.flash('error', "User not found.") res.redirect('/login') } }); //res.redirect('/login') }); // todo: user registration with captcha app.get('/registration', function(req, res) { res.render('registration') }) app.post('/registration', function(req, res) { // TODO: // create gitlab account? // send email to activate profile? // user data var curDate = new Date() var userData = { title: req.body.inputTitle, firstname: req.body.inputFirstname, lastname: req.body.inputLastname, email: req.body.inputEmail, organisation: req.body.inputOrganisation, industry: req.body.inputIndustry, speciality: req.body.inputSpeciality, createdDate: curDate.toISOString().slice(0,10) } // encrypt password bcrypt.genSalt(saltRounds, function(err, salt) { bcrypt.hash(req.body.inputPassword, salt, function(err, hash) { // create account var newAccount = { profile: userData, password: hash } methods.registerNewUser(newAccount, function(err){ if (err) { req.flash('error', "Failed"); } else { req.flash('success', 'Your account has been created. Please log in.'); } res.redirect('/registration'); }) }); }); }) app.get('/email/:email', function(req, res) { methods.checkUserEmail(req.params.email, function(err, user){ if (!err) { if (user) { res.send(false) } else { res.send(true) } } }) }) };