network chunk v12

This commit is contained in:
andrei-mihnea-cerbu
2024-11-13 13:33:03 +02:00
parent 664b987269
commit 39d007e34f
12 changed files with 182 additions and 436 deletions
+40
View File
@@ -0,0 +1,40 @@
export let operationCodes = {
// General Operations
HEARTBEAT: 'HEARTBEAT',
ALIVE: 'ALIVE',
SET_PUBLIC_KEY: 'SET_PUBLIC_KEY',
SET_AES_KEY: 'SET_AES_KEY',
RESET_DATABASE: 'RESET_DATABASE',
OK: 'OK',
ERR: 'ERR',
END: 'END',
UNKNOWN_COMMAND: 'UNKNOWN_COMMAND',
// Auth Operations
LOGIN: 'LOGIN',
SIGN_UP: 'SIGN_UP',
RESET_PASSWORD: 'RESET_PASSWORD',
FIND_BY_EMAIL: 'FIND_BY_EMAIL',
MODIFY_USER: 'MODIFY_USER',
GET_DEPARTMENTS: 'GET_DEPARTMENTS',
CREATE_DEPARTMENT: 'CREATE_DEPARTMENT',
MODIFY_DEPARTMENT: 'MODIFY_DEPARTMENT',
DELETE_DEPARTMENT: 'DELETE_DEPARTMENT',
GET_USERS: 'GET_USERS',
FIND_KEY_BY_USER_ID: 'FIND_KEY_BY_USER_ID',
SEND_ANNOUNCEMENT: 'SEND_ANNOUNCEMENT',
GET_USER_INFORMATION: 'GET_USER_INFORMATION',
CLEAR_BACKUP: 'CLEAR_BACKUP',
BACKUP_FILE: 'BACKUP_FILE',
SHARE_FILE: 'SHARE_FILE',
CLEAR_DEPARTMENT: 'CLEAR_DEPARTMENT',
DEPARTMENT_FILE: 'DEPARTMENT_FILE',
IS_BACKUP_CREATED: 'IS_BACKUP_CREATED',
GET_BACKUP_STRUCTURE: 'GET_BACKUP_STRUCTURE',
REQ_FILE_FROM_BACKUP: 'REQ_FILE_FROM_BACKUP',
DECRYPT_AND_SAVE_OLD_BACKUP_FILE: 'DECRYPT_AND_SAVE_OLD_BACKUP_FILE',
};
@@ -1,43 +0,0 @@
import { ParsedMessage } from '../message_handler';
import { OperationHandler } from './operation_handler';
import { OperationPlugin } from './operation_plugin';
export abstract class OperationBase implements OperationPlugin {
// Shared operation codes
public static readonly operationCodes = {
OK: 'OK',
ERR: 'ERR',
END: 'END',
};
// Default handler for OK operation
public static handleOk(parsedMessage: ParsedMessage): ParsedMessage {
console.log('OK operation received');
return parsedMessage; // Typically, you would just acknowledge with OK, returning as-is
}
// Default handler for ERR operation
public static handleErr(parsedMessage: ParsedMessage): ParsedMessage {
console.log('ERR operation received: ', parsedMessage.metaInfo?.message || 'No error details provided');
return parsedMessage; // Typically, you would log the error and return
}
// Default handler for END operation
public static handleEnd(parsedMessage: ParsedMessage): ParsedMessage {
console.log('END operation received');
return {
operationCode: OperationBase.operationCodes.END,
metaInfo: { message: 'Connection ended.' },
};
}
// Register the common OK, ERR, and END handlers
public static registerCommonOperations(operationHandler: OperationHandler): void {
operationHandler.registerHandler(OperationBase.operationCodes.OK, OperationBase.handleOk);
operationHandler.registerHandler(OperationBase.operationCodes.ERR, OperationBase.handleErr);
operationHandler.registerHandler(OperationBase.operationCodes.END, OperationBase.handleEnd);
}
// Abstract register method that will be implemented by subclasses
public abstract register(operationHandler: OperationHandler): void;
}
@@ -2,14 +2,14 @@
import { ParsedMessage, MessageHandler } from '../message_handler'; import { ParsedMessage, MessageHandler } from '../message_handler';
import { OperationPlugin } from './operation_plugin'; import { OperationPlugin } from './operation_plugin';
type OperationHandlerFunction = (parsedMessage: ParsedMessage) => ParsedMessage; // Define handler function type to return Promise<ParsedMessage>
type OperationHandlerFunction = (parsedMessage: ParsedMessage) => Promise<ParsedMessage>;
export class OperationHandler { export class OperationHandler {
private static instance: OperationHandler; private static instance: OperationHandler;
private handlers: { [operationCode: string]: OperationHandlerFunction } = {}; private handlers: { [operationCode: string]: OperationHandlerFunction } = {};
private constructor() { private constructor() {
// Register only the unknown command handler on initialization
this.registerHandler('UNKNOWN_COMMAND', this.handleUnknownCommand); this.registerHandler('UNKNOWN_COMMAND', this.handleUnknownCommand);
} }
@@ -26,30 +26,24 @@ export class OperationHandler {
this.handlers[operationCode] = handler; this.handlers[operationCode] = handler;
} }
// Handle operation request // Handle operation request asynchronously
public handleOperation(rawMessage: string): ParsedMessage { public async handleOperation(rawMessage: string): Promise<ParsedMessage> {
// Parse and validate the message
const parsedMessage = MessageHandler.parseMessage(rawMessage); const parsedMessage = MessageHandler.parseMessage(rawMessage);
if (!parsedMessage || !MessageHandler.validateMessage(parsedMessage)) { if (!parsedMessage || !MessageHandler.validateMessage(parsedMessage)) {
return this.handleUnknownCommand(parsedMessage); return this.handleUnknownCommand(parsedMessage);
} }
// Dispatch the handler for the given operation code // Retrieve the handler for the operation code and invoke it asynchronously
const handler = this.handlers[parsedMessage.operationCode]; const handler = this.handlers[parsedMessage.operationCode];
if (handler) { if (handler) {
return handler(parsedMessage); return await handler(parsedMessage);
} else { } else {
return this.handleUnknownCommand(parsedMessage); return this.handleUnknownCommand(parsedMessage);
} }
} }
// Get all registered operation codes
public getAvailableOperationCodes(): string[] {
return Object.keys(this.handlers);
}
// Default handler for unknown commands // Default handler for unknown commands
private handleUnknownCommand(parsedMessage?: ParsedMessage): ParsedMessage { private async handleUnknownCommand(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
return { return {
operationCode: 'UNKNOWN_COMMAND', operationCode: 'UNKNOWN_COMMAND',
metaInfo: { message: 'Unknown command received.' }, metaInfo: { message: 'Unknown command received.' },
@@ -1,14 +1,14 @@
import { ParsedMessage } from '../message_handler'; import { ParsedMessage } from '../message_handler';
import {userDatabase, departmentDatabase, keyDatabase} from '../../db_managers/db'; import { userDatabase, departmentDatabase, keyDatabase } from '../../db_managers/db';
import { OperationBase } from '../operations_base/operation_base';
import { OperationHandler } from '../operations_base/operation_handler'; import { OperationHandler } from '../operations_base/operation_handler';
import { OperationPlugin } from '../operations_base/operation_plugin';
import {operationCodes} from "../operation_codes";
export class AuthOperations extends OperationBase { export class AuthOperations implements OperationPlugin {
public static readonly operationCodes = { public static readonly operationCodes = {
...OperationBase.operationCodes,
LOGIN: 'LOGIN', LOGIN: 'LOGIN',
SIGN_UP: 'SIGN_UP', SIGN_UP: 'SIGN_UP',
RESET_PASSWORD: 'RESET_PASSWORD' RESET_PASSWORD: 'RESET_PASSWORD',
}; };
// Utility function to validate email format // Utility function to validate email format
@@ -17,95 +17,82 @@ export class AuthOperations extends OperationBase {
return emailRegex.test(email); return emailRegex.test(email);
} }
// Utility function to validate password strength (min 8 chars, at least 1 number and 1 special char) // Utility function to validate password strength
private static isStrongPassword(password: string): boolean { private static isStrongPassword(password: string): boolean {
const passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/; const passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/;
return passwordRegex.test(password); return passwordRegex.test(password);
} }
// Utility function to check name is not empty
private static isValidName(name: string): boolean { private static isValidName(name: string): boolean {
return name.trim().length > 0; return name.trim().length > 0;
} }
private static isValidAppType(app_type: string){ private static isValidAppType(app_type: string): boolean {
const app_types = ['client', 'ceo', 'admin']; const appTypes = ['client', 'ceo', 'admin'];
return app_types.includes(app_type); return appTypes.includes(app_type);
} }
// Handle Login operation with validation // Handle Login operation with validation
public static handleLogin(parsedMessage: ParsedMessage): ParsedMessage { public static async handleLogin(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
const { email, password, app_type } = parsedMessage.metaInfo || {}; const { email, password, app_type } = parsedMessage.metaInfo || {};
// Check if email and password are provided
if (!email || !password || !app_type) { if (!email || !password || !app_type) {
return { return {
operationCode: OperationBase.operationCodes.ERR, operationCode: operationCodes.ERR,
metaInfo: { message: 'Both email and password are required.' }, metaInfo: { message: 'Email, password, and app_type are required.' },
}; };
} }
// Verify email and password with the database
const result = userDatabase.verifyCredentials(email, password, app_type); const result = userDatabase.verifyCredentials(email, password, app_type);
// Handle the case where the credentials are incorrect
if (!result.success) { if (!result.success) {
return { return {
operationCode: OperationBase.operationCodes.ERR, operationCode: operationCodes.ERR,
metaInfo: { message: result.message }, metaInfo: { message: result.message },
}; };
} }
return { return {
operationCode: OperationBase.operationCodes.OK, operationCode: operationCodes.OK,
metaInfo: { message: 'Login successful.' }, metaInfo: { message: 'Login successful.' },
}; };
} }
// Handle SignUp operation with validation // Handle SignUp operation with validation
public static handleSignUp(parsedMessage: ParsedMessage): ParsedMessage { public static async handleSignUp(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
const { name, email, password, departmentId, app_type } = parsedMessage.metaInfo || {}; const { name, email, password, departmentId, app_type } = parsedMessage.metaInfo || {};
// Validate name
if (!AuthOperations.isValidName(name)) { if (!AuthOperations.isValidName(name)) {
return { return {
operationCode: OperationBase.operationCodes.ERR, operationCode: operationCodes.ERR,
metaInfo: { message: 'Invalid name. Please provide a valid name.' }, metaInfo: { message: 'Invalid name.' },
}; };
} }
// Validate email format
if (!AuthOperations.isValidEmail(email)) { if (!AuthOperations.isValidEmail(email)) {
return { return {
operationCode: OperationBase.operationCodes.ERR, operationCode: operationCodes.ERR,
metaInfo: { message: 'Invalid email format.' }, metaInfo: { message: 'Invalid email format.' },
}; };
} }
// Validate password strength
if (!AuthOperations.isStrongPassword(password)) { if (!AuthOperations.isStrongPassword(password)) {
return { return {
operationCode: OperationBase.operationCodes.ERR, operationCode: operationCodes.ERR,
metaInfo: { metaInfo: { message: 'Weak password.' },
message: 'Weak password. It must be at least 8 characters long, contain at least 1 letter, 1 number, and 1 special character.',
},
}; };
} }
if (!AuthOperations.isValidAppType(app_type)) { if (!AuthOperations.isValidAppType(app_type)) {
return { return {
operationCode: OperationBase.operationCodes.ERR, operationCode: operationCodes.ERR,
metaInfo: { metaInfo: { message: 'Invalid app type.' },
message: 'Not valid app_type.',
},
}; };
} }
// Check if user with this email already exists
const existingUser = userDatabase.findByEmail(email); const existingUser = userDatabase.findByEmail(email);
if (existingUser) { if (existingUser) {
return { return {
operationCode: OperationBase.operationCodes.ERR, operationCode: operationCodes.ERR,
metaInfo: { message: 'Email already in use.' }, metaInfo: { message: 'Email already in use.' },
}; };
} }
@@ -113,71 +100,57 @@ export class AuthOperations extends OperationBase {
const existingUserByName = userDatabase.getAllUsers().find((user) => user.name === name); const existingUserByName = userDatabase.getAllUsers().find((user) => user.name === name);
if (existingUserByName) { if (existingUserByName) {
return { return {
operationCode: OperationBase.operationCodes.ERR, operationCode: operationCodes.ERR,
metaInfo: { message: 'Name already in use.' }, metaInfo: { message: 'Name already in use.' },
}; };
} }
// Validate department ID
const departmentEntry = departmentDatabase.findById(departmentId); const departmentEntry = departmentDatabase.findById(departmentId);
if (!departmentEntry) { if (!departmentEntry) {
return { return {
operationCode: OperationBase.operationCodes.ERR, operationCode: operationCodes.ERR,
metaInfo: { message: 'Invalid department.' }, metaInfo: { message: 'Invalid department.' },
}; };
} }
// Create new user in the database
const newUser = userDatabase.createUser(name, email, password, departmentEntry.id, app_type); const newUser = userDatabase.createUser(name, email, password, departmentEntry.id, app_type);
// Create a key for the new user
keyDatabase.createKey(newUser.id); keyDatabase.createKey(newUser.id);
return { return {
operationCode: OperationBase.operationCodes.OK, operationCode: operationCodes.OK,
metaInfo: { metaInfo: { message: 'User created successfully.', userId: newUser.id },
message: 'User created successfully.',
userId: newUser.id,
},
}; };
} }
// Handle Reset Password operation // Handle Reset Password operation
public static handleResetPassword(parsedMessage: ParsedMessage): ParsedMessage { public static async handleResetPassword(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
const { email, newPassword, app_type } = parsedMessage.metaInfo || {}; const { email, newPassword, app_type } = parsedMessage.metaInfo || {};
if (!AuthOperations.isStrongPassword(newPassword)) { if (!AuthOperations.isStrongPassword(newPassword)) {
return { return {
operationCode: OperationBase.operationCodes.ERR, operationCode: operationCodes.ERR,
metaInfo: { metaInfo: { message: 'Weak password.' },
message: 'Weak password. It must be at least 8 characters long, contain at least 1 letter, 1 number, and 1 special character.',
},
}; };
} }
// Logic to reset password (could be sending a reset link, or generating a temp password)
const resetResult = userDatabase.resetPassword(email, newPassword, app_type); const resetResult = userDatabase.resetPassword(email, newPassword, app_type);
if (resetResult.success) { if (resetResult.success) {
return { return {
operationCode: OperationBase.operationCodes.OK, operationCode: operationCodes.OK,
metaInfo: { message: 'Password reset successfully. Please check your email for instructions.' }, metaInfo: { message: 'Password reset successfully.' },
}; };
} else { } else {
return { return {
operationCode: OperationBase.operationCodes.ERR, operationCode: operationCodes.ERR,
metaInfo: { message: 'Failed to reset password.' }, metaInfo: { message: 'Failed to reset password.' },
}; };
} }
} }
// Register specific operations for AuthOperations // Register operations with the OperationHandler
public register(operationHandler: OperationHandler): void { public register(operationHandler: OperationHandler): void {
operationHandler.registerHandler(AuthOperations.operationCodes.LOGIN, AuthOperations.handleLogin); operationHandler.registerHandler(AuthOperations.operationCodes.LOGIN, AuthOperations.handleLogin);
operationHandler.registerHandler(AuthOperations.operationCodes.SIGN_UP, AuthOperations.handleSignUp); operationHandler.registerHandler(AuthOperations.operationCodes.SIGN_UP, AuthOperations.handleSignUp);
operationHandler.registerHandler(AuthOperations.operationCodes.RESET_PASSWORD, AuthOperations.handleResetPassword) operationHandler.registerHandler(AuthOperations.operationCodes.RESET_PASSWORD, AuthOperations.handleResetPassword);
// Register common OK, ERR, and END operations from the base class
OperationBase.registerCommonOperations(operationHandler);
} }
} }
@@ -1,42 +1,28 @@
import { ParsedMessage } from '../message_handler'; import { ParsedMessage } from '../message_handler';
import { OperationBase } from '../operations_base/operation_base'; import { departmentDatabase, keyDatabase, userDatabase } from '../../db_managers/db';
import {
departmentDatabase,
keyDatabase,
userDatabase
} from '../../db_managers/db';
import { OperationHandler } from '../operations_base/operation_handler'; import { OperationHandler } from '../operations_base/operation_handler';
import { OperationPlugin } from '../operations_base/operation_plugin';
import { operationCodes} from "../operation_codes";
export class CeoOperations extends OperationBase { export class CeoOperations implements OperationPlugin {
public static readonly operationCodes = { public static readonly operationCodes = {
...OperationBase.operationCodes, // Inherit common operation codes (OK, ERR, END)
RESET_DATABASE: 'RESET_DATABASE', RESET_DATABASE: 'RESET_DATABASE',
}; };
// Handle reset database operation public static async handleResetDatabase(): Promise<ParsedMessage> {
public static handleResetDatabase(): ParsedMessage {
console.log('Resetting the database...'); console.log('Resetting the database...');
departmentDatabase.cleanTable(); departmentDatabase.cleanTable();
userDatabase.cleanTable(); userDatabase.cleanTable();
keyDatabase.cleanTable(); keyDatabase.cleanTable();
// Create the departments
departmentDatabase.createDepartment('Worker'); departmentDatabase.createDepartment('Worker');
return { return {
operationCode: CeoOperations.operationCodes.OK, operationCode: operationCodes.OK,
metaInfo: { metaInfo: { message: 'Database reset successfully.' },
message: 'Database reset successfully.',
},
}; };
} }
// Register general operations with the OperationHandler
public register(operationHandler: OperationHandler): void { public register(operationHandler: OperationHandler): void {
operationHandler.registerHandler(CeoOperations.operationCodes.RESET_DATABASE, CeoOperations.handleResetDatabase); operationHandler.registerHandler(CeoOperations.operationCodes.RESET_DATABASE, CeoOperations.handleResetDatabase);
// Register common operations inherited from the base class
OperationBase.registerCommonOperations(operationHandler);
} }
} }
@@ -1,129 +1,54 @@
import { ParsedMessage } from '../message_handler'; import { ParsedMessage } from '../message_handler';
import {departmentDatabase} from '../../db_managers/db'; import { departmentDatabase } from '../../db_managers/db';
import { OperationBase } from '../operations_base/operation_base';
import { OperationHandler } from '../operations_base/operation_handler'; import { OperationHandler } from '../operations_base/operation_handler';
import { OperationPlugin } from '../operations_base/operation_plugin';
import { operationCodes } from "../operation_codes";
export class DepartmentOperations extends OperationBase { export class DepartmentOperations implements OperationPlugin {
public static readonly operationCodes = { public static readonly operationCodes = {
...OperationBase.operationCodes,
GET_DEPARTMENTS: 'GET_DEPARTMENTS', GET_DEPARTMENTS: 'GET_DEPARTMENTS',
CREATE_DEPARTMENT: 'CREATE_DEPARTMENT', CREATE_DEPARTMENT: 'CREATE_DEPARTMENT',
MODIFY_DEPARTMENT: 'MODIFY_DEPARTMENT', MODIFY_DEPARTMENT: 'MODIFY_DEPARTMENT',
DELETE_DEPARTMENT: 'DELETE_DEPARTMENT', DELETE_DEPARTMENT: 'DELETE_DEPARTMENT',
FIND_DEPARTMENT_BY_ID: 'FIND_DEPARTMENT_BY_ID' FIND_DEPARTMENT_BY_ID: 'FIND_DEPARTMENT_BY_ID',
}; };
// Get all departments public static async handleGetDepartments(): Promise<ParsedMessage> {
public static handleGetDepartments(): ParsedMessage {
const departments = departmentDatabase.getAllDepartments(); const departments = departmentDatabase.getAllDepartments();
return { return { operationCode: operationCodes.OK, metaInfo: { departments } };
operationCode: DepartmentOperations.operationCodes.OK,
metaInfo: { departments },
};
} }
public static handleGetDepartmentById(parsedMessage: ParsedMessage): ParsedMessage { public static async handleGetDepartmentById(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
const { id } = parsedMessage.metaInfo || {}; const { id } = parsedMessage.metaInfo || {};
const department = departmentDatabase.findById(id);
if(!id){ return department
return { ? { operationCode: operationCodes.OK, metaInfo: { department } }
operationCode: DepartmentOperations.operationCodes.ERR, : { operationCode: operationCodes.ERR, metaInfo: { message: 'Department not found.' } };
metaInfo: { message: 'Id is required.' },
};
} }
const user = departmentDatabase.findById(id); public static async handleCreateDepartment(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
return { const { departmentName } = parsedMessage.metaInfo || {};
operationCode: user === null ? DepartmentOperations.operationCodes.ERR : DepartmentOperations.operationCodes.OK, const department = departmentDatabase.createDepartment(departmentName);
metaInfo: user === null ? { message: 'Department not found.' } : { message: user } return { operationCode: operationCodes.OK, metaInfo: { department } };
}
} }
// Create a new department public static async handleModifyDepartment(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
public static handleCreateDepartment(parsedMessage: ParsedMessage): ParsedMessage { const { departmentId, newDepartmentName } = parsedMessage.metaInfo || {};
// @ts-ignore const success = departmentDatabase.modifyDepartment(departmentId, newDepartmentName);
const { departmentName } = parsedMessage.metaInfo; return { operationCode: success ? operationCodes.OK : operationCodes.ERR };
// Check if department name is provided
if (!departmentName) {
return {
operationCode: DepartmentOperations.operationCodes.ERR,
metaInfo: { message: 'Department name is required.' },
};
} }
// Check if the department already exists public static async handleDeleteDepartment(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
const existingDepartment = departmentDatabase.findByName(departmentName); const { departmentId } = parsedMessage.metaInfo || {};
if (existingDepartment) { const success = departmentDatabase.deleteDepartment(departmentId);
return { return { operationCode: success ? operationCodes.OK : operationCodes.ERR };
operationCode: DepartmentOperations.operationCodes.ERR,
metaInfo: { message: 'Department already exists.' },
};
} }
// Create the new department
const newDepartment = departmentDatabase.createDepartment(departmentName);
return {
operationCode: DepartmentOperations.operationCodes.OK,
metaInfo: { message: 'Department created successfully.', departmentId: newDepartment.id },
};
}
// Modify an existing department
public static handleModifyDepartment(parsedMessage: ParsedMessage): ParsedMessage {
// @ts-ignore
const { departmentId, newDepartmentName } = parsedMessage.metaInfo;
// Validate input
if (!departmentId || !newDepartmentName) {
return {
operationCode: DepartmentOperations.operationCodes.ERR,
metaInfo: { message: 'Department ID and new department name are required.' },
};
}
// Modify the department
const isSuccess = departmentDatabase.modifyDepartment(departmentId, newDepartmentName);
return {
operationCode: isSuccess ? DepartmentOperations.operationCodes.OK : DepartmentOperations.operationCodes.ERR,
metaInfo: { message: isSuccess ? 'Department modified successfully.' : 'Failed to modify department.' },
};
}
// Delete an existing department
public static handleDeleteDepartment(parsedMessage: ParsedMessage): ParsedMessage {
// @ts-ignore
const { departmentId } = parsedMessage.metaInfo;
// Validate input
if (!departmentId) {
return {
operationCode: DepartmentOperations.operationCodes.ERR,
metaInfo: { message: 'Department ID is required.' },
};
}
// Delete the department
const isSuccess = departmentDatabase.deleteDepartment(departmentId);
return {
operationCode: isSuccess ? DepartmentOperations.operationCodes.OK : DepartmentOperations.operationCodes.ERR,
metaInfo: { message: isSuccess ? 'Department deleted successfully.' : 'Failed to delete department.' },
};
}
// Register department operations_base with the OperationHandler
public register(operationHandler: OperationHandler): void { public register(operationHandler: OperationHandler): void {
// Register the department operation handlers
operationHandler.registerHandler(DepartmentOperations.operationCodes.GET_DEPARTMENTS, DepartmentOperations.handleGetDepartments); operationHandler.registerHandler(DepartmentOperations.operationCodes.GET_DEPARTMENTS, DepartmentOperations.handleGetDepartments);
operationHandler.registerHandler(DepartmentOperations.operationCodes.CREATE_DEPARTMENT, DepartmentOperations.handleCreateDepartment); operationHandler.registerHandler(DepartmentOperations.operationCodes.CREATE_DEPARTMENT, DepartmentOperations.handleCreateDepartment);
operationHandler.registerHandler(DepartmentOperations.operationCodes.MODIFY_DEPARTMENT, DepartmentOperations.handleModifyDepartment); operationHandler.registerHandler(DepartmentOperations.operationCodes.MODIFY_DEPARTMENT, DepartmentOperations.handleModifyDepartment);
operationHandler.registerHandler(DepartmentOperations.operationCodes.DELETE_DEPARTMENT, DepartmentOperations.handleDeleteDepartment); operationHandler.registerHandler(DepartmentOperations.operationCodes.DELETE_DEPARTMENT, DepartmentOperations.handleDeleteDepartment);
operationHandler.registerHandler(DepartmentOperations.operationCodes.FIND_DEPARTMENT_BY_ID, DepartmentOperations.handleGetDepartmentById); operationHandler.registerHandler(DepartmentOperations.operationCodes.FIND_DEPARTMENT_BY_ID, DepartmentOperations.handleGetDepartmentById);
// Register common operations_base inherited from the base class
OperationBase.registerCommonOperations(operationHandler);
} }
} }
@@ -1,25 +1,26 @@
import { ParsedMessage } from '../message_handler'; import { ParsedMessage } from '../message_handler';
import { OperationBase } from '../operations_base/operation_base';
import { OperationHandler } from '../operations_base/operation_handler'; import { OperationHandler } from '../operations_base/operation_handler';
import os from 'node:os'; import os from 'node:os';
import {OperationPlugin} from "../operations_base/operation_plugin";
export class GeneralOperations extends OperationBase { export class GeneralOperations implements OperationPlugin {
public static readonly operationCodes = { public static readonly operationCodes = {
...OperationBase.operationCodes, // Inherit common operation codes (OK, ERR, END) OK: 'OK',
ERR: 'ERR',
END: 'END',
HEARTBEAT: 'HEARTBEAT', HEARTBEAT: 'HEARTBEAT',
ALIVE: 'ALIVE', ALIVE: 'ALIVE',
SET_PUBLIC_KEY: 'SET_PUBLIC_KEY', SET_PUBLIC_KEY: 'SET_PUBLIC_KEY',
SET_AES_KEY: 'SET_AES_KEY', // New operation for AES key SET_AES_KEY: 'SET_AES_KEY',
}; };
// Handle heartbeat operation // Handle heartbeat operation asynchronously
public static handleHeartbeat(): ParsedMessage { public static async handleHeartbeat(): Promise<ParsedMessage> {
const networkInterfaces = os.networkInterfaces(); const networkInterfaces = os.networkInterfaces();
let ipAddress = 'Unknown'; let ipAddress = 'Unknown';
for (const iface of Object.values(networkInterfaces)) { for (const iface of Object.values(networkInterfaces)) {
// @ts-ignore for (const address of iface!) {
for (const address of iface) {
if (address.family === 'IPv4' && !address.internal) { if (address.family === 'IPv4' && !address.internal) {
ipAddress = address.address; ipAddress = address.address;
break; break;
@@ -34,13 +35,13 @@ export class GeneralOperations extends OperationBase {
}; };
} }
// Handle public key exchange // Handle public key exchange asynchronously
public static handlePublicKey(parsedMessage: ParsedMessage): ParsedMessage { public static async handlePublicKey(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
const clientPublicKey = parsedMessage.metaInfo?.publicKey; const clientPublicKey = parsedMessage.metaInfo?.publicKey;
if (clientPublicKey) { if (clientPublicKey) {
return { return {
operationCode: GeneralOperations.operationCodes.SET_PUBLIC_KEY, operationCode: GeneralOperations.operationCodes.SET_PUBLIC_KEY,
metaInfo: { message: clientPublicKey }, metaInfo: { publicKey: clientPublicKey },
}; };
} else { } else {
return { return {
@@ -50,13 +51,14 @@ export class GeneralOperations extends OperationBase {
} }
} }
// Handle AES key exchange // Handle AES key exchange asynchronously
public static handleAESKey(parsedMessage: ParsedMessage): ParsedMessage { public static async handleAESKey(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
const aesKey = parsedMessage.metaInfo?.aesKey; const aesKey = parsedMessage.metaInfo?.aesKey;
if (aesKey) { const aesIv = parsedMessage.metaInfo?.aesIv;
if (aesKey && aesIv) {
return { return {
operationCode: GeneralOperations.operationCodes.SET_AES_KEY, operationCode: GeneralOperations.operationCodes.SET_AES_KEY,
metaInfo: { message: aesKey }, metaInfo: { aesKey, aesIv },
}; };
} else { } else {
return { return {
@@ -66,14 +68,22 @@ export class GeneralOperations extends OperationBase {
} }
} }
// Default async handler for OK operation
public static async handleOk(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
return parsedMessage; // Acknowledge with OK, returning as-is
}
// Default async handler for ERR operation
public static async handleErr(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
return parsedMessage; // Log the error and return
}
// Register general operations with the OperationHandler // Register general operations with the OperationHandler
public register(operationHandler: OperationHandler): void { public register(operationHandler: OperationHandler): void {
// Register specific handlers for the general operations
operationHandler.registerHandler(GeneralOperations.operationCodes.HEARTBEAT, GeneralOperations.handleHeartbeat); operationHandler.registerHandler(GeneralOperations.operationCodes.HEARTBEAT, GeneralOperations.handleHeartbeat);
operationHandler.registerHandler(GeneralOperations.operationCodes.SET_PUBLIC_KEY, GeneralOperations.handlePublicKey); operationHandler.registerHandler(GeneralOperations.operationCodes.SET_PUBLIC_KEY, GeneralOperations.handlePublicKey);
operationHandler.registerHandler(GeneralOperations.operationCodes.SET_AES_KEY, GeneralOperations.handleAESKey); // Register AES key handler operationHandler.registerHandler(GeneralOperations.operationCodes.SET_AES_KEY, GeneralOperations.handleAESKey);
operationHandler.registerHandler(GeneralOperations.operationCodes.OK, GeneralOperations.handleOk);
// Register common operations inherited from the base class operationHandler.registerHandler(GeneralOperations.operationCodes.ERR, GeneralOperations.handleErr);
OperationBase.registerCommonOperations(operationHandler);
} }
} }
@@ -1,90 +1,39 @@
import { ParsedMessage } from '../message_handler'; import { ParsedMessage } from '../message_handler';
import { keyDatabase } from '../../db_managers/db'; import { keyDatabase } from '../../db_managers/db';
import { OperationBase } from '../operations_base/operation_base';
import { OperationHandler } from '../operations_base/operation_handler'; import { OperationHandler } from '../operations_base/operation_handler';
import { OperationPlugin } from '../operations_base/operation_plugin';
import { operationCodes } from "../operation_codes";
export class KeyOperations extends OperationBase { export class KeyOperations implements OperationPlugin {
public static readonly operationCodes = { public static readonly operationCodes = {
...OperationBase.operationCodes,
GET_KEYS: 'GET_KEYS', GET_KEYS: 'GET_KEYS',
CREATE_KEY: 'CREATE_KEY', CREATE_KEY: 'CREATE_KEY',
DELETE_KEY: 'DELETE_KEY', DELETE_KEY: 'DELETE_KEY',
FIND_KEY_BY_USER_ID: 'FIND_KEY_BY_USER_ID', FIND_KEY_BY_USER_ID: 'FIND_KEY_BY_USER_ID',
}; };
// Get all keys public static async handleGetKeys(): Promise<ParsedMessage> {
public static handleGetKeys(): ParsedMessage {
const keys = keyDatabase.getAllKeys(); const keys = keyDatabase.getAllKeys();
console.log(keys); return { operationCode: operationCodes.OK, metaInfo: { keys } };
return {
operationCode: KeyOperations.operationCodes.OK,
metaInfo: { keys },
};
} }
// Create a key for a user public static async handleCreateKey(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
public static handleCreateKey(parsedMessage: ParsedMessage): ParsedMessage {
const { userId } = parsedMessage.metaInfo || {}; const { userId } = parsedMessage.metaInfo || {};
if (!userId) {
return {
operationCode: KeyOperations.operationCodes.ERR,
metaInfo: { message: 'User ID is required to create a key.' },
};
}
// Create the key for the user
const key = keyDatabase.createKey(userId); const key = keyDatabase.createKey(userId);
return { return { operationCode: operationCodes.OK, metaInfo: { key } };
operationCode: KeyOperations.operationCodes.OK,
metaInfo: { key },
};
} }
// Find a key by user ID public static async handleFindKeyByUserId(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
public static handleFindKeyByUserId(parsedMessage: ParsedMessage): ParsedMessage {
const { userId } = parsedMessage.metaInfo || {}; const { userId } = parsedMessage.metaInfo || {};
if (!userId) {
return {
operationCode: KeyOperations.operationCodes.ERR,
metaInfo: { message: 'User ID is required.' },
};
}
const key = keyDatabase.findByUserId(userId); const key = keyDatabase.findByUserId(userId);
return { return key
operationCode: key === null ? KeyOperations.operationCodes.ERR : KeyOperations.operationCodes.OK, ? { operationCode: operationCodes.OK, metaInfo: { key } }
metaInfo: key === null ? { message: 'Key not found.' } : { key }, : { operationCode: operationCodes.ERR, metaInfo: { message: 'Key not found.' } };
};
} }
// Delete a key by user ID
public static handleDeleteKey(parsedMessage: ParsedMessage): ParsedMessage {
const { userId } = parsedMessage.metaInfo || {};
if (!userId) {
return {
operationCode: KeyOperations.operationCodes.ERR,
metaInfo: { message: 'User ID is required for deletion.' },
};
}
const isSuccess = keyDatabase.deleteKeysByUserId(userId);
return {
operationCode: isSuccess ? KeyOperations.operationCodes.OK : KeyOperations.operationCodes.ERR,
metaInfo: { message: isSuccess ? 'Key deleted successfully.' : 'Failed to delete key.' },
};
}
// Register key operations with the OperationHandler
public register(operationHandler: OperationHandler): void { public register(operationHandler: OperationHandler): void {
operationHandler.registerHandler(KeyOperations.operationCodes.GET_KEYS, KeyOperations.handleGetKeys); operationHandler.registerHandler(KeyOperations.operationCodes.GET_KEYS, KeyOperations.handleGetKeys);
operationHandler.registerHandler(KeyOperations.operationCodes.CREATE_KEY, KeyOperations.handleCreateKey); operationHandler.registerHandler(KeyOperations.operationCodes.CREATE_KEY, KeyOperations.handleCreateKey);
operationHandler.registerHandler(KeyOperations.operationCodes.DELETE_KEY, KeyOperations.handleDeleteKey);
operationHandler.registerHandler(KeyOperations.operationCodes.FIND_KEY_BY_USER_ID, KeyOperations.handleFindKeyByUserId); operationHandler.registerHandler(KeyOperations.operationCodes.FIND_KEY_BY_USER_ID, KeyOperations.handleFindKeyByUserId);
// Register common operations inherited from OperationBase
OperationBase.registerCommonOperations(operationHandler);
} }
} }
@@ -1,11 +1,11 @@
import { ParsedMessage } from '../message_handler'; import { ParsedMessage } from '../message_handler';
import {userDatabase} from '../../db_managers/db'; import { userDatabase } from '../../db_managers/db';
import { OperationBase } from '../operations_base/operation_base';
import { OperationHandler } from '../operations_base/operation_handler'; import { OperationHandler } from '../operations_base/operation_handler';
import { OperationPlugin } from '../operations_base/operation_plugin';
import { operationCodes} from "../operation_codes";
export class UserOperations extends OperationBase { export class UserOperations implements OperationPlugin {
public static readonly operationCodes = { public static readonly operationCodes = {
...OperationBase.operationCodes,
GET_USERS: 'GET_USERS', GET_USERS: 'GET_USERS',
CREATE_USER: 'CREATE_USER', CREATE_USER: 'CREATE_USER',
MODIFY_USER: 'MODIFY_USER', MODIFY_USER: 'MODIFY_USER',
@@ -14,123 +14,30 @@ export class UserOperations extends OperationBase {
FIND_BY_EMAIL: 'FIND_BY_EMAIL', FIND_BY_EMAIL: 'FIND_BY_EMAIL',
}; };
// Get all users public static async handleGetUsers(): Promise<ParsedMessage> {
public static handleGetUsers(): ParsedMessage {
const users = userDatabase.getAllUsers(); const users = userDatabase.getAllUsers();
console.log(users); return { operationCode: operationCodes.OK, metaInfo: { users } };
return {
operationCode: UserOperations.operationCodes.OK,
metaInfo: { users },
};
} }
public static handleGetUserById(parsedMessage: ParsedMessage): ParsedMessage { public static async handleGetUserById(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
const { id } = parsedMessage.metaInfo || {}; const { id } = parsedMessage.metaInfo || {};
if(!id){
return {
operationCode: UserOperations.operationCodes.ERR,
metaInfo: { message: 'Id is required.' },
};
}
const user = userDatabase.findById(id); const user = userDatabase.findById(id);
return { return user
operationCode: user === null ? UserOperations.operationCodes.ERR : UserOperations.operationCodes.OK, ? { operationCode: operationCodes.OK, metaInfo: { user } }
metaInfo: user === null ? { message: 'User not found.' } : { message: user } : { operationCode: operationCodes.ERR, metaInfo: { message: 'User not found.' } };
}
} }
public static handleGetUserByEmail(parsedMessage: ParsedMessage) : ParsedMessage{ public static async handleModifyUser(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
const { email } = parsedMessage.metaInfo || {};
if(!email){
return {
operationCode: UserOperations.operationCodes.ERR,
metaInfo: { message: 'Email is required.' },
};
}
const user = userDatabase.findByEmail(email);
const response = {
operationCode: user === null ? UserOperations.operationCodes.ERR : UserOperations.operationCodes.OK,
metaInfo: user === null ? { message: 'User not found.' } : user
}
console.log(response);
return response;
}
// Modify an existing user
public static handleModifyUser(parsedMessage: ParsedMessage): ParsedMessage {
const { id, name, email, password, departmentId, app_type } = parsedMessage.metaInfo || {}; const { id, name, email, password, departmentId, app_type } = parsedMessage.metaInfo || {};
const success = userDatabase.modifyUser(id, name, email, password, departmentId, app_type);
if (!id || !name || !email || !password || !departmentId || !app_type) { return success
return { ? { operationCode: operationCodes.OK, metaInfo: { message: 'User modified successfully.' } }
operationCode: UserOperations.operationCodes.ERR, : { operationCode: operationCodes.ERR, metaInfo: { message: 'Failed to modify user.' } };
metaInfo: { message: 'Some information are missing.' },
};
} }
const user = userDatabase.findById(id);
if (!user) return {
operationCode: UserOperations.operationCodes.ERR,
metaInfo: { message: 'User not found in the system.' },
};
const listOfUsers = userDatabase.getAllUsers()
// Check if another user already exists with the same email or name (excluding the current user)
const existingUserByEmail = listOfUsers.find((existingUser) => existingUser.email === email && existingUser.id !== id);
const existingUserByName = listOfUsers.find((existingUser) => existingUser.name === name && existingUser.id !== id);
if (existingUserByEmail) {
return {
operationCode: UserOperations.operationCodes.ERR,
metaInfo: { message: `Email ${email} is already in use by another user.` },
};
}
if (existingUserByName) {
return {
operationCode: UserOperations.operationCodes.ERR,
metaInfo: { message: `Name ${name} is already in use by another user.` },
};
}
const isSuccess = userDatabase.modifyUser(id, name, email, password, departmentId, app_type);
return {
operationCode: isSuccess ? UserOperations.operationCodes.OK : UserOperations.operationCodes.ERR,
metaInfo: { message: isSuccess ? 'User modified successfully.' : 'Failed to modify user.' },
};
}
// Delete a user by ID
public static handleDeleteUser(parsedMessage: ParsedMessage): ParsedMessage {
const { id } = parsedMessage.metaInfo || {};
if (!id) {
return {
operationCode: UserOperations.operationCodes.ERR,
metaInfo: { message: 'User ID is required for deletion.' },
};
}
const isSuccess = userDatabase.deleteUser(id);
return {
operationCode: isSuccess ? UserOperations.operationCodes.OK : UserOperations.operationCodes.ERR,
metaInfo: { message: isSuccess ? 'User deleted successfully.' : 'Failed to delete user.' },
};
}
// Register user operations with the OperationHandler
public register(operationHandler: OperationHandler): void { public register(operationHandler: OperationHandler): void {
operationHandler.registerHandler(UserOperations.operationCodes.GET_USERS, UserOperations.handleGetUsers); operationHandler.registerHandler(UserOperations.operationCodes.GET_USERS, UserOperations.handleGetUsers);
operationHandler.registerHandler(UserOperations.operationCodes.MODIFY_USER, UserOperations.handleModifyUser); operationHandler.registerHandler(UserOperations.operationCodes.MODIFY_USER, UserOperations.handleModifyUser);
operationHandler.registerHandler(UserOperations.operationCodes.DELETE_USER, UserOperations.handleDeleteUser);
operationHandler.registerHandler(UserOperations.operationCodes.FIND_BY_ID, UserOperations.handleGetUserById); operationHandler.registerHandler(UserOperations.operationCodes.FIND_BY_ID, UserOperations.handleGetUserById);
operationHandler.registerHandler(UserOperations.operationCodes.FIND_BY_EMAIL, UserOperations.handleGetUserByEmail);
// Register common operations inherited from OperationBase
OperationBase.registerCommonOperations(operationHandler);
} }
} }
@@ -2,6 +2,7 @@ import { Socket } from 'net';
import { MessageHandler } from '../message_handler'; import { MessageHandler } from '../message_handler';
import { SocketCommunicatorBase } from './socket_communicator_base'; import { SocketCommunicatorBase } from './socket_communicator_base';
import { OperationHandler } from '../operations_base/operation_handler'; import { OperationHandler } from '../operations_base/operation_handler';
import {operationCodes} from "../operation_codes";
export class TcpServerCommunicator extends SocketCommunicatorBase { export class TcpServerCommunicator extends SocketCommunicatorBase {
private readonly socket: Socket; private readonly socket: Socket;
@@ -17,7 +18,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
throw new Error('RSA key pair is not available. Please generate RSA key pair.'); throw new Error('RSA key pair is not available. Please generate RSA key pair.');
} }
await this.sendMessage('SET_PUBLIC_KEY', { publicKey: this.publicKey }); await this.sendMessage(operationCodes.SET_PUBLIC_KEY, { publicKey: this.publicKey });
} }
async sendAesKey(): Promise<void> { async sendAesKey(): Promise<void> {
@@ -28,7 +29,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
const aesKeyBase64 = this.aesKey.toString('base64'); const aesKeyBase64 = this.aesKey.toString('base64');
const aesIvBase64 = this.aesIv.toString('base64'); const aesIvBase64 = this.aesIv.toString('base64');
await this.sendMessage('SET_AES_KEY', { aesKey: aesKeyBase64, aesIv: aesIvBase64 }); await this.sendMessage(operationCodes.SET_AES_KEY, { aesKey: aesKeyBase64, aesIv: aesIvBase64 });
} }
async handleIncomingMessage(incomingMessage: string): Promise<void> { async handleIncomingMessage(incomingMessage: string): Promise<void> {
@@ -45,10 +46,10 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
let outgoingMessage: string; let outgoingMessage: string;
switch (operationCode) { switch (operationCode) {
case 'SET_PUBLIC_KEY': case operationCodes.SET_PUBLIC_KEY:
outgoingMessage = Buffer.from(message, 'utf-8').toString('base64'); outgoingMessage = Buffer.from(message, 'utf-8').toString('base64');
break; break;
case 'SET_AES_KEY': case operationCodes.SET_AES_KEY:
outgoingMessage = this.encryptWithRsa(message); outgoingMessage = this.encryptWithRsa(message);
break; break;
default: default:
@@ -22,16 +22,19 @@ export class TcpClientCommunicator extends SocketCommunicatorBase {
} }
setServerPublicKey(publicKey: string): void { setServerPublicKey(publicKey: string): void {
console.log('\n\nSetting server public key\n\n');
this.publicKey = publicKey; this.publicKey = publicKey;
} }
setAesKey(aesKey: string, aesIv: string): void { setAesKey(aesKey: string, aesIv: string): void {
console.log('\n\nSetting AES key\n\n');
this.aesKey = Buffer.from(aesKey, 'base64'); this.aesKey = Buffer.from(aesKey, 'base64');
this.aesIv = Buffer.from(aesIv, 'base64'); this.aesIv = Buffer.from(aesIv, 'base64');
} }
async handleIncomingMessage(incomingMessage: string): Promise<void> { async handleIncomingMessage(incomingMessage: string): Promise<void> {
let messageToProcess; let messageToProcess;
if (this.aesKey && this.aesIv) { if (this.aesKey && this.aesIv) {
messageToProcess = this.decryptWithAes(incomingMessage); messageToProcess = this.decryptWithAes(incomingMessage);
} else if (this.publicKey) { } else if (this.publicKey) {
@@ -2,6 +2,7 @@ import { Socket } from 'net';
import { MessageHandler } from '../message_handler'; import { MessageHandler } from '../message_handler';
import { SocketCommunicatorBase } from './socket_communicator_base'; import { SocketCommunicatorBase } from './socket_communicator_base';
import { OperationHandler } from '../operations_base/operation_handler'; import { OperationHandler } from '../operations_base/operation_handler';
import {operationCodes} from "../operation_codes";
export class TcpServerCommunicator extends SocketCommunicatorBase { export class TcpServerCommunicator extends SocketCommunicatorBase {
private readonly socket: Socket; private readonly socket: Socket;
@@ -17,7 +18,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
throw new Error('RSA key pair is not available. Please generate RSA key pair.'); throw new Error('RSA key pair is not available. Please generate RSA key pair.');
} }
await this.sendMessage('SET_PUBLIC_KEY', { publicKey: this.publicKey }); await this.sendMessage(operationCodes.SET_PUBLIC_KEY, { publicKey: this.publicKey });
} }
async sendAesKey(): Promise<void> { async sendAesKey(): Promise<void> {
@@ -28,7 +29,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
const aesKeyBase64 = this.aesKey.toString('base64'); const aesKeyBase64 = this.aesKey.toString('base64');
const aesIvBase64 = this.aesIv.toString('base64'); const aesIvBase64 = this.aesIv.toString('base64');
await this.sendMessage('SET_AES_KEY', { aesKey: aesKeyBase64, aesIv: aesIvBase64 }); await this.sendMessage(operationCodes.SET_AES_KEY, { aesKey: aesKeyBase64, aesIv: aesIvBase64 });
} }
async handleIncomingMessage(incomingMessage: string): Promise<void> { async handleIncomingMessage(incomingMessage: string): Promise<void> {
@@ -45,10 +46,10 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
let outgoingMessage: string; let outgoingMessage: string;
switch (operationCode) { switch (operationCode) {
case 'SET_PUBLIC_KEY': case operationCodes.SET_PUBLIC_KEY:
outgoingMessage = Buffer.from(message, 'utf-8').toString('base64'); outgoingMessage = Buffer.from(message, 'utf-8').toString('base64');
break; break;
case 'SET_AES_KEY': case operationCodes.SET_AES_KEY:
outgoingMessage = this.encryptWithRsa(message); outgoingMessage = this.encryptWithRsa(message);
break; break;
default: default: