summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Controllers/AdministratorSessionsController.cs34
-rw-r--r--Controllers/WeatherForecastController.cs32
-rw-r--r--Errors/ErrorBase.cs11
-rw-r--r--Errors/InvalidLoginError.cs5
-rw-r--r--Forms/AuthenticationToken.cs6
-rw-r--r--Forms/UserAccountLoginForm.cs10
-rw-r--r--Logics/CreateAdministratorSessionLogic.cs43
-rw-r--r--Migrations/ApplicationDbContextModelSnapshot.cs19
-rw-r--r--Models/ApplicationDbContext.cs12
-rw-r--r--Services/TokenGenerator.cs2
10 files changed, 127 insertions, 47 deletions
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<UserAccount> _manager;
+
+ public AdministratorSessionsController(ApplicationDbContext context, ITokenGenerator token_generator, UserManager<UserAccount> manager) {
+ _context = context;
+ _token_generator = token_generator;
+ _manager = manager;
+ }
+
+ [HttpPost("login")]
+ public async Task<ActionResult<AuthenticationToken>> 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<WeatherForecastController> _logger;
-
- public WeatherForecastController(ILogger<WeatherForecastController> logger)
- {
- _logger = logger;
- }
-
- [HttpGet(Name = "GetWeatherForecast")]
- public IEnumerable<WeatherForecast> 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<UserAccount> _manager;
+ private readonly UserAccountLoginForm _form;
+ private AuthenticationToken _token;
+
+ public AuthenticationToken Token { get { return _token; } }
+
+ public CreateAdministratorSessionLogic(ITokenGenerator token_generator, UserManager<UserAccount> manager, UserAccountLoginForm form) {
+ _token_generator = token_generator;
+ _manager = manager;
+ _form = form;
+ }
+
+ public async Task<bool> 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<DateTime?>("SessionTokenExpiryTime")
+ .HasColumnType("timestamp with time zone");
+
b.Property<bool>("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>();
- 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<IdentityRole>().HasData(new IdentityRole {
Name = "Administrator",
NormalizedName = "ADMINISTRATOR",
- Id = role_id.ToString(),
- ConcurrencyStamp = role_id.ToString()
+ Id = role_id,
+ ConcurrencyStamp = role_id
});
- builder.Entity<IdentityUserRole<string>>().HasData(new IdentityUserRole<string> { UserId = user_id.ToString(), RoleId = role_id.ToString() });
+ builder.Entity<IdentityUserRole<string>>().HasData(new IdentityUserRole<string> { 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<Claim> {
new Claim("sid", user.Id),