Update Usuarios Extended Fields
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 5m25s

This commit is contained in:
Leandro Hernan Rojas 2025-04-29 18:52:49 -03:00
parent d179b1b74d
commit 050cf9af0f
9 changed files with 843 additions and 287 deletions

View File

@ -10,6 +10,7 @@ using Services.Models;
using Services.Interfaces; using Services.Interfaces;
using phronCare.API.Models.Authentication.Login; using phronCare.API.Models.Authentication.Login;
using phronCare.API.Models.Authentication.SingUp; using phronCare.API.Models.Authentication.SingUp;
using phronCare.API.Models.Security;
using Google.Authenticator; using Google.Authenticator;
using QRCoder; using QRCoder;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
@ -18,51 +19,52 @@ namespace phronCare.API.Controllers
{ {
[Route("api/[controller]")] [Route("api/[controller]")]
[ApiController] [ApiController]
public class AuthenticationController(UserManager<IdentityUser> userManager, RoleManager<IdentityRole> roleManager, IEmailService emailService, IConfiguration configuration, SignInManager<IdentityUser> signInManager, TwoFactorAuthenticator twoFactorAuthenticator) : ControllerBase public class AuthenticationController(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager,
IEmailService emailService,
IConfiguration configuration,
SignInManager<ApplicationUser> signInManager,
TwoFactorAuthenticator twoFactorAuthenticator) : ControllerBase
{ {
private const int JWT_TOKEN_VALIDITY_HOURS = 48; private const int JWT_TOKEN_VALIDITY_HOURS = 48;
#region Declaration Section #region Declaration Section
private readonly UserManager<IdentityUser> userManager = userManager; private readonly UserManager<ApplicationUser> userManager = userManager;
private readonly RoleManager<IdentityRole> roleManager = roleManager; private readonly RoleManager<IdentityRole> roleManager = roleManager;
private readonly SignInManager<IdentityUser> signInManager = signInManager; private readonly SignInManager<ApplicationUser> signInManager = signInManager;
private readonly TwoFactorAuthenticator authenticator = twoFactorAuthenticator; private readonly TwoFactorAuthenticator authenticator = twoFactorAuthenticator;
private readonly IEmailService emailService = emailService; private readonly IEmailService emailService = emailService;
private readonly IConfiguration configuration = configuration; private readonly IConfiguration configuration = configuration;
#endregion #endregion
#region Segurity Endpoints #region Security Endpoints
[HttpPost] [HttpPost]
[Route("generate-qr-code")] [Route("generate-qr-code")]
public async Task<IActionResult> GenerateQRCodeAsync() public async Task<IActionResult> GenerateQRCodeAsync()
{ {
try try
{ {
var user = await signInManager.GetTwoFactorAuthenticationUserAsync(); // Obtén el nombre de usuario actual var user = await signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null) if (user == null)
{ {
return BadRequest(new Response { Status = "Error", Message = "Usuario no autenticado." }); return BadRequest(new Response { Status = "Error", Message = "Usuario no autenticado." });
} }
var setupInfo = authenticator.GenerateSetupCode("MiApp", user.Email, user.Id, false, 10);
// Genera el código QR como una imagen var setupInfo = authenticator.GenerateSetupCode("MiApp", user.Email, user.Id, false, 10);
var qrCodeGenerator = new QRCodeGenerator(); var qrCodeGenerator = new QRCodeGenerator();
var qrCodeData = qrCodeGenerator.CreateQrCode(setupInfo.ManualEntryKey, QRCodeGenerator.ECCLevel.Q); var qrCodeData = qrCodeGenerator.CreateQrCode(setupInfo.ManualEntryKey, QRCodeGenerator.ECCLevel.Q);
var qrCode = new PngByteQRCode(qrCodeData); var qrCode = new PngByteQRCode(qrCodeData);
var qrCodeImage = qrCode.GetGraphic(10);
// Convierte la imagen del código QR en bytes
var qrCodeImage = qrCode.GetGraphic(10); // Ajusta el tamaño según tus necesidades
var qrCodeImageData = Convert.ToBase64String(qrCodeImage); var qrCodeImageData = Convert.ToBase64String(qrCodeImage);
// Devuelve el userId (clave secreta) y la imagen del código QR en la respuesta
return Ok(new AuthResponse return Ok(new AuthResponse
{ {
Status = "Success", Status = "Success",
Message = "Código QR generado satisfactoriamente.", Message = "Código QR generado satisfactoriamente.",
ManualSetupKey = setupInfo.ManualEntryKey, // Proporciona esto al usuario para configuración manual en Google Authenticator ManualSetupKey = setupInfo.ManualEntryKey,
QRCodeImage = qrCodeImageData // Devuelve la imagen del código QR QRCodeImage = qrCodeImageData
}); });
} }
catch (Exception) catch (Exception)
@ -75,23 +77,25 @@ namespace phronCare.API.Controllers
[Route("register")] [Route("register")]
public async Task<IActionResult> Register([FromBody] RegisterUser registerUser) public async Task<IActionResult> Register([FromBody] RegisterUser registerUser)
{ {
//Chequeo Correo existente
var userExist = await userManager.FindByEmailAsync(registerUser.EmailAddress); var userExist = await userManager.FindByEmailAsync(registerUser.EmailAddress);
if (userExist != null) if (userExist != null)
{ return StatusCode(StatusCodes.Status409Conflict, "El correo ingresado ya está en uso.");
return StatusCode(StatusCodes.Status409Conflict, "El correo ingresado ya esta en uso.");
}
userExist = await userManager.FindByNameAsync(registerUser.UserName); userExist = await userManager.FindByNameAsync(registerUser.UserName);
if (userExist != null) if (userExist != null)
{
return StatusCode(StatusCodes.Status409Conflict, "El usuario ingresado ya existe."); return StatusCode(StatusCodes.Status409Conflict, "El usuario ingresado ya existe.");
}
IdentityUser user = new() var user = new ApplicationUser
{ {
Email = registerUser.EmailAddress, Email = registerUser.EmailAddress,
UserName = registerUser.UserName,
SecurityStamp = Guid.NewGuid().ToString(), SecurityStamp = Guid.NewGuid().ToString(),
UserName = registerUser.UserName FirstName = registerUser.FirstName,
LastName = registerUser.LastName,
Address = registerUser.Address,
Department = registerUser.Department,
CompanyName = registerUser.CompanyName,
BirthDate = registerUser.BirthDate
}; };
if (await roleManager.RoleExistsAsync(registerUser.Role)) if (await roleManager.RoleExistsAsync(registerUser.Role))
@ -99,22 +103,20 @@ namespace phronCare.API.Controllers
var result = await userManager.CreateAsync(user, registerUser.Password); var result = await userManager.CreateAsync(user, registerUser.Password);
if (!result.Succeeded) if (!result.Succeeded)
{ {
return StatusCode(StatusCodes.Status500InternalServerError, $"Creacion de usuario fallida: {result}"); return StatusCode(StatusCodes.Status500InternalServerError, $"Creación de usuario fallida: {string.Join(", ", result.Errors.Select(e => e.Description))}");
} }
await userManager.AddToRoleAsync(user, registerUser.Role); await userManager.AddToRoleAsync(user, registerUser.Role);
//Add Token to Verify the email
var token = await userManager.GenerateEmailConfirmationTokenAsync(user); var token = await userManager.GenerateEmailConfirmationTokenAsync(user);
var confirmationLink = Url.Action(nameof(ConfirmEmail), "Authentication", new { token, email = user.Email }, Request.Scheme); var confirmationLink = Url.Action(nameof(ConfirmEmail), "Authentication", new { token, email = user.Email }, Request.Scheme);
var message = new Message([user.Email!], "phronCare - Link de verificacion", confirmationLink!); var message = new Message(new[] { user.Email! }, "phronCare - Link de verificación", confirmationLink!);
emailService.SendEmail(message); emailService.SendEmail(message);
return StatusCode(StatusCodes.Status201Created, $"Usuario creado satisfactoriamente. Debe autenticar la cuenta generada desde la direccion de correo registrada: {user.Email}"); return StatusCode(StatusCodes.Status201Created, $"Usuario creado satisfactoriamente. Debe autenticar la cuenta desde su correo: {user.Email}");
}
else
{
return StatusCode(StatusCodes.Status500InternalServerError, "El rol no existe. Intentalo nuevamente!");
} }
return StatusCode(StatusCodes.Status500InternalServerError, "El rol no existe. ¡Inténtalo nuevamente!");
} }
[HttpGet("confirmemail")] [HttpGet("confirmemail")]
@ -125,13 +127,9 @@ namespace phronCare.API.Controllers
{ {
var result = await userManager.ConfirmEmailAsync(user, token); var result = await userManager.ConfirmEmailAsync(user, token);
if (result.Succeeded) if (result.Succeeded)
{ return Ok(new Response { Status = "Success", Message = "Email verificado satisfactoriamente." });
return StatusCode(StatusCodes.Status200OK,
new Response { Status = "Success", Message = "Email verificado satisfactoriamente" });
}
} }
return StatusCode(StatusCodes.Status409Conflict, return Conflict(new Response { Status = "Error", Message = "Este usuario no existe." });
new Response { Status = "Error", Message = "Este usuario no existe" });
} }
[HttpPost] [HttpPost]
@ -141,9 +139,7 @@ namespace phronCare.API.Controllers
await signInManager.SignOutAsync(); await signInManager.SignOutAsync();
var user = await userManager.FindByNameAsync(loginModel.Username); var user = await userManager.FindByNameAsync(loginModel.Username);
if (user is null) if (user is null)
{ return Unauthorized("El nombre de usuario o contraseña son incorrectos.");
return Unauthorized("El nombre de usuario o contraseña son incorrectos");
};
var canSignIn = await signInManager.CanSignInAsync(user); var canSignIn = await signInManager.CanSignInAsync(user);
if (!canSignIn) if (!canSignIn)
@ -151,26 +147,24 @@ namespace phronCare.API.Controllers
string message = string.Empty; string message = string.Empty;
if (userManager.Options.SignIn.RequireConfirmedEmail && !(await userManager.IsEmailConfirmedAsync(user))) if (userManager.Options.SignIn.RequireConfirmedEmail && !(await userManager.IsEmailConfirmedAsync(user)))
{ {
message = "El usuario {" + user.UserName + "} no puede iniciar sesion sin tener el email confirmado."; message = $"El usuario {user.UserName} no puede iniciar sesión sin tener el email confirmado.";
} }
if (userManager.Options.SignIn.RequireConfirmedPhoneNumber && !(await userManager.IsPhoneNumberConfirmedAsync(user))) if (userManager.Options.SignIn.RequireConfirmedPhoneNumber && !(await userManager.IsPhoneNumberConfirmedAsync(user)))
{ {
message = "El usuario {" + user.UserName + "} no puede iniciar sesion sin tener confirmado el numero de telefono."; message = $"El usuario {user.UserName} no puede iniciar sesión sin tener confirmado el número de teléfono.";
} }
return StatusCode(StatusCodes.Status401Unauthorized, message); return Unauthorized(message);
} }
if (user != null && await userManager.CheckPasswordAsync(user, loginModel.Password))
if (await userManager.CheckPasswordAsync(user, loginModel.Password))
{ {
if (!user.TwoFactorEnabled) if (!user.TwoFactorEnabled)
{
return await GenerateAccess(user); return await GenerateAccess(user);
}
else return Accepted(new Response { Status = "Success", Message = $"Debe ingresar el código desde la app Google Authenticator: {user.UserName}" });
{
return StatusCode(StatusCodes.Status202Accepted, new Response { Status = "Success", Message = $"Debe ingresar el codigo desde la app Google Authenticator : {user.UserName}" });
};
} }
return Unauthorized("El nombre de usuario o contraseña son incorrectos");
return Unauthorized("El nombre de usuario o contraseña son incorrectos.");
} }
[HttpPost] [HttpPost]
@ -180,21 +174,17 @@ namespace phronCare.API.Controllers
var user = await userManager.FindByNameAsync(username); var user = await userManager.FindByNameAsync(username);
if (user is not null) if (user is not null)
{ {
string secretKey = user.Id; // Obtén la clave secreta del usuario desde tu base de datos bool validate = authenticator.ValidateTwoFactorPIN(user.Id, code);
bool isBase32 = false; // Define si la clave secreta está en formato Base32
bool validate = authenticator.ValidateTwoFactorPIN(secretKey, code, isBase32);
if (validate) if (validate)
{
return await GenerateAccess(user); return await GenerateAccess(user);
}
} }
return StatusCode(StatusCodes.Status401Unauthorized, new Response { Status = "Error", Message = "Código de verificación incorrecto." }); return Unauthorized(new Response { Status = "Error", Message = "Código de verificación incorrecto." });
} }
[HttpPost] [HttpPost]
[AllowAnonymous] [AllowAnonymous]
[Route("forgot-password")] [Route("forgot-password")]
public async Task<IActionResult> ForgotPassword([Required]string email) public async Task<IActionResult> ForgotPassword([Required] string email)
{ {
var user = await userManager.FindByEmailAsync(email); var user = await userManager.FindByEmailAsync(email);
if (user != null) if (user != null)
@ -202,27 +192,21 @@ namespace phronCare.API.Controllers
var token = await userManager.GeneratePasswordResetTokenAsync(user); var token = await userManager.GeneratePasswordResetTokenAsync(user);
var forgotPasswordLink = Url.Action(nameof(ResetPassword), "Authentication", new { token, email = user.Email }, Request.Scheme); var forgotPasswordLink = Url.Action(nameof(ResetPassword), "Authentication", new { token, email = user.Email }, Request.Scheme);
var message = new Message([user.Email!], "phronCare - Enlace de recuperacion de contraseña.", forgotPasswordLink!); var message = new Message(new[] { user.Email! }, "phronCare - Enlace de recuperación de contraseña", forgotPasswordLink!);
emailService.SendEmail(message); emailService.SendEmail(message);
return StatusCode(StatusCodes.Status200OK, $"La solicitud de cambio de contraseña se envio a: {user.Email} con éxito. Por favor verifique el enlace enviado a la casilla de correo."); return Ok($"La solicitud de cambio de contraseña se envió a: {user.Email} exitosamente.");
} }
return StatusCode(StatusCodes.Status400BadRequest, "No se pudo enviar el enlace al correo, por favor intente nuevamente."); return BadRequest("No se pudo enviar el enlace al correo, por favor intente nuevamente.");
} }
[HttpGet("reset-password")] [HttpGet("reset-password")]
public async Task<IActionResult> ResetPassword(string token, string email) public IActionResult ResetPassword(string token, string email)
{ {
//var model =new ResetPassword {Token=token, Email = email}; var htmlContent = GetResetPasswordHtmlContent(email, token);
var htmlContent = GetResetPasswordHtmlContent(email,token);
var bytes = Encoding.UTF8.GetBytes(htmlContent); var bytes = Encoding.UTF8.GetBytes(htmlContent);
var stream = new MemoryStream(bytes); var stream = new MemoryStream(bytes);
return File(stream, "text/html"); return File(stream, "text/html");
} }
private static string GetResetPasswordHtmlContent(string username, string token)
{
string htmlContent = @"<!DOCTYPE html><html><head><meta name=viewport content=""width=device-width""><meta http-equiv=Content-Type content=""text/html; charset=UTF-8""><title>phronCare - Notificación</title><style>body{background:white;font-family:'Helvetica',sans-serif;color:#fff}.container{background-image:linear-gradient(45deg,rgb(5,39,103) 0%,#3a0647 70%);border-radius:50px;color:#fff;padding:20px;margin:30px auto;width:500px;text-align:center;}h1{font-size:20px}h2{font-size:16px;color:yellow;overflow-wrap:break-word;}p{font-size:16px}.form-control{width: 90%;padding: .375rem .75rem;height: 110px;max-height: 150px;border: 1px solid #ced4da;border-radius: .25rem;font-size: 1rem;}textarea{resize: none;}</style></head><body><table class=container><tr><td><h1>phronCare XL: Notificación</h1><h2>" + username + "</h2><h2>Token</h2><textarea class='form-control' readonly style='max-height: 150px;'>" + token + "</textarea><h3>Tu solicitud de restablecimiento de contraseña ha sido confirmada</h3><p>Para restablecer tu contraseña en phronCare, ingrese las credenciales proporcionadas.</p></td></tr></table></body></html>";
return htmlContent;
}
[HttpPost] [HttpPost]
[AllowAnonymous] [AllowAnonymous]
@ -237,37 +221,100 @@ namespace phronCare.API.Controllers
{ {
return StatusCode(StatusCodes.Status500InternalServerError, resetPassResult.Errors.First().Description); return StatusCode(StatusCodes.Status500InternalServerError, resetPassResult.Errors.First().Description);
} }
return StatusCode(StatusCodes.Status200OK,$"La password ha sido cambiada correctamente."); return Ok("La contraseña ha sido cambiada correctamente.");
} }
return StatusCode(StatusCodes.Status500InternalServerError,"No se pudo encontrar el usuario, por favor intente nuevamente."); return StatusCode(StatusCodes.Status500InternalServerError, "No se pudo encontrar el usuario, por favor intente nuevamente.");
}
private static string GetResetPasswordHtmlContent(string username, string token)
{
return @$"
<!DOCTYPE html>
<html>
<head><meta charset='UTF-8'><meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>phronCare - Notificación</title></head>
<body><h2>Usuario: {username}</h2><h3>Token: {token}</h3></body>
</html>";
} }
#endregion #endregion
#region GenerateAccess #region GenerateAccess
private async Task<IActionResult> GenerateAccess(IdentityUser? user) //private async Task<IActionResult> GenerateAccess(ApplicationUser user)
//{
// try
// {
// var authClaims = new List<Claim>
// {
// new(ClaimTypes.Name, user.UserName),
// new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
// };
// var userRoles = await userManager.GetRolesAsync(user);
// foreach (var role in userRoles)
// {
// authClaims.Add(new Claim(ClaimTypes.Role, role));
// }
// var jwtToken = GetToken(authClaims);
// var userSession = new UserSession
// {
// UserName = user.UserName,
// Role = userRoles.First(),
// Token = new JwtSecurityTokenHandler().WriteToken(jwtToken),
// ExpiresIn = (int)jwtToken.ValidTo.Subtract(DateTime.Now).TotalSeconds,
// ExpiryTimeStamp = jwtToken.ValidTo
// };
// return Ok(userSession);
// }
// catch (Exception ex)
// {
// return BadRequest(ex.Message);
// }
//}
private async Task<IActionResult> GenerateAccess(ApplicationUser user)
{ {
var authClaims = new List<Claim> try
{ {
new(ClaimTypes.Name, user.UserName), var authClaims = new List<Claim>
new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), {
}; new(ClaimTypes.Name, user.UserName),
var userRoles = await userManager.GetRolesAsync(user); new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
foreach (var role in userRoles) };
{
authClaims.Add(new Claim(ClaimTypes.Role, role)); var userRoles = await userManager.GetRolesAsync(user);
if (!userRoles.Any())
{
return BadRequest("El usuario no tiene ningún rol asignado. Verifica el proceso de registro.");
}
foreach (var role in userRoles)
{
authClaims.Add(new Claim(ClaimTypes.Role, role));
}
var jwtToken = GetToken(authClaims);
var userSession = new UserSession
{
UserName = user.UserName,
Role = userRoles.First(), // ya validado que hay al menos uno
Token = new JwtSecurityTokenHandler().WriteToken(jwtToken),
ExpiresIn = (int)jwtToken.ValidTo.Subtract(DateTime.Now).TotalSeconds,
ExpiryTimeStamp = jwtToken.ValidTo
};
return Ok(userSession);
} }
catch (Exception ex)
var jwtToken = GetToken(authClaims);
var userSession = new UserSession
{ {
UserName = user.UserName, // O podés loguearlo con logger si querés
Role = userRoles.First(), return StatusCode(StatusCodes.Status500InternalServerError, $"Error generando token: {ex.Message}");
Token = new JwtSecurityTokenHandler().WriteToken(jwtToken), }
ExpiresIn = (int)jwtToken.ValidTo.Subtract(DateTime.Now).TotalSeconds,
ExpiryTimeStamp = jwtToken.ValidTo
};
return Ok(userSession);
} }
public class UserSession public class UserSession
{ {
public string UserName { get; set; } public string UserName { get; set; }
@ -277,31 +324,25 @@ namespace phronCare.API.Controllers
public DateTime ExpiryTimeStamp { get; set; } public DateTime ExpiryTimeStamp { get; set; }
} }
#endregion #endregion
#region GenerateToken #region GenerateToken
private JwtSecurityToken GetToken(List<Claim> authClaims) private JwtSecurityToken GetToken(List<Claim> authClaims)
{ {
var secret = configuration["JWT:Secret"]; var secret = configuration["JWT:Secret"];
if (string.IsNullOrWhiteSpace(secret)) if (string.IsNullOrWhiteSpace(secret))
throw new InvalidOperationException("El Secret no está configurado."); throw new InvalidOperationException("El Secret no está configurado.");
// Convertir explícitamente a bytes var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
var keyBytes = Encoding.UTF8.GetBytes(secret);
var authSigningKey = new SymmetricSecurityKey(keyBytes);
var credentials = new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256); var credentials = new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken( return new JwtSecurityToken(
issuer: configuration["JWT:ValidIssuer"], issuer: configuration["JWT:ValidIssuer"],
audience: configuration["JWT:ValidAudience"], audience: configuration["JWT:ValidAudience"],
expires: DateTime.UtcNow.AddHours(JWT_TOKEN_VALIDITY_HOURS), expires: DateTime.UtcNow.AddHours(JWT_TOKEN_VALIDITY_HOURS),
claims: authClaims, claims: authClaims,
signingCredentials: credentials signingCredentials: credentials
); );
return token;
} }
#endregion #endregion
} }
} }

