Skip to content

Commit 58be75a

Browse files
author
Ben Lavender
authored
Merge pull request #20 from github/release-docs
Update docs for release
2 parents 7885540 + 4912665 commit 58be75a

3 files changed

Lines changed: 356 additions & 22 deletions

File tree

README.md

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
# Chatops Controller
22

3-
Rails helpers for easy, JSON-RPC based chatops.
3+
Rails helpers for easy and well-tested Chatops RPC. See the [protocol docs](docs/protocol-description.md)
4+
for background information on Chatops RPC.
45

56
A minimal controller example:
67

78
```ruby
89
class ChatopsController < ApplicationController
910
include ::Chatops::Controller
1011

12+
# The default chatops RPC prefix. Clients may replace this.
1113
chatops_namespace :echo
1214

1315
chatop :echo,
14-
/echo (?<text>.*)?/,
15-
"echo <text> - Echo some text back" do
16+
/(?<text>.*)?/,
17+
"<text> - Echo some text back" do
1618
jsonrpc_success "Echoing back to you: #{jsonrpc_params[:text]}"
1719
end
1820
end
@@ -27,30 +29,52 @@ Rails.application.routes.draw do
2729
end
2830
```
2931

32+
It's easy to test:
33+
34+
```
35+
class MyControllerTestCase < ActionController::TestCase
36+
include Chatops::Controller::TestCaseHelpers
37+
before do
38+
chatops_prefix "echo"
39+
chatops_auth!
40+
end
41+
42+
def test_it_works
43+
chat "echo foo bar baz"
44+
assert_equal "foo bar baz", chatop_response
45+
end
46+
end
47+
```
48+
3049
Before you deploy, add the RPC authentication tokens to your app's environment,
3150
below.
3251

33-
You're all done. Try `.echo foo`, and you should see Hubot respond with `Echoing back to you: foo`.
52+
You're all done. Try `.echo foo`, and you should see your client respond with
53+
`Echoing back to you: foo`.
54+
55+
A hubot client implementation is available at
56+
<https://github.com/hubot-scripts/hubot-chatops-rpc>
3457

3558
## Usage
3659

3760
#### Namespaces
3861

3962
Every chatops controller has a namespace. All commands associated with this
40-
controller will be displayed with `.help <namespace>`. Commands will also be
41-
dispalyed with subsets of their text.
63+
controller will be displayed with `.<namespace>` in chat. The namespace is a
64+
default chatops RPC prefix and may be overridden by a client.
4265

4366
```
4467
chatops_namespace :foo
4568
```
69+
4670
#### Creating Chatops
4771

4872
Creating a chatop is a DSL:
4973

