# pouchdb-admins

> PouchDB plugin to simulate CouchDB’s admin accounts

Latest version **1.0.6** (published 2017-01-05) · Apache-2.0 license · 0 weekly downloads

## Install

```sh
npm install pouchdb-admins
pnpm add pouchdb-admins
yarn add pouchdb-admins
bun add pouchdb-admins
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.0.6 |
| Published | 2017-01-05 |
| First published | 2015-12-20 |
| Weekly downloads | 0 |
| License | Apache-2.0 |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 8 |
| Known vulnerabilities | 0 (+1 in 1 direct dependencies) |
| Install scripts | no |
| Author | The Hoodie Community |
| Maintainers | hoodie |
| Keywords | couchdb, pouchdb, plugin, users, authentication |

## Links

- npm: https://www.npmjs.com/package/pouchdb-admins
- Repository: https://github.com/hoodiehq/pouchdb-admins
- Homepage: https://github.com/hoodiehq/pouchdb-admins#readme
- Issues: https://github.com/hoodiehq/pouchdb-admins/issues
- npm.io page: https://npm.io/package/pouchdb-admins

## Dependencies (8)

- [lie](https://npm.io/package/lie.md) ^3.0.1
- [lodash](https://npm.io/package/lodash.md) ^4.8.2
- [base64url](https://npm.io/package/base64url.md) ^2.0.0
- [secure-random](https://npm.io/package/secure-random.md) ^1.1.1
- [pouchdb-errors](https://npm.io/package/pouchdb-errors.md) ^6.0.4
- [pouchdb-wrappers](https://npm.io/package/pouchdb-wrappers.md) ^1.3.6
- [pouchdb-bulkdocs-wrapper](https://npm.io/package/pouchdb-bulkdocs-wrapper.md) ^1.0.2
- [couchdb-calculate-session-id](https://npm.io/package/couchdb-calculate-session-id.md) ^1.0.0

## Alternatives

- [@pothos/plugin-authz](https://npm.io/package/@pothos/plugin-authz.md) — 12.4K weekly downloads
- [@bounded-sh/client](https://npm.io/package/@bounded-sh/client.md) — 3.2K weekly downloads
- [@luigi-project/plugin-auth-oauth2](https://npm.io/package/@luigi-project/plugin-auth-oauth2.md) — 2.3K weekly downloads
- [@nocobase/plugin-verification](https://npm.io/package/@nocobase/plugin-verification.md) — 2.0K weekly downloads
- [escher-request](https://npm.io/package/escher-request.md) — 545 weekly downloads

## Recent versions

- 1.0.6 (latest) — 2017-01-05
- 1.0.5 — 2016-11-03
- 1.0.4 — 2016-09-05
- 1.0.3 — 2016-07-14
- 1.0.2 — 2016-06-06
- 1.0.1 — 2016-04-06
- 1.0.0 — 2015-12-20

## README

# pouchdb-admins

> PouchDB plugin to simulate CouchDB’s admin accounts

[![Build Status](https://travis-ci.org/hoodiehq/pouchdb-admins.svg?branch=master)](https://travis-ci.org/hoodiehq/pouchdb-admins)
[![Coverage Status](https://coveralls.io/repos/hoodiehq/pouchdb-admins/badge.svg?branch=master)](https://coveralls.io/r/hoodiehq/pouchdb-admins?branch=master)
[![Dependency Status](https://david-dm.org/hoodiehq/pouchdb-admins.svg)](https://david-dm.org/hoodiehq/pouchdb-admins)
[![devDependency Status](https://david-dm.org/hoodiehq/pouchdb-admins/dev-status.svg)](https://david-dm.org/hoodiehq/pouchdb-admins#info=devDependencies)

`pouchdb-admins` mimics how CouchDB stores a map of admin accounts in its configuration.

## Example

```js
var PouchDB = require('pouchdb')
PouchDB.plugin(require('pouchdb-admins'))

var db = new PouchDB('my-users')

var admins = db.admins({
  secret: 'secret123'
})

admins.get('kim').then(function (doc) {
  console.log('"%s" is an admin', doc.name)
})

admins.validatePassword('pat', 'secret').then(function () {
  // "pat" is admin and "secret" is correct password
})

