Merge pull request #5 from heroku/fix-auth

Fix Auth
This commit is contained in:
Nate Black 2016-06-27 17:01:08 -07:00 коммит произвёл GitHub
Родитель 4bd01a1ddf 78bca75239
Коммит eef27abd67
8 изменённых файлов: 146 добавлений и 114 удалений

Просмотреть файл

@ -39,8 +39,7 @@ cp -a $SALESFORCE_CACHE_DIR/node $SALESFORCE_DIR/node
export PATH="$SALESFORCE_DIR/node/bin":$PATH
status "Coping deploy script to .salesforce/deploy"
cp $BP_DIR/lib/deploy.js $SALESFORCE_DIR/deploy.js
cp $BP_DIR/lib/auth.js $SALESFORCE_DIR/auth.js
cp -R $BP_DIR/lib/* $SALESFORCE_DIR/
cp $BP_DIR/package.json $SALESFORCE_DIR/package.json
echo ".salesforce/node/bin/node .salesforce/deploy.js" > $SALESFORCE_DIR/deploy

Просмотреть файл

@ -1,74 +0,0 @@
/*
force://<clientId>:<clientSecret>:<refreshToken>@<instanceUrl>
*/
var URL_REGEX = /force:\/\/(.*):(.*):(.*)@(.*)/;
var TOKEN_REGEX = /.*\"access_token\":\"([A-Za-z0-9.!_]*)\".*/;
const forceApiVersion = '37.0';
function parseUrl(oauth_url) {
if(oauth_url) {
const matcher = oauth_url.match(URL_REGEX);
if(matcher) {
return {
clientId : matcher[1],
clientSecret : matcher[2],
refreshToken : matcher[3],
instance : matcher[4]
}
}
}
return null;
};
function oauthConfig(addonInfo) {
const oauth2 = {
clientId: addonInfo.clientId,
clientSecret: addonInfo.clientSecret,
redirectUri: 'https://www.example.com' // we don't care what this is but it has to match the Salesforce Connected App config set in the Environment Hub
};
const instanceUrl = `https://${addonInfo.instance}`;
return {
oauth2,
instanceUrl,
loginUrl: instanceUrl,
serverUrl: `https://${instanceUrl}/services/Soap/u/${forceApiVersion}`,
refreshToken: addonInfo.refreshToken,
version: forceApiVersion
}
}
const request = require('request');
const refreshAuth = function(oauth) {
return new Promise((resolve, reject) => {
request
.post(`https://${oauth.instance}/services/oauth2/token`, {
qs: {
grant_type: 'refresh_token',
client_id: oauth.clientId,
client_secret: oauth.clientSecret,
refresh_token: oauth.refreshToken
},
headers: {
Accept: 'application/json'
}
}, function (error, response, body) {
if (error != null) {
console.error(error);
reject(error);
return;
}
console.log('-----> Refresh auth', response.statusCode, body);
resolve(JSON.parse(response.body).access_token);
});
})
};
module.exports = {
parseUrl,
oauthConfig,
refreshAuth
};

Просмотреть файл

@ -1,9 +1,7 @@
const zipPath = '.salesforce/src.zip';
const fs = require('fs');
const jsforce = require('jsforce');
const Promise = require('bluebird');
const { parseUrl, oauthConfig } = require('./auth');
const jsforceConnection = require('./jsforce-connection');
const prompt = function(message) {
console.log(`-----> ${message}`);
@ -17,37 +15,33 @@ const error = function(message) {
console.error(` ! ${message}`);
};
const addonInfo = parseUrl(process.env.SALESFORCE_URL);
return jsforceConnection()
.then( conn => {
return new Promise((resolve, reject) => {
console.log('-----> Deploying metadata');
const zipStream = fs.createReadStream(zipPath);
conn.metadata.pollTimeout = 240*1000;
const deployLocator = conn.metadata.deploy(zipStream, {});
deployLocator.complete(true, function(err, result) {
if (err) {
reject(err);
return;
}
resolve(result);
});
})
})
.then( result => {
info('done ? :' + result.done);
info('success ? : ' + result.true);
info('state : ' + result.state);
info('component errors: ' + result.numberComponentErrors);
info('components deployed: ' + result.numberComponentsDeployed);
info('tests completed: ' + result.numberTestsCompleted);
info(' ' + (result.success ? 'Success' : 'Failed'));
})
.catch( err => {
error(err.stack);
process.exit(1);
});
if(!addonInfo) {
error(`Didn't find a valid SALESFORCE_URL: ${process.env.SALESFORCE_URL}`);
process.exit(1);
}
const config = oauthConfig(addonInfo);
const conn = new jsforce.Connection(config);
conn.on('refresh', function(accessToken, res) {
info(`refreshed access token`);
});
console.log('-----> Deploying metadata');
const zipStream = fs.createReadStream(zipPath);
conn.metadata.pollTimeout = 240*1000;
const deployLocator = conn.metadata.deploy(zipStream, {});
deployLocator.on('progress', function(results) {
info('polling...');
});
deployLocator.complete(true, function(err, result) {
if (err) {
error(err);
return;
}
info('done ? :' + result.done);
info('success ? : ' + result.true);
info('state : ' + result.state);
info('component errors: ' + result.numberComponentErrors);
info('components deployed: ' + result.numberComponentsDeployed);
info('tests completed: ' + result.numberTestsCompleted);
info(' ' + (result.success ? 'Success' : 'Failed'));
});

Просмотреть файл

@ -0,0 +1,21 @@
const fetch = require('node-fetch');
module.exports = function getSalesforceIdentity(accessToken, idUrl) {
console.log('-----> Get Salesforce identity');
return fetch(idUrl, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
})
.then( response => {
const status = response.status;
if (status >= 300) { throw new Error(`Request status ${status} for ${idUrl}`) }
//console.log(' response status', status);
return response.json();
})
.then( salesforceIdentity => {
//console.log(' Identity', salesforceIdentity);
return salesforceIdentity;
});
}

