Skip to content

Commit ac4ae6f

Browse files
dispositivos de confianza sistema
1 parent efeca7d commit ac4ae6f

12 files changed

Lines changed: 751 additions & 24 deletions

File tree

backend/ipc/session_ipc.js

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ipcMain, app } from '../utils/libs.js';
2-
import { loginUsuario, registerUsuario, ValidarCodeRegistroUsuario, ValidarCodeLogin, cerrarSesionUsuario } from '../services/sesionUsuario.js';
2+
import { loginUsuario, registerUsuario, ValidarCodeRegistroUsuario, ValidarCodeLogin, cerrarSesionUsuario, marcarDispositivoConfianza, revocarDispositivoConfianza, estadoDispositivoConfianza, obtenerGestionDispositivos, revocarSesionDispositivo, revocarConfianzaDispositivo } from '../services/sesionUsuario.js';
33
import { comprobaciones_Correo, comprobarContrasenaValidaciones, comprobar_apodo, comprobar_codigo_verificacion, comprobar_contraseña_cuenta } from '../services/validadores.js';
44
import { BorrarVC, BorrarCuentaVC } from '../repositories/SecurityRepository.js';
55
import { machineIdSync } from '../utils/libs.js';
@@ -256,4 +256,41 @@ export function registerSessionHandlers(mainWindow) {
256256
return false;
257257
}
258258
});
259+
260+
// DISPOSITIVO DE CONFIANZA
261+
ipcMain.handle('marcar-dispositivo-confianza', async () => {
262+
const correo = getCorreoSesion();
263+
if (!correo) return { success: false };
264+
return await marcarDispositivoConfianza(correo);
265+
});
266+
267+
ipcMain.handle('revocar-dispositivo-confianza', async () => {
268+
const correo = getCorreoSesion();
269+
if (!correo) return { success: false };
270+
return await revocarDispositivoConfianza(correo);
271+
});
272+
273+
ipcMain.handle('estado-dispositivo-confianza', async () => {
274+
const correo = getCorreoSesion();
275+
if (!correo) return false;
276+
return await estadoDispositivoConfianza(correo);
277+
});
278+
279+
ipcMain.handle('obtener-gestion-dispositivos', async () => {
280+
const correo = getCorreoSesion();
281+
if (!correo) return null;
282+
return await obtenerGestionDispositivos(correo);
283+
});
284+
285+
ipcMain.handle('revocar-sesion-dispositivo', async (_, id_dp_hash) => {
286+
const correo = getCorreoSesion();
287+
if (!correo || typeof id_dp_hash !== 'string' || !/^[a-f0-9]{64}$/.test(id_dp_hash)) return { success: false };
288+
return await revocarSesionDispositivo(correo, id_dp_hash);
289+
});
290+
291+
ipcMain.handle('revocar-confianza-dispositivo', async (_, id_dp_hash) => {
292+
const correo = getCorreoSesion();
293+
if (!correo || typeof id_dp_hash !== 'string' || !/^[a-f0-9]{64}$/.test(id_dp_hash)) return { success: false };
294+
return await revocarConfianzaDispositivo(correo, id_dp_hash);
295+
});
259296
}

backend/models/Security.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ const TokenSchema = new mongoose.Schema({
5858
default: () => new Date()
5959
},
6060
id_dp: { type: EncryptedDataSchema, required: true },
61-
id_dp_hash: { type: String, required: true, index: true }
61+
id_dp_hash: { type: String, required: true, index: true },
62+
os: { type: String, default: null },
63+
nombre: { type: String, default: null }
6264
});
6365

