UNPKG

hubot-stackstorm-auth

Version:

A hubot plugin for integrating with StackStorm event-driven infrastructure automation platform with role based auth.

235 lines (198 loc) 7.33 kB
"use strict"; var _ = require('lodash'); var util = require('util'); var env = _.clone(process.env); var Promise = require('rsvp').Promise; var utils = require('./utils.js'); var st2client = require('st2client'); var EventEmitter = require('events').EventEmitter; // Setup the Environment env.ST2_API = env.ST2_API || 'http://localhost:9101'; env.ST2_ROUTE = env.ST2_ROUTE || null; // Optional authentication info env.ST2_AUTH_USERNAME = env.ST2_AUTH_USERNAME || null; env.ST2_AUTH_PASSWORD = env.ST2_AUTH_PASSWORD || null; // Optional authentication token env.ST2_AUTH_TOKEN = env.ST2_AUTH_TOKEN || null; // Optional API key env.ST2_API_KEY = env.ST2_API_KEY || null; // Optional, if not provided, we infer it from the API URL env.ST2_AUTH_URL = env.ST2_AUTH_URL || null; env.ST2_ALIAS_PACK_RESTRICTION = env.ST2_ALIAS_PACK_RESTRICTION || null; var START_MESSAGES = [ "I'll take it from here! Your execution ID for reference is %s", "Got it! Remember %s as your execution ID", "I'm on it! Your execution ID is %s", "Let me get right on that. Remember %s as your execution ID", "Always something with you. :) I'll take care of that. Your ID is %s", "I have it covered. Your execution ID is %s", "Let me start up the machine! Your execution ID is %s", "I'll throw that task in the oven and get cookin'! Your execution ID is %s", "Want me to take that off your hand? You got it! Don't forget your execution ID: %s", "River Tam will get it done with her psychic powers. Your execution ID is %s" ]; var ERROR_MESSAGES = [ "I'm sorry, Dave. I'm afraid I can't do that. {~} %s" ]; function StackStormApi(logger) { var self = this; self.logger = logger; var url = utils.parseUrl(env.ST2_API); var opts = { protocol: url.protocol, host: url.hostname, port: url.port, prefix: url.path, rejectUnauthorized: false }; self.api = st2client(opts); if (env.ST2_API_KEY) { self.api.setKey({ key: env.ST2_API_KEY }); } else if (env.ST2_AUTH_TOKEN) { self.api.setToken({ token: env.ST2_AUTH_TOKEN }); } if (env.ST2_API_KEY || env.ST2_AUTH_TOKEN || env.ST2_AUTH_USERNAME || env.ST2_AUTH_PASSWORD) { // If using username and password then all are required. if ((env.ST2_AUTH_USERNAME || env.ST2_AUTH_PASSWORD) && !(env.ST2_AUTH_USERNAME && env.ST2_AUTH_PASSWORD && env.ST2_AUTH_URL)) { throw new Error('Env variables ST2_AUTH_USERNAME, ST2_AUTH_PASSWORD and ST2_AUTH_URL should only be used together.'); } } EventEmitter.call(this); } util.inherits(StackStormApi, EventEmitter); StackStormApi.prototype.startListener = function start() { var self = this; return self.api.stream.listen() .catch(function (err) { self.logger.error('Unable to connect to stream:', err); }) .then(function (source) { source.onerror = function (err) { // TODO: squeeze a little bit more info out of evensource.js self.logger.error('Stream error:', err); }; source.addEventListener('st2.announcement__chatops', function (e) { var data; self.logger.debug('Chatops message received:', e.data); if (e.data) { data = JSON.parse(e.data).payload; } else { data = e.data; } self.emit('st2.chatops_announcement', data); }); return source; }) }; StackStormApi.prototype.getAliases = function () { var self = this; self.logger.info('Getting Action Aliases....'); return self.api.actionAlias.list() .then(function (aliases) { if (env.ST2_ALIAS_PACK_RESTRICTION) { self.logger.info('Alias Restrictions are in place, only retrieving aliases from the following pack(s): ' + env.ST2_ALIAS_PACK_RESTRICTION); var return_aliases = []; var restrictions = env.ST2_ALIAS_PACK_RESTRICTION.split(','); _.each(aliases, function(alias) { if (restrictions.indexOf(alias.pack) > -1) { return_aliases.push(alias); } }); return return_aliases; } else { return aliases; } }) .catch(function (err) { var error_msg = 'Failed to retrieve commands from "%s": %s'; self.logger.error(util.format(error_msg, env.ST2_API, err.message)); return []; }); }; StackStormApi.prototype.sendAck = function (msg, res) { var history_url = utils.getExecutionHistoryUrl(res.execution); var history = history_url ? util.format(' (details available at %s)', history_url) : ''; if (res.actionalias && res.actionalias.ack) { if (res.actionalias.ack.enabled === false) { return; } else if (res.actionalias.ack.append_url === false) { history = ''; } } if (res.message) { return msg.send(res.message + history); } var message = util.format(_.sample(START_MESSAGES), res.execution.id); return msg.send(message + history); }; // TODO: decouple the msg object from stackstorm api, this should use an event emitter StackStormApi.prototype.executeCommand = function (msg, alias_name, format_string, command, addressee) { var self = this; var payload = { 'name': alias_name, 'format': format_string, 'command': command, 'user': addressee.name, 'source_channel': addressee.room, 'notification_route': env.ST2_ROUTE || 'hubot' }; self.logger.debug('Sending command payload:', JSON.stringify(payload)); return self.api.aliasExecution.create(payload) .then(function (res) { self.sendAck(msg, res); }) .catch(function (err) { if (err.status === 200) { return self.sendAck(msg, { execution: { id: err.message } }); } self.logger.error('Failed to create an alias execution:', err); var message = util.format(_.sample(ERROR_MESSAGES), err.message); if (err.requestId) { message = util.format( message, util.format('; Use request ID %s to grep st2 api logs.', err.requestId)); } self.emit('st2.execution_error', { name: alias_name, format_string: format_string, message: message, addressee: addressee, command: command }); throw err; }); }; StackStormApi.prototype.authenticate = function authenticate() { var self = this; self.api.removeListener('expiry', authenticate); // API key gets precedence 1 if (env.ST2_API_KEY) { self.logger.info('Using ST2_API_KEY as authentication. Expiry will lead to bot exit.'); return Promise.resolve(); } // Auth token gets precedence 2 if (env.ST2_AUTH_TOKEN) { self.logger.info('Using ST2_AUTH_TOKEN as authentication. Expiry will lead to bot exit.'); return Promise.resolve(); } self.logger.info('Requesting a token...'); var url = utils.parseUrl(env.ST2_AUTH_URL); var client = st2client({ auth: { protocol: url.protocol, host: url.hostname, port: url.port, prefix: url.path } }); return client.authenticate(env.ST2_AUTH_USERNAME, env.ST2_AUTH_PASSWORD) .then(function (token) { self.logger.info('Token received. Expiring ' + token.expiry); self.api.setToken(token); client.on('expiry', self.authenticate); }) .catch(function (err) { self.logger.error('Failed to authenticate: ' + err.message); throw err; }); }; module.exports = StackStormApi;