/* eslint-disable arrow-body-style */ const { getInstance } = require('../setup-crowi'); describe('loginRequired', () => { let crowi; const fallbackMock = jest.fn().mockReturnValue('fallback'); let loginRequiredStrictly; let loginRequired; let loginRequiredWithFallback; beforeEach(async() => { crowi = await getInstance(); loginRequiredStrictly = require('~/server/middlewares/login-required')(crowi); loginRequired = require('~/server/middlewares/login-required')(crowi, true); loginRequiredWithFallback = require('~/server/middlewares/login-required')(crowi, false, fallbackMock); }); describe('not strict mode', () => { const res = { redirect: jest.fn().mockReturnValue('redirect'), sendStatus: jest.fn().mockReturnValue('sendStatus'), }; const next = jest.fn().mockReturnValue('next'); describe('and when aclService.isGuestAllowedToRead() returns false', () => { let req; let isGuestAllowedToReadSpy; beforeEach(async() => { // setup req req = { originalUrl: 'original url 1', session: {}, }; // reset session object req.session = {}; // prepare spy for AclService.isGuestAllowedToRead isGuestAllowedToReadSpy = jest.spyOn(crowi.aclService, 'isGuestAllowedToRead') .mockImplementation(() => false); }); /* eslint-disable indent */ test.each` userStatus | expectedPath ${1} | ${'/login/error/registered'} ${3} | ${'/login/error/suspended'} ${5} | ${'/invited'} `('redirect to \'$expectedPath\' when user.status is \'$userStatus\'', ({ userStatus, expectedPath }) => { req.user = { _id: 'user id', status: userStatus, }; const result = loginRequired(req, res, next); expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled(); expect(next).not.toHaveBeenCalled(); expect(fallbackMock).not.toHaveBeenCalled(); expect(res.sendStatus).not.toHaveBeenCalled(); expect(res.redirect).toHaveBeenCalledTimes(1); expect(res.redirect).toHaveBeenCalledWith(expectedPath); expect(result).toBe('redirect'); expect(req.session.redirectTo).toBe(undefined); }); /* eslint-disable indent */ test('redirect to \'/login\' when the user does not loggedin', () => { req.baseUrl = '/path/that/requires/loggedin'; const result = loginRequired(req, res, next); expect(isGuestAllowedToReadSpy).toHaveBeenCalled(); expect(next).not.toHaveBeenCalled(); expect(fallbackMock).not.toHaveBeenCalled(); expect(res.sendStatus).not.toHaveBeenCalled(); expect(res.redirect).toHaveBeenCalledTimes(1); expect(res.redirect).toHaveBeenCalledWith('/login'); expect(result).toBe('redirect'); expect(req.session.redirectTo).toBe('original url 1'); }); test('pass anyone into sharedPage', () => { req.isSharedPage = true; const result = loginRequired(req, res, next); expect(isGuestAllowedToReadSpy).toHaveBeenCalled(); expect(fallbackMock).not.toHaveBeenCalled(); expect(res.sendStatus).not.toHaveBeenCalled(); expect(next).toHaveBeenCalled(); expect(res.redirect).not.toHaveBeenCalled(); expect(result).toBe('next'); }); }); describe('and when aclService.isGuestAllowedToRead() returns true', () => { let req; let isGuestAllowedToReadSpy; beforeEach(async() => { // setup req req = { originalUrl: 'original url 1', session: {}, }; // reset session object req.session = {}; // prepare spy for AclService.isGuestAllowedToRead isGuestAllowedToReadSpy = jest.spyOn(crowi.aclService, 'isGuestAllowedToRead') .mockImplementation(() => true); }); /* eslint-disable indent */ test.each` userStatus | expectedPath ${1} | ${'/login/error/registered'} ${3} | ${'/login/error/suspended'} ${5} | ${'/invited'} `('redirect to \'$expectedPath\' when user.status is \'$userStatus\'', ({ userStatus, expectedPath }) => { req.user = { _id: 'user id', status: userStatus, }; const result = loginRequired(req, res, next); expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled(); expect(next).not.toHaveBeenCalled(); expect(fallbackMock).not.toHaveBeenCalled(); expect(res.sendStatus).not.toHaveBeenCalled(); expect(res.redirect).toHaveBeenCalledTimes(1); expect(res.redirect).toHaveBeenCalledWith(expectedPath); expect(result).toBe('redirect'); expect(req.session.redirectTo).toBe(undefined); }); /* eslint-disable indent */ test('pass guest user', () => { const result = loginRequired(req, res, next); expect(isGuestAllowedToReadSpy).toHaveBeenCalledTimes(1); expect(fallbackMock).not.toHaveBeenCalled(); expect(res.sendStatus).not.toHaveBeenCalled(); expect(next).toHaveBeenCalled(); expect(res.redirect).not.toHaveBeenCalled(); expect(result).toBe('next'); }); test('pass anyone into sharedPage', () => { req.isSharedPage = true; const result = loginRequired(req, res, next); expect(isGuestAllowedToReadSpy).toHaveBeenCalled(); expect(fallbackMock).not.toHaveBeenCalled(); expect(res.sendStatus).not.toHaveBeenCalled(); expect(next).toHaveBeenCalled(); expect(res.redirect).not.toHaveBeenCalled(); expect(result).toBe('next'); }); }); }); describe('strict mode', () => { // setup req/res/next const req = { originalUrl: 'original url 1', session: null, }; const res = { redirect: jest.fn().mockReturnValue('redirect'), sendStatus: jest.fn().mockReturnValue('sendStatus'), }; const next = jest.fn().mockReturnValue('next'); let isGuestAllowedToReadSpy; beforeEach(async() => { // reset session object req.session = {}; // spy for AclService.isGuestAllowedToRead isGuestAllowedToReadSpy = jest.spyOn(crowi.aclService, 'isGuestAllowedToRead'); }); test('send status 403 when \'req.baseUrl\' starts with \'_api\'', () => { req.baseUrl = '/_api/someapi'; const result = loginRequiredStrictly(req, res, next); expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled(); expect(next).not.toHaveBeenCalled(); expect(fallbackMock).not.toHaveBeenCalled(); expect(res.redirect).not.toHaveBeenCalled(); expect(res.sendStatus).toHaveBeenCalledTimes(1); expect(res.sendStatus).toHaveBeenCalledWith(403); expect(result).toBe('sendStatus'); }); test('redirect to \'/login\' when the user does not loggedin', () => { req.baseUrl = '/path/that/requires/loggedin'; const result = loginRequiredStrictly(req, res, next); expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled(); expect(next).not.toHaveBeenCalled(); expect(fallbackMock).not.toHaveBeenCalled(); expect(res.sendStatus).not.toHaveBeenCalled(); expect(res.redirect).toHaveBeenCalledTimes(1); expect(res.redirect).toHaveBeenCalledWith('/login'); expect(result).toBe('redirect'); expect(req.session.redirectTo).toBe('original url 1'); }); test('pass user who logged in', () => { const User = crowi.model('User'); req.user = { _id: 'user id', status: User.STATUS_ACTIVE, }; const result = loginRequiredStrictly(req, res, next); expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled(); expect(fallbackMock).not.toHaveBeenCalled(); expect(res.sendStatus).not.toHaveBeenCalled(); expect(res.redirect).not.toHaveBeenCalled(); expect(next).toHaveBeenCalledTimes(1); expect(result).toBe('next'); expect(req.session.redirectTo).toBe(undefined); }); /* eslint-disable indent */ test.each` userStatus | expectedPath ${1} | ${'/login/error/registered'} ${3} | ${'/login/error/suspended'} ${5} | ${'/invited'} `('redirect to \'$expectedPath\' when user.status is \'$userStatus\'', ({ userStatus, expectedPath }) => { req.user = { _id: 'user id', status: userStatus, }; const result = loginRequiredStrictly(req, res, next); expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled(); expect(next).not.toHaveBeenCalled(); expect(fallbackMock).not.toHaveBeenCalled(); expect(res.sendStatus).not.toHaveBeenCalled(); expect(res.redirect).toHaveBeenCalledTimes(1); expect(res.redirect).toHaveBeenCalledWith(expectedPath); expect(result).toBe('redirect'); expect(req.session.redirectTo).toBe(undefined); }); /* eslint-disable indent */ test('redirect to \'/login\' when user.status is \'STATUS_DELETED\'', () => { const User = crowi.model('User'); req.baseUrl = '/path/that/requires/loggedin'; req.user = { _id: 'user id', status: User.STATUS_DELETED, }; const result = loginRequiredStrictly(req, res, next); expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled(); expect(next).not.toHaveBeenCalled(); expect(fallbackMock).not.toHaveBeenCalled(); expect(res.sendStatus).not.toHaveBeenCalled(); expect(res.redirect).toHaveBeenCalledTimes(1); expect(res.redirect).toHaveBeenCalledWith('/login'); expect(result).toBe('redirect'); expect(req.session.redirectTo).toBe('original url 1'); }); }); describe('specified fallback', () => { // setup req/res/next const req = { originalUrl: 'original url 1', session: null, }; const res = { redirect: jest.fn().mockReturnValue('redirect'), sendStatus: jest.fn().mockReturnValue('sendStatus'), }; const next = jest.fn().mockReturnValue('next'); let isGuestAllowedToReadSpy; beforeEach(async() => { // reset session object req.session = {}; // spy for AclService.isGuestAllowedToRead isGuestAllowedToReadSpy = jest.spyOn(crowi.aclService, 'isGuestAllowedToRead'); }); test('invoke fallback when \'req.path\' starts with \'_api\'', () => { req.path = '/_api/someapi'; const result = loginRequiredWithFallback(req, res, next); expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled(); expect(next).not.toHaveBeenCalled(); expect(res.redirect).not.toHaveBeenCalled(); expect(res.sendStatus).not.toHaveBeenCalled(); expect(fallbackMock).toHaveBeenCalledTimes(1); expect(fallbackMock).toHaveBeenCalledWith(req, res, next); expect(result).toBe('fallback'); }); test('invoke fallback when the user does not loggedin', () => { req.path = '/path/that/requires/loggedin'; const result = loginRequiredWithFallback(req, res, next); expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled(); expect(next).not.toHaveBeenCalled(); expect(res.sendStatus).not.toHaveBeenCalled(); expect(res.redirect).not.toHaveBeenCalled(); expect(fallbackMock).toHaveBeenCalledTimes(1); expect(fallbackMock).toHaveBeenCalledWith(req, res, next); expect(result).toBe('fallback'); }); }); });