From 1bea8f9017d396203566330040712fdf0def32b4 Mon Sep 17 00:00:00 2001 From: LumaLivy <7350336+LumaLivy@users.noreply.github.com> Date: Sat, 20 Nov 2021 04:23:42 -0500 Subject: [PATCH 01/18] Add most of the remaining community slot categories --- .../Controllers/SlotsController.cs | 139 +++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/ProjectLighthouse/Controllers/SlotsController.cs b/ProjectLighthouse/Controllers/SlotsController.cs index 2ded22b4..a8be7e87 100644 --- a/ProjectLighthouse/Controllers/SlotsController.cs +++ b/ProjectLighthouse/Controllers/SlotsController.cs @@ -92,7 +92,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers [HttpGet("slots/lbp2cool")] [HttpGet("slots/cool")] - public async Task CoolSlots([FromQuery] int page) => await LuckyDipSlots(30 * page, 30, 69); + public async Task CoolSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] int? page = null) + { + int _pageStart = pageStart; + if (page != null) _pageStart = (int)page * 30; + // bit of a better placeholder until we can track average user interaction with /stream endpoint + return await ThumbsSlots(_pageStart, Math.Min(pageSize, 30), gameFilterType, players, move, "thisWeek"); + } [HttpGet("slots")] public async Task NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) @@ -152,5 +158,136 @@ namespace LBPUnion.ProjectLighthouse.Controllers return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30))); } + [HttpGet("slots/thumbs")] + public async Task ThumbsSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null) + { + // v--- not sure of API in LBP3 here, needs testing + GameVersion gameVersion = gameFilterType == "both" ? GameVersion.LittleBigPlanet2 : GameVersion.LittleBigPlanet1; + + long oldestTime; + + string _dateFilterType = dateFilterType != null ? dateFilterType : ""; + + switch (_dateFilterType) + { + case "thisWeek": + oldestTime = DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(); + break; + case "thisMonth": + oldestTime = DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(); + break; + default: + oldestTime = 0; + break; + } + + Random rand = new(); + + IEnumerable slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime) + .Include(s => s.Creator) + .Include(s => s.Location) + .AsEnumerable() + .OrderByDescending(s => s.Thumbsup) + .ThenBy(_ => rand.Next()) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)); + + string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize()); + + return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30))); + } + + [HttpGet("slots/mostUniquePlays")] + public async Task MostUniquePlaysSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null) + { + // v--- not sure of API in LBP3 here, needs testing + GameVersion gameVersion = gameFilterType == "both" ? GameVersion.LittleBigPlanet2 : GameVersion.LittleBigPlanet1; + + long oldestTime; + + string _dateFilterType = dateFilterType != null ? dateFilterType : ""; + + switch (_dateFilterType) + { + case "thisWeek": + oldestTime = DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(); + break; + case "thisMonth": + oldestTime = DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(); + break; + default: + oldestTime = 0; + break; + } + + Random rand = new(); + + IEnumerable slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime) + .Include(s => s.Creator) + .Include(s => s.Location) + .AsEnumerable() + .OrderByDescending(s => { + // probably not the best way to do this + switch (gameVersion) { + case GameVersion.LittleBigPlanet1: + return s.PlaysLBP1Unique; + case GameVersion.LittleBigPlanet2: + return s.PlaysLBP2Unique; + case GameVersion.LittleBigPlanet3: + return s.PlaysLBP3Unique; + case GameVersion.LittleBigPlanetVita: + return s.PlaysLBPVitaUnique; + default: + return s.PlaysUnique; + } + }) + .ThenBy(_ => rand.Next()) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)); + + string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize()); + + return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30))); + } + + [HttpGet("slots/mostHearted")] + public async Task MostHeartedSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null) + { + // v--- not sure of API in LBP3 here, needs testing + GameVersion gameVersion = gameFilterType == "both" ? GameVersion.LittleBigPlanet2 : GameVersion.LittleBigPlanet1; + + long oldestTime; + + string _dateFilterType = dateFilterType != null ? dateFilterType : ""; + + switch (_dateFilterType) + { + case "thisWeek": + oldestTime = DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(); + break; + case "thisMonth": + oldestTime = DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(); + break; + default: + oldestTime = 0; + break; + } + + Random rand = new(); + + IEnumerable slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime) + .Include(s => s.Creator) + .Include(s => s.Location) + .AsEnumerable() + .OrderByDescending(s => s.Hearts) + .ThenBy(_ => rand.Next()) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)); + + string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize()); + + return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30))); + } + } } \ No newline at end of file From 62aafc91083d7c8f49f8a411998978f454bf6d86 Mon Sep 17 00:00:00 2001 From: LumaLivy <7350336+LumaLivy@users.noreply.github.com> Date: Tue, 14 Dec 2021 14:12:49 -0500 Subject: [PATCH 02/18] Use better gameVersion checking when filtering --- .../Controllers/SlotsController.cs | 120 ++++++++++++++++-- 1 file changed, 108 insertions(+), 12 deletions(-) diff --git a/ProjectLighthouse/Controllers/SlotsController.cs b/ProjectLighthouse/Controllers/SlotsController.cs index 6e104235..55923f40 100644 --- a/ProjectLighthouse/Controllers/SlotsController.cs +++ b/ProjectLighthouse/Controllers/SlotsController.cs @@ -208,8 +208,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers [HttpGet("slots/thumbs")] public async Task ThumbsSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null) { - // v--- not sure of API in LBP3 here, needs testing - GameVersion gameVersion = gameFilterType == "both" ? GameVersion.LittleBigPlanet2 : GameVersion.LittleBigPlanet1; + long oldestTime; @@ -228,9 +227,42 @@ namespace LBPUnion.ProjectLighthouse.Controllers break; } - Random rand = new(); + Random rand = new(); GameVersion gameVersion; - IEnumerable slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime) + switch (gameFilterType) + { + case "lbp1": + gameVersion = GameVersion.LittleBigPlanet1; + break; + case "lbp2": + gameVersion = GameVersion.LittleBigPlanet2; + break; + case "lbp3": + gameVersion = GameVersion.LittleBigPlanet3; + break; + case "both": // LBP2 default option + gameVersion = GameVersion.LittleBigPlanet2; + break; + default: + gameVersion = GameVersion.Unknown; + break; + } + + IQueryable whereSlots; + + if (gameFilterType == "both") + { + // Get game versions less than the current version + // Needs support for LBP3 ("both" = LBP1+2) + whereSlots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime); + } + else + { + // Get game versions exactly equal to gamefiltertype + whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime); + } + + IEnumerable slots = whereSlots .Include(s => s.Creator) .Include(s => s.Location) .AsEnumerable() @@ -247,8 +279,6 @@ namespace LBPUnion.ProjectLighthouse.Controllers [HttpGet("slots/mostUniquePlays")] public async Task MostUniquePlaysSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null) { - // v--- not sure of API in LBP3 here, needs testing - GameVersion gameVersion = gameFilterType == "both" ? GameVersion.LittleBigPlanet2 : GameVersion.LittleBigPlanet1; long oldestTime; @@ -269,13 +299,48 @@ namespace LBPUnion.ProjectLighthouse.Controllers Random rand = new(); - IEnumerable slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime) + GameVersion gameVersion; + + switch (gameFilterType) + { + case "lbp1": + gameVersion = GameVersion.LittleBigPlanet1; + break; + case "lbp2": + gameVersion = GameVersion.LittleBigPlanet2; + break; + case "lbp3": + gameVersion = GameVersion.LittleBigPlanet3; + break; + case "both": // LBP2 default option + gameVersion = GameVersion.LittleBigPlanet2; + break; + default: + gameVersion = GameVersion.Unknown; + break; + } + + IQueryable whereSlots; + + if (gameFilterType == "both") + { + // Get game versions less than the current version + // Needs support for LBP3 ("both" = LBP1+2) + whereSlots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime); + } + else + { + // Get game versions exactly equal to gamefiltertype + whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime); + } + + IEnumerable slots = whereSlots .Include(s => s.Creator) .Include(s => s.Location) .AsEnumerable() .OrderByDescending(s => { - // probably not the best way to do this + // probably not the best way to do this? switch (gameVersion) { case GameVersion.LittleBigPlanet1: @@ -302,8 +367,6 @@ namespace LBPUnion.ProjectLighthouse.Controllers [HttpGet("slots/mostHearted")] public async Task MostHeartedSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null) { - // v--- not sure of API in LBP3 here, needs testing - GameVersion gameVersion = gameFilterType == "both" ? GameVersion.LittleBigPlanet2 : GameVersion.LittleBigPlanet1; long oldestTime; @@ -322,9 +385,42 @@ namespace LBPUnion.ProjectLighthouse.Controllers break; } - Random rand = new(); + Random rand = new(); GameVersion gameVersion; - IEnumerable slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime) + switch (gameFilterType) + { + case "lbp1": + gameVersion = GameVersion.LittleBigPlanet1; + break; + case "lbp2": + gameVersion = GameVersion.LittleBigPlanet2; + break; + case "lbp3": + gameVersion = GameVersion.LittleBigPlanet3; + break; + case "both": // LBP2 default option + gameVersion = GameVersion.LittleBigPlanet2; + break; + default: + gameVersion = GameVersion.Unknown; + break; + } + + IQueryable whereSlots; + + if (gameFilterType == "both") + { + // Get game versions less than the current version + // Needs support for LBP3 ("both" = LBP1+2) + whereSlots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime); + } + else + { + // Get game versions exactly equal to gamefiltertype + whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime); + } + + IEnumerable slots = whereSlots .Include(s => s.Creator) .Include(s => s.Location) .AsEnumerable() From 2a963567b5655528c77ff77766acc488b27ffd0f Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 18 Dec 2021 15:17:48 -0500 Subject: [PATCH 03/18] Add support for banning a user --- .../.idea/dataSources.xml | 2 +- .../Controllers/LoginController.cs | 2 +- .../Website/Admin/AdminSlotController.cs | 6 +- .../Website/Admin/AdminUserController.cs | 61 +++++++++++++++++++ ...0211217000749_AddBannedPropertiesToUser.cs | 41 +++++++++++++ .../Migrations/DatabaseModelSnapshot.cs | 6 ++ ProjectLighthouse/Pages/UserPage.cshtml | 22 +++++++ ProjectLighthouse/Pages/UserPage.cshtml.cs | 3 +- ProjectLighthouse/Startup.cs | 47 +++++++++----- ProjectLighthouse/Types/User.cs | 4 ++ 10 files changed, 173 insertions(+), 21 deletions(-) create mode 100644 ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs create mode 100644 ProjectLighthouse/Migrations/20211217000749_AddBannedPropertiesToUser.cs diff --git a/.idea/.idea.ProjectLighthouse/.idea/dataSources.xml b/.idea/.idea.ProjectLighthouse/.idea/dataSources.xml index 702fe04b..c8fd1969 100644 --- a/.idea/.idea.ProjectLighthouse/.idea/dataSources.xml +++ b/.idea/.idea.ProjectLighthouse/.idea/dataSources.xml @@ -8,7 +8,7 @@ jdbc:mysql://localhost:3306/lighthouse $ProjectFileDir$ - + mariadb true org.mariadb.jdbc.Driver diff --git a/ProjectLighthouse/Controllers/LoginController.cs b/ProjectLighthouse/Controllers/LoginController.cs index a8126a94..f475ecb1 100644 --- a/ProjectLighthouse/Controllers/LoginController.cs +++ b/ProjectLighthouse/Controllers/LoginController.cs @@ -52,7 +52,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers if (token == null) return this.StatusCode(403, ""); User? user = await this.database.UserFromGameToken(token, true); - if (user == null) return this.StatusCode(403, ""); + if (user == null || user.Banned) return this.StatusCode(403, ""); if (ServerSettings.Instance.UseExternalAuth) { diff --git a/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs b/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs index 70c973ef..4787f1a7 100644 --- a/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs +++ b/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs @@ -19,7 +19,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin this.database = database; } - [Route("teamPick")] + [HttpGet("teamPick")] public async Task TeamPick([FromRoute] int id) { User? user = this.database.UserFromWebRequest(this.Request); @@ -35,7 +35,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin return this.Ok(); } - [Route("removeTeamPick")] + [HttpGet("removeTeamPick")] public async Task RemoveTeamPick([FromRoute] int id) { User? user = this.database.UserFromWebRequest(this.Request); @@ -51,7 +51,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin return this.Ok(); } - [Route("delete")] + [HttpGet("delete")] public async Task DeleteLevel([FromRoute] int id) { User? user = this.database.UserFromWebRequest(this.Request); diff --git a/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs b/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs new file mode 100644 index 00000000..96e74702 --- /dev/null +++ b/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs @@ -0,0 +1,61 @@ +#nullable enable +using System.Linq; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin +{ + [ApiController] + [Route("admin/user/{id:int}")] + public class AdminUserController : ControllerBase + { + private readonly Database database; + + public AdminUserController(Database database) + { + this.database = database; + } + + [HttpGet("ban")] + public async Task BanUser([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); + + User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + ; + if (targetedUser == null) return this.NotFound(); + + targetedUser.Banned = true; + targetedUser.BannedReason = $"Banned by admin {user.Username} (id: {user.UserId})"; + + // invalidate all currently active gametokens + this.database.GameTokens.RemoveRange(this.database.GameTokens.Where(t => t.UserId == targetedUser.UserId)); + + // invalidate all currently active webtokens + this.database.WebTokens.RemoveRange(this.database.WebTokens.Where(t => t.UserId == targetedUser.UserId)); + + await this.database.SaveChangesAsync(); + return this.Redirect($"/user/{targetedUser.UserId}"); + } + + [HttpGet("unban")] + public async Task UnbanUser([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); + + User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + ; + if (targetedUser == null) return this.NotFound(); + + targetedUser.Banned = false; + targetedUser.BannedReason = null; + + await this.database.SaveChangesAsync(); + return this.Redirect($"/user/{targetedUser.UserId}"); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Migrations/20211217000749_AddBannedPropertiesToUser.cs b/ProjectLighthouse/Migrations/20211217000749_AddBannedPropertiesToUser.cs new file mode 100644 index 00000000..6f2f5a64 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211217000749_AddBannedPropertiesToUser.cs @@ -0,0 +1,41 @@ +using LBPUnion.ProjectLighthouse; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20211217000749_AddBannedPropertiesToUser")] + public partial class AddBannedPropertiesToUser : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Banned", + table: "Users", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "BannedReason", + table: "Users", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Banned", + table: "Users"); + + migrationBuilder.DropColumn( + name: "BannedReason", + table: "Users"); + } + } +} diff --git a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs index 4afc7b51..def721ba 100644 --- a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs +++ b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs @@ -541,6 +541,12 @@ namespace ProjectLighthouse.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + b.Property("Banned") + .HasColumnType("tinyint(1)"); + + b.Property("BannedReason") + .HasColumnType("longtext"); + b.Property("Biography") .HasColumnType("longtext"); diff --git a/ProjectLighthouse/Pages/UserPage.cshtml b/ProjectLighthouse/Pages/UserPage.cshtml index 128a7883..a6c7a5af 100644 --- a/ProjectLighthouse/Pages/UserPage.cshtml +++ b/ProjectLighthouse/Pages/UserPage.cshtml @@ -10,12 +10,27 @@ Model.ShowTitleInPage = false; } +@if (Model.User != null && Model.User.IsAdmin && Model.ProfileUser.Banned) +{ +
+

+ User is banned! +

+

Reason: @Model.ProfileUser.BannedReason

+ + + Unban User + +
+} +

