cli-engine-heroku
Advanced tools
Comparing version
@@ -12,2 +12,10 @@ 'use strict'; | ||
var _yubikey = require('./yubikey'); | ||
var _yubikey2 = _interopRequireDefault(_yubikey); | ||
var _mutex = require('./mutex'); | ||
var _mutex2 = _interopRequireDefault(_mutex); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -25,3 +33,4 @@ | ||
if (info.length) super([options.message, ''].concat(info).join('\n'));else super(options.message); | ||
this.httpError = httpError; | ||
this.http = httpError; | ||
this.body = options; | ||
} | ||
@@ -32,5 +41,8 @@ } | ||
class Heroku extends _http2.default { | ||
constructor(output, options = {}) { | ||
super(output); | ||
constructor(command, options = {}) { | ||
super(command.out); | ||
if (options.required === undefined) options.required = true; | ||
options.preauth = options.preauth !== false; | ||
this.cmd = command; | ||
this.options = options; | ||
@@ -42,12 +54,37 @@ this.requestOptions.host = 'api.heroku.com'; | ||
this.requestOptions.headers['accept'] = 'application/vnd.heroku+json; version=3'; | ||
this.twoFactorMutex = new _mutex2.default(); | ||
this.preauthPromises = {}; | ||
let self = this; | ||
this.http = class extends this.http { | ||
async request() { | ||
self._logRequest(this); | ||
static async twoFactorRetry(err, url, opts = {}, retries = 3) { | ||
const app = err.body.app ? err.body.app.name : null; | ||
if (!app || !options.preauth) { | ||
opts.headers['Heroku-Two-Factor-Code'] = await self.twoFactorPrompt(); | ||
return this.request(url, opts, retries); | ||
} else { | ||
// if multiple requests are run in parallel for the same app, we should | ||
// only preauth for the first so save the fact we already preauthed | ||
if (!self.preauthPromises[app]) { | ||
self.preauthPromises[app] = self.twoFactorPrompt().then(factor => self.preauth(app, factor)); | ||
} | ||
await self.preauthPromises[app]; | ||
return this.request(url, opts, retries); | ||
} | ||
} | ||
static async request(url, opts, retries = 3) { | ||
retries--; | ||
try { | ||
await super.request(); | ||
return await super.request(url, opts); | ||
} catch (err) { | ||
if (err.__httpcall) throw new HerokuAPIError(err);else throw err; | ||
if (!err.__httpcall) throw err; | ||
let apiError = new HerokuAPIError(err); | ||
if (retries > 0) { | ||
if (apiError.http.statusCode === 403 && apiError.body.id === 'two_factor') { | ||
return this.twoFactorRetry(apiError, url, opts, retries); | ||
} | ||
} | ||
throw apiError; | ||
} | ||
self._logResponse(this); | ||
} | ||
@@ -67,3 +104,23 @@ }; | ||
} | ||
twoFactorPrompt() { | ||
_yubikey2.default.enable(); | ||
return this.twoFactorMutex.synchronize(async () => { | ||
try { | ||
let factor = await this.out.prompt('Two-factor code', { mask: true }); | ||
_yubikey2.default.disable(); | ||
return factor; | ||
} catch (err) { | ||
_yubikey2.default.disable(); | ||
throw err; | ||
} | ||
}); | ||
} | ||
preauth(app, factor) { | ||
return this.put(`/apps/${app}/pre-authorizations`, { | ||
headers: { 'Heroku-Two-Factor-Code': factor } | ||
}); | ||
} | ||
} | ||
exports.default = Heroku; |
@@ -26,3 +26,3 @@ 'use strict'; | ||
if (this._heroku) return this._heroku; | ||
this._heroku = new _api_client2.default(this.out); | ||
this._heroku = new _api_client2.default(this); | ||
return this._heroku; | ||
@@ -29,0 +29,0 @@ } |
{ | ||
"name": "cli-engine-heroku", | ||
"description": "heroku specific functionality for the cli-engine", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"author": "Jeff Dickey @dickeyxxx", | ||
@@ -18,3 +18,3 @@ "bugs": "https://github.com/heroku/cli-engine-heroku/issues", | ||
"babel-plugin-transform-flow-strip-types": "6.22.0", | ||
"cli-engine-command": "^5.0.2", | ||
"cli-engine-command": "5.0.3", | ||
"cli-engine-config": "^1.3.6", | ||
@@ -21,0 +21,0 @@ "eslint": "3.19.0", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
27637
27.97%24
20%404
28.25%3
50%