UNPKG

@sync-in/server

Version:

The secure, open-source platform for file storage, sharing, collaboration, and sync

292 lines (291 loc) 11.7 kB
/* * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com> * This file is part of Sync-in | The open source file sync and share solution * See the LICENSE file for licensing details */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const _cookie = require("@fastify/cookie"); const _tsjest = require("@golevelup/ts-jest"); const _config = require("@nestjs/config"); const _core = require("@nestjs/core"); const _jwt = require("@nestjs/jwt"); const _passport = require("@nestjs/passport"); const _testing = require("@nestjs/testing"); const _nestjspino = require("nestjs-pino"); const _nodecrypto = /*#__PURE__*/ _interop_require_default(require("node:crypto")); const _usersmanagerservice = require("../../applications/users/services/users-manager.service"); const _webdavcontextdecorator = require("../../applications/webdav/decorators/webdav-context.decorator"); const _configenvironment = require("../../configuration/config.environment"); const _auth = require("../constants/auth"); const _authtokenoptionaldecorator = require("../decorators/auth-token-optional.decorator"); const _authtokenskipdecorator = require("../decorators/auth-token-skip.decorator"); const _tokeninterface = require("../interfaces/token.interface"); const _authmanagerservice = require("../services/auth-manager.service"); const _authanonymousguard = require("./auth-anonymous.guard"); const _authanonymousstrategy = require("./auth-anonymous.strategy"); const _authtokenaccessguard = require("./auth-token-access.guard"); const _authtokenaccessstrategy = require("./auth-token-access.strategy"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } describe(_authtokenaccessguard.AuthTokenAccessGuard.name, ()=>{ const csrfToken = _nodecrypto.default.randomUUID(); let authConfig; let jwtService; let authAccessGuard; let authAnonymousGuard; let authAnonymousStrategy; let reflector; let accessTokenWithoutCSRF; let accessToken; let context; beforeAll(async ()=>{ const module = await _testing.Test.createTestingModule({ imports: [ await _config.ConfigModule.forRoot({ load: [ _configenvironment.exportConfiguration ], isGlobal: true }), _jwt.JwtModule.register({ global: true }), _passport.PassportModule ], providers: [ _authtokenaccessstrategy.AuthTokenAccessStrategy, _authanonymousstrategy.AuthAnonymousStrategy, _authanonymousguard.AuthAnonymousGuard, _authmanagerservice.AuthManager, { provide: _usersmanagerservice.UsersManager, useValue: {} }, { provide: _nestjspino.PinoLogger, useValue: { assign: ()=>undefined } } ] }).compile(); authConfig = module.get(_config.ConfigService).get('auth'); jwtService = module.get(_jwt.JwtService); reflector = new _core.Reflector(); authAccessGuard = new _authtokenaccessguard.AuthTokenAccessGuard(reflector); authAnonymousStrategy = module.get(_authanonymousstrategy.AuthAnonymousStrategy); authAnonymousGuard = module.get(_authanonymousguard.AuthAnonymousGuard); accessToken = await jwtService.signAsync({ identity: { id: 1, login: 'foo' }, [_tokeninterface.TOKEN_TYPE.CSRF]: csrfToken }, { secret: authConfig.token.access.secret, expiresIn: 30 }); accessTokenWithoutCSRF = await jwtService.signAsync({ identity: { id: 1, login: 'foo' } }, { secret: authConfig.token.access.secret, expiresIn: 30 }); context = (0, _tsjest.createMock)(); }); it('should be defined', ()=>{ expect(authConfig).toBeDefined(); expect(jwtService).toBeDefined(); expect(authAccessGuard).toBeDefined(); expect(authAnonymousGuard).toBeDefined(); expect(authAnonymousStrategy).toBeDefined(); expect(accessToken).toBeDefined(); expect(accessTokenWithoutCSRF).toBeDefined(); expect(csrfToken).toBeDefined(); }); it('should pass with a valid access token in cookies with CSRF', async ()=>{ context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, headers: { [authConfig.token.csrf.name]: (0, _cookie.sign)(csrfToken, authConfig.token.csrf.secret) }, cookies: { [authConfig.token.access.name]: accessToken } }); expect(await authAccessGuard.canActivate(context)).toBe(true); }); it('should pass with a valid access token in request header with no CSRF', async ()=>{ context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, headers: { authorization: `Bearer ${accessToken}` } }); expect(await authAccessGuard.canActivate(context)).toBe(true); }); it('should throw an error with an invalid access token in cookies', async ()=>{ // Cookies test context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, headers: {}, cookies: { [authConfig.token.access.name]: 'bar' } }); await expect(authAccessGuard.canActivate(context)).rejects.toThrow('Unauthorized'); }); it('should throw an error with an invalid access token in request header', async ()=>{ context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, headers: { authorization: `Bearer bar` } }); await expect(authAccessGuard.canActivate(context)).rejects.toThrow('Unauthorized'); }); it('should throw an error with a valid access token in cookies and a missing CSRF in request header', async ()=>{ context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, headers: {}, cookies: { [authConfig.token.access.name]: accessToken } }); await expect(authAccessGuard.canActivate(context)).rejects.toThrow(new RegExp(_auth.CSRF_ERROR.MISSING_HEADERS)); }); it('should throw an error with a valid access token in cookies and a missing CSRF claim in the access token', async ()=>{ context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, headers: { [authConfig.token.csrf.name]: csrfToken }, cookies: { [authConfig.token.access.name]: accessTokenWithoutCSRF } }); await expect(authAccessGuard.canActivate(context)).rejects.toThrow(new RegExp(_auth.CSRF_ERROR.MISSING_JWT)); }); it('should throw an error with a valid access token in cookies and a mismatch CSRF', async ()=>{ context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, headers: { [authConfig.token.csrf.name]: csrfToken + '*' }, cookies: { [authConfig.token.access.name]: accessToken } }); await expect(authAccessGuard.canActivate(context)).rejects.toThrow(new RegExp(_auth.CSRF_ERROR.MISMATCH)); }); it('should pass with method GET and a valid access token in cookies and a mismatch CSRF', async ()=>{ context.switchToHttp().getRequest.mockReturnValue({ method: 'GET', raw: { user: '' }, headers: { [authConfig.token.csrf.name]: csrfToken + '*' }, cookies: { [authConfig.token.access.name]: accessToken } }); await expect(authAccessGuard.canActivate(context)).resolves.not.toThrow(); }); it('should throw an error with method POST and a valid access token in cookies and a mismatch CSRF', async ()=>{ context.switchToHttp().getRequest.mockReturnValue({ method: 'POST', raw: { user: '' }, headers: { [authConfig.token.csrf.name]: csrfToken + '*' }, cookies: { [authConfig.token.access.name]: accessToken } }); await expect(authAccessGuard.canActivate(context)).rejects.toThrow(new RegExp(_auth.CSRF_ERROR.MISMATCH)); }); it('should bypass access token when AuthTokenSkip decorator is applied to context', ()=>{ context = (0, _tsjest.createMock)(); (0, _authtokenskipdecorator.AuthTokenSkip)()(context.getHandler()); expect(reflector.getAllAndOverride(_authtokenskipdecorator.AUTH_TOKEN_SKIP, [ context.getHandler(), context.getClass() ])).toBe(true); context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, headers: { authorization: `Bearer bar` } }); expect(authAccessGuard.canActivate(context)).toBe(true); }); it('should bypass access token with WebDAVContext decorator is applied to context', ()=>{ context = (0, _tsjest.createMock)(); (0, _webdavcontextdecorator.WebDAVContext)()(context.getHandler()); expect(reflector.getAllAndOverride(_authtokenskipdecorator.AUTH_TOKEN_SKIP, [ context.getHandler(), context.getClass() ])).toBe(undefined); expect(reflector.getAllAndOverride(_webdavcontextdecorator.WEB_DAV_CONTEXT, [ context.getHandler(), context.getClass() ])).toBe(true); context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, headers: { authorization: `Bearer bar` } }); expect(authAccessGuard.canActivate(context)).toBe(true); }); it('should pass without a valid access token when AuthTokenOptional is applied to context', async ()=>{ const spyAuthenticate = jest.spyOn(authAnonymousStrategy, 'authenticate'); context = (0, _tsjest.createMock)(); (0, _authtokenoptionaldecorator.AuthTokenOptional)()(context.getHandler()); expect(reflector.getAllAndOverride(_authtokenskipdecorator.AUTH_TOKEN_SKIP, [ context.getHandler(), context.getClass() ])).toBe(true); context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, authorization: `Bearer bar` }); expect(authAccessGuard.canActivate(context)).toBe(true); expect(await authAnonymousGuard.canActivate(context)).toBe(true); expect(spyAuthenticate).toHaveBeenCalledTimes(1); spyAuthenticate.mockClear(); }); }); //# sourceMappingURL=auth-token-access.guard.spec.js.map