@Model.Title

@Model.ProfileUser!.Status

+
@Model.ProfileUser.Hearts @Model.ProfileUser.Comments @@ -49,6 +64,13 @@ Reset Password } + @if (Model.User != null && Model.User.IsAdmin && !Model.ProfileUser.Banned) + { + + + Ban User + + }
diff --git a/ProjectLighthouse/Pages/UserPage.cshtml.cs b/ProjectLighthouse/Pages/UserPage.cshtml.cs index 0240ab59..e903ff09 100644 --- a/ProjectLighthouse/Pages/UserPage.cshtml.cs +++ b/ProjectLighthouse/Pages/UserPage.cshtml.cs @@ -24,8 +24,9 @@ namespace LBPUnion.ProjectLighthouse.Pages public async Task OnGet([FromRoute] int userId) { + bool canViewBannedUsers = this.User != null && this.User.IsAdmin; this.ProfileUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == userId); - if (this.ProfileUser == null) return this.NotFound(); + if (this.ProfileUser == null || !canViewBannedUsers && this.ProfileUser.Banned) return this.NotFound(); this.Photos = await this.Database.Photos.OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(5).ToListAsync(); this.Comments = await this.Database.Comments.Include diff --git a/ProjectLighthouse/Startup.cs b/ProjectLighthouse/Startup.cs index fe68aea5..cecb0370 100644 --- a/ProjectLighthouse/Startup.cs +++ b/ProjectLighthouse/Startup.cs @@ -87,11 +87,34 @@ namespace LBPUnion.ProjectLighthouse Stopwatch requestStopwatch = new(); requestStopwatch.Start(); + context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging + // Log all headers. // foreach (KeyValuePair header in context.Request.Headers) Logger.Log($"{header.Key}: {header.Value}"); - context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging + await next(context); // Handle the request so we can get the status code from it + requestStopwatch.Stop(); + + Logger.Log + ( + $"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}", + LoggerLevelHttp.Instance + ); + + if (context.Request.Method == "POST") + { + context.Request.Body.Position = 0; + Logger.Log(await new StreamReader(context.Request.Body).ReadToEndAsync(), LoggerLevelHttp.Instance); + } + } + ); + + // Digest check + app.Use + ( + async (context, next) => + { // Client digest check. if (!context.Request.Cookies.TryGetValue("MM_AUTH", out string authCookie)) authCookie = string.Empty; string digestPath = context.Request.Path; @@ -119,7 +142,7 @@ namespace LBPUnion.ProjectLighthouse Stream oldResponseStream = context.Response.Body; context.Response.Body = responseBuffer; - await next(); // Handle the request so we can get the status code from it + await next(context); // Handle the request so we can get the server digest hash // Compute the server digest hash. if (computeDigests) @@ -138,7 +161,13 @@ namespace LBPUnion.ProjectLighthouse responseBuffer.Position = 0; await responseBuffer.CopyToAsync(oldResponseStream); context.Response.Body = oldResponseStream; + } + ); + app.Use + ( + async (context, next) => + { #nullable enable // Log LastContact for LBP1. This is done on LBP2/3/V on a Match request. if (context.Request.Path.ToString().StartsWith("/LITTLEBIGPLANETPS3_XML")) @@ -153,19 +182,7 @@ namespace LBPUnion.ProjectLighthouse } #nullable disable - requestStopwatch.Stop(); - - Logger.Log - ( - $"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}", - LoggerLevelHttp.Instance - ); - - if (context.Request.Method == "POST") - { - context.Request.Body.Position = 0; - Logger.Log(await new StreamReader(context.Request.Body).ReadToEndAsync(), LoggerLevelHttp.Instance); - } + await next(context); } ); diff --git a/ProjectLighthouse/Types/User.cs b/ProjectLighthouse/Types/User.cs index 726c706e..b941d656 100644 --- a/ProjectLighthouse/Types/User.cs +++ b/ProjectLighthouse/Types/User.cs @@ -125,6 +125,10 @@ namespace LBPUnion.ProjectLighthouse.Types } #nullable disable + public bool Banned { get; set; } + + public string BannedReason { get; set; } + public string Serialize(GameVersion gameVersion = GameVersion.LittleBigPlanet1) { string user = LbpSerializer.TaggedStringElement("npHandle", this.Username, "icon", this.IconHash) + From 2ca80bf8fdbe2916377ee4eca8996b94d3a6efb0 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sun, 19 Dec 2021 22:35:29 -0500 Subject: [PATCH 04/18] Reject web logins when banned --- ProjectLighthouse/Pages/LoginForm.cshtml | 2 +- ProjectLighthouse/Pages/LoginForm.cshtml.cs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ProjectLighthouse/Pages/LoginForm.cshtml b/ProjectLighthouse/Pages/LoginForm.cshtml index 5967a096..f89075af 100644 --- a/ProjectLighthouse/Pages/LoginForm.cshtml +++ b/ProjectLighthouse/Pages/LoginForm.cshtml @@ -24,7 +24,7 @@
Uh oh!
-

