@sync-in/server
Version:
The secure, open-source platform for file storage, sharing, collaboration, and sync
269 lines (268 loc) • 12.1 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 _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