daff0501e3f0cab2cd8a00a414cf01c4a30ab303
[squeep-authentication-module] / test / lib / session-manager.js
1 /* eslint-env mocha */
2 /* eslint-disable capitalized-comments, sonarjs/no-duplicate-string, sonarjs/no-identical-functions */
3
4 'use strict';
5
6 const assert = require('assert');
7 const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require
8
9 const SessionManager = require('../../lib/session-manager');
10 const Config = require('../stub-config');
11 const stubLogger = require('../stub-logger');
12
13 describe('SessionManager', function () {
14 let manager, options, stubAuthenticator;
15 let res, ctx;
16
17 beforeEach(function () {
18 options = new Config('test');
19 res = {
20 end: sinon.stub(),
21 setHeader: sinon.stub(),
22 };
23 ctx = {
24 cookie: '',
25 params: {},
26 queryParams: {},
27 parsedBody: {},
28 };
29 stubAuthenticator = {
30 isValidIdentifierCredential: sinon.stub(),
31 };
32 manager = new SessionManager(stubLogger, stubAuthenticator, options);
33 sinon.stub(manager.indieAuthCommunication);
34 stubLogger._reset();
35 });
36 afterEach(function () {
37 sinon.restore();
38 });
39
40 describe('_sessionCookieSet', function () {
41 let session, maxAge;
42 beforeEach(function () {
43 session = {};
44 maxAge = 86400;
45 });
46 it('covers', async function () {
47 await manager._sessionCookieSet(res, session, maxAge);
48 assert(res.setHeader.called);
49 });
50 it('covers reset', async function () {
51 session = undefined;
52 maxAge = 0;
53 await manager._sessionCookieSet(res, session, maxAge);
54 assert(res.setHeader.called);
55 });
56 it('covers options', async function() {
57 options.authenticator.secureAuthOnly = false;
58 await manager._sessionCookieSet(res, session, undefined, '');
59 assert(res.setHeader.called);
60 });
61 }); // _sessionCookieSet
62
63 describe('getAdminLogin', function () {
64 it('covers', async function () {
65 await manager.getAdminLogin(res, ctx);
66 });
67 }); // getAdminLogin
68
69 describe('postAdminLogin', function () {
70 it('covers valid local', async function () {
71 ctx.parsedBody.identifier = 'user';
72 ctx.parsedBody.credential = 'password';
73 manager.authenticator.isValidIdentifierCredential.resolves(true);
74 await manager.postAdminLogin(res, ctx);
75 assert.strictEqual(res.statusCode, 302);
76 });
77 it('covers invalid local', async function () {
78 ctx.parsedBody.identifier = 'user';
79 ctx.parsedBody.credential = 'password';
80 manager.authenticator.isValidIdentifierCredential.resolves(false);
81 await manager.postAdminLogin(res, ctx);
82 assert(!res.setHeader.called);
83 });
84 it('covers valid profile', async function () {
85 ctx.parsedBody.me = 'https://example.com/profile';
86 manager.indieAuthCommunication.fetchProfile.resolves({
87 authorizationEndpoint: 'https://example.com/auth',
88 });
89 await manager.postAdminLogin(res, ctx);
90 assert.strictEqual(res.statusCode, 302);
91 });
92 it('covers invalid profile', async function () {
93 ctx.parsedBody.me = 'not a profile';
94 manager.indieAuthCommunication.fetchProfile.resolves();
95 await manager.postAdminLogin(res, ctx);
96 assert(!res.setHeader.called);
97 });
98 it('covers invalid profile response', async function () {
99 ctx.parsedBody.me = 'https://example.com/profile';
100 manager.indieAuthCommunication.fetchProfile.resolves();
101 await manager.postAdminLogin(res, ctx);
102 assert(!res.setHeader.called);
103 });
104 it('covers invalid profile response endpoint', async function () {
105 ctx.parsedBody.me = 'https://example.com/profile';
106 manager.indieAuthCommunication.fetchProfile.resolves({
107 authorizationEndpoint: 'not an auth endpoint',
108 });
109 await manager.postAdminLogin(res, ctx);
110 assert(!res.setHeader.called);
111 });
112 }); // postAdminLogin
113
114 describe('getAdminLogout', function () {
115 it('covers', async function () {
116 await manager.getAdminLogout(res, ctx);
117 });
118 }); // getAdminLogout
119
120 describe('getAdminIA', function () {
121 let state, me, authorizationEndpoint;
122 beforeEach(function () {
123 state = '4ea7e936-3427-11ec-9f4b-0025905f714a';
124 me = 'https://example.com/profile';
125 authorizationEndpoint = 'https://example.com/auth'
126 ctx.cookie = 'squeepSession=sessionCookie';
127 manager.indieAuthCommunication.redeemProfileCode.resolves({
128 me,
129 });
130 manager.indieAuthCommunication.fetchProfile.resolves({
131 authorizationEndpoint,
132 });
133 sinon.stub(manager.mysteryBox, 'unpack').resolves({
134 authorizationEndpoint,
135 state,
136 me,
137 });
138 });
139 it('covers valid', async function () {
140 ctx.queryParams['state'] = state;
141 ctx.queryParams['code'] = 'codeCodeCode';
142
143 await manager.getAdminIA(res, ctx);
144
145 assert.strictEqual(res.statusCode, 302);
146 });
147 it('covers missing cookie', async function () {
148 delete ctx.cookie;
149
150 await manager.getAdminIA(res, ctx);
151
152 assert(ctx.errors.length);
153 });
154 it('covers invalid cookie', async function () {
155 manager.mysteryBox.unpack.restore();
156 sinon.stub(manager.mysteryBox, 'unpack').rejects();
157
158 await manager.getAdminIA(res, ctx);
159
160 assert(ctx.errors.length);
161 });
162 it('covers mis-matched state', async function () {
163 ctx.queryParams['state'] = 'incorrect-state';
164 ctx.queryParams['code'] = 'codeCodeCode';
165
166 await manager.getAdminIA(res, ctx);
167
168 assert(ctx.errors.length);
169 });
170 it('relays auth endpoint errors', async function () {
171 ctx.queryParams['state'] = state;
172 ctx.queryParams['code'] = 'codeCodeCode';
173 ctx.queryParams['error'] = 'error_code';
174 ctx.queryParams['error_description'] = 'something went wrong';
175
176 await manager.getAdminIA(res, ctx);
177
178 assert(ctx.errors.length);
179 });
180 it('covers empty error_description', async function () {
181 ctx.queryParams['state'] = state;
182 ctx.queryParams['code'] = 'codeCodeCode';
183 ctx.queryParams['error'] = 'error_code';
184
185 await manager.getAdminIA(res, ctx);
186
187 assert(ctx.errors.length);
188 });
189 it('covers invalid restored session', async function () {
190 manager.mysteryBox.unpack.restore();
191 sinon.stub(manager.mysteryBox, 'unpack').resolves({
192 authorizationEndpoint: 'not a url',
193 state,
194 me,
195 });
196 ctx.queryParams['state'] = state;
197 ctx.queryParams['code'] = 'codeCodeCode';
198
199 await manager.getAdminIA(res, ctx);
200
201 assert(ctx.errors.length);
202 });
203 it('covers empty profile redemption response', async function () {
204 ctx.queryParams['state'] = state;
205 ctx.queryParams['code'] = 'codeCodeCode';
206 manager.indieAuthCommunication.redeemProfileCode.restore();
207 sinon.stub(manager.indieAuthCommunication, 'redeemProfileCode').resolves();
208
209 await manager.getAdminIA(res, ctx);
210
211 assert(ctx.errors.length);
212 });
213 it('covers missing profile in redemption response', async function () {
214 ctx.queryParams['state'] = state;
215 ctx.queryParams['code'] = 'codeCodeCode';
216 manager.indieAuthCommunication.redeemProfileCode.restore();
217 sinon.stub(manager.indieAuthCommunication, 'redeemProfileCode').resolves({
218 });
219
220 await manager.getAdminIA(res, ctx);
221
222 assert(ctx.errors.length);
223 });
224 it('covers different canonical profile response', async function () {
225 ctx.queryParams['state'] = state;
226 ctx.queryParams['code'] = 'codeCodeCode';
227 manager.indieAuthCommunication.redeemProfileCode.restore();
228 sinon.stub(manager.indieAuthCommunication, 'redeemProfileCode').resolves({
229 me: 'https://different.example.com/profile',
230 });
231
232 await manager.getAdminIA(res, ctx);
233
234 assert.strictEqual(res.statusCode, 302);
235 });
236 it('covers different canonical profile response mis-matched endpoint', async function () {
237 ctx.queryParams['state'] = state;
238 ctx.queryParams['code'] = 'codeCodeCode';
239 manager.indieAuthCommunication.redeemProfileCode.restore();
240 sinon.stub(manager.indieAuthCommunication, 'redeemProfileCode').resolves({
241 me: 'https://different.example.com/profile',
242 });
243 manager.indieAuthCommunication.fetchProfile.restore();
244 sinon.stub(manager.indieAuthCommunication, 'fetchProfile').resolves({
245 authorizationEndpoint: 'https://elsewhere.example.com/auth',
246 });
247
248 await manager.getAdminIA(res, ctx);
249
250 assert(ctx.errors.length);
251 });
252 }); // getAdminIA
253
254 }); // SessionManager