@Model.Error

+

@Model.Error

} diff --git a/ProjectLighthouse/Pages/LoginForm.cshtml.cs b/ProjectLighthouse/Pages/LoginForm.cshtml.cs index c7871109..45f6d9cb 100644 --- a/ProjectLighthouse/Pages/LoginForm.cshtml.cs +++ b/ProjectLighthouse/Pages/LoginForm.cshtml.cs @@ -1,7 +1,9 @@ #nullable enable using System.Threading.Tasks; using JetBrains.Annotations; +using Kettu; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; @@ -36,16 +38,25 @@ namespace LBPUnion.ProjectLighthouse.Pages User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username); if (user == null) { + Logger.Log($"User {username} failed to login on web due to invalid username", LoggerLevelLogin.Instance); this.Error = "The username or password you entered is invalid."; return this.Page(); } if (!BCrypt.Net.BCrypt.Verify(password, user.Password)) { + Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to invalid password", LoggerLevelLogin.Instance); this.Error = "The username or password you entered is invalid."; return this.Page(); } + if (user.Banned) + { + Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to being banned", LoggerLevelLogin.Instance); + this.Error = "You have been banned. Please contact an administrator for more information.\nReason: " + user.BannedReason; + return this.Page(); + } + WebToken webToken = new() { UserId = user.UserId, From 11067d4f3a697a2ce22a60c7a25a6363c3e5ab3a Mon Sep 17 00:00:00 2001 From: jvyden Date: Sun, 19 Dec 2021 23:47:00 -0500 Subject: [PATCH 05/18] Dont hide banned user's profiles --- ProjectLighthouse/Pages/UserPage.cshtml | 21 ++++++++++++++------- ProjectLighthouse/Pages/UserPage.cshtml.cs | 3 +-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/ProjectLighthouse/Pages/UserPage.cshtml b/ProjectLighthouse/Pages/UserPage.cshtml index b7843b06..382f2f40 100644 --- a/ProjectLighthouse/Pages/UserPage.cshtml +++ b/ProjectLighthouse/Pages/UserPage.cshtml @@ -12,17 +12,24 @@ Model.ShowTitleInPage = false; } -@if (Model.User != null && Model.User.IsAdmin && Model.ProfileUser.Banned) +@if (Model.ProfileUser.Banned) {

- User is banned! + User is currently banned!

-

Reason: @Model.ProfileUser.BannedReason

