login-required.test.js 12 KB

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