Sfoglia il codice sorgente

refactor access token parser middleware to remove integration tests and simplify scope handling

reiji-h 1 anno fa
parent
commit
42a91898ca

+ 0 - 162
apps/app/src/server/middlewares/access-token-parser/access-token-parser.integ.ts

@@ -1,162 +0,0 @@
-import { faker } from '@faker-js/faker';
-import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
-import type { Response } from 'express';
-import { mock } from 'vitest-mock-extended';
-
-import { SCOPE } from '~/interfaces/scope';
-import type Crowi from '~/server/crowi';
-import type UserEvent from '~/server/events/user';
-import { AccessToken } from '~/server/models/access-token';
-
-import type { AccessTokenParserReq } from './interfaces';
-
-import { accessTokenParser } from '.';
-
-
-vi.mock('@growi/core/dist/models/serializers', { spy: true });
-
-
-describe('access-token-parser middleware', () => {
-
-  let User;
-
-  beforeAll(async() => {
-    const crowiMock = mock<Crowi>({
-      event: vi.fn().mockImplementation((eventName) => {
-        if (eventName === 'user') {
-          return mock<UserEvent>({
-            on: vi.fn(),
-          });
-        }
-      }),
-    });
-    const userModelFactory = (await import('../../models/user')).default;
-    User = userModelFactory(crowiMock);
-  });
-
-  it('should call next if no access token is provided', async() => {
-    // arrange
-    const reqMock = mock<AccessTokenParserReq>({
-      user: undefined,
-    });
-    const resMock = mock<Response>();
-    const nextMock = vi.fn();
-
-    expect(reqMock.user).toBeUndefined();
-
-    // act
-    await accessTokenParser([SCOPE.READ.USER.ALL])(reqMock, resMock, nextMock);
-
-    // assert
-    expect(reqMock.user).toBeUndefined();
-    expect(serializeUserSecurely).not.toHaveBeenCalled();
-    expect(nextMock).toHaveBeenCalled();
-  });
-
-  it('should not authenticate with no scopes', async() => {
-    // arrange
-    const reqMock = mock<AccessTokenParserReq>({
-      user: undefined,
-    });
-    const resMock = mock<Response>();
-    const nextMock = vi.fn();
-
-    expect(reqMock.user).toBeUndefined();
-
-    // prepare a user
-    const targetUser = await User.create({
-      name: faker.person.fullName(),
-      username: faker.string.uuid(),
-      password: faker.internet.password(),
-      lang: 'en_US',
-    });
-
-    // generate token with write:user:info scope
-    const { token } = await AccessToken.generateToken(
-      targetUser._id,
-      new Date(Date.now() + 1000 * 60 * 60 * 24),
-      [SCOPE.WRITE.USER.INFO],
-    );
-
-    // act - try to access with read:user:info scope
-    reqMock.query.access_token = token;
-    await accessTokenParser()(reqMock, resMock, nextMock);
-
-    // assert
-    expect(reqMock.user).toBeUndefined();
-    expect(serializeUserSecurely).not.toHaveBeenCalledOnce();
-    expect(nextMock).toHaveBeenCalled();
-  });
-
-  it('should authenticate from api-token', async() => {
-    // arrange
-    const reqMock = mock<AccessTokenParserReq>({
-      user: undefined,
-    });
-    const resMock = mock<Response>();
-    const nextMock = vi.fn();
-
-    expect(reqMock.user).toBeUndefined();
-
-    // prepare a user
-    const targetUser = await User.create({
-      name: faker.person.fullName(),
-      username: faker.string.uuid(),
-      password: faker.internet.password(),
-      lang: 'en_US',
-      apiToken: faker.internet.password(),
-    });
-
-    // generate token with write:user:info scope
-    await AccessToken.generateToken(
-      targetUser._id,
-      new Date(Date.now() + 1000 * 60 * 60 * 24),
-      [SCOPE.READ.USER.INFO],
-    );
-
-    reqMock.query.access_token = targetUser.apiToken;
-    await accessTokenParser([SCOPE.WRITE.USER.INFO])(reqMock, resMock, nextMock);
-
-    // assert
-    expect(reqMock.user).toBeDefined();
-    expect(reqMock.user?._id).toStrictEqual(targetUser._id);
-    expect(serializeUserSecurely).toHaveBeenCalledOnce();
-    expect(nextMock).toHaveBeenCalled();
-  });
-
-  it('should authenticate from access-token', async() => {
-    // arrange
-    const reqMock = mock<AccessTokenParserReq>({
-      user: undefined,
-    });
-    const resMock = mock<Response>();
-    const nextMock = vi.fn();
-
-    expect(reqMock.user).toBeUndefined();
-
-    // prepare a user
-    const targetUser = await User.create({
-      name: faker.person.fullName(),
-      username: faker.string.uuid(),
-      password: faker.internet.password(),
-      lang: 'en_US',
-      apiToken: faker.internet.password(),
-    });
-
-    // generate token with write:user:info scope
-    const { token } = await AccessToken.generateToken(
-      targetUser._id,
-      new Date(Date.now() + 1000 * 60 * 60 * 24),
-      [SCOPE.READ.USER.INFO],
-    );
-
-    reqMock.query.access_token = token;
-    await accessTokenParser([SCOPE.READ.USER.INFO])(reqMock, resMock, nextMock);
-
-    // assert
-    expect(reqMock.user).toBeDefined();
-    expect(reqMock.user?._id).toStrictEqual(targetUser._id);
-    expect(serializeUserSecurely).toHaveBeenCalledOnce();
-    expect(nextMock).toHaveBeenCalled();
-  });
-});

+ 2 - 4
apps/app/src/server/middlewares/access-token-parser/index.ts

@@ -6,13 +6,11 @@ import { parserForAccessToken } from './access-token';
 import { parserForApiToken } from './api-token';
 import type { AccessTokenParserReq } from './interfaces';
 
-export const accessTokenParser = (scopes?: Scope[]) => {
+export const accessTokenParser = (scopes: Scope[]) => {
   return async(req: AccessTokenParserReq, res: Response, next: NextFunction): Promise<void> => {
     // TODO: comply HTTP header of RFC6750 / Authorization: Bearer
 
-    if (scopes != null) {
-      parserForAccessToken(scopes)(req, res, next);
-    }
+    parserForAccessToken(scopes)(req, res, next);
     parserForApiToken(req, res, next);
 
     return next();