Source

password.ts

import {randomBytes} from "crypto";
import {readJSON, writeJSON} from "./jsonWorker.js";
import {hash, verify} from "argon2";

import type {SecureObject, SecureObjects} from "../types/password.js";

import {dirname} from "path";
import {fileURLToPath} from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));

/**
 * @name db
 * @description This constant is the path to the database
 * @example const db = __dirname + '/../database/passwords.json';
 * @default __dirname + '/../database/passwords.json'
 * @constant
 */
const db: string = __dirname + "/../database/passwords.json";

/**
 * @name makeSalt
 * @description This function generates a random salt
 * @returns The salt
 * @example makeSalt();
 */
function makeSalt(): string {
    return randomBytes(64).toString("hex");
}

/**
 * @name buildSecureObject
 * @description This function builds an object with the path, the hash and the salt
 * @async
 * @param file The path to the file
 * @param password The password to hash
 * @returns The object with the path, the hash and the salt
 * @example buildSecureObject('public/OSI-password.png', 'password');
 */
async function buildSecureObject(
    file: string,
    password: string
): Promise<SecureObject> {
    const salt = makeSalt();
    return {
        path: file,
        hash: await hash(password + salt),
        salt: salt
    };
}

/**
 * @name newSecureFile
 * @description This function adds a new secure file to the database
 * @async
 * @param  file The path to the file
 * @param password The password to hash
 * @param [dbp=db] The path to the database
 * @throws {Error} If the file already exists
 * @example newSecureFile('public/OSI-password.png', 'password', false, 'database/passwords.json');
 */
export async function newSecureFile(
    file: string,
    password: string,
    dbp: string = db
): Promise<void> {
    const {data} = await readJSON<SecureObjects>(dbp);
    const secureObject = await buildSecureObject(file, password);
    if (data.find((e: SecureObject) => e.path === file)) {
        throw new Error("File already exists");
    } else {
        data.push(secureObject);
        await writeJSON(dbp, data);
    }
}

/**
 * @name deleteSecureFile
 * @description This function deletes a secure file from the database
 * @async
 * @param file The path to the file
 * @param [dbp=db] The path to the database
 * @throws {Error} If the file doesn't exist
 * @example deleteSecureFile('public/OSI-password.png', 'database/passwords.json');
 */
export async function deleteSecureFile(
    file: string,
    dbp: string = db
): Promise<void> {
    const {data} = await readJSON<SecureObjects>(dbp);
    const secureObject = data.find((e: SecureObject) => e.path === file);
    if (!secureObject) {
        throw new Error("File does not exist");
    } else {
        data.splice(data.indexOf(secureObject), 1);
        await writeJSON(dbp, data);
    }
}

/**
 * @name changePassword
 * @description This function changes the password on a secure file
 * @async
 * @param file The path to the file
 * @param password The new password
 * @param [dbp=db] The path to the database
 * @throws {Error} If the file doesn't exist
 * @example changePassword('public/OSI-password.png', 'password', 'database/passwords.json');
 */
export async function changePassword(
    file: string,
    password: string,
    dbp: string = db
): Promise<void> {
    const {data} = await readJSON<SecureObjects>(dbp);
    const secureObject = data.find((e: SecureObject) => e.path === file);
    if (!secureObject) {
        throw new Error("File does not exist");
    } else {
        await deleteSecureFile(file, dbp);
        await newSecureFile(file, password, dbp);
    }
}

/**
 * @name authenticate
 * @description This function checks if the password is correct
 * @async
 * @param file The path to the file
 * @param password The password to check
 * @param [dbp=db] The path to the database
 * @returns True if the password is correct, false otherwise
 * @example auth('public/OSI-password.png', 'password', 'database/passwords.json');
 */
export async function authenticate(
    file: string,
    password: string,
    dbp: string = db
): Promise<boolean> {
    const {data} = await readJSON<SecureObjects>(dbp);
    const secureObject = data.find((e: SecureObject) => e.path === file);
    if (!secureObject) return false;
    return verify(secureObject.hash, password + secureObject.salt);
}