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');
params: {},
queryParams: {},
parsedBody: {},
+ errors: [],
};
stubAuthenticator = {
isValidIdentifierCredential: sinon.stub(),
+ checkOTP: sinon.stub(),
};
manager = new SessionManager(stubLogger, stubAuthenticator, options);
sinon.stub(manager.indieAuthCommunication);
}); // _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';
});
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);
}); // 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);