From 4a7a713cf9c9bbd6b188a3fb0a383c4a27fffe87 Mon Sep 17 00:00:00 2001 From: jvyden Date: Mon, 13 Dec 2021 15:59:33 -0500 Subject: [PATCH] Add ability to approve an IP address --- .../ExternalAuth/AutoApprovalController.cs | 67 +++++++++++++++++++ ProjectLighthouse/Database.cs | 2 +- ...211213195540_AddUserApprovedIpAddresses.cs | 50 ++++++++++++++ .../Migrations/DatabaseModelSnapshot.cs | 30 +++++++++ .../ExternalAuth/AuthenticationPage.cshtml | 26 +++---- .../ManageUserApprovedIpAddressesPage.cshtml | 22 ++++++ ...anageUserApprovedIpAddressesPage.cshtml.cs | 29 ++++++++ .../Types/UserApprovedIpAddress.cs | 18 +++++ 8 files changed, 230 insertions(+), 14 deletions(-) create mode 100644 ProjectLighthouse/Controllers/Website/ExternalAuth/AutoApprovalController.cs create mode 100644 ProjectLighthouse/Migrations/20211213195540_AddUserApprovedIpAddresses.cs create mode 100644 ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml create mode 100644 ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs create mode 100644 ProjectLighthouse/Types/UserApprovedIpAddress.cs diff --git a/ProjectLighthouse/Controllers/Website/ExternalAuth/AutoApprovalController.cs b/ProjectLighthouse/Controllers/Website/ExternalAuth/AutoApprovalController.cs new file mode 100644 index 00000000..f801b055 --- /dev/null +++ b/ProjectLighthouse/Controllers/Website/ExternalAuth/AutoApprovalController.cs @@ -0,0 +1,67 @@ +#nullable enable +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth +{ + [ApiController] + [Route("/authentication")] + public class AutoApprovalController : ControllerBase + { + private readonly Database database; + + public AutoApprovalController(Database database) + { + this.database = database; + } + + [HttpGet("autoApprove/{id:int}")] + public async Task AutoApprove([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("/login"); + + AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include + (a => a.GameToken) + .FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id); + + if (authAttempt == null) return this.BadRequest(); + if (authAttempt.GameToken.UserId != user.UserId) return this.Redirect("/login"); + + authAttempt.GameToken.Approved = true; + + UserApprovedIpAddress approvedIpAddress = new() + { + UserId = user.UserId, + User = user, + IpAddress = authAttempt.IPAddress, + }; + + this.database.UserApprovedIpAddresses.Add(approvedIpAddress); + this.database.AuthenticationAttempts.Remove(authAttempt); + + await this.database.SaveChangesAsync(); + + return this.Redirect("/authentication"); + } + + [HttpGet("revokeAutoApproval/{id:int}")] + public async Task RevokeAutoApproval([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("/login"); + + UserApprovedIpAddress? approvedIpAddress = await this.database.UserApprovedIpAddresses.FirstOrDefaultAsync(a => a.UserApprovedIpAddressId == id); + if (approvedIpAddress == null) return this.BadRequest(); + if (approvedIpAddress.UserId != user.UserId) return this.Redirect("/login"); + + this.database.UserApprovedIpAddresses.Remove(approvedIpAddress); + + await this.database.SaveChangesAsync(); + + return this.Redirect("/authentication/autoApprovals"); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs index 2af08e30..9fda3cab 100644 --- a/ProjectLighthouse/Database.cs +++ b/ProjectLighthouse/Database.cs @@ -34,6 +34,7 @@ namespace LBPUnion.ProjectLighthouse public DbSet AuthenticationAttempts { get; set; } public DbSet Reviews { get; set; } public DbSet RatedReviews { get; set; } + public DbSet UserApprovedIpAddresses { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseMySql(ServerSettings.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion); @@ -66,7 +67,6 @@ namespace LBPUnion.ProjectLighthouse #nullable enable public async Task AuthenticateUser(LoginData loginData, string userLocation, string titleId = "") { - // TODO: don't use psn name to authenticate User? user = await this.Users.FirstOrDefaultAsync(u => u.Username == loginData.Username); if (user == null) return null; diff --git a/ProjectLighthouse/Migrations/20211213195540_AddUserApprovedIpAddresses.cs b/ProjectLighthouse/Migrations/20211213195540_AddUserApprovedIpAddresses.cs new file mode 100644 index 00000000..4f9264df --- /dev/null +++ b/ProjectLighthouse/Migrations/20211213195540_AddUserApprovedIpAddresses.cs @@ -0,0 +1,50 @@ +using LBPUnion.ProjectLighthouse; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20211213195540_AddUserApprovedIpAddresses")] + public partial class AddUserApprovedIpAddresses : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "UserApprovedIpAddresses", + columns: table => new + { + UserApprovedIpAddressId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "int", nullable: false), + IpAddress = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_UserApprovedIpAddresses", x => x.UserApprovedIpAddressId); + table.ForeignKey( + name: "FK_UserApprovedIpAddresses_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "UserId", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_UserApprovedIpAddresses_UserId", + table: "UserApprovedIpAddresses", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "UserApprovedIpAddresses"); + } + } +} diff --git a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs index 4afc7b51..64dfac1f 100644 --- a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs +++ b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs @@ -587,6 +587,25 @@ namespace ProjectLighthouse.Migrations b.ToTable("Users"); }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.UserApprovedIpAddress", b => + { + b.Property("UserApprovedIpAddressId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("IpAddress") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("UserApprovedIpAddressId"); + + b.HasIndex("UserId"); + + b.ToTable("UserApprovedIpAddresses"); + }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => { b.Property("TokenId") @@ -829,6 +848,17 @@ namespace ProjectLighthouse.Migrations b.Navigation("Location"); }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.UserApprovedIpAddress", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); #pragma warning restore 612, 618 } } diff --git a/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml b/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml index 97278a6a..d5a56773 100644 --- a/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml +++ b/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml @@ -18,21 +18,21 @@ else {

