From ddd05197030ee6cbd6fd08d4e27a7feab6eb7983 Mon Sep 17 00:00:00 2001 From: Watts Martin Date: Wed, 15 Jun 2016 16:22:45 -0700 Subject: [PATCH] Closes #77; closes #45 Squashed commit of the following: commit f39e8e5bca3393d12b6dbfa5f5072d13673dd4e4 Author: Watts Martin Date: Wed Jun 15 16:03:18 2016 -0700 Update embed docs with new example commit 768597121bd2dfddb10ffaff9ad693fff7ce6553 Author: Watts Martin Date: Wed Jun 15 15:41:24 2016 -0700 Updates based on review commit e8d494dd895865fcf82677d62e41f0d375e31de3 Author: Watts Martin Date: Wed Jun 15 11:36:21 2016 -0700 Link to docs for setting up oauth endpoints in server object commit 83c4387a68cbcb40c151c608d95f1c647be59582 Author: Watts Martin Date: Wed Jun 15 11:35:31 2016 -0700 Update CLI docs; remove scaling section commit 6683d4b2f7c41167d116426b6d2384f32278395c Author: Watts Martin Date: Wed Jun 15 11:35:11 2016 -0700 Rearrange examples file commit d512033c64850bc841b0c0134e2dc5fb64108e3a Author: Watts Martin Date: Wed Jun 15 11:34:47 2016 -0700 Draft frameworks integration document (and rename file) commit 55870a6512e4273f39ccdfb8ddab35e5bf9519f2 Author: Watts Martin Date: Tue Jun 14 14:58:33 2016 -0700 Document hz make-token; closes #45 commit fba1396d7bcca627269473685c04a28b6bbc58aa Author: Watts Martin Date: Tue Jun 14 14:15:50 2016 -0700 Integration document placeholder commit 5dddbbd223278c80946ab2ae31b9e7b8d021a702 Author: Watts Martin Date: Tue Jun 14 14:14:33 2016 -0700 Reorg permissions docs slightly commit b8493a58e8e59de804292366ba2a1b4326489cde Author: Watts Martin Date: Tue Jun 14 14:14:10 2016 -0700 Typo fix commit 56b8fe32057cafd59312fcb717fe69b23800f78e Author: Watts Martin Date: Tue Jun 14 14:13:36 2016 -0700 Start CLI documentation --- authentication.md | 8 ++- server.md => cli.md | 63 ++++++++++++++++------ embed.md | 127 ++++++++++++++++++++++++++++++++++++++++++++ examples.md | 32 ++++++++--- index.md | 3 ++ permissions.md | 42 ++++++++++++++- 6 files changed, 248 insertions(+), 27 deletions(-) rename server.md => cli.md (65%) create mode 100644 embed.md diff --git a/authentication.md b/authentication.md index 28f6310..d01ccf8 100644 --- a/authentication.md +++ b/authentication.md @@ -40,7 +40,7 @@ const horizon = Horizon({ authType: 'anonymous' }); In effect, this authentication type creates a "temporary user" for use with the current session. This allows user information to be saved while that session is active, but the user has no way of reauthenticating with the same account if the token is lost. (Note that the temporary user ID is stored in the Horizon database, and must be cleaned up manually.) -# Using OAuth +# Using OAuth {#oauth} Your application will need a client ID and "secret" for each OAuth provider you want to connect with. The providers Horizon currently supports are: @@ -55,7 +55,7 @@ Each provider will let you register your application, and will give you the clie ## Configuring the server -In order to use OAuth with Horizon, you'll need to configure a TLS certificate so you can serve assets with HTTPS, and either specify the `--key-file` and `--cert-file` options to `hz serve` or add them to the server's `.hz/config.toml` file. (See [The config.toml file][cf] for more details.) You can create a self-signed certificate with `hz create-cert` +In order to use OAuth with Horizon, you'll need to configure a TLS certificate so you can serve assets with HTTPS, and either specify the `--key-file` and `--cert-file` options to `hz serve` or add them to the server's `.hz/config.toml` file. (See [The config.toml file][cf] for more details.) You can create a self-signed certificate with `hz create-cert`. [cf]: /docs/configuration @@ -89,6 +89,10 @@ Verify the configuration by running `hz serve` and browsing to `https://localhos If instead you only see empty brackets (e.g., `{ }`), ensure you've restarted the Horizon server, and that it's using the `.hz/config.toml` file you've edited. +**Note:** If your application embeds Horizon rather than using `hz serve`, you'll need to pass the OAuth endpoint to that object. Read "Configuring OAuth providers" in [Embedding Horizon][eh] for details. + +[eh]: /docs/embed + ## Configuring the client application Use `authType: 'token'` when initializing Horizon in your client, and then use the `authEndpoint()` command to retrieve the endpoint for your OAuth identity provider and redirect the user to that URL. diff --git a/server.md b/cli.md similarity index 65% rename from server.md rename to cli.md index 27077e4..ab445c5 100644 --- a/server.md +++ b/cli.md @@ -1,32 +1,47 @@ --- layout: documentation -title: Running the Horizon server -id: server -permalink: /docs/server/ +title: The Horizon CLI +id: cli +permalink: /docs/cli/ --- +The `hz` command line tool's primary function is to start a standalone Horizon server. It also provides utilities for initializing a new Horizon project, creating a self-signed SSL certificate, and other tasks. + +**Commands** + +* Table of Contents +{:toc} + +# init + +Initialize a new Horizon project. `hz init` with no argument will initialize a project in the current directory; `hz init ` will initialize a project in the specified directory, creating the directory if necessary. The project will be given the name of the specified directory (the current directory if no argument is given). + +# serve + The `hz serve ` command starts a Horizon server for the given Horizon project. Once started, it serves HTTP(S) requests for your application on the configured port. -Every Horizon server requires a RethinkDB server to connect to. Use the `--connect ` option to connect to an existing RethinkDB server, or use `--start-rethinkdb` to automatically start a local RethinkDB server. +Every Horizon server requires a RethinkDB server to connect to. Use the `--connect ` option to connect to an existing RethinkDB server, or use `--start-rethinkdb` to automatically start a local RethinkDB server. (Note that the `--dev` option for development mode includes `--start-rethinkdb` by default.) -# Command-line options {#options} +## Command-line options {#serve-options} `hz serve` supports the following command-line options: -## General options -* `--project-name NAME, -n NAME` Name of the Horizon project. Determines the name of the RethinkDB database that stores the project data. Default: Last component of the project path +### General options + +* `--project-name NAME, -n NAME` Name of the Horizon project. Determines the name of the RethinkDB database that stores the project data. * `--serve-static [PATH]` Enable serving static files via HTTP(S). You can additionally specify the path from which static files will be served (default: `./dist`). * `--config PATH` Which [config file][config-file] to use. Default: `.hz/config.toml` * `--debug [yes|no]` Print additional debug output. Default: `no` -## Network options +### Network options + * `--bind HOST, -b HOST` The host name or IP address that the Horizon server should listen on for incoming requests. Can be specified multiple times to bind to multiple addresses. Default: `localhost` * `--port PORT, -p PORT` The port number the Horizon server should listen on for incoming requests. Default: `8181` * `--connect HOST:PORT, -c HOST:PORT` The host and port of the RethinkDB server to connect to. Default: `localhost:28015` * `--key-file PATH` The key file to use for the HTTPS server. Default: `./horizon-key.pem` * `--cert-file PATH` The certificate to use for the HTTPS server. Default: `./horizon-cert.pem` -## Authentication options +### Authentication options * `--token-secret SECRET` A key string for signing JWTs. If not specified, a new random secret is used on each server start. * `--allow-unauthenticated [yes|no]` Allow unauthenticated users. See [Authentication][auth] for details. Default: `no` @@ -34,7 +49,7 @@ Every Horizon server requires a RethinkDB server to connect to. Use the `--conne * `--auth PROVIDER,ID,SECRET` Enable an auth provider with the given options. E.g. `facebook,ID,SECRET`. See [Authentication][auth] for details. * `--auth-redirect URL` The URL to redirect to upon completing authentication. Default: `/` -## Development options +### Development options * `--dev` Runs the server in [development mode](#development-mode). * `--secure [yes|no]` Serve websockets and files over encrypted (HTTPS) connections. Ignores the `--key-file` and `--cert-file` options if set to `no`. Default: `yes` @@ -47,7 +62,7 @@ Every Horizon server requires a RethinkDB server to connect to. Use the `--conne [config-file]: /docs/configuration [permissions]: /docs/permissions -# Development mode {#development-mode} +## Development mode {#development-mode} In development mode (`hz serve --dev`), the following flags are enabled by default: @@ -64,14 +79,28 @@ Development mode makes it easy to run a local Horizon instance during applicatio Development mode should never be enabled on a production server that is publicly accessible. An attacker can exploit a development server by sending unintended requests. These requests can be used to read and/or modify arbitrary data stored for your Horizon application, or can be used to exhaust the resources of your server. -# Scaling Horizon {#scaling} +# create-cert + +Create a private and public TLS certificate pair for development. Running this will create two files, `horizon-cert.pem` and `horizon-key.pem`. These can be specified as options to `hz serve` or placed in the [configuration file][config-file]. + +Note that the certificate created by `create-cert` uses no local identity information; the data is completely random. If you need to use an existing certificate or credentials, you'll have to create the certificate on your own using `openssl` or a similar tool. + +# get-schema + +Extract the currently defined Horizon schema, including validation rules, collection and index specifications, as a TOML file. For an example of this command in practice, read the section on "Configuring rules" in [Permissions and schema enforcement][perm]. + +[perm]: /permissions/#configuring + +Run `hz get-schema -h` for details on options. + +# set-schema -Horizon supports horizontal scalability. You can serve the same application from multiple Horizon servers across different machines or even data centers. +Load a previously-extracted schema into a Horizon cluster. Run `hz set-schema -h` for details on options. -Servers in a Horizon cluster access a common RethinkDB cluster in order to synchronize the state of your application's state and any internal metadata. +# make-token -To serve your application from multiple machines, simply copy the static application files to each machine. Then start one Horizon server per machine, using the `--connect ` option to connect to a common RethinkDB cluster. Make sure that the `--project-name` option is identical among all servers, or they will be unable to synchronize the application state. +Manually create a JSON Web Token for a user, allowing user bootstrapping. This is necessary to log in as the Horizon admin user the first time. -See the [RethinkDB documentation][rethinkdb-scaling] on information on how to horizontally scale a RethinkDB cluster. +For more details, read "Making an admin auth token" in [Permissions and schema enforcement][admin]. -[rethinkdb-scaling]: http://www.rethinkdb.com/docs/sharding-and-replication/ +[admin]: /permissions/#admin diff --git a/embed.md b/embed.md new file mode 100644 index 0000000..cc4649c --- /dev/null +++ b/embed.md @@ -0,0 +1,127 @@ +--- +layout: documentation +title: Embedding Horizon +id: embed +permalink: /docs/embed/ +--- + +While you can start the Horizon server from the `hz` [command line tool][cli], it's also possible to embed it into a Node web app by importing `@horizon/server` and passing a server connection to the Horizon constructor. + +[cli]: /docs/cli + +For instance, using the [Express][] framework, the steps are: + +[express]: http://expressjs.com + +```js +#!/usr/bin/env node +'use strict' + +const express = require('express'); +const horizon = require('@horizon/server'); + +const app = express(); +const http_server = app.listen(8181); +const options = { auth: { token_secret: 'my_super_secret_secret' } }; +const horizon_server = horizon(http_server, options); + +console.log('Listening on port 8181.'); +``` + +Express and Horizon are required, and Express is instantiated with `app.listen()`. Then the resulting `http_server` object is passed to `horizon` along with an option object. Options that can be passed to the Horizon server constructor are identical to the similarly-named options that can be defined in the [configuration file][cf], with the same defaults: + +* `project_name` +* `rdb_host`: `'localhost'` +* `rdb_port`: `28015` +* `auto_create_collection`: `false` +* `auto_create_index`: `false` +* `permissions`: `true` +* `path`: `'/horizon;` +* `auth`: + * `success_redirect`: `'/'` + * `failure_redirect`: `'/'` + * `duration`: `'1d'` + * `create_new_users`: `true` + * `new_user_group`: `'authenticated'` + * `token_secret`: `null` + * `allow_anonymous`: `false` + * `allow_unauthenticated`: `false` + +**Note:** Passing options to the constructor is the only way to configure the Horizon server when it's embedded. The `.hz/config.toml` configuration file will not be read. + +[cf]: /docs/configuration + +For some examples with other frameworks, including Koa and Hapi, consult the Horizon [examples page][ex]. + +[ex]: /docs/examples + +## Configuring OAuth providers + +OAuth endpoints cannot be set up through the options passed to the Horizon server constructor. Instead, you'll need to use the `add_auth_provider` method on the instantiated Horizon server object. + +```js +// ... initialization code as above for Express +const horizon_server = horizon(http_server, options); + +horizon_server.add_auth_provider( + horizon_instance.auth.github, + { id: 'id', secret: 'secret', path: 'github' } +); +``` + +For more details on setting up Oauth, read the section in [Authentication][a]. + +[a]: /docs/auth/#oauth + +## Attaching Horizon to multiple HTTP servers + +It's possible to pass a list of HTTP servers to the Horizon constructor rather than just one. Here's an example script that attaches Horizon to a public HTTPS server on port 8181 and a local HTTP server on port 8282: + +```js +'use strict'; +const horizon = require('@horizon/server'); + +const fs = require('fs'); +const http = require('http'); +const https = require('https'); + +// Attach the horizon server to two http servers +// one on [::]:8181 over HTTPS and one on 127.0.0.1:8282 over HTTP +const on_http_request = (req, res) => { + res.writeHead(404); + res.end('File not found.'); +}; + +const public_server = https.createServer({ + key: fs.readFileSync('key.pem'), + cert: fs.readFileSync('cert.pem'), +}, on_http_request); + +const loopback_server = http.createServer(on_http_request); + +public_server.listen(8181); +loopback_server.listen(8282, '127.0.0.1'); + +const horizon_server = horizon([public_server, loopback_server], { + project_name: 'foo', + auth: { + token_secret: 'bar', + allow_anonymous: true, + allow_unauthenticated: true, + }, +}); + +// Add Twitch authentication +horizon_server.add_auth_provider(horizon.auth.twitch, { + path: 'twitch', + id: '0000000000000000000000000000000', + secret: '0000000000000000000000000000000', +}); + +// Shut down the server after 60 seconds +setTimeout(() => { + horizon_server.close(); + public_server.close(); + loopback_server.close(); +}, 60000); +``` diff --git a/examples.md b/examples.md index 0eda899..9d02846 100644 --- a/examples.md +++ b/examples.md @@ -10,10 +10,28 @@ example applications to help you get started. -* [Horizon Repo Examples Directory](https://github.com/rethinkdb/horizon/tree/next/examples) - * [CycleJS Chat App](https://github.com/rethinkdb/horizon/tree/next/examples/cyclejs-chat-app) - * [RiotJS Chat App](https://github.com/rethinkdb/horizon/tree/next/examples/riotjs-chat-app) - * [React Chat App](https://github.com/rethinkdb/horizon/tree/next/examples/react-chat-app) - * [React TodoMVC App](https://github.com/rethinkdb/horizon/tree/next/examples/react-todo-app) - * [Vue Chat App](https://github.com/rethinkdb/horizon/tree/next/examples/vue-chat-app) - * [Vue TodoMVC App](https://github.com/rethinkdb/horizon/tree/next/examples/vue-todo-app) +* Server-side frameworks: + * [Express][e] + * [Koa][k] + * [Hapi][h] +* React: + * [Chat app][4] + * [Todo MVC app][5] +* Vue: + * [Chat app][6] + * [Todo MVC app][7] +* CycleJS: + * [Chat app][2] +* RiotJS: + * [Chat app][3] + + +[e]: https://github.com/rethinkdb/horizon/tree/next/examples/express-server +[k]: https://github.com/rethinkdb/horizon/tree/next/examples/koa-server +[h]: https://github.com/rethinkdb/horizon/tree/next/examples/hapi-server +[2]: https://github.com/rethinkdb/horizon/tree/next/examples/cyclejs-chat-app +[3]: https://github.com/rethinkdb/horizon/tree/next/examples/riotjs-chat-app +[4]: https://github.com/rethinkdb/horizon/tree/next/examples/react-chat-app +[5]: https://github.com/rethinkdb/horizon/tree/next/examples/react-todo-app +[6]: https://github.com/rethinkdb/horizon/tree/next/examples/vue-chat-app +[7]: https://github.com/rethinkdb/horizon/tree/next/examples/vue-todo-app diff --git a/index.md b/index.md index 5fdf09a..f037c04 100644 --- a/index.md +++ b/index.md @@ -23,4 +23,7 @@ Horizon consists of three components: * [Permissions](/docs/permissions): how Horizon's permissions and schema enforcement system works. * [Users and groups](/docs/users/): an overview of Horizon's user management system. * [Authentication](/docs/auth): integrating Horizon apps with Github, Twitter and other OAuth providers. +* Running the Horizon server: + * [CLI](/docs/cli): running the `hz` command line tool. + * [Embedding](/docs/embed): using Horizon with web frameworks like Express and Koa. * [Configuration](/docs/configuration): all about the Horizon configuration file, `.hz/config.toml`. diff --git a/permissions.md b/permissions.md index fd984e5..f86404e 100644 --- a/permissions.md +++ b/permissions.md @@ -5,6 +5,11 @@ id: permissions permalink: /docs/permissions/ --- +* Table of Contents +{:toc} + +# The whitelist + Horizon's permission system is based on a query whitelist. Any operation on a Horizon collection is disallowed by default, unless there is a rule that allows the operation. A whitelist rule has three properties that define which operations it covers: @@ -31,7 +36,6 @@ template = "collection('messages').anyWrite()" These rules would allow users in the `authenticated` group complete read and write access to the "messages" collection. Much finer-grained control is possible; read on for more information. - For example the following rule allows authenticated users to read their own messages from the `messages` collection: ```toml @@ -350,3 +354,39 @@ validator = """ ``` While there is no single rule that validates all results of the query, for each result there now is a matching rule for which the validator function passes. + +# Making an admin auth token {#admin} + +To log in as the admin user initially, your application will need to be bootstrapped using the [hz make-token](/cli/#make-token) command. + +From the directory of your Horizon application, stop the server if it's running. (If you haven't run it yet, you'll need to initialize the database; the easiest way to do that is to start in development mode with `hz serve --dev`, then stop the server.) Then run: + +```sh +hz make-token admin +``` + +A JSON Web Token will be printed to the console. Copy that token, and create a Horizon object with it: + +```js +var horizon = Horizon({ + authType: { + token: "", + storeLocally: false + } +}); +horizon.connect(); +``` + +(The `storeLocally` option controls whether the token should be preserved in the browser's local storage area; if you set it to `true`, you'll remain logged in as the admin from this browser.) + +The `make-token` command can be used to create a token for any user that exists in Horizon's user database. If you wished to manually create a token for a user with the ID value of '4C720BD1-2729-46BA-9213-ED84DEDE3120`, you can create the user first: + +```js +horizon('users').store({id: '4C720BD1-2729-46BA-9213-ED84DEDE3120'}); +``` + +And then get the token from the command line: + +```sh +hz make-token 4C720BD1-2729-46BA-9213-ED84DEDE3120 +```