From 3da6bc20b41df088a3d2c72ffeb400546b01110c Mon Sep 17 00:00:00 2001 From: sudokoko Date: Wed, 27 Mar 2024 22:58:13 -0400 Subject: [PATCH 01/12] Implement read-only mode --- .../BaseLayout.resx | 6 +++++ .../StringLists/BaseLayoutStrings.cs | 3 +++ .../Controllers/CommentController.cs | 6 +++++ .../Controllers/MessageController.cs | 7 +++++ .../Controllers/Resources/PhotosController.cs | 5 +++- .../Resources/ResourcesController.cs | 9 +++++-- .../Controllers/Slots/PublishController.cs | 9 +++++++ .../Controllers/Slots/ReviewController.cs | 9 ++++++- .../Controllers/UserController.cs | 7 +++++ .../Controllers/UserPageController.cs | 3 +++ .../Pages/Layouts/BaseLayout.cshtml | 12 +++++++++ .../Pages/Partials/CommentsPartial.cshtml | 27 ++++++++++++++----- .../Pages/Partials/ReviewPartial.cshtml | 19 ++++++++++--- .../Pages/UserSettingsPage.cshtml.cs | 11 +++++++- .../UserGeneratedContentLimitConfiguration.cs | 6 +++++ .../Configuration/ServerConfiguration.cs | 2 +- 16 files changed, 125 insertions(+), 16 deletions(-) diff --git a/ProjectLighthouse.Localization/BaseLayout.resx b/ProjectLighthouse.Localization/BaseLayout.resx index 6cd9c681a..65d1229f7 100644 --- a/ProjectLighthouse.Localization/BaseLayout.resx +++ b/ProjectLighthouse.Localization/BaseLayout.resx @@ -87,4 +87,10 @@ If not, please publish the source code somewhere accessible to your users. + + Read-Only Mode + + + This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled. + \ No newline at end of file diff --git a/ProjectLighthouse.Localization/StringLists/BaseLayoutStrings.cs b/ProjectLighthouse.Localization/StringLists/BaseLayoutStrings.cs index 64f390219..f46cb4e60 100644 --- a/ProjectLighthouse.Localization/StringLists/BaseLayoutStrings.cs +++ b/ProjectLighthouse.Localization/StringLists/BaseLayoutStrings.cs @@ -23,5 +23,8 @@ public static class BaseLayoutStrings public static readonly TranslatableString LicenseWarn2 = create("license_warn_2"); public static readonly TranslatableString LicenseWarn3 = create("license_warn_3"); + public static readonly TranslatableString ReadOnlyWarnTitle = create("read_only_warn_title"); + public static readonly TranslatableString ReadOnlyWarn = create("read_only_warn"); + private static TranslatableString create(string key) => new(TranslationAreas.BaseLayout, key); } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs index 76f2b715b..a906b170b 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs @@ -119,6 +119,9 @@ public async Task PostComment(string? username, string? slotType, { GameTokenEntity token = this.GetToken(); + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + GameComment? comment = await this.DeserializeBody(); if (comment?.Message == null) return this.BadRequest(); @@ -159,6 +162,9 @@ public async Task DeleteComment([FromQuery] int commentId, string { GameTokenEntity token = this.GetToken(); + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest(); CommentEntity? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs index 3d3f16be9..3405818f9 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs @@ -59,6 +59,13 @@ public async Task Announce() announceText.Replace("%user", username); announceText.Replace("%id", token.UserId.ToString()); + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) + { + announceText.Insert(0, "This instance is currently in read-only mode. Level and photo uploads, comments, " + + "reviews, and certain profile changes will be restricted until read-only mode is " + + "disabled."); + } + #if DEBUG announceText.Append("\n\n---DEBUG INFO---\n" + $"user.UserId: {token.UserId}\n" + diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs index e79626439..4c73d0a04 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs @@ -37,6 +37,9 @@ public async Task UploadPhoto() { GameTokenEntity token = this.GetToken(); + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + int photoCount = await this.database.Photos.CountAsync(p => p.CreatorId == token.UserId); if (photoCount >= ServerConfiguration.Instance.UserGeneratedContentLimits.PhotosQuota) return this.BadRequest(); @@ -90,7 +93,7 @@ public async Task UploadPhoto() case SlotType.Developer: { SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == photoSlot.SlotType && s.InternalSlotId == photoSlot.SlotId); - if (slot != null) + if (slot != null) photoSlot.SlotId = slot.SlotId; else photoSlot.SlotId = await SlotHelper.GetPlaceholderSlotId(this.database, photoSlot.SlotId, photoSlot.SlotType); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs index 3c1c2f822..7231ccafb 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs @@ -1,5 +1,6 @@ #nullable enable using System.Text; +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Logging; @@ -58,10 +59,14 @@ public async Task UploadResource(string 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 + + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + + // 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 + // 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); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs index e09a06e65..7bedc01dc 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs @@ -43,6 +43,9 @@ public async Task StartPublish() UserEntity? user = await this.database.UserFromGameToken(token); if (user == null) return this.Forbid(); + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + GameUserSlot? slot = await this.DeserializeBody(); if (slot == null) { @@ -116,6 +119,9 @@ public async Task Publish([FromQuery] string? game) UserEntity? user = await this.database.UserFromGameToken(token); if (user == null) return this.Forbid(); + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + GameUserSlot? slot = await this.DeserializeBody(); if (slot == null) @@ -335,6 +341,9 @@ public async Task Unpublish(int id) { GameTokenEntity token = this.GetToken(); + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs index aa4e1c5b9..7feae4bf4 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs @@ -1,4 +1,5 @@ #nullable enable +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; @@ -92,6 +93,9 @@ public async Task PostReview(int slotId) { GameTokenEntity token = this.GetToken(); + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + GameReview? newReview = await this.DeserializeBody(); if (newReview == null) return this.BadRequest(); @@ -115,7 +119,7 @@ public async Task PostReview(int slotId) } review.Thumb = Math.Clamp(newReview.Thumb, -1, 1); review.LabelCollection = LabelHelper.RemoveInvalidLabels(newReview.LabelCollection); - + review.Text = newReview.Text; review.Deleted = false; review.Timestamp = TimeHelper.TimestampMillis; @@ -239,6 +243,9 @@ public async Task DeleteReview(int slotId, string username) { GameTokenEntity token = this.GetToken(); + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + int creatorId = await this.database.Slots.Where(s => s.SlotId == slotId).Select(s => s.CreatorId).FirstOrDefaultAsync(); if (creatorId == 0) return this.BadRequest(); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs index fb5b6128f..cbed989be 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs @@ -1,4 +1,5 @@ using System.Text.Json; +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Files; @@ -73,6 +74,9 @@ public async Task UpdateUser() if (update.Biography != null) { + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + if (update.Biography.Length > 512) return this.BadRequest(); user.Biography = update.Biography; @@ -85,6 +89,9 @@ public async Task UpdateUser() { if (string.IsNullOrWhiteSpace(resource)) continue; + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + if (!FileHelper.ResourceExists(resource) && !resource.StartsWith('g')) return this.BadRequest(); if (!GameResourceHelper.IsValidTexture(resource)) return this.BadRequest(); diff --git a/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs index 161bb1a87..284522ff6 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs @@ -39,6 +39,9 @@ public async Task PostComment([FromRoute] int id, [FromForm] stri WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + if (msg == null) { Logger.Error($"Refusing to post comment from {token.UserId} on user {id}, {nameof(msg)} is null", LogArea.Comments); diff --git a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml index 5de98aa34..14f147822 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml @@ -178,6 +178,18 @@ } + @if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) + { +
+
+ + @Model.Translate(BaseLayoutStrings.ReadOnlyWarnTitle) +

+ @Html.Raw(Model.Translate(BaseLayoutStrings.ReadOnlyWarn)) +

+
+
+ }
diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml index 4a2a98691..cbab2fdbb 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml @@ -1,4 +1,5 @@ @using System.Web +@using LBPUnion.ProjectLighthouse.Configuration @using LBPUnion.ProjectLighthouse.Database @using LBPUnion.ProjectLighthouse.Localization @using LBPUnion.ProjectLighthouse.Servers.Website.Extensions @@ -31,18 +32,32 @@ @if (Model.CommentsEnabled && Model.User != null) {
-
-
- + @if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) + { +
+

+ + @ServerConfiguration.Instance.Customization.ServerName is currently in read-only mode. + You will not be able to post comments until read-only mode is disabled. + +

- - + } + else + { +
+
+ +
+ +
+ } @if (Model.Comments.Count > 0) {
} } - + @{ int i = 0; foreach (KeyValuePair commentAndReaction in Model.Comments) diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/ReviewPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/ReviewPartial.cshtml index 610342658..f8c16a5ff 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/ReviewPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/ReviewPartial.cshtml @@ -4,7 +4,6 @@ @using LBPUnion.ProjectLighthouse.Helpers @using LBPUnion.ProjectLighthouse.Types.Entities.Level @using LBPUnion.ProjectLighthouse.Types.Serialization - @{ bool isMobile = (bool?)ViewData["IsMobile"] ?? false; bool canDelete = (bool?)ViewData["CanDelete"] ?? false; @@ -29,6 +28,18 @@
} + @if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) + { +
+

+ + @ServerConfiguration.Instance.Customization.ServerName is currently in read-only mode. + You will not be able to post reviews in-game until read-only mode is disabled. + +

+
+ } + @for(int i = 0; i < Model.Reviews.Count; i++) { ReviewEntity review = Model.Reviews[i]; @@ -36,7 +47,7 @@ -1 => review.Reviewer?.BooHash, 0 => review.Reviewer?.MehHash, 1 => review.Reviewer?.YayHash, - + _ => throw new ArgumentOutOfRangeException(), }) ?? ""; @@ -49,7 +60,7 @@ -1 => "Boo!", 0 => "Meh.", 1 => "Yay!", - + _ => throw new ArgumentOutOfRangeException(), }; @@ -114,7 +125,7 @@ if (window.confirm("Are you sure you want to delete this?\nThis action cannot be undone.")){ window.location.hash = "reviews"; window.location.href = "/moderation/deleteReview/" + reviewId + "?callbackUrl=" + this.window.location; - } + } }
diff --git a/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs index 066ca973b..cf24b0b2b 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs @@ -41,12 +41,21 @@ [FromForm] string? language string? avatarHash = await FileHelper.ParseBase64Image(avatar); - if (avatarHash != null) this.ProfileUser.IconHash = avatarHash; + if (avatarHash != null) + { + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + + this.ProfileUser.IconHash = avatarHash; + } if (this.User.IsAdmin) this.ProfileUser.ProfileTag = profileTag; if (biography != null) { + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + biography = CensorHelper.FilterMessage(biography); if (this.ProfileUser.Biography != biography && biography.Length <= 512) this.ProfileUser.Biography = biography; diff --git a/ProjectLighthouse/Configuration/ConfigurationCategories/UserGeneratedContentLimitConfiguration.cs b/ProjectLighthouse/Configuration/ConfigurationCategories/UserGeneratedContentLimitConfiguration.cs index 6430b7541..f725c2788 100644 --- a/ProjectLighthouse/Configuration/ConfigurationCategories/UserGeneratedContentLimitConfiguration.cs +++ b/ProjectLighthouse/Configuration/ConfigurationCategories/UserGeneratedContentLimitConfiguration.cs @@ -11,6 +11,12 @@ public class UserGeneratedContentLimitConfiguration public int PhotosQuota { get; set; } = 500; + /// + /// When enabled, all UGC uploads are disabled. This includes levels, photos, reviews, + /// comments, and certain profile settings. + /// + public bool ReadOnlyMode { get; set; } = false; + public bool ProfileCommentsEnabled { get; set; } = true; public bool LevelCommentsEnabled { get; set; } = true; diff --git a/ProjectLighthouse/Configuration/ServerConfiguration.cs b/ProjectLighthouse/Configuration/ServerConfiguration.cs index 7258804e0..30c7c89be 100644 --- a/ProjectLighthouse/Configuration/ServerConfiguration.cs +++ b/ProjectLighthouse/Configuration/ServerConfiguration.cs @@ -11,7 +11,7 @@ public class ServerConfiguration : ConfigurationBase // This is so Lighthouse can properly identify outdated configurations and update them with newer settings accordingly. // If you are modifying anything here, this value MUST be incremented. // Thanks for listening~ - public override int ConfigVersion { get; set; } = 25; + public override int ConfigVersion { get; set; } = 26; public override string ConfigName { get; set; } = "lighthouse.yml"; public string WebsiteListenUrl { get; set; } = "http://localhost:10060"; From 7dc185d9017df980ee2c0f84661ab3f88fc1af78 Mon Sep 17 00:00:00 2001 From: sudokoko Date: Wed, 27 Mar 2024 23:20:26 -0400 Subject: [PATCH 02/12] Use localized string under default language for announce text --- .../Controllers/MessageController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs index 3405818f9..4666366f6 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs @@ -3,6 +3,8 @@ using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Localization; +using LBPUnion.ProjectLighthouse.Localization.StringLists; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Entities.Notifications; @@ -61,9 +63,7 @@ public async Task Announce() if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) { - announceText.Insert(0, "This instance is currently in read-only mode. Level and photo uploads, comments, " + - "reviews, and certain profile changes will be restricted until read-only mode is " + - "disabled."); + announceText.Insert(0, BaseLayoutStrings.ReadOnlyWarn.Translate(LocalizationManager.DefaultLang)); } #if DEBUG From d078afd99a30cf7718b80698f63a71bf4311ec0c Mon Sep 17 00:00:00 2001 From: sudokoko Date: Wed, 27 Mar 2024 23:48:55 -0400 Subject: [PATCH 03/12] Redirect to user page rather than returning blank 400 --- .../Pages/UserSettingsPage.cshtml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs index cf24b0b2b..2704e752c 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs @@ -44,7 +44,7 @@ [FromForm] string? language if (avatarHash != null) { // Deny request if in read-only mode - if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.Redirect($"~/user/{userId}"); this.ProfileUser.IconHash = avatarHash; } @@ -54,7 +54,7 @@ [FromForm] string? language if (biography != null) { // Deny request if in read-only mode - if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.Redirect($"~/user/{userId}"); biography = CensorHelper.FilterMessage(biography); if (this.ProfileUser.Biography != biography && biography.Length <= 512) From aafdaf19fdd8c9dd2ec2d372df5e00b738b16739 Mon Sep 17 00:00:00 2001 From: sudokoko Date: Wed, 27 Mar 2024 23:50:23 -0400 Subject: [PATCH 04/12] Protect call to `ParseBase64Image` --- .../Pages/UserSettingsPage.cshtml.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs index 2704e752c..c1db93957 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs @@ -39,15 +39,13 @@ [FromForm] string? language if (!this.User.IsModerator && this.User != this.ProfileUser) return this.Redirect("~/user/" + userId); - string? avatarHash = await FileHelper.ParseBase64Image(avatar); + // Deny request if in read-only mode + if (avatar != null && ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) + return this.Redirect($"~/user/{userId}"); - if (avatarHash != null) - { - // Deny request if in read-only mode - if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.Redirect($"~/user/{userId}"); + string? avatarHash = await FileHelper.ParseBase64Image(avatar); - this.ProfileUser.IconHash = avatarHash; - } + if (avatarHash != null) this.ProfileUser.IconHash = avatarHash; if (this.User.IsAdmin) this.ProfileUser.ProfileTag = profileTag; From 1e188a017cae4de494aa64acd38cb6201d90e9f0 Mon Sep 17 00:00:00 2001 From: sudokoko Date: Thu, 28 Mar 2024 00:01:27 -0400 Subject: [PATCH 05/12] Add protections to SlotSettingsPage and nitpick format --- .../Pages/SlotSettingsPage.cshtml.cs | 7 ++++++- .../Pages/UserSettingsPage.cshtml.cs | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/SlotSettingsPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/SlotSettingsPage.cshtml.cs index 310386e76..b461d9825 100644 --- a/ProjectLighthouse.Servers.Website/Pages/SlotSettingsPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/SlotSettingsPage.cshtml.cs @@ -1,4 +1,5 @@ #nullable enable +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Helpers; @@ -25,6 +26,10 @@ public async Task OnPost([FromRoute] int slotId, [FromForm] strin if (!this.User.IsModerator && this.User != this.Slot.Creator) return this.Redirect("~/slot/" + slotId); + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) + return this.Redirect($"~/slot/{slotId}"); + string? avatarHash = await FileHelper.ParseBase64Image(avatar); if (avatarHash != null) this.Slot.IconHash = avatarHash; @@ -46,7 +51,7 @@ public async Task OnPost([FromRoute] int slotId, [FromForm] strin if (labels != null) { labels = LabelHelper.RemoveInvalidLabels(labels); - if (this.Slot.AuthorLabels != labels) + if (this.Slot.AuthorLabels != labels) this.Slot.AuthorLabels = labels; } diff --git a/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs index c1db93957..85b0c242f 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs @@ -52,7 +52,8 @@ [FromForm] string? language if (biography != null) { // Deny request if in read-only mode - if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.Redirect($"~/user/{userId}"); + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) + return this.Redirect($"~/user/{userId}"); biography = CensorHelper.FilterMessage(biography); if (this.ProfileUser.Biography != biography && biography.Length <= 512) From f3331011fb51e81c83b3aa5af5c42c79bd210f7a Mon Sep 17 00:00:00 2001 From: sudokoko Date: Thu, 28 Mar 2024 13:46:16 -0400 Subject: [PATCH 06/12] Display the latest announcement (if any) on the landing page --- .../Pages/LandingPage.cshtml | 29 ++++++++++++++++++- .../Pages/LandingPage.cshtml.cs | 7 +++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml index 903dbf0cb..95c749163 100644 --- a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml @@ -60,7 +60,34 @@ } } -

+@if (Model.LatestAnnouncement != null) +{ +
+
+

@Model.LatestAnnouncement.Title

+
+ + @(Model.LatestAnnouncement.Content.Length > 250 + ? Model.LatestAnnouncement.Content[..250] + $"... [read more]({ServerConfiguration.Instance.ExternalUrl}/notifications)" + : Model.LatestAnnouncement.Content) + +
+ @if (Model.LatestAnnouncement.Publisher != null) + { + + } +
+
+} +else +{ +

+}
diff --git a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs index f866557b3..37edf44bd 100644 --- a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs @@ -5,6 +5,7 @@ using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; +using LBPUnion.ProjectLighthouse.Types.Entities.Website; using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -19,6 +20,8 @@ public class LandingPage : BaseLayout public int PendingAuthAttempts; public List PlayersOnline = new(); + public WebsiteAnnouncementEntity? LatestAnnouncement; + public LandingPage(DatabaseContext database) : base(database) { } @@ -54,6 +57,10 @@ public async Task OnGet() .Include(s => s.Creator) .ToListAsync(); + this.LatestAnnouncement = await this.Database.WebsiteAnnouncements.Include(a => a.Publisher) + .OrderByDescending(a => a.AnnouncementId) + .FirstOrDefaultAsync(); + return this.Page(); } } \ No newline at end of file From 939e192732df8fa6757141ffdc9a01b308fb352f Mon Sep 17 00:00:00 2001 From: sudokoko Date: Thu, 28 Mar 2024 13:54:42 -0400 Subject: [PATCH 07/12] Fix a kokoism Accidentally tried to use markdown within the landing page... I'm rather smart aren't I --- .../Pages/LandingPage.cshtml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml index 95c749163..b27711ad3 100644 --- a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml @@ -67,9 +67,13 @@

