diff --git a/Aspnet/Controllers/UsuarioController.cs b/Aspnet/Controllers/UsuarioController.cs index 38ba1bf..3c6678d 100644 --- a/Aspnet/Controllers/UsuarioController.cs +++ b/Aspnet/Controllers/UsuarioController.cs @@ -2,6 +2,7 @@ using AlquilaFacil.Builder; using Microsoft.AspNetCore.Mvc; using Modelo; using Entidades; +using AlquilaFacil.Emailer.Sender; namespace AlquilaFacil.Controllers; @@ -51,7 +52,15 @@ public class UsuarioController : ControllerBase if (!emailrecuperacion.Contains("@")) return BadRequest(new { message = "Tiene que ser un email" }); bool ret = RepositorioUsuarios.Singleton.SetEmailRecuperacion(emailrecuperacion, cli); - return ret ? Ok(new { message = "Email de recuperación actualizado con éxito" }) : BadRequest(new { message = "No se pudo actualizar el email de recuperación" }); + if (ret == false) return BadRequest(new { message = "No se pudo actualizar el email de recuperación" }); + + Task.Run(() => + { + AvisoEmailSender s = new(); + s.Send(cli.Email, setemail.EmailRecuperacion); + }); + + return Ok(new { message = "Email de recuperación actualizado con éxito" }); } public record recuperarusuario(string Email, string EmailRecuperacion); @@ -61,7 +70,34 @@ public class UsuarioController : ControllerBase bool check = RepositorioUsuarios.Singleton.CheckEmailRecuperacion(mails.Email, mails.EmailRecuperacion); if (check == false) return BadRequest(new { message = "El email no corresponde al email de recuperacion" }); - //WIP hacer emailer + string pin = ""; + var ran = new Random(); + for (int i = 0; i < 6; i++) pin += ran.Next(0, 10); + bool ret = RepositorioUsuarios.Singleton.SetF2aPin(pin, mails.Email); + if (ret == false) return BadRequest(new { message = "no se pudo generar/guardar el codigo 2fa" }); + + OtpEmailSender s = new(); + s.Send(mails.EmailRecuperacion, mails.Email, pin); + + return Ok(new { message = $"Se envio un email de recuperacion a {mails.EmailRecuperacion}" }); + } + + public record ingreso2fa(string Pin, string Email); + [HttpPost("/api/ingresar2fa")] + public IActionResult IngresarUsuario([FromBody] ingreso2fa data) + { + if (!data.Email.Contains("@")) return BadRequest(new { message = "Tiene que ser un email" }); + if (data.Pin.Length != 6) return BadRequest(new { message = "el pin tiene que tener 6 digitos" }); + + (bool check, long Dni) = RepositorioUsuarios.Singleton.Check2fa(data.Email, data.Pin); + if (check == false) return BadRequest(new { message = "El pin es incorrecto" }); + + var cli = RepositorioUsuarios.Singleton.ObtenerClientePorDni(Dni); + + //esto literalmente no se puede triggerear pero lo pongo para evitar una warning + if (cli == null) return BadRequest(new { message = "El usuario no existe" }); + + return Ok(new { token = cli.Token }); } } diff --git a/Aspnet/Emailer/Builder/EmailBuilder.cs b/Aspnet/Emailer/Builder/EmailBuilder.cs index 34777c6..20df36b 100644 --- a/Aspnet/Emailer/Builder/EmailBuilder.cs +++ b/Aspnet/Emailer/Builder/EmailBuilder.cs @@ -17,10 +17,20 @@ public class EmailBuilder return this; } - public EmailBuilder Body(string email, string pin) + public EmailBuilder Body(string email, string pin, string modo = "2fa") { _message.IsBodyHtml = true; - _message.Body = new HtmlGenerator().GenerarMail2fa(email, pin); + switch (modo) + { + case "2fa": + _message.Body = new HtmlGenerator().GenerarMail2fa(email, pin); + break; + case "aviso": + _message.Body = new HtmlGenerator().AvisoSetEmail(email, pin); + break; + default: + break; + } return this; } diff --git a/Aspnet/Emailer/Builder/EmailHtmlGenerator.cs b/Aspnet/Emailer/Builder/EmailHtmlGenerator.cs index bb27711..8475e6c 100644 --- a/Aspnet/Emailer/Builder/EmailHtmlGenerator.cs +++ b/Aspnet/Emailer/Builder/EmailHtmlGenerator.cs @@ -55,4 +55,58 @@ public class HtmlGenerator return msg; } + public string AvisoSetEmail(string emailUsuario, string emailreq) + { + var msg = $""" + + + +
+ + + + + + +
+
+ Aviso: +
+

