diff --git a/lib/http/service.js b/lib/http/service.js index 19d6070dc2..2b0d6441e 100644 --- a/lib/http/service.js +++ b/lib/http/service.js @@ -11,6 +11,7 @@ // defined elsewhere into an actual Express service. The only thing it needs in // order to do this is a valid dependency injection context container. const { match } = require('path-to-regexp'); +const { without } = require('ramda'); module.exports = (container) => { const service = require('express')(); @@ -62,7 +63,10 @@ module.exports = (container) => { const { builder } = require('./endpoint'); const { emptyAuthInjector, authHandler, queryOptionsHandler, userAgentHandler } = require('./preprocessors'); - const endpoint = builder(container, [ emptyAuthInjector, authHandler, queryOptionsHandler, userAgentHandler ]); + + const preprocessors = [ emptyAuthInjector, authHandler, queryOptionsHandler, userAgentHandler ]; + const endpoint = builder(container, preprocessors); + const anonymousEndpoint = builder(container, without([authHandler], preprocessors)); //////////////////////////////////////////////////////////////////////////////// @@ -73,7 +77,7 @@ module.exports = (container) => { require('../resources/odata-entities')(service, endpoint); require('../resources/forms')(service, endpoint); require('../resources/users')(service, endpoint); - require('../resources/sessions')(service, endpoint); + require('../resources/sessions')(service, endpoint, anonymousEndpoint); require('../resources/submissions')(service, endpoint); require('../resources/config')(service, endpoint); require('../resources/projects')(service, endpoint); diff --git a/lib/resources/sessions.js b/lib/resources/sessions.js index 4f448ca60..915a4ae85 100644 --- a/lib/resources/sessions.js +++ b/lib/resources/sessions.js @@ -16,10 +16,10 @@ const { SESSION_COOKIE, createUserSession } = require('../http/sessions'); const oidc = require('../util/oidc'); const { partial } = require('ramda'); -module.exports = (service, endpoint) => { +module.exports = (service, endpoint, anonymousEndpoint) => { if (!oidc.isEnabled()) { - service.post('/sessions', endpoint(({ Audits, Users, Sessions }, { body, headers }) => { + service.post('/sessions', anonymousEndpoint(({ Audits, Users, Sessions }, { body, headers }) => { // TODO if we're planning to offer multiple authN methods, we should be looking for // any calls to verifyPassword(), and blocking them if that authN method is not // appropriate for the current user. diff --git a/test/integration/api/sessions.js b/test/integration/api/sessions.js index 22d4d8cdb..02532606d 100644 --- a/test/integration/api/sessions.js +++ b/test/integration/api/sessions.js @@ -14,6 +14,32 @@ describe('api: /sessions', () => { body.should.be.a.Session(); }))); + [ + { desc: 'invalid session cookie', header: 'Cookie', value: 'session=invalid' }, + { desc: 'invalid bearer token', header: 'Authorization', value: 'Bearer invalid' }, + { desc: 'invalid basic auth', header: 'Authorization', value: 'Basic invalid' }, + ].forEach(t => { + it(`should return a new session even if invalid auth is passed in the header - ${t.desc}`, testService((service) => + service.post('/v1/sessions') + .set(t.header, t.value) + .set('x-forwarded-proto', 'https') + .send({ email: 'chelsea@getodk.org', password: 'password4chelsea' }) + .expect(200) + .then(({ body }) => { + body.should.be.a.Session(); + }))); + }); + + it('should return a new session even if valid cookie is passed', testService((service) => + service.post('/v1/sessions') + .send({ email: 'chelsea@getodk.org', password: 'password4chelsea' }) + .expect(200) + .then(({ body }) => service.post('/v1/sessions') + .set('x-forwarded-proto', 'https') + .set('Cookie', `session=${body.token}`) + .send({ email: 'chelsea@getodk.org', password: 'password4chelsea' }) + .expect(200)))); + // These demonstrate a strange feature of bcrypt - a valid password can be // repeated multiple times and still validate successfully. An alternative // to these tests would be to check for NUL characters in supplied passwords