Security News
NVD Backlog Tops 20,000 CVEs Awaiting Analysis as NIST Prepares System Updates
NVD’s backlog surpasses 20,000 CVEs as analysis slows and NIST announces new system updates to address ongoing delays.
ac-koa-hipchat
Advanced tools
A Node.js and Koa.js-based library for building HipChat Connect add-ons, built up from lower-level libraries such as ac-node, ac-node-hipchat, and ac-koa.
This library is designed to make writing Node.js-based add-ons as simple as possible while improving on the design limitations of its predecessor, Atlassian Connect Express - HipChat.
This is an early, alpha-quality release, but can be used to build real add-ons today. Future versions may include backward-incompatible changes.
For a simple alternative to the following set up instructions, you may consider using the Vagrant starter project to get up and running quickly.
If you're already comfortable with Node.js, the main thing you need to know to get started is that Koa (and therefore this library) requires Node v0.11.x. Using nvm to manage multiple instances of Node is recommended. If you need more help with Node, nvm
, or npm
, please start with their public documentation.
By default, ac-koa-hipchat
expects Redis to be available, for the persistence of tenant installations. You can provide your own persistent store implementation if necessary, though that is a more advanced topic that will be treated elsewhere. See the ac-node library for the Redis store implementation and compatibility tests as a guide if you need a custom store, otherwise make sure you have Redis installed and running locally.
Writing basic HipChat add-ons with ac-koa-hipchat
requires very little code to get up and running. Here's an example of a simple yet complete add-on, in two files:
var ack = require('ac-koa').require('hipchat');
var pkg = require('./package.json');
var app = ack(pkg);
var addon = app.addon()
.hipchat()
.allowRoom(true)
.scopes('send_notification');
addon.webhook('room_message', /^\/hello$/, function *() {
yield this.roomClient.sendNotification('Hi, ' + this.sender.name + '!');
});
app.listen();
{
"name": "ac-koa-hipchat-greeter",
"displayName": "HipChat Greeter Example Add-on",
"description": "Greets people when they type /hello in chat",
"version": "0.1.0",
"author": {
"name": "Atlassian",
"url": "http://atlassian.com"
},
"license": "Apache 2.0",
"engines": {
"node": "~0.11.13"
},
"scripts": {
"web": "node --harmony web.js",
"web-dev": "nodemon --harmony -e js,css,hbs,html",
"tunnel": "ngrok 3000"
},
"development": {
"port": 3000
},
"production": {
"localBaseUrl": "https://hipchat-greeter-example.herokuapp.com",
"redisEnv": "REDISCLOUD_URL",
"port": "$PORT"
},
"dependencies": {
"ac-koa": "^0.2.3",
"ac-koa-hipchat": "^0.2.4"
}
}
To run this example yourself, add these files to a new directory and run the following commands there:
$ npm install
$ npm run web
If the server started as expected, you'll see something like the following emitted:
info: Atlassian Connect add-on started at http://hostname.local:3000
To double check that the server is running correctly, try requesting it's add-on descriptor:
$ curl http://hostname.local:3000/addon/capabilities
A successful request will return a HipChat capabilities descriptor for the add-on.
To get the server to restart automatically when code changes, nodemon is recommended, and can be installed by simply running the following:
$ npm install -g nodemon
Then you can run this command to start the server for development:
$ npm run web-dev
In the example above, nodemon
is set up to monitor js, json, css, and hbs (handlebars) files. If your add-on use additional file types, you may want to add them to the web-dev
script configuration in package.json
.
If you encounter errors while following these steps, double check that you're using Node v0.11.x and that all of the above dependencies installed correctly.
Now that you have a server running, you'll want to try it somehow. The next step is different depending on whether you're going to be developing with hipchat.com or a private HipChat instance being hosted behind your corporate firewall.
The easiest way to test with hipchat.com while developing on your local machine is to use ngrok. Download and install it now if you need to -- it's an amazing tool that will change the way you develop and share web applications.
You may also want to set up your own ngrok
account to access advanced features like being able to specify custom subdomains, but it's not necessary.
Now you can start a secure tunnel (in a new console) to your add-on that's accessible by the outside world, which will allow you to install it at hipchat.com while you test it:
$ npm run tunnel
That command will start ngrok and output the address of your new tunnel, which should look something like https://3a4bfceb.ngrok.com
. This will be the value you use for your "local base url" needed by the Installation step.
While ngrok will forward both HTTP and HTTPS, for the protection of you and your HipChat group members, you should always use HTTPS when running your add-on on the public internet.
To install your add-on on a private HipChat server, both the add-on server and HipChat server need to be able to connect to each other via HTTP or HTTPS on your local network. Simply determine an HTTP url that your HipChat server can use to connect to your locally running add-on, and use that as the value of your "local base url" needed by the Installation step.
If all goes well, you won't have to change anything from the defaults, as ac-koa-hipchat
will simply attempt to use the OS's hostname to build the local base url, which may already be good enough for your private network.
Now, we need to tell the add-on server where it's running so that it can successfully be installed. You can do this in one of the following ways, using the local base url determined in the prior step, appropriate to your environment:
LOCAL_BASE_URL
environment variable when you start the server:$ LOCAL_BASE_URL=https://xxxxxxxx.ngrok.com npm run web-dev
package.json
configuration in the development
section:"development": {
"localBaseUrl" : "https://xxxxxxxx.ngrok.com",
"port": 3000
},
The ac-koa-hipchat
library first looks for the configuration values it needs as environment variables, and then in the current runtime environment section of the configuration. If the local base url isn't not found in either location, it defaults to http://<hostname>:$PORT
, but when advertising that address it can't be properly installed with hipchat.com.
Which section of the configuration is used (e.g. production
, development
, test
, etc) depends on the value of the environment variable NODE_ENV
, whose value defaults to development
. When running your server on a PaaS such as Heroku, you'll want to set NODE_ENV=production
.
When properly configured, you'll see the server report the new local base url when it starts up:
info: Atlassian Connect add-on started at https://xxxxxxxx.ngrok.com
Note: by signing up for an ngrok account, you can specify a generally stable, custom subdomain for even easier iterative development. See ngrok for more information.
To install your add-on into HipChat, you have to register your addon's capabilities descriptor.
HipChat add-ons can operate inside a room or within the entire account. When developing, you should probably register your add-on inside a room you've created just for testing. Also, you can only register add-ons inside a room where you are an administrator.
To register your add-on descriptor, navigate to the rooms administration page at https://<your-account>.hipchat.com/rooms
(or whatever url your private server is running at, if appropriate). Then select one of your rooms in the list. In the following page, select Integrations
in the sidebar, and then click the "Build and install your own integration" link at the bottom of the page:
Paste your descriptor url in the Integration URL
field of the opened dialog and then click Add integration
. This will initiate the installation of your add-on for that room.
The example illustrated above comes from the following example project:
See these additional add-ons for more complete examples:
This library, in conjunction with it's product-agnostic base library ac-koa
, provides help with many aspects of add-on development, such as:
configurable
capability)In the documentation below, we use the terms ctx
and 'Koa context' interchangably to refer to the context object that contains both standard Koa request/response data and objects and this library's request objects and services.
A convenience method for creating a Koa app augmented with the ability to define add-ons is provided by the following boilerplate setup:
var ack = require('ac-koa').require('hipchat');
var pkg = require('./package.json');
var app = ack(pkg);
In this example, the app
object is the configured Koa application, ready to define one or more add-ons in following steps.
Add-ons can be defined on the app
object in two ways:
app.addon()
methodapp.addon({
// optional descriptor metas here (taken from package.json when not overridden here)
capabilities: {
// standard HipChat capabilities descriptor body
}
});
When using this style, you're responsible for manually configuring routes to handle any webhooks defined.
app.addon()
and use the more concise descriptor builder APIIf you pass nothing to the addon
method, a descriptor builder API will be applied that allows critical sections of the descriptor to be defined with builder methods. This is particularly useful for conveniently defining webhook handlers while also building the descriptor.
For example, the following defines a fully functional descriptor while also associating a webhook handler:
var addon = app.addon()
.hipchat()
.allowRoom(true)
.scopes('send_notification');
addon.webhook('room_message', /^\/hello$/, function *() {
yield this.roomClient.sendNotification('Hi, ' + this.sender.name + '!');
});
Any webhooks defined through the builder interface, as well as the automatically configured install
and uninstall
webhooks, generate events on the addon
object. To listen to webhook events with Koa-style generator functions, you can use the following API:
addon.onWebhook('install', function *() {
// use the koa context (this) to access request, response, tenant info, and tenant services normally
});
Sometimes, you won't know what webhooks you want to define at the time that the descriptor is defined or built. This may happen if your add-on creates webhooks in response to add-on configuration, for example. In this case, you can add or remove webhooks dynamically using the tenantWebhooks
service API available on any Koa add-on context.
this.tenantWebhooks.add('room_message', /^\/hello/i);
To handle this event, simply define a webhook listener on the addon
:
addon.onWebhook('room_message', function *() {
// handle the webhook, dispatching to an appropriate subroutine based on data in the query string and webhook body
});
More than one add-on can be mounted at a time in a single Koa app by giving each a unique mount scope. For example:
var addon1 = app.addon('addon1')
.hipchat()
.key('addon1-key')
.name('Addon 1')
.allowRoom(true)
.scopes('send_notification');
var addon2 = app.addon('addon2')
.hipchat()
.key('addon2-key')
.name('Addon 2')
.allowRoom(true)
.scopes('send_notification');
The scope given to each addon()
method is automatically used for both data partioning and route disambiguation.
One add-on can be installed with multiple HipChat OAuth2 clients, referred to here as 'tenants'. In practice, a tenant is either a HipChat room or group, depending on the installation scope of the add-on.
Tenant installation and uninstallation is handled automatically by the library, which configures the necessary routes and handlers for each mounted add-on. Each installation results in the registration information for that tenant to be verified and stored for later use, including the tenant's shared secret, used for bi-directional authentication.
Add-on implementations are given access to the tenant
information object in every Koa context for routes and webhook listeners in which this library is involved.
ctx.tenant
Field | Description |
---|---|
id | This tenant's id. |
group | This tenant's group id. |
secret | This tenant's shared secret. |
room | This tenant's room id, if installed in a single room. |
webhookToken | A secret token added to all dynamically generated webhooks, as an extra measure of security. |
links | A collection of this tenant's relevant URLs. |
links.capabilities | This tenant's capabilities descriptor URL. |
links.base | This tenant's base URL. |
links.api | This tenant's base API URL. |
links.token | This tenant's OAuth2 token generation URL. |
This library handles bi-direction authentication between tenants and add-ons. It provides the following facilities:
For add-on routes that provide web UI to the tenant, such as the one defined in an add-on's configurable
capabilitity, use the addon
object's authenticate()
middleware to protect your route. This middleware will then verify that requests have a valid JWT signature provided either as the signed_request
query parameter or in a standard HTTP Authorization
header with the format Authorization: JWT token=<jwt-token-value>
.
For example, one would secure an addon's /configure
route with this middleware as follows:
addon.get('/configure',
addon.authenticate(),
function *() {
// Normal Koa route handling here...
}
);
Requests successfully passing through this middleware will have the following object available on the Koa context:
ctx.authentication
Field | Description |
---|---|
issuer | The authenticated tenant's id. |
issued | The timestamp at which the token was generated. |
userId | The id of the user making the request. |
expiry | The time at which the token should be expired. |
context | An additional request context object sent by the tenant as part of the signed data. This may contain the current user's timezone in a field named tz . |
token | A refreshed version of the JWT token, suitable for use in subsequent requests over Ajax or as form or link parameters. See the Hearsay example add-on for a demonstration of this technique. We prefer this approach to maintaining state for iframed add-on UI over cookies due to some anti-click-jacking browser security models that prevent cookies from being set in cross-domain iframes. |
Outbound requests to the tenant's REST APIs require an current OAuth2 bearer token, which must be refreshed via the tenant's token API when it expires. As long as the add-on uses the ctx.tenantClient
or ctx.roomClient
APIs, this token management is handled automatically. See below for information about the these services.
For convenient add-on implementation, several service objects are attached to the Koa context that provide tenant-aware operations.
In order to support multiple tenants concurrently, a tenant-aware data storage abstraction is used throughout the library to partition tenant data, and a derivative of a tenant's unique storage object is provided with each Koa context, allowing add-ons access to a simple, partioned location to store basic key/value information. Storage of more structured data in alternative data stores is left as an exercise for each add-on, though every Koa context contains the full tenant data model, making such manual data partioning straightforward.
ctx.tenantStore
Method | Description |
---|---|
get(key) | Gets a value for a given key. Returns a promise. |
set(key, value) | Sets a value for a given key. Returns a promise. |
del(key) | Deletes a value for a given key. Returns a promise. |
all() | Gets all values in the current storage scope. Returns a promise. |
narrow(scope) | Creates and returns a substore narrowed by the given scope. |
A tenant REST client is provided that automatically handles the construction of authenticated requests for each of opertions available in the HipChat API v2.
ctx.tenantClient
A HipChat API v2 REST client API with automatic automatic Oauth2 token acquisition and refresh.
Method | Description |
---|---|
sendNotification(roomIdOrName, message, options) | Sends a notification message to a room by name or id. The optional options object may include any or all of color , notify , or format -- see the API docs for valid values. Returns a promise. |
createWebhook(roomIdOrName, definition) | Creates a new webhook in a specific room given a definition -- see the API docs for the definition fields. Returns a promise. |
deleteWebhook(roomIdOrName, webhookId) | Deletes an existing webhook by id in a specific room. Returns a promise. |
TODO | (Implement and document the remaining API methods) |
ctx.roomClient
A thin wrapper around the tenantClient
object that's provided in room-specific request contexts. Only the methods of the tenantClient
that require a room id are exposed on this object, with the room id already partially applied for convenience.
Method | Description |
---|---|
sendNotification(message, options) | Sends a notification message to the current room. The optional options object may include any or all of color , notify , or format -- see the API docs for valid values. Returns a promise. |
TODO | (Implement and document the remaining API methods) |
ctx.tenantWebhooks
A high-level API for adding or removing webhooks for a given tenant, in such a way that this library can manage any required routing, authentication, and dispatching necessary to provide a similar level of service as is enjoyed by statically defined webhooks (i.e. those defined as part of the capabilities decriptor when the addon is defined at server startup).
See the Hearsay example add-on for a functioning example of the webhook manager API.
Method | Description |
---|---|
get(name) | TODO |
get(roomId, name) | TODO |
add(event) | TODO |
add('room_message', pattern) | TODO |
add(definition) | TODO |
add(roomId, event) | TODO |
add(roomId, 'room_message', pattern) | TODO |
add(roomId, definition) | TODO |
remove(name) | TODO |
remove(roomId, name) | TODO |
Webhook listeners added via either addon.webhook(...)
(for webhooks defined using the add-on builder API convenience method) or addon.onWebhook(...)
(for dynamic or non-builder based webhook listeners) are provided with both raw and normalized views of the webhook body. Since not all webhooks organize their data the same way, extracted and normalized webhook fields are attached directly to the Koa context, along side the raw webhook object. Not all webhooks provide all fields and some of these fields are themselves complex objects, so consult the API docs for information about what to expect when.
Field | Description |
---|---|
ctx.webhook | The raw webhook payload. |
ctx.webhookId | The webhook id. |
ctx.room | The relevant room model object. |
ctx.sender | The relevant sender model object. |
ctx.message | The message object, if any. Only in room_notification and room_message webhooks. |
ctx.content | The message field of the message object, if any. Only in room_notification and room_message webhooks. |
ctx.topic | The room topic, if any. Only in room_topic_change webhooks. |
Here's a closer look at the original example add-on, with comments for illustration:
// Require the 'ac-koa' module, and then tell it to load the 'hipchat' adapter
// from 'ac-koa-hipchat'
var ack = require('ac-koa').require('hipchat');
// Require our package.json file, which doubles as the configuration from which
// we'll generate the add-on descriptor and server's runtime parameters
var pkg = require('./package.json');
// Create the base Koa app, via an 'ac-koa' factory method that helps preconfigure
// and decorate the app object
var app = ack(pkg);
// Now build and mount an AC add-on on the Koa app; we can either pass a full or
// partial descriptor object to the 'addon()' method, or when we provide none, as
// in this example, we can instead create the descriptor using a product-specific
// builder API
var addon = app.addon()
// Use the hipchat descriptor builder
.hipchat()
// Indicate that the descriptor should mark this as installable in rooms
.allowRoom(true)
// Provide the list of permissions scopes the add-on requires
.scopes('send_notification');
// Subscribe to the 'room_enter' webhook, and provide an event listener. Under
// the covers, this adds a webhook entry to the add-on descriptor, mounts a common
// webhook endpoint on the Koa app, and brokers webhook POST requests to the event
// listener as appropriate
addon.webhook('room_enter', function *() {
// 'this' is a Koa context object, containing standard Koa request and response
// contextual information as well as hipchat-specific models and services that
// make handling the webhook as simple as possible
yield this.roomClient.sendNotification('Hi, ' + this.sender.name + '!');
});
// Now that the descriptor has been defined along with a useful webhook handler,
// start the server
app.listen();
TODO
FAQs
A Koa.js library for building Atlassian Connect HipChat add-ons
The npm package ac-koa-hipchat receives a total of 46 weekly downloads. As such, ac-koa-hipchat popularity was classified as not popular.
We found that ac-koa-hipchat demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
NVD’s backlog surpasses 20,000 CVEs as analysis slows and NIST announces new system updates to address ongoing delays.
Security News
Research
A malicious npm package disguised as a WhatsApp client is exploiting authentication flows with a remote kill switch to exfiltrate data and destroy files.
Security News
PyPI now supports digital attestations, enhancing security and trust by allowing package maintainers to verify the authenticity of Python packages.