X-Git-Url: http://git.squeep.com/?p=squeep-authentication-module;a=blobdiff_plain;f=test%2Flib%2Fsession-manager.js;h=79abbf007e8805ab05018aa77a5aae541489c62c;hp=a3efeafce3b812fcfd3961393e4e015a5986afad;hb=2ca511865b0caf3108819cfd6ee775124ea70dff;hpb=8b998e55749e8613c0dece7a156b5edf83fb3608 diff --git a/test/lib/session-manager.js b/test/lib/session-manager.js index a3efeaf..79abbf0 100644 --- a/test/lib/session-manager.js +++ b/test/lib/session-manager.js @@ -7,6 +7,7 @@ const assert = require('assert'); const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require const SessionManager = require('../../lib/session-manager'); +const Enum = require('../../lib/enum'); const Config = require('../stub-config'); const stubLogger = require('../stub-logger'); @@ -25,9 +26,11 @@ describe('SessionManager', function () { params: {}, queryParams: {}, parsedBody: {}, + errors: [], }; stubAuthenticator = { isValidIdentifierCredential: sinon.stub(), + checkOTP: sinon.stub(), }; manager = new SessionManager(stubLogger, stubAuthenticator, options); sinon.stub(manager.indieAuthCommunication); @@ -61,12 +64,27 @@ describe('SessionManager', function () { }); // _sessionCookieSet describe('getAdminLogin', function () { - it('covers', async function () { + it('covers no session', async function () { + await manager.getAdminLogin(res, ctx); + }); + it('covers established session', async function () { + ctx.authenticationId = 'identifier'; + ctx.queryParams['r'] = '/admin'; await manager.getAdminLogin(res, ctx); + assert.strictEqual(res.statusCode, 302); + assert(res.setHeader.called); }); }); // getAdminLogin describe('postAdminLogin', function () { + beforeEach(function () { + sinon.stub(manager, '_otpSubmission').resolves(false); + }); + it('covers otp submission', async function () { + manager._otpSubmission.resolves(true); + await manager.postAdminLogin(res, ctx); + assert(res.end.notCalled); + }); it('covers valid local', async function () { ctx.parsedBody.identifier = 'user'; ctx.parsedBody.credential = 'password'; @@ -115,15 +133,15 @@ describe('SessionManager', function () { }); it('covers profile scheme fallback', async function () { ctx.parsedBody.me = 'https://example.com/profile'; - ctx.parsedBody.me_auto_scheme = '1'; + ctx.parsedBody['me_auto_scheme'] = '1'; manager.indieAuthCommunication.fetchProfile .onCall(0).resolves() .onCall(1).resolves({ - metadata: { - issuer: 'https://example.com/', - authorizationEndpoint: 'https://example.com/auth', - }, - }); + metadata: { + issuer: 'https://example.com/', + authorizationEndpoint: 'https://example.com/auth', + }, + }); await manager.postAdminLogin(res, ctx); assert.strictEqual(res.statusCode, 302); @@ -165,6 +183,103 @@ describe('SessionManager', function () { }); // living-standard-20220212 }); // postAdminLogin + describe('_otpSubmission', function () { + beforeEach(function () { + sinon.useFakeTimers(new Date()); + sinon.stub(manager.mysteryBox, 'unpack').resolves({ + authenticatedIdentifier: 'identifier', + attempt: 0, + epochMs: Date.now(), + }); + manager.authenticator.checkOTP.resolves(Enum.OTPResult.Valid); + ctx.parsedBody.state = 'state_data'; + ctx.parsedBody.otp = '123456'; + }); + it('returns false if no otp state', async function () { + delete ctx.parsedBody.state; + const result = await manager._otpSubmission(res, ctx); + assert(manager.mysteryBox.unpack.notCalled); + assert.strictEqual(result, false); + }); + it('returns false when presented with invalid otp state', async function () { + manager.mysteryBox.unpack.rejects(); + const result = await manager._otpSubmission(res, ctx); + assert(manager.mysteryBox.unpack.called); + assert.strictEqual(result, false); + }); + it('returns true when submitted otp is invalid, but allowed to retry', async function () { + manager.authenticator.checkOTP.resolves(Enum.OTPResult.InvalidSoftFail); + const result = await manager._otpSubmission(res, ctx); + assert(manager.mysteryBox.unpack.called); + assert.strictEqual(result, true); + assert(res.end.called); + }); + it('returns false when submitted otp is invalid and too many attempts', async function () { + manager.mysteryBox.unpack.resolves({ + authenticatedIdentifier: 'identifier', + attempt: 10, + epochMs: Date.now(), + }); + manager.authenticator.checkOTP.resolves(Enum.OTPResult.InvalidHardFail); + const result = await manager._otpSubmission(res, ctx); + assert(manager.mysteryBox.unpack.called); + assert.strictEqual(result, false); + }); + it('returns false when submitted otp is invalid and too much time has passed', async function () { + manager.mysteryBox.unpack.resolves({ + authenticatedIdentifier: 'identifier', + attempt: 0, + epochMs: Date.now() - 99999999, + }); + manager.authenticator.checkOTP.resolves(Enum.OTPResult.InvalidHardFail); + const result = await manager._otpSubmission(res, ctx); + assert(manager.mysteryBox.unpack.called); + assert.strictEqual(result, false); + }); + it('returns true when submitted otp is valid', async function () { + const result = await manager._otpSubmission(res, ctx); + assert(res.end.called); + assert.strictEqual(result, true); + }); + it('covers unexpected otp response', async function () { + manager.authenticator.checkOTP.resolves('wrong'); + assert.rejects(() => manager._otpSubmission(res, ctx), RangeError); + }); + }); // _otpSubmission + + describe('_localUserAuth', function () { + beforeEach(function () { + ctx.parsedBody.identifier = 'identifier'; + ctx.parsedBody.credential = 'credential'; + manager.authenticator.isValidIdentifierCredential.resolves(true); + sinon.stub(manager.mysteryBox, 'pack').resolves('box'); + }); + it('returns false if indieauth available', async function () { + ctx.parsedBody.me = 'https://example.com/'; + const result = await manager._localUserAuth(res, ctx); + assert.strictEqual(result, false); + }); + it('returns true if identifier is invalid', async function () { + manager.authenticator.isValidIdentifierCredential.resolves(false); + const result = await manager._localUserAuth(res, ctx); + assert.strictEqual(result, true); + assert(manager.authenticator.isValidIdentifierCredential.called); + assert(res.end.called); + }); + it('returns true if valid identifier', async function () { + const result = await manager._localUserAuth(res, ctx); + assert.strictEqual(result, true); + assert(res.end.called); + }); + it('returns true if valid identifier requires otp entry', async function () { + ctx.otpNeeded = true; + const result = await manager._localUserAuth(res, ctx); + assert.strictEqual(result, true); + assert(manager.mysteryBox.pack.called); + assert(res.end.called); + }); + }); // _localUserAuth + describe('getAdminLogout', function () { it('covers', async function () { await manager.getAdminLogout(res, ctx);