admins.on('change', function (adminMap) {
  // adminMap is all admins with their hashed password
  // {
  //   "kim": "-pbkdf2-e079757b4cb58ae17467c8befe725778ce97e422,0aef36ccafa33f3e81ae897baf23f85c,10"
  // }
})
admins.on('add', function (username) {
  console.log('%s added as admin', username)
})
admins.on('update', function (username) {
  console.log('password updated for %s', username)
})
admins.on('remove', function (username) {
  console.log('%s removed from admins', username)
})
```

## API

- [Factory](#factory)
- [get()](#adminsget)
- [set()](#adminsset)
- [validatePassword()](#adminsvalidatepassword)
- [calculateSessionId()](#adminscalculatesessionid)
- [validateSession()](#adminsvalidatesession)
- [Events](#events)

### Factory

```js
db.admins(options)
```

<table>
  <thead>
    <tr>
      <th>Argument</th>
      <th>Type</th>
      <th>Description</th>
      <th>Required</th>
    </tr>
  </thead>
  <tr>
    <th align="left"><code>options.secret</code></th>
    <td>String</td>
    <td>
      Server secret, like CouchDB’s <code>couch_httpd_auth.secret</code>
    </td>
    <td>Yes</td>
  </tr>
  <tr>
    <th align="left"><code>options.admins</code></th>
    <td>Object</td>
    <td>
      Map of existing admin accounts
    </td>
    <td>No</td>
  </tr>
  <tr>
    <th align="left"><code>options.sessionTimeout</code></th>
    <td>Number</td>
    <td>
      Default timeout for sessions in milliseconds, used in
      <code>.calculateSessionId</code>
    </td>
    <td>No</td>
  </tr>
</table>

Returns `admins` API.

Throws

<table>
  <tr>
    <th align="left"><code>TypeError</code></th>
    <td><code>options.secret</code> is not a String</td>
  </tr>
</table>

Example

```js
var admins = db.admins({
  secret: 'secret123',
  admins: {
    kim: '-pbkdf2-e079757b4cb58ae17467c8befe725778ce97e422,0aef36ccafa33f3e81ae897baf23f85c,10'
  }
})
```

### admins.get()

Looks up admin account by username

```js
admins.get(username)
```

<table>
  <thead>
    <tr>
      <th>Argument</th>
      <th>Type</th>
      <th>Description</th>
      <th>Required</th>
    </tr>
  </thead>
  <tr>
    <th align="left"><code>username</code></th>
    <td>String</td>
    <td>
      -
    </td>
    <td>Yes</td>
  </tr>
</table>

Resolves without doc for admin as if it would be stored in CouchDB’s `_users`
but without a `_rev` property.

```js
{
  id: 'org.couchdb.user:kim',
  type: 'user',
  name: 'kim',
  password_scheme: 'pbkdf2',
  derived_key: 'e079757b4cb58ae17467c8befe725778ce97e422',
  salt: '0aef36ccafa33f3e81ae897baf23f85c',
  iterations: 10,
  roles: ['_admin']
}
```

Rejects with

<table>
  <tr>
    <th align="left"><code>TypeError</code></th>
    <td><code>username</code> is not a String</td>
  </tr>
  <tr>
    <th align="left"><code>not_found</code></th>
    <td>admin not found</td>
  </tr>
</table>

Example

```js
admins.get('kim')
  .then(function () {
    console.log('"kim" is an admin')
  })
  .catch(function (error) {
    if (error.name === 'not_found') {
      console.log('"kim" is not an admin')
    } else {
      // something unforeseen happened
      console.log(error)
    }
  })
```

Session validation example

```js
var isValidSessionId = require('couchdb-is-valid-session-id')
admins.get('kim')
  .then(function (doc) {
    if (!isValidSessionId('secret123', doc.salt, sessionId)) {
      throw new Error('Invalid sesion')
    }

    // kim has valid session
  })
```

### admins.set()

Adds or updates admin password

```js
admins.set(username, password)
```

<table>
  <thead>
    <tr>
      <th>Argument</th>
      <th>Type</th>
      <th>Description</th>
      <th>Required</th>
    </tr>
  </thead>
  <tr>
    <th align="left"><code>username</code></th>
    <td>String</td>
    <td>
      -
    </td>
    <td>Yes</td>
  </tr>
  <tr>
    <th align="left"><code>password</code></th>
    <td>String</td>
    <td>
      -
    </td>
    <td>Yes</td>
  </tr>
</table>

Resolves without argument. Rejects with

<table>
  <tr>
    <th align="left"><code>TypeError</code></th>
    <td><code>username</code> is not a String</td>
  </tr>
  <tr>
    <th align="left"><code>TypeError</code></th>
    <td><code>password</code> is not a String</td>
  </tr>
</table>

Example

```js
admins.set('pat', 'secret')
  .then(function () {
    // pat added as admin with hashed password, or existing password updated
  })