+ Se seteo este email : {emailreq}, como email de respaldo +

+
+ +
+
+ Si no sabes para que es el email, ignoralo. +
+
+
+ + + + """; + + return msg; + } + } diff --git a/Aspnet/Emailer/Decorator/AvisoEmailSender.cs b/Aspnet/Emailer/Decorator/AvisoEmailSender.cs new file mode 100644 index 0000000..94bd831 --- /dev/null +++ b/Aspnet/Emailer/Decorator/AvisoEmailSender.cs @@ -0,0 +1,11 @@ +namespace AlquilaFacil.Emailer.Sender; +using AlquilaFacil.Emailer.Builder; + +public class AvisoEmailSender : EmailSender +{ + public void Send(string emailusu, string emailreq) + { + var mail = new EmailBuilder().Body(emailusu, emailreq, "aviso").To(emailreq).Subject("AvisoEmail").Build(); + base.Send(mail); + } +} diff --git a/Aspnet/Emailer/Decorator/EmailerSender.cs b/Aspnet/Emailer/Decorator/EmailerSender.cs new file mode 100644 index 0000000..d3d0f7d --- /dev/null +++ b/Aspnet/Emailer/Decorator/EmailerSender.cs @@ -0,0 +1,50 @@ +using System.Net.Mail; +using System.Net; +using System.Text.Json; + +namespace AlquilaFacil.Emailer.Sender; + +public class EmailSender +{ + protected static SmtpClient? smtp = null; + protected void configSmtp(MailMessage mail) + { + var jsonContent = File.ReadAllText("settings.json"); + var options = JsonSerializer.Deserialize>(jsonContent); + if (options == null) return; + + + bool check = options.ContainsKey("smtpHost"); + check = options.ContainsKey("smtpPort"); + check = options.ContainsKey("emailAddr"); + check = options.ContainsKey("emailPass"); + + if (check == false) return; + mail.Sender = new MailAddress(options["emailAddr"]); + mail.From = new MailAddress(options["emailAddr"]); + + if (null != smtp) return; + smtp = new(); + smtp.DeliveryMethod = SmtpDeliveryMethod.Network; + smtp.EnableSsl = true; + smtp.Host = options["smtpHost"]; + smtp.Port = int.Parse(options["smtpPort"].ToString()); + smtp.Credentials = new NetworkCredential(options["emailAddr"], options["emailPass"]); + } + + public virtual void Send(MailMessage message) + { + configSmtp(message); + if (smtp == null) return; + try + { + smtp.Send(message); + message.Dispose(); + } + catch (Exception) + { + throw; + } + } + +} diff --git a/Aspnet/Emailer/Decorator/IEmailerSender.cs b/Aspnet/Emailer/Decorator/IEmailerSender.cs deleted file mode 100644 index a5007a4..0000000 --- a/Aspnet/Emailer/Decorator/IEmailerSender.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Net.Mail; - -namespace AlquilaFacil.Emailer.Sender; - -public interface IEmailSender -{ - public void Send(MailMessage message, SmtpClient smtp); - -} diff --git a/Aspnet/Emailer/Decorator/OtpEmailSender.cs b/Aspnet/Emailer/Decorator/OtpEmailSender.cs new file mode 100644 index 0000000..c0a20b0 --- /dev/null +++ b/Aspnet/Emailer/Decorator/OtpEmailSender.cs @@ -0,0 +1,13 @@ +namespace AlquilaFacil.Emailer.Sender; +using AlquilaFacil.Emailer.Builder; +public class OtpEmailSender : EmailSender +{ + + public void Send(string To, string email, string pin) + { + var mail = new EmailBuilder().To(To).Body(email, pin).Subject("Mail de Recuperacion").Build(); + + base.Send(mail); + + } +} diff --git a/Aspnet/Emailer/Decorator/OtpEmailSenderDecorator.cs b/Aspnet/Emailer/Decorator/OtpEmailSenderDecorator.cs deleted file mode 100644 index e6615f5..0000000 --- a/Aspnet/Emailer/Decorator/OtpEmailSenderDecorator.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace AlquilaFacil.Emailer.Sender; -using System.Net.Mail; -public class OtpEmailSender : IEmailSender -{ - private readonly int _codigoLength; - - public OtpEmailSenderDecorator(int codigoLength = 6) - { - _codigoLength = codigoLength; - } - - public void Send(MailMessage message, SmtpClient? smtp = null) - { - if (smtp == null) - { - smtp = new(); - //WIP - smtp.DeliveryMethod = SmtpDeliveryMethod.Network; - - } - - - // 4.2 Construir HTML de verificación - - } diff --git a/Aspnet/bin/Debug/net8.0/settings.json b/Aspnet/bin/Debug/net8.0/settings.json index f44d236..b3fc1c9 100644 --- a/Aspnet/bin/Debug/net8.0/settings.json +++ b/Aspnet/bin/Debug/net8.0/settings.json @@ -1,5 +1,9 @@ { "usr": "nwFNMLJcn5m0owbzeXMs", "scrt": "Mf9HxTir5mIGwWSBtQXd6DRK2k00V0EyXk7QTu70", - "connectiondb": "Server=127.0.0.1;Port=3306;Database=AlquilaFacil;Uid=AlquilaFacil;Pwd=.n@9c2ve*0,b1ETv].Kipa/~pR~V;Connection Timeout=5;SslMode=none" + "connectiondb": "Server=127.0.0.1;Port=3306;Database=AlquilaFacil;Uid=AlquilaFacil;Pwd=.n@9c2ve*0,b1ETv].Kipa/~pR~V;Connection Timeout=5;SslMode=none", + "smtpHost": "smtp.gmail.com", + "smtpPort": "587", + "emailAddr": "emailerpasillo@gmail.com", + "emailPass": "hgwa mznx xuff exws" } diff --git a/Entidades/AlquilaFacilContext.cs b/Entidades/Alquilafacilcontext.cs similarity index 99% rename from Entidades/AlquilaFacilContext.cs rename to Entidades/Alquilafacilcontext.cs index 64cf8b5..d0fb684 100644 --- a/Entidades/AlquilaFacilContext.cs +++ b/Entidades/Alquilafacilcontext.cs @@ -136,6 +136,9 @@ public partial class AlquilaFacilContext : DbContext entity.Property(e => e.EmailRecuperacion) .HasMaxLength(50) .HasColumnName("emailRecuperacion"); + entity.Property(e => e.F2a) + .HasMaxLength(6) + .HasColumnName("f2a"); entity.Property(e => e.Habilitado) .HasDefaultValueSql("b'1'") .HasColumnType("bit(1)") diff --git a/Entidades/Cliente.cs b/Entidades/Cliente.cs index fc67897..8bb00d0 100644 --- a/Entidades/Cliente.cs +++ b/Entidades/Cliente.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace Entidades; @@ -25,6 +25,9 @@ public partial class Cliente public string? EmailRecuperacion { get; set; } + public string? F2a { get; set; } + + public virtual ICollection ContratoDniinquilinoNavigations { get; set; } = new List(); public virtual ICollection ContratoDnipropietarioNavigations { get; set; } = new List(); diff --git a/Entidades/EstadoPropiedad.cs b/Entidades/EstadoPropiedad.cs deleted file mode 100644 index ed87dfa..0000000 --- a/Entidades/EstadoPropiedad.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Entidades; - -public partial class EstadoPropiedad -{ - public int Id { get; set; } - - public string Descripcion { get; set; } = null!; - - public virtual ICollection Propiedades { get; set; } = new List(); -} diff --git a/Entidades/Estadopropiedad.cs b/Entidades/Estadopropiedad.cs index a220579..ed87dfa 100644 --- a/Entidades/Estadopropiedad.cs +++ b/Entidades/Estadopropiedad.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace Entidades; diff --git a/Entidades/LogDetalle.cs b/Entidades/LogDetalle.cs deleted file mode 100644 index 9b3b469..0000000 --- a/Entidades/LogDetalle.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Entidades; - -public partial class LogDetalle -{ - public DateTime Fecha { get; set; } - - public long Dniusuario { get; set; } - - public string NombreTabla { get; set; } = null!; - - public string Columna { get; set; } = null!; - - public string? ValorAnterior { get; set; } - - public string? ValorNuevo { get; set; } - - public int Id { get; set; } - - public virtual Log Log { get; set; } = null!; -} diff --git a/Entidades/Logdetalle.cs b/Entidades/Logdetalle.cs index db3946f..9b3b469 100644 --- a/Entidades/Logdetalle.cs +++ b/Entidades/Logdetalle.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace Entidades; diff --git a/Entidades/TipoPropiedad.cs b/Entidades/TipoPropiedad.cs deleted file mode 100644 index c842eec..0000000 --- a/Entidades/TipoPropiedad.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Entidades; - -public partial class TipoPropiedad -{ - public int Id { get; set; } - - public string Descripcion { get; set; } = null!; - - public virtual ICollection Propiedades { get; set; } = new List(); -} diff --git a/Entidades/Tipopropiedad.cs b/Entidades/Tipopropiedad.cs index c0235cb..c842eec 100644 --- a/Entidades/Tipopropiedad.cs +++ b/Entidades/Tipopropiedad.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace Entidades; diff --git a/Front/src/Componentes/LoginPanel.svelte b/Front/src/Componentes/LoginPanel.svelte index b9a8769..6678c44 100644 --- a/Front/src/Componentes/LoginPanel.svelte +++ b/Front/src/Componentes/LoginPanel.svelte @@ -50,15 +50,73 @@ let showrecuperarmodal = $state(false); let modaldata = $state(""); + + let emailr = $state(""); + let emailrecovery = $state(""); async function SubmitRecuperarContraseñaEmail(e) { e.preventDefault(); - return; try { - const req = await fetch($urlG + ""); + const req = await fetch($urlG + "/api/recuperarUsuario", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + Email: emailr, + EmailRecuperacion: emailrecovery, + }), + }); + const data = await req.json(); + showrecuperarmodal = false; + if (req.ok) { + showf2amodal = true; + return; + } + + //hago esto para que no puedan haber errores en caso de que intente recuperar 1 cuenta, aborte y intente recuperar una segunda + emailr = ""; + emailrecovery = ""; + // + modaldata = data.message; } catch { modaldata = "Fallo al hacer la request"; } - } //WIP + } + + let showf2amodal = $state(false); + async function submitf2a(e) { + e.preventDefault(); + + const inputs = document.querySelectorAll(".otp-input"); + + let otppin = ""; + + inputs.forEach((x) => { + otppin += x.value.trim(); + }); + if (otppin.length != 6) { + modaldata = "la longitud del pin es incorrecta"; + return; + } + if (emailr == "") { + modaldata = "Fallo vuelva a intentar"; + return; + } + try { + const req = await fetch($urlG + "/api/ingresar2fa", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ Pin: otppin, Email: emailr }), + }); + const data = await req.json(); + if (req.ok) { + sessionStorage.setItem("token", data.token); + localStorage.setItem("email", emailr); + showf2amodal = false; + navigate("/usuario"); + } + } catch { + modaldata = "Fallo al hacer la request"; + } + } {#if modaldata} @@ -116,6 +174,86 @@ > +{#if showf2amodal} + +{/if} {#if showrecuperarmodal}