@sync-in/server
Version:
The secure, open-source platform for file storage, sharing, collaboration, and sync
292 lines (291 loc) • 11.7 kB
JavaScript
/*
* 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