View File

@ -0,0 +1,302 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using phronCare.API.Models;
#nullable disable
namespace phronCare.API.Migrations
{
[DbContext(typeof(phronCareDbContext))]
[Migration("20250429190347_ExtendApplicationUser")]
partial class ExtendApplicationUser
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("phronCare.API.Models.Security.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("Address")
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("BirthDate")
.HasColumnType("datetime2");
b.Property<string>("CompanyName")
.HasColumnType("nvarchar(max)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Department")
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("ProfileImageUrl")
.HasColumnType("nvarchar(max)");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("phronCare.API.Models.Security.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("phronCare.API.Models.Security.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("phronCare.API.Models.Security.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("phronCare.API.Models.Security.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,112 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
namespace phronCare.API.Migrations
{
/// <inheritdoc />
public partial class ExtendApplicationUser : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "AspNetRoles",
keyColumn: "Id",
keyValue: "a36da698-f5bc-4af3-bc25-81c675d52914");
migrationBuilder.DeleteData(
table: "AspNetRoles",
keyColumn: "Id",
keyValue: "ee45c015-8a59-469d-b2ef-1019d9ab246b");
migrationBuilder.AddColumn<string>(
name: "Address",
table: "AspNetUsers",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "BirthDate",
table: "AspNetUsers",
type: "datetime2",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "CompanyName",
table: "AspNetUsers",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Department",
table: "AspNetUsers",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "FirstName",
table: "AspNetUsers",
type: "nvarchar(max)",
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "LastName",
table: "AspNetUsers",
type: "nvarchar(max)",
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "ProfileImageUrl",
table: "AspNetUsers",
type: "nvarchar(max)",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Address",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "BirthDate",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "CompanyName",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "Department",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "FirstName",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "LastName",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "ProfileImageUrl",
table: "AspNetUsers");
migrationBuilder.InsertData(
table: "AspNetRoles",
columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
values: new object[,]
{
{ "a36da698-f5bc-4af3-bc25-81c675d52914", "1/7/2023 00:00:00", "Administrator", "ADMINISTRATOR" },
{ "ee45c015-8a59-469d-b2ef-1019d9ab246b", "1/7/2023 00:00:00", "User", "User" }
});
}
}
}

View File

@ -17,7 +17,7 @@ namespace phronCare.API.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "7.0.8") .HasAnnotation("ProductVersion", "8.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 128); .HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
@ -47,22 +47,6 @@ namespace phronCare.API.Migrations
.HasFilter("[NormalizedName] IS NOT NULL"); .HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null); b.ToTable("AspNetRoles", (string)null);
b.HasData(
new
{
Id = "a36da698-f5bc-4af3-bc25-81c675d52914",
ConcurrencyStamp = "1/7/2023 00:00:00",
Name = "Administrator",
NormalizedName = "ADMINISTRATOR"
},
new
{
Id = "ee45c015-8a59-469d-b2ef-1019d9ab246b",
ConcurrencyStamp = "1/7/2023 00:00:00",
Name = "User",
NormalizedName = "User"
});
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
@ -90,71 +74,6 @@ namespace phronCare.API.Migrations
b.ToTable("AspNetRoleClaims", (string)null); b.ToTable("AspNetRoleClaims", (string)null);
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@ -236,6 +155,94 @@ namespace phronCare.API.Migrations
b.ToTable("AspNetUserTokens", (string)null); b.ToTable("AspNetUserTokens", (string)null);
}); });
modelBuilder.Entity("phronCare.API.Models.Security.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("Address")
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("BirthDate")
.HasColumnType("datetime2");
b.Property<string>("CompanyName")
.HasColumnType("nvarchar(max)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Department")
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("ProfileImageUrl")
.HasColumnType("nvarchar(max)");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
@ -247,7 +254,7 @@ namespace phronCare.API.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) b.HasOne("phronCare.API.Models.Security.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
@ -256,7 +263,7 @@ namespace phronCare.API.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) b.HasOne("phronCare.API.Models.Security.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
@ -271,7 +278,7 @@ namespace phronCare.API.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) b.HasOne("phronCare.API.Models.Security.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
@ -280,7 +287,7 @@ namespace phronCare.API.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) b.HasOne("phronCare.API.Models.Security.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)

View File

@ -4,9 +4,33 @@ namespace phronCare.API.Models.Authentication.SingUp
{ {
public class RegisterUser public class RegisterUser
{ {
[Required(ErrorMessage ="El Username es un dato obligatorio")] public string UserName { get; set; } = string.Empty; [Required(ErrorMessage = "El Username es un dato obligatorio")]
[EmailAddress,Required(ErrorMessage = "El correo electronico es un dato obligatorio")] public string EmailAddress { get; set;} = string.Empty; public string UserName { get; set; } = string.Empty;
[Required(ErrorMessage = "La contraseña es un dato obligatorio")] public string Password { get; set; } = string.Empty;
[Required(ErrorMessage = "El rol es un dato obligatorio")] public string Role { get; set; } = string.Empty; [Required(ErrorMessage = "El correo electrónico es un dato obligatorio")]
[EmailAddress]
public string EmailAddress { get; set; } = string.Empty;
[Required(ErrorMessage = "La contraseña es un dato obligatorio")]
public string Password { get; set; } = string.Empty;
[Required(ErrorMessage = "El rol es un dato obligatorio")]
public string Role { get; set; } = string.Empty;
[Required(ErrorMessage = "El nombre es obligatorio")]
public string FirstName { get; set; } = string.Empty;
[Required(ErrorMessage = "El apellido es obligatorio")]
public string LastName { get; set; } = string.Empty;
public string? Address { get; set; }
public string? Department { get; set; }
public string? CompanyName { get; set; }
[DataType(DataType.Date)]
public DateTime? BirthDate { get; set; }
// Podés dejar esto para el futuro
public string? ProfileImageUrl { get; set; }
} }
} }

View File

@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Identity;
namespace phronCare.API.Models.Security
{
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string? Address { get; set; }
public string? Department { get; set; }
public string? CompanyName { get; set; }
public DateTime? BirthDate { get; set; }
// Nuevo campo para imagen
public string? ProfileImageUrl { get; set; }
// Propiedad calculada
public string FullName => $"{FirstName} {LastName}";
}
}

