From b3a00da5541cb033e965ac9b472dcfbca2ffc2b5 Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 10 Nov 2022 21:14:16 -0600 Subject: [PATCH] Refactor deserialization and authentication (#550) * Refactor deserialization and more * Refactor authentication flow * Fix unit tests * Make deserialization better --- .../ClientConfigurationController.cs | 20 ++- .../Controllers/CommentController.cs | 139 +++++++++--------- .../Controllers/DeveloperController.cs | 20 +-- .../Controllers/FriendsController.cs | 14 +- .../Controllers/LoginController.cs | 9 +- .../Controllers/LogoutController.cs | 7 +- .../Matching/EnterLevelController.cs | 24 +-- .../Controllers/Matching/MatchController.cs | 126 ++++++++-------- .../Controllers/MessageController.cs | 22 +-- .../Controllers/ReportController.cs | 20 ++- .../Controllers/Resources/PhotosController.cs | 39 ++--- .../Resources/ResourcesController.cs | 35 ++--- .../Controllers/Slots/CollectionController.cs | 53 ++----- .../Controllers/Slots/LevelTagsController.cs | 14 +- .../Controllers/Slots/ListController.cs | 43 ++---- .../Controllers/Slots/PublishController.cs | 51 +++---- .../Controllers/Slots/ReviewController.cs | 43 ++---- .../Controllers/Slots/ScoreController.cs | 16 +- .../Controllers/Slots/SearchController.cs | 9 +- .../Controllers/Slots/SlotsController.cs | 77 ++++------ .../Controllers/StatisticsController.cs | 2 + .../Controllers/StoreController.cs | 4 +- .../Controllers/UserController.cs | 47 +++--- .../Startup/GameServerStartup.cs | 20 ++- .../Startup/TokenAuthHandler.cs | 49 ++++++ .../Admin/AdminReportController.cs | 3 +- .../Admin/ModerationSlotController.cs | 4 + .../Controllers/ResourcesController.cs | 3 +- .../Pages/Admin/AdminPanelUsersPage.cshtml | 3 +- .../Pages/LoginForm.cshtml.cs | 8 +- .../Pages/Moderation/ModPanelPage.cshtml | 2 +- .../Pages/Partials/SlotCardPartial.cshtml | 2 +- .../Pages/SendVerificationEmailPage.cshtml.cs | 2 +- .../Pages/SlotSettingsPage.cshtml.cs | 2 +- .../Pages/UserPage.cshtml.cs | 2 +- .../Pages/UserSettingsPage.cshtml | 2 +- .../Pages/UserSettingsPage.cshtml.cs | 2 +- .../Tests/SlotTests.cs | 3 - .../Maintenance/MaintenanceHelper.cs | 2 +- .../RepeatingTasks/PerformCaseActionsTask.cs | 2 +- .../Configuration/ServerConfiguration.cs | 1 - ProjectLighthouse/Database.cs | 131 +++++++++-------- .../Extensions/ControllerExtensions.cs | 58 ++++++++ ProjectLighthouse/Files/FileHelper.cs | 10 +- ProjectLighthouse/Helpers/CryptoHelper.cs | 2 +- ProjectLighthouse/Logging/LogArea.cs | 1 + ProjectLighthouse/PlayerData/Profiles/User.cs | 14 +- ProjectLighthouse/StartupTasks.cs | 2 +- 48 files changed, 575 insertions(+), 589 deletions(-) create mode 100644 ProjectLighthouse.Servers.GameServer/Startup/TokenAuthHandler.cs create mode 100644 ProjectLighthouse/Extensions/ControllerExtensions.cs diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs index d633402b..923d6fac 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs @@ -1,14 +1,17 @@ #nullable enable using System.Diagnostics.CodeAnalysis; -using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/plain")] public class ClientConfigurationController : ControllerBase @@ -22,11 +25,8 @@ public class ClientConfigurationController : ControllerBase [HttpGet("network_settings.nws")] [SuppressMessage("ReSharper", "StringLiteralTypo")] - public async Task NetworkSettings() + public IActionResult NetworkSettings() { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - string hostname = ServerConfiguration.Instance.GameApiExternalUrl; return this.Ok ( @@ -52,7 +52,9 @@ public class ClientConfigurationController : ControllerBase [Produces("text/xml")] public async Task GetPrivacySettings() { - User? user = await this.database.UserFromGameRequest(this.Request); + GameToken token = this.GetToken(); + + User? user = await this.database.UserFromGameToken(token); if (user == null) return this.StatusCode(403, ""); PrivacySettings ps = new() @@ -71,11 +73,7 @@ public class ClientConfigurationController : ControllerBase User? user = await this.database.UserFromGameRequest(this.Request); if (user == null) return this.StatusCode(403, ""); - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - XmlSerializer serializer = new(typeof(PrivacySettings)); - PrivacySettings? settings = (PrivacySettings?)serializer.Deserialize(new StringReader(bodyString)); + PrivacySettings? settings = await this.DeserializeBody(); if (settings == null) return this.BadRequest(); if (settings.LevelVisibility != null) diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs index 968948a4..9318758d 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs @@ -1,17 +1,19 @@ #nullable enable -using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] public class CommentController : ControllerBase @@ -26,10 +28,10 @@ public class CommentController : ControllerBase [HttpPost("rateComment/{slotType}/{slotId:int}")] public async Task RateComment([FromQuery] int commentId, [FromQuery] int rating, string? username, string? slotType, int slotId) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); - if (username == null && (SlotHelper.IsTypeInvalid(slotType) || slotId == 0)) return this.BadRequest(); + // Return bad request if both are true or both are false + if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest(); bool success = await this.database.RateComment(token.UserId, commentId, rating); if (!success) return this.BadRequest(); @@ -41,37 +43,33 @@ public class CommentController : ControllerBase [HttpGet("userComments/{username}")] public async Task GetComments([FromQuery] int pageStart, [FromQuery] int pageSize, string? username, string? slotType, int slotId) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); - if (pageSize <= 0) return this.BadRequest(); + if (pageSize <= 0 || pageStart < 0) return this.BadRequest(); - int targetId = slotId; - CommentType type = CommentType.Level; - if (!string.IsNullOrWhiteSpace(username)) + if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest(); + + if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer); + + int targetId; + CommentType type = username == null ? CommentType.Level : CommentType.Profile; + + if (type == CommentType.Level) { - targetId = this.database.Users.First(u => u.Username.Equals(username)).UserId; - type = CommentType.Profile; + targetId = await this.database.Slots.Where(s => s.SlotId == slotId) + .Where(s => s.CommentsEnabled && !s.Hidden) + .Select(s => s.SlotId) + .FirstOrDefaultAsync(); } else { - if (SlotHelper.IsTypeInvalid(slotType) || slotId == 0) return this.BadRequest(); + targetId = await this.database.Users.Where(u => u.Username == username) + .Where(u => u.CommentsEnabled) + .Select(u => u.UserId) + .FirstOrDefaultAsync(); } - if (type == CommentType.Level && slotType == "developer") targetId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer); - - if (type == CommentType.Profile) - { - User? profile = await this.database.Users.FirstOrDefaultAsync(s => s.UserId == targetId); - if (profile == null) return this.BadRequest(); - if (!profile.CommentsEnabled) return this.NotFound(); - } - else - { - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == targetId); - if (slot == null) return this.BadRequest(); - if (!slot.CommentsEnabled) return this.NotFound(); - } + if (targetId == 0) return this.NotFound(); List comments = await this.database.Comments.Include (c => c.Poster) @@ -88,7 +86,8 @@ public class CommentController : ControllerBase private async Task getReaction(int userId, int commentId) { - return await this.database.Reactions.Where(r => r.UserId == userId && r.TargetId == commentId) + return await this.database.Reactions.Where(r => r.UserId == userId) + .Where(r => r.TargetId == commentId) .Select(r => r.Rating) .FirstOrDefaultAsync(); } @@ -97,28 +96,28 @@ public class CommentController : ControllerBase [HttpPost("postComment/{slotType}/{slotId:int}")] public async Task PostComment(string? username, string? slotType, int slotId) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - XmlSerializer serializer = new(typeof(Comment)); - Comment? comment = (Comment?)serializer.Deserialize(new StringReader(bodyString)); - - SanitizationHelper.SanitizeStringsInClass(comment); - - CommentType type = (slotId == 0 ? CommentType.Profile : CommentType.Level); - - if (type == CommentType.Level && (SlotHelper.IsTypeInvalid(slotType) || slotId == 0)) return this.BadRequest(); + GameToken token = this.GetToken(); + Comment? comment = await this.DeserializeBody(); if (comment == null) return this.BadRequest(); - int targetId = slotId; + if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest(); - if (type == CommentType.Profile) targetId = this.database.Users.First(u => u.Username == username).UserId; + CommentType type = username == null ? CommentType.Level : CommentType.Profile; - if (slotType == "developer") targetId = await SlotHelper.GetPlaceholderSlotId(this.database, targetId, SlotType.Developer); + int targetId; + if (type == CommentType.Level) + { + slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer); + targetId = await this.database.Slots.Where(s => s.SlotId == slotId) + .Where(s => s.CommentsEnabled && !s.Hidden) + .Select(s => s.SlotId) + .FirstOrDefaultAsync(); + } + else + { + targetId = await this.database.UserIdFromUsername(username!); + } bool success = await this.database.PostComment(token.UserId, targetId, type, comment.Message); if (success) return this.Ok(); @@ -130,44 +129,44 @@ public class CommentController : ControllerBase [HttpPost("deleteComment/{slotType}/{slotId:int}")] public async Task DeleteComment([FromQuery] int commentId, string? username, string? slotType, int slotId) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); + + if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest(); Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId); if (comment == null) return this.NotFound(); - if (comment.Type == CommentType.Level && (SlotHelper.IsTypeInvalid(slotType) || slotId == 0)) return this.BadRequest(); + if (comment.Deleted) return this.Ok(); - if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer); - - // if you are not the poster - if (comment.PosterUserId != token.UserId) + bool canDelete; + if (comment.Type == CommentType.Profile) { - if (comment.Type == CommentType.Profile) - { - // if you aren't the poster and aren't the profile owner - if (comment.TargetId != token.UserId) - { - return this.StatusCode(403, ""); - } - } - else - { - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == comment.TargetId); - // if you aren't the creator of the level - if (slot == null || slot.CreatorId != token.UserId || slotId != slot.SlotId) - { - return this.StatusCode(403, ""); - } - } + canDelete = comment.PosterUserId == token.UserId || comment.TargetId == token.UserId; } + else + { + if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer); + + if (slotId != comment.TargetId) return this.BadRequest(); + + int slotCreator = await this.database.Slots.Where(s => s.SlotId == comment.TargetId) + .Where(s => s.CommentsEnabled) + .Select(s => s.CreatorId) + .FirstOrDefaultAsync(); + + // Comments are disabled or the slot doesn't have a creator + if (slotCreator == 0) return this.BadRequest(); + + canDelete = comment.PosterUserId == token.UserId || slotCreator == token.UserId; + } + + if (!canDelete) return this.StatusCode(403, ""); comment.Deleted = true; comment.DeletedBy = await this.database.UsernameFromGameToken(token); comment.DeletedType = "user"; await this.database.SaveChangesAsync(); - return this.Ok(); } } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/DeveloperController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/DeveloperController.cs index 457eb4e4..e515b843 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/DeveloperController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/DeveloperController.cs @@ -1,28 +1,14 @@ -using LBPUnion.ProjectLighthouse.PlayerData; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] public class DeveloperController : Controller { - - private readonly Database database; - - public DeveloperController(Database database) - { - this.database = database; - } - [HttpGet("developer_videos")] - public async Task DeveloperVideos() - { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - - if (token == null) return this.StatusCode(403, ""); - - return this.Ok(""); - } + public IActionResult DeveloperVideos() => this.Ok(""); } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs index b86d6a79..15844430 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs @@ -1,16 +1,18 @@ #nullable enable -using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.StorableLists.Stores; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] public class FriendsController : ControllerBase { @@ -24,14 +26,9 @@ public class FriendsController : ControllerBase [HttpPost("npdata")] public async Task NPData() { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - XmlSerializer serializer = new(typeof(NPData)); - NPData? npData = (NPData?)serializer.Deserialize(new StringReader(bodyString)); + NPData? npData = await this.DeserializeBody(); if (npData == null) return this.BadRequest(); SanitizationHelper.SanitizeStringsInClass(npData); @@ -73,7 +70,6 @@ public class FriendsController : ControllerBase if (userAndToken == null) return this.StatusCode(403, ""); - // ReSharper disable once PossibleInvalidOperationException User user = userAndToken.Value.Item1; GameToken gameToken = userAndToken.Value.Item2; diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/LoginController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/LoginController.cs index 18fd3244..11ae7662 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/LoginController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/LoginController.cs @@ -72,7 +72,14 @@ public class LoginController : ControllerBase } } - User? user = await this.database.UserFromGameToken(token, true); + // The GameToken LINQ statement above is case insensitive so we check that they are equal here + if (token.User.Username != npTicket.Username) + { + Logger.Warn($"Username case does not match for user {npTicket.Username}, expected={token.User.Username}", LogArea.Login); + return this.StatusCode(403, ""); + } + + User? user = await this.database.UserFromGameToken(token); if (user == null || user.IsBanned) { diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/LogoutController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/LogoutController.cs index 72dba258..3ecfcb6e 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/LogoutController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/LogoutController.cs @@ -2,12 +2,14 @@ using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/goodbye")] [Produces("text/xml")] public class LogoutController : ControllerBase @@ -23,10 +25,9 @@ public class LogoutController : ControllerBase [HttpPost] public async Task OnPost() { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); - User? user = await this.database.Users.Where(u => u.UserId == token.UserId).FirstOrDefaultAsync(); + User? user = await this.database.UserFromGameToken(token); if (user == null) return this.StatusCode(403, ""); user.LastLogout = TimeHelper.TimestampMillis; diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs index 12aee5a0..cb57e0c5 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs @@ -1,15 +1,18 @@ #nullable enable +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.PlayerData; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Matching; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] -// [Produces("text/plain")] +[Produces("text/xml")] public class EnterLevelController : ControllerBase { private readonly Database database; @@ -22,8 +25,7 @@ public class EnterLevelController : ControllerBase [HttpPost("play/{slotType}/{slotId:int}")] public async Task PlayLevel(string slotType, int slotId) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest(); @@ -33,13 +35,11 @@ public class EnterLevelController : ControllerBase Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); if (slot == null) return this.StatusCode(403, ""); - GameVersion gameVersion = token.GameVersion; - IQueryable visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == token.UserId); VisitedLevel? v; if (!visited.Any()) { - switch (gameVersion) + switch (token.GameVersion) { case GameVersion.LittleBigPlanet2: case GameVersion.LittleBigPlanetVita: @@ -48,6 +48,9 @@ public class EnterLevelController : ControllerBase case GameVersion.LittleBigPlanet3: slot.PlaysLBP3Unique++; break; + case GameVersion.LittleBigPlanet1: + case GameVersion.LittleBigPlanetPSP: + case GameVersion.Unknown: default: return this.BadRequest(); } @@ -65,7 +68,7 @@ public class EnterLevelController : ControllerBase if (v == null) return this.NotFound(); - switch (gameVersion) + switch (token.GameVersion) { case GameVersion.LittleBigPlanet2: case GameVersion.LittleBigPlanetVita: @@ -76,9 +79,9 @@ public class EnterLevelController : ControllerBase slot.PlaysLBP3++; v.PlaysLBP3++; break; - case GameVersion.LittleBigPlanetPSP: throw new NotImplementedException(); - case GameVersion.Unknown: case GameVersion.LittleBigPlanet1: + case GameVersion.LittleBigPlanetPSP: + case GameVersion.Unknown: default: return this.BadRequest(); } @@ -92,8 +95,7 @@ public class EnterLevelController : ControllerBase [HttpPost("enterLevel/{slotType}/{slotId:int}")] public async Task EnterLevel(string slotType, int slotId) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest(); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs index 3dcf785c..87e77543 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs @@ -8,12 +8,14 @@ using LBPUnion.ProjectLighthouse.Match.MatchCommands; using LBPUnion.ProjectLighthouse.Match.Rooms; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Matching; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] public class MatchController : ControllerBase @@ -27,26 +29,16 @@ public class MatchController : ControllerBase [HttpPost("gameState")] [Produces("text/plain")] - public async Task GameState() - { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - - if (token == null) return this.StatusCode(403, ""); - - return this.Ok("VALID"); - } + public IActionResult GameState() => this.Ok("VALID"); [HttpPost("match")] [Produces("text/plain")] public async Task Match() { - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + GameToken token = this.GetToken(); - if (userAndToken == null) return this.StatusCode(403, ""); - - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; + User? user = await this.database.UserFromGameToken(token); + if (user == null) return this.StatusCode(403, ""); #region Parse match data @@ -81,70 +73,74 @@ public class MatchController : ControllerBase #endregion - await LastContactHelper.SetLastContact(this.database, user, gameToken.GameVersion, gameToken.Platform); + await LastContactHelper.SetLastContact(this.database, user, token.GameVersion, token.Platform); #region Process match data - if (matchData is UpdateMyPlayerData playerData) + switch (matchData) { - MatchHelper.SetUserLocation(user.UserId, gameToken.UserLocation); - Room? room = RoomHelper.FindRoomByUser(user.UserId, gameToken.GameVersion, gameToken.Platform, true); - - if (playerData.RoomState != null) - if (room != null && Equals(room.HostId, user.UserId)) - room.State = (RoomState)playerData.RoomState; - } - - // Check how many people are online in release builds, disabled for debug for ..well debugging. - #if DEBUG - else if (matchData is FindBestRoom diveInData) - #else - else if (matchData is FindBestRoom diveInData && MatchHelper.UserLocations.Count > 1) - #endif - { - FindBestRoomResponse? response = RoomHelper.FindBestRoom - (user, gameToken.GameVersion, diveInData.RoomSlot, gameToken.Platform, gameToken.UserLocation); - - if (response == null) return this.NotFound(); - - string serialized = JsonSerializer.Serialize(response, typeof(FindBestRoomResponse)); - foreach (Player player in response.Players) MatchHelper.AddUserRecentlyDivedIn(user.UserId, player.User.UserId); - - return this.Ok($"[{{\"StatusCode\":200}},{serialized}]"); - } - - else if (matchData is CreateRoom createRoom && MatchHelper.UserLocations.Count >= 1) - { - List users = new(); - foreach (string playerUsername in createRoom.Players) + case UpdateMyPlayerData playerData: { - User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername); - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (player != null) users.Add(player.UserId); - else return this.BadRequest(); + MatchHelper.SetUserLocation(user.UserId, token.UserLocation); + Room? room = RoomHelper.FindRoomByUser(user.UserId, token.GameVersion, token.Platform, true); + + if (playerData.RoomState != null) + if (room != null && Equals(room.HostId, user.UserId)) + room.State = (RoomState)playerData.RoomState; + break; } - - // Create a new one as requested - RoomHelper.CreateRoom(users, gameToken.GameVersion, gameToken.Platform, createRoom.RoomSlot); - } - - else if (matchData is UpdatePlayersInRoom updatePlayersInRoom) - { - Room? room = RoomHelper.Rooms.FirstOrDefault(r => r.HostId == user.UserId); - - if (room != null) + // Check how many people are online in release builds, disabled for debug for ..well debugging. + #if DEBUG + case FindBestRoom diveInData: + #else + case FindBestRoom diveInData when MatchHelper.UserLocations.Count > 1: + #endif { - List users = new(); - foreach (string playerUsername in updatePlayersInRoom.Players) + FindBestRoomResponse? response = RoomHelper.FindBestRoom + (user, token.GameVersion, diveInData.RoomSlot, token.Platform, token.UserLocation); + + if (response == null) return this.NotFound(); + + string serialized = JsonSerializer.Serialize(response, typeof(FindBestRoomResponse)); + foreach (Player player in response.Players) MatchHelper.AddUserRecentlyDivedIn(user.UserId, player.User.UserId); + + return this.Ok($"[{{\"StatusCode\":200}},{serialized}]"); + } + case CreateRoom createRoom when MatchHelper.UserLocations.Count >= 1: + { + List users = new(); + foreach (string playerUsername in createRoom.Players) { User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername); // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (player != null) users.Add(player); + if (player != null) users.Add(player.UserId); else return this.BadRequest(); } - room.PlayerIds = users.Select(u => u.UserId).ToList(); - await RoomHelper.CleanupRooms(null, room); + // Create a new one as requested + RoomHelper.CreateRoom(users, token.GameVersion, token.Platform, createRoom.RoomSlot); + break; + } + case UpdatePlayersInRoom updatePlayersInRoom: + { + Room? room = RoomHelper.Rooms.FirstOrDefault(r => r.HostId == user.UserId); + + if (room != null) + { + List users = new(); + foreach (string playerUsername in updatePlayersInRoom.Players) + { + User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername); + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + if (player != null) users.Add(player); + else return this.BadRequest(); + } + + room.PlayerIds = users.Select(u => u.UserId).ToList(); + await RoomHelper.CleanupRooms(null, room); + } + + break; } } diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs index abd863ce..4d9ccc3a 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs @@ -1,14 +1,17 @@ #nullable enable using System.Globalization; using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.PlayerData; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/plain")] public class MessageController : ControllerBase @@ -35,20 +38,12 @@ along with this program. If not, see ."; } [HttpGet("eula")] - public async Task Eula() - { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - // ReSharper disable once ConvertIfStatementToReturnStatement - if (token == null) return this.StatusCode(403, ""); - - return this.Ok($"{license}\n{ServerConfiguration.Instance.EulaText}"); - } + public IActionResult Eula() => this.Ok($"{license}\n{ServerConfiguration.Instance.EulaText}"); [HttpGet("announce")] public async Task Announce() { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); string username = await this.database.UsernameFromGameToken(token); @@ -76,16 +71,15 @@ along with this program. If not, see ."; [HttpGet("notification")] public IActionResult Notification() => this.Ok(); + /// /// Filters chat messages sent by a user. - /// The reponse sent is the text that will appear in-game. + /// The response sent is the text that will appear in-game. /// [HttpPost("filter")] public async Task Filter() { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); string response = await new StreamReader(this.Request.Body).ReadToEndAsync(); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/ReportController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/ReportController.cs index 484837ba..e5de5e4b 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/ReportController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/ReportController.cs @@ -1,16 +1,18 @@ #nullable enable using System.Text.Json; -using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Administration.Reports; using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] public class ReportController : ControllerBase @@ -25,15 +27,11 @@ public class ReportController : ControllerBase [HttpPost("grief")] public async Task Report() { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - XmlSerializer serializer = new(typeof(GriefReport)); - GriefReport? report = (GriefReport?)serializer.Deserialize(new StringReader(bodyString)); + string username = await this.database.UsernameFromGameToken(token); + GriefReport? report = await this.DeserializeBody(); if (report == null) return this.BadRequest(); SanitizationHelper.SanitizeStringsInClass(report); @@ -41,14 +39,14 @@ public class ReportController : ControllerBase report.Bounds = JsonSerializer.Serialize(report.XmlBounds.Rect, typeof(Rectangle)); report.Players = JsonSerializer.Serialize(report.XmlPlayers, typeof(ReportPlayer[])); report.Timestamp = TimeHelper.UnixTimeMilliseconds(); - report.ReportingPlayerId = user.UserId; + report.ReportingPlayerId = token.UserId; this.database.Reports.Add(report); await this.database.SaveChangesAsync(); await WebhookHelper.SendWebhook( title: "New grief report", - description: $"Submitted by {user.Username}\n" + + description: $"Submitted by {username}\n" + $"To view it, click [here]({ServerConfiguration.Instance.ExternalUrl}/moderation/report/{report.ReportId}).", dest: WebhookHelper.WebhookDestination.Moderation ); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs index 044236ee..389996e2 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs @@ -1,5 +1,4 @@ #nullable enable -using System.Xml.Serialization; using Discord; using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Extensions; @@ -9,12 +8,14 @@ using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Resources; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] public class PhotosController : ControllerBase @@ -29,16 +30,12 @@ public class PhotosController : ControllerBase [HttpPost("uploadPhoto")] public async Task UploadPhoto() { - User? user = await this.database.UserFromGameRequest(this.Request); + User? user = await this.database.UserFromGameToken(this.GetToken()); if (user == null) return this.StatusCode(403, ""); if (user.PhotosByMe >= ServerConfiguration.Instance.UserGeneratedContentLimits.PhotosQuota) return this.BadRequest(); - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - XmlSerializer serializer = new(typeof(Photo)); - Photo? photo = (Photo?)serializer.Deserialize(new StringReader(bodyString)); + Photo? photo = await this.DeserializeBody(); if (photo == null) return this.BadRequest(); SanitizationHelper.SanitizeStringsInClass(photo); @@ -65,9 +62,9 @@ public class PhotosController : ControllerBase { // We'll grab the slot by the RootLevel and see what happens from here. Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == SlotType.User && s.ResourceCollection.Contains(photoSlot.RootLevel)); - if(slot == null) break; + if (slot == null) break; - if (!string.IsNullOrEmpty(slot!.RootLevel)) validLevel = true; + if (!string.IsNullOrEmpty(slot.RootLevel)) validLevel = true; if (slot.IsAdventurePlanet) photoSlot.SlotId = slot.SlotId; break; } @@ -83,6 +80,10 @@ public class PhotosController : ControllerBase validLevel = true; break; } + case SlotType.Moon: + case SlotType.Unknown: + case SlotType.Unknown2: + case SlotType.DLC: default: Logger.Warn($"Invalid photo level type: {photoSlot.SlotType}", LogArea.Photos); break; } @@ -103,10 +104,8 @@ public class PhotosController : ControllerBase subjectUserIds.Add(subject.Username); } - foreach (PhotoSubject subject in photo.Subjects) + foreach (PhotoSubject subject in photo.Subjects.Where(subject => !string.IsNullOrEmpty(subject.Username))) { - if (string.IsNullOrEmpty(subject.Username)) continue; - subject.User = await this.database.Users.FirstOrDefaultAsync(u => u.Username == subject.Username); if (subject.User == null) continue; @@ -144,9 +143,6 @@ public class PhotosController : ControllerBase [HttpGet("photos/{slotType}/{id:int}")] public async Task SlotPhotos([FromQuery] int pageStart, [FromQuery] int pageSize, string slotType, int id) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - if (pageSize <= 0) return this.BadRequest(); if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest(); @@ -166,12 +162,9 @@ public class PhotosController : ControllerBase [HttpGet("photos/by")] public async Task UserPhotosBy([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - if (pageSize <= 0) return this.BadRequest(); - int targetUserId = await this.database.Users.Where(u => u.Username == user).Select(u => u.UserId).FirstOrDefaultAsync(); + int targetUserId = await this.database.UserIdFromUsername(user); if (targetUserId == 0) return this.NotFound(); List photos = await this.database.Photos.Include @@ -188,12 +181,9 @@ public class PhotosController : ControllerBase [HttpGet("photos/with")] public async Task UserPhotosWith([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - if (pageSize <= 0) return this.BadRequest(); - int targetUserId = await this.database.Users.Where(u => u.Username == user).Select(u => u.UserId).FirstOrDefaultAsync(); + int targetUserId = await this.database.UserIdFromUsername(user); if (targetUserId == 0) return this.NotFound(); List photoSubjectIds = new(); @@ -220,8 +210,7 @@ public class PhotosController : ControllerBase [HttpPost("deletePhoto/{id:int}")] public async Task DeletePhoto(int id) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); Photo? photo = await this.database.Photos.FirstOrDefaultAsync(p => p.PhotoId == id); if (photo == null) return this.NotFound(); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs index d1caa841..a0bc3a7b 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs @@ -1,28 +1,23 @@ #nullable enable using System.Buffers; using System.IO.Pipelines; -using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using IOFile = System.IO.File; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Resources; [ApiController] +[Authorize] [Produces("text/xml")] [Route("LITTLEBIGPLANETPS3_XML")] public class ResourcesController : ControllerBase { - private readonly Database database; - - public ResourcesController(Database database) - { - this.database = database; - } [HttpPost("showModerated")] public IActionResult ShowModerated() => this.Ok(LbpSerializer.BlankElement("resources")); @@ -31,14 +26,7 @@ public class ResourcesController : ControllerBase [HttpPost("showNotUploaded")] public async Task FilterResources() { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - XmlSerializer serializer = new(typeof(ResourceList)); - ResourceList? resourceList = (ResourceList?)serializer.Deserialize(new StringReader(bodyString)); - + ResourceList? resourceList = await this.DeserializeBody(); if (resourceList == null) return this.BadRequest(); string resources = resourceList.Resources.Where @@ -49,18 +37,14 @@ public class ResourcesController : ControllerBase } [HttpGet("r/{hash}")] - public async Task GetResource(string hash) + public IActionResult GetResource(string hash) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - string path = FileHelper.GetResourcePath(hash); string fullPath = Path.GetFullPath(path); - string basePath = Path.GetFullPath(FileHelper.ResourcePath); // Prevent directory traversal attacks - if (!fullPath.StartsWith(basePath)) return this.BadRequest(); + if (!fullPath.StartsWith(FileHelper.FullResourcePath)) return this.BadRequest(); if (FileHelper.ResourceExists(hash)) return this.File(IOFile.OpenRead(path), "application/octet-stream"); @@ -72,16 +56,17 @@ public class ResourcesController : ControllerBase [HttpPost("upload/{hash}")] public async Task UploadResource(string hash) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - string assetsDirectory = FileHelper.ResourcePath; string path = FileHelper.GetResourcePath(hash); + string fullPath = Path.GetFullPath(path); FileHelper.EnsureDirectoryCreated(assetsDirectory); // lbp treats code 409 as success and as an indicator that the file is already present if (FileHelper.ResourceExists(hash)) return this.Conflict(); + // theoretically shouldn't be possible because of hash check but handle anyways + if (!fullPath.StartsWith(FileHelper.FullResourcePath)) return this.BadRequest(); + Logger.Info($"Processing resource upload (hash: {hash})", LogArea.Resources); LbpFile file = new(await readFromPipeReader(this.Request.BodyReader)); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/CollectionController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/CollectionController.cs index 1184a603..8cbbacda 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/CollectionController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/CollectionController.cs @@ -1,19 +1,20 @@ #nullable enable -using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Configuration; -using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.Levels.Categories; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] public class CollectionController : ControllerBase @@ -28,9 +29,6 @@ public class CollectionController : ControllerBase [HttpGet("playlists/{playlistId:int}/slots")] public async Task GetPlaylistSlots(int playlistId) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - Playlist? targetPlaylist = await this.database.Playlists.FirstOrDefaultAsync(p => p.PlaylistId == playlistId); if (targetPlaylist == null) return this.BadRequest(); @@ -50,8 +48,7 @@ public class CollectionController : ControllerBase [HttpPost("playlists/{playlistId:int}/order_slots")] public async Task UpdatePlaylist(int playlistId, int slotId) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); Playlist? targetPlaylist = await this.database.Playlists.FirstOrDefaultAsync(p => p.PlaylistId == playlistId); if (targetPlaylist == null) return this.BadRequest(); @@ -66,7 +63,7 @@ public class CollectionController : ControllerBase return this.Ok(this.GetUserPlaylists(token.UserId)); } - Playlist? newPlaylist = await this.getPlaylistFromBody(); + Playlist? newPlaylist = await this.DeserializeBody("playlist", "levels"); if (newPlaylist == null) return this.BadRequest(); @@ -116,14 +113,13 @@ public class CollectionController : ControllerBase [HttpPost("playlists")] public async Task CreatePlaylist() { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); int playlistCount = await this.database.Playlists.CountAsync(p => p.CreatorId == token.UserId); if (playlistCount > ServerConfiguration.Instance.UserGeneratedContentLimits.ListsQuota) return this.BadRequest(); - Playlist? playlist = await this.getPlaylistFromBody(); + Playlist? playlist = await this.DeserializeBody("playlist"); if (playlist == null) return this.BadRequest(); @@ -139,10 +135,7 @@ public class CollectionController : ControllerBase [HttpGet("user/{username}/playlists")] public async Task GetUserPlaylists(string username) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - - int targetUserId = await this.database.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync(); + int targetUserId = await this.database.UserIdFromUsername(username); if (targetUserId == 0) return this.BadRequest(); return this.Ok(this.GetUserPlaylists(targetUserId)); @@ -152,8 +145,9 @@ public class CollectionController : ControllerBase [HttpGet("genres")] public async Task GenresAndSearches() { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); + + User? user = await this.database.UserFromGameToken(token); string categoriesSerialized = CategoryHelper.Categories.Aggregate ( @@ -196,13 +190,9 @@ public class CollectionController : ControllerBase [HttpGet("searches/{endpointName}")] public async Task GetCategorySlots(string endpointName, [FromQuery] int pageStart, [FromQuery] int pageSize) { - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + GameToken token = this.GetToken(); - if (userAndToken == null) return this.StatusCode(403, ""); - - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; + User? user = await this.database.UserFromGameToken(token); Category? category = CategoryHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName); if (category == null) return this.NotFound(); @@ -223,7 +213,7 @@ public class CollectionController : ControllerBase totalSlots = category.GetTotalSlots(this.database); } - string slotsSerialized = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(gameToken.GameVersion)); + string slotsSerialized = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion)); return this.Ok ( @@ -243,19 +233,4 @@ public class CollectionController : ControllerBase ) ); } - - private async Task getPlaylistFromBody() - { - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - string rootElement = bodyString.StartsWith("") ? "playlist" : "levels"; - XmlSerializer serializer = new(typeof(Playlist), new XmlRootAttribute(rootElement)); - Playlist? playlist = (Playlist?)serializer.Deserialize(new StringReader(bodyString)); - - SanitizationHelper.SanitizeStringsInClass(playlist); - - return playlist; - } - } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/LevelTagsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/LevelTagsController.cs index d71773fd..e937ac68 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/LevelTagsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/LevelTagsController.cs @@ -1,12 +1,15 @@ +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.PlayerData; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML")] [Produces("text/plain")] public class LevelTagsController : ControllerBase @@ -34,15 +37,14 @@ public class LevelTagsController : ControllerBase } [HttpPost("tag/{slotType}/{id:int}")] - public async Task PostTag([FromForm] string t, [FromRoute] string slotType, [FromRoute] int id) - { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + public async Task PostTag([FromForm(Name = "t")] string tagName, [FromRoute] string slotType, [FromRoute] int id) + { + GameToken token = this.GetToken(); Slot? slot = await this.database.Slots.Where(s => s.SlotId == id).FirstOrDefaultAsync(); if (slot == null) return this.BadRequest(); - if (!LabelHelper.IsValidTag(t)) return this.BadRequest(); + if (!LabelHelper.IsValidTag(tagName)) return this.BadRequest(); if (token.UserId == slot.CreatorId) return this.BadRequest(); @@ -53,7 +55,7 @@ public class LevelTagsController : ControllerBase RatedLevel? rating = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.UserId == token.UserId && r.SlotId == slot.SlotId); if (rating == null) return this.BadRequest(); - rating.TagLBP1 = t; + rating.TagLBP1 = tagName; await this.database.SaveChangesAsync(); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs index f9783862..118742c3 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs @@ -5,12 +5,14 @@ using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] public class ListController : ControllerBase @@ -37,8 +39,7 @@ public class ListController : ControllerBase [FromQuery] string? dateFilterType = null ) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -64,8 +65,7 @@ public class ListController : ControllerBase [HttpPost("lolcatftw/add/user/{id:int}")] public async Task AddQueuedLevel(int id) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); @@ -78,8 +78,7 @@ public class ListController : ControllerBase [HttpPost("lolcatftw/remove/user/{id:int}")] public async Task RemoveQueuedLevel(int id) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); @@ -92,8 +91,7 @@ public class ListController : ControllerBase [HttpPost("lolcatftw/clear")] public async Task ClearQueuedLevels() { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == token.UserId)); @@ -118,8 +116,7 @@ public class ListController : ControllerBase [FromQuery] string? dateFilterType = null ) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -150,8 +147,7 @@ public class ListController : ControllerBase [HttpPost("favourite/slot/{slotType}/{id:int}")] public async Task AddFavouriteSlot(string slotType, int id) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest(); @@ -174,8 +170,7 @@ public class ListController : ControllerBase [HttpPost("unfavourite/slot/{slotType}/{id:int}")] public async Task RemoveFavouriteSlot(string slotType, int id) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest(); @@ -202,12 +197,9 @@ public class ListController : ControllerBase [HttpGet("favouritePlaylists/{username}")] public async Task GetFavouritePlaylists(string username, [FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - if (pageSize <= 0) return this.BadRequest(); - int targetUserId = await this.database.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync(); + int targetUserId = await this.database.UserIdFromUsername(username); if (targetUserId == 0) return this.StatusCode(403, ""); IEnumerable heartedPlaylists = this.database.HeartedPlaylists.Where(p => p.UserId == targetUserId) @@ -228,8 +220,7 @@ public class ListController : ControllerBase [HttpPost("favourite/playlist/{playlistId:int}")] public async Task AddFavouritePlaylist(int playlistId) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); Playlist? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId); if (playlist == null) return this.NotFound(); @@ -242,8 +233,7 @@ public class ListController : ControllerBase [HttpPost("unfavourite/playlist/{playlistId:int}")] public async Task RemoveFavouritePlaylist(int playlistId) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); Playlist? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId); if (playlist == null) return this.NotFound(); @@ -262,8 +252,7 @@ public class ListController : ControllerBase [HttpGet("favouriteUsers/{username}")] public async Task GetFavouriteUsers(string username, [FromQuery] int pageSize, [FromQuery] int pageStart) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); User? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); if (targetUser == null) return this.StatusCode(403, ""); @@ -295,8 +284,7 @@ public class ListController : ControllerBase [HttpPost("favourite/user/{username}")] public async Task AddFavouriteUser(string username) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); if (heartedUser == null) return this.NotFound(); @@ -309,8 +297,7 @@ public class ListController : ControllerBase [HttpPost("unfavourite/user/{username}")] public async Task RemoveFavouriteUser(string username) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); if (heartedUser == null) return this.NotFound(); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs index 1f35667d..9b409218 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs @@ -1,6 +1,6 @@ #nullable enable -using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Levels; @@ -8,12 +8,14 @@ using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] public class PublishController : ControllerBase @@ -31,15 +33,12 @@ public class PublishController : ControllerBase [HttpPost("startPublish")] public async Task StartPublish() { - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + GameToken token = this.GetToken(); - if (userAndToken == null) return this.StatusCode(403, ""); + User? user = await this.database.UserFromGameToken(token); + if (user == null) return this.StatusCode(403, ""); - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; - - Slot? slot = await this.getSlotFromBody(); + Slot? slot = await this.DeserializeBody(); if (slot == null) { Logger.Warn("Rejecting level upload, slot is null", LogArea.Publish); @@ -69,7 +68,7 @@ public class PublishController : ControllerBase return this.BadRequest(); } } - else if (user.GetUsedSlotsForGame(gameToken.GameVersion) > user.EntitledSlots) + else if (user.GetUsedSlotsForGame(token.GameVersion) > user.EntitledSlots) { return this.StatusCode(403, ""); } @@ -89,14 +88,12 @@ public class PublishController : ControllerBase [HttpPost("publish")] public async Task Publish([FromQuery] string? game) { - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + GameToken token = this.GetToken(); - if (userAndToken == null) return this.StatusCode(403, ""); + User? user = await this.database.UserFromGameToken(token); + if (user == null) return this.StatusCode(403, ""); - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; - Slot? slot = await this.getSlotFromBody(); + Slot? slot = await this.DeserializeBody(); if (slot == null) { @@ -156,7 +153,7 @@ public class PublishController : ControllerBase GameVersion slotVersion = FileHelper.ParseLevelVersion(rootLevel); slot.GameVersion = slotVersion; - if (slotVersion == GameVersion.Unknown) slot.GameVersion = gameToken.GameVersion; + if (slotVersion == GameVersion.Unknown) slot.GameVersion = token.GameVersion; slot.AuthorLabels = LabelHelper.RemoveInvalidLabels(slot.AuthorLabels); @@ -185,7 +182,7 @@ public class PublishController : ControllerBase if (intendedVersion != GameVersion.Unknown && intendedVersion != slotVersion) { // Delete the useless rootLevel that lbp3 just uploaded - if(slotVersion == GameVersion.LittleBigPlanet3) + if (slotVersion == GameVersion.LittleBigPlanet3) FileHelper.DeleteResource(slot.RootLevel); slot.GameVersion = oldSlot.GameVersion; @@ -230,7 +227,7 @@ public class PublishController : ControllerBase this.database.Entry(oldSlot).CurrentValues.SetValues(slot); await this.database.SaveChangesAsync(); - return this.Ok(oldSlot.Serialize(gameToken.GameVersion)); + return this.Ok(oldSlot.Serialize(token.GameVersion)); } if (user.GetUsedSlotsForGame(slotVersion) > user.EntitledSlots) @@ -269,14 +266,13 @@ public class PublishController : ControllerBase Logger.Success($"Successfully published level {slot.Name} (id: {slot.SlotId}) by {user.Username} (id: {user.UserId})", LogArea.Publish); - return this.Ok(slot.Serialize(gameToken.GameVersion)); + return this.Ok(slot.Serialize(token.GameVersion)); } [HttpPost("unpublish/{id:int}")] public async Task Unpublish(int id) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); Slot? slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); @@ -305,17 +301,4 @@ public class PublishController : ControllerBase _ => GameVersion.Unknown, }; } - - private async Task getSlotFromBody() - { - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - XmlSerializer serializer = new(typeof(Slot)); - Slot? slot = (Slot?)serializer.Deserialize(new StringReader(bodyString)); - - SanitizationHelper.SanitizeStringsInClass(slot); - - return slot; - } } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs index ed219045..d96faa78 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs @@ -1,5 +1,4 @@ #nullable enable -using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Administration; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; @@ -7,12 +6,14 @@ using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Reviews; using LBPUnion.ProjectLighthouse.Serialization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] public class ReviewController : ControllerBase @@ -28,8 +29,7 @@ public class ReviewController : ControllerBase [HttpPost("rate/user/{slotId:int}")] public async Task Rate(int slotId, [FromQuery] int rating) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId); if (slot == null) return this.StatusCode(403, ""); @@ -58,8 +58,7 @@ public class ReviewController : ControllerBase [HttpPost("dpadrate/user/{slotId:int}")] public async Task DPadRate(int slotId, [FromQuery] int rating) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); Slot? slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId); if (slot == null) return this.StatusCode(403, ""); @@ -90,10 +89,9 @@ public class ReviewController : ControllerBase [HttpPost("postReview/user/{slotId:int}")] public async Task PostReview(int slotId) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); - Review? newReview = await this.getReviewFromBody(); + Review? newReview = await this.DeserializeBody(); if (newReview == null) return this.BadRequest(); if (newReview.Text.Length > 512) return this.BadRequest(); @@ -143,8 +141,7 @@ public class ReviewController : ControllerBase [HttpGet("reviewsFor/user/{slotId:int}")] public async Task ReviewsFor(int slotId, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -195,14 +192,13 @@ public class ReviewController : ControllerBase [HttpGet("reviewsBy/{username}")] public async Task ReviewsBy(string username, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); GameVersion gameVersion = token.GameVersion; - int targetUserId = await this.database.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync(); + int targetUserId = await this.database.UserIdFromUsername(username); if (targetUserId == 0) return this.BadRequest(); @@ -249,10 +245,9 @@ public class ReviewController : ControllerBase [HttpPost("rateReview/user/{slotId:int}/{username}")] public async Task RateReview(int slotId, string username, [FromQuery] int rating = 0) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); - int reviewerId = await this.database.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync(); + int reviewerId = await this.database.UserIdFromUsername(username); if (reviewerId == 0) return this.StatusCode(400, ""); Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId); @@ -303,15 +298,14 @@ public class ReviewController : ControllerBase [HttpPost("deleteReview/user/{slotId:int}/{username}")] public async Task DeleteReview(int slotId, string username) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); int creatorId = await this.database.Slots.Where(s => s.SlotId == slotId).Select(s => s.CreatorId).FirstOrDefaultAsync(); if (creatorId == 0) return this.StatusCode(400, ""); if (token.UserId != creatorId) return this.StatusCode(403, ""); - int reviewerId = await this.database.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync(); + int reviewerId = await this.database.UserIdFromUsername(username); if (reviewerId == 0) return this.StatusCode(400, ""); Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId); @@ -323,15 +317,4 @@ public class ReviewController : ControllerBase await this.database.SaveChangesAsync(); return this.Ok(); } - - private async Task getReviewFromBody() - { - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - XmlSerializer serializer = new(typeof(Review)); - Review? review = (Review?)serializer.Deserialize(new StringReader(bodyString)); - SanitizationHelper.SanitizeStringsInClass(review); - return review; - } } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs index 1a335872..eea811ca 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs @@ -1,6 +1,7 @@ #nullable enable using System.Diagnostics.CodeAnalysis; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.Logging; @@ -30,8 +31,7 @@ public class ScoreController : ControllerBase [HttpPost("scoreboard/{slotType}/{id:int}/{childId:int}")] public async Task SubmitScore(string slotType, int id, int childId, [FromQuery] bool lbp1 = false, [FromQuery] bool lbp2 = false, [FromQuery] bool lbp3 = false) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); string username = await this.database.UsernameFromGameToken(token); @@ -41,11 +41,7 @@ public class ScoreController : ControllerBase return this.BadRequest(); } - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - XmlSerializer serializer = new(typeof(Score)); - Score? score = (Score?)serializer.Deserialize(new StringReader(bodyString)); + Score? score = await this.DeserializeBody(); if (score == null) { Logger.Warn($"Rejecting score upload, score is null (slotType={slotType}, slotId={id}, user={username})", LogArea.Score); @@ -157,8 +153,7 @@ public class ScoreController : ControllerBase [HttpGet("friendscores/{slotType}/{slotId:int}/{childId:int}/{type:int}")] public async Task FriendScores(string slotType, int slotId, int? childId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -189,8 +184,7 @@ public class ScoreController : ControllerBase [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] public async Task TopScores(string slotType, int slotId, int? childId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs index 9addc61a..a3431423 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs @@ -3,12 +3,14 @@ using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Serialization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/slots")] [Produces("text/xml")] public class SearchController : ControllerBase @@ -31,8 +33,7 @@ public class SearchController : ControllerBase string? keyName = "slots" ) { - GameToken? gameToken = await this.database.GameTokenFromRequest(this.Request); - if (gameToken == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -42,7 +43,7 @@ public class SearchController : ControllerBase string[] keywords = query.Split(" "); - IQueryable dbQuery = this.database.Slots.ByGameVersion(gameToken.GameVersion, false, true) + IQueryable dbQuery = this.database.Slots.ByGameVersion(token.GameVersion, false, true) .Where(s => s.Type == SlotType.User) .OrderBy(s => !s.TeamPick) .ThenByDescending(s => s.FirstUploaded) @@ -60,7 +61,7 @@ public class SearchController : ControllerBase List slots = await dbQuery.Skip(Math.Max(0, pageStart - 1)).Take(Math.Min(pageSize, 30)).ToListAsync(); - string response = slots.Aggregate("", (current, slot) => current + slot.Serialize(gameToken.GameVersion)); + string response = slots.Aggregate("", (current, slot) => current + slot.Serialize(token.GameVersion)); return this.Ok(LbpSerializer.TaggedStringElement(keyName, response, "total", dbQuery.Count())); } diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs index d32f23f7..26b427ee 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs @@ -8,12 +8,14 @@ using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.PlayerData.Reviews; using LBPUnion.ProjectLighthouse.Serialization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] public class SlotsController : ControllerBase @@ -38,38 +40,36 @@ public class SlotsController : ControllerBase }); [HttpGet("slots/by")] - public async Task SlotsBy([FromQuery] string u, [FromQuery] int pageStart, [FromQuery] int pageSize) + public async Task SlotsBy([FromQuery(Name="u")] string username, [FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); GameVersion gameVersion = token.GameVersion; - User? targetUser = await this.database.Users.Where(dbUser => dbUser.Username == u).FirstOrDefaultAsync(); - if (targetUser == null) return this.NotFound(); + int targetUserId = await this.database.UserIdFromUsername(username); + if (targetUserId == 0) return this.NotFound(); + + int usedSlots = this.database.Slots.Count(s => s.CreatorId == targetUserId); string response = Enumerable.Aggregate ( - this.database.Slots.ByGameVersion(gameVersion, token.UserId == targetUser.UserId, true) - .Where(s => s.CreatorId == targetUser.UserId) + this.database.Slots.Where(s => s.CreatorId == targetUserId) + .ByGameVersion(gameVersion, token.UserId == targetUserId, true) .Skip(Math.Max(0, pageStart - 1)) - .Take(Math.Min(pageSize, targetUser.UsedSlots)), + .Take(Math.Min(pageSize, usedSlots)), string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion) ); - int start = pageStart + Math.Min(pageSize, targetUser.UsedSlots); - int total = await this.database.Slots.CountAsync(s => s.CreatorId == targetUser.UserId); + int start = pageStart + Math.Min(pageSize, usedSlots); + int total = await this.database.Slots.CountAsync(s => s.CreatorId == targetUserId); return this.Ok(generateSlotsResponse(response, start, total)); } [HttpGet("slotList")] public async Task GetSlotListAlt([FromQuery] int[] s) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - List serializedSlots = new(); foreach (int slotId in s) { @@ -93,9 +93,6 @@ public class SlotsController : ControllerBase [HttpGet("slots/developer")] public async Task StoryPlayers() { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - List activeSlotIds = RoomHelper.Rooms.Where(r => r.Slot.SlotType == SlotType.Developer).Select(r => r.Slot.SlotId).ToList(); List serializedSlots = new(); @@ -115,9 +112,6 @@ public class SlotsController : ControllerBase [HttpGet("s/developer/{id:int}")] public async Task SDev(int id) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - int slotId = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer); Slot slot = await this.database.Slots.FirstAsync(s => s.SlotId == slotId); @@ -127,8 +121,7 @@ public class SlotsController : ControllerBase [HttpGet("s/user/{id:int}")] public async Task SUser(int id) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); GameVersion gameVersion = token.GameVersion; @@ -168,8 +161,7 @@ public class SlotsController : ControllerBase [HttpGet("slots")] public async Task NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -189,8 +181,7 @@ public class SlotsController : ControllerBase [HttpGet("slots/like/{slotType}/{slotId:int}")] public async Task SimilarSlots([FromRoute] string slotType, [FromRoute] int slotId, [FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -225,8 +216,7 @@ public class SlotsController : ControllerBase [HttpGet("slots/highestRated")] public async Task HighestRatedSlots([FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -248,8 +238,7 @@ public class SlotsController : ControllerBase [HttpGet("slots/tag")] public async Task SimilarSlots([FromQuery] string tag, [FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -260,8 +249,8 @@ public class SlotsController : ControllerBase .Select(s => s.SlotId) .ToListAsync(); - IQueryable slots = this.database.Slots.ByGameVersion(gameVersion, false, true) - .Where(s => slotIdsWithTag.Contains(s.SlotId)) + IQueryable slots = this.database.Slots.Where(s => slotIdsWithTag.Contains(s.SlotId)) + .ByGameVersion(gameVersion, false, true) .OrderByDescending(s => s.PlaysLBP1) .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)); @@ -276,15 +265,14 @@ public class SlotsController : ControllerBase [HttpGet("slots/mmpicks")] public async Task TeamPickedSlots([FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); GameVersion gameVersion = token.GameVersion; - IQueryable slots = this.database.Slots.ByGameVersion(gameVersion, false, true) - .Where(s => s.TeamPick) + IQueryable slots = this.database.Slots.Where(s => s.TeamPick) + .ByGameVersion(gameVersion, false, true) .OrderByDescending(s => s.LastUpdated) .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)); @@ -298,8 +286,7 @@ public class SlotsController : ControllerBase [HttpGet("slots/lbp2luckydip")] public async Task LuckyDipSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] int seed) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -325,8 +312,7 @@ public class SlotsController : ControllerBase [FromQuery] string? dateFilterType = null ) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -357,8 +343,7 @@ public class SlotsController : ControllerBase [FromQuery] string? dateFilterType = null ) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -403,8 +388,7 @@ public class SlotsController : ControllerBase [FromQuery] string? dateFilterType = null ) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -435,8 +419,7 @@ public class SlotsController : ControllerBase [FromQuery] bool? move = null ) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -445,7 +428,7 @@ public class SlotsController : ControllerBase foreach (Room room in RoomHelper.Rooms) { // TODO: support developer slotTypes? - if(room.Slot.SlotType != SlotType.User) continue; + if (room.Slot.SlotType != SlotType.User) continue; if (!playersBySlotId.TryGetValue(room.Slot.SlotId, out int playerCount)) playersBySlotId.Add(room.Slot.SlotId, 0); @@ -468,7 +451,7 @@ public class SlotsController : ControllerBase { Slot? slot = await this.database.Slots.ByGameVersion(token.GameVersion, false, true) .FirstOrDefaultAsync(s => s.SlotId == slotId); - if(slot == null) continue; // shouldn't happen ever unless the room is borked + if (slot == null) continue; // shouldn't happen ever unless the room is borked slots.Add(slot); } diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/StatisticsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/StatisticsController.cs index ce4846fa..755214b1 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/StatisticsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/StatisticsController.cs @@ -1,10 +1,12 @@ using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Serialization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/plain")] public class StatisticsController : ControllerBase diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/StoreController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/StoreController.cs index ad214da9..0b6a8407 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/StoreController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/StoreController.cs @@ -1,8 +1,10 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] public class StoreController : Controller diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs index f75d6fb2..709157b4 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs @@ -1,18 +1,20 @@ #nullable enable using System.Text.Json; -using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] +[Authorize] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] public class UserController : ControllerBase @@ -34,14 +36,11 @@ public class UserController : ControllerBase { // use an anonymous type to only fetch certain columns var partialUser = await this.database.Users.Where(u => u.Username == username) - .Select - ( - u => new - { - u.Username, - u.IconHash, - } - ) + .Select(u => new + { + u.Username, + u.IconHash, + }) .FirstOrDefaultAsync(); if (partialUser == null) return null; @@ -52,8 +51,7 @@ public class UserController : ControllerBase [HttpGet("user/{username}")] public async Task GetUser(string username) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + GameToken token = this.GetToken(); string? user = await this.getSerializedUser(username, token.GameVersion); if (user == null) return this.NotFound(); @@ -64,9 +62,6 @@ public class UserController : ControllerBase [HttpGet("users")] public async Task GetUserAlt([FromQuery] string[] u) { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - List serializedUsers = new(); foreach (string userId in u) serializedUsers.Add(await this.getSerializedUserPicture(userId)); @@ -78,20 +73,12 @@ public class UserController : ControllerBase [HttpPost("updateUser")] public async Task UpdateUser() { - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + GameToken token = this.GetToken(); - if (userAndToken == null) return this.StatusCode(403, ""); + User? user = await this.database.UserFromGameToken(token); + if (user == null) return this.StatusCode(403, ""); - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; - - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - // xml hack so we can use one class to deserialize different root names - string rootElement = bodyString.Contains("updateUser") ? "updateUser" : "user"; - XmlSerializer serializer = new(typeof(UserUpdate), new XmlRootAttribute(rootElement)); - UserUpdate? update = (UserUpdate?)serializer.Deserialize(new StringReader(bodyString)); + UserUpdate? update = await this.DeserializeBody("updateUser", "user"); if (update == null) return this.BadRequest(); @@ -133,7 +120,7 @@ public class UserController : ControllerBase Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == updateSlot.SlotId); if (slot == null) continue; - if (slot.CreatorId != gameToken.UserId) continue; + if (slot.CreatorId != token.UserId) continue; Location? loc = await this.database.Locations.FirstOrDefaultAsync(l => l.Id == slot.LocationId); @@ -146,7 +133,7 @@ public class UserController : ControllerBase if (update.PlanetHash != null) { - switch (gameToken.GameVersion) + switch (token.GameVersion) { case GameVersion.LittleBigPlanet2: // LBP2 planets will apply to LBP3 { @@ -169,7 +156,7 @@ public class UserController : ControllerBase case GameVersion.Unknown: default: // The rest do not support custom earths. { - throw new ArgumentException($"invalid gameVersion {gameToken.GameVersion} for setting earth"); + throw new ArgumentException($"invalid gameVersion {token.GameVersion} for setting earth"); } } } @@ -190,7 +177,7 @@ public class UserController : ControllerBase [HttpPost("update_my_pins")] public async Task UpdateMyPins() { - User? user = await this.database.UserFromGameRequest(this.Request); + User? user = await this.database.UserFromGameToken(this.GetToken()); if (user == null) return this.StatusCode(403, ""); string pinsString = await new StreamReader(this.Request.Body).ReadToEndAsync(); diff --git a/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs b/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs index 1ee53179..26c0093a 100644 --- a/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs +++ b/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs @@ -3,6 +3,9 @@ using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Middlewares; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.HttpOverrides; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Startup; @@ -21,6 +24,19 @@ public class GameServerStartup { services.AddControllers(); + services.AddAuthentication(options => + { + options.DefaultScheme = "tokenAuth"; + options.AddScheme("tokenAuth", null); + }); + + services.AddAuthorization(o => + { + AuthorizationPolicyBuilder builder = new("tokenAuth"); + builder = builder.RequireClaim("userId"); + o.DefaultPolicy = builder.Build(); + }); + services.AddMvc ( options => @@ -64,12 +80,14 @@ public class GameServerStartup app.UseForwardedHeaders(); app.UseMiddleware(); + app.UseMiddleware(); app.UseMiddleware(computeDigests); app.UseMiddleware(); - app.UseMiddleware(); app.UseRouting(); + app.UseAuthorization(); + app.UseEndpoints(endpoints => endpoints.MapControllers()); app.UseEndpoints(endpoints => endpoints.MapRazorPages()); } diff --git a/ProjectLighthouse.Servers.GameServer/Startup/TokenAuthHandler.cs b/ProjectLighthouse.Servers.GameServer/Startup/TokenAuthHandler.cs new file mode 100644 index 00000000..d0cb8b54 --- /dev/null +++ b/ProjectLighthouse.Servers.GameServer/Startup/TokenAuthHandler.cs @@ -0,0 +1,49 @@ +using System.Security.Claims; +using System.Text.Encodings.Web; +using LBPUnion.ProjectLighthouse.PlayerData; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; + +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Startup; + +public class TokenAuthHandler : AuthenticationHandler +{ + private readonly Database database; + private const string cookie = "MM_AUTH"; + + public TokenAuthHandler + ( + IOptionsMonitor options, + UrlEncoder encoder, + ISystemClock clock, + Database database + // I said I don't want any damn vegetables (logs) + ) : base(options, new NullLoggerFactory(), encoder, clock) + { + this.database = database; + } + + protected override Task HandleChallengeAsync(AuthenticationProperties properties) + { + this.Context.Response.StatusCode = 403; + return Task.CompletedTask; + } + + protected override async Task HandleAuthenticateAsync() + { + if (!this.Context.Request.Cookies.ContainsKey(cookie)) return AuthenticateResult.Fail("No auth cookie"); + + GameToken? gameToken = await this.database.GameTokenFromRequest(this.Request); + if (gameToken == null) return AuthenticateResult.Fail("No game token"); + + this.Context.Items["Token"] = gameToken; + Claim[] claims = { + new("userId", gameToken.UserId.ToString()), + }; + ClaimsIdentity identity = new(claims, this.Scheme.Name); + ClaimsPrincipal principal = new(identity); + AuthenticationTicket ticket = new(principal, this.Scheme.Name); + return AuthenticateResult.Success(ticket); + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminReportController.cs b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminReportController.cs index d2c43f51..55def113 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminReportController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminReportController.cs @@ -1,7 +1,6 @@ #nullable enable using LBPUnion.ProjectLighthouse.Administration.Reports; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -32,7 +31,7 @@ public class AdminReportController : ControllerBase report.JpegHash, report.GriefStateHash, }; - if(report.LevelType != "user") + if (report.LevelType != "user") hashes.Add(report.InitialStateHash); foreach (string hash in hashes) { diff --git a/ProjectLighthouse.Servers.Website/Controllers/Admin/ModerationSlotController.cs b/ProjectLighthouse.Servers.Website/Controllers/Admin/ModerationSlotController.cs index 20ebae4a..d00db37e 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/Admin/ModerationSlotController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/Admin/ModerationSlotController.cs @@ -27,12 +27,14 @@ public class ModerationSlotController : ControllerBase Slot? slot = await this.database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); + slot.TeamPick = true; // Send webhook with slot.Name and slot.Creator.Username await WebhookHelper.SendWebhook("New Team Pick!", $"The level [**{slot.Name}**]({ServerConfiguration.Instance.ExternalUrl}/slot/{slot.SlotId}) by **{slot.Creator?.Username}** has been team picked"); await this.database.SaveChangesAsync(); + return this.Redirect("~/slot/" + id); } @@ -44,9 +46,11 @@ public class ModerationSlotController : ControllerBase Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); + slot.TeamPick = false; await this.database.SaveChangesAsync(); + return this.Redirect("~/slot/" + id); } diff --git a/ProjectLighthouse.Servers.Website/Controllers/ResourcesController.cs b/ProjectLighthouse.Servers.Website/Controllers/ResourcesController.cs index cdef6071..f54da460 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/ResourcesController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/ResourcesController.cs @@ -14,10 +14,9 @@ public class ResourcesController : ControllerBase string path = FileHelper.GetImagePath($"{hash}.png"); string fullPath = Path.GetFullPath(path); - string basePath = Path.GetFullPath(FileHelper.ImagePath); // Prevent directory traversal attacks - if (!fullPath.StartsWith(basePath)) return this.BadRequest(); + if (!fullPath.StartsWith(FileHelper.FullImagePath)) return this.BadRequest(); if (IOFile.Exists(path)) return this.File(IOFile.OpenRead(path), "image/png"); diff --git a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml index 375df0b8..77b79a58 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml @@ -1,7 +1,6 @@ @page "/admin/users" @using LBPUnion.ProjectLighthouse.Administration @using LBPUnion.ProjectLighthouse.PlayerData.Profiles -@using LBPUnion.ProjectLighthouse.Types @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin.AdminPanelUsersPage @{ @@ -61,7 +60,7 @@