5074
```ruby
5175
chatop :echo,
52-
/echo (?<text>.*)?/,
53-
"echo <text> - Echo some text back" do
76+
/(?<text>.*)?/,
77+
"<text> - Echo some text back" do
5478
jsonrpc_success "Echoing back to you: #{jsonrpc_params[:text]}"
5579
end
5680
```
@@ -61,12 +85,12 @@ captures](http://ruby-doc.org/core-1.9.3/Regexp.html#method-i-named_captures).
6185
In this example, only one capture group is available, `text`.
6286

6387
The next line is a string, which is a single line of help that will be displayed
64-
in chat for `.help echo`.
88+
in chat for `.echo`.
6589

6690
The DSL takes a block, which is the code that will run when the chat robot sees
6791
this regex. Arguments will be available in the `params` hash. `params[:user]`
68-
and `params[:room_id]` are special, and will be set by hubot. `user` will always
69-
be the login of the user typing the command, and `room_id` will be where
92+
and `params[:room_id]` are special, and will be set by the client. `user` will
93+
always be the login of the user typing the command, and `room_id` will be where
7094
it was typed.
7195

7296
You can return `jsonrpc_success` with a string to return text to chat. If you
@@ -83,24 +107,22 @@ Authentication uses the Chatops v3 public key signing protocol. You'll need
83107
two environment variables to use this protocol:
84108

85109
`CHATOPS_AUTH_PUBLIC_KEY` is the public key of your chatops client in PEM
86-
format. This environment variable will contain newlines.
110+
format. This environment variable will be the contents of a `.pub` file,
111+
newlines and all.
87112

88113
`CHATOPS_AUTH_BASE_URL` is the base URL of your server as the chatops client
89114
sees it. This is specified as an environment variable since rails will trust
90-
client headers about forwarded host. For example, if your chatops client has
91-
added the url `https://example.com/_chatops`, you'd set this to
115+
client headers about a forwarded hostname. For example, if your chatops client
116+
has added the url `https://example.com/_chatops`, you'd set this to
92117
`https://example.com`.
93118

94-
You can also set `CHATOPS_AUTH_ALT_PUBLIC_KEY` to a second public key which
95-
will be accepted. This is helpful when rolling keys.
96-
97-
TODO: link to protocol docs.
119+
You can also optionally set `CHATOPS_AUTH_ALT_PUBLIC_KEY` to a second public key
120+
which will be accepted. This is helpful when rolling keys.
98121

99-
## Staging
122+
## Rails compatibility
100123

101-
Use `.rpc set suffix https://myapp.example.com/_chatops in staging`, and all
102-
your chatops will require the suffix `in staging`. This means you can do `.echo
103-
foo` and `.echo foo in staging` to use two different servers to run `.echo foo`.
124+
This gem is intended to work with rails 4.x and 5.x. If you find a version
125+
with a problem, please report it in an issue.
104126

105127
## Development
106128

@@ -161,6 +183,9 @@ Becomes:
161183

162184
```ruby
163185
chat "build foobar"
186+
# or
187+
chatops_prefix "ci"
188+
chat "ci build foobar"
164189
```
165190

166191
##### Using public key authentication

docs/protocol-description.md

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
## Chatops RPC Protocol
2+
3+
CRPC is a client-server protocol; Hubot is a client. Servers expose an endpoint
4+
listing available methods. Each endpoint provides a regex to fire on and a
5+
relative URL path to execute it. CRPC is distilled from several years'
6+
experience with Chatops; see [the why](why.md) for some background.
7+
8+
Chatops RPC pushes a lot of complexity to clients. This is a design decision,
9+
intended to keep the burden of creating new chat commands in existing systems
10+
as low as possible.
11+
12+
## Listing Commands
13+
14+
A CRPC service listing is an endpoint that exposes JSON including the following
15+
fields:
16+
17+
* `namespace`: A globally unique namespace for these commands. Clients can use this to uniquely identify this endpoint. A namespace should be a slug of the form `/[a-Z0-9\-_]+/`.
18+
* `help`: **Optional:** Overall help for this namespace, if a client chooses to provide help
19+
* `error_response`: **Optional:** A message to present when this endpoint returns an error. This can direct users to next steps when the server fails.
20+
* `methods`: A mapping of named operations to their metadata.
21+
* `version`: The version of ChatOps RPC protocol to use, currently version 3
22+
23+
Each key in the `methods` hash will be a string name. Each name should be a
24+
slug of the form `/[a-Z0-9]\-_]+/`. Clients can use these method names to uniquely
25+
identify methods within a namespace. Each name shall point to an object with the
26+
following fields:
27+
28+
* `regex`: A string regular expression source used to execute the command. This regular expression should use named capture groups of the form `(?<parameter_name>.+)`.
29+
* `path`: A path, relative to the listing URL, to execute the command.
30+
* `params`: A list of available named parameters for this command.
31+
* `help`: **Optional:** User help for a given command.
32+
33+
Each server is assumed to be given a prefix, which the client will handle
34+
prepending to a command's regex source. Clients can use the `namespace` as a
35+
default prefix if they wish, but servers may not demand a particular prefix.
36+
Chatops RPC clients should require whitespace after the prefix, so a command with a
37+
regex like `/ping/` with a prefix of `test` would match on `test ping`.
38+
39+
## Executing Commands
40+
41+
CRPC clients use the listings to create a listing of available commands. When a
42+
chat message matches a command's regex matcher, the CRPC client creates a method
43+
invocation. A method invocation is a JSON object with the following fields:
44+
45+
* `user`: A slug username corresponding to to the command giver's GitHub login.
46+
* `room_id`: A slug room name where the command originated.
47+
* `method`: The method name, without namespace, of the matching regex.
48+
* `params`: A mapping of parameter names to matches extracted from named capture groups in the command's regex. Parameters that are empty or null should not be passed.
49+
50+
The JSON object is posted to the `path` associated with the command from the
51+
listing of commands. CRPC servers should assume that parameters in the `params`
52+
hash are under user control, but trust that the `user` and `room_id` to be
53+
correct.
54+
55+
CRPC servers must produce a response JSON object with the following fields:
56+
57+
* `result`: A string to be displayed in the originating chat room.
58+
59+
CRPC may optionally include the following fields in a response JSON object for
60+
use in situations where richer results can be displayed. Clients will optionally
61+
utilize some or all of the extra information to provide an enhanced response,
62+
but it is important that `result` be sufficient on its own.
63+
64+
* `title`: The title text for the response
65+
* `title_link`: Optional URL to link the title text to
66+
* `color`: Hex color for the message, to indicate status/group e.g. "ddeeaa'
67+
* `buttons`: An array of button objects
68+
* `label`: The text to display on the button
69+
* `image_url`: An image URL to display as the button, will generally take precedence
70+
* `command`: The command to use when the button is clicked
71+
* `image_url`: An image URL to be included with the response
72+
73+
CRPC may also produce error JSON according to the JSON-RPC spec, consisting of
74+
an object containing an `error` object with a `message` string. This is
75+
sometimes helpful for clients that make a distinction between failed and
76+
successful commands, such as a terminal. CRPC point of view. CRPC clients should
77+
still parse these error messages.
78+
79+
## Examples
80+
81+
Here is an end-to-end transaction, sans authentication (see below):
82+
83+
CRPC client issues:
84+
```
85+
GET /_chatops HTTP/1.1
86+
Accept: application/json
87+
88+
{
89+
"namespace": "deploy",
90+
"help": null,
91+
"version": 3,
92+
"error_response": "The server had an unexpected error. More information is perhaps available in the [error tracker](https://example.com)"
93+
"methods": {
94+
"options": {
95+
"help": "hubot deploy options <app> - List available environments for <app>",
96+
"regex": "options(?: (?<app>\\S+))?",
97+
"params": [
98+
"app"
99+
],
100+
"path": "wcid"
101+
}
102+
}
103+
}
104+
```
105+
106+
The client will use the suggested `namespace` as a prefix, `deploy`. Thus, when
107+
the client receives a command matching `.deploy options hubot`, the CRPC client
108+
issues:
109+
110+
```
111+
POST /_chatops/wcid HTTP/1.1
112+
Accept: application/json
113+
Content-type: application/json
114+
Content-length: 77
115+
116+
{"user":"bhuga","method":"wcid","params":{"app": "hubot"},"room_id":"developer-experience"}
117+
```
118+
119+
The CRPC server should respond with output like the following:
120+
121+
```
122+
{"result":"Hubot is unlocked in production, you're free to deploy.\nHubot is unlocked in staging, you're free to deploy.\n"}
123+
```
124+
125+
The CRPC client should output "Hubot is unlocked in production, you're free to
126+
deploy.\nHubot is unlocked in staging, you're free to deploy.\n" to the chat
127+
room. The client can optionally display the output intelligently if it contains
128+
newlines, links in formats like markdown, etc. It's strongly recommended that
129+
a client support markdown links if possible.
130+
131+
## Authentication
132+
133+
#### Authenticating clients
134+
135+
Clients authenticate themselves to servers by signing requests with RS256
136+
using a private key. Servers have a public key associated with clients and
137+
verify the signature with it.
138+
139+
By convention, a CRPC server should allow authentication with two secrets
140+
simultaneously to allow seamless token rolling.
141+
142+
Clients send three additional HTTP headers for authentication: `Chatops-Nonce`,
143+
`Chatops-timestamp`, and `Chatops-Signature`.
144+
145+
* `Chatops-Nonce`: A random, base64-encoded string unique to every chatops
146+
request. Servers can cache seen nonces and refuse to execute them a second time.
147+
* `Chatops-Timestamp`: An ISO 8601 time signature in UTC, such as
148+
`2017-05-11T19:15:23Z`.
149+
* `Chatops-Signature`: The signature for this request.
150+
151+
The value to be signed is formed by concatenating the value of the full http path,
152+
followed by a newline character, followed by the contents of the nonce
153+
header, followed by a newline character, followed by the value of the timestamp header,
154+
followed by a newline character, followed by the entire HTTP post body, if any. For example,
155+
for a `GET` request with these headers:
156+
157+
```
158+
Chatops-Nonce: abc123
159+
Chatops-Timestamp: 2017-05-11T19:15:23Z
160+
```
161+
162+
Sent to the following URL:
163+
164+
`https://example.com/_chatops`
165+
166+
The string to be signed is:
167+
`https://example.com/_chatops\nabc123\n2017-05-11T19:15:23Z\n`
168+
169+
For a request with the same headers and a POST body of `{"method": "foo"}`, the
170+
string to be signed is:
171+
172+
`https://example.com/_chatops\nabc123.2017-05-11T19:15:23Z\n{"method": "foo"}`
173+
174+
The signature header starts with the word `Signature`, followed by whitespace,
175+
followed by comma-separated key-value pairs separated by an `=`. Each
176+
value is closed with double quotes. Keys must be all lowercase.
177+
178+
* `keyid`: An implementation-specific key identifier that servers can use to
179+
determine which private key signed this request.
180+
* `signature`: The base64-encoded RSA-SHA256 signature of the signing string.
181+
182+
An example signature header would be:
183+
184+
`Chatops-Signature: Signature keyid="rsakey1",signature="<base64-encoded-signature>"`
185+
186+
#### Authentication
187+
188+
CRPC must trust that a user is authenticated by the `user` parameter sent with
189+
every command. Individual servers may request a second authentication factor
190+
after receiving a command; this is beyond the scope of CRPC.
191+
192+
#### Authorization
193+
194+
CRPC servers are responsible for ensuring that the given `user` has the proper
195+
authorization to perform an operation.
196+
197+
### Execution
198+
199+
Chatops RPC clients are expected to add a few niceties not covered by the wire
200+
protocol. This complexity is exported to clients to keep the burden of
201+
implementing new automation low.
202+
203+
* Regex anchoring. Clients should anchor regexes received from servers. If a
204+
command is exported as `where can i deploy`, it should not be triggered on
205+
`tell me where i can deploy` or `where can i deploy, i'm bored`.
206+
* Prefixing. Different execution contexts may prefix commands, such as `.`,
207+
`hubot`, or another sigil.
208+
* Help display systems. These are heavily context dependent. Servers provide
209+
text snippets about commands, but accessing and displaying them is up to the
210+
client.
211+
212+
These niceties are optional and context-dependent. Different clients may or may
213+
not implement them. But if any of these are required in any execution context,
214+
they should not be pushed to the server.
215+
216+
### Protocol Changes
217+
218+
The version of the ChatopsRPC protocol in use by a server is given as the
219+
`version` field. If no version is returned, `3` is assumed.

0 commit comments

Comments
 (0)