diff --git a/backend/ApplicationState.cs b/backend/ApplicationState.cs index 7371687..1fb72ae 100644 --- a/backend/ApplicationState.cs +++ b/backend/ApplicationState.cs @@ -6,6 +6,6 @@ namespace backend.Application public static class ApplicationState { public static IMqttClient? MqttClient { get; set; } - public static DbContext? DbContext { get; set; } + public static DispenserContext? DbContext { get; set; } } } diff --git a/backend/Controllers/UserController.cs b/backend/Controllers/UserController.cs index 7a62422..163ca91 100644 --- a/backend/Controllers/UserController.cs +++ b/backend/Controllers/UserController.cs @@ -3,38 +3,82 @@ using backend.Application; using backend.Models; using System.Text.Json.Nodes; using Microsoft.AspNetCore.Identity; +using System.Web; namespace backend.Controllers; [ApiController] public class UserController : ControllerBase { - [HttpPost("Register")] - public IActionResult Register([FromBody] JsonObject input) - { - if (input["username"] == null || input["password"] == null) { - return BadRequest("Username and password required"); - } + [HttpPost("Register")] + public IActionResult Register([FromBody] JsonObject input) + { + // Validate + if (String.IsNullOrEmpty(input["username"]?.ToString()) || String.IsNullOrEmpty(input["password"]?.ToString())) { + return BadRequest("Username and password required"); + } - var passwordHasher = new PasswordHasher(); - string hashedPassword = passwordHasher.HashPassword(null, input["password"]!.ToString()); + // Hash password + var passwordHasher = new PasswordHasher(); + string hashedPassword = passwordHasher.HashPassword(null, input["password"]!.ToString()); - string touchCode = ""; - for (int i = 0; i < 4; i++) { - touchCode += (1 + new Random().Next() % 5).ToString(); - } + // Generate touch code + string touchCode = ""; + for (int i = 0; i < 4; i++) { + touchCode += (1 + new Random().Next() % 5).ToString(); + } - var user = new User { - Username = input["username"]!.ToString(), - Password = hashedPassword, - TouchCode = touchCode, - }; + // Create user + var user = new User { + Username = input["username"]!.ToString(), + Password = hashedPassword, + TouchCode = touchCode, + }; + // Save user ApplicationState.DbContext!.Add(user); ApplicationState.DbContext!.SaveChanges(); - Console.WriteLine("Created user: " + user.Username); + Console.WriteLine("Created user: " + user.Username); - return Ok(); - } + return Ok(); + } + + [HttpPost("Login")] + public IActionResult Login([FromBody] JsonObject input) + { + // Validate + if (String.IsNullOrEmpty(input["username"]?.ToString()) || String.IsNullOrEmpty(input["password"]?.ToString())) { + return BadRequest("Username and password required"); + } + + if (Request.Cookies["session"] != null) { + return BadRequest("You are already logged in"); + } + + // Get user + var user = ApplicationState.DbContext!.Users.FirstOrDefault(user => user.Username == input["username"]!.ToString()); + if (user == null) { + return BadRequest("Invalid username"); + } + + // Verify password + var passwordHasher = new PasswordHasher(); + if (passwordHasher.VerifyHashedPassword(null, user.Password, input["password"]!.ToString()) == PasswordVerificationResult.Failed) { + return BadRequest("Invalid password"); + } + + // Create session token if necessary + if (string.IsNullOrEmpty(user.SessionToken)) { + user.SessionToken = Guid.NewGuid().ToString(); + ApplicationState.DbContext!.SaveChanges(); + } + + // Set session cookie + Response.Cookies.Append("session", user.SessionToken); + + Console.WriteLine(user.Username + " has logged in"); + + return Ok(); + } } diff --git a/backend/Migrations/20231207234050_AddSessionTokenToUser.Designer.cs b/backend/Migrations/20231207234050_AddSessionTokenToUser.Designer.cs new file mode 100644 index 0000000..692fa69 --- /dev/null +++ b/backend/Migrations/20231207234050_AddSessionTokenToUser.Designer.cs @@ -0,0 +1,64 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace backend.Migrations +{ + [DbContext(typeof(DispenserContext))] + [Migration("20231207234050_AddSessionTokenToUser")] + partial class AddSessionTokenToUser + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("backend.Models.DispenserLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("DispenserLogs"); + }); + + modelBuilder.Entity("backend.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SessionToken") + .HasColumnType("TEXT"); + + b.Property("TouchCode") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/Migrations/20231207234050_AddSessionTokenToUser.cs b/backend/Migrations/20231207234050_AddSessionTokenToUser.cs new file mode 100644 index 0000000..9882c23 --- /dev/null +++ b/backend/Migrations/20231207234050_AddSessionTokenToUser.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace backend.Migrations +{ + /// + public partial class AddSessionTokenToUser : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SessionToken", + table: "Users", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SessionToken", + table: "Users"); + } + } +} diff --git a/backend/Migrations/DispenserContextModelSnapshot.cs b/backend/Migrations/DispenserContextModelSnapshot.cs index 934fa00..5b0fb43 100644 --- a/backend/Migrations/DispenserContextModelSnapshot.cs +++ b/backend/Migrations/DispenserContextModelSnapshot.cs @@ -40,6 +40,9 @@ namespace backend.Migrations .IsRequired() .HasColumnType("TEXT"); + b.Property("SessionToken") + .HasColumnType("TEXT"); + b.Property("TouchCode") .IsRequired() .HasColumnType("TEXT"); diff --git a/backend/Models/User.cs b/backend/Models/User.cs index f251a7e..9b29242 100644 --- a/backend/Models/User.cs +++ b/backend/Models/User.cs @@ -12,4 +12,5 @@ public class User public string Username { get; set; } public string Password { get; set; } public string TouchCode { get; set; } + public string? SessionToken { get; set; } }