2 /* eslint-disable capitalized-comments, sonarjs/no-duplicate-string, sonarjs/no-identical-functions */
6 const assert
= require('assert');
7 const sinon
= require('sinon'); // eslint-disable-line node/no-unpublished-require
9 const SessionManager
= require('../../lib/session-manager');
10 const Config
= require('../stub-config');
11 const stubLogger
= require('../stub-logger');
13 describe('SessionManager', function () {
14 let manager
, options
, stubAuthenticator
;
17 beforeEach(function () {
18 options
= new Config('test');
21 setHeader: sinon
.stub(),
30 isValidIdentifierCredential: sinon
.stub(),
32 manager
= new SessionManager(stubLogger
, stubAuthenticator
, options
);
33 sinon
.stub(manager
.indieAuthCommunication
);
36 afterEach(function () {
40 describe('_sessionCookieSet', function () {
42 beforeEach(function () {
46 it('covers', async
function () {
47 await manager
._sessionCookieSet(res
, session
, maxAge
);
48 assert(res
.setHeader
.called
);
50 it('covers reset', async
function () {
53 await manager
._sessionCookieSet(res
, session
, maxAge
);
54 assert(res
.setHeader
.called
);
56 it('covers options', async
function() {
57 options
.authenticator
.secureAuthOnly
= false;
58 await manager
._sessionCookieSet(res
, session
, undefined, '');
59 assert(res
.setHeader
.called
);
61 }); // _sessionCookieSet
63 describe('getAdminLogin', function () {
64 it('covers', async
function () {
65 await manager
.getAdminLogin(res
, ctx
);
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);
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
);
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',
89 await manager
.postAdminLogin(res
, ctx
);
90 assert
.strictEqual(res
.statusCode
, 302);
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
);
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
);
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',
109 await manager
.postAdminLogin(res
, ctx
);
110 assert(!res
.setHeader
.called
);
112 }); // postAdminLogin
114 describe('getAdminLogout', function () {
115 it('covers', async
function () {
116 await manager
.getAdminLogout(res
, ctx
);
118 }); // getAdminLogout
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({
130 manager
.indieAuthCommunication
.fetchProfile
.resolves({
131 authorizationEndpoint
,
133 sinon
.stub(manager
.mysteryBox
, 'unpack').resolves({
134 authorizationEndpoint
,
139 it('covers valid', async
function () {
140 ctx
.queryParams
['state'] = state
;
141 ctx
.queryParams
['code'] = 'codeCodeCode';
143 await manager
.getAdminIA(res
, ctx
);
145 assert
.strictEqual(res
.statusCode
, 302);
147 it('covers missing cookie', async
function () {
150 await manager
.getAdminIA(res
, ctx
);
152 assert(ctx
.errors
.length
);
154 it('covers invalid cookie', async
function () {
155 manager
.mysteryBox
.unpack
.restore();
156 sinon
.stub(manager
.mysteryBox
, 'unpack').rejects();
158 await manager
.getAdminIA(res
, ctx
);
160 assert(ctx
.errors
.length
);
162 it('covers mis-matched state', async
function () {
163 ctx
.queryParams
['state'] = 'incorrect-state';
164 ctx
.queryParams
['code'] = 'codeCodeCode';
166 await manager
.getAdminIA(res
, ctx
);
168 assert(ctx
.errors
.length
);
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';
176 await manager
.getAdminIA(res
, ctx
);
178 assert(ctx
.errors
.length
);
180 it('covers empty error_description', async
function () {
181 ctx
.queryParams
['state'] = state
;
182 ctx
.queryParams
['code'] = 'codeCodeCode';
183 ctx
.queryParams
['error'] = 'error_code';
185 await manager
.getAdminIA(res
, ctx
);
187 assert(ctx
.errors
.length
);
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',
196 ctx
.queryParams
['state'] = state
;
197 ctx
.queryParams
['code'] = 'codeCodeCode';
199 await manager
.getAdminIA(res
, ctx
);
201 assert(ctx
.errors
.length
);
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();
209 await manager
.getAdminIA(res
, ctx
);
211 assert(ctx
.errors
.length
);
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({
220 await manager
.getAdminIA(res
, ctx
);
222 assert(ctx
.errors
.length
);
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',
232 await manager
.getAdminIA(res
, ctx
);
234 assert
.strictEqual(res
.statusCode
, 302);
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',
243 manager
.indieAuthCommunication
.fetchProfile
.restore();
244 sinon
.stub(manager
.indieAuthCommunication
, 'fetchProfile').resolves({
245 authorizationEndpoint: 'https://elsewhere.example.com/auth',
248 await manager
.getAdminIA(res
, ctx
);
250 assert(ctx
.errors
.length
);
254 }); // SessionManager