appcenter-cli
Version:
Command line tool for Visual Studio App Center
140 lines (139 loc) • 5.27 kB
JavaScript
;
// Token store implementation over OSX keychain
Object.defineProperty(exports, "__esModule", { value: true });
//
// Access to the OSX keychain - list, add, get password, remove
//
const _ = require("lodash");
const rx = require("rxjs");
const childProcess = require("child_process");
const from = require("from2");
const split = require("split2");
const through = require("through2");
const osx_keychain_parser_1 = require("./osx-keychain-parser");
const debug = require("debug")("appcenter-cli:util:token-store:osx:osx-token-store");
const util_1 = require("util");
const securityPath = "/usr/bin/security";
const serviceName = "appcenter-cli";
const oldServiceName = "mobile-center-cli";
class OsxTokenStore {
list() {
return rx.Observable.create((observer) => {
const securityProcess = childProcess.spawn(securityPath, ["dump-keychain"]);
const securityStream = securityProcess.stdout
.pipe(split())
.pipe(through(function (line, enc, done) {
done(null, line.toString().replace(/\\134/g, "\\"));
}))
.pipe(new osx_keychain_parser_1.OsxSecurityParsingStream());
securityStream.on("data", (data) => {
debug(`listing, got data ${util_1.inspect(data)}`);
if (data.svce !== serviceName) {
debug(`service does not match, skipping`);
return;
}
const key = data.acct;
// Have to get specific token to get tokens, but we have ids
const accessToken = {
id: data.gena,
token: null
};
debug(`Outputting ${util_1.inspect({ key, accessToken })}`);
observer.next({ key, accessToken });
});
securityStream.on("end", (err) => {
debug(`output from security program complete`);
if (err) {
observer.error(err);
}
else {
observer.complete();
}
});
});
}
get(key, useOldName = false) {
const args = [
"find-generic-password",
"-a", key,
"-s", useOldName ? oldServiceName : serviceName,
"-g"
];
return new Promise((resolve, reject) => {
resolve = _.once(resolve);
reject = _.once(reject);
childProcess.execFile(securityPath, args, (err, stdout, stderr) => {
if (err) {
return reject(err);
}
const match = /^password: (?:0x[0-9A-F]+. )?"(.*)"$/m.exec(stderr);
if (match) {
const accessToken = match[1].replace(/\\134/g, "\\");
debug(`stdout for security program = "${stdout}"`);
debug(`parsing stdout`);
// Parse the rest of the information from stdout to get user & token ID
const parsed = from([stdout])
.pipe(osx_keychain_parser_1.createOsxSecurityParsingStream());
parsed.on("data", (data) => {
debug(`got data on key lookup: ${util_1.inspect(data)}`);
resolve({
key: data.acct,
accessToken: {
id: data.gena,
token: accessToken
}
});
});
parsed.on("error", (err) => {
debug(`parsed string failed`);
reject(err);
});
}
else {
reject(new Error("Password in incorrect format"));
}
});
});
}
set(key, value) {
const args = [
"add-generic-password",
"-a", key,
"-D", "appcenter cli password",
"-s", serviceName,
"-w", value.token,
"-U"
];
if (value.id) {
args.push("-G", value.id);
}
return new Promise((resolve, reject) => {
childProcess.execFile(securityPath, args, function (err, stdout, stderr) {
if (err) {
return reject(new Error("Could not add password to keychain: " + stderr));
}
return resolve();
});
});
}
remove(key) {
const args = [
"delete-generic-password",
"-a", key,
"-s", serviceName
];
return new Promise((resolve, reject) => {
childProcess.execFile(securityPath, args, function (err, stdout, stderr) {
if (err) {
return reject(new Error("Could not remove account from keychain, " + stderr));
}
return resolve();
});
});
}
}
exports.OsxTokenStore = OsxTokenStore;
function createOsxTokenStore() {
return new OsxTokenStore();
}
exports.createOsxTokenStore = createOsxTokenStore;