diff --git a/package.json b/package.json index 3f1ea74..4657404 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ "prepublishOnly": "npm run build", "build": "npm-run-all clean rollup minify", "lint": "eslint bin/ src/ tests/", - "dev": "npm run rollup-cjs && zuul --local 9000 --no-coverage --ui mocha-bdd test/test.js", - "test": "npm run rollup-cjs && zuul --phantom --ui mocha-bdd test/test.js", + "dev": "npm run rollup-cjs && zuul --local 9000 --no-coverage --ui mocha-bdd test/*", + "test": "npm run rollup-cjs && zuul --phantom --ui mocha-bdd test/*", "release": "standard-version" }, "keywords": [ diff --git a/test/test.authentication.js b/test/test.authentication.js new file mode 100644 index 0000000..55cbb5d --- /dev/null +++ b/test/test.authentication.js @@ -0,0 +1,309 @@ +'use strict'; + +var PouchDB = require('pouchdb-memory'); +var Authentication = require('../lib'); +PouchDB.plugin(Authentication); +var chai = require('chai'); +var should = chai.should(); +chai.use(require('chai-as-promised')); + +var users = ['batman', 'superman', 'green_lantern', 'robin', 'aquaman', 'spiderman']; + +describe('authentication', function () { + + var dbName = 'http://localhost:5984/testdb'; + + var db; + + beforeEach(function () { + db = new PouchDB(dbName); + return db; + }); + afterEach(function () { + return db.logout().then(function () { + return db.destroy().then(function () { + var usersUrl = db.getUsersDatabaseUrl(); + var usersDb = new PouchDB(usersUrl); + // remove the fake users, hopefully we're in the admin party + return usersDb.allDocs({ + include_docs: true, + keys: users.map(function (user) { + return 'org.couchdb.user:' + user; + }) + }).then(function (res) { + var rows = res.rows.filter(function (row) { + return row.doc; + }); + var docs = rows.map(function (row) { + row.doc._deleted = true; + return row.doc; + }); + return usersDb.bulkDocs({docs: docs}); + }); + }); + }); + }); + + it('Test signup', function () { + return db.signup('batman', 'brucewayne').then(function (res) { + res.ok.should.equal(true); + res.id.should.equal('org.couchdb.user:batman'); + }); + }); + + it('Test signup conflict', function () { + return db.signup('superman', 'clarkkent').then(function (res) { + res.ok.should.equal(true); + return db.signup('superman', 'notclarkkent').then(function (res) { + should.not.exist(res); + }).catch(function (err) { + err.name.should.equal('conflict'); + }); + }); + }); + + it('Test bad signup args', function () { + return db.signup().catch(function (err) { + should.exist(err); + }); + }); + + it('Test bad signup args 2', function () { + return db.signup('green_lantern').catch(function (err) { + should.exist(err); + }); + }); + + it('Test login/logout', function () { + return db.signup('aquaman', 'sleeps_with_fishes').then(function (res) { + return db.getSession(); + }).then(function (res) { + should.equal(res.userCtx.name, null); + return db.login('aquaman', 'sleeps_with_fishes'); + }).then(function (res) { + res.ok.should.equal(true); + return db.getSession(); + }).then(function (res) { + res.userCtx.name.should.equal('aquaman'); + return db.logout(); + }).then(function () { + return db.getSession(); + }).then(function (res) { + should.equal(res.userCtx.name, null); + }); + }); + + it('Test metadata', function () { + var metadata = {alias: 'boywonder', profession: 'acrobat'}; + var opts = {metadata: metadata}; + return db.signup('robin', 'dickgrayson', opts).then(function (res) { + res.ok.should.equal(true); + return db.login('robin', 'dickgrayson'); + }).then(function () { + return db.getUser('robin'); + }).then(function (user) { + user.name.should.equal('robin'); + user.alias.should.equal('boywonder'); + user.profession.should.equal('acrobat'); + }); + }); + + it('Test changing metadata', function () { + var metadata = {alias: 'boywonder', profession: 'acrobat'}; + var newMetadata = {alias: 'rednowyob', profession: 'taborca'}; + var opts = {metadata: metadata}; + return db.signup('robin', 'dickgrayson', opts).then(function (res) { + res.ok.should.equal(true); + return db.login('robin', 'dickgrayson'); + }).then(function () { + return db.getUser('robin'); + }).then(function (user) { + user.name.should.equal('robin'); + user.alias.should.equal('boywonder'); + user.profession.should.equal('acrobat'); + }).then(function () { + return db.putUser('robin', {metadata: newMetadata}); + }).then(function () { + return db.getUser('robin'); + }).then(function (user) { + user.name.should.equal('robin'); + user.alias.should.equal('rednowyob'); + user.profession.should.equal('taborca'); + }); + }); + + var reservedWords = [ + '_id', + '_rev', + 'name', + 'type', + 'roles', + 'password', + 'password_scheme', + 'iterations', + 'derived_key', + 'salt' + ]; + + reservedWords.forEach(function (key) { + it('Test changing metadata using reserved word "' + key + '"', function () { + return db.signup('robin', 'dickgrayson').then(function (res) { + res.ok.should.equal(true); + return db.login('robin', 'dickgrayson'); + }).then(function () { + return db.getUser('robin').then(function (user) { + var metadata = {}; + metadata[key] = 'test'; + return db.putUser('robin', {metadata: metadata}).then(function (res) { + res.ok.should.not.equal(true); + }).catch(function (err) { + should.exist(err); + err.status.should.equal(400); + err.name.should.equal('authentication_error'); + err.message.should.equal('cannot use reserved word in metadata: "' + key + '"'); + err.error.should.equal(true); + + if (key === 'password') { + return db.login('robin', 'dickgrayson').then(function (res) { + res.ok.should.equal(true); + }).catch(function (err) { + should.not.exist(err); + }); + } else { + return db.getUser('robin').then(function (changedUser) { + changedUser[key].should.deep.equal(user[key]); + }).catch(function (err) { + should.not.exist(err); + }); + } + }); + }); + }); + }); + }); + + it('Test changing metadata using non-reserved word "metadata"', function () { + var metadata = {test: 'test'}; + return db.signup('robin', 'dickgrayson').then(function (res) { + res.ok.should.equal(true); + return db.login('robin', 'dickgrayson'); + }).then(function () { + return db.putUser('robin', {metadata: {metadata: metadata}}); + }).then(function (res) { + res.ok.should.equal(true); + return db.getUser('robin'); + }).then(function (changedUser) { + changedUser.metadata.should.deep.equal(metadata); + }).catch(function (err) { + should.not.exist(err); + }); + }); + + it('Test that admin can change roles', function () { + var roles = ['sidekick']; + var newRoles = ['superhero', 'villain']; + return db.signup('robin', 'dickgrayson', {roles: roles}).then(function (res) { + res.ok.should.equal(true); + return db.getUser('robin'); + }).then(function (user) { + user.roles.should.deep.equal(roles); + }).then(function () { + return db.putUser('robin', {roles: newRoles}); + }).then(function (res) { + res.ok.should.equal(true); + return db.getUser('robin'); + }).then(function (user) { + user.roles.should.deep.equal(newRoles); + }).catch(function (err) { + should.not.exist(err); + }); + }); + + it('Test that user cannot change roles', function () { + var roles = ['sidekick']; + var newRoles = ['superhero', 'villain']; + // We can't test for initial roles as we are in admin party + // Let us have faith in CouchDB + return db.signup('robin', 'dickgrayson', {roles: roles}).then(function (res) { + res.ok.should.equal(true); + return db.login('robin', 'dickgrayson'); + }).then(function () { + return db.getUser('robin'); + }).then(function (user) { + user.roles.should.deep.equal(roles); + }).then(function () { + return db.putUser('robin', {roles: newRoles}); + }).then(function (res) { + res.ok.should.not.equal(true); + return db.getUser('robin').then(function (user) { + user.roles.should.deep.equal(roles); + }); + }).catch(function (err) { + should.exist(err); + }); + }); + + it('Test wrong user for getUser', function () { + return db.signup('robin', 'dickgrayson').then(function (res) { + return db.signup('aquaman', 'sleeps_with_fishes'); + }).then(function () { + return db.login('robin', 'dickgrayson'); + }).then(function () { + return db.getUser('robin'); + }).then(function (res) { + res.name.should.equal('robin'); + return db.getUser('aquaman').then(function (res) { + should.not.exist(res); + }).catch(function (err) { + should.exist(err); + return db.login('aquaman', 'sleeps_with_fishes').then(function () { + return db.getUser('aquaman').then(function (res) { + res.name.should.equal('aquaman'); + return db.getSession(); + }).then(function (res) { + res.userCtx.name.should.equal('aquaman'); + return db.getUser('robin').then(function (res) { + should.not.exist(res); + }).catch(function (err) { + should.exist(err); + }); + }); + }); + }); + }); + }); + + it('Test change password', function () { + return db.signup('spiderman', 'will-forget').then(function (res) { + return db.changePassword('spiderman', 'will-remember').then(function (res) { + res.ok.should.equal(true); + }).then(function () { + return db.login('spiderman', 'will-remember'); + }).then(function (res) { + res.ok.should.equal(true); + }); + }); + }); + + it('Test change username', function () { + return db.signup('spiderman', 'will-forget').then(function (res) { + return db.changeUsername('spiderman', 'batman').then(function () { + return db.login('batman', 'will-forget'); + }).then(function (res) { + res.ok.should.equal(true); + }); + }); + }); + + it('Shouldn\'t change username if new username already exists', function () { + return db.signup('spiderman', 'will-forget').then(function (res) { + return db.signup('batman', 'will-remember'); + }).then(function () { + return db.changeUsername('spiderman', 'batman'); + }).then(function () { + throw new Error('shouldn\'t have worked'); + }, function (err) { + should.exist(err); + }); + }); +}); diff --git a/test/test.js b/test/test.js deleted file mode 100644 index 577eb5a..0000000 --- a/test/test.js +++ /dev/null @@ -1,320 +0,0 @@ -'use strict'; - -var PouchDB = require('pouchdb-memory'); -var Authentication = require('../lib'); -PouchDB.plugin(Authentication); -var chai = require('chai'); -var should = chai.should(); -chai.use(require('chai-as-promised')); - -var users = ['batman', 'superman', 'green_lantern', 'robin', 'aquaman', 'spiderman']; - -var testCases = [ - 'normal', - 'trailing-slash' -]; - -testCases.forEach(function (testCase) { - - describe('authentication-' + testCase, function () { - - var dbName = testCase === 'normal' ? - 'http://localhost:5984/testdb' : - 'http://localhost:5984/testdb/'; // trailing slash - - var db; - - beforeEach(function () { - db = new PouchDB(dbName); - return db; - }); - afterEach(function () { - return db.logout().then(function () { - return db.destroy().then(function () { - var usersUrl = db.getUsersDatabaseUrl(); - var usersDb = new PouchDB(usersUrl); - // remove the fake users, hopefully we're in the admin party - return usersDb.allDocs({ - include_docs: true, - keys: users.map(function (user) { - return 'org.couchdb.user:' + user; - }) - }).then(function (res) { - var rows = res.rows.filter(function (row) { - return row.doc; - }); - var docs = rows.map(function (row) { - row.doc._deleted = true; - return row.doc; - }); - return usersDb.bulkDocs({docs: docs}); - }); - }); - }); - }); - - it('Test signup', function () { - return db.signup('batman', 'brucewayne').then(function (res) { - res.ok.should.equal(true); - res.id.should.equal('org.couchdb.user:batman'); - }); - }); - - it('Test signup conflict', function () { - return db.signup('superman', 'clarkkent').then(function (res) { - res.ok.should.equal(true); - return db.signup('superman', 'notclarkkent').then(function (res) { - should.not.exist(res); - }).catch(function (err) { - err.name.should.equal('conflict'); - }); - }); - }); - - it('Test bad signup args', function () { - return db.signup().catch(function (err) { - should.exist(err); - }); - }); - - it('Test bad signup args 2', function () { - return db.signup('green_lantern').catch(function (err) { - should.exist(err); - }); - }); - - it('Test login/logout', function () { - return db.signup('aquaman', 'sleeps_with_fishes').then(function (res) { - return db.getSession(); - }).then(function (res) { - should.equal(res.userCtx.name, null); - return db.login('aquaman', 'sleeps_with_fishes'); - }).then(function (res) { - res.ok.should.equal(true); - return db.getSession(); - }).then(function (res) { - res.userCtx.name.should.equal('aquaman'); - return db.logout(); - }).then(function () { - return db.getSession(); - }).then(function (res) { - should.equal(res.userCtx.name, null); - }); - }); - - it('Test metadata', function () { - var metadata = {alias: 'boywonder', profession: 'acrobat'}; - var opts = {metadata: metadata}; - return db.signup('robin', 'dickgrayson', opts).then(function (res) { - res.ok.should.equal(true); - return db.login('robin', 'dickgrayson'); - }).then(function () { - return db.getUser('robin'); - }).then(function (user) { - user.name.should.equal('robin'); - user.alias.should.equal('boywonder'); - user.profession.should.equal('acrobat'); - }); - }); - - it('Test changing metadata', function () { - var metadata = {alias: 'boywonder', profession: 'acrobat'}; - var newMetadata = {alias: 'rednowyob', profession: 'taborca'}; - var opts = {metadata: metadata}; - return db.signup('robin', 'dickgrayson', opts).then(function (res) { - res.ok.should.equal(true); - return db.login('robin', 'dickgrayson'); - }).then(function () { - return db.getUser('robin'); - }).then(function (user) { - user.name.should.equal('robin'); - user.alias.should.equal('boywonder'); - user.profession.should.equal('acrobat'); - }).then(function () { - return db.putUser('robin', {metadata: newMetadata}); - }).then(function () { - return db.getUser('robin'); - }).then(function (user) { - user.name.should.equal('robin'); - user.alias.should.equal('rednowyob'); - user.profession.should.equal('taborca'); - }); - }); - - var reservedWords = [ - '_id', - '_rev', - 'name', - 'type', - 'roles', - 'password', - 'password_scheme', - 'iterations', - 'derived_key', - 'salt' - ]; - - reservedWords.forEach(function (key) { - it('Test changing metadata using reserved word "' + key + '"', function () { - return db.signup('robin', 'dickgrayson').then(function (res) { - res.ok.should.equal(true); - return db.login('robin', 'dickgrayson'); - }).then(function () { - return db.getUser('robin').then(function (user) { - var metadata = {}; - metadata[key] = 'test'; - return db.putUser('robin', {metadata: metadata}).then(function (res) { - res.ok.should.not.equal(true); - }).catch(function (err) { - should.exist(err); - err.status.should.equal(400); - err.name.should.equal('authentication_error'); - err.message.should.equal('cannot use reserved word in metadata: "' + key + '"'); - err.error.should.equal(true); - - if (key === 'password') { - return db.login('robin', 'dickgrayson').then(function (res) { - res.ok.should.equal(true); - }).catch(function (err) { - should.not.exist(err); - }); - } else { - return db.getUser('robin').then(function (changedUser) { - changedUser[key].should.deep.equal(user[key]); - }).catch(function (err) { - should.not.exist(err); - }); - } - }); - }); - }); - }); - }); - - it('Test changing metadata using non-reserved word "metadata"', function () { - var metadata = {test: 'test'}; - return db.signup('robin', 'dickgrayson').then(function (res) { - res.ok.should.equal(true); - return db.login('robin', 'dickgrayson'); - }).then(function () { - return db.putUser('robin', {metadata: {metadata: metadata}}); - }).then(function (res) { - res.ok.should.equal(true); - return db.getUser('robin'); - }).then(function (changedUser) { - changedUser.metadata.should.deep.equal(metadata); - }).catch(function (err) { - should.not.exist(err); - }); - }); - - it('Test that admin can change roles', function () { - var roles = ['sidekick']; - var newRoles = ['superhero', 'villain']; - return db.signup('robin', 'dickgrayson', {roles: roles}).then(function (res) { - res.ok.should.equal(true); - return db.getUser('robin'); - }).then(function (user) { - user.roles.should.deep.equal(roles); - }).then(function () { - return db.putUser('robin', {roles: newRoles}); - }).then(function (res) { - res.ok.should.equal(true); - return db.getUser('robin'); - }).then(function (user) { - user.roles.should.deep.equal(newRoles); - }).catch(function (err) { - should.not.exist(err); - }); - }); - - it('Test that user cannot change roles', function () { - var roles = ['sidekick']; - var newRoles = ['superhero', 'villain']; - // We can't test for initial roles as we are in admin party - // Let us have faith in CouchDB - return db.signup('robin', 'dickgrayson', {roles: roles}).then(function (res) { - res.ok.should.equal(true); - return db.login('robin', 'dickgrayson'); - }).then(function () { - return db.getUser('robin'); - }).then(function (user) { - user.roles.should.deep.equal(roles); - }).then(function () { - return db.putUser('robin', {roles: newRoles}); - }).then(function (res) { - res.ok.should.not.equal(true); - return db.getUser('robin').then(function (user) { - user.roles.should.deep.equal(roles); - }); - }).catch(function (err) { - should.exist(err); - }); - }); - - it('Test wrong user for getUser', function () { - return db.signup('robin', 'dickgrayson').then(function (res) { - return db.signup('aquaman', 'sleeps_with_fishes'); - }).then(function () { - return db.login('robin', 'dickgrayson'); - }).then(function () { - return db.getUser('robin'); - }).then(function (res) { - res.name.should.equal('robin'); - return db.getUser('aquaman').then(function (res) { - should.not.exist(res); - }).catch(function (err) { - should.exist(err); - return db.login('aquaman', 'sleeps_with_fishes').then(function () { - return db.getUser('aquaman').then(function (res) { - res.name.should.equal('aquaman'); - return db.getSession(); - }).then(function (res) { - res.userCtx.name.should.equal('aquaman'); - return db.getUser('robin').then(function (res) { - should.not.exist(res); - }).catch(function (err) { - should.exist(err); - }); - }); - }); - }); - }); - }); - - it('Test change password', function () { - return db.signup('spiderman', 'will-forget').then(function (res) { - return db.changePassword('spiderman', 'will-remember').then(function (res) { - res.ok.should.equal(true); - }).then(function () { - return db.login('spiderman', 'will-remember'); - }).then(function (res) { - res.ok.should.equal(true); - }); - }); - }); - - it('Test change username', function () { - return db.signup('spiderman', 'will-forget').then(function (res) { - return db.changeUsername('spiderman', 'batman').then(function () { - return db.login('batman', 'will-forget'); - }).then(function (res) { - res.ok.should.equal(true); - }); - }); - }); - - it('Shouldn\'t change username if new username already exists', function () { - return db.signup('spiderman', 'will-forget').then(function (res) { - return db.signup('batman', 'will-remember'); - }).then(function () { - return db.changeUsername('spiderman', 'batman'); - }).then(function () { - throw new Error('shouldn\'t have worked'); - }, function (err) { - should.exist(err); - }); - }); - }); - -}); diff --git a/test/test.urls.js b/test/test.urls.js new file mode 100644 index 0000000..f689d75 --- /dev/null +++ b/test/test.urls.js @@ -0,0 +1,46 @@ +'use strict'; + +var PouchDB = require('pouchdb-memory'); +var Authentication = require('../lib'); +PouchDB.plugin(Authentication); +var chai = require('chai'); +var should = chai.should(); + +describe('urls', function () { + + var hostUrl = 'http://localhost:5984'; + var dbName = 'testdb' + var dbUrl = hostUrl + '/' + dbName; + + it('Correct users database url for database without trailing slash', function () { + var db = new PouchDB(dbUrl); + var usersUrl = db.getUsersDatabaseUrl(); + usersUrl.should.equal(hostUrl + '/_users'); + }); + + it('Correct users database url for database with trailing slash', function () { + var db = new PouchDB(dbUrl + '/'); + var usersUrl = db.getUsersDatabaseUrl(); + usersUrl.should.equal(hostUrl + '/_users'); + }); + + it('Correct users database url using prefix without trailing slash', function () { + var PouchWithPrefix = PouchDB.defaults({prefix: hostUrl}); + var db = new PouchWithPrefix(dbName); + var usersUrl = db.getUsersDatabaseUrl(); + usersUrl.should.equal(hostUrl + '/_users'); + }); + + it('Correct users database url using prefix with trailing slash', function () { + var PouchWithPrefix = PouchDB.defaults({prefix: hostUrl + '/'}); + var db = new PouchWithPrefix(dbName); + var usersUrl = db.getUsersDatabaseUrl(); + usersUrl.should.equal(hostUrl + '/_users'); + }); + + it('Correct users database url for cloudant-style database urls', function () { + var db = new PouchDB(hostUrl + '/'); + var usersUrl = db.getUsersDatabaseUrl(); + usersUrl.should.equal(hostUrl + '/_users'); + }); +});