BACKEND DONE FOR ALL APPS
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
import path from "path";
|
||||
|
||||
import { UserDatabase } from "./user_database";
|
||||
import { DepartmentDatabase } from "./department_database";
|
||||
import { KeyDatabase } from "./key_database";
|
||||
|
||||
const departmentDatabase = new DepartmentDatabase();
|
||||
const userDatabase = new UserDatabase();
|
||||
const keyDatabase = new KeyDatabase();
|
||||
|
||||
export {
|
||||
keyDatabase,
|
||||
userDatabase,
|
||||
departmentDatabase
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import SQLiteDatabase from './sql_lite_database'; // Singleton instance of SQLite DB
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export interface Department {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class DepartmentDatabase {
|
||||
private db: any;
|
||||
|
||||
constructor() {
|
||||
this.db = SQLiteDatabase.getInstance();
|
||||
this.createTableIfNotExists();
|
||||
}
|
||||
|
||||
// Create the table if it doesn't exist
|
||||
private createTableIfNotExists() {
|
||||
const createTableQuery = `
|
||||
CREATE TABLE IF NOT EXISTS departments (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
`;
|
||||
this.db.exec(createTableQuery);
|
||||
|
||||
const ceoDepartment = this.db.prepare('SELECT COUNT(*) as count FROM departments WHERE name = ?').get('CEO').count;
|
||||
const defaultDepartment = this.db.prepare('SELECT COUNT(*) as count FROM departments WHERE name = ?').get('Worker').count;
|
||||
|
||||
// Create CEO department if it doesn't exist
|
||||
if (ceoDepartment === 0) {
|
||||
this.createDepartment('CEO');
|
||||
console.log('Created CEO department.');
|
||||
} else {
|
||||
console.log('CEO department already exists.');
|
||||
}
|
||||
|
||||
if (defaultDepartment === 0) {
|
||||
this.createDepartment('Worker');
|
||||
console.log('Created Worker department.');
|
||||
} else {
|
||||
console.log('Worker department already exists.');
|
||||
}
|
||||
|
||||
console.log('Departments table checked/created.');
|
||||
}
|
||||
|
||||
|
||||
// Find a department by name
|
||||
public findByName(name: string): Department | null {
|
||||
const stmt = this.db.prepare('SELECT * FROM departments WHERE name = ?');
|
||||
const department = stmt.get(name);
|
||||
return department || null;
|
||||
}
|
||||
|
||||
// Create a new department
|
||||
public createDepartment(name: string): Department {
|
||||
const department: Department = {
|
||||
id: uuidv4(),
|
||||
name,
|
||||
};
|
||||
|
||||
const stmt = this.db.prepare('INSERT INTO departments (id, name) VALUES (?, ?)');
|
||||
stmt.run(department.id, department.name);
|
||||
return department;
|
||||
}
|
||||
|
||||
// Find a department by ID
|
||||
public findById(id: string): Department | null {
|
||||
const stmt = this.db.prepare('SELECT * FROM departments WHERE id = ?');
|
||||
const department = stmt.get(id);
|
||||
return department || null;
|
||||
}
|
||||
|
||||
// Modify an existing department by ID
|
||||
public modifyDepartment(departmentId: string, newName: string): boolean {
|
||||
const stmt = this.db.prepare('UPDATE departments SET name = ? WHERE id = ?');
|
||||
const result = stmt.run(newName, departmentId);
|
||||
return result.changes > 0;
|
||||
}
|
||||
|
||||
// Delete a department by ID
|
||||
public deleteDepartment(departmentId: string): boolean {
|
||||
const stmt = this.db.prepare('DELETE FROM departments WHERE id = ?');
|
||||
const result = stmt.run(departmentId);
|
||||
return result.changes > 0;
|
||||
}
|
||||
|
||||
// Get all departments
|
||||
public getAllDepartments(): Department[] {
|
||||
const stmt = this.db.prepare('SELECT * FROM departments');
|
||||
return stmt.all();
|
||||
}
|
||||
|
||||
public cleanTable(): { success: boolean; message: string } {
|
||||
try {
|
||||
// Fetch the CEO department ID
|
||||
const ceoDepartmentRow = this.db.prepare(`SELECT id FROM departments WHERE LOWER(name) = 'ceo'`).get();
|
||||
if (!ceoDepartmentRow) {
|
||||
console.error('CEO department not found in the database.');
|
||||
return { success: false, message: 'Failed to find CEO department.' };
|
||||
}
|
||||
|
||||
const ceoDepartmentId = ceoDepartmentRow.id;
|
||||
|
||||
// Delete all departments except the CEO department
|
||||
const deleteDepartmentsStmt = this.db.prepare(`DELETE FROM departments WHERE id != ?`);
|
||||
deleteDepartmentsStmt.run(ceoDepartmentId);
|
||||
|
||||
return { success: true, message: 'All non-CEO departments have been deleted.' };
|
||||
} catch (error) {
|
||||
console.error('Error while cleaning the departments table:', error);
|
||||
return { success: false, message: 'Failed to clean departments table.' };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
import { randomBytes } from 'crypto';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import SQLiteDatabase from './sql_lite_database';
|
||||
import { userDatabase, departmentDatabase } from './db';
|
||||
|
||||
export interface UserKey {
|
||||
id: string; // Unique ID for the key entry
|
||||
userId: string; // ID of the user this key belongs to
|
||||
key: string; // AES key in Base64 format
|
||||
iv: string; // Initialization Vector (IV) in Base64 format
|
||||
}
|
||||
|
||||
export class KeyDatabase {
|
||||
private db: any;
|
||||
|
||||
constructor() {
|
||||
// Get the singleton instance of the database
|
||||
this.db = SQLiteDatabase.getInstance();
|
||||
|
||||
// Create the keys table if it doesn't exist
|
||||
this.createTableIfNotExists();
|
||||
}
|
||||
|
||||
// Create the table if it doesn't exist
|
||||
private createTableIfNotExists() {
|
||||
const createTableQuery = `
|
||||
CREATE TABLE IF NOT EXISTS user_keys (
|
||||
id TEXT PRIMARY KEY,
|
||||
userId TEXT UNIQUE NOT NULL, -- Ensure one key per user
|
||||
key TEXT NOT NULL,
|
||||
iv TEXT NOT NULL,
|
||||
FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
`;
|
||||
this.db.exec(createTableQuery);
|
||||
|
||||
// Fetch the CEO department and create keys for all users in the CEO department
|
||||
this.createKeysForCeoUsers();
|
||||
}
|
||||
|
||||
// Generate AES key and IV
|
||||
private generateAESKeyAndIV(): { key: string, iv: string } {
|
||||
const key = randomBytes(32).toString('base64'); // AES-256 key (32 bytes)
|
||||
const iv = randomBytes(16).toString('base64'); // IV for AES (16 bytes)
|
||||
return { key, iv };
|
||||
}
|
||||
|
||||
// Create a key for a specific user
|
||||
private createKeyForUser(userId: string): UserKey {
|
||||
const { key, iv } = this.generateAESKeyAndIV();
|
||||
|
||||
// Delete any existing key for the user
|
||||
this.deleteKeysByUserId(userId);
|
||||
|
||||
// Insert the new key
|
||||
const newKey: UserKey = {
|
||||
id: uuidv4(),
|
||||
userId: userId,
|
||||
key: key,
|
||||
iv: iv
|
||||
};
|
||||
|
||||
const stmt = this.db.prepare('INSERT INTO user_keys (id, userId, key, iv) VALUES (?, ?, ?, ?)');
|
||||
stmt.run(newKey.id, newKey.userId, newKey.key, newKey.iv);
|
||||
|
||||
return newKey;
|
||||
}
|
||||
|
||||
// Create keys for all users in the CEO department
|
||||
private createKeysForCeoUsers() {
|
||||
const departments = departmentDatabase.getAllDepartments();
|
||||
|
||||
// Find the CEO department by name
|
||||
const ceoDepartment = departments.find(dept => dept.name.toLowerCase() === 'ceo');
|
||||
|
||||
if (!ceoDepartment) {
|
||||
console.error('CEO department not found in the database.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch all users in the CEO department
|
||||
const ceoUsers = userDatabase.findByDepartmentId(ceoDepartment.id);
|
||||
|
||||
// Create keys for each user in the CEO department
|
||||
ceoUsers.forEach(user => {
|
||||
console.log(`Creating key for user ${user.name} (ID: ${user.id})`);
|
||||
this.createKeyForUser(user.id);
|
||||
});
|
||||
|
||||
console.log('Keys created for all users in the CEO department.');
|
||||
}
|
||||
|
||||
public createKey(userId: string): UserKey {
|
||||
const { key, iv } = this.generateAESKeyAndIV();
|
||||
|
||||
// Check if a key for this user already exists
|
||||
const existingKey = this.findByUserId(userId);
|
||||
if (existingKey) {
|
||||
// Delete the existing key if present
|
||||
this.deleteKeysByUserId(userId);
|
||||
}
|
||||
|
||||
// Create the new key
|
||||
const newKey: UserKey = {
|
||||
id: uuidv4(),
|
||||
userId: userId,
|
||||
key: key,
|
||||
iv: iv
|
||||
};
|
||||
|
||||
const stmt = this.db.prepare('INSERT INTO user_keys (id, userId, key, iv) VALUES (?, ?, ?, ?)');
|
||||
stmt.run(newKey.id, newKey.userId, newKey.key, newKey.iv);
|
||||
|
||||
return newKey;
|
||||
}
|
||||
|
||||
// Find a key by userId (returns null if no key is found)
|
||||
public findByUserId(userId: string): UserKey | null {
|
||||
const stmt = this.db.prepare('SELECT * FROM user_keys WHERE userId = ?');
|
||||
const row = stmt.get(userId);
|
||||
return row || null;
|
||||
}
|
||||
|
||||
// Delete a key by userId (only one key per user is allowed)
|
||||
public deleteKeysByUserId(userId: string): boolean {
|
||||
const stmt = this.db.prepare('DELETE FROM user_keys WHERE userId = ?');
|
||||
const result = stmt.run(userId);
|
||||
return result.changes > 0;
|
||||
}
|
||||
|
||||
// Get all keys (for potential admin purposes)
|
||||
public getAllKeys(): UserKey[] {
|
||||
const stmt = this.db.prepare('SELECT * FROM user_keys');
|
||||
return stmt.all();
|
||||
}
|
||||
|
||||
// Clean the table but keep CEO keys
|
||||
public cleanTable(): { success: boolean; message: string } {
|
||||
try {
|
||||
const departments = departmentDatabase.getAllDepartments();
|
||||
const ceoDepartment = departments.find(dept => dept.name.toLowerCase() === 'ceo');
|
||||
|
||||
if (!ceoDepartment) {
|
||||
console.error('CEO department not found in the database.');
|
||||
return { success: false, message: 'Failed to find CEO department.' };
|
||||
}
|
||||
|
||||
// Fetch all users in the CEO department
|
||||
const ceoUsers = userDatabase.findByDepartmentId(ceoDepartment.id);
|
||||
const ceoUserIds = ceoUsers.map(user => user.id);
|
||||
|
||||
// Delete all keys except for the CEO users
|
||||
const stmt = this.db.prepare(`
|
||||
DELETE FROM user_keys WHERE userId NOT IN (${ceoUserIds.map(() => '?').join(', ')})
|
||||
`);
|
||||
stmt.run(...ceoUserIds);
|
||||
|
||||
return { success: true, message: 'All non-CEO keys have been deleted.' };
|
||||
} catch (error) {
|
||||
console.error('Error while cleaning the keys table:', error);
|
||||
return { success: false, message: 'Failed to clean keys table.' };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import path from "path";
|
||||
|
||||
class SQLiteDatabase {
|
||||
// Static variable to hold the single instance
|
||||
private static instance: Database.Database | null = null;
|
||||
private static readonly dbFilePath = path.join(__dirname, '..', '..', 'db', 'database.db');
|
||||
|
||||
// Private constructor prevents direct instantiation
|
||||
private constructor() {}
|
||||
|
||||
// Static method to get the instance of the database
|
||||
public static getInstance(): Database.Database {
|
||||
if (!SQLiteDatabase.instance) {
|
||||
// If no instance exists, create it
|
||||
SQLiteDatabase.instance = new Database(SQLiteDatabase.dbFilePath, {
|
||||
verbose: console.log, // Log queries (optional)
|
||||
});
|
||||
console.log('Database initialized');
|
||||
}
|
||||
|
||||
// Return the existing instance
|
||||
return SQLiteDatabase.instance;
|
||||
}
|
||||
|
||||
// Optional method to close the database connection
|
||||
public static closeDatabase(): void {
|
||||
if (SQLiteDatabase.instance) {
|
||||
SQLiteDatabase.instance.close();
|
||||
SQLiteDatabase.instance = null;
|
||||
console.log('Database connection closed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SQLiteDatabase;
|
||||
@@ -0,0 +1,235 @@
|
||||
import { pbkdf2Sync, randomBytes } from 'crypto';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import SQLiteDatabase from './sql_lite_database';
|
||||
import { departmentDatabase} from "./db";
|
||||
import * as dotenv from 'dotenv';
|
||||
|
||||
dotenv.config(); // Load environment variables from the .env file
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
hashedPassword: string,
|
||||
salt: string,
|
||||
departmentId: string;
|
||||
app_type: string;
|
||||
}
|
||||
|
||||
export class UserDatabase {
|
||||
private db: any;
|
||||
|
||||
constructor() {
|
||||
// Get the singleton instance of the database
|
||||
this.db = SQLiteDatabase.getInstance();
|
||||
|
||||
// Check if the table exists and create it if not
|
||||
this.createTableIfNotExists();
|
||||
}
|
||||
|
||||
// Create the table if it doesn't exist
|
||||
private createTableIfNotExists() {
|
||||
const createTableQuery = `
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
salt TEXT NOT NULL,
|
||||
hashedPassword TEXT NOT NULL,
|
||||
departmentId TEXT NOT NULL,
|
||||
app_type TEXT NOT NULL
|
||||
);
|
||||
`;
|
||||
this.db.exec(createTableQuery);
|
||||
|
||||
// Check if any users already exist
|
||||
const userCount = this.db.prepare('SELECT COUNT(*) as count FROM users').get().count;
|
||||
|
||||
if (userCount === 0) {
|
||||
console.log('No users found, creating default CEO user...');
|
||||
|
||||
// Fetch departments
|
||||
const departments = departmentDatabase.getAllDepartments();
|
||||
|
||||
// Find department ID for CEO
|
||||
const ceoDepartment = departments.find(dept => dept.name.toLowerCase() === 'ceo');
|
||||
|
||||
if (!ceoDepartment) {
|
||||
console.error('CEO department not found in the database.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get CEO credentials from the .env file
|
||||
const ceoEmail = process.env.CEO_EMAIL || 'ceo@yourfirm.com';
|
||||
const ceoPassword = process.env.CEO_PASSWORD || 'Password123!';
|
||||
|
||||
// Create the CEO user with the credentials from .env
|
||||
this.createUser('CEO', ceoEmail, ceoPassword, ceoDepartment.id, 'ceo');
|
||||
|
||||
console.log('Default CEO user created.');
|
||||
} else {
|
||||
console.log('Users already exist, skipping creation of default CEO user.');
|
||||
}
|
||||
}
|
||||
|
||||
// Find a user by email
|
||||
public findByEmail(email: string): User | null {
|
||||
const stmt = this.db.prepare('SELECT * FROM users WHERE email = ?');
|
||||
const row = stmt.get(email);
|
||||
return row || null;
|
||||
}
|
||||
|
||||
// Find a user by ID
|
||||
public findById(id: string): User | null {
|
||||
const stmt = this.db.prepare('SELECT * FROM users WHERE id = ?');
|
||||
const row = stmt.get(id);
|
||||
return row || null;
|
||||
}
|
||||
|
||||
// Create a new user
|
||||
public createUser(name: string, email: string, password: string, departmentId: string, app_type: string): User {
|
||||
const salt = randomBytes(16).toString('hex');
|
||||
const hashedPassword = this.hashPassword(password, salt);
|
||||
|
||||
const user: User = {
|
||||
id: uuidv4(),
|
||||
name,
|
||||
email,
|
||||
salt,
|
||||
hashedPassword,
|
||||
departmentId: departmentId,
|
||||
app_type: app_type
|
||||
};
|
||||
|
||||
const stmt = this.db.prepare('INSERT INTO users (id, name, email, salt, hashedPassword, departmentId, app_type) VALUES (?, ?, ?, ?, ?, ?, ?)');
|
||||
stmt.run(user.id, user.name, user.email, user.salt, user.hashedPassword, user.departmentId, user.app_type);
|
||||
return user;
|
||||
}
|
||||
|
||||
// Modify an existing user
|
||||
public modifyUser(id: string, name: string, email: string, password: string, departmentId: string, app_type: string): boolean {
|
||||
// Find the user by ID
|
||||
const user = this.findById(id);
|
||||
if (!user) return false;
|
||||
|
||||
// Hash the new password using the existing salt
|
||||
const hashedPassword = this.hashPassword(password, user.salt);
|
||||
|
||||
// Prepare the updated user data
|
||||
const updatedUser: User = {
|
||||
...user,
|
||||
name: name,
|
||||
email: email,
|
||||
salt: user.salt, // Use the same salt
|
||||
hashedPassword: hashedPassword, // Use the newly hashed password
|
||||
departmentId: departmentId,
|
||||
app_type: app_type,
|
||||
};
|
||||
|
||||
// Prepare and execute the SQL query to update the user
|
||||
const stmt = this.db.prepare(`
|
||||
UPDATE users
|
||||
SET name = ?, email = ?, salt = ?, hashedPassword = ?, departmentId = ?, app_type = ?
|
||||
WHERE id = ?
|
||||
`);
|
||||
|
||||
stmt.run(updatedUser.name, updatedUser.email, updatedUser.salt, updatedUser.hashedPassword, updatedUser.departmentId, updatedUser.app_type, updatedUser.id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Delete a user by ID
|
||||
public deleteUser(id: string): boolean {
|
||||
const stmt = this.db.prepare('DELETE FROM users WHERE id = ?');
|
||||
const result = stmt.run(id);
|
||||
return result.changes > 0;
|
||||
}
|
||||
|
||||
public findByDepartmentId(departmentId: string): User[] {
|
||||
const stmt = this.db.prepare('SELECT * FROM users WHERE departmentId = ?');
|
||||
return stmt.all(departmentId);
|
||||
}
|
||||
|
||||
// Get all users
|
||||
public getAllUsers(): User[] {
|
||||
const stmt = this.db.prepare('SELECT * FROM users');
|
||||
return stmt.all();
|
||||
}
|
||||
|
||||
// Verify email, password, and app_type for authentication
|
||||
public verifyCredentials(email: string, password: string, app_type: string): { success: boolean; message: string } {
|
||||
const user = this.findByEmail(email);
|
||||
|
||||
if (!user) {
|
||||
return { success: false, message: 'User not found.' };
|
||||
}
|
||||
|
||||
// Check if app_type matches
|
||||
if (user.app_type !== app_type) {
|
||||
return { success: false, message: `Access denied for app type: ${app_type}.` };
|
||||
}
|
||||
|
||||
// Check if password matches
|
||||
const hashedPassword = this.hashPassword(password, user.salt);
|
||||
if (hashedPassword === user.hashedPassword) {
|
||||
return { success: true, message: 'Authentication successful.' };
|
||||
} else {
|
||||
return { success: false, message: 'Incorrect password.' };
|
||||
}
|
||||
}
|
||||
|
||||
// Reset password by user email and new password
|
||||
public resetPassword(email: string, newPassword: string, app_type: string): { success: boolean; message: string } {
|
||||
const user = this.findByEmail(email);
|
||||
if (!user) {
|
||||
return { success: false, message: 'User not found.' };
|
||||
}
|
||||
|
||||
if(user.app_type !== app_type){
|
||||
return { success: false, message: 'Invalid app type for operation,' };
|
||||
}
|
||||
|
||||
// Generate a new salt for the new password
|
||||
const newSalt = randomBytes(16).toString('hex');
|
||||
const newHashedPassword = this.hashPassword(newPassword, newSalt);
|
||||
|
||||
const stmt = this.db.prepare('UPDATE users SET salt = ?, hashedPassword = ? WHERE email = ?');
|
||||
const result = stmt.run(newSalt, newHashedPassword, email);
|
||||
|
||||
if (result.changes > 0) {
|
||||
return { success: true, message: 'Password reset successfully.' };
|
||||
} else {
|
||||
return { success: false, message: 'Failed to reset password.' };
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to hash the password with the salt
|
||||
private hashPassword(password: string, salt: string): string {
|
||||
return pbkdf2Sync(password, salt, 1000, 64, 'sha256').toString('hex');
|
||||
}
|
||||
|
||||
public cleanTable(): { success: boolean; message: string } {
|
||||
try {
|
||||
// Fetch departments
|
||||
const departments = departmentDatabase.getAllDepartments();
|
||||
|
||||
const ceoDepartment = departments.find(dept => dept.name.toLowerCase() === 'ceo');
|
||||
|
||||
if (!ceoDepartment) {
|
||||
console.error('Admin or CEO department not found in the database.');
|
||||
return { success: false, message: 'Failed to find Admin or CEO department.' };
|
||||
}
|
||||
|
||||
// Delete users from the table except for those in the Admin and CEO departments
|
||||
const stmt = this.db.prepare(`
|
||||
DELETE FROM users WHERE departmentId NOT IN (?, ?)
|
||||
`);
|
||||
stmt.run(ceoDepartment.id);
|
||||
|
||||
return { success: true, message: 'All users except Admin and CEO have been deleted.' };
|
||||
} catch (error) {
|
||||
console.error('Error while cleaning the users table:', error);
|
||||
return { success: false, message: 'Failed to clean users table.' };
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user