access-token-parser.integ.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import { faker } from '@faker-js/faker';
  2. import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
  3. import type { Response } from 'express';
  4. import { mock } from 'vitest-mock-extended';
  5. import type Crowi from '~/server/crowi';
  6. import type UserEvent from '~/server/events/user';
  7. import { AccessToken } from '~/server/models/access-token';
  8. import type { AccessTokenParserReq } from './interfaces';
  9. import { accessTokenParser } from '.';
  10. vi.mock('@growi/core/dist/models/serializers', { spy: true });
  11. describe('access-token-parser middleware', () => {
  12. let User;
  13. beforeAll(async() => {
  14. const crowiMock = mock<Crowi>({
  15. event: vi.fn().mockImplementation((eventName) => {
  16. if (eventName === 'user') {
  17. return mock<UserEvent>({
  18. on: vi.fn(),
  19. });
  20. }
  21. }),
  22. });
  23. const userModelFactory = (await import('../../models/user')).default;
  24. User = userModelFactory(crowiMock);
  25. });
  26. it('should call next if no access token is provided', async() => {
  27. // arrange
  28. const reqMock = mock<AccessTokenParserReq>({
  29. user: undefined,
  30. });
  31. const resMock = mock<Response>();
  32. const nextMock = vi.fn();
  33. expect(reqMock.user).toBeUndefined();
  34. // act
  35. await accessTokenParser(reqMock, resMock, nextMock);
  36. // assert
  37. expect(reqMock.user).toBeUndefined();
  38. expect(serializeUserSecurely).not.toHaveBeenCalled();
  39. expect(nextMock).toHaveBeenCalled();
  40. });
  41. it('should call next if the given access token is invalid', async() => {
  42. // arrange
  43. const reqMock = mock<AccessTokenParserReq>({
  44. user: undefined,
  45. });
  46. const resMock = mock<Response>();
  47. const nextMock = vi.fn();
  48. expect(reqMock.user).toBeUndefined();
  49. // act
  50. reqMock.query.access_token = 'invalidToken';
  51. await accessTokenParser(reqMock, resMock, nextMock);
  52. // assert
  53. expect(reqMock.user).toBeUndefined();
  54. expect(serializeUserSecurely).not.toHaveBeenCalled();
  55. expect(nextMock).toHaveBeenCalled();
  56. });
  57. it('should set req.user with a valid api token in query', async() => {
  58. // arrange
  59. const reqMock = mock<AccessTokenParserReq>({
  60. user: undefined,
  61. });
  62. const resMock = mock<Response>();
  63. const nextMock = vi.fn();
  64. expect(reqMock.user).toBeUndefined();
  65. // prepare a user with an access token
  66. const targetUser = await User.create({
  67. name: faker.person.fullName(),
  68. username: faker.string.uuid(),
  69. password: faker.internet.password(),
  70. lang: 'en_US',
  71. apiToken: faker.internet.password(),
  72. });
  73. // act
  74. reqMock.query.access_token = targetUser.apiToken;
  75. await accessTokenParser(reqMock, resMock, nextMock);
  76. // assert
  77. expect(reqMock.user).toBeDefined();
  78. expect(reqMock.user?._id).toStrictEqual(targetUser._id);
  79. expect(serializeUserSecurely).toHaveBeenCalledOnce();
  80. expect(nextMock).toHaveBeenCalled();
  81. });
  82. it('should set req.user with a valid api token in body', async() => {
  83. // arrange
  84. const reqMock = mock<AccessTokenParserReq>({
  85. user: undefined,
  86. });
  87. const resMock = mock<Response>();
  88. const nextMock = vi.fn();
  89. expect(reqMock.user).toBeUndefined();
  90. // prepare a user with an access token
  91. const targetUser = await User.create({
  92. name: faker.person.fullName(),
  93. username: faker.string.uuid(),
  94. password: faker.internet.password(),
  95. lang: 'en_US',
  96. apiToken: faker.internet.password(),
  97. });
  98. // act
  99. reqMock.body.access_token = targetUser.apiToken;
  100. await accessTokenParser(reqMock, resMock, nextMock);
  101. // assert
  102. expect(reqMock.user).toBeDefined();
  103. expect(reqMock.user?._id).toStrictEqual(targetUser._id);
  104. expect(serializeUserSecurely).toHaveBeenCalledOnce();
  105. expect(nextMock).toHaveBeenCalled();
  106. });
  107. });
  108. describe('access-token-parser middleware for access token', () => {
  109. let User;
  110. beforeAll(async() => {
  111. const crowiMock = mock<Crowi>({
  112. event: vi.fn().mockImplementation((eventName) => {
  113. if (eventName === 'user') {
  114. return mock<UserEvent>({
  115. on: vi.fn(),
  116. });
  117. }
  118. }),
  119. });
  120. const userModelFactory = (await import('../../models/user')).default;
  121. User = userModelFactory(crowiMock);
  122. });
  123. it('should set req.user with a valid access token in query', async() => {
  124. // arrange
  125. const reqMock = mock<AccessTokenParserReq>({
  126. user: undefined,
  127. });
  128. const resMock = mock<Response>();
  129. const nextMock = vi.fn();
  130. expect(reqMock.user).toBeUndefined();
  131. // prepare a user with an access token
  132. const targetUser = await User.create({
  133. name: faker.person.fullName(),
  134. username: faker.string.uuid(),
  135. password: faker.internet.password(),
  136. lang: 'en_US',
  137. });
  138. // act
  139. const { token } = await AccessToken.generateToken(targetUser._id, new Date(Date.now() + 1000 * 60 * 60 * 24), []);
  140. reqMock.query.access_token = token;
  141. await accessTokenParser(reqMock, resMock, nextMock);
  142. // assert
  143. expect(reqMock.user).toBeDefined();
  144. expect(reqMock.user?._id).toStrictEqual(targetUser._id);
  145. expect(serializeUserSecurely).toHaveBeenCalledOnce();
  146. expect(nextMock).toHaveBeenCalled();
  147. });
  148. it('should set req.user with a valid access token in body', async() => {
  149. // arrange
  150. const reqMock = mock<AccessTokenParserReq>({
  151. user: undefined,
  152. });
  153. const resMock = mock<Response>();
  154. const nextMock = vi.fn();
  155. expect(reqMock.user).toBeUndefined();
  156. // prepare a user with an access token
  157. const targetUser = await User.create({
  158. name: faker.person.fullName(),
  159. username: faker.string.uuid(),
  160. password: faker.internet.password(),
  161. lang: 'en_US',
  162. });
  163. // act
  164. const { token } = await AccessToken.generateToken(targetUser._id, new Date(Date.now() + 1000 * 60 * 60 * 24), []);
  165. reqMock.query.access_token = token;
  166. await accessTokenParser(reqMock, resMock, nextMock);
  167. // assert
  168. expect(reqMock.user).toBeDefined();
  169. expect(reqMock.user?._id).toStrictEqual(targetUser._id);
  170. expect(serializeUserSecurely).toHaveBeenCalledOnce();
  171. expect(nextMock).toHaveBeenCalled();
  172. });
  173. });