relations
=========

entity relationship, role, and permissions API for Node.js

[![build status](https://secure.travis-ci.org/carlos8f/node-relations.png)](http://travis-ci.org/carlos8f/node-relations)

**relations** is a simple permissions API which uses a natural language approach.

Contexts
--------

First, you'll create a **context**, which contains a list of roles which map to
actions. Here we'll create a context called `repos`, to model Github repositories.

```js
var relations = require('relations');

relations.define('repos', {
  owner: ['pull', 'push', 'administrate'],
  collaborator: ['pull', 'push'],
  watcher: ['pull']
});
```

Defining the context makes available a method on `relations` that matches the
context name, in this case, `relations.repos()`. For permission checks, this is
the only method we'll need to call.

### Dynamic roles

To add or modify roles at runtime, you can also use the following methods:

```js
// add a role dynamically
relations.repos.addRole('scientist', ['test', 'hyphothesize']);
// update the actions for a role
relations.repos.updateRole('scientist', ['test', 'hypothesize', 'absquatulate']);
// remove a role
relations.repos.removeRole('scientist');
```

**Please note** that the role -> action map is defined exclusively in the code,
and not stored. If you run a cluster of servers, and choose to use dynamic roles,
you must call `addRole()` etc on ALL servers in the cluster (I suggest using
pub/sub).

Declarations
------------

Now, we need to tell our app who has those roles for which repos.

```js
relations.repos('Carlos is the owner of buffet.');
```

This assigns the role `owner` to the subject `Carlos` for the object `buffet`.

### Token replacements

Note that the API has multiple syntaxes, and this is functionally equivalent:

```js
relations.repos(':user is owner of :repo', {user: 'Carlos', repo: 'buffet'});
```

As is this:

```js
relations.repos('%s is an owner of %s', 'Carlos', 'buffet');
```

To assign a role which should apply to all objects, simply leave the object out
of the sentence:

```js
relations.repos('%s is a watcher.', 'Brian');
```

**Note: Using token replacements is recommended, to prevent injection attacks!**

### Syntax

The syntax for a **declaration** consists of:

```
<subject> is [ a / an / the ] <role> [ [ of / to / from / in / with ] <object> ] [.]
```

Verb question
-------------

To ask if a user can perform an action:

```js
relations.repos('Can %s pull?', 'Brian', function (err, can) {
  // can = true (based on "watcher" role)
});
```

We can also check if an action can be performed on a specific object:

```js
relations.repos('Can %s push to buffet?', 'Brian', function (err, can) {
  // can = false (Brian doesn't have "owner" or "collaborator" roles)
});
```

### Syntax

The syntax for an **verb question** consists of:

```
( Can | can ) <subject> <verb> [ [ of / to / from / in / with ] <object> ] [?]
```

Role question
-------------

To check if a user has a role:

```js
relations.repos('Is %s a collaborator of %s?', 'Brian', 'buffet', function (err, is) {
  // is = false
});
```

We can also leave the object out to check for a global role:

```js
relations.repos('Is %s a %s?', 'Brian', 'watcher', function (err, is) {
  // is = true
});
```

### Syntax

The syntax for a **role question** consists of:

```
( Is | is ) <subject> [ a / an / the ] <role> [ [ of / to / from / in / with ] <object> ] [?]
```

Verb request
------------

In addition to true/false checks, **relations** can return an array of objects
which match certain criteria. For example:

```js
relations.repos('What can %s pull from?', 'Carlos', function (err, repos) {
  // repos = ['buffet']
});
```

### Syntax

The syntax for a **verb request** consists of:

```
( What | what ) can <subject> <verb> [ of / to / from / in / with ] [?]
```

Role request
------------

Also, we can ask for an array of objects a user has a role for:

```js
relations.repos('What is %s the owner of?', 'Carlos', function (err, repos) {
  // repos = ['buffet']
});
```

### Syntax

The syntax for a **role request** consists of:

```
( What | what ) is <subject> [ a / an / the ] <role> [ of / to / from / in / with ] [?]
```

Verb subject request
--------------------

To request an array of subjects who can perform an action on an object:

```js
relations.repos('Who can pull from %s?', 'buffet', function (err, users) {
  // users = ['Carlos']
});
```

### Syntax

```
( Who | who ) can <verb> [ of / to / from / in / with ] <object> [?]
```

Role subject request
--------------------

To request an array of subjects who have a role for an object:

```js
relations.repos('Who is the owner of %s?', 'buffet', function (err, users) {
  // users = ['Carlos']
});
```

### Syntax

```
( Who | who ) is [ a / an / the ] <role> [ of / to / from / in / with ] <object> [?]
```

Object verb request
-------------------

To request an array of verbs a subject can perform on an object:

```js
relations.repos('What actions can %s do with %s?', 'Carlos', 'buffet', function (err, verbs) {
  // verbs = ['pull', 'push', 'administrate']
});
```

### Syntax

```
What actions can <subject> do [ of / to / from / in / with ] <object> [?]
```

Object-Role map request
-----------------------

To get a map of object->role pairs for a subject:

```js
relations.repos('Describe what %s can do', 'Carlos', function (err, map) {
  // map = { '': [ 'watcher' ],
             'buffet': [ 'owner' ] }
});
```

### Syntax

```
[ Describe / detail / explain / get ] what <subject> can do [.]
```

Subject-Role map request
-----------------------

To get a map of subject->role pairs, optionally pertaining to an object:

```js
relations.repos('Get who can act', function (err, map) {
  // map = { 'carlos': [ 'watcher' ],
             'brian': [ 'watcher' ] }
});
```

```js
relations.repos('Explain who can act on %s', 'buffet', function (err, map) {
  // map = { 'carlos': [ 'owner' ] }
});
```

### Syntax

```
[ Describe / detail / explain / get ] who can act [ on <object> ] [.]
```

Revocation
----------

To revoke a role:

```js
relations.repos('%s is not the owner of %s', 'Carlos', 'buffet');
```

### Syntax

```
<subject> ( is not | isn't ) [ a / an / the ] <role> [ [ of / to / from / in / with ] <object> ] [.]
```

Persistence
-----------

Data can be persisted through [database plugins](#pluggable-data-store) or a flat
file. To use a flat file, initialize `relations` like this:

```js
var relations = require('relations');
relations.use(relations.stores.memory, {dataFile: '/path/to/datafile.json'});
```

`relations` will store and load data, as a JSON blob, in the specified file.

Pluggable data store
--------------------

Two additional data stores are provided: Redis and MySQL.

### Redis store

To use the redis store, your app must make a
[node_redis](https://github.com/mranney/node_redis) client and pass it like so:

```js
var relations = require('relations')
  , redis = require('redis')

relations.use(relations.stores.redis, {
  client: redis.createClient(),
  prefix: 'optional-key-prefix'
});
```

### MySQL store

To use the MySQL store, your app must make a
[node-mysql](https://github.com/felixge/node-mysql) client and pass it like so:

```js
var relations = require('relations')
  , mysql = require('mysql')

relations.use(relations.stores.mysql, {client: mysql.createConnection({user: 'root', database: 'test'})});
```

### Make your own store

A **relations** store is simply a node module that exports an event emitter
and responds to the following events:

#### `init` (options, cb)

Initialize the store with `options` (from `relations.use()`) and call `cb(err)`
when done.

#### `declaration` (cmd, cb)

Respond to a declaration and call `cb()` when done. `cmd` will be an object
containing the properties:

- ctx - context object
- subject
- role
- object (optional)

#### `revocation` (cmd, cb)

Respond to a revocation and call `cb()` when done. `cmd` will be an object
containing the properties:

- ctx - context object
- subject
- role
- object (optional)

### `verb-question` (cmd, cb)

Respond to a verb question and call `cb(err, /* boolean */ can)` with the result.
`cmd` will be an object containing the properties:

- ctx - context object
- subject
- verb
- object (optional)

### `role-question` (cmd, cb)

Respond to a role question and call `cb(err, /* boolean */ is)` with the result.
`cmd` will be an object containing the properties:

- ctx - context object
- subject
- role
- object (optional)

### `verb-request` (cmd, cb)

Respond to a verb request and call `cb(err, /* array */ objects)` with the result.
`cmd` will be an object containing the properties:

- ctx - context object
- subject
- verb

### `role-request` (cmd, cb)

Respond to a role request and call `cb(err, /* array */ objects)` with the result.
`cmd` will be an object containing the properties:

- ctx - context object
- subject
- role

### `verb-subject-request` (cmd, cb)

Respond to a verb subject request and call `cb(err, /* array */ subjects)` with
the result. `cmd` will be an object containing the properties:

- ctx - context object
- verb
- object

### `role-subject-request` (cmd, cb)

Respond to a role subject request and call `cb(err, /* array */ subjects)` with
the result. `cmd` will be an object containing the properties:

- ctx - context object
- role
- object

### `object-verb-request` (cmd, cb)

Respond to an object verb request and call `cb(err, /* array */ verbs)` with
the result. `cmd` will be an object containing the properties:

- ctx - context object
- object
- subject

### `object-role-map-request` (cmd, cb)

Respond to an object-role map request and call `cb(err, /* object */ map)` with
the result. `cmd` will be an object containing the properties:

- ctx - context object
- subject

### `subject-role-map-request` (cmd, cb)

Respond to a subject-role map request and call `cb(err, /* object */ map)` with
the result. `cmd` will be an object containing the properties:

- ctx - context object
- object (optional)

### `reset` (cb)

Reset the store, dumping all storage and structure, calling `cb(err)` when done.

- - -

### Developed by [Terra Eclipse](http://www.terraeclipse.com)
Terra Eclipse, Inc. is a nationally recognized political technology and
strategy firm located in Aptos, CA and Washington, D.C.

- - -

### License: MIT

- Copyright (C) 2012 Carlos Rodriguez (http://s8f.org/)
- Copyright (C) 2012 Terra Eclipse, Inc. (http://www.terraeclipse.com/)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &quot;Software&quot;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
