login-required.test.js 11 KB

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