Просмотреть файл

@ -0,0 +1,46 @@
const url = require('url');
const jsforce = require('jsforce');
const requireEnvVar = require('./require-env-var');
const refreshSalesforceAuth = require('./refresh-salesforce-auth');
const getSalesforceIdentity = require('./get-salesforce-identity');
// Return Promise of authenticated jsForce connection.
module.exports = function createJsforceConnection(forceComVersion = '37.0') {
const forceComAlmUrl = url.parse(requireEnvVar('SALESFORCE_URL'));
const forceComAuth = forceComAlmUrl.auth.split(':');
const forceComId = forceComAuth[0];
const forceComSecret = forceComAuth[1];
const forceComRefreshToken = forceComAuth[2];
const forceComHost = forceComAlmUrl.host;
const forceComUrl = `https://${forceComHost}`;
console.log('-----> Force.com connecting', forceComUrl);
// Dynamic assignments with top-level scope
let forceComAuthToken;
let forceComUserId;
let forceComUsername;
let connection;
return refreshSalesforceAuth(forceComHost, forceComId, forceComSecret, forceComRefreshToken)
.then( ({accessToken, idUrl}) => {
forceComAuthToken = accessToken;
connection = new jsforce.Connection({
accessToken: accessToken,
loginUrl: forceComUrl,
instanceUrl: forceComUrl,
serverUrl: `${forceComUrl}/services/Soap/u/${forceComVersion}`,
version: forceComVersion
});
return getSalesforceIdentity(accessToken, idUrl);
})
.then( res => {
console.log('-----> Salesforce org ID', res.organization_id);
console.log('-----> Salesforce admin user ID', res.user_id);
console.log('-----> Salesforce admin username', res.username);
return connection;
});
}

Просмотреть файл

@ -0,0 +1,38 @@
const url = require('url');
const fetch = require('node-fetch');
module.exports = function refreshSalesforceAuth(forceComHost, forceComId, forceComSecret, forceComRefreshToken) {
if (forceComHost == null || forceComId == null || forceComSecret == null || forceComRefreshToken == null) {
throw new Error('Requires arguments `forceComHost, forceComId, forceComSecret, forceComRefreshToken`');
}
const query = `grant_type=refresh_token&client_id=${
encodeURIComponent(forceComId)}&client_secret=${
encodeURIComponent(forceComSecret)}&refresh_token=${
encodeURIComponent(forceComRefreshToken)}`;
const refreshAuthUrl = `https://${forceComHost}/services/oauth2/token?${query}`;
console.log('-----> Refresh Salesforce auth');
return fetch(refreshAuthUrl, {
method: 'POST',
headers: {
Accept: 'application/json'
}
})
.then( response => {
const status = response.status;
if (status >= 300) { throw new Error(`Request status ${status} for ${refreshAuthUrl}`) }
//console.log(' response status', status);
return response.json();
})
.then( salesforceAuth => {
// Reset the identity URL to the specified instance host, otherwise Salesforce always uses "login.salesforce.com"
const salesforceIdentityUrl = url.parse(salesforceAuth.id);
salesforceIdentityUrl.host = forceComHost;
const idUrl = url.format(salesforceIdentityUrl);
//console.log(` instance identity URL ${idUrl}`);
return {
accessToken: salesforceAuth.access_token,
idUrl
}
});
}

Просмотреть файл

@ -0,0 +1,9 @@
module.exports = function requireEnvVar(name) {
var value = process.env[name];
if (value != null) {
return value;
} else {
throw new Error(`! Environment variable "${name}" is required`);
}
}

Просмотреть файл

@ -13,8 +13,7 @@
},
"homepage": "https://github.com/forcedotcom/force-com-buildpack#readme",
"dependencies": {
"bluebird": "^3.4.0",
"jsforce": "^1.6.5",
"request": "^2.72.0"
"node-fetch": "^1.5.3"
}
}