This device's IP address is @(Model.IpAddress.ToString()). If this matches with an authentication attempt below, then it's safe to assume the authentication attempt came from the same network as this device.

} - - - - - - - } + + + + + + + @foreach (AuthenticationAttempt authAttempt in Model.AuthenticationAttempts) { DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(authAttempt.Timestamp); diff --git a/ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml b/ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml new file mode 100644 index 00000000..dde2fa8a --- /dev/null +++ b/ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml @@ -0,0 +1,22 @@ +@page "/authentication/autoApprovals" +@using LBPUnion.ProjectLighthouse.Types +@model LBPUnion.ProjectLighthouse.Pages.ExternalAuth.ManageUserApprovedIpAddressesPage + +@{ + Layout = "Layouts/BaseLayout"; + Model.Title = "Automatically approved IP addresses"; +} + + +@foreach (UserApprovedIpAddress approvedIpAddress in Model.ApprovedIpAddresses) +{ +
+

@approvedIpAddress.IpAddress

+ + + +
+} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs b/ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs new file mode 100644 index 00000000..00dfa3be --- /dev/null +++ b/ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs @@ -0,0 +1,29 @@ +#nullable enable +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth +{ + public class ManageUserApprovedIpAddressesPage : BaseLayout + { + public ManageUserApprovedIpAddressesPage(Database database) : base(database) + {} + + public List ApprovedIpAddresses; + + public async Task OnGet() + { + User? user = this.Database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("/login"); + + this.ApprovedIpAddresses = await this.Database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).ToListAsync(); + + return this.Page(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/UserApprovedIpAddress.cs b/ProjectLighthouse/Types/UserApprovedIpAddress.cs new file mode 100644 index 00000000..06c84183 --- /dev/null +++ b/ProjectLighthouse/Types/UserApprovedIpAddress.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LBPUnion.ProjectLighthouse.Types +{ + public class UserApprovedIpAddress + { + [Key] + public int UserApprovedIpAddressId { get; set; } + + public int UserId { get; set; } + + [ForeignKey(nameof(UserId))] + public User User { get; set; } + + public string IpAddress { get; set; } + } +} \ No newline at end of file