alks
Version:
CLI for working with ALKS
309 lines (275 loc) • 9.34 kB
JavaScript
/*jslint node: true */
;
var prompt = require('prompt'),
_ = require('underscore'),
async = require('async'),
loki = require('lokijs'),
clortho = require('clortho').forService('alkscli'),
netrc = require('node-netrc'),
inquirer = require('inquirer'),
semVer = require('semver'),
path = require('path'),
clc = require('cli-color'),
alks = require('alks-node'),
utils = require('./utils'),
fs = require('fs'),
ua = require('universal-analytics'),
pkg = require(path.join(__dirname, '../', 'package.json'));
var exports = module.exports = {};
var ALKS_USERID = 'alksuid',
SERVICE = 'alkscli',
GA_ID = 'UA-88747959-1';
var db = new loki(utils.getDBFile()),
visitor = null,
logger = 'developer';
function getDeveloperCollection(callback){
// have the DB load from disk
db.loadDatabase({}, function(){
// grab the developer collection
var developer = db.getCollection('account');
// if its null this is a new run, create the collection
if(developer === null){
developer = db.addCollection('account');
}
try{
callback(null, developer);
} catch(e){
console.error('Error encountered during database developer transaction! Swallowing to preserve file integrity.');
console.error(e);
}
});
}
function getPasswordFromKeystore(cb){
if(utils.isPasswordSecurelyStorable()){
clortho.getFromKeychain(ALKS_USERID)
.then(function(data){
if(data){
cb(data.password);
}
else{
cb(null);
}
})
.catch(function(err){
cb(null);
}
);
}
else{
var auth = netrc(SERVICE);
if(!_.isEmpty(auth.password)){
cb(auth.password);
}
else{
cb(null);
}
}
}
function getChangeLog(){
var file = path.join(__dirname, '../', 'changelog.txt'),
contents = fs.readFileSync(file, 'utf8');
return contents;
}
var storePassword = exports.storePassword = function(password, callback){
if(utils.isPasswordSecurelyStorable()){
return clortho.saveToKeychain(ALKS_USERID, password)
.then(function(){
callback(null, true);
})
.catch(function(e){
callback(e, false);
}
);
}
else{
netrc.update(SERVICE, {
login: ALKS_USERID,
password: password
});
callback(null, true);
}
};
exports.removePassword = function(){
if(utils.isPasswordSecurelyStorable()){
return clortho.removeFromKeychain(ALKS_USERID);
}
else{
netrc.update(SERVICE, {});
}
};
var getPasswordFromPrompt = exports.getPasswordFromPrompt = function (callback, text){
prompt.start();
prompt.message = '';
prompt.get([{
name: 'password',
description: text ? text : 'Password',
hidden: true,
replace: '*',
required: true
}], function(err, result){
if(err){
callback(err);
}
else{
callback(null, utils.trim(result.password));
}
});
};
exports.ensureConfigured = function(callback){
getDeveloper(function(err, developer){
// validate we have a valid configuration
if(_.isEmpty(developer.server) ||
_.isEmpty(developer.userid) ||
_.isEmpty(developer.alksAccount) ||
_.isEmpty(developer.alksRole)){
callback(new Error('ALKS CLI is not configured. Please run: alks developer configure'), developer);
}
else{
// since this is the first func to always get called check for update
var currentVersion = pkg.version,
lastRunVerion = developer.lastVersion;
// if they dont have a last version, set to current and save
if(!lastRunVerion){
developer.lastVersion = lastRunVerion = currentVersion;
saveDeveloper(developer);
}
// check if they just updated
if(semVer.gt(currentVersion, lastRunVerion)){
// give them release notes
utils.showBorderedMessage(90, clc.white(getChangeLog()));
// save the last version
developer.lastVersion = currentVersion;
saveDeveloper(developer);
}
callback(null);
}
});
};
var saveDeveloper = exports.saveDeveloper = function(data, callback){
getDeveloperCollection(function(err, dev){
dev.removeDataOnly();
dev.insert({
server: utils.trim(data.server),
userid: utils.trim(data.userid),
alksAccount: utils.trim(data.alksAccount),
alksRole: utils.trim(data.alksRole),
lastVersion: pkg.version
});
if(data.savePassword && !_.isEmpty(data.password)){
storePassword(data.password, function(err, saved){
if(!saved){
utils.passwordSaveErrorHandler(err);
}
});
}
db.save(function(err, data){
if(_.isFunction(callback)) callback(err, data);
});
});
};
var getDeveloper = exports.getDeveloper = function(callback){
getDeveloperCollection(function(err, devs){
if(err){
return callback(err);
}
var data = devs.chain().data()[0];
var resp = {
server: data ? data.server : null,
userid: data ? data.userid : null,
alksAccount: data ? data.alksAccount : null,
alksRole: data ? data.alksRole : null,
lastVersion: data ? data.lastVersion : null
};
callback(null, resp);
});
};
exports.getALKSAccount = function(program, options, callback){
var opts = _.extend({
iamOnly: false
}, options);
async.waterfall([
// get developer
function(cb){
getDeveloper(cb);
},
// get password
function(developer, cb){
getPassword(program, function(err, password){
cb(err, developer, password);
});
},
// load available account/roles
function(developer, password, cb){
alks.getAccounts(developer.server, developer.userid, password, { filters: { iamOnly: opts.iamOnly } }, function(err, alksAccounts){
var indexedAlksAccounts = [];
_.each(alksAccounts, function(alksAccount, i){
var idx = (i+1) + ') '
if(i < 10) idx = ' ' + idx;
alksAccount = idx + alksAccount;
indexedAlksAccounts.push(alksAccount);
});
cb(err, developer, password, indexedAlksAccounts);
});
},
// ask user which account/role
function(developer, password, alksAccounts, cb){
if(!alksAccounts.length){
return cb(new Error('No accounts found.'));
}
inquirer.prompt([
{
type: 'list',
name: 'alksAccount',
message: 'Please select an ALKS account/role',
choices: alksAccounts
}
]).then(function(answers){
var data = answers.alksAccount.substring(4).split(alks.getAccountSelectorDelimiter()),
alksAccount = data[0],
alksRole = data[1];
developer.alksAccount = alksAccount;
developer.alksRole = alksRole;
cb(null, developer);
});
},
], function(err, data){
callback(err, data);
});
};
var getPassword = exports.getPassword = function(program, callback){
// first check password from CLI argument
if(program && !_.isEmpty(program.password)){
return callback(null, program.password);
}
// then check for an environment variable
else if(!_.isEmpty(process.env.ALKS_PASSWORD)){
return callback(null, process.env.ALKS_PASSWORD);
}
// then check the keystore
else{
getPasswordFromKeystore(function(password){
if(!_.isEmpty(password)) return callback(null, password);
else{
// otherwise prompt the user (if we have program)
return program ? getPasswordFromPrompt(callback) : callback(null);
}
});
}
};
exports.trackActivity = function(activity){
var onComplete = function(){
utils.log(null, logger, 'tracking activity: ' + activity);
visitor.event('activity', activity).send();
};
if(!visitor){
getDeveloper(function(err, dev){
if(err){ return; }
utils.log(null, logger, 'creating tracker for: ' + dev.userid);
visitor = ua(GA_ID, dev.userid, { https: true, strictCidFormat: false }),
onComplete();
});
}
else{
onComplete();
}
};