@sync-in/server
Version:
The secure, open-source platform for file storage, sharing, collaboration, and sync
218 lines (217 loc) • 11.6 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
});
Object.defineProperty(exports, "SyncClientsManager", {
enumerable: true,
get: function() {
return SyncClientsManager;
}
});
const _axios = require("@nestjs/axios");
const _common = require("@nestjs/common");
const _nodecrypto = /*#__PURE__*/ _interop_require_default(require("node:crypto"));
const _promises = /*#__PURE__*/ _interop_require_default(require("node:fs/promises"));
const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
const _authmethod = require("../../../authentication/models/auth-method");
const _authmanagerservice = require("../../../authentication/services/auth-manager.service");
const _functions = require("../../../common/functions");
const _shared = require("../../../common/shared");
const _configconstants = require("../../../configuration/config.constants");
const _configenvironment = require("../../../configuration/config.environment");
const _cachedecorator = require("../../../infrastructure/cache/cache.decorator");
const _applicationsconstants = require("../../applications.constants");
const _files = require("../../files/utils/files");
const _user = require("../../users/constants/user");
const _usermodel = require("../../users/models/user.model");
const _usersqueriesservice = require("../../users/services/users-queries.service");
const _auth = require("../constants/auth");
const _store = require("../constants/store");
const _sync = require("../constants/sync");
const _syncqueriesservice = require("./sync-queries.service");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _ts_decorate(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
function _ts_metadata(k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
}
let SyncClientsManager = class SyncClientsManager {
async register(clientRegistrationDto, ip) {
const user = await this.authMethod.validateUser(clientRegistrationDto.login, clientRegistrationDto.password);
if (!user) {
this.logger.warn(`${this.register.name} - auth failed for user *${clientRegistrationDto.login}*`);
throw new _common.HttpException('Not authorized', _common.HttpStatus.UNAUTHORIZED);
}
if (!user.havePermission(_user.USER_PERMISSION.DESKTOP_APP)) {
this.logger.warn(`${this.register.name} - does not have permission : ${_user.USER_PERMISSION.DESKTOP_APP}`);
throw new _common.HttpException('Missing permission', _common.HttpStatus.FORBIDDEN);
}
try {
const token = await this.syncQueries.getOrCreateClient(user.id, clientRegistrationDto.clientId, clientRegistrationDto.info, ip);
this.logger.log(`${this.register.name} - client *${clientRegistrationDto.info.type}* was registered for user *${user.login}* (${user.id})`);
return {
clientToken: token
};
} catch (e) {
this.logger.error(`${this.register.name} - ${e}`);
throw new _common.HttpException('Error during the client registration', _common.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async unregister(user) {
try {
await this.syncQueries.deleteClient(user.id, user.clientId);
} catch (e) {
this.logger.error(`${this.unregister.name} - ${e}`);
throw new _common.HttpException('Error during the removing of client registration', _common.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async authenticate(authType, syncClientAuthDto, ip, res) {
const client = await this.syncQueries.getClient(syncClientAuthDto.clientId, null, syncClientAuthDto.token);
if (!client) {
throw new _common.HttpException('Client is unknown', _common.HttpStatus.FORBIDDEN);
}
if (!client.enabled) {
throw new _common.HttpException('Client is disabled', _common.HttpStatus.FORBIDDEN);
}
if ((0, _shared.currentTimeStamp)() >= client.tokenExpiration) {
throw new _common.HttpException(_auth.CLIENT_TOKEN_EXPIRED_ERROR, _common.HttpStatus.FORBIDDEN);
}
this.syncQueries.updateClientInfo(client, client.info, ip).catch((e)=>this.logger.error(`${this.authenticate.name} - ${e}`));
const user = await this.usersQueries.from(client.ownerId);
if (!user) {
throw new _common.HttpException('User does not exist', _common.HttpStatus.FORBIDDEN);
}
if (!user.isActive) {
throw new _common.HttpException('Account suspended or not authorized', _common.HttpStatus.FORBIDDEN);
}
const owner = new _usermodel.UserModel(user);
if (!owner.havePermission(_user.USER_PERMISSION.DESKTOP_APP)) {
this.logger.warn(`${this.register.name} - does not have permission : ${_user.USER_PERMISSION.DESKTOP_APP}`);
throw new _common.HttpException('Missing permission', _common.HttpStatus.FORBIDDEN);
}
// set clientId
owner.clientId = client.id;
// update accesses
this.usersQueries.updateUserOrGuest(owner.id, {
lastAccess: owner.currentAccess,
currentAccess: new Date(),
lastIp: owner.currentIp,
currentIp: ip
}).catch((e)=>this.logger.error(`${this.authenticate.name} - ${e}`));
let r;
if (authType === _auth.CLIENT_AUTH_TYPE.COOKIE) {
// used by the desktop app to perform the login setup using cookies
r = await this.authManager.setCookies(owner, res);
} else if (authType === _auth.CLIENT_AUTH_TYPE.TOKEN) {
// used by the cli app and the sync core
r = await this.authManager.getTokens(owner);
}
// check if the client token must be updated
r.client_token_update = await this.renewTokenAndExpiration(client, owner);
return r;
}
getClients(user) {
return this.syncQueries.getClients(user);
}
async renewTokenAndExpiration(client, owner) {
if ((0, _shared.currentTimeStamp)() + (0, _functions.convertHumanTimeToSeconds)(_auth.CLIENT_TOKEN_RENEW_TIME) < client.tokenExpiration) {
// client token expiration is not close enough
return undefined;
}
const token = _nodecrypto.default.randomUUID();
const expiration = (0, _shared.currentTimeStamp)() + (0, _functions.convertHumanTimeToSeconds)(_auth.CLIENT_TOKEN_EXPIRATION_TIME);
this.logger.log(`${this.renewTokenAndExpiration.name} - renew token for user *${owner.login}* and client *${client.id}*`);
try {
await this.syncQueries.renewClientTokenAndExpiration(client.id, token, expiration);
} catch (e) {
this.logger.error(`${this.renewTokenAndExpiration.name} - unable to renew token for user *${owner.login}* and client *${client.id}* : ${e}`);
throw new _common.HttpException('Unable to update client token', _common.HttpStatus.BAD_REQUEST);
}
return token;
}
async deleteClient(user, clientId) {
try {
await this.syncQueries.deleteClient(user.id, clientId);
} catch (e) {
this.logger.error(`${this.deleteClient.name} - ${e}`);
throw new _common.HttpException('Unable to delete client', _common.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async checkAppStore() {
let manifest = null;
if (_configenvironment.configuration.applications.appStore.repository === _store.APP_STORE_REPOSITORY.PUBLIC) {
const url = `${_store.APP_STORE_URL}/${_store.APP_STORE_MANIFEST_FILE}`;
try {
const res = await this.http.axiosRef({
method: _applicationsconstants.HTTP_METHOD.GET,
url: url
});
manifest = res.data;
manifest.repository = _store.APP_STORE_REPOSITORY.PUBLIC;
} catch (e) {
this.logger.warn(`${this.checkAppStore.name} - unable to retrieve ${url} : ${e}`);
}
} else {
const latestFile = _nodepath.default.join(_configconstants.STATIC_PATH, _store.APP_STORE_DIRNAME, _store.APP_STORE_MANIFEST_FILE);
if (!await (0, _files.isPathExists)(latestFile)) {
this.logger.warn(`${this.checkAppStore.name} - ${latestFile} does not exist`);
} else {
try {
manifest = JSON.parse(await _promises.default.readFile(latestFile, 'utf8'));
manifest.repository = _store.APP_STORE_REPOSITORY.LOCAL;
// rewrite urls to local repository
for (const [os, packages] of Object.entries(manifest.platform)){
for (const p of packages){
if (p.package.toLowerCase().startsWith(_sync.SYNC_CLIENT_TYPE.DESKTOP)) {
p.url = `${_store.APP_STORE_DIRNAME}/${_sync.SYNC_CLIENT_TYPE.DESKTOP}/${os}/${p.package}`;
} else {
p.url = `${_store.APP_STORE_DIRNAME}/${_sync.SYNC_CLIENT_TYPE.CLI}/${p.package}`;
}
}
}
} catch (e) {
this.logger.error(`${this.checkAppStore.name} - ${latestFile} : ${e}`);
}
}
}
return manifest;
}
constructor(http, authManager, authMethod, usersQueries, syncQueries){
this.http = http;
this.authManager = authManager;
this.authMethod = authMethod;
this.usersQueries = usersQueries;
this.syncQueries = syncQueries;
this.logger = new _common.Logger(SyncClientsManager.name);
}
};
_ts_decorate([
(0, _cachedecorator.CacheDecorator)(3600),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", []),
_ts_metadata("design:returntype", Promise)
], SyncClientsManager.prototype, "checkAppStore", null);
SyncClientsManager = _ts_decorate([
(0, _common.Injectable)(),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", [
typeof _axios.HttpService === "undefined" ? Object : _axios.HttpService,
typeof _authmanagerservice.AuthManager === "undefined" ? Object : _authmanagerservice.AuthManager,
typeof _authmethod.AuthMethod === "undefined" ? Object : _authmethod.AuthMethod,
typeof _usersqueriesservice.UsersQueries === "undefined" ? Object : _usersqueriesservice.UsersQueries,
typeof _syncqueriesservice.SyncQueries === "undefined" ? Object : _syncqueriesservice.SyncQueries
])
], SyncClientsManager);
//# sourceMappingURL=sync-clients-manager.service.js.map