diff --git a/.env.example b/.env.example index 7ec86a4..f93f836 100644 --- a/.env.example +++ b/.env.example @@ -18,4 +18,5 @@ VITE_URL_MANUAL_USUARIO= # URL del manual de instrucciones para los usua VITE_URL_MANUAL_ADMIN= # URL del manual de instrucciones para los administradores VITE_CANT_LIM_DIAGNOSTICOS= # Cantidad de diagnósticos a partir del cual mostrar la advertencia de espacio VITE_RECAPTCHA_SITE_KEY= # Clave de reCAPTCHA para el sitio -VITE_URL_CONDICIONES= # URL al documento que contiene los términos y condiciones de uso de la aplicación \ No newline at end of file +VITE_URL_CONDICIONES= # URL al documento que contiene los términos y condiciones de uso de la aplicación +VITE_AES_KEY= # Clave AES utilizada para encriptar las credenciales \ No newline at end of file diff --git a/README.md b/README.md index ae10312..8c9065e 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ VITE_URL_MANUAL_USUARIO= # URL del manual de instrucciones para los VITE_URL_MANUAL_ADMIN= # URL del manual de instrucciones para los administradores VITE_CANT_LIM_DIAGNOSTICOS= # Cantidad de diagnósticos a partir del cual mostrar la advertencia de espacio VITE_URL_CONDICIONES= # URL al documento que contiene los términos y condiciones de uso de la aplicación +VITE_AES_KEY= # Clave AES utilizada para encriptar las credenciales ``` ## Ejecución del frontend sin backend diff --git a/constants.js b/constants.js index 0303a17..3370b45 100644 --- a/constants.js +++ b/constants.js @@ -34,6 +34,6 @@ export const CAMPOS_BIN = [ "derrame", "tepPrevio", "edema", "disautonomicos", "inmovilidad", "viajeProlongado", "cirugiaReciente", "otraEnfermedad", "soplos" ]; - export const CAMPOS_TXT = ["edad", "presionSis", "presionDias", "frecRes", - "frecCard", "so2", "plaquetas", "hemoglobina", "wbc"]; \ No newline at end of file + "frecCard", "so2", "plaquetas", "hemoglobina", "wbc"]; +export const AES_KEY = import.meta.env.VITE_CLAVE_AES; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c544406..a787407 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@mui/x-date-pickers": "^8.5.3", "babel-jest": "^29.7.0", "babel-preset-vite": "^1.1.3", + "crypto-js": "^4.2.0", "dayjs": "^1.11.13", "firebase": "^11.9.1", "i18next": "^25.5.2", @@ -5710,6 +5711,12 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, "node_modules/cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", diff --git a/package.json b/package.json index d61adac..1d33720 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@mui/x-date-pickers": "^8.5.3", "babel-jest": "^29.7.0", "babel-preset-vite": "^1.1.3", + "crypto-js": "^4.2.0", "dayjs": "^1.11.13", "firebase": "^11.9.1", "i18next": "^25.5.2", diff --git a/src/components/menu/MenuUsuario.jsx b/src/components/menu/MenuUsuario.jsx index ff6d7dc..25ab9d9 100644 --- a/src/components/menu/MenuUsuario.jsx +++ b/src/components/menu/MenuUsuario.jsx @@ -16,6 +16,8 @@ import CloseIcon from "@mui/icons-material/Close"; import dayjs from "dayjs"; import { Timestamp } from "firebase/firestore"; import { useTranslation } from "react-i18next"; +import { AES, enc } from "crypto-js"; +import { AES_KEY } from "../../../constants"; /** * Menú principal para los usuarios. Muestra la cantidad de pacientes y diagnósticos registrados este mes y @@ -73,7 +75,8 @@ export default function MenuUsuario() { useEffect(() => { const token = sessionStorage.getItem("session-tokens"); if (token != null) { - drive.setToken(JSON.parse(token).accessToken); + const tokens = JSON.parse(AES.decrypt(token, AES_KEY).toString(enc.Utf8)); + drive.setToken(tokens.accessToken); } else if (auth.tokenDrive != null) { drive.setToken(auth.tokenDrive); } diff --git a/src/contexts/AuthContext.jsx b/src/contexts/AuthContext.jsx index 67d5eb8..2e40da3 100644 --- a/src/contexts/AuthContext.jsx +++ b/src/contexts/AuthContext.jsx @@ -4,8 +4,9 @@ import { onAuthStateChanged, signInWithPopup, GoogleAuthProvider } from "firebas import { verUsuario } from "../firestore/usuarios-collection"; import { cambiarUsuario, verSiEstaRegistrado } from "../firestore/usuarios-collection"; import { FirebaseError } from "firebase/app"; -import { CODIGO_ADMIN } from "../../constants"; +import { AES_KEY, CODIGO_ADMIN } from "../../constants"; import { useTranslation } from "react-i18next"; +import { AES, enc } from "crypto-js"; export const authContext = createContext(); @@ -332,7 +333,7 @@ export function AuthProvider({ children }) { const valores = sessionStorage.getItem("session-tokens"); if (valores != null) { - const tokens = JSON.parse(valores); + const tokens = JSON.parse(AES.decrypt(valores, AES_KEY).toString(enc.Utf8)); setTokenDrive(tokens.accessToken); verificarPermisos(tokens.scopesDrive, scopes); @@ -356,7 +357,8 @@ export function AuthProvider({ children }) { * @param {JSON} tokens - Credenciales OAuth de Google. */ const guardarAuthCredsSesion = (tokens) => { - sessionStorage.setItem("session-tokens", JSON.stringify(tokens)); + const res = AES.encrypt(JSON.stringify(tokens), AES_KEY).toString(); + sessionStorage.setItem("session-tokens", res); }; /** diff --git a/src/contexts/CredencialesContext.jsx b/src/contexts/CredencialesContext.jsx index b3735e4..b1710a7 100644 --- a/src/contexts/CredencialesContext.jsx +++ b/src/contexts/CredencialesContext.jsx @@ -2,8 +2,9 @@ import { createContext, useState, useContext, useEffect } from "react"; import { initializeApp } from "firebase/app"; import { getAuth } from "firebase/auth"; import { getFirestore } from "firebase/firestore"; -import { API_URL, ENTORNO } from "../../constants"; +import { AES_KEY, API_URL, ENTORNO } from "../../constants"; import Cookies from "js-cookie"; +import { AES, enc } from "crypto-js"; export const credencialesContext = createContext(); @@ -23,7 +24,7 @@ export const useCredenciales = () => { * Proveedor del contexto que permite gestionar las credenciales de Firebase * de la aplicación. * @param {JSX.Element} children - * @returns JSX.Element + * @returns {JSX.Element} */ export function CredencialesProvider({ children }) { @@ -128,14 +129,15 @@ export function CredencialesProvider({ children }) { * @param {Array} scopes - Scopes de acceso a Google Drive. */ const almacenarCredenciales = (firebaseCreds, scopes) => { - Cookies.set("session-credentials", JSON.stringify(firebaseCreds)); + const json = AES.encrypt(JSON.stringify(firebaseCreds), AES_KEY).toString(); + Cookies.set("session-credentials", json); Cookies.set("session-drive-scopes", scopes); }; /** * Carga las credenciales de los servicios desde las cookies. * Devuelve el resultado de las operación. - * @returns Boolean + * @returns {Boolean} */ const cargarCredsCookies = () => { const firebaseCreds = Cookies.get("session-credentials"); @@ -143,7 +145,7 @@ export function CredencialesProvider({ children }) { let res = (firebaseCreds != undefined && firebaseCreds != null); if (res && (driveScopes != undefined && driveScopes != null)) { - const creds = JSON.parse(firebaseCreds); + const creds = JSON.parse(AES.decrypt(firebaseCreds, AES_KEY).toString(enc.Utf8)); setScopesDrive(driveScopes.split(",")); setCredsInfo((x) => ({ ...x, ...creds })); @@ -160,7 +162,7 @@ export function CredencialesProvider({ children }) { /** * Obtiene la instancia de Firestore. - * @returns Object + * @returns {Object} */ const obtenerInstanciaDB = () => { return credsInfo.db; @@ -168,7 +170,7 @@ export function CredencialesProvider({ children }) { /** * Obtiene la instancia de autenticación de Firebase. - * @returns Object + * @returns {Object} */ const obtenerInstanciaAuth = () => { return credsInfo.auth; @@ -176,7 +178,7 @@ export function CredencialesProvider({ children }) { /** * Verificar si las credenciales de Firebase están cargadas. - * @returns Boolean + * @returns {Boolean} */ const verSiCredsFirebaseEstancargadas = () => { return credsInfo.app != null && credsInfo.db != null && credsInfo.auth != null; @@ -184,7 +186,7 @@ export function CredencialesProvider({ children }) { /** * Obtiene la clave de reCAPTCHA de las credenciales. - * @returns String + * @returns {String} */ const obtenerRecaptcha = () => { return credsInfo.reCAPTCHA; diff --git a/src/pages/diagnosticos/DiagnosticoPacientePage.jsx b/src/pages/diagnosticos/DiagnosticoPacientePage.jsx index c93bab8..e5b2b03 100644 --- a/src/pages/diagnosticos/DiagnosticoPacientePage.jsx +++ b/src/pages/diagnosticos/DiagnosticoPacientePage.jsx @@ -7,6 +7,9 @@ import ModalSimple from "../../components/modals/ModalSimple"; import CloseIcon from "@mui/icons-material/Close"; import { useTranslation } from "react-i18next"; import { useNavegacion } from "../../contexts/NavegacionContext"; +import { AES, enc } from "crypto-js"; +import { AES_KEY } from "../../../constants"; + /** * Página para realizar un diagnóstico de TEP al paciente. * @returns {JSX.Element} @@ -28,12 +31,14 @@ export default function DiagnosticoPacientePage() { useEffect(() => { const token = sessionStorage.getItem("session-tokens"); if (token != null) { - drive.setToken(JSON.parse(token).accessToken); + const tokens = JSON.parse(AES.decrypt(token, AES_KEY).toString(enc.Utf8)); + drive.setToken(tokens.accessToken); } else if (auth.tokenDrive != null) { drive.setToken(auth.tokenDrive); } }, [auth.tokenDrive]); + /** * Actualizando los datos de los pacientes cuando son descargados. */ diff --git a/src/pages/diagnosticos/VerDiagnosticoPage.jsx b/src/pages/diagnosticos/VerDiagnosticoPage.jsx index f95e985..f65af32 100644 --- a/src/pages/diagnosticos/VerDiagnosticoPage.jsx +++ b/src/pages/diagnosticos/VerDiagnosticoPage.jsx @@ -30,7 +30,8 @@ import { peticionApi } from "../../services/Api"; import { ChipDiagnostico, ChipSexo, ChipValidado } from "../../components/tabs/Chips"; import ContLime from "../../components/diagnosticos/ContLime"; import { useTranslation } from "react-i18next"; -import { DatosIcono } from "../../components/icons/IconosSidebar"; +import { AES, enc } from "crypto-js"; +import { AES_KEY } from "../../../constants"; /** * Página para ver los datos de un diagnóstico. @@ -137,7 +138,8 @@ export default function VerDiagnosticoPage() { useEffect(() => { const token = sessionStorage.getItem("session-tokens"); if (token != null) { - drive.setToken(JSON.parse(token).accessToken); + const tokens = JSON.parse(AES.decrypt(token, AES_KEY).toString(enc.Utf8)); + drive.setToken(tokens.accessToken); } else if (auth.tokenDrive != null) { drive.setToken(auth.tokenDrive); } diff --git a/src/pages/diagnosticos/VerDiagnosticosPage.jsx b/src/pages/diagnosticos/VerDiagnosticosPage.jsx index 35b2ac1..ae070fd 100644 --- a/src/pages/diagnosticos/VerDiagnosticosPage.jsx +++ b/src/pages/diagnosticos/VerDiagnosticosPage.jsx @@ -26,6 +26,8 @@ import AdvertenciaEspacio from "../../components/menu/AdvertenciaEspacio"; import CloseIcon from "@mui/icons-material/Close"; import { ChipDiagnostico, ChipValidado, ChipSexo } from "../../components/tabs/Chips"; import { useTranslation } from "react-i18next"; +import { AES, enc } from "crypto-js"; +import { AES_KEY } from "../../../constants"; /** * Página para ver los diagnósticos del usuario. @@ -131,7 +133,8 @@ export default function VerDiagnosticosPage() { useEffect(() => { const token = sessionStorage.getItem("session-tokens"); if (token != null) { - drive.setToken(JSON.parse(token).accessToken); + const tokens = JSON.parse(AES.decrypt(token, AES_KEY).toString(enc.Utf8)); + drive.setToken(tokens.accessToken); } else if (auth.tokenDrive != null) { drive.setToken(auth.tokenDrive); } diff --git a/src/pages/pacientes/AnadirPacientePage.jsx b/src/pages/pacientes/AnadirPacientePage.jsx index b88b719..87651e1 100644 --- a/src/pages/pacientes/AnadirPacientePage.jsx +++ b/src/pages/pacientes/AnadirPacientePage.jsx @@ -5,7 +5,8 @@ import FormPaciente from "../../components/forms/FormPaciente"; import MenuLayout from "../../components/layout/MenuLayout"; import { useTranslation } from "react-i18next"; import { useNavegacion } from "../../contexts/NavegacionContext"; - +import { AES, enc } from "crypto-js"; +import { AES_KEY } from "../../../constants"; /** * Página para añadir un nuevo paciente al sistema. * @returns {JSX.Element} @@ -26,7 +27,8 @@ export default function AnadirPacientePage() { useEffect(() => { const token = sessionStorage.getItem("session-tokens"); if (token != null && drive.token == null) { - drive.setToken(JSON.parse(token).accessToken); + const tokens = JSON.parse(AES.decrypt(token, AES_KEY).toString(enc.Utf8)); + drive.setToken(tokens.accessToken); } else if (auth.tokenDrive != null) { drive.setToken(auth.tokenDrive); } diff --git a/src/pages/pacientes/EditarPacientePage.jsx b/src/pages/pacientes/EditarPacientePage.jsx index 10b75f3..344ff2c 100644 --- a/src/pages/pacientes/EditarPacientePage.jsx +++ b/src/pages/pacientes/EditarPacientePage.jsx @@ -7,6 +7,8 @@ import { useNavigate, useSearchParams } from "react-router"; import { validarId } from "../../utils/Validadores"; import { useTranslation } from "react-i18next"; import { useNavegacion } from "../../contexts/NavegacionContext"; +import { AES, enc } from "crypto-js"; +import { AES_KEY } from "../../../constants"; /** * Página para editar los datos de un paciente. @@ -31,7 +33,8 @@ export default function EditarPacientePage() { useEffect(() => { const token = sessionStorage.getItem("session-tokens"); if (token != null) { - drive.setToken(JSON.parse(token).accessToken); + const tokens = JSON.parse(AES.decrypt(token, AES_KEY).toString(enc.Utf8)); + drive.setToken(tokens.accessToken); } else if (auth.tokenDrive != null) { drive.setToken(auth.tokenDrive); } diff --git a/src/pages/pacientes/VerPacientePage.jsx b/src/pages/pacientes/VerPacientePage.jsx index 56ffb9a..cb2b5b1 100644 --- a/src/pages/pacientes/VerPacientePage.jsx +++ b/src/pages/pacientes/VerPacientePage.jsx @@ -20,6 +20,8 @@ import ModalAccion from "../../components/modals/ModalAccion"; import ContComorbilidades from "../../components/diagnosticos/ContComorbilidades"; import { ChipSexo } from "../../components/tabs/Chips"; import { useTranslation } from "react-i18next"; +import { AES, enc } from "crypto-js"; +import { AES_KEY } from "../../../constants"; /** * Página para ver los datos de un paciente. @@ -73,7 +75,8 @@ export default function VerPacientePage() { useEffect(() => { const token = sessionStorage.getItem("session-tokens"); if (token != null) { - drive.setToken(JSON.parse(token).accessToken); + const tokens = JSON.parse(AES.decrypt(token, AES_KEY).toString(enc.Utf8)); + drive.setToken(tokens.accessToken); } else if (auth.tokenDrive != null) { drive.setToken(auth.tokenDrive); } diff --git a/src/pages/pacientes/VerPacientesPage.jsx b/src/pages/pacientes/VerPacientesPage.jsx index ba9ae01..97899f4 100644 --- a/src/pages/pacientes/VerPacientesPage.jsx +++ b/src/pages/pacientes/VerPacientesPage.jsx @@ -16,6 +16,8 @@ import customParseFormat from "dayjs/plugin/customParseFormat"; import RefreshIcon from "@mui/icons-material/Refresh"; import CloseIcon from "@mui/icons-material/Close"; import { ChipSexo } from "../../components/tabs/Chips"; +import { AES, enc } from "crypto-js"; +import { AES_KEY } from "../../../constants"; /** * Página para ver la lista de pacientes. @@ -51,7 +53,8 @@ export default function VerPacientesPage() { useEffect(() => { const token = sessionStorage.getItem("session-tokens"); if (token != null) { - drive.setToken(JSON.parse(token).accessToken); + const tokens = JSON.parse(AES.decrypt(token, AES_KEY).toString(enc.Utf8)); + drive.setToken(tokens.accessToken); } else if (auth.tokenDrive != null) { drive.setToken(auth.tokenDrive); }