mirror of https://github.com/rancher/ui.git
504 lines
14 KiB
JavaScript
504 lines
14 KiB
JavaScript
module.exports = function(app/*, options*/) {
|
|
var bodyParser = require('body-parser');
|
|
var mysql = require('mysql');
|
|
var config = require('../../config/environment')().APP;
|
|
var request = require('request');
|
|
|
|
var helper = require('sendgrid').mail;
|
|
var crypto = require('crypto');
|
|
|
|
var rancherApiUrl = `${config.apiServer}${config.apiEndpoint}`;
|
|
var tablePrefix = process.env.DB_TABLE_PREFIX || '';
|
|
|
|
const ERRORS = {
|
|
auth: {
|
|
message: 'Unauthorized',
|
|
status: 401,
|
|
type: 'error',
|
|
detail: 'Unauthorized'
|
|
},
|
|
account: {
|
|
message: 'Unauthorized',
|
|
status: 401,
|
|
type: 'error',
|
|
detail: 'There was an error trying to retrieve that account, ensure you have entered the correct credentials and try again later'
|
|
},
|
|
db: {
|
|
message: 'Internal Server Error',
|
|
status: 500,
|
|
type: 'error',
|
|
detail: 'There was an error retrieving your data, ensure the info you entered is correct and try again'
|
|
},
|
|
email: {
|
|
message: 'Internal Server Error',
|
|
status: 500,
|
|
type: 'error',
|
|
detail: 'There was an error with your email, ensure you have entered the correct email and try again'
|
|
},
|
|
exists: {
|
|
message: 'Account Exists',
|
|
status: 409,
|
|
type: 'error',
|
|
detail: 'That account exists already, please use the reset password tool or create a new account with a different email.'
|
|
},
|
|
generic: {
|
|
message: 'Internal Server Error',
|
|
status: 500,
|
|
type: 'error',
|
|
detail: 'Internal Server Error'
|
|
},
|
|
token: {
|
|
message: 'Internal Server Error',
|
|
status: 500,
|
|
type: 'error',
|
|
detail: 'There was an error trying to retrieve that token, try again later'
|
|
},
|
|
};
|
|
|
|
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, req.headers.origin, 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;
|
|
|
|
console.log('checking account');
|
|
isAccountActive(accountModel, function(isActive){
|
|
if (isActive) {
|
|
newRequest({
|
|
url: `${rancherApiUrl}/passwords`,
|
|
method: 'POST',
|
|
body: credential
|
|
}, function(/*credentialModel*/) {
|
|
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'});
|
|
});
|
|
});
|
|
}, 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, req.headers.origin, 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, req.headers.origin, 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);
|
|
});
|
|
}
|
|
|
|
// opts should only contain url, method and data
|
|
function newRequest(opts, cb, ogRes) {
|
|
var optsOut = {
|
|
auth: {
|
|
user: process.env.CATTLE_ACCESS_KEY,
|
|
pass: process.env.CATTLE_SECRET_KEY,
|
|
},
|
|
json: true,
|
|
};
|
|
|
|
Object.assign(optsOut, opts);
|
|
|
|
return request(optsOut, function(err, response, body) {
|
|
if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
return cb(body, response);
|
|
}
|
|
|
|
var errOut = null;
|
|
if (err) {
|
|
errOut = err;
|
|
} else {
|
|
errOut = response;
|
|
}
|
|
|
|
console.log('error:', errOut);
|
|
if (ogRes) {
|
|
generateError('account', err, ogRes);
|
|
}
|
|
});
|
|
}
|
|
|
|
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, host, 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="${host}/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, host, 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="${host}/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, host, 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="${host}/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 generateError(code, detail, response) {
|
|
// eventually put real error log in this function
|
|
console.log('Error Generator: ', detail);
|
|
var err = ERRORS[code];
|
|
return response.status(err.status).send(err);
|
|
}
|
|
|
|
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 isAccountActive(model, cb) {
|
|
|
|
var selfLink = model.links.self;
|
|
var count = 30;
|
|
|
|
function getStatus() {
|
|
setTimeout(function(){
|
|
count--;
|
|
if (count > 0) {
|
|
newRequest({
|
|
url: selfLink,
|
|
method: 'GET',
|
|
}, function(body, response) {
|
|
if (body) {
|
|
if (body.state === 'active') {
|
|
return cb(true);
|
|
} else {
|
|
getStatus();
|
|
}
|
|
}
|
|
}, null);
|
|
} else {
|
|
return cb(false);
|
|
}
|
|
}, 1000);
|
|
}
|
|
getStatus();
|
|
}
|
|
};
|