login-required.test.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /* eslint-disable arrow-body-style */
  2. const { getInstance } = require('../setup-crowi');
  3. describe('loginRequired', () => {
  4. let crowi;
  5. const fallbackMock = jest.fn().mockReturnValue('fallback');
  6. let loginRequiredStrictly;
  7. let loginRequired;
  8. let loginRequiredWithFallback;
  9. beforeEach(async() => {
  10. crowi = await getInstance();
  11. loginRequiredStrictly = require('~/server/middlewares/login-required')(crowi);
  12. loginRequired = require('~/server/middlewares/login-required')(crowi, true);
  13. loginRequiredWithFallback = require('~/server/middlewares/login-required')(crowi, false, fallbackMock);
  14. });
  15. describe('not strict mode', () => {
  16. const res = {
  17. redirect: jest.fn().mockReturnValue('redirect'),
  18. sendStatus: jest.fn().mockReturnValue('sendStatus'),
  19. };
  20. const next = jest.fn().mockReturnValue('next');
  21. describe('and when aclService.isGuestAllowedToRead() returns false', () => {
  22. let req;
  23. let isGuestAllowedToReadSpy;
  24. beforeEach(async() => {
  25. // setup req
  26. req = {
  27. originalUrl: 'original url 1',
  28. session: {},
  29. };
  30. // reset session object
  31. req.session = {};
  32. // prepare spy for AclService.isGuestAllowedToRead
  33. isGuestAllowedToReadSpy = jest.spyOn(crowi.aclService, 'isGuestAllowedToRead')
  34. .mockImplementation(() => false);
  35. });
  36. /* eslint-disable indent */
  37. test.each`
  38. userStatus | expectedPath
  39. ${1} | ${'/login/error/registered'}
  40. ${3} | ${'/login/error/suspended'}
  41. ${5} | ${'/invited'}
  42. `('redirect to \'$expectedPath\' when user.status is \'$userStatus\'', ({ userStatus, expectedPath }) => {
  43. req.user = {
  44. _id: 'user id',
  45. status: userStatus,
  46. };
  47. const result = loginRequired(req, res, next);
  48. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  49. expect(next).not.toHaveBeenCalled();
  50. expect(fallbackMock).not.toHaveBeenCalled();
  51. expect(res.sendStatus).not.toHaveBeenCalled();
  52. expect(res.redirect).toHaveBeenCalledTimes(1);
  53. expect(res.redirect).toHaveBeenCalledWith(expectedPath);
  54. expect(result).toBe('redirect');
  55. expect(req.session.redirectTo).toBe(undefined);
  56. });
  57. /* eslint-disable indent */
  58. test('redirect to \'/login\' when the user does not loggedin', () => {
  59. req.baseUrl = '/path/that/requires/loggedin';
  60. const result = loginRequired(req, res, next);
  61. expect(isGuestAllowedToReadSpy).toHaveBeenCalled();
  62. expect(next).not.toHaveBeenCalled();
  63. expect(fallbackMock).not.toHaveBeenCalled();
  64. expect(res.sendStatus).not.toHaveBeenCalled();
  65. expect(res.redirect).toHaveBeenCalledTimes(1);
  66. expect(res.redirect).toHaveBeenCalledWith('/login');
  67. expect(result).toBe('redirect');
  68. expect(req.session.redirectTo).toBe('original url 1');
  69. });
  70. test('pass anyone into sharedPage', () => {
  71. req.isSharedPage = true;
  72. const result = loginRequired(req, res, next);
  73. expect(isGuestAllowedToReadSpy).toHaveBeenCalled();
  74. expect(fallbackMock).not.toHaveBeenCalled();
  75. expect(res.sendStatus).not.toHaveBeenCalled();
  76. expect(next).toHaveBeenCalled();
  77. expect(res.redirect).not.toHaveBeenCalled();
  78. expect(result).toBe('next');
  79. });
  80. });
  81. describe('and when aclService.isGuestAllowedToRead() returns true', () => {
  82. let req;
  83. let isGuestAllowedToReadSpy;
  84. beforeEach(async() => {
  85. // setup req
  86. req = {
  87. originalUrl: 'original url 1',
  88. session: {},
  89. };
  90. // reset session object
  91. req.session = {};
  92. // prepare spy for AclService.isGuestAllowedToRead
  93. isGuestAllowedToReadSpy = jest.spyOn(crowi.aclService, 'isGuestAllowedToRead')
  94. .mockImplementation(() => true);
  95. });
  96. /* eslint-disable indent */
  97. test.each`
  98. userStatus | expectedPath
  99. ${1} | ${'/login/error/registered'}
  100. ${3} | ${'/login/error/suspended'}
  101. ${5} | ${'/invited'}
  102. `('redirect to \'$expectedPath\' when user.status is \'$userStatus\'', ({ userStatus, expectedPath }) => {
  103. req.user = {
  104. _id: 'user id',
  105. status: userStatus,
  106. };
  107. const result = loginRequired(req, res, next);
  108. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  109. expect(next).not.toHaveBeenCalled();
  110. expect(fallbackMock).not.toHaveBeenCalled();
  111. expect(res.sendStatus).not.toHaveBeenCalled();
  112. expect(res.redirect).toHaveBeenCalledTimes(1);
  113. expect(res.redirect).toHaveBeenCalledWith(expectedPath);
  114. expect(result).toBe('redirect');
  115. expect(req.session.redirectTo).toBe(undefined);
  116. });
  117. /* eslint-disable indent */
  118. test('pass guest user', () => {
  119. const result = loginRequired(req, res, next);
  120. expect(isGuestAllowedToReadSpy).toHaveBeenCalledTimes(1);
  121. expect(fallbackMock).not.toHaveBeenCalled();
  122. expect(res.sendStatus).not.toHaveBeenCalled();
  123. expect(next).toHaveBeenCalled();
  124. expect(res.redirect).not.toHaveBeenCalled();
  125. expect(result).toBe('next');
  126. });
  127. test('pass anyone into sharedPage', () => {
  128. req.isSharedPage = true;
  129. const result = loginRequired(req, res, next);
  130. expect(isGuestAllowedToReadSpy).toHaveBeenCalled();
  131. expect(fallbackMock).not.toHaveBeenCalled();
  132. expect(res.sendStatus).not.toHaveBeenCalled();
  133. expect(next).toHaveBeenCalled();
  134. expect(res.redirect).not.toHaveBeenCalled();
  135. expect(result).toBe('next');
  136. });
  137. });
  138. });
  139. describe('strict mode', () => {
  140. // setup req/res/next
  141. const req = {
  142. originalUrl: 'original url 1',
  143. session: null,
  144. };
  145. const res = {
  146. redirect: jest.fn().mockReturnValue('redirect'),
  147. sendStatus: jest.fn().mockReturnValue('sendStatus'),
  148. };
  149. const next = jest.fn().mockReturnValue('next');
  150. let isGuestAllowedToReadSpy;
  151. beforeEach(async() => {
  152. // reset session object
  153. req.session = {};
  154. // spy for AclService.isGuestAllowedToRead
  155. isGuestAllowedToReadSpy = jest.spyOn(crowi.aclService, 'isGuestAllowedToRead');
  156. });
  157. test('send status 403 when \'req.baseUrl\' starts with \'_api\'', () => {
  158. req.baseUrl = '/_api/someapi';
  159. const result = loginRequiredStrictly(req, res, next);
  160. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  161. expect(next).not.toHaveBeenCalled();
  162. expect(fallbackMock).not.toHaveBeenCalled();
  163. expect(res.redirect).not.toHaveBeenCalled();
  164. expect(res.sendStatus).toHaveBeenCalledTimes(1);
  165. expect(res.sendStatus).toHaveBeenCalledWith(403);
  166. expect(result).toBe('sendStatus');
  167. });
  168. test('redirect to \'/login\' when the user does not loggedin', () => {
  169. req.baseUrl = '/path/that/requires/loggedin';
  170. const result = loginRequiredStrictly(req, res, next);
  171. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  172. expect(next).not.toHaveBeenCalled();
  173. expect(fallbackMock).not.toHaveBeenCalled();
  174. expect(res.sendStatus).not.toHaveBeenCalled();
  175. expect(res.redirect).toHaveBeenCalledTimes(1);
  176. expect(res.redirect).toHaveBeenCalledWith('/login');
  177. expect(result).toBe('redirect');
  178. expect(req.session.redirectTo).toBe('original url 1');
  179. });
  180. test('pass user who logged in', () => {
  181. const User = crowi.model('User');
  182. req.user = {
  183. _id: 'user id',
  184. status: User.STATUS_ACTIVE,
  185. };
  186. const result = loginRequiredStrictly(req, res, next);
  187. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  188. expect(fallbackMock).not.toHaveBeenCalled();
  189. expect(res.sendStatus).not.toHaveBeenCalled();
  190. expect(res.redirect).not.toHaveBeenCalled();
  191. expect(next).toHaveBeenCalledTimes(1);
  192. expect(result).toBe('next');
  193. expect(req.session.redirectTo).toBe(undefined);
  194. });
  195. /* eslint-disable indent */
  196. test.each`
  197. userStatus | expectedPath
  198. ${1} | ${'/login/error/registered'}
  199. ${3} | ${'/login/error/suspended'}
  200. ${5} | ${'/invited'}
  201. `('redirect to \'$expectedPath\' when user.status is \'$userStatus\'', ({ userStatus, expectedPath }) => {
  202. req.user = {
  203. _id: 'user id',
  204. status: userStatus,
  205. };
  206. const result = loginRequiredStrictly(req, res, next);
  207. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  208. expect(next).not.toHaveBeenCalled();
  209. expect(fallbackMock).not.toHaveBeenCalled();
  210. expect(res.sendStatus).not.toHaveBeenCalled();
  211. expect(res.redirect).toHaveBeenCalledTimes(1);
  212. expect(res.redirect).toHaveBeenCalledWith(expectedPath);
  213. expect(result).toBe('redirect');
  214. expect(req.session.redirectTo).toBe(undefined);
  215. });
  216. /* eslint-disable indent */
  217. test('redirect to \'/login\' when user.status is \'STATUS_DELETED\'', () => {
  218. const User = crowi.model('User');
  219. req.baseUrl = '/path/that/requires/loggedin';
  220. req.user = {
  221. _id: 'user id',
  222. status: User.STATUS_DELETED,
  223. };
  224. const result = loginRequiredStrictly(req, res, next);
  225. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  226. expect(next).not.toHaveBeenCalled();
  227. expect(fallbackMock).not.toHaveBeenCalled();
  228. expect(res.sendStatus).not.toHaveBeenCalled();
  229. expect(res.redirect).toHaveBeenCalledTimes(1);
  230. expect(res.redirect).toHaveBeenCalledWith('/login');
  231. expect(result).toBe('redirect');
  232. expect(req.session.redirectTo).toBe(undefined);
  233. });
  234. });
  235. describe('specified fallback', () => {
  236. // setup req/res/next
  237. const req = {
  238. originalUrl: 'original url 1',
  239. session: null,
  240. };
  241. const res = {
  242. redirect: jest.fn().mockReturnValue('redirect'),
  243. sendStatus: jest.fn().mockReturnValue('sendStatus'),
  244. };
  245. const next = jest.fn().mockReturnValue('next');
  246. let isGuestAllowedToReadSpy;
  247. beforeEach(async() => {
  248. // reset session object
  249. req.session = {};
  250. // spy for AclService.isGuestAllowedToRead
  251. isGuestAllowedToReadSpy = jest.spyOn(crowi.aclService, 'isGuestAllowedToRead');
  252. });
  253. test('invoke fallback when \'req.path\' starts with \'_api\'', () => {
  254. req.path = '/_api/someapi';
  255. const result = loginRequiredWithFallback(req, res, next);
  256. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  257. expect(next).not.toHaveBeenCalled();
  258. expect(res.redirect).not.toHaveBeenCalled();
  259. expect(res.sendStatus).not.toHaveBeenCalled();
  260. expect(fallbackMock).toHaveBeenCalledTimes(1);
  261. expect(fallbackMock).toHaveBeenCalledWith(req, res, next);
  262. expect(result).toBe('fallback');
  263. });
  264. test('invoke fallback when the user does not loggedin', () => {
  265. req.path = '/path/that/requires/loggedin';
  266. const result = loginRequiredWithFallback(req, res, next);
  267. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  268. expect(next).not.toHaveBeenCalled();
  269. expect(res.sendStatus).not.toHaveBeenCalled();
  270. expect(res.redirect).not.toHaveBeenCalled();
  271. expect(fallbackMock).toHaveBeenCalledTimes(1);
  272. expect(fallbackMock).toHaveBeenCalledWith(req, res, next);
  273. expect(result).toBe('fallback');
  274. });
  275. });
  276. });