@Model.LatestAnnouncement.Title

- @(Model.LatestAnnouncement.Content.Length > 250 - ? Model.LatestAnnouncement.Content[..250] + $"... [read more]({ServerConfiguration.Instance.ExternalUrl}/notifications)" - : Model.LatestAnnouncement.Content) + @{ + string truncatedAnnouncement = Model.LatestAnnouncement.Content.Length > 250 + ? Model.LatestAnnouncement.Content[..250] + + $"... read more" + : Model.LatestAnnouncement.Content; + } + @Html.Raw(truncatedAnnouncement)
@if (Model.LatestAnnouncement.Publisher != null) From 1884ffd95f85d5a510635a5e42b34de153f3c502 Mon Sep 17 00:00:00 2001 From: sudokoko Date: Fri, 29 Mar 2024 16:54:03 -0400 Subject: [PATCH 08/12] Prevent possible XSS --- .../Pages/LandingPage.cshtml | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml index b27711ad3..abce659a3 100644 --- a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml @@ -66,15 +66,19 @@

@Model.LatestAnnouncement.Title

- - @{ - string truncatedAnnouncement = Model.LatestAnnouncement.Content.Length > 250 - ? Model.LatestAnnouncement.Content[..250] + - $"... read more" - : Model.LatestAnnouncement.Content; + @if (Model.LatestAnnouncement.Content.Length > 250) + { + + @Model.LatestAnnouncement.Content[..250] + read more + + } + else + { + + @Model.LatestAnnouncement.Content + } - @Html.Raw(truncatedAnnouncement) -
@if (Model.LatestAnnouncement.Publisher != null) { From 46dfd3dc117f30e927faf1b6cfb13f202690ebc8 Mon Sep 17 00:00:00 2001 From: sudokoko Date: Fri, 29 Mar 2024 16:56:21 -0400 Subject: [PATCH 09/12] Separate truncated announcement text and link with "..." --- ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml index abce659a3..9f9ba991e 100644 --- a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml @@ -69,7 +69,7 @@ @if (Model.LatestAnnouncement.Content.Length > 250) { - @Model.LatestAnnouncement.Content[..250] + @Model.LatestAnnouncement.Content[..250]... read more } From 83ca4ce9c865279ae4a854b2c0dfaf71ae09e6a0 Mon Sep 17 00:00:00 2001 From: sudokoko Date: Fri, 29 Mar 2024 22:41:41 -0400 Subject: [PATCH 10/12] Apply suggestion from code review --- .../Controllers/UserPageController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs index 284522ff6..afd23458d 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs @@ -40,7 +40,7 @@ public async Task PostComment([FromRoute] int id, [FromForm] stri if (token == null) return this.Redirect("~/login"); // Deny request if in read-only mode - if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest(); + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.Redirect("~/user/" + id); if (msg == null) { From 773b414a13b575697d6d66a8cb967c7bc2b36436 Mon Sep 17 00:00:00 2001 From: sudokoko Date: Fri, 29 Mar 2024 22:46:57 -0400 Subject: [PATCH 11/12] Add read-only check to /postComment in slot page controller --- .../Controllers/SlotPageController.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs b/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs index 66eafbc08..7385f9c8b 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -// I would like to apologize in advance for anyone dealing with this file. +// I would like to apologize in advance for anyone dealing with this file. // Theres probably a better way to do this with delegates but I'm tired. // TODO: Clean up this file // - jvyden @@ -63,6 +63,9 @@ public async Task PostComment([FromRoute] int id, [FromForm] stri WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.Redirect("~/slot/" + id); + if (msg == null) { Logger.Error($"Refusing to post comment from {token.UserId} on level {id}, {nameof(msg)} is null", LogArea.Comments); From 6cf126d22daf11e251f61d802dd36c354db33c01 Mon Sep 17 00:00:00 2001 From: sudokoko Date: Fri, 29 Mar 2024 22:48:02 -0400 Subject: [PATCH 12/12] Fix inconsistent tabbing --- .../Controllers/SlotPageController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs b/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs index 7385f9c8b..7f22b0b6b 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs @@ -63,8 +63,8 @@ public async Task PostComment([FromRoute] int id, [FromForm] stri WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); - // Deny request if in read-only mode - if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.Redirect("~/slot/" + id); + // Deny request if in read-only mode + if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.Redirect("~/slot/" + id); if (msg == null) {