UNPKG

@sync-in/server

Version:

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

269 lines (268 loc) 12.1 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 _config = require("@nestjs/config"); const _testing = require("@nestjs/testing"); const _promises = /*#__PURE__*/ _interop_require_default(require("node:fs/promises")); const _nodeos = /*#__PURE__*/ _interop_require_default(require("node:os")); const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path")); const _functions = require("../../../common/functions"); const _configenvironment = require("../../../configuration/config.environment"); const _cacheservice = require("../../../infrastructure/cache/services/cache.service"); const _contextmanagerservice = require("../../../infrastructure/context/services/context-manager.service"); const _constants = require("../../../infrastructure/database/constants"); const _fileerror = require("../../files/models/file-error"); const _filesqueriesservice = require("../../files/services/files-queries.service"); const _files = require("../../files/utils/files"); const _linksqueriesservice = require("../../links/services/links-queries.service"); const _notificationsmanagerservice = require("../../notifications/services/notifications-manager.service"); const _sharesmanagerservice = require("../../shares/services/shares-manager.service"); const _sharesqueriesservice = require("../../shares/services/shares-queries.service"); const _member = require("../../users/constants/member"); const _usermodel = require("../../users/models/user.model"); const _usersqueriesservice = require("../../users/services/users-queries.service"); const _test = require("../../users/utils/test"); const _spaces = require("../constants/spaces"); const _createorupdatespacedto = require("../dto/create-or-update-space.dto"); const _spacerootsdto = require("../dto/space-roots.dto"); const _spacemodel = require("../models/space.model"); const _paths = require("../utils/paths"); const _spacesmanagerservice = require("./spaces-manager.service"); const _spacesqueriesservice = require("./spaces-queries.service"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } describe(_spacesmanagerservice.SpacesManager.name, ()=>{ let filesConfig; let spacesManager; let spacesQueries; let userTest; const spaceAlias = 'project'; const tmpDir = _nodeos.default.tmpdir(); beforeAll(async ()=>{ const module = await _testing.Test.createTestingModule({ imports: [ await _config.ConfigModule.forRoot({ load: [ _configenvironment.exportConfiguration ], isGlobal: true }) ], providers: [ { provide: _constants.DB_TOKEN_PROVIDER, useValue: {} }, { provide: _cacheservice.Cache, useValue: { get: ()=>null } }, { provide: _contextmanagerservice.ContextManager, useValue: {} }, { provide: _notificationsmanagerservice.NotificationsManager, useValue: {} }, _spacesmanagerservice.SpacesManager, _spacesqueriesservice.SpacesQueries, _usersqueriesservice.UsersQueries, _sharesmanagerservice.SharesManager, _sharesqueriesservice.SharesQueries, _filesqueriesservice.FilesQueries, _linksqueriesservice.LinksQueries ] }).compile(); module.useLogger([ 'fatal' ]); filesConfig = module.get(_config.ConfigService).get('applications.files'); spacesManager = module.get(_spacesmanagerservice.SpacesManager); spacesQueries = module.get(_spacesqueriesservice.SpacesQueries); spacesManager['setQuotaExceeded'] = jest.fn(); userTest = new _usermodel.UserModel((0, _test.generateUserTest)()); // todo: validate shares, permissions }); afterAll(async ()=>{ await (0, _files.removeFiles)(userTest.homePath); await (0, _files.removeFiles)(_spacemodel.SpaceModel.getHomePath(spaceAlias)); }); it('should be defined', ()=>{ expect(filesConfig).toBeDefined(); expect(spacesManager).toBeDefined(); expect(userTest).toBeDefined(); }); it('should prevent path traversal', ()=>{ const createOrUpdateSpaceDto = (0, _functions.transformAndValidate)(_createorupdatespacedto.CreateOrUpdateSpaceDto, { name: '../../../foo/..bar', alias: '../../bar.', managers: [ (0, _functions.transformAndValidate)(_createorupdatespacedto.SpaceMemberDto, { id: 0, type: _member.MEMBER_TYPE.USER }) ] }); expect(createOrUpdateSpaceDto.name).toEqual('foobar'); expect(createOrUpdateSpaceDto.alias).toEqual('bar'); const spaceRootFileDto = (0, _functions.transformAndValidate)(_spacerootsdto.SpaceRootFileDto, { id: 0, path: '../../foo/bar' }); expect(spaceRootFileDto.path).toEqual('foo/bar'); const spaceRootFileDto2 = (0, _functions.transformAndValidate)(_spacerootsdto.SpaceRootFileDto, { id: 0, path: `${_spaces.SPACE_REPOSITORY.FILES}/${_spaces.SPACE_ALIAS.PERSONAL}/../../../foo/bar` }); expect(spaceRootFileDto2.path).toEqual('foo/bar'); }); it('should validate the permissions on personal & shares space', async ()=>{ const personalSpace = await spacesManager.spaceEnv(userTest, [ 'files', _spaces.SPACE_ALIAS.PERSONAL ]); expect(personalSpace.envPermissions).toBe(Object.values(_spaces.SPACE_OPERATION).filter((p)=>p !== _spaces.SPACE_OPERATION.DELETE).join(_spaces.SPACE_PERMS_SEP)); const sharesSpace = await spacesManager.spaceEnv(userTest, [ 'shares' ]); expect(sharesSpace.envPermissions).toBe(''); }); it("should validate (or not) the user's personal space (files & trash repositories)", async ()=>{ for (const repository of [ _spaces.SPACE_REPOSITORY.FILES, _spaces.SPACE_REPOSITORY.TRASH ]){ const spaceEnv = await spacesManager.spaceEnv(userTest, [ repository, _spaces.SPACE_ALIAS.PERSONAL, 'foo', 'bar' ]); expect(spaceEnv.envPermissions).toBe(_spaces.SPACE_ALL_OPERATIONS); await expect((0, _paths.IsRealPathIsDirAndExists)(spaceEnv.realPath)).rejects.toThrow(); const repoPath = _usermodel.UserModel.getRepositoryPath(userTest.login, spaceEnv.inTrashRepository); await _promises.default.mkdir(_nodepath.default.join(repoPath, 'foo', 'bar'), { recursive: true }); await expect((0, _paths.IsRealPathIsDirAndExists)(spaceEnv.realPath)).resolves.toBeUndefined(); await (0, _files.removeFiles)(_nodepath.default.join(repoPath, 'foo', 'bar')); try { await (0, _paths.IsRealPathIsDirAndExists)(spaceEnv.realPath); } catch (e) { expect(e).toBeInstanceOf(_fileerror.FileError); } } }); it(`should validate (or not) the space : ${spaceAlias} (files & trash repositories)`, async ()=>{ const permissions = { id: 1, alias: spaceAlias, name: spaceAlias, enabled: true, permissions: 'a:d:m:so', role: 0 }; spacesQueries.permissions = jest.fn().mockReturnValue(permissions); for (const repository of [ _spaces.SPACE_REPOSITORY.FILES, _spaces.SPACE_REPOSITORY.TRASH ]){ const spaceEnv = await spacesManager.spaceEnv(userTest, [ repository, spaceAlias, 'foo', 'bar' ]); await expect((0, _paths.IsRealPathIsDirAndExists)(spaceEnv.realPath)).rejects.toThrow(); const repoPath = _spacemodel.SpaceModel.getRepositoryPath(spaceAlias, spaceEnv.inTrashRepository); await _promises.default.mkdir(_nodepath.default.join(repoPath, spaceEnv.root.alias, ...spaceEnv.paths), { recursive: true }); await expect((0, _paths.IsRealPathIsDirAndExists)(spaceEnv.realPath)).resolves.toBeUndefined(); await (0, _files.removeFiles)(_nodepath.default.join(repoPath, spaceEnv.root.alias, ...spaceEnv.paths)); } }); it(`should validate (or not) the space : ${spaceAlias} with a root (an external dir)`, async ()=>{ const permissions = { id: 0, alias: spaceAlias, name: spaceAlias, permissions: _spaces.SPACE_ALL_OPERATIONS, root: { id: 1, alias: 'foo', name: 'foo', externalPath: tmpDir, permissions: _spaces.SPACE_ALL_OPERATIONS } }; spacesQueries.permissions = jest.fn().mockReturnValueOnce(permissions); const spaceEnv = await spacesManager.spaceEnv(userTest, [ _spaces.SPACE_REPOSITORY.FILES, spaceAlias, 'foo', 'bar' ]); const rootPath = _nodepath.default.join(tmpDir, ...spaceEnv.paths); await _promises.default.mkdir(rootPath, { recursive: true }); await expect((0, _paths.IsRealPathIsDirAndExists)(spaceEnv.realPath)).resolves.toBeUndefined(); await (0, _files.removeFiles)(rootPath); await expect((0, _paths.IsRealPathIsDirAndExists)(spaceEnv.realPath)).rejects.toThrow(); }); it(`should validate (or not) the space : ${spaceAlias} with a root (a file/directory from user)`, async ()=>{ const permissions = { id: 0, alias: spaceAlias, name: spaceAlias, permissions: _spaces.SPACE_ALL_OPERATIONS, root: { id: 1, alias: 'document', name: 'document', file: { id: 0, path: '/foo', inTrash: false }, owner: { id: 0, login: userTest.login }, permissions: _spaces.SPACE_ALL_OPERATIONS } }; spacesQueries.permissions = jest.fn().mockReturnValueOnce(permissions); const spaceEnv = await spacesManager.spaceEnv(userTest, [ _spaces.SPACE_REPOSITORY.FILES, spaceAlias, 'document', 'bar' ]); await _promises.default.mkdir(_nodepath.default.join(_usermodel.UserModel.getFilesPath(userTest.login), 'foo', 'bar'), { recursive: true }); await expect((0, _paths.IsRealPathIsDirAndExists)(spaceEnv.realPath)).resolves.toBeUndefined(); await (0, _files.removeFiles)(_nodepath.default.join(_usermodel.UserModel.getFilesPath(userTest.login), 'foo', 'bar')); await expect((0, _paths.IsRealPathIsDirAndExists)(spaceEnv.realPath)).rejects.toThrow(); delete spaceEnv.root.file.path; try { await (0, _paths.IsRealPathIsDirAndExists)(spaceEnv.realPath); } catch (e) { expect(e).toBeInstanceOf(_fileerror.FileError); } }); }); //# sourceMappingURL=spaces-manager.service.spec.js.map