```

### admins.validatePassword

Validates password of admin account

```js
admins.validatePassword(username, password)
```

<table>
  <thead>
    <tr>
      <th>Argument</th>
      <th>Type</th>
      <th>Description</th>
      <th>Required</th>
    </tr>
  </thead>
  <tr>
    <th align="left"><code>username</code></th>
    <td>String</td>
    <td>
      -
    </td>
    <td>Yes</td>
  </tr>
  <tr>
    <th align="left"><code>password</code></th>
    <td>String</td>
    <td>
      -
    </td>
    <td>Yes</td>
  </tr>
</table>

Resolves without argument. Rejects with

<table>
  <tr>
    <th align="left"><code>TypeError</code></th>
    <td><code>username</code> is not a String</td>
  </tr>
  <tr>
    <th align="left"><code>TypeError</code></th>
    <td><code>password</code> is not a String</td>
  </tr>
  <tr>
    <th align="left"><code>not_found</code></th>
    <td>missing</td>
  </tr>
  <tr>
    <th align="left"><code>unauthorized</code></th>
    <td>Name or password is incorrect</td>
  </tr>
</table>

Example

```js
admins.validatePassword('pat', 'secret')
  .then(function () {
    // "pat" is admin and "secret" is correct password
  })
  .catch(function (error) {
    switch (error.name) {
      case 'unauthorized':
        // "pat" is admin but "secret" is incorrect password
        break
      case 'not_found':
        // "pat" is not an admin
        break
      default:
        // something unforeseen happened ...
        console.log(error)
    }
  })
```

### calculateSessionId()

_tbd_

### validateSession()

_tbd_

### Events

`admins` is an [event emitter](https://nodejs.org/api/events.html) and exposes
all methods `.on`, `once`, `.removeListener` etc.

<table>
  <thead>
    <tr>
      <th>
        Event
      </th>
      <th>
        Description
      </th>
      <th>
        Argument
      </th>
    </tr>
  </thead>
  <tr>
    <th align="left"><code>change</code></th>
    <td>
      triggered for any <code>add</code>, <code>update</code> and <code>remove</code> event
    </td>
    <td>
      <code>adminsMap</code> of all admin accounts with hashed password
    </td>
  </tr>
  <tr>
    <th align="left"><code>add</code></th>
    <td>
      New admin account created
    </td>
    <td>
      <code>username</code> of new admin account
    </td>
  </tr>
  <tr>
    <th align="left"><code>update</code></th>
    <td>
      Existing admin account updated
    </td>
    <td>
      <code>username</code> of updated admin account
    </td>
  </tr>
  <tr>
    <th align="left"><code>remove</code></th>
    <td>
      New admin account created
    </td>
    <td>
      <code>username</code> of removed admin account
    </td>
  </tr>
</table>

## How it works

Here an excerpt of a `couch.ini` that stores a map of two admin accounts with
their hashed passwords. The format is `-{password scheme}-{derived key},{salt},{iterations}`.

```ini
[admins]
admin=-pbkdf2-e079757b4cb58ae17467c8befe725778ce97e422,0aef36ccafa33f3e81ae897baf23f85c,10
anotheradmin=-pbkdf2-67c8befe725778ce97e422e079757b4cb58ae174,7baf23f85c0aef36ccafa33f3e81ae89,10
```

## Testing

Local setup

```
git clone git@github.com:hoodiehq/pouchdb-admins.git
cd pouchdb-admins
npm install
```

Run all tests and code style checks

```
npm test
```

Run all tests on file change

```
npm run test:watch
```

Run tests from single file

```
node tests/integration/constructor-test.js
# PROTIP™: pipe output through https://www.npmjs.com/package/tape#pretty-reporters
```

### License

[Apache-2.0](https://github.com/hoodiehq/hoodie/blob/master/LICENSE)

---
_Source: https://npm.io/package/pouchdb-admins · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
