slouch/test/spec/user.js
Jeffrey Layanto a2d47ab1e0
Add support for partitioned check (#126)
* Add support for partitioned check

* Add system.supportPartitioned() to API.md

* Fix jshint line too long error

* Fix broken test

* Fix broken test

* Fix broken test

* Clean up test

* Fix broken test

* Fix broken test

* Revert fix

* Fix broken test

* Revert destroySession change

* Fix user test

Co-authored-by: Jeffrey Layanto <jeffrey@layanto.com>
2020-05-13 06:57:51 -07:00

315 lines
9.4 KiB
JavaScript

'use strict';
var Slouch = require('../../scripts'),
utils = require('../utils'),
sporks = require('sporks'),
Promise = require('sporks/scripts/promise'),
NotAuthenticatedError = require('../../scripts/not-authenticated-error'),
NotAuthorizedError = require('../../scripts/not-authorized-error');
describe('user', function () {
var slouch = null,
user = null,
defaultUpdate = null,
username = null,
dbs = null,
slouchNoAuth = null,
notAuthenticatedErr = new NotAuthenticatedError(),
notAuthorizedErr = new NotAuthorizedError();
beforeEach(function () {
slouch = new Slouch(utils.couchDBURL());
slouchNoAuth = new Slouch(utils.couchDBURLNoAuth());
user = slouch.user;
username = 'test_' + utils.nextId();
dbs = [];
return user.create(username, 'testpassword', ['testrole1'], {
firstName: 'Jill',
email: 'test@example.com'
});
});
var destroyDBs = function () {
if (dbs.length > 0) {
var promises = [];
dbs.forEach(function (db) {
promises.push(slouch.db.destroy(db));
});
return Promise.all(promises);
}
};
afterEach(function () {
slouch = new Slouch(utils.couchDBURL());
user = slouch.user;
return user.destroy(username).then(function () {
return destroyDBs();
});
});
var fakeConflict = function () {
var i = 0;
defaultUpdate = user._update;
user._update = function () {
if (i++ < 3) {
var err = new Error();
err.error = 'conflict';
return sporks.promiseError(err);
} else {
return defaultUpdate.apply(this, arguments);
}
};
};
var createPrivateDB = function () {
return slouch.db.create(username).then(function () {
// Add DB to list that will be destroyed
dbs.push(username);
// Set security so that only this user can access it
return slouch.security.onlyRoleCanView(username, 'testrole1');
}).then(function () {
return slouch.doc.create(username, {
foo: 'bar'
});
});
};
// NOTE: create and destroy tested by beforeEach() and afterEach()
it('should not create when already exists', function () {
var err = null;
return user.create(username, 'testpassword').catch(function (_err) {
err = _err;
}).then(function () {
(err === null).should.eql(false);
});
});
it('should get', function () {
return user.get(username).then(function (_user) {
_user._id.should.eql('org.couchdb.user:' + username);
_user.name.should.eql(username);
_user.roles.should.eql(['testrole1']);
_user.type.should.eql('user');
_user.metadata.should.eql({
firstName: 'Jill',
email: 'test@example.com'
});
user.toUsername(_user._id).should.eql(username);
});
});
it('should add role', function () {
return user.addRole(username, 'testrole2').then(function () {
return user.get(username);
}).then(function (_user) {
_user.roles.should.eql(['testrole1', 'testrole2']);
});
});
it('should upsert role', function () {
fakeConflict();
return user.upsertRole(username, 'testrole2').then(function () {
return user.get(username);
}).then(function (_user) {
_user.roles.should.eql(['testrole1', 'testrole2']);
});
});
it('should remove role', function () {
return user.addRole(username, 'testrole2').then(function () {
return user.removeRole(username, 'testrole1');
}).then(function () {
return user.get(username);
}).then(function (_user) {
_user.roles.should.eql(['testrole2']);
});
});
it('should downsert role', function () {
return user.addRole(username, 'testrole2').then(function () {
fakeConflict();
return user.downsertRole(username, 'testrole1');
}).then(function () {
return user.get(username);
}).then(function (_user) {
_user.roles.should.eql(['testrole2']);
});
});
it('should set password', function () {
var origUser = null;
return user.get(username).then(function (_user) {
origUser = _user;
return user.setPassword(username, 'testpassword2');
}).then(function () {
return user.get(username);
}).then(function (_user) {
// Make sure password changed
(_user.derived_key === origUser.derived_key).should.eql(false);
});
});
it('should set metadata', function () {
return user.setMetadata(username, {
firstName: 'Jack'
}).then(function () {
return user.get(username);
}).then(function (_user) {
_user.metadata.firstName.should.eql('Jack');
});
});
it('should get the cookie from the response', function () {
// Simulate browser where a cookie is not in the response
var cookie = user._getCookieFromResponse({
headers: {}
});
(cookie === null).should.eql(true);
// Simulate node where a cookie IS in the response
cookie = user._getCookieFromResponse({
headers: {
'set-cookie': ['my-cookie']
}
});
cookie.should.eql('my-cookie');
});
it('should authenticate and get session', function () {
return user.authenticateAndGetSession(username, 'testpassword').then(function (session) {
// Sanity check
session.userCtx.name.should.eql(username);
session.userCtx.roles.should.eql(['testrole1']);
(session.cookie === undefined).should.eql(false);
return user.destroySession().then(function (response) {
response.body.ok.should.eql(true);
});
});
});
it('should not authenticate and get session', function () {
return sporks.shouldThrow(function () {
return user.authenticateAndGetSession(username, 'bad-password');
}, notAuthenticatedErr);
});
it('should get session with default url', function () {
return user.getSession().then(function (response) {
response.body.userCtx.name.should.eql('admin');
return user.destroySession().then(function (response) {
response.body.ok.should.eql(true);
});
});
});
it('should not be authenticated when cookie missing', function () {
return sporks.shouldThrow(function () {
return user.authenticated('bad-cookie');
}, notAuthenticatedErr);
});
it('should log in, get session and log out', function () {
var n = 0;
return createPrivateDB().then(function () {
// Make sure cannot access DB before log in
return sporks.shouldThrow(function () {
return slouchNoAuth.db.get(username);
}, notAuthorizedErr);
}).then(function () {
return slouchNoAuth.user.logIn(username, 'testpassword');
}).then(function () {
// This would throw if the user does not have access
return slouchNoAuth.db.get(username).then(function () {
return slouchNoAuth.doc.all(username).each(function () {
n++;
});
}).then(function () {
// Make sure we read a doc
n.should.eql(1);
}).then(function () {
return slouchNoAuth.user.getSession().then(function (response) {
// Sanity test
response.body.userCtx.name.should.eql(username);
});
});
}).then(function () {
return slouchNoAuth.user.logOut();
}).then(function () {
// Make sure can no longer access DB
return sporks.shouldThrow(function () {
return slouchNoAuth.db.get(username);
}, notAuthorizedErr);
});
});
it('should not log in when password incorrect', function () {
return createPrivateDB().then(function () {
return sporks.shouldThrow(function () {
return user.logIn(username, 'badpassword');
}, notAuthenticatedErr);
});
});
var generateUserConflict = function () {
// Generate a conflict in a user doc by replicating to and from another DB which we will name
// the same name as the username.
return slouch.db.create(username).then(function () {
// Add DB to list that will be destroyed
dbs.push(username);
// Replicate the user
return slouch.db.replicate({
source: slouch._url + '/_users',
target: slouch._url + '/' + username
});
}).then(function () {
// Add a role to the _users docs
return user.addRole(username, 'testrole2');
}).then(function () {
// Add a role to the other DB's doc
return slouch.doc.get(username, user.toUserId(username)).then(function (doc) {
doc.roles = ['testrole1', 'testrole3'];
return slouch.doc.update(username, doc);
});
}).then(function () {
// Replicate the docs to generate a conflict
return slouch.db.replicate({
source: slouch._url + '/' + username,
target: slouch._url + '/_users'
});
}).then(function () {
// Make sure that the doc is in conflict
return user.get(username, {
conflicts: true
});
}).then(function (doc) {
(doc._conflicts.length > 0).should.eql(true);
});
};
it('should resolve conflicts', function () {
return generateUserConflict().then(function () {
return user.resolveConflicts(username);
}).then(function () {
return user.get(username, {
conflicts: true
});
}).then(function (doc) {
// Make sure there no conflicts
(doc._conflicts === undefined).should.eql(true);
// Make sure roles were merged
doc.roles.sort().should.eql(['testrole1', 'testrole2', 'testrole3']);
});
});
it('should ignore if no conflicts', function () {
// Sanity test for code coverage
return user.resolveConflicts(username);
});
});