diff --git a/api/src/db.js b/api/src/db.js index 81f069a30f9..786d59f121d 100644 --- a/api/src/db.js +++ b/api/src/db.js @@ -46,6 +46,8 @@ if (UNIT_TEST_ENV) { 'saveDocs', 'createVault', 'wipeCacheDb', + 'addRoleAsAdmin', + 'addRoleAsMember', ]; const notStubbed = (first, second) => { @@ -178,4 +180,40 @@ if (UNIT_TEST_ENV) { throw new Error(`Error while saving docs: ${errors.join(', ')}`); }; + + const getDefaultSecurityStructure = () => ({ + names: [], + roles: [], + }); + + const addRoleToSecurity = async (dbname, role, addAsAdmin) => { + if (!dbname || !role) { + throw new Error(`Cannot add security: invalid db name ${dbname} or role ${role}`); + } + + const securityUrl = new URL(environment.serverUrl); + securityUrl.pathname = `${dbname}/_security`; + + const securityObject = await rpn.get({ url: securityUrl.toString(), json: true }); + const property = addAsAdmin ? 'admins' : 'members'; + + if (!securityObject[property]) { + securityObject[property] = getDefaultSecurityStructure(); + } + + if (!securityObject[property].roles || !Array.isArray(securityObject[property].roles)) { + securityObject[property].roles = []; + } + + if (securityObject[property].roles.includes(role)) { + return; + } + + logger.info(`Adding "${role}" role to ${dbname} ${property}`); + securityObject[property].roles.push(role); + await rpn.put({ url: securityUrl.toString(), json: true, body: securityObject }); + }; + + module.exports.addRoleAsAdmin = (dbname, role) => addRoleToSecurity(dbname, role, true); + module.exports.addRoleAsMember = (dbname, role) => addRoleToSecurity(dbname, role, false); } diff --git a/api/src/services/config-watcher.js b/api/src/services/config-watcher.js index 25476cdeef9..3ae860a3e5d 100644 --- a/api/src/services/config-watcher.js +++ b/api/src/services/config-watcher.js @@ -13,6 +13,7 @@ const generateXform = require('./generate-xform'); const generateServiceWorker = require('../generate-service-worker'); const manifest = require('./manifest'); const config = require('../config'); +const environment = require('../environment'); const extensionLibs = require('./extension-libs'); const MEDIC_DDOC_ID = '_design/medic'; @@ -92,6 +93,7 @@ const handleSettingsChange = () => { logger.error('Failed to reload settings: %o', err); process.exit(1); }) + .then(() => addUserRolesToDb()) .then(() => initTransitionLib()) .then(() => { configUpdatesEvents.emit('updated'); @@ -143,6 +145,7 @@ const load = () => { loadViewMaps(); return loadTranslations() .then(() => loadSettings()) + .then(() => addUserRolesToDb()) .then(() => initTransitionLib()) .then(() => db.createVault()); }; @@ -184,10 +187,22 @@ const watch = (callback) => { configUpdatesEvents.on('updated', callback); }; +const addUserRolesToDb = async () => { + const roles = config.get('roles'); + if (!roles || typeof roles !== 'object') { + return; + } + + for (const role of Object.keys(roles)) { + await db.addRoleAsMember(environment.db, role); + } +}; + module.exports = { load, listen, updateServiceWorker, loadTranslations, watch, + addUserRolesToDb, }; diff --git a/api/tests/mocha/db.spec.js b/api/tests/mocha/db.spec.js index 249e00efc11..a354f274307 100644 --- a/api/tests/mocha/db.spec.js +++ b/api/tests/mocha/db.spec.js @@ -233,4 +233,175 @@ describe('db', () => { expect(db.close.args).to.deep.equal([[dbObject]]); }); }); + + describe('addRoleToSecurity', () => { + it('should add role as member to db security', async () => { + sinon.stub(env, 'serverUrl').get(() => 'http://admin:pass@couchdb:5984'); + sinon.stub(rpn, 'get').resolves({ admins: { roles: ['role1'] }, members: { roles: ['role2'] } }); + sinon.stub(rpn, 'put').resolves(); + + await db.addRoleAsMember('dbname', 'rolename'); + + expect(rpn.get.args).to.deep.equal([[ { url: 'http://admin:pass@couchdb:5984/dbname/_security', json: true } ]]); + expect(rpn.put.args).to.deep.equal([[ + { + url: 'http://admin:pass@couchdb:5984/dbname/_security', + json: true, + body: { + admins: { roles: ['role1'] }, + members: { roles: ['role2', 'rolename'] }, + } + } + ]]); + }); + + it('should add role as admin to db security', async () => { + sinon.stub(env, 'serverUrl').get(() => 'http://admin:pass@couchdb:5984'); + sinon.stub(rpn, 'get').resolves({ admins: { roles: ['role1'] }, members: { roles: ['role2'] } }); + sinon.stub(rpn, 'put').resolves(); + + await db.addRoleAsAdmin('dbname', 'rolename'); + + expect(rpn.get.args).to.deep.equal([[ { url: 'http://admin:pass@couchdb:5984/dbname/_security', json: true } ]]); + expect(rpn.put.args).to.deep.equal([[ + { + url: 'http://admin:pass@couchdb:5984/dbname/_security', + json: true, + body: { + admins: { roles: ['role1', 'rolename'], }, + members: { roles: ['role2'] }, + } + } + ]]); + }); + + it('should skip member roles that already exist', async () => { + sinon.stub(env, 'serverUrl').get(() => 'http://admin:pass@couchdb:5984'); + sinon.stub(rpn, 'get').resolves({ admins: { roles: ['role1'] }, members: { roles: ['role2'] } }); + sinon.stub(rpn, 'put').resolves(); + + await db.addRoleAsMember('dbname', 'role2'); + + expect(rpn.get.args).to.deep.equal([[ { url: 'http://admin:pass@couchdb:5984/dbname/_security', json: true } ]]); + expect(rpn.put.called).to.equal(false); + }); + + it('should skip admin roles that already exist', async () => { + sinon.stub(env, 'serverUrl').get(() => 'http://admin:pass@couchdb:5984'); + sinon.stub(rpn, 'get').resolves({ admins: { roles: ['role1'] }, members: { roles: ['role2'] } }); + sinon.stub(rpn, 'put').resolves(); + + await db.addRoleAsAdmin('dbname', 'role1'); + + expect(rpn.get.args).to.deep.equal([[ { url: 'http://admin:pass@couchdb:5984/dbname/_security', json: true } ]]); + expect(rpn.put.called).to.equal(false); + }); + + it('should set members security property if not existing', async () => { + sinon.stub(env, 'serverUrl').get(() => 'http://admin:pass@couchdb:5984'); + sinon.stub(rpn, 'get').resolves({ admins: { roles: ['role1'] } }); + sinon.stub(rpn, 'put').resolves(); + + await db.addRoleAsMember('dbname', 'rolename'); + + expect(rpn.get.args).to.deep.equal([[ { url: 'http://admin:pass@couchdb:5984/dbname/_security', json: true } ]]); + expect(rpn.put.args).to.deep.equal([[ + { + url: 'http://admin:pass@couchdb:5984/dbname/_security', + json: true, + body: { + admins: { roles: ['role1'] }, + members: { roles: ['rolename'], names: [], }, + } + } + ]]); + }); + + it('should not mutate default roles', async () => { + sinon.stub(env, 'serverUrl').get(() => 'http://admin:pass@couchdb:5984'); + sinon.stub(rpn, 'get').callsFake(() => ({ members: {} })); + sinon.stub(rpn, 'put').resolves(); + + await db.addRoleAsMember('dbname1', 'rolename1'); + await db.addRoleAsMember('dbname2', 'rolename2'); + + expect(rpn.put.args).to.deep.equal([[ + { + url: 'http://admin:pass@couchdb:5984/dbname1/_security', + json: true, + body: { + members: { roles: ['rolename1'] }, + } + } + ], [ + { + url: 'http://admin:pass@couchdb:5984/dbname2/_security', + json: true, + body: { + members: { roles: ['rolename2'] }, + } + } + ]]); + }); + + it('should set admins security property if not existing', async () => { + sinon.stub(env, 'serverUrl').get(() => 'http://admin:pwd@host:6984'); + sinon.stub(rpn, 'get').resolves({ members: { roles: ['role2'] } }); + sinon.stub(rpn, 'put').resolves(); + + await db.addRoleAsAdmin('name', 'arole'); + + expect(rpn.get.args).to.deep.equal([[ { url: 'http://admin:pwd@host:6984/name/_security', json: true } ]]); + expect(rpn.put.args).to.deep.equal([[ + { + url: 'http://admin:pwd@host:6984/name/_security', + json: true, + body: { + admins: { roles: ['arole'], names: [] }, + members: { roles: ['role2'] }, + } + } + ]]); + }); + + it('should throw get security errors', async () => { + sinon.stub(env, 'serverUrl').get(() => 'http://admin:pass@couchdb:5984'); + sinon.stub(rpn, 'get').rejects(new Error('not_found')); + + await expect(db.addRoleAsMember('data', 'attr')).to.be.rejectedWith(Error, 'not_found'); + }); + + it('should throw put security errors', async () => { + sinon.stub(env, 'serverUrl').get(() => 'http://admin:pass@couchdb:5984'); + sinon.stub(rpn, 'get').resolves({ members: { roles: ['role2'] } }); + sinon.stub(rpn, 'put').rejects(new Error('forbidden or something')); + + await expect(db.addRoleAsMember('data', 'attr')).to.be.rejectedWith(Error, 'forbidden or something'); + }); + + it('should set default security when security is invalid', async () => { + sinon.stub(env, 'serverUrl').get(() => 'http://admin:pass@couchdb:5984'); + sinon.stub(rpn, 'get').resolves({ members: false }); + sinon.stub(rpn, 'put').resolves(); + + await db.addRoleAsMember('data', 'attr'); + expect(rpn.put.args).to.deep.equal([[ + { + url: 'http://admin:pass@couchdb:5984/data/_security', + json: true, + body: { + members: { roles: ['attr'], names: [], }, + } + } + ]]); + + }); + + it('should throw when missing dbname or role', async () => { + await expect(db.addRoleAsMember('', 'arole')) + .to.be.rejectedWith(Error, `Cannot add security: invalid db name or role arole`); + await expect(db.addRoleAsMember('dbanme', '')) + .to.be.rejectedWith(Error, `Cannot add security: invalid db name dbanme or role`); + }); + }); }); diff --git a/api/tests/mocha/services/config-watcher.spec.js b/api/tests/mocha/services/config-watcher.spec.js index b54a794bd30..7b0a259c535 100644 --- a/api/tests/mocha/services/config-watcher.spec.js +++ b/api/tests/mocha/services/config-watcher.spec.js @@ -13,6 +13,7 @@ const generateXform = require('../../../src/services/generate-xform'); const config = require('../../../src/config'); const bootstrap = require('../../../src/services/config-watcher'); const manifest = require('../../../src/services/manifest'); +const environment = require('../../../src/environment'); describe('Configuration', () => { beforeEach(() => { @@ -217,6 +218,9 @@ describe('Configuration', () => { settingsService.get.resolves({ settings: 'yes' }); const listener = sinon.stub(); bootstrap.watch(listener); + sinon.stub(db, 'addRoleAsMember'); + sinon.stub(config, 'get').withArgs('roles').returns({ chw: {} }); + sinon.stub(environment, 'db').get(() => 'medicdb'); return dbWatcher.medic.args[0][0]({ id: 'settings' }).then(() => { chai.expect(settingsService.update.callCount).to.equal(1); @@ -224,6 +228,37 @@ describe('Configuration', () => { chai.expect(config.set.callCount).to.equal(1); chai.expect(config.set.args[0]).to.deep.equal([{ settings: 'yes' }]); chai.expect(listener.callCount).to.equal(1); + chai.expect(config.get.withArgs('roles').callCount).to.equal(1); + chai.expect(db.addRoleAsMember.args).to.deep.equal([['medicdb', 'chw']]); + }); + }); + + it('should add all configured user roles to the main database', () => { + settingsService.update.resolves(); + settingsService.get.resolves({ settings: 'yes' }); + sinon.stub(db, 'addRoleAsMember'); + sinon.stub(config, 'get') + .withArgs('roles') + .returns({ + chw1: {}, + chw2: {}, + chw3: {}, + chw4: {}, + }); + sinon.stub(environment, 'db').get(() => 'medicdb'); + + return dbWatcher.medic.args[0][0]({ id: 'settings' }).then(() => { + chai.expect(settingsService.update.callCount).to.equal(1); + chai.expect(settingsService.get.callCount).to.equal(1); + chai.expect(config.set.callCount).to.equal(1); + chai.expect(config.set.args[0]).to.deep.equal([{ settings: 'yes' }]); + chai.expect(config.get.withArgs('roles').callCount).to.equal(1); + chai.expect(db.addRoleAsMember.args).to.deep.equal([ + ['medicdb', 'chw1'], + ['medicdb', 'chw2'], + ['medicdb', 'chw3'], + ['medicdb', 'chw4'], + ]); }); }); diff --git a/couchdb/10-docker-default.ini b/couchdb/10-docker-default.ini index 4dfabd06b3c..92e42a2863e 100644 --- a/couchdb/10-docker-default.ini +++ b/couchdb/10-docker-default.ini @@ -1,7 +1,7 @@ ; couchdb/local.d/package.ini [fabric] -request_timeout = infinity +request_timeout = 31536000 ; 1 year in seconds [query_server_config] os_process_limit = 1000 @@ -20,14 +20,12 @@ socket_options = [{sndbuf, 262144}, {nodelay, true}] require_valid_user = true [httpd] -port = 5986 -bind_address = 0.0.0.0 secure_rewrites = false max_http_request_size = 4294967296 ; 4 GB WWW-Authenticate = Basic realm="Medic Mobile Web Services" [couch_httpd_auth] -# timeout is set to 1 year in seconds, 31536000 +; timeout is set to 1 year in seconds, 31536000 timeout = 31536000 allow_persistent_cookies = true require_valid_user = true @@ -39,3 +37,7 @@ ssl_certificate_max_depth = 4 [cluster] q=12 n=1 + +[attachments] +compressible_types = text/*, application/javascript, application/json, application/xml +compression_level = 8 diff --git a/couchdb/Dockerfile b/couchdb/Dockerfile index 6be8b5c7f21..018a2c13c80 100644 --- a/couchdb/Dockerfile +++ b/couchdb/Dockerfile @@ -1,4 +1,4 @@ -FROM couchdb:2.3.1 as base_couchdb_build +FROM couchdb:3.3.2 as base_couchdb_build # Add configuration COPY --chown=couchdb:couchdb 10-docker-default.ini /opt/couchdb/etc/default.d/ diff --git a/couchdb/set-up-cluster.sh b/couchdb/set-up-cluster.sh index c3a40d55984..884234d95d2 100644 --- a/couchdb/set-up-cluster.sh +++ b/couchdb/set-up-cluster.sh @@ -91,20 +91,25 @@ add_peers_to_cluster() { check_cluster_membership } +create_system_databases() { + for db in _users _replicator _global_changes; do + if ! curl -sfX PUT "http://$COUCHDB_USER:$COUCHDB_PASSWORD@$SVC_NAME:5984/$db" > /dev/null; then + echo "Failed to create system database '$db'" + fi + done +} + main(){ check_if_couchdb_is_ready http://$COUCHDB_USER:$COUCHDB_PASSWORD@$SVC_NAME:5984 # only attempt clustering if CLUSTER_PEER_IPS environment variable is present. if [ ! -z "$CLUSTER_PEER_IPS" ]; then enable_cluster add_peers_to_cluster + create_system_databases fi # only attempt to setup initial databases if single node is used if [ -z "$CLUSTER_PEER_IPS" ] && [ -z "$COUCHDB_SYNC_ADMINS_NODE" ]; then - for db in _users _replicator _global_changes; do - if ! curl -sfX PUT "http://$COUCHDB_USER:$COUCHDB_PASSWORD@$SVC_NAME:5984/$db" > /dev/null; then - echo "Failed to create system database '$db'" - fi - done + create_system_databases fi # end process exit 1 diff --git a/tests/e2e/default/db/db-access-for-custom-roles.wdio-spec.js b/tests/e2e/default/db/db-access-for-custom-roles.wdio-spec.js new file mode 100644 index 00000000000..90b1bb1ed8b --- /dev/null +++ b/tests/e2e/default/db/db-access-for-custom-roles.wdio-spec.js @@ -0,0 +1,81 @@ +const uuid = require('uuid').v4; + +const utils = require('@utils'); +const commonPage = require('@page-objects/default/common/common.wdio.page'); +const browserDbUtils = require('@utils/cht-db'); +const loginPage = require('@page-objects/default/login/login.wdio.page'); +const personFactory = require('@factories/cht/contacts/person'); +const placeFactory = require('@factories/cht/contacts/place'); + +const places = placeFactory.generateHierarchy(); +const clinic = places.get('clinic'); + +const contact = personFactory.build({ + parent: { + _id: clinic._id, + parent: clinic.parent + }, + phone: '+254712345670' +}); + +const docs = [...places.values(), contact]; +const newRole = 'new_chw'; + +const addRole = async (role = newRole) => { + const settings = await utils.getSettings(); + settings.roles[role] = { name: newRole, offline: true }; + Object.values(settings.permissions).forEach(roles => { + if (roles.includes('chw')) { + roles.push(newRole); + } + }); + await utils.updateSettings(settings, true); +}; + +const username = uuid(); +const user = { + username: username, + password: uuid(), + place: clinic._id, + contact: contact._id, + roles: [newRole] +}; + +describe('Database access for new roles', () => { + before(async () => { + await utils.saveDocs(docs); + await addRole(newRole); + await utils.createUsers([user]); + }); + + it('user with custom role should be able to log in', async () => { + await loginPage.login({ username: username, password: user.password }); + }); + + it('should be able to sync documents up', async () => { + const report = { + _id: uuid(), + type: 'data_record', + form: 'something', + contact: { _id: contact._id }, + fields: { patient_id: contact._id, }, + }; + await browserDbUtils.createDoc(report); + await commonPage.sync(); + + await utils.getDoc(report._id); + }); + + it('should be able to sync documents down', async () => { + const report = { + _id: uuid(), + type: 'data_record', + form: 'something', + contact: { _id: contact._id }, + fields: { patient_id: contact._id, }, + }; + await utils.saveDoc(report); + await commonPage.sync(); + await browserDbUtils.getDoc(report._id); + }); +}); diff --git a/tests/e2e/default/users/create-meta-db.wdio-spec.js b/tests/e2e/default/users/create-meta-db.wdio-spec.js index 9b9239aff1b..3fc07fcedc0 100644 --- a/tests/e2e/default/users/create-meta-db.wdio-spec.js +++ b/tests/e2e/default/users/create-meta-db.wdio-spec.js @@ -1,8 +1,9 @@ const _ = require('lodash'); const utils = require('@utils'); const usersPage = require('@page-objects/default/users/user.wdio.page'); -const commonElements = require('@page-objects/default/common/common.wdio.page'); +const commonPage = require('@page-objects/default/common/common.wdio.page'); const loginPage = require('@page-objects/default/login/login.wdio.page'); +const uuid = require('uuid').v4; const username = 'fulltester'; const fullName = 'Roger Milla'; @@ -23,21 +24,26 @@ describe('Create user meta db : ', () => { await usersPage.openAddUserDialog(); await usersPage.inputAddUserFields(username, fullName, 'program_officer', '', '', password); await usersPage.saveUser(); - await commonElements.goToMessages(); - await commonElements.logout(); + await commonPage.goToMessages(); + await commonPage.logout(); await loginPage.login({ username, password }); - await commonElements.waitForPageLoaded(); + await commonPage.waitForPageLoaded(); + await commonPage.goToReports(); - const doc = { _id: 'this is a random uuid' }; + const doc = { _id: uuid() }; await utils.requestOnTestMetaDb(_.defaults({ method: 'POST', body: doc }, options)); const response = await utils.requestOnTestMetaDb(_.defaults({ path: '/_changes' }, options)); const changes = response.results; - expect(changes.length).to.equal(2); - const ids = changes.map(change => change.id).sort(); - expect(ids[0]).to.equal('_design/medic-user'); - expect(ids[1]).to.equal(doc._id); + const ids = changes.map(change => change.id); + expect(ids).to.include.members(['_design/medic-user', doc._id]); + + // admins can also read and write from the users meta db + const adminDoc = { _id: uuid() }; + await utils.requestOnTestMetaDb({ method: 'POST', body: adminDoc, userName: options.userName }); + const adminChanges = await utils.requestOnTestMetaDb({ path: '/_changes', userName: options.userName }); + const adminChangeIds = adminChanges.results.map(change => change.id); + expect(adminChangeIds).to.include.members(['_design/medic-user', doc._id, adminDoc._id]); }); - }); diff --git a/tests/integration/api/routing.spec.js b/tests/integration/api/routing.spec.js index 724ba23e83e..a9c014094b0 100644 --- a/tests/integration/api/routing.spec.js +++ b/tests/integration/api/routing.spec.js @@ -898,12 +898,12 @@ describe('routing', () => { describe('admin access to Fauxton', () => { it('should allow access with a trailing slash', async () => { const response = await utils.request({ path: '/_utils/', json: false }); - expect(response).to.include('Fauxton Release'); + expect(response).to.include('Project Fauxton'); }); it('should allow access without a trailing slash', async () => { const response = await utils.request({ path: '/_utils', json: false }); - expect(response).to.include('Fauxton Release'); + expect(response).to.include('Project Fauxton'); }); }); }); diff --git a/tests/integration/api/server.spec.js b/tests/integration/api/server.spec.js index bab82c7208a..dab44698b7d 100644 --- a/tests/integration/api/server.spec.js +++ b/tests/integration/api/server.spec.js @@ -217,6 +217,7 @@ describe('server', () => { await utils.stopApi(); await utils.startApi(false); await utils.startHaproxy(); + await utils.delayPromise(1000); await utils.listenForApi(); await utils.delayPromise(1000); diff --git a/tests/integration/cht-conf/cht-conf-actions.spec.js b/tests/integration/cht-conf/cht-conf-actions.spec.js index 049c92204d1..f251a1e48ce 100644 --- a/tests/integration/cht-conf/cht-conf-actions.spec.js +++ b/tests/integration/cht-conf/cht-conf-actions.spec.js @@ -35,7 +35,6 @@ describe('cht-conf actions tests', () => { const result = await runCommand('upload-app-settings', configPath); expect(result).to.contain(`INFO Settings updated successfully`); const settings = await utils.getDoc('settings'); - console.log('newVersion', settings._rev); const newVersion = Number(settings._rev.charAt(0)); expect(newVersion).to.be.greaterThanOrEqual(originalVersion); expect(settings.settings.roles).to.include.all.keys('program_officer', 'chw_supervisor', 'chw'); @@ -51,7 +50,7 @@ describe('cht-conf actions tests', () => { it('should upload branding', async () => { const branding = await utils.getDoc('branding').catch(error => { if (error){ - console.log(error); + console.error(error); } }); expect(branding.title).to.equal('Medic'); @@ -63,7 +62,7 @@ describe('cht-conf actions tests', () => { method: 'GET' }).catch(error => { if (error){ - console.log(error); + console.error(error); } }); expect(forms).to.deep.equal([ @@ -92,7 +91,7 @@ describe('cht-conf actions tests', () => { it('should upload resources', async () => { const resources = await utils.getDoc('resources').catch(error => { if (error){ - console.log(error); + console.error(error); } }); expect(resources.resources).to.deep.equal({ diff --git a/tests/integration/couchdb/couch_chttpd.spec.js b/tests/integration/couchdb/couch_chttpd.spec.js index e492a485b78..a17cfa989d9 100644 --- a/tests/integration/couchdb/couch_chttpd.spec.js +++ b/tests/integration/couchdb/couch_chttpd.spec.js @@ -34,11 +34,28 @@ const getLogs = async () => { } }; -describe('accessing couch_httpd', () => { - it('should not be available to the host', async () => { +const expectCorrectMetadata = (metadata) => { + expect(metadata._id).to.equal(constants.DB_NAME); + // 12 shards, evenly distributed across 3 nodes + expect(Object.keys(metadata.by_range).length).to.equal(12); + expect(metadata.by_node).to.have.keys([ + 'couchdb@couchdb-1.local', 'couchdb@couchdb-2.local', 'couchdb@couchdb-3.local' + ]); + expect(metadata.by_node['couchdb@couchdb-1.local'].length).to.equal(4); + expect(metadata.by_node['couchdb@couchdb-2.local'].length).to.equal(4); + expect(metadata.by_node['couchdb@couchdb-3.local'].length).to.equal(4); +}; + +describe('accessing couch clustering endpoint', () => { + it('should block unauthenticated access through the host network', async () => { await expect( - utils.request({ uri: `https://localhost:5986/_dbs/${constants.DB_NAME}` }) - ).to.be.rejectedWith('Error: connect ECONNREFUSED 127.0.0.1:5986'); + utils.request({ uri: `https://localhost/_node/_local/_dbs/${constants.DB_NAME}`, noAuth: true }) + ).to.be.rejectedWith(Error, 'unauthorized'); + }); + + it('should allow authenticated access through host network', async () => { + const metadata = await utils.request({ path: `/_node/_local/_dbs/${constants.DB_NAME}` }); + expectCorrectMetadata(metadata); }); it('should block unauthenticated access through docker network', async () => { @@ -53,14 +70,7 @@ describe('accessing couch_httpd', () => { const logs = await getLogs(); expect(logs.length).to.equal(1); const metadata = logs[0]; - expect(metadata._id).to.equal(constants.DB_NAME); - // 12 shards, evenly distributed across 3 nodes - expect(Object.keys(metadata.by_range).length).to.equal(12); - expect(metadata.by_node).to.have.keys([ - 'couchdb@couchdb-1.local', 'couchdb@couchdb-2.local', 'couchdb@couchdb-3.local' - ]); - expect(metadata.by_node['couchdb@couchdb-1.local'].length).to.equal(4); - expect(metadata.by_node['couchdb@couchdb-2.local'].length).to.equal(4); - expect(metadata.by_node['couchdb@couchdb-3.local'].length).to.equal(4); + expectCorrectMetadata(metadata); }); }); + diff --git a/tests/integration/couchdb/couch_httpd_script/index.js b/tests/integration/couchdb/couch_httpd_script/index.js index 3183fccb0fe..6486ec76e45 100644 --- a/tests/integration/couchdb/couch_httpd_script/index.js +++ b/tests/integration/couchdb/couch_httpd_script/index.js @@ -4,8 +4,8 @@ const { COUCH_AUTH } = process.env; (() => { const options = { hostname: 'couchdb-1.local', - port: 5986, - path: '/_dbs/medic-test', + port: 5984, + path: '/_node/_local/_dbs/medic-test', method: 'GET', auth: COUCH_AUTH, headers: { diff --git a/tests/integration/sentinel/queue.spec.js b/tests/integration/sentinel/queue.spec.js index ee63aec74b7..ebc75622ccc 100644 --- a/tests/integration/sentinel/queue.spec.js +++ b/tests/integration/sentinel/queue.spec.js @@ -103,7 +103,7 @@ describe('Sentinel queue drain', () => { expect(info.transitions.update_clinics.ok).to.be.true; }); }); - }).timeout(300 * 1000); + }).timeout(400 * 1000); it('queue should work after restarting haproxy', async () => { await utils.stopHaproxy(); // this will also crash Sentinel and API diff --git a/tests/integration/transitions/utils.js b/tests/integration/transitions/utils.js index 5f54d3f0ef4..f7bf7c33143 100644 --- a/tests/integration/transitions/utils.js +++ b/tests/integration/transitions/utils.js @@ -14,7 +14,7 @@ const getApiSmsChanges = (messages) => { const timeout = setTimeout(() => { listener.cancel(); reject('timer expired'); - }, 5000); + }, 10000); listener.on('change', change => { if (change.doc.sms_message) { if (ids.includes(change.id)) {