X-Git-Url: https://git.squeep.com/?a=blobdiff_plain;f=test%2Flib%2Fauthenticator.js;h=75761689556c9adda18a7773901037c34bb2b71b;hb=842a3da269de1ab82e9a2a12aae8ed5677f064d8;hp=22fc9d218eb3953f154cef9bf3d2b692332cf4d3;hpb=c511cb4c6d0e14972de87f46921908ff2468d55b;p=squeep-authentication-module diff --git a/test/lib/authenticator.js b/test/lib/authenticator.js index 22fc9d2..7576168 100644 --- a/test/lib/authenticator.js +++ b/test/lib/authenticator.js @@ -6,12 +6,9 @@ const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-requi const Authenticator = require('../../lib/authenticator'); const stubLogger = require('../stub-logger'); const stubDb = require('../stub-db'); -const Errors = require('../../lib/errors'); const Enum = require('../../lib/enum'); const Config = require('../stub-config'); -const noExpectedException = 'did not receive expected exception'; - describe('Authenticator', function () { let authenticator, credential, ctx, identifier, password, options; function _authMechanismRequired(a, m) { @@ -35,12 +32,9 @@ describe('Authenticator', function () { it('covers no auth mechanisms', function () { options.authenticator.authnEnabled = []; - try { - authenticator = new Authenticator(stubLogger, stubDb, options); - assert.fail(noExpectedException); - } catch (e) { - assert.strictEqual(e.message, 'no authentication mechanisms available'); - } + assert.throws(() => new Authenticator(stubLogger, stubDb, options), { + message: 'no authentication mechanisms available', + }); }); it('covers empty realm', function () { @@ -117,6 +111,12 @@ describe('Authenticator', function () { assert.strictEqual(result, false); assert.strictEqual(ctx.authenticationId, undefined); }); + it('covers non-string credential', async function () { + credential = '$argon2id$v=19$m=4096,t=3,p=1$SbAlHo5x2HM0PvMAWYHqww$gNn/o+B6+IWsnrVupPkTAiiK9tvwV+eM/HoXG41bnzM'; + const result = await authenticator.isValidIdentifierCredential(identifier, undefined, ctx); + assert.strictEqual(result, false); + assert.strictEqual(ctx.authenticationId, undefined); + }); it('covers unknown password hash', async function () { authenticator.db.authenticationGet.resolves({ identifier, @@ -165,12 +165,7 @@ describe('Authenticator', function () { _authMechanismRequired(authenticator, 'pam'); const expected = new Error('blah'); authenticator.authn.pam.pamAuthenticatePromise.rejects(expected); - try { - await authenticator._isValidPAMIdentifier(identifier, credential); - assert.fail(noExpectedException); - } catch (e) { - assert.deepStrictEqual(e, expected); - } + assert.rejects(() => authenticator._isValidPAMIdentifier(identifier, credential), expected); }); it('covers forbidden', async function () { identifier = 'root'; @@ -222,16 +217,13 @@ describe('Authenticator', function () { describe('requestBasic', function () { it('covers', function () { - try { - const res = { - setHeader: () => {}, - }; - authenticator.requestBasic(res); - assert.fail(noExpectedException); - } catch (e) { - assert(e instanceof Errors.ResponseError); - assert.strictEqual(e.statusCode, Enum.ErrorResponse.Unauthorized.statusCode); - } + const res = { + setHeader: () => {}, + }; + assert.throws(() => authenticator.requestBasic(res), { + name: 'ResponseError', + statusCode: Enum.ErrorResponse.Unauthorized.statusCode, + }); }); }); // requestBasic @@ -274,6 +266,40 @@ describe('Authenticator', function () { }); }); // isValidCookieAuth + describe('checkOTP', function () { + let state, otp; + this.beforeEach(function () { + sinon.stub(authenticator.TOTP.prototype, 'validate').returns(true); + state = { + key: Buffer.from('12345678901234567890'), + attempt: 0, + epochMs: Date.now(), + }; + otp = '000000'; + }); + it('covers valid OTP entry', function () { + const result = authenticator.checkOTP(state, otp); + assert.strictEqual(result, Enum.OTPResult.Valid); + }); + it('covers invalid OTP entry', function () { + authenticator.TOTP.prototype.validate.returns(false); + const result = authenticator.checkOTP(state, otp); + assert.strictEqual(result, Enum.OTPResult.InvalidSoftFail); + }); + it('covers invalid OTP entry, too many failures', function () { + state.attempt = 10; + authenticator.TOTP.prototype.validate.returns(false); + const result = authenticator.checkOTP(state, otp); + assert.strictEqual(result, Enum.OTPResult.InvalidHardFail); + }); + it('covers invalid OTP entry', function () { + state.epochMs = 0; + authenticator.TOTP.prototype.validate.returns(false); + const result = authenticator.checkOTP(state, otp); + assert.strictEqual(result, Enum.OTPResult.InvalidHardFail); + }); + }); // checkOTP + describe('sessionCheck', function () { let cookie, req, res, loginPath, required, profilesAllowed; beforeEach(function () { @@ -299,15 +325,22 @@ describe('Authenticator', function () { const result = await authenticator.sessionCheck(req, res, ctx, loginPath, required, profilesAllowed); assert.strictEqual(result, true); }); + it('covers valid insecure cookie session', async function () { + authenticator.secureAuthOnly = false; + req.getHeader.returns(cookie); + sinon.stub(authenticator, 'isValidCookieAuth').resolves(true); + ctx.session = { + authenticatedIdentifier: 'user', + }; + const result = await authenticator.sessionCheck(req, res, ctx, loginPath, required, profilesAllowed); + assert.strictEqual(result, true); + }); it('rejects insecure connection', async function () { ctx.clientProtocol = 'http'; - try { - await authenticator.sessionCheck(req, res, ctx, loginPath, required, profilesAllowed); - assert.fail(noExpectedException); - } catch (e) { - assert(e instanceof Errors.ResponseError); - assert.strictEqual(e.statusCode, Enum.ErrorResponse.Forbidden.statusCode); - } + assert.rejects(() => authenticator.sessionCheck(req, res, ctx, loginPath, required, profilesAllowed), { + name: 'ResponseError', + sttausCode: Enum.ErrorResponse.Forbidden.statusCode, + }); }); it('ignores insecure connection if auth not required', async function () { ctx.clientProtocol = 'http'; @@ -431,14 +464,12 @@ describe('Authenticator', function () { req.getHeader.returns('Basic Zm9vOmJhcg=='); sinon.stub(authenticator, 'sessionCheck').resolves(false); sinon.stub(authenticator, 'isValidAuthorization').resolves(false); - try { - await authenticator.apiRequiredLocal(req, res, ctx); - assert.fail(noExpectedException); - } catch (e) { - assert.strictEqual(e.statusCode, 401); - assert(!authenticator.sessionCheck.called); - assert(authenticator.isValidAuthorization.called); - } + assert.rejects(() => authenticator.apiRequiredLocal(req, res, ctx), { + name: 'ResponseError', + statusCode: 401, + }); + assert(!authenticator.sessionCheck.called); + assert(authenticator.isValidAuthorization.called); }); it('covers missing basic auth, valid session', async function () { req.getHeader.returns(); @@ -452,15 +483,13 @@ describe('Authenticator', function () { it('covers missing basic auth, ignores session', async function () { req.getHeader.returns(); sinon.stub(authenticator, 'isValidAuthorization').resolves(true); - try { - await authenticator.apiRequiredLocal(req, res, ctx, false); - assert.fail(noExpectedException); - } catch (e) { - assert.strictEqual(e.statusCode, 401); - assert(!authenticator.sessionCheck.called); - assert(!authenticator.isValidAuthorization.called); - assert(res.setHeader.called); - } + assert.rejects(authenticator.apiRequiredLocal(req, res, ctx, false), { + name: 'ResponseError', + statusCode: 401, + }); + assert(!authenticator.sessionCheck.called); + assert(!authenticator.isValidAuthorization.called); + assert(res.setHeader.called); }); }); // apiRequiredLocal