From a640608ab8ff895be3125ebf75a4d3d19139f5c3 Mon Sep 17 00:00:00 2001 From: HombreLaser Date: Fri, 18 Nov 2022 16:32:38 -0600 Subject: AƱadido login de administradores MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Controllers/AdministratorSessionsController.cs | 34 +++++++++++++++++++ Controllers/WeatherForecastController.cs | 32 ------------------ Errors/ErrorBase.cs | 11 +++++++ Errors/InvalidLoginError.cs | 5 +++ Forms/AuthenticationToken.cs | 6 ++++ Forms/UserAccountLoginForm.cs | 10 ++++++ Logics/CreateAdministratorSessionLogic.cs | 43 +++++++++++++++++++++++++ Migrations/ApplicationDbContextModelSnapshot.cs | 19 ++++++----- Models/ApplicationDbContext.cs | 12 +++---- Services/TokenGenerator.cs | 2 +- 10 files changed, 127 insertions(+), 47 deletions(-) create mode 100644 Controllers/AdministratorSessionsController.cs delete mode 100644 Controllers/WeatherForecastController.cs create mode 100644 Errors/ErrorBase.cs create mode 100644 Errors/InvalidLoginError.cs create mode 100644 Forms/AuthenticationToken.cs create mode 100644 Forms/UserAccountLoginForm.cs create mode 100644 Logics/CreateAdministratorSessionLogic.cs diff --git a/Controllers/AdministratorSessionsController.cs b/Controllers/AdministratorSessionsController.cs new file mode 100644 index 0000000..2ecf5e0 --- /dev/null +++ b/Controllers/AdministratorSessionsController.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using BackendPIA.Forms; +using BackendPIA.Models; +using BackendPIA.Services; +using BackendPIA.Errors; +using BackendPIA.Logics; + +namespace BackendPIA.Controllers { + [Route("api/admin")] + [ApiController] + public class AdministratorSessionsController : ControllerBase { + private readonly ApplicationDbContext _context; + private readonly ITokenGenerator _token_generator; + private readonly UserManager _manager; + + public AdministratorSessionsController(ApplicationDbContext context, ITokenGenerator token_generator, UserManager manager) { + _context = context; + _token_generator = token_generator; + _manager = manager; + } + + [HttpPost("login")] + public async Task> Create(UserAccountLoginForm form) { + CreateAdministratorSessionLogic logic = new CreateAdministratorSessionLogic(_token_generator, _manager, form); + var result = await logic.Call(); + + if(result) + return Ok(logic.Token); + + return StatusCode(401, new InvalidLoginError(401, "Check your credentials")); + } + } +} \ No newline at end of file diff --git a/Controllers/WeatherForecastController.cs b/Controllers/WeatherForecastController.cs deleted file mode 100644 index e1ba5c4..0000000 --- a/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace BackendPIA.Controllers; - -[ApiController] -[Route("[controller]")] -public class WeatherForecastController : ControllerBase -{ - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }) - .ToArray(); - } -} diff --git a/Errors/ErrorBase.cs b/Errors/ErrorBase.cs new file mode 100644 index 0000000..e82e932 --- /dev/null +++ b/Errors/ErrorBase.cs @@ -0,0 +1,11 @@ +namespace BackendPIA.Errors { + public abstract class ErrorBase { + protected int Status { get; } + protected string Message { get; } + + public ErrorBase(int status, string message) { + Status = status; + Message = message; + } + } +} \ No newline at end of file diff --git a/Errors/InvalidLoginError.cs b/Errors/InvalidLoginError.cs new file mode 100644 index 0000000..c776d00 --- /dev/null +++ b/Errors/InvalidLoginError.cs @@ -0,0 +1,5 @@ +namespace BackendPIA.Errors { + public class InvalidLoginError : ErrorBase { + public InvalidLoginError(int status, string message) : base(status, message) {} + } +} \ No newline at end of file diff --git a/Forms/AuthenticationToken.cs b/Forms/AuthenticationToken.cs new file mode 100644 index 0000000..099080b --- /dev/null +++ b/Forms/AuthenticationToken.cs @@ -0,0 +1,6 @@ +namespace BackendPIA.Forms { + public class AuthenticationToken { + public string Token { get; set; } + public string RefreshToken { get; set; } + } +} \ No newline at end of file diff --git a/Forms/UserAccountLoginForm.cs b/Forms/UserAccountLoginForm.cs new file mode 100644 index 0000000..a19698d --- /dev/null +++ b/Forms/UserAccountLoginForm.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace BackendPIA.Forms { + public class UserAccountLoginForm { + [Required] + public string? Email { get; set; } + [Required] + public string? Password { get; set; } + } +} \ No newline at end of file diff --git a/Logics/CreateAdministratorSessionLogic.cs b/Logics/CreateAdministratorSessionLogic.cs new file mode 100644 index 0000000..a7e1860 --- /dev/null +++ b/Logics/CreateAdministratorSessionLogic.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Identity; +using BackendPIA.Services; +using BackendPIA.Models; +using BackendPIA.Forms; + +namespace BackendPIA.Logics { + public class CreateAdministratorSessionLogic { + private readonly ITokenGenerator _token_generator; + private readonly UserManager _manager; + private readonly UserAccountLoginForm _form; + private AuthenticationToken _token; + + public AuthenticationToken Token { get { return _token; } } + + public CreateAdministratorSessionLogic(ITokenGenerator token_generator, UserManager manager, UserAccountLoginForm form) { + _token_generator = token_generator; + _manager = manager; + _form = form; + } + + public async Task Call() { + var user = await _manager.FindByEmailAsync(_form.Email); + + if(user == null) + return false; + + var result = await _manager.CheckPasswordAsync(user, _form.Password); + + if(result) { + _token = new AuthenticationToken { Token = _token_generator.Generate(user, "administrator"), + RefreshToken = _token_generator.GenerateRefreshToken() }; + // We overwrite or set the value of the session token in the database: all other previous logins are invalid. + user.SessionToken = _token.RefreshToken; + user.SessionTokenExpiryTime = DateTime.UtcNow.AddHours(3); + await _manager.UpdateAsync(user); + + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/Migrations/ApplicationDbContextModelSnapshot.cs b/Migrations/ApplicationDbContextModelSnapshot.cs index be690de..5a233a6 100644 --- a/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Migrations/ApplicationDbContextModelSnapshot.cs @@ -71,6 +71,9 @@ namespace BackendPIA.Migrations .HasMaxLength(64) .HasColumnType("character varying(64)"); + b.Property("SessionTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + b.Property("TwoFactorEnabled") .HasColumnType("boolean"); @@ -92,17 +95,17 @@ namespace BackendPIA.Migrations b.HasData( new { - Id = "d42006bc-7f69-4aa4-b247-eb9e2abfe0ec", + Id = "24edc3d6-bf9c-41a1-9371-224e4419ccb0", AccessFailedCount = 0, - ConcurrencyStamp = "8bbb8fce-308b-4822-97e1-5741fc955a90", + ConcurrencyStamp = "bd624bcb-3f06-4bce-b924-2666f82e5f23", Email = "admin@example.com", EmailConfirmed = false, LockoutEnabled = false, NormalizedEmail = "ADMIN@EXAMPLE.COM", NormalizedUserName = "ADMIN", - PasswordHash = "AQAAAAIAAYagAAAAEENVY01/0BOrBai8zaioq9GOr+ftYIZhUBtulPtda1tTREUCOeVst9cnrB7Ogz4Bsg==", + PasswordHash = "AQAAAAIAAYagAAAAEL19rXYOEkR3ftL+T5E5vlsLGPu3HSnJuTSLNp/nyffvQvaXlNJFqU1UO3VKB+K6yg==", PhoneNumberConfirmed = false, - SecurityStamp = "5a1e1053-690e-4610-ab66-7a86fe2e04c8", + SecurityStamp = "282566ca-8a3b-4310-8e61-8380d16fa07e", TwoFactorEnabled = false, UserName = "admin" }); @@ -136,8 +139,8 @@ namespace BackendPIA.Migrations b.HasData( new { - Id = "24edc3d6-bf9c-41a1-9371-224e4419ccb0", - ConcurrencyStamp = "24edc3d6-bf9c-41a1-9371-224e4419ccb0", + Id = "d42006bc-7f69-4aa4-b247-eb9e2abfe0ec", + ConcurrencyStamp = "d42006bc-7f69-4aa4-b247-eb9e2abfe0ec", Name = "Administrator", NormalizedName = "ADMINISTRATOR" }); @@ -232,8 +235,8 @@ namespace BackendPIA.Migrations b.HasData( new { - UserId = "d42006bc-7f69-4aa4-b247-eb9e2abfe0ec", - RoleId = "24edc3d6-bf9c-41a1-9371-224e4419ccb0" + UserId = "24edc3d6-bf9c-41a1-9371-224e4419ccb0", + RoleId = "d42006bc-7f69-4aa4-b247-eb9e2abfe0ec" }); }); diff --git a/Models/ApplicationDbContext.cs b/Models/ApplicationDbContext.cs index d2b8d26..eda476c 100644 --- a/Models/ApplicationDbContext.cs +++ b/Models/ApplicationDbContext.cs @@ -11,10 +11,10 @@ namespace BackendPIA.Models { protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); - Guid user_id = Guid.NewGuid(); - Guid role_id = Guid.NewGuid(); + string user_id = "24edc3d6-bf9c-41a1-9371-224e4419ccb0"; + string role_id = "d42006bc-7f69-4aa4-b247-eb9e2abfe0ec"; var hasher = new PasswordHasher(); - UserAccount user_seed = new UserAccount { Id = user_id.ToString(), UserName = "admin", Email = "admin@example.com", + UserAccount user_seed = new UserAccount { Id = user_id, UserName = "admin", Email = "admin@example.com", NormalizedEmail = "ADMIN@EXAMPLE.COM", NormalizedUserName = "ADMIN" }; // TODO: save the seeded admin password in a user secret. user_seed.PasswordHash = hasher.HashPassword(user_seed, "admin_password"); @@ -22,10 +22,10 @@ namespace BackendPIA.Models { builder.Entity().HasData(new IdentityRole { Name = "Administrator", NormalizedName = "ADMINISTRATOR", - Id = role_id.ToString(), - ConcurrencyStamp = role_id.ToString() + Id = role_id, + ConcurrencyStamp = role_id }); - builder.Entity>().HasData(new IdentityUserRole { UserId = user_id.ToString(), RoleId = role_id.ToString() }); + builder.Entity>().HasData(new IdentityUserRole { UserId = user_id, RoleId = role_id }); } } } diff --git a/Services/TokenGenerator.cs b/Services/TokenGenerator.cs index 7d9d1ec..32418cd 100644 --- a/Services/TokenGenerator.cs +++ b/Services/TokenGenerator.cs @@ -16,7 +16,7 @@ namespace BackendPIA.Services { public string Generate(UserAccount user, string role) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_key)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); - var expiration = DateTime.UtcNow.AddMinutes(30); + var expiration = DateTime.UtcNow.AddMinutes(20); //var issuer = _configuration["Jwt:Issuer"]; var claims = new List { new Claim("sid", user.Id), -- cgit v1.2.3