login-required.test.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. const { UserStatus } = require('../../../src/server/models/user/conts');
  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(() => {
  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. test.each`
  44. userStatus | expectedPath
  45. ${1} | ${'/login/error/registered'}
  46. ${3} | ${'/login/error/suspended'}
  47. ${5} | ${'/invited'}
  48. `(
  49. "redirect to '$expectedPath' when user.status is '$userStatus'",
  50. ({ userStatus, expectedPath }) => {
  51. req.user = {
  52. _id: 'user id',
  53. status: userStatus,
  54. };
  55. const result = loginRequired(req, res, next);
  56. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  57. expect(next).not.toHaveBeenCalled();
  58. expect(fallbackMock).not.toHaveBeenCalled();
  59. expect(res.sendStatus).not.toHaveBeenCalled();
  60. expect(res.redirect).toHaveBeenCalledTimes(1);
  61. expect(res.redirect).toHaveBeenCalledWith(expectedPath);
  62. expect(result).toBe('redirect');
  63. expect(req.session.redirectTo).toBe(undefined);
  64. },
  65. );
  66. test("redirect to '/login' when the user does not loggedin", () => {
  67. req.baseUrl = '/path/that/requires/loggedin';
  68. const result = loginRequired(req, res, next);
  69. expect(isGuestAllowedToReadSpy).toHaveBeenCalled();
  70. expect(next).not.toHaveBeenCalled();
  71. expect(fallbackMock).not.toHaveBeenCalled();
  72. expect(res.sendStatus).not.toHaveBeenCalled();
  73. expect(res.redirect).toHaveBeenCalledTimes(1);
  74. expect(res.redirect).toHaveBeenCalledWith('/login');
  75. expect(result).toBe('redirect');
  76. expect(req.session.redirectTo).toBe('original url 1');
  77. });
  78. test('pass anyone into sharedPage', () => {
  79. req.isSharedPage = true;
  80. const result = loginRequired(req, res, next);
  81. expect(isGuestAllowedToReadSpy).toHaveBeenCalled();
  82. expect(fallbackMock).not.toHaveBeenCalled();
  83. expect(res.sendStatus).not.toHaveBeenCalled();
  84. expect(next).toHaveBeenCalled();
  85. expect(res.redirect).not.toHaveBeenCalled();
  86. expect(result).toBe('next');
  87. });
  88. });
  89. describe('and when aclService.isGuestAllowedToRead() returns true', () => {
  90. let req;
  91. let isGuestAllowedToReadSpy;
  92. beforeEach(() => {
  93. // setup req
  94. req = {
  95. originalUrl: 'original url 1',
  96. session: {},
  97. };
  98. // reset session object
  99. req.session = {};
  100. // prepare spy for AclService.isGuestAllowedToRead
  101. isGuestAllowedToReadSpy = jest
  102. .spyOn(crowi.aclService, 'isGuestAllowedToRead')
  103. .mockImplementation(() => true);
  104. });
  105. test.each`
  106. userStatus | expectedPath
  107. ${1} | ${'/login/error/registered'}
  108. ${3} | ${'/login/error/suspended'}
  109. ${5} | ${'/invited'}
  110. `(
  111. "redirect to '$expectedPath' when user.status is '$userStatus'",
  112. ({ userStatus, expectedPath }) => {
  113. req.user = {
  114. _id: 'user id',
  115. status: userStatus,
  116. };
  117. const result = loginRequired(req, res, next);
  118. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  119. expect(next).not.toHaveBeenCalled();
  120. expect(fallbackMock).not.toHaveBeenCalled();
  121. expect(res.sendStatus).not.toHaveBeenCalled();
  122. expect(res.redirect).toHaveBeenCalledTimes(1);
  123. expect(res.redirect).toHaveBeenCalledWith(expectedPath);
  124. expect(result).toBe('redirect');
  125. expect(req.session.redirectTo).toBe(undefined);
  126. },
  127. );
  128. test('pass guest user', () => {
  129. const result = loginRequired(req, res, next);
  130. expect(isGuestAllowedToReadSpy).toHaveBeenCalledTimes(1);
  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. test('pass anyone into sharedPage', () => {
  138. req.isSharedPage = true;
  139. const result = loginRequired(req, res, next);
  140. expect(isGuestAllowedToReadSpy).toHaveBeenCalled();
  141. expect(fallbackMock).not.toHaveBeenCalled();
  142. expect(res.sendStatus).not.toHaveBeenCalled();
  143. expect(next).toHaveBeenCalled();
  144. expect(res.redirect).not.toHaveBeenCalled();
  145. expect(result).toBe('next');
  146. });
  147. });
  148. });
  149. describe('strict mode', () => {
  150. // setup req/res/next
  151. const req = {
  152. originalUrl: 'original url 1',
  153. session: null,
  154. };
  155. const res = {
  156. redirect: jest.fn().mockReturnValue('redirect'),
  157. sendStatus: jest.fn().mockReturnValue('sendStatus'),
  158. };
  159. const next = jest.fn().mockReturnValue('next');
  160. let isGuestAllowedToReadSpy;
  161. beforeEach(() => {
  162. // reset session object
  163. req.session = {};
  164. // spy for AclService.isGuestAllowedToRead
  165. isGuestAllowedToReadSpy = jest.spyOn(
  166. crowi.aclService,
  167. 'isGuestAllowedToRead',
  168. );
  169. });
  170. test("send status 403 when 'req.baseUrl' starts with '_api'", () => {
  171. req.baseUrl = '/_api/someapi';
  172. const result = loginRequiredStrictly(req, res, next);
  173. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  174. expect(next).not.toHaveBeenCalled();
  175. expect(fallbackMock).not.toHaveBeenCalled();
  176. expect(res.redirect).not.toHaveBeenCalled();
  177. expect(res.sendStatus).toHaveBeenCalledTimes(1);
  178. expect(res.sendStatus).toHaveBeenCalledWith(403);
  179. expect(result).toBe('sendStatus');
  180. });
  181. test("redirect to '/login' when the user does not loggedin", () => {
  182. req.baseUrl = '/path/that/requires/loggedin';
  183. const result = loginRequiredStrictly(req, res, next);
  184. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  185. expect(next).not.toHaveBeenCalled();
  186. expect(fallbackMock).not.toHaveBeenCalled();
  187. expect(res.sendStatus).not.toHaveBeenCalled();
  188. expect(res.redirect).toHaveBeenCalledTimes(1);
  189. expect(res.redirect).toHaveBeenCalledWith('/login');
  190. expect(result).toBe('redirect');
  191. expect(req.session.redirectTo).toBe('original url 1');
  192. });
  193. test('pass user who logged in', () => {
  194. req.user = {
  195. _id: 'user id',
  196. status: UserStatus.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. req.baseUrl = '/path/that/requires/loggedin';
  232. req.user = {
  233. _id: 'user id',
  234. status: UserStatus.STATUS_DELETED,
  235. };
  236. const result = loginRequiredStrictly(req, res, next);
  237. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  238. expect(next).not.toHaveBeenCalled();
  239. expect(fallbackMock).not.toHaveBeenCalled();
  240. expect(res.sendStatus).not.toHaveBeenCalled();
  241. expect(res.redirect).toHaveBeenCalledTimes(1);
  242. expect(res.redirect).toHaveBeenCalledWith('/login');
  243. expect(result).toBe('redirect');
  244. expect(req.session.redirectTo).toBe(undefined);
  245. });
  246. });
  247. describe('specified fallback', () => {
  248. // setup req/res/next
  249. const req = {
  250. originalUrl: 'original url 1',
  251. session: null,
  252. };
  253. const res = {
  254. redirect: jest.fn().mockReturnValue('redirect'),
  255. sendStatus: jest.fn().mockReturnValue('sendStatus'),
  256. };
  257. const next = jest.fn().mockReturnValue('next');
  258. let isGuestAllowedToReadSpy;
  259. beforeEach(() => {
  260. // reset session object
  261. req.session = {};
  262. // spy for AclService.isGuestAllowedToRead
  263. isGuestAllowedToReadSpy = jest.spyOn(
  264. crowi.aclService,
  265. 'isGuestAllowedToRead',
  266. );
  267. });
  268. test("invoke fallback when 'req.path' starts with '_api'", () => {
  269. req.path = '/_api/someapi';
  270. const result = loginRequiredWithFallback(req, res, next);
  271. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  272. expect(next).not.toHaveBeenCalled();
  273. expect(res.redirect).not.toHaveBeenCalled();
  274. expect(res.sendStatus).not.toHaveBeenCalled();
  275. expect(fallbackMock).toHaveBeenCalledTimes(1);
  276. expect(fallbackMock).toHaveBeenCalledWith(req, res, next);
  277. expect(result).toBe('fallback');
  278. });
  279. test('invoke fallback when the user does not loggedin', () => {
  280. req.path = '/path/that/requires/loggedin';
  281. const result = loginRequiredWithFallback(req, res, next);
  282. expect(isGuestAllowedToReadSpy).not.toHaveBeenCalled();
  283. expect(next).not.toHaveBeenCalled();
  284. expect(res.sendStatus).not.toHaveBeenCalled();
  285. expect(res.redirect).not.toHaveBeenCalled();
  286. expect(fallbackMock).toHaveBeenCalledTimes(1);
  287. expect(fallbackMock).toHaveBeenCalledWith(req, res, next);
  288. expect(result).toBe('fallback');
  289. });
  290. });
  291. });