hoodiehq / pouchdb-admins

PouchDB plugin to simulate CouchDB’s admin accounts
Apache License 2.0
7 stars 2 forks source link

pouchdb-admins

PouchDB plugin to simulate CouchDB’s admin accounts

Build Status Coverage Status Dependency Status devDependency Status

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

Example

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

db.admins(options)
Argument Type Description Required
options.secret String Server secret, like CouchDB’s couch_httpd_auth.secret Yes
options.admins Object Map of existing admin accounts No
options.sessionTimeout Number Default timeout for sessions in milliseconds, used in .calculateSessionId No

Returns admins API.

Throws

TypeError options.secret is not a String

Example

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

admins.get()

Looks up admin account by username

admins.get(username)
Argument Type Description Required
username String - Yes

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

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

Rejects with

TypeError username is not a String
not_found admin not found

Example

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

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

admins.set(username, password)
Argument Type Description Required
username String - Yes
password String - Yes

Resolves without argument. Rejects with

TypeError username is not a String
TypeError password is not a String

Example

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

admins.validatePassword

Validates password of admin account

admins.validatePassword(username, password)
Argument Type Description Required
username String - Yes
password String - Yes

Resolves without argument. Rejects with

TypeError username is not a String
TypeError password is not a String
not_found missing
unauthorized Name or password is incorrect

Example

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 and exposes all methods .on, once, .removeListener etc.

Event Description Argument
change triggered for any add, update and remove event adminsMap of all admin accounts with hashed password
add New admin account created username of new admin account
update Existing admin account updated username of updated admin account
remove New admin account created username of removed admin account

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}.

[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