mirror of https://github.com/rancher/ui.git
472 lines
14 KiB
JavaScript
472 lines
14 KiB
JavaScript
/* jshint esversion: 6 */
|
|
module.exports = function(app/*, options*/) {
|
|
const bodyParser = require('body-parser');
|
|
const mysql = require('mysql');
|
|
const config = require('../../../config/environment')().APP;
|
|
const request = require('request');
|
|
|
|
const helper = require('sendgrid').mail;
|
|
const crypto = require('crypto');
|
|
|
|
const newRequest = require('../../util/util.js').newRequest;
|
|
const generateError = require('../../util/util.js').generateError;
|
|
|
|
const rancherApiUrl = `${config.apiServer}${config.apiEndpoint}`;
|
|
const tablePrefix = process.env.DB_TABLE_PREFIX || '';
|
|
|
|
const siteUrl = process.env.SITE_URL || ('https://' + process.env.RANCHER);
|
|
|
|
app.use(bodyParser.json()); // for parsing application/json
|
|
|
|
var pool = mysql.createPool({
|
|
host: process.env.DB_HOST,
|
|
user: process.env.DB_USER,
|
|
password: process.env.DB_PASSWORD,
|
|
database: process.env.DB_NAME,
|
|
});
|
|
|
|
app.use('/register-new', function(req, res) {
|
|
checkExistingAccount(req.body.email, function(exists) {
|
|
if (exists) {
|
|
return generateError('exists', 'Account exists', res);
|
|
} else {
|
|
generateAndInsertToken(null, req.body.name, req.body.email, "create", function(err, token) {
|
|
if (err) {
|
|
return generateError('auth', err, res);
|
|
}
|
|
|
|
sendVerificationEmail(req.body.email, req.body.name, token, function(err) {
|
|
if (err) {
|
|
return generateError('email', err, res);
|
|
}
|
|
|
|
res.status(200).json({success: 'User registration email sent'});
|
|
});
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
app.use('/verify-token', function(req, res) {
|
|
getChallengeToken(req.body.token, function(err, row) {
|
|
if (err || !row) {
|
|
return generateError('token', err, res);
|
|
}
|
|
|
|
var out = {
|
|
email: row.email,
|
|
name: row.name,
|
|
};
|
|
res.status(200).send(out);
|
|
});
|
|
});
|
|
|
|
app.use('/create-user', function(req, res) {
|
|
var user = req.body;
|
|
|
|
var account = {
|
|
type: 'user',
|
|
name: user.name
|
|
};
|
|
|
|
var credential = {
|
|
type: 'password',
|
|
publicValue: user.email,
|
|
secretValue: user.pw,
|
|
accountId: null
|
|
};
|
|
|
|
getChallengeToken(user.token, function(err, row) {
|
|
if (err || !row ) {
|
|
return generateError('token', err, res);
|
|
}
|
|
|
|
newRequest({
|
|
url: `${rancherApiUrl}/account`,
|
|
method: 'POST',
|
|
body: account
|
|
}, function(accountModel) {
|
|
credential.accountId = accountModel.id;
|
|
|
|
isAccountActive(accountModel, function(isActive){
|
|
if (isActive) {
|
|
newRequest({
|
|
url: `${rancherApiUrl}/passwords`,
|
|
method: 'POST',
|
|
body: credential
|
|
}, function(credentialModel) {
|
|
isPasswordActive(credentialModel.accountId, function(passwordActive) {
|
|
if (passwordActive) {
|
|
removeUserFromTable(user.email, function(err) {
|
|
if ( err ) {
|
|
return generateError('db', err, res);
|
|
}
|
|
|
|
getTokenForLogin(user.email, user.pw, function(err, token, serverResponse) {
|
|
if (err) {
|
|
return res.status(serverResponse.status).send(token);
|
|
}
|
|
|
|
res.cookie('token', token.jwt, {secure: req.secure}).status(200).send({type: 'success'});
|
|
});
|
|
});
|
|
} else {
|
|
return generateError('account', 'Account never became active', res);
|
|
}
|
|
});
|
|
|
|
}, res);
|
|
} else {
|
|
return generateError('account', 'Account never became active', res);
|
|
}
|
|
});
|
|
}, res);
|
|
});
|
|
});
|
|
|
|
app.use('/reset-password', function(req, res) {
|
|
var user = req.body;
|
|
var userEmail = user.email;
|
|
var name = user.name;
|
|
var url = `${rancherApiUrl}/credentials?kind=password&publicValue=${encodeURIComponent(userEmail)}&limit=1&sort=name`;
|
|
|
|
if (!user.email) {
|
|
return generateError('account', 'No email for account', res);
|
|
}
|
|
|
|
newRequest({
|
|
url: url,
|
|
method: 'GET',
|
|
}, function(body) {
|
|
if (!body || !body.data || !body.data.length ) {
|
|
return generateError('account', 'No password found ', res);
|
|
}
|
|
|
|
var credential = body.data[0];
|
|
var url = `${rancherApiUrl}/accounts/${encodeURIComponent(credential.accountId)}`;
|
|
var credEmail = credential.publicValue;
|
|
|
|
newRequest({
|
|
url: url,
|
|
method: 'GET'
|
|
}, function(body) {
|
|
if (body.type !== 'account') {
|
|
return generateError('account', 'Body type is not account', res);
|
|
}
|
|
|
|
generateAndInsertToken(body.id, name, credEmail, "reset", function(err, token) {
|
|
if (err) {
|
|
return generateError('token', err, res);
|
|
}
|
|
|
|
sendPasswordReset(req.body.email, body.name, token, function(err) {
|
|
if (err) {
|
|
return generateError('email', err, res);
|
|
}
|
|
|
|
res.status(200).json({success: 'Email sent'});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
app.use('/update-password', function(req, res) {
|
|
var user = req.body;
|
|
|
|
getChallengeToken(user.token, function(err, credential) {
|
|
if (err || !credential || !credential.email) {
|
|
return generateError('token', err, res);
|
|
}
|
|
|
|
newRequest({
|
|
url: `${rancherApiUrl}/passwords?publicValue=${encodeURIComponent(credential.email)}`,
|
|
method: 'GET'
|
|
}, function(body) {
|
|
|
|
if ( !body || !body.data || !body.data.length ) {
|
|
return generateError('token', err, res);
|
|
}
|
|
|
|
newRequest({
|
|
url: body.data[0].actions.changesecret,
|
|
method: 'POST',
|
|
body: {newSecret: user.pw, oldSecret: ''}
|
|
}, function(/*body*/) {
|
|
removeUserFromTable(credential.email, function(err) {
|
|
if ( err ) {
|
|
return generateError('db', err, res);
|
|
}
|
|
|
|
getTokenForLogin(credential.email, user.pw, function(err, body, serverResponse) {
|
|
if (err) {
|
|
return res.status(serverResponse.status).send(body);
|
|
}
|
|
|
|
sendPasswordVerificationEmail(credential.email, credential.name, function(err) {
|
|
if (err) {
|
|
return generateError('email', err, res);
|
|
}
|
|
|
|
res.cookie('token', body.jwt, {secure: req.secure}).status(200).send({type: 'success'});
|
|
});
|
|
}, res);
|
|
});
|
|
}, res);
|
|
});
|
|
});
|
|
});
|
|
|
|
function generateAndInsertToken(id, name, email, request, cb) {
|
|
var challengeToken = crypto.randomBytes(20);
|
|
challengeToken = challengeToken.toString('hex');
|
|
return pool.query(`INSERT INTO ${tablePrefix}challenge SET account_id = ?, name = ?, email = ?, token = ?, request = ?, created = NOW()`, [id, name, email, challengeToken, request], function(err) {
|
|
cb(err, challengeToken);
|
|
});
|
|
}
|
|
|
|
function getTokenForLogin(username, password, cb) {
|
|
return request({
|
|
method: 'POST',
|
|
json: true,
|
|
url: `${rancherApiUrl}/token`,
|
|
body: {
|
|
code: `${username}:${password}`
|
|
}
|
|
}, function(err, response, body) {
|
|
if (err) {
|
|
console.log('getTokenForLogin error: ', err);
|
|
}
|
|
|
|
if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
return cb(null, body, response);
|
|
}
|
|
|
|
var errOut = null;
|
|
if (err) {
|
|
errOut = err;
|
|
} else {
|
|
errOut = response;
|
|
}
|
|
|
|
// cattle error just pass it along
|
|
cb(errOut, body, response);
|
|
});
|
|
}
|
|
|
|
function removeUserFromTable(email, cb) {
|
|
return pool.query(`DELETE FROM ${tablePrefix}challenge WHERE email = ? OR created > DATE_SUB(NOW(), INTERVAL 24 HOUR)`, [email], function(err){
|
|
if (err) {
|
|
console.log('error', 'could not delete record:', email);
|
|
return cb(err);
|
|
}
|
|
|
|
cb(null);
|
|
});
|
|
}
|
|
|
|
function getChallengeToken(token, cb) {
|
|
return pool.query(`SELECT * FROM ${tablePrefix}challenge WHERE token = ? AND created > DATE_SUB(NOW(), INTERVAL 24 HOUR)`, [token], function(err, results) {
|
|
if (err) {
|
|
console.log('error', 'could not retrieve token');
|
|
return cb(err);
|
|
}
|
|
|
|
return cb(null, results[0]);
|
|
});
|
|
}
|
|
|
|
function fetchSendGridApiDetails(detail, cb) {
|
|
var base = `${rancherApiUrl}/settings`;
|
|
|
|
return newRequest({
|
|
url: `${base}/ui.sendgrid.api_key`,
|
|
method: 'GET',
|
|
}, function(body) {
|
|
|
|
if (body) {
|
|
var apiKey = body.activeValue;
|
|
|
|
return newRequest({
|
|
url: `${base}/${detail}`,
|
|
method: 'GET'
|
|
}, function(body) {
|
|
|
|
if (body) {
|
|
cb({apiKey: apiKey, id: body.value});
|
|
} else {
|
|
cb(false);
|
|
}
|
|
}, null);
|
|
|
|
} else {
|
|
|
|
cb(false);
|
|
}
|
|
});
|
|
}
|
|
|
|
function sendPasswordReset(email, name, token, cb) {
|
|
fetchSendGridApiDetails('ui.sendgrid.template.password_reset', function(response) {
|
|
if (response) {
|
|
var from_email = new helper.Email('no-reply@rancher.com');
|
|
var to_email = new helper.Email(email);
|
|
var subject = 'Password Reset Request';
|
|
var contentLink = `<html><a href="${siteUrl}/verify-reset-password/${token}">Reset Password</a></html>`;
|
|
var content = new helper.Content(
|
|
'text/html', contentLink);
|
|
var mail = new helper.Mail(from_email, subject, to_email, content);
|
|
mail.personalizations[0].addSubstitution(
|
|
new helper.Substitution("-username-", name));
|
|
mail.setTemplateId(response.id);
|
|
|
|
var sg = require('sendgrid')(response.apiKey);
|
|
var request = sg.emptyRequest({
|
|
method: 'POST',
|
|
path: '/v3/mail/send',
|
|
body: mail.toJSON(),
|
|
});
|
|
|
|
return sg.API(request, cb);
|
|
} else {
|
|
cb('There was a problem retrieving the email api key or email template id.', null);
|
|
}
|
|
});
|
|
}
|
|
|
|
function sendVerificationEmail(email, name, token, cb) {
|
|
fetchSendGridApiDetails('ui.sendgrid.template.create_user', function(response) {
|
|
if (!response) {
|
|
return cb('There was a problem retrieving the email api key or email template id.', null);
|
|
}
|
|
|
|
var from_email = new helper.Email('no-reply@rancher.com');
|
|
var to_email = new helper.Email(email);
|
|
var subject = 'Verify your Rancher Cloud Account';
|
|
var contentLink = `<html><a href="${siteUrl}/verify/${token}">Verify Email</a></html>`;
|
|
var content = new helper.Content(
|
|
'text/html', contentLink);
|
|
var mail = new helper.Mail(from_email, subject, to_email, content);
|
|
mail.setTemplateId(response.id);
|
|
|
|
var sg = require('sendgrid')(response.apiKey);
|
|
var request = sg.emptyRequest({
|
|
method: 'POST',
|
|
path: '/v3/mail/send',
|
|
body: mail.toJSON(),
|
|
});
|
|
|
|
return sg.API(request, cb);
|
|
});
|
|
}
|
|
|
|
function sendPasswordVerificationEmail(email, name, cb) {
|
|
fetchSendGridApiDetails('ui.sendgrid.template.verify_password', function(response) {
|
|
if (!response) {
|
|
return cb('There was a problem retrieving the email api key or email template id.', null);
|
|
}
|
|
|
|
var from_email = new helper.Email('no-reply@rancher.com');
|
|
var to_email = new helper.Email(email);
|
|
var subject = 'Password Reset Confirmation';
|
|
var contentLink = `<html><a href="${siteUrl}/login?resetpw=true">Reset Password</a></html>`;
|
|
var content = new helper.Content(
|
|
'text/html', contentLink);
|
|
var mail = new helper.Mail(from_email, subject, to_email, content);
|
|
mail.setTemplateId(response.id);
|
|
|
|
var sg = require('sendgrid')(response.apiKey);
|
|
var request = sg.emptyRequest({
|
|
method: 'POST',
|
|
path: '/v3/mail/send',
|
|
body: mail.toJSON(),
|
|
});
|
|
|
|
return sg.API(request, cb);
|
|
});
|
|
|
|
}
|
|
|
|
function checkExistingAccount(email, cb) {
|
|
return newRequest({
|
|
url: `${rancherApiUrl}/passwords?publicValue=${email}`,
|
|
method: 'GET',
|
|
}, function(body, response) {
|
|
if (body && body.data.length) {
|
|
return cb(true);
|
|
} else {
|
|
return cb(false);
|
|
}
|
|
}, null);
|
|
}
|
|
|
|
function isPasswordActive(accountId, cb) {
|
|
var count = 30;
|
|
var id = accountId;
|
|
|
|
function getPasswordStatus() {
|
|
setTimeout(function(){
|
|
count--;
|
|
if (count > 0) {
|
|
newRequest({
|
|
url: `${rancherApiUrl}/passwords?accountId=${id}`,
|
|
method: 'GET',
|
|
}, function(body, response) {
|
|
if (body && body.data && body.data.length) {
|
|
var okay = true;
|
|
body.data.forEach((pw) => {
|
|
if (pw.state !== 'active') {
|
|
okay = false;
|
|
}
|
|
});
|
|
if (okay) {
|
|
return cb(true);
|
|
} else {
|
|
getPasswordStatus();
|
|
}
|
|
} else {
|
|
return cb(false);
|
|
}
|
|
}, null);
|
|
} else {
|
|
return cb(false);
|
|
}
|
|
}, 1000);
|
|
}
|
|
|
|
getPasswordStatus();
|
|
}
|
|
|
|
function isAccountActive(model, cb) {
|
|
|
|
var selfLink = model.links.self;
|
|
var count = 30;
|
|
|
|
function getAccountStatus() {
|
|
setTimeout(function(){
|
|
count--;
|
|
if (count > 0) {
|
|
newRequest({
|
|
url: selfLink,
|
|
method: 'GET',
|
|
}, function(body, response) {
|
|
if (body) {
|
|
if (body.state === 'active') {
|
|
return cb(true);
|
|
} else {
|
|
getAccountStatus();
|
|
}
|
|
}
|
|
}, null);
|
|
} else {
|
|
return cb(false);
|
|
}
|
|
}, 1000);
|
|
}
|
|
|
|
|
|
getAccountStatus();
|
|
}
|
|
};
|