View File

@ -1,26 +1,59 @@
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using phronCare.API.Models.Security;
namespace phronCare.API.Models namespace phronCare.API.Models
{ {
public class phronCareDbContext: IdentityDbContext<IdentityUser> public class phronCareDbContext : IdentityDbContext<ApplicationUser>
{ {
public phronCareDbContext(DbContextOptions<phronCareDbContext> options):base(options) public phronCareDbContext(DbContextOptions<phronCareDbContext> options) : base(options)
{ {
} }
protected override void OnModelCreating(ModelBuilder builder) protected override void OnModelCreating(ModelBuilder builder)
{ {
base.OnModelCreating(builder); base.OnModelCreating(builder);
//SeedRoles(builder); //SeedRoles(builder);
} }
private static void SeedRoles(ModelBuilder modelBuilder) private static void SeedRoles(ModelBuilder modelBuilder)
{ {
modelBuilder.Entity<IdentityRole>().HasData modelBuilder.Entity<IdentityRole>().HasData
( (
new IdentityRole() { Name = "Administrator", ConcurrencyStamp = DateTime.Today.ToString(), NormalizedName = "administrator" }, new IdentityRole() { Name = "Administrator", ConcurrencyStamp = DateTime.Today.ToString(), NormalizedName = "administrator" },
new IdentityRole() { Name = "User", ConcurrencyStamp = DateTime.Today.ToString(), NormalizedName = "user" } new IdentityRole() { Name = "User", ConcurrencyStamp = DateTime.Today.ToString(), NormalizedName = "user" }
); );
} }
} }
} }
//using Microsoft.AspNetCore.Identity;
//using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
//using Microsoft.EntityFrameworkCore;
//namespace phronCare.API.Models
//{
// public class phronCareDbContext: IdentityDbContext<IdentityUser>
// {
// public phronCareDbContext(DbContextOptions<phronCareDbContext> options):base(options)
// {
// }
// protected override void OnModelCreating(ModelBuilder builder)
// {
// base.OnModelCreating(builder);
// //SeedRoles(builder);
// }
// private static void SeedRoles(ModelBuilder modelBuilder)
// {
// modelBuilder.Entity<IdentityRole>().HasData
// (
// new IdentityRole() { Name = "Administrator", ConcurrencyStamp = DateTime.Today.ToString(), NormalizedName = "administrator" },
// new IdentityRole() { Name = "User", ConcurrencyStamp = DateTime.Today.ToString(), NormalizedName = "user" }
// );
// }
// }
//}