- - - Unban User - + @if (Model.User != null && Model.User.IsAdmin) + { +

Reason: @Model.ProfileUser.BannedReason

+ + + Unban User + + } + else + { +

For shame...

+ }
} diff --git a/ProjectLighthouse/Pages/UserPage.cshtml.cs b/ProjectLighthouse/Pages/UserPage.cshtml.cs index e903ff09..0240ab59 100644 --- a/ProjectLighthouse/Pages/UserPage.cshtml.cs +++ b/ProjectLighthouse/Pages/UserPage.cshtml.cs @@ -24,9 +24,8 @@ namespace LBPUnion.ProjectLighthouse.Pages public async Task OnGet([FromRoute] int userId) { - bool canViewBannedUsers = this.User != null && this.User.IsAdmin; this.ProfileUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == userId); - if (this.ProfileUser == null || !canViewBannedUsers && this.ProfileUser.Banned) return this.NotFound(); + if (this.ProfileUser == null) return this.NotFound(); this.Photos = await this.Database.Photos.OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(5).ToListAsync(); this.Comments = await this.Database.Comments.Include From 1fbabe0000c1e9cdcb237afc2dd10a7109f8540a Mon Sep 17 00:00:00 2001 From: jvyden Date: Wed, 22 Dec 2021 22:33:50 -0500 Subject: [PATCH 06/18] Split normal tests from game api tests --- .../AuthenticationTests.cs | 5 ++- .../DatabaseTests.cs | 6 ++- .../MatchTests.cs | 5 ++- ...rojectLighthouse.Tests.GameApiTests.csproj | 41 +++++++++++++++++++ .../SlotTests.cs | 6 ++- .../UploadTests.cs | 5 ++- .../{Tests => }/FileTypeTests.cs | 0 ...hthouseTest.cs => LighthouseServerTest.cs} | 4 +- .../ProjectLighthouse.Tests.csproj | 2 +- .../{Tests => }/SerializerTests.cs | 2 +- ProjectLighthouse.sln | 12 ++++++ 11 files changed, 74 insertions(+), 14 deletions(-) rename {ProjectLighthouse.Tests/Tests => ProjectLighthouse.Tests.GameApiTests}/AuthenticationTests.cs (94%) rename {ProjectLighthouse.Tests/Tests => ProjectLighthouse.Tests.GameApiTests}/DatabaseTests.cs (81%) rename {ProjectLighthouse.Tests/Tests => ProjectLighthouse.Tests.GameApiTests}/MatchTests.cs (93%) create mode 100644 ProjectLighthouse.Tests.GameApiTests/ProjectLighthouse.Tests.GameApiTests.csproj rename {ProjectLighthouse.Tests/Tests => ProjectLighthouse.Tests.GameApiTests}/SlotTests.cs (94%) rename {ProjectLighthouse.Tests/Tests => ProjectLighthouse.Tests.GameApiTests}/UploadTests.cs (93%) rename ProjectLighthouse.Tests/{Tests => }/FileTypeTests.cs (100%) rename ProjectLighthouse.Tests/{LighthouseTest.cs => LighthouseServerTest.cs} (98%) rename ProjectLighthouse.Tests/{Tests => }/SerializerTests.cs (96%) diff --git a/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs b/ProjectLighthouse.Tests.GameApiTests/AuthenticationTests.cs similarity index 94% rename from ProjectLighthouse.Tests/Tests/AuthenticationTests.cs rename to ProjectLighthouse.Tests.GameApiTests/AuthenticationTests.cs index d7f5fd19..05f0c413 100644 --- a/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/AuthenticationTests.cs @@ -1,13 +1,14 @@ using System.Net; using System.Net.Http; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Tests; using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Settings; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests +namespace ProjectLighthouse.Tests.GameApiTests { - public class AuthenticationTests : LighthouseTest + public class AuthenticationTests : LighthouseServerTest { [Fact] public async Task ShouldReturnErrorOnNoPostData() diff --git a/ProjectLighthouse.Tests/Tests/DatabaseTests.cs b/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs similarity index 81% rename from ProjectLighthouse.Tests/Tests/DatabaseTests.cs rename to ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs index 19416bcb..ccaa758b 100644 --- a/ProjectLighthouse.Tests/Tests/DatabaseTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs @@ -1,12 +1,14 @@ using System; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Tests; using LBPUnion.ProjectLighthouse.Types; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests +namespace ProjectLighthouse.Tests.GameApiTests { - public class DatabaseTests : LighthouseTest + public class DatabaseTests : LighthouseServerTest { [DatabaseFact] public async Task CanCreateUserTwice() diff --git a/ProjectLighthouse.Tests/Tests/MatchTests.cs b/ProjectLighthouse.Tests.GameApiTests/MatchTests.cs similarity index 93% rename from ProjectLighthouse.Tests/Tests/MatchTests.cs rename to ProjectLighthouse.Tests.GameApiTests/MatchTests.cs index 721dc941..c7e83feb 100644 --- a/ProjectLighthouse.Tests/Tests/MatchTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/MatchTests.cs @@ -4,12 +4,13 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Tests; using LBPUnion.ProjectLighthouse.Types; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests +namespace ProjectLighthouse.Tests.GameApiTests { - public class MatchTests : LighthouseTest + public class MatchTests : LighthouseServerTest { private static readonly SemaphoreSlim semaphore = new(1, 1); diff --git a/ProjectLighthouse.Tests.GameApiTests/ProjectLighthouse.Tests.GameApiTests.csproj b/ProjectLighthouse.Tests.GameApiTests/ProjectLighthouse.Tests.GameApiTests.csproj new file mode 100644 index 00000000..4a5850d3 --- /dev/null +++ b/ProjectLighthouse.Tests.GameApiTests/ProjectLighthouse.Tests.GameApiTests.csproj @@ -0,0 +1,41 @@ + + + + net6.0 + enable + + false + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + PreserveNewest + + + + + diff --git a/ProjectLighthouse.Tests/Tests/SlotTests.cs b/ProjectLighthouse.Tests.GameApiTests/SlotTests.cs similarity index 94% rename from ProjectLighthouse.Tests/Tests/SlotTests.cs rename to ProjectLighthouse.Tests.GameApiTests/SlotTests.cs index 4eef03b6..568a9ed4 100644 --- a/ProjectLighthouse.Tests/Tests/SlotTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/SlotTests.cs @@ -1,15 +1,17 @@ using System; using System.Net.Http; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Tests; using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Profiles; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests +namespace ProjectLighthouse.Tests.GameApiTests { - public class SlotTests : LighthouseTest + public class SlotTests : LighthouseServerTest { [DatabaseFact] public async Task ShouldOnlyShowUsersLevels() diff --git a/ProjectLighthouse.Tests/Tests/UploadTests.cs b/ProjectLighthouse.Tests.GameApiTests/UploadTests.cs similarity index 93% rename from ProjectLighthouse.Tests/Tests/UploadTests.cs rename to ProjectLighthouse.Tests.GameApiTests/UploadTests.cs index 8c7f0bd5..faac8aa9 100644 --- a/ProjectLighthouse.Tests/Tests/UploadTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/UploadTests.cs @@ -3,11 +3,12 @@ using System.IO; using System.Net; using System.Net.Http; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Tests; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests +namespace ProjectLighthouse.Tests.GameApiTests { - public class UploadTests : LighthouseTest + public class UploadTests : LighthouseServerTest { public UploadTests() { diff --git a/ProjectLighthouse.Tests/Tests/FileTypeTests.cs b/ProjectLighthouse.Tests/FileTypeTests.cs similarity index 100% rename from ProjectLighthouse.Tests/Tests/FileTypeTests.cs rename to ProjectLighthouse.Tests/FileTypeTests.cs diff --git a/ProjectLighthouse.Tests/LighthouseTest.cs b/ProjectLighthouse.Tests/LighthouseServerTest.cs similarity index 98% rename from ProjectLighthouse.Tests/LighthouseTest.cs rename to ProjectLighthouse.Tests/LighthouseServerTest.cs index dd96dbf0..0a02da01 100644 --- a/ProjectLighthouse.Tests/LighthouseTest.cs +++ b/ProjectLighthouse.Tests/LighthouseServerTest.cs @@ -14,12 +14,12 @@ using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Tests { [SuppressMessage("ReSharper", "UnusedMember.Global")] - public class LighthouseTest + public class LighthouseServerTest { public readonly HttpClient Client; public readonly TestServer Server; - public LighthouseTest() + public LighthouseServerTest() { this.Server = new TestServer(new WebHostBuilder().UseStartup()); diff --git a/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj b/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj index 0f656960..91fa33ee 100644 --- a/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj +++ b/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj @@ -32,9 +32,9 @@ - PreserveNewest + diff --git a/ProjectLighthouse.Tests/Tests/SerializerTests.cs b/ProjectLighthouse.Tests/SerializerTests.cs similarity index 96% rename from ProjectLighthouse.Tests/Tests/SerializerTests.cs rename to ProjectLighthouse.Tests/SerializerTests.cs index 2c8af05e..39a12ce4 100644 --- a/ProjectLighthouse.Tests/Tests/SerializerTests.cs +++ b/ProjectLighthouse.Tests/SerializerTests.cs @@ -4,7 +4,7 @@ using Xunit; namespace LBPUnion.ProjectLighthouse.Tests { - public class SerializerTests : LighthouseTest + public class SerializerTests { [Fact] public void BlankElementWorks() diff --git a/ProjectLighthouse.sln b/ProjectLighthouse.sln index 96db324b..745241a4 100644 --- a/ProjectLighthouse.sln +++ b/ProjectLighthouse.sln @@ -4,6 +4,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse", "Projec EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Tests", "ProjectLighthouse.Tests\ProjectLighthouse.Tests.csproj", "{AFC74569-B289-4ACC-B21C-313A3A62C017}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{D360C08E-EA47-43AC-A566-FDF413442980}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Tests.GameApiTests", "ProjectLighthouse.Tests.GameApiTests\ProjectLighthouse.Tests.GameApiTests.csproj", "{200EED99-FE3E-45C6-A51E-76ED9819CA2B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +22,13 @@ Global {AFC74569-B289-4ACC-B21C-313A3A62C017}.Debug|Any CPU.Build.0 = Debug|Any CPU {AFC74569-B289-4ACC-B21C-313A3A62C017}.Release|Any CPU.ActiveCfg = Release|Any CPU {AFC74569-B289-4ACC-B21C-313A3A62C017}.Release|Any CPU.Build.0 = Release|Any CPU + {200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {AFC74569-B289-4ACC-B21C-313A3A62C017} = {D360C08E-EA47-43AC-A566-FDF413442980} + {200EED99-FE3E-45C6-A51E-76ED9819CA2B} = {D360C08E-EA47-43AC-A566-FDF413442980} EndGlobalSection EndGlobal From 281be33640c6ea5706b59ae5081924a76851c5ab Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 23 Dec 2021 00:03:04 -0500 Subject: [PATCH 07/18] Add website authentication tests --- .../DatabaseTests.cs | 2 - .../AuthenticationTests.cs | 133 ++++++++++++++++++ ...rojectLighthouse.Tests.WebsiteTests.csproj | 36 +++++ .../LighthouseServerTest.cs | 1 - ProjectLighthouse.sln | 7 + ProjectLighthouse.sln.DotSettings | 1 + 6 files changed, 177 insertions(+), 3 deletions(-) create mode 100644 ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs create mode 100644 ProjectLighthouse.Tests.WebsiteTests/ProjectLighthouse.Tests.WebsiteTests.csproj diff --git a/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs b/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs index ccaa758b..86b26ccd 100644 --- a/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs @@ -23,8 +23,6 @@ namespace ProjectLighthouse.Tests.GameApiTests Assert.NotNull(userB); await database.RemoveUser(userA); // Only remove userA since userA and userB are the same user - - await database.SaveChangesAsync(); } } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs b/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs new file mode 100644 index 00000000..ce7f7f74 --- /dev/null +++ b/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs @@ -0,0 +1,133 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Tests; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.EntityFrameworkCore; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; +using Xunit; + +namespace ProjectLighthouse.Tests.WebsiteTests +{ + public class AuthenticationTests : IDisposable + { + public readonly IWebHost WebHost = new WebHostBuilder().UseKestrel().UseStartup().UseWebRoot("StaticFiles").Build(); + public readonly string BaseAddress; + + public readonly IWebDriver Driver = new ChromeDriver(); + + public AuthenticationTests() + { + this.WebHost.Start(); + + IServerAddressesFeature? serverAddressesFeature = WebHost.ServerFeatures.Get(); + if (serverAddressesFeature == null) throw new ArgumentNullException(); + + this.BaseAddress = serverAddressesFeature.Addresses.First(); + } + + [DatabaseFact] + public async Task ShouldLoginWithPassword() + { + await using Database database = new(); + Random random = new(); + + string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); + User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash(HashHelper.Sha256Hash(password))); + + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); + + this.Driver.FindElement(By.Id("text")).SendKeys(user.Username); + this.Driver.FindElement(By.Id("password")).SendKeys(password); + + this.Driver.FindElement(By.Id("submit")).Click(); + + WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId); + Assert.NotNull(webToken); + + await database.RemoveUser(user); + } + + [DatabaseFact] + public async Task ShouldNotLoginWithNoPassword() + { + await using Database database = new(); + Random random = new(); + User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("just like the hindenberg,")); + + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); + + this.Driver.FindElement(By.Id("text")).SendKeys(user.Username); + + this.Driver.FindElement(By.Id("submit")).Click(); + + WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId); + Assert.Null(webToken); + + await database.RemoveUser(user); + } + + [DatabaseFact] + public async Task ShouldNotLoginWithWrongPassword() + { + await using Database database = new(); + Random random = new(); + User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); + + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); + + this.Driver.FindElement(By.Id("text")).SendKeys(user.Username); + this.Driver.FindElement(By.Id("password")).SendKeys("nah man"); + + this.Driver.FindElement(By.Id("submit")).Click(); + + WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId); + Assert.Null(webToken); + + await database.RemoveUser(user); + } + + [DatabaseFact] + public async Task ShouldLoginWithInjectedCookie() + { + const string loggedInAsUsernameTextXPath = "/html/body/div/div/div/p[1]/b"; + + await using Database database = new(); + Random random = new(); + User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); + + WebToken webToken = new() + { + UserId = user.UserId, + UserToken = HashHelper.GenerateAuthToken(), + }; + + database.WebTokens.Add(webToken); + await database.SaveChangesAsync(); + + INavigation navigation = this.Driver.Navigate(); + + navigation.GoToUrl(this.BaseAddress + "/"); + this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); + Assert.Throws(() => this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath))); + navigation.Refresh(); + Assert.True(this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)).Text == user.Username); + + await database.RemoveUser(user); + } + + public void Dispose() + { + this.Driver.Close(); + this.Driver.Dispose(); + this.WebHost.Dispose(); + + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Tests.WebsiteTests/ProjectLighthouse.Tests.WebsiteTests.csproj b/ProjectLighthouse.Tests.WebsiteTests/ProjectLighthouse.Tests.WebsiteTests.csproj new file mode 100644 index 00000000..93e88c08 --- /dev/null +++ b/ProjectLighthouse.Tests.WebsiteTests/ProjectLighthouse.Tests.WebsiteTests.csproj @@ -0,0 +1,36 @@ + + + + net6.0 + enable + + false + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/ProjectLighthouse.Tests/LighthouseServerTest.cs b/ProjectLighthouse.Tests/LighthouseServerTest.cs index 0a02da01..4e79d578 100644 --- a/ProjectLighthouse.Tests/LighthouseServerTest.cs +++ b/ProjectLighthouse.Tests/LighthouseServerTest.cs @@ -22,7 +22,6 @@ namespace LBPUnion.ProjectLighthouse.Tests public LighthouseServerTest() { this.Server = new TestServer(new WebHostBuilder().UseStartup()); - this.Client = this.Server.CreateClient(); } public async Task AuthenticateResponse(int number = -1, bool createUser = true) diff --git a/ProjectLighthouse.sln b/ProjectLighthouse.sln index 745241a4..c1ae0363 100644 --- a/ProjectLighthouse.sln +++ b/ProjectLighthouse.sln @@ -8,6 +8,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{D360C08E EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Tests.GameApiTests", "ProjectLighthouse.Tests.GameApiTests\ProjectLighthouse.Tests.GameApiTests.csproj", "{200EED99-FE3E-45C6-A51E-76ED9819CA2B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Tests.WebsiteTests", "ProjectLighthouse.Tests.WebsiteTests\ProjectLighthouse.Tests.WebsiteTests.csproj", "{CF65EB5B-5364-4D2A-8639-F147A67F08E7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -26,9 +28,14 @@ Global {200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Debug|Any CPU.Build.0 = Debug|Any CPU {200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Release|Any CPU.ActiveCfg = Release|Any CPU {200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Release|Any CPU.Build.0 = Release|Any CPU + {CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {AFC74569-B289-4ACC-B21C-313A3A62C017} = {D360C08E-EA47-43AC-A566-FDF413442980} {200EED99-FE3E-45C6-A51E-76ED9819CA2B} = {D360C08E-EA47-43AC-A566-FDF413442980} + {CF65EB5B-5364-4D2A-8639-F147A67F08E7} = {D360C08E-EA47-43AC-A566-FDF413442980} EndGlobalSection EndGlobal diff --git a/ProjectLighthouse.sln.DotSettings b/ProjectLighthouse.sln.DotSettings index 385c8365..7bbd6e1f 100644 --- a/ProjectLighthouse.sln.DotSettings +++ b/ProjectLighthouse.sln.DotSettings @@ -89,6 +89,7 @@ True True True + True True True True From f36038910a4b3af874991dd71e002b006e3f9ff9 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 23 Dec 2021 00:23:14 -0500 Subject: [PATCH 08/18] Run ChromeDriver in headless mode if in a CI environment --- .../AuthenticationTests.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs b/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs index ce7f7f74..5266abf4 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs @@ -19,7 +19,7 @@ namespace ProjectLighthouse.Tests.WebsiteTests public readonly IWebHost WebHost = new WebHostBuilder().UseKestrel().UseStartup().UseWebRoot("StaticFiles").Build(); public readonly string BaseAddress; - public readonly IWebDriver Driver = new ChromeDriver(); + public readonly IWebDriver Driver; public AuthenticationTests() { @@ -29,6 +29,15 @@ namespace ProjectLighthouse.Tests.WebsiteTests if (serverAddressesFeature == null) throw new ArgumentNullException(); this.BaseAddress = serverAddressesFeature.Addresses.First(); + + ChromeOptions chromeOptions = new(); + if (Convert.ToBoolean(Environment.GetEnvironmentVariable("CI") ?? "false")) + { + chromeOptions.AddArgument("headless"); + Console.WriteLine("We are in a CI environment, so chrome headless mode has been enabled."); + } + + this.Driver = new ChromeDriver(chromeOptions); } [DatabaseFact] From cabe48585481d3ca4bc68e83966f284d51565997 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 23 Dec 2021 00:33:18 -0500 Subject: [PATCH 09/18] Disable chrome sandboxing and dev-shm usage in CI environment --- ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs | 2 ++ ProjectLighthouse.sln | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs b/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs index 5266abf4..e3ed939c 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs @@ -34,6 +34,8 @@ namespace ProjectLighthouse.Tests.WebsiteTests if (Convert.ToBoolean(Environment.GetEnvironmentVariable("CI") ?? "false")) { chromeOptions.AddArgument("headless"); + chromeOptions.AddArgument("no-sandbox"); + chromeOptions.AddArgument("disable-dev-shm-usage"); Console.WriteLine("We are in a CI environment, so chrome headless mode has been enabled."); } diff --git a/ProjectLighthouse.sln b/ProjectLighthouse.sln index c1ae0363..9f80122f 100644 --- a/ProjectLighthouse.sln +++ b/ProjectLighthouse.sln @@ -23,15 +23,12 @@ Global {AFC74569-B289-4ACC-B21C-313A3A62C017}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AFC74569-B289-4ACC-B21C-313A3A62C017}.Debug|Any CPU.Build.0 = Debug|Any CPU {AFC74569-B289-4ACC-B21C-313A3A62C017}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AFC74569-B289-4ACC-B21C-313A3A62C017}.Release|Any CPU.Build.0 = Release|Any CPU {200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Debug|Any CPU.Build.0 = Debug|Any CPU {200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Release|Any CPU.Build.0 = Release|Any CPU {CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Debug|Any CPU.Build.0 = Debug|Any CPU {CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {AFC74569-B289-4ACC-B21C-313A3A62C017} = {D360C08E-EA47-43AC-A566-FDF413442980} From bbaccef2e675a50a518a10e9903d190666e158e8 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 23 Dec 2021 00:42:04 -0500 Subject: [PATCH 10/18] Only do CI WebsiteTests on Linux --- .github/workflows/ci.yml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87ba9197..e36c241d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,9 +13,9 @@ jobs: fail-fast: false matrix: os: - - { prettyName: Windows, fullName: windows-latest, database: true } - - { prettyName: macOS, fullName: macos-latest, database: true } - - { prettyName: Linux, fullName: ubuntu-latest, database: true } + - { prettyName: Windows, fullName: windows-latest, database: true, webTest: false } + - { prettyName: macOS, fullName: macos-latest, database: true, webTest: false } + - { prettyName: Linux, fullName: ubuntu-latest, database: true, webTest: true } timeout-minutes: 10 env: DB_DATABASE: lighthouse @@ -44,11 +44,19 @@ jobs: - name: Compile run: dotnet build -c Debug - - name: Test + - name: ProjectLighthouse.Tests continue-on-error: true - run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}.trx" + run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}.trx" ProjectLighthouse.Tests + - name: ProjectLighthouse.Tests.GameApiTests + continue-on-error: true + run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}.trx" ProjectLighthouse.Tests.GameApiTests + - name: ProjectLighthouse.Tests.WebsiteTests + if: ${{ matrix.os.webTest }} + continue-on-error: true + run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}.trx" ProjectLighthouse.Tests.WebsiteTests + # Attempt to upload results even if test fails. # https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#always - name: Upload Test Results From 89857b99bcea4301ea0c1be6d48ce17bf00ac112 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 23 Dec 2021 00:47:23 -0500 Subject: [PATCH 11/18] Upload individual test log files --- .github/workflows/ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e36c241d..355cc520 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,18 +44,18 @@ jobs: - name: Compile run: dotnet build -c Debug - - name: ProjectLighthouse.Tests + - name: Run tests on ProjectLighthouse.Tests continue-on-error: true - run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}.trx" ProjectLighthouse.Tests + run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-Tests.trx" ProjectLighthouse.Tests - - name: ProjectLighthouse.Tests.GameApiTests + - name: Run tests on ProjectLighthouse.Tests.GameApiTests continue-on-error: true - run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}.trx" ProjectLighthouse.Tests.GameApiTests + run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-GameApiTests.trx" ProjectLighthouse.Tests.GameApiTests - - name: ProjectLighthouse.Tests.WebsiteTests + - name: Run tests on ProjectLighthouse.Tests.WebsiteTests if: ${{ matrix.os.webTest }} continue-on-error: true - run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}.trx" ProjectLighthouse.Tests.WebsiteTests + run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-WebsiteTests.trx" ProjectLighthouse.Tests.WebsiteTests # Attempt to upload results even if test fails. # https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#always @@ -64,7 +64,7 @@ jobs: if: ${{ always() }} with: name: lighthouse-test-results-${{matrix.os.prettyName}} - path: ${{github.workspace}}/TestResults-${{matrix.os.prettyName}}.trx + path: ${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-*.trx - name: Process Test Results id: process-trx From b5767c6f21e6b8b1939cc442dc69f9ab7d33b3a6 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 23 Dec 2021 01:22:48 -0500 Subject: [PATCH 12/18] Add registration tests --- .../AuthenticationTests.cs | 40 +-------- .../LighthouseWebTest.cs | 50 +++++++++++ .../RegisterTests.cs | 84 +++++++++++++++++++ 3 files changed, 135 insertions(+), 39 deletions(-) create mode 100644 ProjectLighthouse.Tests.WebsiteTests/LighthouseWebTest.cs create mode 100644 ProjectLighthouse.Tests.WebsiteTests/RegisterTests.cs diff --git a/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs b/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs index e3ed939c..598969d9 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs @@ -5,43 +5,14 @@ using LBPUnion.ProjectLighthouse; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Tests; using LBPUnion.ProjectLighthouse.Types; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.EntityFrameworkCore; using OpenQA.Selenium; -using OpenQA.Selenium.Chrome; using Xunit; namespace ProjectLighthouse.Tests.WebsiteTests { - public class AuthenticationTests : IDisposable + public class AuthenticationTests : LighthouseWebTest { - public readonly IWebHost WebHost = new WebHostBuilder().UseKestrel().UseStartup().UseWebRoot("StaticFiles").Build(); - public readonly string BaseAddress; - - public readonly IWebDriver Driver; - - public AuthenticationTests() - { - this.WebHost.Start(); - - IServerAddressesFeature? serverAddressesFeature = WebHost.ServerFeatures.Get(); - if (serverAddressesFeature == null) throw new ArgumentNullException(); - - this.BaseAddress = serverAddressesFeature.Addresses.First(); - - ChromeOptions chromeOptions = new(); - if (Convert.ToBoolean(Environment.GetEnvironmentVariable("CI") ?? "false")) - { - chromeOptions.AddArgument("headless"); - chromeOptions.AddArgument("no-sandbox"); - chromeOptions.AddArgument("disable-dev-shm-usage"); - Console.WriteLine("We are in a CI environment, so chrome headless mode has been enabled."); - } - - this.Driver = new ChromeDriver(chromeOptions); - } - [DatabaseFact] public async Task ShouldLoginWithPassword() { @@ -131,14 +102,5 @@ namespace ProjectLighthouse.Tests.WebsiteTests await database.RemoveUser(user); } - - public void Dispose() - { - this.Driver.Close(); - this.Driver.Dispose(); - this.WebHost.Dispose(); - - GC.SuppressFinalize(this); - } } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests.WebsiteTests/LighthouseWebTest.cs b/ProjectLighthouse.Tests.WebsiteTests/LighthouseWebTest.cs new file mode 100644 index 00000000..dc96a596 --- /dev/null +++ b/ProjectLighthouse.Tests.WebsiteTests/LighthouseWebTest.cs @@ -0,0 +1,50 @@ +using System; +using System.Linq; +using LBPUnion.ProjectLighthouse; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server.Features; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; +using Xunit; + +namespace ProjectLighthouse.Tests.WebsiteTests +{ + [Collection(nameof(LighthouseWebTest))] + public class LighthouseWebTest : IDisposable + { + public readonly IWebHost WebHost = new WebHostBuilder().UseKestrel().UseStartup().UseWebRoot("StaticFiles").Build(); + public readonly string BaseAddress; + + public readonly IWebDriver Driver; + + public LighthouseWebTest() + { + this.WebHost.Start(); + + IServerAddressesFeature? serverAddressesFeature = WebHost.ServerFeatures.Get(); + if (serverAddressesFeature == null) throw new ArgumentNullException(); + + this.BaseAddress = serverAddressesFeature.Addresses.First(); + + ChromeOptions chromeOptions = new(); + if (Convert.ToBoolean(Environment.GetEnvironmentVariable("CI") ?? "false")) + { + chromeOptions.AddArgument("headless"); + chromeOptions.AddArgument("no-sandbox"); + chromeOptions.AddArgument("disable-dev-shm-usage"); + Console.WriteLine("We are in a CI environment, so chrome headless mode has been enabled."); + } + + this.Driver = new ChromeDriver(chromeOptions); + } + + public void Dispose() + { + this.Driver.Close(); + this.Driver.Dispose(); + this.WebHost.Dispose(); + + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Tests.WebsiteTests/RegisterTests.cs b/ProjectLighthouse.Tests.WebsiteTests/RegisterTests.cs new file mode 100644 index 00000000..2c3cf855 --- /dev/null +++ b/ProjectLighthouse.Tests.WebsiteTests/RegisterTests.cs @@ -0,0 +1,84 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Tests; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.EntityFrameworkCore; +using OpenQA.Selenium; +using Xunit; + +namespace ProjectLighthouse.Tests.WebsiteTests +{ + public class RegisterTests : LighthouseWebTest + { + [DatabaseFact] + public async Task ShouldRegister() + { + await using Database database = new(); + + string username = "unitTestUser" + new Random().Next(); + string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); + + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register"); + + this.Driver.FindElement(By.Id("text")).SendKeys(username); + + this.Driver.FindElement(By.Id("password")).SendKeys(password); + this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password); + + this.Driver.FindElement(By.Id("submit")).Click(); + + User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); + Assert.NotNull(user); + + await database.RemoveUser(user); + } + + [DatabaseFact] + public async Task ShouldNotRegisterWithMismatchingPasswords() + { + await using Database database = new(); + + string username = "unitTestUser" + new Random().Next(); + string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); + + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register"); + + this.Driver.FindElement(By.Id("text")).SendKeys(username); + + this.Driver.FindElement(By.Id("password")).SendKeys(password); + this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password + "a"); + + this.Driver.FindElement(By.Id("submit")).Click(); + + User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); + Assert.Null(user); + } + + [DatabaseFact] + public async Task ShouldNotRegisterWithTakenUsername() + { + await using Database database = new(); + + string username = "unitTestUser" + new Random().Next(); + string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); + + await database.CreateUser(username, HashHelper.BCryptHash(password)); + User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); + Assert.NotNull(user); + + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register"); + + this.Driver.FindElement(By.Id("text")).SendKeys(username); + + this.Driver.FindElement(By.Id("password")).SendKeys(password); + this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password); + + this.Driver.FindElement(By.Id("submit")).Click(); + + Assert.Contains("The username you've chosen is already taken.", this.Driver.PageSource); + } + } +} \ No newline at end of file From 456921d0c7cc6c589e98600b95b3a64a57cb335b Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 23 Dec 2021 01:46:10 -0500 Subject: [PATCH 13/18] Add admin panel testing --- .../AdminTests.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 ProjectLighthouse.Tests.WebsiteTests/AdminTests.cs diff --git a/ProjectLighthouse.Tests.WebsiteTests/AdminTests.cs b/ProjectLighthouse.Tests.WebsiteTests/AdminTests.cs new file mode 100644 index 00000000..83d2118b --- /dev/null +++ b/ProjectLighthouse.Tests.WebsiteTests/AdminTests.cs @@ -0,0 +1,64 @@ +using System; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Tests; +using LBPUnion.ProjectLighthouse.Types; +using OpenQA.Selenium; +using Xunit; + +namespace ProjectLighthouse.Tests.WebsiteTests +{ + public class AdminTests : LighthouseWebTest + { + public const string AdminPanelButtonXPath = "/html/body/div/header/div/div/div/a[2]"; + + [DatabaseFact] + public async Task ShouldShowAdminPanelButtonWhenAdmin() + { + await using Database database = new(); + Random random = new(); + User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); + + WebToken webToken = new() + { + UserId = user.UserId, + UserToken = HashHelper.GenerateAuthToken(), + }; + + database.WebTokens.Add(webToken); + user.IsAdmin = true; + await database.SaveChangesAsync(); + + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/"); + this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); + this.Driver.Navigate().Refresh(); + + Assert.Contains("Admin Panel", this.Driver.FindElement(By.XPath(AdminPanelButtonXPath)).Text); + } + + [DatabaseFact] + public async Task ShouldNotShowAdminPanelButtonWhenNotAdmin() + { + await using Database database = new(); + Random random = new(); + User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); + + WebToken webToken = new() + { + UserId = user.UserId, + UserToken = HashHelper.GenerateAuthToken(), + }; + + database.WebTokens.Add(webToken); + user.IsAdmin = false; + await database.SaveChangesAsync(); + + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/"); + this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); + this.Driver.Navigate().Refresh(); + + Assert.DoesNotContain("Admin Panel", this.Driver.FindElement(By.XPath(AdminPanelButtonXPath)).Text); + } + } +} \ No newline at end of file From 9f57eb1c115ee28c2c0403fcc1d7877d937ec1ab Mon Sep 17 00:00:00 2001 From: Michael Youngling <89745384+m88youngling@users.noreply.github.com> Date: Wed, 29 Dec 2021 14:04:49 -0500 Subject: [PATCH 14/18] Update README.md Updated the leading description to reflect the updated description of the GitHub project, added links to our contact page and the Project Lighthouse questions/discussion megathread. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 9ece56f8..d87d9bf7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Project Lighthouse -Project Lighthouse is an umbrella project for all work to investigate and develop private servers for LittleBigPlanet. -This project is the main server component that LittleBigPlanet games connect to. +Project Lighthouse is a clean-room, open-source custom server for LittleBigPlanet. This is a project conducted by the [LBP Union Ministry of Technology Research and Development team.](https://www.lbpunion.com/technology) For concerns and inquiries about the project, please [contact us here.](https://www.lbpunion.com/contact) For general questions and discussion about Project Lighthouse, please see the [megathread](https://www.lbpunion.com/forum/union-hall/project-lighthouse-littlebigplanet-private-servers-megathread) on our forum. ## WARNING! From ace4ef8f5b48e4b7d4e88ccc1442c4b51b4fec34 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sun, 9 Jan 2022 00:48:06 -0500 Subject: [PATCH 15/18] Remove most duplicated code --- .../Controllers/SlotsController.cs | 282 ++++++------------ 1 file changed, 96 insertions(+), 186 deletions(-) diff --git a/ProjectLighthouse/Controllers/SlotsController.cs b/ProjectLighthouse/Controllers/SlotsController.cs index 55923f40..ba047e86 100644 --- a/ProjectLighthouse/Controllers/SlotsController.cs +++ b/ProjectLighthouse/Controllers/SlotsController.cs @@ -91,7 +91,15 @@ namespace LBPUnion.ProjectLighthouse.Controllers [HttpGet("slots/lbp2cool")] [HttpGet("slots/cool")] - public async Task CoolSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] int? page = null) + public async Task CoolSlots + ( + [FromQuery] int pageStart, + [FromQuery] int pageSize, + [FromQuery] string gameFilterType, + [FromQuery] int players, + [FromQuery] Boolean move, + [FromQuery] int? page = null + ) { int _pageStart = pageStart; if (page != null) _pageStart = (int)page * 30; @@ -206,65 +214,19 @@ namespace LBPUnion.ProjectLighthouse.Controllers } [HttpGet("slots/thumbs")] - public async Task ThumbsSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null) + public async Task ThumbsSlots + ( + [FromQuery] int pageStart, + [FromQuery] int pageSize, + [FromQuery] string gameFilterType, + [FromQuery] int players, + [FromQuery] Boolean move, + [FromQuery] string? dateFilterType = null + ) { + Random rand = new(); - - long oldestTime; - - string _dateFilterType = dateFilterType != null ? dateFilterType : ""; - - switch (_dateFilterType) - { - case "thisWeek": - oldestTime = DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(); - break; - case "thisMonth": - oldestTime = DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(); - break; - default: - oldestTime = 0; - break; - } - - Random rand = new(); GameVersion gameVersion; - - switch (gameFilterType) - { - case "lbp1": - gameVersion = GameVersion.LittleBigPlanet1; - break; - case "lbp2": - gameVersion = GameVersion.LittleBigPlanet2; - break; - case "lbp3": - gameVersion = GameVersion.LittleBigPlanet3; - break; - case "both": // LBP2 default option - gameVersion = GameVersion.LittleBigPlanet2; - break; - default: - gameVersion = GameVersion.Unknown; - break; - } - - IQueryable whereSlots; - - if (gameFilterType == "both") - { - // Get game versions less than the current version - // Needs support for LBP3 ("both" = LBP1+2) - whereSlots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime); - } - else - { - // Get game versions exactly equal to gamefiltertype - whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime); - } - - IEnumerable slots = whereSlots - .Include(s => s.Creator) - .Include(s => s.Location) + IEnumerable slots = FilterByRequest(gameFilterType, dateFilterType) .AsEnumerable() .OrderByDescending(s => s.Thumbsup) .ThenBy(_ => rand.Next()) @@ -277,84 +239,35 @@ namespace LBPUnion.ProjectLighthouse.Controllers } [HttpGet("slots/mostUniquePlays")] - public async Task MostUniquePlaysSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null) + public async Task MostUniquePlaysSlots + ( + [FromQuery] int pageStart, + [FromQuery] int pageSize, + [FromQuery] string gameFilterType, + [FromQuery] int players, + [FromQuery] Boolean move, + [FromQuery] string? dateFilterType = null + ) { - - long oldestTime; - - string _dateFilterType = dateFilterType != null ? dateFilterType : ""; - - switch (_dateFilterType) - { - case "thisWeek": - oldestTime = DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(); - break; - case "thisMonth": - oldestTime = DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(); - break; - default: - oldestTime = 0; - break; - } - Random rand = new(); - GameVersion gameVersion; - - switch (gameFilterType) - { - case "lbp1": - gameVersion = GameVersion.LittleBigPlanet1; - break; - case "lbp2": - gameVersion = GameVersion.LittleBigPlanet2; - break; - case "lbp3": - gameVersion = GameVersion.LittleBigPlanet3; - break; - case "both": // LBP2 default option - gameVersion = GameVersion.LittleBigPlanet2; - break; - default: - gameVersion = GameVersion.Unknown; - break; - } - - IQueryable whereSlots; - - if (gameFilterType == "both") - { - // Get game versions less than the current version - // Needs support for LBP3 ("both" = LBP1+2) - whereSlots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime); - } - else - { - // Get game versions exactly equal to gamefiltertype - whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime); - } - - IEnumerable slots = whereSlots - .Include(s => s.Creator) - .Include(s => s.Location) + IEnumerable slots = FilterByRequest(gameFilterType, dateFilterType) .AsEnumerable() - .OrderByDescending(s => - { - // probably not the best way to do this? - switch (gameVersion) + .OrderByDescending + ( + s => { - case GameVersion.LittleBigPlanet1: - return s.PlaysLBP1Unique; - case GameVersion.LittleBigPlanet2: - return s.PlaysLBP2Unique; - case GameVersion.LittleBigPlanet3: - return s.PlaysLBP3Unique; - case GameVersion.LittleBigPlanetVita: - return s.PlaysLBPVitaUnique; - default: - return s.PlaysUnique; + // probably not the best way to do this? + return GetGameFilter(gameFilterType) switch + { + GameVersion.LittleBigPlanet1 => s.PlaysLBP1Unique, + GameVersion.LittleBigPlanet2 => s.PlaysLBP2Unique, + GameVersion.LittleBigPlanet3 => s.PlaysLBP3Unique, + GameVersion.LittleBigPlanetVita => s.PlaysLBPVitaUnique, + _ => s.PlaysUnique, + }; } - }) + ) .ThenBy(_ => rand.Next()) .Skip(pageStart - 1) .Take(Math.Min(pageSize, 30)); @@ -365,64 +278,19 @@ namespace LBPUnion.ProjectLighthouse.Controllers } [HttpGet("slots/mostHearted")] - public async Task MostHeartedSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null) + public async Task MostHeartedSlots + ( + [FromQuery] int pageStart, + [FromQuery] int pageSize, + [FromQuery] string gameFilterType, + [FromQuery] int players, + [FromQuery] Boolean move, + [FromQuery] string? dateFilterType = null + ) { + Random rand = new(); - long oldestTime; - - string _dateFilterType = dateFilterType != null ? dateFilterType : ""; - - switch (_dateFilterType) - { - case "thisWeek": - oldestTime = DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(); - break; - case "thisMonth": - oldestTime = DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(); - break; - default: - oldestTime = 0; - break; - } - - Random rand = new(); GameVersion gameVersion; - - switch (gameFilterType) - { - case "lbp1": - gameVersion = GameVersion.LittleBigPlanet1; - break; - case "lbp2": - gameVersion = GameVersion.LittleBigPlanet2; - break; - case "lbp3": - gameVersion = GameVersion.LittleBigPlanet3; - break; - case "both": // LBP2 default option - gameVersion = GameVersion.LittleBigPlanet2; - break; - default: - gameVersion = GameVersion.Unknown; - break; - } - - IQueryable whereSlots; - - if (gameFilterType == "both") - { - // Get game versions less than the current version - // Needs support for LBP3 ("both" = LBP1+2) - whereSlots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime); - } - else - { - // Get game versions exactly equal to gamefiltertype - whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime); - } - - IEnumerable slots = whereSlots - .Include(s => s.Creator) - .Include(s => s.Location) + IEnumerable slots = FilterByRequest(gameFilterType, dateFilterType) .AsEnumerable() .OrderByDescending(s => s.Hearts) .ThenBy(_ => rand.Next()) @@ -434,5 +302,47 @@ namespace LBPUnion.ProjectLighthouse.Controllers return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30))); } + public GameVersion GetGameFilter(string gameFilterType) + { + return gameFilterType switch + { + "lbp1" => GameVersion.LittleBigPlanet1, + "lbp2" => GameVersion.LittleBigPlanet2, + "lbp3" => GameVersion.LittleBigPlanet3, + "both" => GameVersion.LittleBigPlanet2, // LBP2 default option + _ => GameVersion.Unknown, + }; + } + + public IQueryable FilterByRequest(string gameFilterType, string? dateFilterType) + { + string _dateFilterType = dateFilterType ?? ""; + + long oldestTime = _dateFilterType switch + { + "thisWeek" => DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(), + "thisMonth" => DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(), + _ => 0, + }; + + GameVersion gameVersion = GetGameFilter(gameFilterType); + + IQueryable whereSlots; + + // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression + if (gameFilterType == "both") + { + // Get game versions less than the current version + // Needs support for LBP3 ("both" = LBP1+2) + whereSlots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime); + } + else + { + // Get game versions exactly equal to gamefiltertype + whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime); + } + + return whereSlots.Include(s => s.Creator).Include(s => s.Location); + } } } \ No newline at end of file From 11297b3f066b68e72e3f89f39d9a2ab7802dbae0 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sun, 9 Jan 2022 22:41:39 -0500 Subject: [PATCH 16/18] Allow more customization of the eula & announce screens --- .../Controllers/MessageController.cs | 15 ++++++++++----- .../Types/Settings/ServerSettings.cs | 8 +++++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/ProjectLighthouse/Controllers/MessageController.cs b/ProjectLighthouse/Controllers/MessageController.cs index c16f6921..98680ffb 100644 --- a/ProjectLighthouse/Controllers/MessageController.cs +++ b/ProjectLighthouse/Controllers/MessageController.cs @@ -28,7 +28,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers User? user = await this.database.UserFromGameRequest(this.Request); if (user == null) return this.StatusCode(403, ""); - return this.Ok(ServerSettings.Instance.EulaText + "\n" + $"{EulaHelper.License}\n"); + return this.Ok($"{EulaHelper.License}\n{ServerSettings.Instance.EulaText}"); } [HttpGet("announce")] @@ -47,19 +47,24 @@ namespace LBPUnion.ProjectLighthouse.Controllers GameToken gameToken = userAndToken.Value.Item2; #endif + string announceText = ServerSettings.Instance.AnnounceText; + + announceText = announceText.Replace("%user", user.Username); + announceText = announceText.Replace("%id", user.UserId.ToString()); + return this.Ok ( - $"You are now logged in as {user.Username}.\n\n" + + announceText + #if DEBUG - "---DEBUG INFO---\n" + + "\n\n---DEBUG INFO---\n" + $"user.UserId: {user.UserId}\n" + $"token.Approved: {gameToken.Approved}\n" + $"token.Used: {gameToken.Used}\n" + $"token.UserLocation: {gameToken.UserLocation}\n" + $"token.GameVersion: {gameToken.GameVersion}\n" + - "---DEBUG INFO---\n\n" + + "---DEBUG INFO---" + #endif - ServerSettings.Instance.EulaText + "\n" ); } diff --git a/ProjectLighthouse/Types/Settings/ServerSettings.cs b/ProjectLighthouse/Types/Settings/ServerSettings.cs index bdbb7676..67a3aeec 100644 --- a/ProjectLighthouse/Types/Settings/ServerSettings.cs +++ b/ProjectLighthouse/Types/Settings/ServerSettings.cs @@ -12,7 +12,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings public class ServerSettings { - public const int CurrentConfigVersion = 13; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE! + public const int CurrentConfigVersion = 14; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE! static ServerSettings() { if (ServerStatics.IsUnitTesting) return; // Unit testing, we don't want to read configurations here since the tests will provide their own @@ -74,6 +74,12 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings public string EulaText { get; set; } = ""; + #if !DEBUG + public string AnnounceText { get; set; } = "You are now logged in as %user."; + #else + public string AnnounceText { get; set; } = "You are now logged in as %user (id: %id)."; + #endif + public string DbConnectionString { get; set; } = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; public string ExternalUrl { get; set; } = "http://localhost:10060"; From 1f2d9f3303f07fd64937d3df5326dbef311e8be0 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sun, 9 Jan 2022 23:05:32 -0500 Subject: [PATCH 17/18] Dont refer to DeleteUserCommand as banning --- ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs b/ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs index d7c7802c..740c5c99 100644 --- a/ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs +++ b/ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs @@ -11,11 +11,11 @@ namespace LBPUnion.ProjectLighthouse.Maintenance.Commands public class DeleteUserCommand : ICommand { private readonly Database database = new(); - public string Name() => "Delete/Ban User"; + public string Name() => "Delete User"; public string[] Aliases() => new[] { - "deleteUser", "wipeUser", "banUser", + "deleteUser", "wipeUser", }; public string Arguments() => ""; public int RequiredArgs() => 1; From 960f26b95c1edf352ebd0757b6208ee0bca1b827 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sun, 9 Jan 2022 23:43:07 -0500 Subject: [PATCH 18/18] Add ban confirmation page --- .../Website/Admin/AdminUserController.cs | 26 +--------- .../Pages/Admin/AdminBanUserPage.cshtml | 20 ++++++++ .../Pages/Admin/AdminBanUserPage.cshtml.cs | 49 +++++++++++++++++++ .../Pages/{ => Admin}/AdminPanelPage.cshtml | 2 +- .../{ => Admin}/AdminPanelPage.cshtml.cs | 2 +- .../Pages/PasswordResetRequiredPage.cshtml | 2 +- 6 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml create mode 100644 ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml.cs rename ProjectLighthouse/Pages/{ => Admin}/AdminPanelPage.cshtml (96%) rename ProjectLighthouse/Pages/{ => Admin}/AdminPanelPage.cshtml.cs (96%) diff --git a/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs b/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs index 96e74702..e92266fe 100644 --- a/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs +++ b/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs @@ -1,5 +1,4 @@ #nullable enable -using System.Linq; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; @@ -18,34 +17,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin this.database = database; } - [HttpGet("ban")] - public async Task BanUser([FromRoute] int id) - { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); - - User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); - ; - if (targetedUser == null) return this.NotFound(); - - targetedUser.Banned = true; - targetedUser.BannedReason = $"Banned by admin {user.Username} (id: {user.UserId})"; - - // invalidate all currently active gametokens - this.database.GameTokens.RemoveRange(this.database.GameTokens.Where(t => t.UserId == targetedUser.UserId)); - - // invalidate all currently active webtokens - this.database.WebTokens.RemoveRange(this.database.WebTokens.Where(t => t.UserId == targetedUser.UserId)); - - await this.database.SaveChangesAsync(); - return this.Redirect($"/user/{targetedUser.UserId}"); - } - [HttpGet("unban")] public async Task UnbanUser([FromRoute] int id) { User? user = this.database.UserFromWebRequest(this.Request); - if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); + if (user == null || !user.IsAdmin) return this.NotFound(); User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); ; diff --git a/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml b/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml new file mode 100644 index 00000000..2fd9e27a --- /dev/null +++ b/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml @@ -0,0 +1,20 @@ +@page "/admin/user/{id:int}/ban" +@model LBPUnion.ProjectLighthouse.Pages.Admin.AdminBanUserPage + +@{ + Layout = "Layouts/BaseLayout"; + Model.Title = "Ban " + Model.TargetedUser!.Username + "?"; +} + +

Are you sure you want to ban this user?

+ +
+ @Html.AntiForgeryToken() + +
+ + +


+ +
+
\ No newline at end of file diff --git a/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml.cs b/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml.cs new file mode 100644 index 00000000..34eff362 --- /dev/null +++ b/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml.cs @@ -0,0 +1,49 @@ +#nullable enable +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.Admin; + +public class AdminBanUserPage : BaseLayout +{ + public AdminBanUserPage(Database database) : base(database) + {} + + public User? TargetedUser; + + public async Task OnGet([FromRoute] int id) + { + User? user = this.Database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.NotFound(); + + this.TargetedUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == id); + if (this.TargetedUser == null) return this.NotFound(); + + return this.Page(); + } + + public async Task OnPost([FromRoute] int id, string reason) + { + User? user = this.Database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.NotFound(); + + this.TargetedUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == id); + if (this.TargetedUser == null) return this.NotFound(); + + this.TargetedUser.Banned = true; + this.TargetedUser.BannedReason = reason; + + // invalidate all currently active gametokens + this.Database.GameTokens.RemoveRange(this.Database.GameTokens.Where(t => t.UserId == this.TargetedUser.UserId)); + + // invalidate all currently active webtokens + this.Database.WebTokens.RemoveRange(this.Database.WebTokens.Where(t => t.UserId == this.TargetedUser.UserId)); + + await this.Database.SaveChangesAsync(); + return this.Redirect($"/user/{this.TargetedUser.UserId}"); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/AdminPanelPage.cshtml b/ProjectLighthouse/Pages/Admin/AdminPanelPage.cshtml similarity index 96% rename from ProjectLighthouse/Pages/AdminPanelPage.cshtml rename to ProjectLighthouse/Pages/Admin/AdminPanelPage.cshtml index 891e8891..9321859f 100644 --- a/ProjectLighthouse/Pages/AdminPanelPage.cshtml +++ b/ProjectLighthouse/Pages/Admin/AdminPanelPage.cshtml @@ -1,7 +1,7 @@ @page "/admin" @using LBPUnion.ProjectLighthouse.Helpers @using LBPUnion.ProjectLighthouse.Maintenance -@model LBPUnion.ProjectLighthouse.Pages.AdminPanelPage +@model LBPUnion.ProjectLighthouse.Pages.Admin.AdminPanelPage @{ Layout = "Layouts/BaseLayout"; diff --git a/ProjectLighthouse/Pages/AdminPanelPage.cshtml.cs b/ProjectLighthouse/Pages/Admin/AdminPanelPage.cshtml.cs similarity index 96% rename from ProjectLighthouse/Pages/AdminPanelPage.cshtml.cs rename to ProjectLighthouse/Pages/Admin/AdminPanelPage.cshtml.cs index e12a2b4a..8881f236 100644 --- a/ProjectLighthouse/Pages/AdminPanelPage.cshtml.cs +++ b/ProjectLighthouse/Pages/Admin/AdminPanelPage.cshtml.cs @@ -7,7 +7,7 @@ using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Pages +namespace LBPUnion.ProjectLighthouse.Pages.Admin { public class AdminPanelPage : BaseLayout { diff --git a/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml b/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml index 616d2f8a..afe20489 100644 --- a/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml +++ b/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml @@ -6,7 +6,7 @@ Model.Title = "Password Reset Required"; } -

An admin has deemed it necessary that you reset your password. Please do so.

+

An administrator has deemed it necessary that you reset your password. Please do so.

Reset Password