6466
const TokenDPCSchema = new mongoose.Schema({
@@ -70,7 +72,9 @@ const TokenDPCSchema = new mongoose.Schema({
7072
default: ""
7173
},
7274
id_dp: { type: EncryptedDataSchema, required: true },
73-
id_dp_hash: { type: String, required: true, index: true }
75+
id_dp_hash: { type: String, required: true, index: true },
76+
os: { type: String, default: null },
77+
nombre: { type: String, default: null }
7478
});
7579

7680
const DPBLOQUEADOSchema = new mongoose.Schema({

backend/repositories/SecurityRepository.js

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export async function BuscarCuentaVC(correo, code, id_dp) {
109109
// ... more security helpers
110110

111111
/*TOKENS JWT */
112-
export async function AñadirJWTUsuario(correo, token = "") {
112+
export async function AñadirJWTUsuario(correo, token = "", info = {}) {
113113
const tokenhash = createHash("sha256").update(token).digest("hex");
114114
const deviceId = getIdDispositivo();
115115
const correoHash = hashDatosSistema(correo);
@@ -121,7 +121,9 @@ export async function AñadirJWTUsuario(correo, token = "") {
121121
token: tokenhash,
122122
expira: new Date(Date.now() + ((7 * 24 * 60 * 60 * 1000) - (90 * 60 * 1000))),
123123
id_dp: encriptarDatosSistema(deviceId),
124-
id_dp_hash: idHash
124+
id_dp_hash: idHash,
125+
os: info.os || null,
126+
nombre: info.nombre || null
125127
});
126128
}
127129
export async function AñadirJWTUsuarioVC(correo, token = "") {
@@ -139,7 +141,7 @@ export async function AñadirJWTUsuarioVC(correo, token = "") {
139141
id_dp_hash: idHash
140142
});
141143
}
142-
export async function AñadirJWTDPConfianza(correo, token = "") {
144+
export async function AñadirJWTDPConfianza(correo, token = "", info = {}) {
143145
const tokenhash = createHash("sha256").update(token).digest("hex");
144146
const deviceId = getIdDispositivo();
145147
const correoHash = hashDatosSistema(correo);
@@ -150,10 +152,28 @@ export async function AñadirJWTDPConfianza(correo, token = "") {
150152
correo_hash: correoHash,
151153
token: tokenhash,
152154
id_dp: encriptarDatosSistema(deviceId),
153-
id_dp_hash: idHash
155+
id_dp_hash: idHash,
156+
os: info.os || null,
157+
nombre: info.nombre || null
154158
});
155159
}
156160

161+
export async function ObtenerSesionesPorCorreo(correo_hash) {
162+
return await TokenSession.find({ correo_hash }).lean();
163+
}
164+
165+
export async function ObtenerDPConfianzasPorCorreo(correo_hash) {
166+
return await TokenDPC.find({ correo_hash }).lean();
167+
}
168+
169+
export async function RevocarSesionPorDispositivo(correo_hash, id_dp_hash) {
170+
await TokenSession.deleteMany({ correo_hash, id_dp_hash });
171+
}
172+
173+
export async function RevocarDPConfianzaPorDispositivo(correo_hash, id_dp_hash) {
174+
await TokenDPC.deleteMany({ correo_hash, id_dp_hash });
175+
}
176+
157177

158178
export async function LimpiarJWTUsuario(correo, token) {
159179
const tokenhash = createHash("sha256").update(token).digest("hex");
@@ -167,4 +187,19 @@ export async function LimpiarJWTUsuarioVC(correo, token = null) {
167187
query.token = createHash("sha256").update(token).digest("hex");
168188
}
169189
await TokenVC.deleteMany(query);
190+
}
191+
192+
export async function LimpiarJWTDPConfianza(correo) {
193+
const correoHash = hashDatosSistema(correo);
194+
const deviceId = getIdDispositivo();
195+
const idHash = hashDatosSistema(deviceId);
196+
await TokenDPC.deleteMany({ correo_hash: correoHash, id_dp_hash: idHash });
197+
}
198+
199+
export async function ObtenerInfoSesionDispositivo(correo_hash, id_dp_hash) {
200+
return await TokenSession.findOne({ correo_hash, id_dp_hash }, { os: 1, nombre: 1 }).lean();
201+
}
202+
203+
export async function ObtenerInfoDPConfianzaDispositivo(correo_hash, id_dp_hash) {
204+
return await TokenDPC.findOne({ correo_hash, id_dp_hash }, { os: 1, nombre: 1 }).lean();
170205
}

backend/services/CreadorTokens.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ const SECRET_KEY_JWT = process.env.SECRET_KEY_JWT;//codigo para crear jwt (un va
77
export async function generarteToken(duracion = "cuenta") {
88
const duraciones = {
99
sesion: '7d',
10-
cuenta: '90m'
10+
cuenta: '90m',
11+
confianza: '365d'
1112
}
1213
// 1. Identificador único del dispositivo
1314
const deviceId = String(machineIdSync());

backend/services/MENSAJERIA/Estructuras_correos.js

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,93 @@ const ConfirmacionCambioCorreo = ({ apodo }) => {
269269
return { asunto, htmlContenido };
270270
}
271271

272+
const _bloqueInfoDispositivo = (nombre, sistemaOperativo, fecha) => `
273+
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="margin: 18px 0; background-color: #0b1120; border: 1px solid #1e293b; border-radius: 10px;">
274+
<tr>
275+
<td style="padding: 16px 20px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;">
276+
<table border="0" cellpadding="0" cellspacing="0" width="100%">
277+
${nombre ? `<tr><td style="padding: 4px 0; border-bottom: 1px solid #1e293b;"><span style="color:#64748b;font-size:11px;text-transform:uppercase;letter-spacing:.05em;">Dispositivo</span><br><strong style="color:#e2e8f0;font-size:14px;">${nombre}</strong></td></tr>` : ''}
278+
${sistemaOperativo ? `<tr><td style="padding: 4px 0; border-bottom: 1px solid #1e293b;"><span style="color:#64748b;font-size:11px;text-transform:uppercase;letter-spacing:.05em;">Sistema operativo</span><br><strong style="color:#e2e8f0;font-size:14px;">${sistemaOperativo}</strong></td></tr>` : ''}
279+
<tr><td style="padding: 4px 0;"><span style="color:#64748b;font-size:11px;text-transform:uppercase;letter-spacing:.05em;">Fecha y hora</span><br><strong style="color:#e2e8f0;font-size:14px;">${fecha}</strong></td></tr>
280+
</table>
281+
</td>
282+
</tr>
283+
</table>`;
284+
285+
const AvisoDispositivoConfianzaAnadido = ({ apodo, nombre, sistemaOperativo, fecha }) => {
286+
const asunto = "Nuevo dispositivo de confianza añadido";
287+
const htmlContenido = BaseEmailWrapper(`
288+
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-bottom: 20px; background-color: #052e16; border-left: 4px solid #22c55e; border-radius: 0 8px 8px 0;">
289+
<tr>
290+
<td style="padding: 14px 18px;">
291+
<h2 style="color: #22c55e; font-size: 17px; margin: 0 0 5px 0; font-weight: 700;">Dispositivo de confianza a&ntilde;adido</h2>
292+
<p style="margin: 0; color: #e2e8f0; font-size: 14px;">Un nuevo dispositivo ya puede iniciar sesi&oacute;n sin verificaci&oacute;n.</p>
293+
</td>
294+
</tr>
295+
</table>
296+
297+
<p style="margin: 0 0 6px 0; font-size: 15px;">Hola, <strong style="color: #f8fafc;">${apodo}</strong>.</p>
298+
<p style="margin: 0 0 4px 0; font-size: 14px; color: #94a3b8;">El siguiente dispositivo ha sido marcado como de confianza en tu cuenta:</p>
299+
300+
${_bloqueInfoDispositivo(nombre, sistemaOperativo, fecha)}
301+
302+
<p style="font-size: 13px; color: #64748b; line-height: 1.6; margin: 0;">
303+
<strong style="color: #ef4444;">¿No has sido t&uacute;?</strong> Accede a los ajustes de la aplicaci&oacute;n, revoca la confianza de ese dispositivo y cambia tu contrase&ntilde;a de inmediato.
304+
</p>
305+
`);
306+
return { asunto, htmlContenido };
307+
};
308+
309+
const AvisoDispositivoConfianzaRevocado = ({ apodo, nombre, sistemaOperativo, fecha }) => {
310+
const asunto = "Dispositivo de confianza eliminado";
311+
const htmlContenido = BaseEmailWrapper(`
312+
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-bottom: 20px; background-color: #2c1502; border-left: 4px solid #f97316; border-radius: 0 8px 8px 0;">
313+
<tr>
314+
<td style="padding: 14px 18px;">
315+
<h2 style="color: #f97316; font-size: 17px; margin: 0 0 5px 0; font-weight: 700;">Confianza de dispositivo revocada</h2>
316+
<p style="margin: 0; color: #e2e8f0; font-size: 14px;">Un dispositivo ya no puede iniciar sesi&oacute;n sin verificaci&oacute;n.</p>
317+
</td>
318+
</tr>
319+
</table>
320+
321+
<p style="margin: 0 0 6px 0; font-size: 15px;">Hola, <strong style="color: #f8fafc;">${apodo}</strong>.</p>
322+
<p style="margin: 0 0 4px 0; font-size: 14px; color: #94a3b8;">El siguiente dispositivo necesitar&aacute; verificaci&oacute;n por correo en su pr&oacute;ximo inicio de sesi&oacute;n:</p>
323+
324+
${_bloqueInfoDispositivo(nombre, sistemaOperativo, fecha)}
325+
326+
<p style="font-size: 13px; color: #64748b; line-height: 1.6; margin: 0;">
327+
Si has sido t&uacute;, puedes ignorar este mensaje.
328+
<strong style="color: #ef4444;">Si no reconoces esta acci&oacute;n</strong>, alguien tiene acceso a tu cuenta — cambia tu contrase&ntilde;a inmediatamente.
329+
</p>
330+
`);
331+
return { asunto, htmlContenido };
332+
};
333+
334+
const AvisoSesionCerrada = ({ apodo, nombre, sistemaOperativo, fecha }) => {
335+
const asunto = "Sesión de dispositivo cerrada";
336+
const htmlContenido = BaseEmailWrapper(`
337+
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-bottom: 20px; background-color: #1c0a2e; border-left: 4px solid #a855f7; border-radius: 0 8px 8px 0;">
338+
<tr>
339+
<td style="padding: 14px 18px;">
340+
<h2 style="color: #a855f7; font-size: 17px; margin: 0 0 5px 0; font-weight: 700;">Sesi&oacute;n cerrada remotamente</h2>
341+
<p style="margin: 0; color: #e2e8f0; font-size: 14px;">El acceso autom&aacute;tico de un dispositivo ha sido eliminado.</p>
342+
</td>
343+
</tr>
344+
</table>
345+
346+
<p style="margin: 0 0 6px 0; font-size: 15px;">Hola, <strong style="color: #f8fafc;">${apodo}</strong>.</p>
347+
<p style="margin: 0 0 4px 0; font-size: 14px; color: #94a3b8;">La sesi&oacute;n guardada del siguiente dispositivo ha sido eliminada. Necesitar&aacute; credenciales completas para volver a entrar:</p>
348+
349+
${_bloqueInfoDispositivo(nombre, sistemaOperativo, fecha)}
350+
351+
<p style="font-size: 13px; color: #64748b; line-height: 1.6; margin: 0;">
352+
Si has sido t&uacute;, puedes ignorar este mensaje.
353+
<strong style="color: #ef4444;">Si no reconoces esta acci&oacute;n</strong>, cambia tu contrase&ntilde;a inmediatamente.
354+
</p>
355+
`);
356+
return { asunto, htmlContenido };
357+
};
358+
272359
const ConfirmacionCambioApodo = ({ apodo }) => {
273360
const asunto = "Apodo Actualizado"
274361
const htmlContenido = BaseEmailWrapper(`
@@ -293,5 +380,8 @@ export {
293380
CodigoCambiarDatosCuenta,
294381
ConfirmacionCambioContraseña,
295382
ConfirmacionCambioCorreo,
296-
ConfirmacionCambioApodo
383+
ConfirmacionCambioApodo,
384+
AvisoDispositivoConfianzaAnadido,
385+
AvisoDispositivoConfianzaRevocado,
386+
AvisoSesionCerrada
297387
};

0 commit comments

Comments
 (0)