UNPKG

@sync-in/server

Version:

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

457 lines (456 loc) 18.9 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 _tsjest = require("@golevelup/ts-jest"); const _common = require("@nestjs/common"); const _testing = require("@nestjs/testing"); const _functions = require("../../../common/functions"); const _cacheservice = require("../../../infrastructure/cache/services/cache.service"); const _contextmanagerservice = require("../../../infrastructure/context/services/context-manager.service"); const _constants = require("../../../infrastructure/database/constants"); const _filesqueriesservice = require("../../files/services/files-queries.service"); const _linksqueriesservice = require("../../links/services/links-queries.service"); const _notificationsmanagerservice = require("../../notifications/services/notifications-manager.service"); const _shares = require("../../shares/constants/shares"); const _sharesmanagerservice = require("../../shares/services/shares-manager.service"); const _sharesqueriesservice = require("../../shares/services/shares-queries.service"); const _user = require("../../users/constants/user"); const _usermodel = require("../../users/models/user.model"); const _usersqueriesservice = require("../../users/services/users-queries.service"); const _test = require("../../users/utils/test"); const _webdavcontextdecorator = require("../../webdav/decorators/webdav-context.decorator"); const _spaces = require("../constants/spaces"); const _spaceskippermissionsdecorator = require("../decorators/space-skip-permissions.decorator"); const _spacesmanagerservice = require("../services/spaces-manager.service"); const _spacesqueriesservice = require("../services/spaces-queries.service"); const _spaceguard = require("./space.guard"); describe(_spaceguard.SpaceGuard.name, ()=>{ let spacesGuard; let spacesManager; let spacesQueries; let userTest; let context; beforeAll(async ()=>{ const module = await _testing.Test.createTestingModule({ providers: [ { provide: _constants.DB_TOKEN_PROVIDER, useValue: {} }, { provide: _cacheservice.Cache, useValue: {} }, { provide: _contextmanagerservice.ContextManager, useValue: {} }, { provide: _notificationsmanagerservice.NotificationsManager, useValue: {} }, _spaceguard.SpaceGuard, _spacesmanagerservice.SpacesManager, _spacesqueriesservice.SpacesQueries, _sharesmanagerservice.SharesManager, _sharesqueriesservice.SharesQueries, _filesqueriesservice.FilesQueries, _usersqueriesservice.UsersQueries, _linksqueriesservice.LinksQueries ] }).compile(); spacesManager = module.get(_spacesmanagerservice.SpacesManager); spacesQueries = module.get(_spacesqueriesservice.SpacesQueries); spacesGuard = module.get(_spaceguard.SpaceGuard); // mocks spacesManager['setQuotaExceeded'] = jest.fn(); userTest = new _usermodel.UserModel((0, _test.generateUserTest)()); context = (0, _tsjest.createMock)(); }); it('should be defined', ()=>{ expect(spacesGuard).toBeDefined(); expect(spacesManager).toBeDefined(); expect(userTest).toBeDefined(); }); it('should pass for a personal space', async ()=>{ context.switchToHttp().getRequest.mockReturnValue({ user: userTest, params: { '*': 'files/personal/root/foo/bar' } }); expect(await spacesGuard.canActivate(context)).toBe(true); const req = context.switchToHttp().getRequest(); expect(req.space.id).toBe(0); expect(req.space.repository).toBe(_spaces.SPACE_REPOSITORY.FILES); expect(req.space.alias).toBe(_spaces.SPACE_ALIAS.PERSONAL); expect(req.space.name).toBe(_spaces.SPACE_ALIAS.PERSONAL); expect(req.space.enabled).toBe(true); expect(req.space.root).toBeUndefined(); expect(req.space.dbFile).toMatchObject({ inTrash: false, ownerId: userTest.id, path: 'root/foo/bar' }); expect(req.space.permissions).toBe(_spaces.SPACE_ALL_OPERATIONS); expect(req.space.envPermissions).toBe(_spaces.SPACE_ALL_OPERATIONS); expect(req.space.inFilesRepository).toBe(true); expect(req.space.inPersonalSpace).toBe(true); expect(req.space.inTrashRepository).toBe(false); expect(req.space.inSharesRepository).toBe(false); expect(req.space.inSharesList).toBe(false); expect(req.space.paths).toEqual(expect.arrayContaining([ 'root', 'foo', 'bar' ])); }); it('should pass for a common space', async ()=>{ const fakeSpace = { id: -1, alias: 'test', name: 'Test', enabled: true, permissions: ':a:d:m:so', role: 0 }; spacesQueries.permissions = jest.fn().mockReturnValueOnce(fakeSpace); context.switchToHttp().getRequest.mockReturnValue({ user: userTest, params: { '*': 'files/test' } }); expect(await spacesGuard.canActivate(context)).toBe(true); const req = context.switchToHttp().getRequest(); expect(req.space.id).toBe(fakeSpace.id); expect(req.space.repository).toBe(_spaces.SPACE_REPOSITORY.FILES); expect(req.space.alias).toBe(fakeSpace.alias); expect(req.space.name).toBe(fakeSpace.name); expect(req.space.enabled).toBe(true); expect(req.space.root).toMatchObject({ id: 0, alias: '', name: '', permissions: 'a:d:m:so' }); expect(req.space.dbFile).toMatchObject({ inTrash: false, spaceId: fakeSpace.id, spaceExternalRootId: null, path: '.' }); expect(req.space.permissions).toBe(_shares.SHARE_ALL_OPERATIONS); expect(req.space.envPermissions).not.toContain('d'); expect(req.space.inFilesRepository).toBe(true); expect(req.space.inPersonalSpace).toBe(false); expect(req.space.inTrashRepository).toBe(false); expect(req.space.inSharesRepository).toBe(false); expect(req.space.inSharesList).toBe(false); expect(req.space.paths).toHaveLength(0); }); it('should pass for a common space root', async ()=>{ const fakeSpace = { id: -1, alias: 'test', name: 'Test', enabled: true, permissions: 'a:d:m:so', role: 1, root: { id: -2, alias: 'root', name: 'Root', permissions: 'a:d:so', owner: { id: -3, login: 'johaven' }, file: { id: -4, path: 'code', inTrash: false }, externalPath: null } }; spacesQueries.permissions = jest.fn().mockReturnValueOnce(fakeSpace); context.switchToHttp().getRequest.mockReturnValue({ user: userTest, params: { '*': 'files/test/root' } }); expect(await spacesGuard.canActivate(context)).toBe(true); const req = context.switchToHttp().getRequest(); expect(req.space.id).toBe(fakeSpace.id); expect(req.space.repository).toBe(_spaces.SPACE_REPOSITORY.FILES); expect(req.space.alias).toBe(fakeSpace.alias); expect(req.space.name).toBe(fakeSpace.name); expect(req.space.enabled).toBe(true); expect(req.space.root).toMatchObject(fakeSpace.root); expect(req.space.dbFile).toMatchObject({ inTrash: false, ownerId: fakeSpace.root.owner.id, path: fakeSpace.root.file.path }); expect(req.space.permissions).toBe(_spaces.SPACE_ALL_OPERATIONS); expect(req.space.envPermissions).toBe((0, _functions.intersectPermissions)(fakeSpace.permissions, fakeSpace.root.permissions).split(_spaces.SPACE_PERMS_SEP).filter((p)=>p !== _spaces.SPACE_OPERATION.DELETE).join(_spaces.SPACE_PERMS_SEP)); expect(req.space.inFilesRepository).toBe(true); expect(req.space.inPersonalSpace).toBe(false); expect(req.space.inTrashRepository).toBe(false); expect(req.space.inSharesRepository).toBe(false); expect(req.space.inSharesList).toBe(false); expect(req.space.paths).toHaveLength(0); }); it('should pass for a common space root with a path', async ()=>{ const fakeSpace = { id: -1, alias: 'test', name: 'Test', enabled: true, permissions: 'a:d:m:so', role: 1, root: { id: -2, alias: 'root', name: 'Root', permissions: 'a:d:so', owner: { id: -3, login: 'johaven' }, file: { id: -4, path: 'code', inTrash: false }, externalPath: null } }; spacesQueries.permissions = jest.fn().mockReturnValueOnce(fakeSpace); context.switchToHttp().getRequest.mockReturnValue({ user: userTest, params: { '*': 'files/test/root/path' } }); expect(await spacesGuard.canActivate(context)).toBe(true); const req = context.switchToHttp().getRequest(); expect(req.space.id).toBe(fakeSpace.id); expect(req.space.repository).toBe(_spaces.SPACE_REPOSITORY.FILES); expect(req.space.alias).toBe(fakeSpace.alias); expect(req.space.name).toBe(fakeSpace.name); expect(req.space.enabled).toBe(true); expect(req.space.root).toMatchObject(fakeSpace.root); expect(req.space.dbFile).toMatchObject({ inTrash: false, ownerId: fakeSpace.root.owner.id, path: `${fakeSpace.root.file.path}/${req.space.paths[0]}` }); expect(req.space.permissions).toBe(_spaces.SPACE_ALL_OPERATIONS); expect(req.space.envPermissions).toBe(fakeSpace.root.permissions); expect(req.space.inFilesRepository).toBe(true); expect(req.space.inPersonalSpace).toBe(false); expect(req.space.inTrashRepository).toBe(false); expect(req.space.inSharesRepository).toBe(false); expect(req.space.inSharesList).toBe(false); expect(req.space.paths).toHaveLength(1); }); it('should pass for a space in shares repository', async ()=>{ context.switchToHttp().getRequest.mockReturnValue({ user: userTest, params: { '*': 'shares' } }); expect(await spacesGuard.canActivate(context)).toBe(true); const req = context.switchToHttp().getRequest(); expect(req.space.id).toBe(0); expect(req.space.repository).toBe(_spaces.SPACE_REPOSITORY.SHARES); expect(req.space.alias).toBe(_spaces.SPACE_REPOSITORY.SHARES); expect(req.space.name).toBe(_spaces.SPACE_REPOSITORY.SHARES); expect(req.space.enabled).toBe(true); expect(req.space.root).toBeUndefined(); expect(req.space.dbFile).toBeUndefined(); expect(req.space.permissions).toBe(''); expect(req.space.envPermissions).toBe(''); expect(req.space.inFilesRepository).toBe(false); expect(req.space.inPersonalSpace).toBe(false); expect(req.space.inTrashRepository).toBe(false); expect(req.space.inSharesRepository).toBe(true); expect(req.space.inSharesList).toBe(true); expect(req.space.paths).toHaveLength(0); }); it('should not pass if the space is not found or not valid', async ()=>{ const fakeSpace = null; spacesQueries.permissions = jest.fn().mockReturnValueOnce(fakeSpace); context.switchToHttp().getRequest.mockReturnValue({ user: userTest, params: { '*': 'files/foo' } }); await expect(spacesGuard.canActivate(context)).rejects.toThrow(_common.HttpException); }); it('should validate (or not) the access to the space depending on the user permissions', async ()=>{ // we only check the `spacesManager.checkAccessToSpace` function, ignores the `spacesManager.spaceEnv` userTest.applications = [ _user.USER_PERMISSION.PERSONAL_SPACE ]; for (const url of [ '', 'shares/personal', 'shares/toto' ]){ context.switchToHttp().getRequest.mockReturnValue({ user: userTest, params: { '*': url } }); await expect(spacesGuard.canActivate(context)).rejects.toThrow(_common.HttpException); } // should pass because it is a standard user // should not pass because user is a guest (and dot not have the permission to access to a personal space) spacesManager.spaceEnv = jest.fn().mockReturnValueOnce({ enabled: true }); // only for user role context.switchToHttp().getRequest.mockReturnValue({ user: userTest, params: { '*': 'files/personal/root/foo/bar' } }); for (const userRole of [ _user.USER_ROLE.USER, _user.USER_ROLE.GUEST ]){ userTest.role = userRole; if (userRole === _user.USER_ROLE.USER) { expect(await spacesGuard.canActivate(context)).toBe(true); } else { await expect(spacesGuard.canActivate(context)).rejects.toThrow(_common.HttpException); } } }); it('should check user permissions on route', async ()=>{ userTest.role = _user.USER_ROLE.USER; spacesManager.spaceEnv = jest.fn().mockReturnValue({ enabled: true, envPermissions: `${_spaces.SPACE_OPERATION.MODIFY}` }); // does not allow creation (only modify) context.switchToHttp().getRequest.mockReturnValueOnce({ method: 'POST', user: userTest, params: { '*': 'files/personal' } }); await expect(spacesGuard.canActivate(context)).rejects.toThrow(_common.HttpException); // allows modification context.switchToHttp().getRequest.mockReturnValue({ method: 'PATCH', user: userTest, params: { '*': 'files/personal' } }); expect(await spacesGuard.canActivate(context)).toBe(true); // does not allow guest (on personal space) userTest.role = _user.USER_ROLE.GUEST; await expect(spacesGuard.canActivate(context)).rejects.toThrow(_common.HttpException); // allow if SkipSpacePermissionsCheck is enabled userTest.role = _user.USER_ROLE.USER; (0, _spaceskippermissionsdecorator.SkipSpacePermissionsCheck)()(context.getHandler()); spacesManager.spaceEnv = jest.fn().mockReturnValue({ enabled: true, envPermissions: `${_spaces.SPACE_OPERATION.MODIFY}` }); // does not allow creation but pass context.switchToHttp().getRequest.mockReturnValueOnce({ method: 'POST', user: userTest, params: { '*': 'files/personal' } }); expect(await spacesGuard.canActivate(context)).toBe(true); // reset context context = (0, _tsjest.createMock)(); }); it('should fail with quota exceeded', async ()=>{ userTest.role = _user.USER_ROLE.USER; spacesManager.spaceEnv = jest.fn().mockReturnValueOnce({ enabled: true, envPermissions: `${_spaces.SPACE_OPERATION.ADD}`, quotaIsExceeded: true }); context.switchToHttp().getRequest.mockReturnValueOnce({ method: 'POST', user: userTest, params: { '*': 'files/personal' } }); let thrown = false; try { await spacesGuard.canActivate(context); } catch (e) { thrown = true; expect(e).toBeInstanceOf(_common.HttpException); expect(e.status).toBe(_common.HttpStatus.INSUFFICIENT_STORAGE); } expect(thrown).toBe(true); }); it('should fail with space disabled', async ()=>{ userTest.role = _user.USER_ROLE.USER; spacesManager.spaceEnv = jest.fn().mockReturnValueOnce({ enabled: false }); context.switchToHttp().getRequest.mockReturnValueOnce({ method: 'POST', user: userTest, params: { '*': 'files/personal' } }); let thrown = false; try { await spacesGuard.canActivate(context); } catch (e) { thrown = true; expect(e).toBeInstanceOf(_common.HttpException); expect(e.status).toBe(_common.HttpStatus.FORBIDDEN); } expect(thrown).toBe(true); }); it('should validate (or not) the webdav routes', async ()=>{ userTest.role = _user.USER_ROLE.USER; spacesManager.spaceEnv = jest.fn().mockReturnValue({ enabled: true }); (0, _webdavcontextdecorator.WebDAVContext)()(context.getHandler()); context.switchToHttp().getRequest.mockReturnValueOnce({ user: userTest, params: { '*': 'webdav/personal' } }); expect(await spacesGuard.canActivate(context)).toBe(true); context.switchToHttp().getRequest.mockReturnValueOnce({ user: userTest, params: { '*': 'webdav/files' } }); await expect(spacesGuard.canActivate(context)).rejects.toThrow(_common.HttpException); }); }); //# sourceMappingURL=space.guard.spec.js.map