program finalizat

This commit is contained in:
andrei-mihnea-cerbu
2024-10-29 15:07:26 +02:00
parent ab1eaec413
commit 979539d3db
138 changed files with 14602 additions and 5068 deletions
+102 -154
View File
@@ -1,10 +1,11 @@
import { JsonManager } from './json_manager'; // Assuming this manages JSON configurations
import { TcpClient } from '../network/tcp/tcp_client'; // Assuming this handles TCP client connections
import { operationCodes } from '../network/operation_codes'; // Assuming this holds operation codes
import { JsonManager } from './json_manager'; // Assuming this manages JSON configurations
import { TcpCommunicator } from "./tcp_communicator";
import { operationCodes } from '../network/operation_codes'; // Assuming this holds operation codes
import { parentPort } from 'worker_threads';
import path from 'path';
import fs from 'fs';
import crypto from 'crypto'; // Import the crypto module for encryption and decryption
import crypto from 'crypto';
import { ParsedMessage } from "../network/message_handler"; // Import the crypto module for encryption and decryption
export class BackupRetrievalWorker {
private userConfig: JsonManager;
@@ -13,6 +14,7 @@ export class BackupRetrievalWorker {
private readonly destinationPath: string;
private encryptionKey: Buffer | null = null;
private iv: Buffer | null = null;
private tcpCommunicator: TcpCommunicator | null = null;
constructor(userConfigPath: string, applicationInfoPath: string, clientPort: number, destinationPath: string) {
this.userConfig = new JsonManager(userConfigPath);
@@ -22,203 +24,149 @@ export class BackupRetrievalWorker {
}
async start(): Promise<void> {
// Retrieve the necessary data from userConfig and applicationInfo
const userInfo = await this.userConfig.readValue('user_info');
if (!userInfo || !userInfo.name) {
console.error('User information or name is missing.');
return;
}
const userName = userInfo.name;
// Load encryption key and IV from userConfig
const encryptionData = await this.userConfig.readValue('encryption_key');
if (!encryptionData || !encryptionData.key || !encryptionData.iv) {
console.error('Encryption key or IV is missing.');
return;
}
// Convert encryption key and IV from base64 to buffer
this.encryptionKey = Buffer.from(encryptionData.key, 'base64');
this.iv = Buffer.from(encryptionData.iv, 'base64');
const activeUsersIp = await this.applicationInfo.readValue('users_ip');
if (!activeUsersIp || !activeUsersIp.length) {
console.error('No active users found.');
return;
}
// Process each IP and request backup data
for (const ip of activeUsersIp) {
try {
const success = await this.processBackupForIp(ip, userName);
if (success) {
console.log(`Backup retrieved successfully from ${ip}`);
} else {
console.error(`Failed to retrieve backup from ${ip}`);
}
} catch (error) {
console.error(`Error processing backup from ${ip}: ${error}`);
try {
const userInfo = await this.userConfig.readValue('user_info');
if (!userInfo || !userInfo.name) {
throw new Error('User information or name is missing.');
}
}
const userName = userInfo.name;
// Notify the parent that the worker is done
parentPort?.postMessage({ success: true, message: 'Backup retrieval completed.' });
const encryptionData = await this.userConfig.readValue('encryption_key');
if (!encryptionData || !encryptionData.key || !encryptionData.iv) {
throw new Error('Encryption key or IV is missing.');
}
this.encryptionKey = Buffer.from(encryptionData.key, 'base64');
this.iv = Buffer.from(encryptionData.iv, 'base64');
const activeUsersIp = await this.applicationInfo.readValue('users_ip');
if (!activeUsersIp || !activeUsersIp.length) {
throw new Error('No active users found.');
}
for (const ip of activeUsersIp) {
const success = await this.processBackupForIp(ip, userName);
if (!success) {
throw new Error(`Failed to retrieve backup from ${ip}`);
}
console.log(`Backup retrieved successfully from ${ip}`);
}
parentPort?.postMessage({ success: true, message: 'Backup retrieval completed successfully.' });
} catch (error: any) {
console.error('Error in BackupRetrievalWorker:', error);
parentPort?.postMessage({ success: false, message: `A problem occurred: ${error.message}` });
}
}
private async processBackupForIp(ip: string, userName: string): Promise<boolean> {
const tcpClient = new TcpClient(this.clientPort);
tcpClient.openSocket(ip);
try {
// Wait for the AES key to be set before continuing
const aesSet = await this.waitForAesKey(tcpClient);
if (!aesSet) {
console.error(`Timeout waiting for AES key on IP ${ip}`);
return false;
}
// Step 1: Check if a backup exists for the user on this IP
const backupExists = await this.checkIfBackupExists(tcpClient, userName);
if (!backupExists) {
console.log(`No backup found for user ${userName} on IP ${ip}`);
return false;
}
// Step 2: Request the structure of the backup directory
const backupStructure = await this.requestBackupStructure(tcpClient, userName);
if (!backupStructure || Object.keys(backupStructure).length === 0) {
console.log(`No files found in backup structure for user ${userName} on IP ${ip}`);
return false;
}
// Step 3: Request and retrieve each file from the backup
for (const relativeFilePath of Object.keys(backupStructure)) {
const fileRequestSuccess = await this.requestBackupFile(tcpClient, userName, relativeFilePath);
if (!fileRequestSuccess) {
console.error(`Failed to retrieve file ${relativeFilePath} from backup for user ${userName} on IP ${ip}`);
return false; // Stop if any file fails to be retrieved
}
}
return true; // All files retrieved successfully
} catch (error) {
console.error(`Error during backup processing for IP ${ip}: ${error}`);
return false;
} finally {
tcpClient.closeSocket(); // Ensure socket is closed
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
if (!await this.tcpCommunicator.connect()) {
return true;
}
const backupExists = await this.checkIfBackupExists(userName);
if (!backupExists) {
console.log(`No backup found for user ${userName} on IP ${ip}`);
await this.tcpCommunicator.disconnect();
return true;
}
const backupStructure = await this.requestBackupStructure(userName);
if (!backupStructure || Object.keys(backupStructure).length === 0) {
console.log(`No files found in backup structure for user ${userName} on IP ${ip}`);
await this.tcpCommunicator.disconnect();
return true;
}
for (const relativeFilePath of Object.keys(backupStructure)) {
const fileRequestSuccess = await this.requestBackupFile(userName, relativeFilePath);
if (!fileRequestSuccess) {
await this.tcpCommunicator.disconnect();
throw new Error(`Failed to retrieve file ${relativeFilePath} from backup for user ${userName} on IP ${ip}`);
}
}
await this.tcpCommunicator.disconnect();
return true;
}
// Wait for AES key to be set
private async waitForAesKey(tcpClient: TcpClient, timeout: number = 10000): Promise<boolean> {
const startTime = Date.now();
private async checkIfBackupExists(userName: string): Promise<boolean> {
if (!this.tcpCommunicator) return false;
return new Promise((resolve, reject) => {
const intervalId = setInterval(() => {
const elapsedTime = Date.now() - startTime;
if (tcpClient.isAesKeySet()) {
clearInterval(intervalId);
resolve(true); // AES key is set, we can proceed
} else if (elapsedTime > timeout) {
clearInterval(intervalId);
resolve(false); // Timeout reached, AES key not set
}
}, 500); // Check every 500ms
});
}
// Wait for a message response with timeout
private async waitForMessageResponse(tcpClient: TcpClient, timeout: number = 10000): Promise<any> {
const startTime = Date.now();
return new Promise((resolve) => {
const intervalId = setInterval(() => {
const elapsedTime = Date.now() - startTime;
const response = tcpClient.getLastResult();
if (response) {
clearInterval(intervalId);
resolve(response); // Return the response
} else if (elapsedTime > timeout) {
clearInterval(intervalId);
resolve(null); // Timeout, no response
}
}, 500); // Check every 500ms
});
}
// Check if the backup exists for the user on the remote IP
private async checkIfBackupExists(tcpClient: TcpClient, userName: string): Promise<boolean> {
const metaInfo = { name: userName };
await tcpClient.sendMessage(operationCodes.IS_BACKUP_CREATED, metaInfo);
if (!await this.tcpCommunicator.sendMessage(operationCodes.IS_BACKUP_CREATED, metaInfo)) return false;
const response = await this.waitForMessageResponse(tcpClient);
const response = await this.waitForResponse();
return response?.metaInfo?.backupExists === true;
}
// Request the backup structure from the remote IP
private async requestBackupStructure(tcpClient: TcpClient, userName: string): Promise<any> {
const metaInfo = { name: userName };
await tcpClient.sendMessage(operationCodes.GET_BACKUP_STRUCTURE, metaInfo);
private async requestBackupStructure(userName: string): Promise<any> {
if (!this.tcpCommunicator) return false;
const response = await this.waitForMessageResponse(tcpClient);
return response?.metaInfo?.structure ? response.metaInfo.structure : null;
const metaInfo = { name: userName };
if (!await this.tcpCommunicator.sendMessage(operationCodes.GET_BACKUP_STRUCTURE, metaInfo)) return null;
const response = await this.waitForResponse();
return response?.metaInfo?.structure || null;
}
// Request a file from the backup and wait for it to be decrypted and stored
private async requestBackupFile(tcpClient: TcpClient, userName: string, relativeFilePath: string): Promise<boolean> {
const metaInfo = { name: userName, relativeFilePath };
await tcpClient.sendMessage(operationCodes.REQ_FILE_FROM_BACKUP, metaInfo);
private async requestBackupFile(userName: string, relativeFilePath: string): Promise<boolean> {
if (!this.tcpCommunicator) return false;
const response = await this.waitForMessageResponse(tcpClient);
const metaInfo = { name: userName, relativeFilePath };
if (!await this.tcpCommunicator.sendMessage(operationCodes.REQ_FILE_FROM_BACKUP, metaInfo)) return false;
const response = await this.waitForResponse();
if (response?.operationCode === operationCodes.OK && response.metaInfo && response.fileContent) {
return this.saveFile(response.metaInfo.relativeFilePath, response.fileContent);
return this.saveFile(response.metaInfo.relativeFilePath, response.fileContent.toString('base64'));
}
return false;
}
// Decrypt the file content using AES-256-CBC and save the decrypted file
private saveFile(relativeFilePath: string, fileContent: string): boolean {
if (!this.encryptionKey || !this.iv) {
console.error('Encryption key or IV is not set.');
return false;
throw new Error('Encryption key or IV is not set.');
}
// Decode the base64-encoded file content into a buffer
let encryptedBuffer: Buffer;
try {
encryptedBuffer = Buffer.from(fileContent, 'base64');
} catch (error) {
console.error('Error decoding base64 file content:', error);
return false;
} catch (error: any) {
throw new Error(`Error decoding base64 file content: ${error.message}`);
}
// Decrypt the file content
let decryptedContent: Buffer;
try {
const decipher = crypto.createDecipheriv('aes-256-cbc', this.encryptionKey, this.iv);
decryptedContent = Buffer.concat([decipher.update(encryptedBuffer), decipher.final()]);
} catch (error) {
console.error('Error decrypting file:', error);
return false;
} catch (error: any) {
throw new Error(`Error decrypting file: ${error.message}`);
}
// Save the decrypted file content
const fullFilePath = path.join(this.destinationPath, relativeFilePath);
try {
const dirPath = path.dirname(fullFilePath);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true }); // Ensure directory exists
fs.mkdirSync(dirPath, { recursive: true });
}
fs.writeFileSync(fullFilePath, decryptedContent);
console.log(`File saved successfully: ${fullFilePath}`);
return true;
} catch (error: any) {
console.error(`Error saving file ${relativeFilePath}: ${error.message}`);
return false;
throw new Error(`Error saving file ${relativeFilePath}: ${error.message}`);
}
}
private async waitForResponse(): Promise<ParsedMessage | null> {
return new Promise((resolve) => {
const idResponseCheck = setInterval(async () => {
if (!this.tcpCommunicator) return null;
if (this.tcpCommunicator.hasResponseArrived()) {
clearInterval(idResponseCheck);
resolve(this.tcpCommunicator.getLastResult());
}
}, 100);
});
}
}