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; const salt = 64; // salt length // forgot pwd const async = require('async') const crypto = require('crypto') const mailer = require('./mailer') module.exports = function (app, config, passport, i18n) { // =========== 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: '/account/', failureFlash: true }), function (req, res) { res.redirect('/account/'); } ); // 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); } ); // ================ test i18n ================== i18n.setLocale('de'); app.get('/de', function(req, res) { var greeting = i18n.__('Hello World') res.send(greeting) }); var lang = 'DE' // ======== APP ROUTES - ACCOUNT ==================== var updatePasswordMailSubject = "Ihr Passwort für das Transferportal wurde gespeichert." var mailSignature = "Mit den besten Grüßen,\ndas Transferportal-Team der HFT Stuttgart\n\n"+ "Transferportal der Hochschule für Technik Stuttgart\n"+ "Schellingstr. 24\n"+ "70174 Stuttgart\n"+ "m4lab@hft-stuttgart.de\n"+ "https://transfer.hft-stuttgart.de" var updatePasswordMailContent = "Lieber Nutzer,\n\n"+"Ihr Passwort wurde erfolgreich geändert.\n\n"+mailSignature app.get('/', function (req, res) { if (req.isAuthenticated()) { methods.getUserByEmail(req.user.email, function(data, err){ if (!err) { res.render(lang+'/account/home', { user: data }); } }) } else { res.redirect('/login'); // localhost } }); 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(lang+'/account/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 = false if (userProjectId.indexOf(projectsOverview[i].id) > -1) { status = true } // 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(lang+'/account/services', { user: req.user, project: allProjects }); } ]) } else { res.redirect('/login'); } }); app.get('/security', function (req, res) { if (req.isAuthenticated()) { res.render(lang+'/account/security', { user: req.user // useful for view engine, useless for HTML }); } else { res.redirect('/login'); } }); app.post('/updateProfile', function (req, res) { var userData = { salutation: req.body.inputSalutation, 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!'); req.flash('success', 'Ihr Benutzerprofil wurde aktualisiert!'); } 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) { console.error(err) res.status(500).render(lang+'/500', { error: err }) } var userPwd = rows[0].password // check if the password is correct bcrypt.compare(currPwd, userPwd, function(err, isMatch) { if (err) { console.error(err) res.status(500).render(lang+'/500', { error: err }) } else if (!isMatch) { //req.flash('error', "Sorry, your password was incorrect. Please double-check your password.") req.flash('error', "Das Passwort ist leider falsch. Bitte überprüfen Sie Ihre Eingabe.") //res.redirect('/security') res.redirect('/security') } else { if ( newPwd != retypePwd ) { //req.flash('error', "Passwords do no match. Please make sure you re-type your new password correctly.") req.flash('error', 'Passwörter stimmen nicht überein. Bitte stellen Sie sicher, dass Sie das Passwort beide Male genau gleich eingeben.') 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.") req.flash('error', "Datenbankfehler: Passwort kann nicht geändert werden.") throw err } else { //req.flash('success', "Pasword updated!") req.flash('success', "Passwort aktualisiert!") mailer.options.to = req.user.email //mailOptions.subject = "Your M4_LAB Password has been updated." mailer.options.subject = updatePasswordMailSubject mailer.options.text = updatePasswordMailContent mailer.transport.sendMail(mailer.options, function(err) { if (err) { console.log(err) } }); } res.redirect('/security') }) }); }); } } }) }) } }) } else { res.redirect('/login'); } }); app.get('/forgotPwd', function (req, res) { res.render(lang+'/account/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"); //var emailSubject = "M4_LAB Password Reset"; var emailSubject = "Ihre Passwort-Anfrage an das Transferportal der HFT Stuttgart"; /* var 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://m4lab.hft-stuttgart.de/account/reset/" + token + "\n" + "This password reset is only valid for 1 hour.\n\n"+ "Thanks,\nM4_LAB Team" */ var emailContent = "Lieber Nutzer,\n\n"+ "wir haben Ihre Anfrage zur Erneuerung Ihres Passwortes erhalten. Falls Sie diese Anfrage nicht gesendet haben, ignorieren Sie bitte diese E-Mail.\n\n"+ "Sie können Ihr Passwort mit dem Klick auf diesen Link ändern: http://m4lab.hft-stuttgart.de/account/reset/" + token + "\n" + // test server //"Sie können Ihr Passwort mit dem Klick auf diesen Link ändern: http://localhost:9989/reset/" + token + "\n" + // localhost "Dieser Link ist aus Sicherheitsgründen nur für 1 Stunde gültig.\n\n"+mailSignature var credentialData = { user_id: user.id, resetPasswordToken: token, resetPasswordExpires: Date.now() + 3600000 // 1 hour } methods.updateCredential(credentialData, function(err) { done(err, token, user); }); // send email mailer.options.to = emailAddress; mailer.options.subject = emailSubject; mailer.options.text = emailContent; mailer.transport.sendMail(mailer.options, function(err) { done(err, 'done'); }); } else { //done(err, null, null); done(err, 'no user found'); } }); } ], function(err) { if (err) { //req.flash('error', 'An error occured. Please try again.'); req.flash('error', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.'); } else { //req.flash('success', 'If your email is registered, an e-mail has been sent to ' + emailAddress + ' with further instructions.'); req.flash('success', 'Wenn Ihre E-Mail-Adresse registriert ist, wurde eine E-Mail mit dem weiteren Vorgehen an ' + emailAddress + ' versendet.'); } //res.redirect('/forgotPwd'); // deployment res.redirect('/forgotPwd'); // localhost }); }); app.get('/reset/:token', function(req, res) { methods.getUserByToken(req.params.token, function(err, user){ if (!user) { //req.flash('error', 'Password reset token is invalid or has expired.'); req.flash('error', 'Der Schlüssel zum zurücksetzen des Passworts ist ungültig oder abgelaufen.'); //res.redirect('/forgotPwd'); // deployment res.redirect('/forgotPwd'); // localhost } else { res.render(lang+'/account/reset'); } }); }); app.post('/reset/:token', function(req, res) { var newPwd = req.body.inputNewPwd methods.getUserByToken(req.params.token, function(err, user){ if (user) { // encrypt password bcrypt.genSalt(saltRounds, function(err, salt) { bcrypt.hash(newPwd, 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.") req.flash('error', "Datenbankfehler: Passwort kann nicht geändert werden.") throw err } else { //req.flash('success', "Your pasword has been updated.") req.flash('success', "Passwort aktualisiert!") // send notifiaction email mailer.options.to = user.email mailer.options.subject = updatePasswordMailSubject mailer.options.text = updatePasswordMailContent mailer.transport.sendMail(mailer.options, function(err) { if (err) { console.log(err) } }); // redirect to login page res.redirect('/login') } }) }); }); } else { req.flash('error', "User not found.") res.redirect('/login') } }); }); // todo: user registration with captcha app.get('/registration', function(req, res) { res.render(lang+'/account/registration') }) app.post('/registration', function(req, res) { // TODO: // create gitlab account? // send email to activate profile? // user data var curDate = new Date() var userData = { salutation: req.body.inputSalutation, 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") req.flash('error', "Fehlgeschlagen") } else { //req.flash('success', 'Your account has been created. Please log in.') req.flash('success', 'Ihr Benutzerkonto wurde angelegt. Bitte melden Sie sich an.') } 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) } } }) }) app.get('/contact', function (req, res) { res.render(lang+'/account/contact', { user: req.user }); }); app.post('/contact', function(req, res, next) { //methods.currentDate(); let emailAddress = req.body.inputEmail; let supportAddress = "support-transfer@hft-stuttgart.de"; let inputName = req.body.name; let inputContent = req.body.message; let emailContent = "Es wurde eine Anfrage an das Transferportal gestellt: \n\n NAME: " + inputName + "\n NACHRICHT:\n "+ inputContent; let emailSubject = "Ihre Anfrage an das Transferportal"; async.waterfall([ function(done) { // send email mailer.options.to = supportAddress; mailer.options.cc = emailAddress; mailer.options.subject = emailSubject; mailer.options.text = emailContent; mailer.transport.sendMail(mailer.options, function(err) { done(err, 'done'); }); } ], function(err) { if (err) { req.flash('error', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.'); } else { req.flash('success', 'Vielen Dank für Ihre Anfrage. Wir melden uns baldmöglichst bei Ihnen. Eine Kopie Ihrer Anfrage wurde an ' + emailAddress + ' versandt.'); } //res.redirect('/forgotPwd'); // deployment res.redirect('/account/contact'); // localhost }); }); };