diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d2d94db..b72494b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,10 +3,16 @@ Changelog in development -------------- -* Exit hubot on unhandled promise rejections and uncaught exceptions, usually caused by unrecoverable errors (bug fix) + + +0.9.5 +----- +* Exit hubot on invalid, expired `ST2_API_KEY` / `ST2_AUTH_TOKEN` or any other Unauthorized response from st2 server (bug fix) +* When st2 username/password is used, re-generate st2 auth token in advance, giving enough time for request to complete, st2client.js (bug fix) 0.9.4 ----- +* Exit hubot on unhandled promise rejections and uncaught exceptions, usually caused by unrecoverable errors (bug fix) * Add pagination support for action aliases - fixes #158 (bug fix) 0.9.3 diff --git a/package.json b/package.json index 280f7a5..243e00f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hubot-stackstorm", "description": "A hubot plugin for integrating with StackStorm event-driven infrastructure automation platform.", - "version": "0.9.4", + "version": "0.9.5", "author": "StackStorm, Inc. ", "license": "Apache-2.0", "keywords": [ @@ -27,7 +27,7 @@ "coffee-script": "1.12.7", "lodash": "^4.17.11", "rsvp": "^4.8.4", - "st2client": "^1.1.2", + "st2client": "^1.1.3", "truncate": "^2.0.1", "uuid": "^3.0.0" }, diff --git a/scripts/stackstorm.js b/scripts/stackstorm.js index fe6ff2a..263a8eb 100644 --- a/scripts/stackstorm.js +++ b/scripts/stackstorm.js @@ -258,8 +258,10 @@ module.exports = function(robot) { robot.logger.info(command_factory.st2_hubot_commands.length + ' commands are loaded'); }) .catch(function (err) { - var error_msg = 'Failed to retrieve commands from "%s": %s'; - robot.logger.error(util.format(error_msg, env.ST2_API_URL, err.message)); + robot.logger.error(util.format('Failed to retrieve commands from "%s": %s', env.ST2_API_URL, err.message)); + if (err.status === 401 || err.message.includes('Unauthorized')) { + throw err; + } }); }; @@ -397,6 +399,9 @@ module.exports = function(robot) { source.onerror = function (err) { // TODO: squeeze a little bit more info out of evensource.js robot.logger.error('Stream error:', err); + if (err.status === 401) { + throw err; + } }; source.addEventListener('st2.announcement__chatops', function (e) { var data; diff --git a/tests/test-st2-unauthorized.js b/tests/test-st2-unauthorized.js new file mode 100644 index 0000000..b87c342 --- /dev/null +++ b/tests/test-st2-unauthorized.js @@ -0,0 +1,99 @@ +// Copyright 2019 Extreme Networks, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*jshint quotmark:false*/ +/*jshint -W030*/ +/*global describe, it, before, after*/ +'use strict'; + +var chai = require('chai'), + expect = chai.expect, + nock = require('nock'), + Robot = require('hubot/src/robot'); + +describe("auth with invalid st2 API key", function() { + var stop; + var robot = new Robot(null, "mock-adapter", true, "Hubot"); + var recordedError = null, + logs = [], + controlledLogger = function(msg) { logs.push(msg); }; + + robot.logger.error = controlledLogger; + robot.logger.warning = controlledLogger; + robot.logger.info = controlledLogger; + robot.logger.debug = controlledLogger; + + + before(function(done) { + process.env.ST2_API_KEY = 'aaaa'; + + // emulate ST2 API response + nock('http://localhost:9101') + .get('/v1/actionalias') + .query({"limit":"-1","offset":"0"}) + .reply(401, {"faultstring":"Unauthorized - ApiKey with key_hash=123 not found."}); + + // emulate ST2 STREAM response + nock('http://localhost:9102') + .get('/v1/stream') + .query({"st2-api-key":"aaa"}) + .reply(401, ""); + + // hack to detect uncaught exceptions + var originalException = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', originalException); + process.prependOnceListener('uncaughtException', function (error) { + process.listeners('uncaughtException').push(originalException); + recordedError = error; + //done(); + }); + + var stackstorm = require("../scripts/stackstorm.js"); + stackstorm(robot).then(function (result) { + stop = result; + done(); + }); + robot.run(); + }); + + after(function() { + stop && stop(); + robot.server.close(); + robot.shutdown(); + // Remove stackstorm.js from the require cache + // https://medium.com/@gattermeier/invalidate-node-js-require-cache-c2989af8f8b0 + delete require.cache[require.resolve("../scripts/stackstorm.js")]; + }); + + + it("is using ST2_API_KEY as authentication", function () { + // debug, if needed + //console.log(logs); + expect(logs).to.contain('Using ST2_API_KEY as authentication. Expiry will lead to bot exit.'); + }); + + it("fails to retrieve the commands from API", function () { + expect(logs).to.include('Failed to retrieve commands from "http://localhost:9101": Unauthorized - ApiKey with key_hash=123 not found.'); + }); + + it("throws an 'Unauthorized' error", function () { + expect(JSON.stringify(recordedError)).to.be.equal( + '{"name":"APIError","status":401,"message":"Unauthorized - ApiKey with key_hash=123 not found."}' + ); + }); + + it("leads to Hubot shutdown", function () { + expect(logs).to.contain('Hubot will shut down ...'); + }); +});