Commit 69ab56ca authored by Rosanny Sihombing's avatar Rosanny Sihombing
Browse files

Initial commit

parents
Pipeline #311 failed with stages
in 28 seconds
const dbconn = require('./dbconn');
var methods = {
// test method
currentDate: function() {
console.log('Current Date is: ' + new Date().toISOString().slice(0, 10));
},
// ======================= user db =======================
checkUserEmail: function(email, callback) {
var user;
dbconn.user.query('SELECT email FROM user WHERE email = "' +email+'"', function (err, rows, fields) {
if (err) {
throw err;
}
else {
if ( rows.length > 0) {
user = rows[0];
}
}
callback(err, user);
});
},
checkUserToken: function(token, callback) {
var user;
dbconn.user.query('SELECT email FROM user WHERE resetPasswordToken = "'+token+'" and resetPasswordExpires > '+Date.now(), function (err, rows, fields) {
if (err) {
throw err;
}
else {
if ( rows.length > 0) {
user = rows[0];
}
}
callback(err, user);
});
},
updateUser: function(userData, callback) {
dbconn.user.query('UPDATE user SET ? WHERE email = "' +userData.email+'"', userData, function (err, rows, fields) {
if (err) throw err;
callback(err);
})
},
updatePassword: function(hash, email, callback) {
dbconn.user.query('UPDATE user SET password = "'+hash+'" WHERE email = "' +email+'"', function (err, rows, fields) {
if (err) throw err;
callback(err);
})
},
getUserIdByEmail: function(email, callback) {
var userId
dbconn.user.query('SELECT id FROM user WHERE email = "' +email+'"', function (err, rows, fields) {
if (err) {
throw err;
}
else {
if ( rows.length > 0) {
userId = rows[0].id;
}
}
callback(userId, err);
});
},
getUserProjectRole: function(userId, callback) {
dbconn.user.query('SELECT project_id, role_id FROM user_project_role WHERE user_id = "' +userId+'"', function (err, rows, fields) {
if (err) throw err;
callback(rows, err);
});
},
// ======================= project db =======================
getAllProjects: function(callback) {
dbconn.project.query('CALL getAllprojects', function (err, rows, fields){
if (err) throw err;
callback(rows[0], err);
})
}
};
module.exports = methods;
\ No newline at end of file
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);
// ============================
/*
app.all('/', function(req, res){
req.flash('test', 'it worked');
res.redirect('/test')
});
app.all('/test', function(req, res){
res.send(JSON.stringify(req.flash('test')));
});
*/
app.get('/', function (req, res) {
res.redirect('/profile')
});
app.get('/login',
passport.authenticate(config.passport.strategy,
{
successRedirect: '/',
failureRedirect: '/login'
})
);
app.post(config.passport.saml.path,
passport.authenticate(config.passport.strategy,
{
failureRedirect: '/',
failureFlash: true
}),
function (req, res) {
res.redirect('/');
}
);
app.get('/profile', function (req, res) {
if (req.isAuthenticated()) {
res.render('profile', {
user: req.user // useful for view engine, useless for HTML
});
} 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()) {
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.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');
}
});
// todo: user registration with captcha
app.post('/changePwd', function (req, res) {
if (req.isAuthenticated()) {
var currPwd = req.body.inputCurrPwd
var newPwd = req.body.inputNewPwd
var retypePwd = req.body.inputConfirm
// Load hashed passwd from DB.
dbconn.query('SELECT password FROM user WHERE email="'+req.user.email+'"', 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) {
methods.updatePassword(hash, req.user.email, 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
});
/*
if email found: send generated email or instruction to reset password
if email not found: send notification, example: https://www.troyhunt.com/everything-you-ever-wanted-to-know/
*/
});
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.post('/forgotPwd', function(req, res, next) {
//methods.currentDate();
/* do something: write down reset password procedure in Technical Req. Document
ref: https://meanstackdeveloper.in/implement-reset-password-functionality-in-node-js-express.html
https://medium.com/@terrychayes/adding-password-reset-functionality-to-a-react-app-with-a-node-backend-4681480195d4
http://sahatyalkabov.com/how-to-implement-password-reset-in-nodejs/
if email found: send generated email or instruction to reset password
if email not found: send notification, example: https://www.troyhunt.com/everything-you-ever-wanted-to-know/
*/
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"
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
methods.updateUser(user, 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) {
// update password
bcrypt.genSalt(saltRounds, function(err, salt) {
bcrypt.hash(req.params.inputNewPwd, salt, function(err, hash) {
methods.updatePassword(hash, user.email, 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!')
// todo: send confirmation email
}
})
});
});
}
else {
req.flash('error', "User not found.")
}
});
res.redirect('/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);
});
});
// 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);
}
);
};
extends error
block content
h2 Cannot find #{url}
\ No newline at end of file
extends error
block content
h1 Error: #{error.message}
if settings['verbose errors']
pre= error.stack
else
p An error ocurred!
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<title>User Profile</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<link rel="stylesheet" type="text/css" href="https://transfer.hft-stuttgart.de/css/bootstrap/bootstrap.css" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous" />
<style>
.collapse {
display: none;
}
.collapse.in {
display: block;
}
.collapsing {
position: relative;
height: 0;
overflow: hidden;
-webkit-transition-timing-function: ease;
-o-transition-timing-function: ease;
transition-timing-function: ease;
-webkit-transition-duration: .35s;
-o-transition-duration: .35s;
transition-duration: .35s;
-webkit-transition-property: height,visibility;
-o-transition-property: height,visibility;
transition-property: height,visibility;
}
</style>
</head>
<body>
<!-- CONTENT -->
<div class="container-fluid">
<div class="row">
<!-- https://getbootstrap.com/docs/4.3/components/navs/ -->
<div class="col-3">
<h5>
<span id="fullname"></span>
</h5>
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
<a class="nav-link" href="#" aria-selected="true">Profile</a>
<a class="nav-link" href="/security" aria-selected="false">Security</a>
<a class="nav-link" href="#" aria-selected="false">Services</a>
</div>
</div>
<div class="col-sm-9">
<form id="profileForm" method="POST" action="/updateProfile">
<!-- p><input type="hidden" th:value="${id}"/></p -->
<table class="table table-borderless">
<tr>
<th><label for="title">Title</label></th>
<td><input id="inputTitle" name="inputTitle" type="text" placeholder="Title"></td>
</tr>
<tr>
<th><label for="firstname">Vorname</label></th>
<td><input id="inputFirstname" name="inputFirstname" type="text" placeholder="Vorname" required></td>
</tr>
<tr>
<th><label for="lastname">Nachname</label></th>
<td><input id="inputLastname" name="inputLastname" type="text" placeholder="Nachname" required></td>
</tr>
<tr>
<th><label for="email">Email</label></th>
<td><input id="inputEmail" name="inputEmail" type="text" placeholder="Email" required></td>
</tr>
<tr>
<th><label for="organisation">Unternehmen</label></th>
<td><input id="inputOrganisation" name="inputOrganisation" type="text" placeholder="Unternehmen"></td>
</tr>
<tr>
<th><label for="industry">Branche</label></th>
<td><input id="inputIndustry" name="inputIndustry" type="text" placeholder="Branche"></td>
</tr>
<tr>
<th><label for="speciality">Fachgebiete</label></th>
<td><input id="inputSpeciality" name="inputSpeciality" type="text" placeholder="Fachgebiete"></td>