View File

@ -1,22 +1,23 @@
using Core.Interfaces;
using Core.Services;
using Google.Authenticator; using Google.Authenticator;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Models.Repositories;
using Models.Interfaces; using Models.Interfaces;
using Models.Models; using Models.Models;
using Models.Repositories;
using phronCare.API.Models;
using Services.Models;
using Services.Services;
using Services.Interfaces; using Services.Interfaces;
using Services.Services;
using Services.Models;
using System.Text; using System.Text;
using Infrastructure.Repositories.Patients; using Infrastructure.Repositories.Patients;
using PhronCare.Core.Services.Sales;
using PhronCare.Core.Data.Repositories.Sales; using PhronCare.Core.Data.Repositories.Sales;
using PhronCare.Core.Services.Sales;
using phronCare.API.Models.Security;
using phronCare.API.Models;
using Core.Interfaces;
using Core.Services;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -34,54 +35,7 @@ builder.Services.AddDbContext<PhronCareOperationsHubContext>(options =>
#endregion #endregion
#region Repositorios y Servicios #region Repositorios y Servicios
builder.Services.AddScoped<ITicketDom, TicketService>(); RepositorysAndServices(builder);
builder.Services.AddScoped<ITicketRepository, TicketRepository>();
builder.Services.AddScoped<IAccountTypeDom, AccountTypeService>();
builder.Services.AddScoped<IPhSAccountTypeRepository, PhSAccountTypeRepository>();
builder.Services.AddScoped<ITaxConditionDom, TaxConditionService>();
builder.Services.AddScoped<IPhSTaxConditionRepository, PhOhTaxConditionRepository>();
builder.Services.AddScoped<IDocumentTypeDom, DocumentTypeService>();
builder.Services.AddScoped<IPhSDocumentTypeRepository, PhSDocumentTypeRepository>();
builder.Services.AddScoped<ICustomerDom, CustomerService>();
builder.Services.AddScoped<IPhSCustomerRepository, PhSCustomerRepository>();
builder.Services.AddScoped<IProductDom, ProductService>();
builder.Services.AddScoped<IPhSProductRepository, PhSProductRepository>();
builder.Services.AddScoped<ICustomerDom, CustomerService>();
builder.Services.AddScoped<IPhSCustomerRepository, PhSCustomerRepository>();
builder.Services.AddScoped<IProductCategoryDom, ProductCategoryService>();
builder.Services.AddScoped<IPhSProductCategoryRepository, PhSProductCategoryRepository>();
builder.Services.AddScoped<IBusinessUnitDom, BusinessUnitService>();
builder.Services.AddScoped<IPhSBusinessUnitRepository, PhSBusinessUnitRepository>();
builder.Services.AddScoped<IPatientDom, PatientService>();
builder.Services.AddScoped<IPhSPatientRepository, PhSPatientRepository>();
builder.Services.AddScoped<IProfessionalSpecialtyDom, ProfessionalSpecialtyService>();
builder.Services.AddScoped<IPhSProfessionalSpecialtyRepository, PhSProfessionalSpecialtyRepository>();
builder.Services.AddScoped<IProfessionalDom, ProfessionalService>();
builder.Services.AddScoped<IPhSProfessionalRepository, PhSProfessionalRepository>();
builder.Services.AddScoped<IInstitutionDom, InstitutionService>();
builder.Services.AddScoped<IPhSInstitutionRepository, PhSInstitutionRepository>();
builder.Services.AddScoped<IPeopleDom, PeopleService>();
builder.Services.AddScoped<IPhSPeopleRepository, PhSPeopleRepository>();
builder.Services.AddScoped<IQuoteDom, QuoteService>();
builder.Services.AddScoped<IPhSQuoteHeaderRepository, PhSQuoteHeaderRepository>();
builder.Services.AddScoped<IPhSQuoteDetailRepository, PhSQuoteDetailRepository>();
builder.Services.AddScoped<IPhSQuoteRoleRepository, PhSQuoteRoleRepository>();
builder.Services.AddScoped<IPhSFormSeriesRepository, PhSFormSeriesRepository>();
#endregion #endregion
@ -90,49 +44,60 @@ builder.Services.Configure<IdentityOptions>(opts => opts.SignIn.RequireConfirmed
#endregion #endregion
#region Security Identity EF Configuration #region Security Identity EF Configuration
builder.Services.AddIdentity<IdentityUser, IdentityRole>() builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<phronCareDbContext>() .AddEntityFrameworkStores<phronCareDbContext>()
.AddDefaultTokenProviders(); .AddDefaultTokenProviders();
builder.Services.Configure<DataProtectionTokenProviderOptions>( opts => opts.TokenLifespan=TimeSpan.FromHours(10));
builder.Services.Configure<DataProtectionTokenProviderOptions>(opts =>
opts.TokenLifespan = TimeSpan.FromHours(10));
builder.Services.AddSingleton<TwoFactorAuthenticator>(); builder.Services.AddSingleton<TwoFactorAuthenticator>();
#endregion #endregion
//#region Security Identity EF Configuration
//builder.Services.AddIdentity<IdentityUser, IdentityRole>()
// .AddEntityFrameworkStores<phronCareDbContext>()
// .AddDefaultTokenProviders();
//builder.Services.Configure<DataProtectionTokenProviderOptions>( opts => opts.TokenLifespan=TimeSpan.FromHours(10));
//builder.Services.AddSingleton<TwoFactorAuthenticator>();
//#endregion
#region Authentication Service #region Authentication Service
builder.Services.AddAuthentication(options => builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Audience = configuration["JWT:ValidAudience"];
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.IncludeErrorDetails = true;
options.TokenValidationParameters = new TokenValidationParameters
{ {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; ValidateIssuer = true,
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; ValidateAudience = true,
options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme; ValidateIssuerSigningKey = true,
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; ValidateLifetime = true,
}).AddJwtBearer(options => ValidIssuer = configuration["JWT:ValidIssuer"],
{ ValidAudience = configuration["JWT:ValidAudience"],
options.Audience = configuration["JWT:ValidAudience"]; IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(configuration["JWT:Secret"])),
options.RequireHttpsMetadata = false; ClockSkew = TimeSpan.Zero
options.SaveToken = true; };
options.IncludeErrorDetails = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
ValidIssuer = configuration["JWT:ValidIssuer"],
ValidAudience = configuration["JWT:ValidAudience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(configuration["JWT:Secret"])),
ClockSkew = TimeSpan.Zero
};
options.Events = new JwtBearerEvents options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{ {
OnAuthenticationFailed = context => var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger("JWTDebug");
{ logger.LogError(context.Exception, "Error de autenticación JWT");
var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger("JWTDebug"); return Task.CompletedTask;
logger.LogError(context.Exception, "Error de autenticación JWT"); }
return Task.CompletedTask; };
} });
};
});
#endregion #endregion
#region Email Configuration #region Email Configuration
@ -185,7 +150,7 @@ builder.Services.AddSwaggerGen(option =>
// })); // }));
builder.Services.AddCors(options => builder.Services.AddCors(options =>
{ {
options.AddPolicy("CORS", policy => options.AddPolicy("CORS", policy =>
{ {
/* /*
@ -216,8 +181,8 @@ var app = builder.Build();
//if (app.Environment.IsDevelopment()) //if (app.Environment.IsDevelopment())
//{ //{
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(); app.UseSwaggerUI();
//} //}
app.UseCors("CORS"); app.UseCors("CORS");
@ -226,4 +191,56 @@ app.UseAuthorization();
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.MapControllers(); app.MapControllers();
app.Run(); app.Run();
static void RepositorysAndServices(WebApplicationBuilder builder)
{
builder.Services.AddScoped<ITicketDom, TicketService>();
builder.Services.AddScoped<ITicketRepository, TicketRepository>();
builder.Services.AddScoped<IAccountTypeDom, AccountTypeService>();
builder.Services.AddScoped<IPhSAccountTypeRepository, PhSAccountTypeRepository>();
builder.Services.AddScoped<ITaxConditionDom, TaxConditionService>();
builder.Services.AddScoped<IPhSTaxConditionRepository, PhOhTaxConditionRepository>();
builder.Services.AddScoped<IDocumentTypeDom, DocumentTypeService>();
builder.Services.AddScoped<IPhSDocumentTypeRepository, PhSDocumentTypeRepository>();
builder.Services.AddScoped<ICustomerDom, CustomerService>();
builder.Services.AddScoped<IPhSCustomerRepository, PhSCustomerRepository>();
builder.Services.AddScoped<IProductDom, ProductService>();
builder.Services.AddScoped<IPhSProductRepository, PhSProductRepository>();
builder.Services.AddScoped<ICustomerDom, CustomerService>();
builder.Services.AddScoped<IPhSCustomerRepository, PhSCustomerRepository>();
builder.Services.AddScoped<IProductCategoryDom, ProductCategoryService>();
builder.Services.AddScoped<IPhSProductCategoryRepository, PhSProductCategoryRepository>();
builder.Services.AddScoped<IBusinessUnitDom, BusinessUnitService>();
builder.Services.AddScoped<IPhSBusinessUnitRepository, PhSBusinessUnitRepository>();
builder.Services.AddScoped<IPatientDom, PatientService>();
builder.Services.AddScoped<IPhSPatientRepository, PhSPatientRepository>();
builder.Services.AddScoped<IProfessionalSpecialtyDom, ProfessionalSpecialtyService>();
builder.Services.AddScoped<IPhSProfessionalSpecialtyRepository, PhSProfessionalSpecialtyRepository>();
builder.Services.AddScoped<IProfessionalDom, ProfessionalService>();
builder.Services.AddScoped<IPhSProfessionalRepository, PhSProfessionalRepository>();
builder.Services.AddScoped<IInstitutionDom, InstitutionService>();
builder.Services.AddScoped<IPhSInstitutionRepository, PhSInstitutionRepository>();
builder.Services.AddScoped<IPeopleDom, PeopleService>();
builder.Services.AddScoped<IPhSPeopleRepository, PhSPeopleRepository>();
builder.Services.AddScoped<IQuoteDom, QuoteService>();
builder.Services.AddScoped<IPhSQuoteHeaderRepository, PhSQuoteHeaderRepository>();
builder.Services.AddScoped<IPhSQuoteDetailRepository, PhSQuoteDetailRepository>();
builder.Services.AddScoped<IPhSQuoteRoleRepository, PhSQuoteRoleRepository>();
builder.Services.AddScoped<IPhSFormSeriesRepository, PhSFormSeriesRepository>();
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>