This commit is contained in:
Henry Asbridge 2025-08-13 21:38:07 +01:00 committed by GitHub
commit 537e56a977
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 113 additions and 6 deletions

View file

@ -21,4 +21,7 @@
<data name="username_notice" xml:space="preserve">
<value>Caution: Your username MUST match your PSN/RPCN username in order to be able to sign in from in-game.</value>
</data>
<data name="email_verify_notice" xml:space="preserve">
<value>You do not have an email set on your account or you have not verified it. You can set an email by opening the text chat and typing "/setemail [youremail@example.com]" (do not include the brackets), then check your inbox for a verification email.</value>
</data>
</root>

View file

@ -3,6 +3,7 @@ namespace LBPUnion.ProjectLighthouse.Localization.StringLists;
public static class RegisterStrings
{
public static readonly TranslatableString UsernameNotice = create("username_notice");
public static readonly TranslatableString EmailVerificationNotice = create("email_verify_notice");
private static TranslatableString create(string key) => new(TranslationAreas.Register, key);
}

View file

@ -3,6 +3,7 @@ using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
using LBPUnion.ProjectLighthouse.Types.Filter;
@ -29,6 +30,7 @@ public class CommentController : ControllerBase
[HttpPost("rateUserComment/{username}")]
[HttpPost("rateComment/{slotType}/{slotId:int}")]
[EmailVerification]
public async Task<IActionResult> RateComment([FromQuery] int commentId, [FromQuery] int rating, string? username, string? slotType, int slotId)
{
GameTokenEntity token = this.GetToken();
@ -113,6 +115,7 @@ public class CommentController : ControllerBase
[HttpPost("postUserComment/{username}")]
[HttpPost("postComment/{slotType}/{slotId:int}")]
[EmailVerification]
public async Task<IActionResult> PostComment(string? username, string? slotType, int slotId)
{
GameTokenEntity token = this.GetToken();
@ -152,6 +155,7 @@ public class CommentController : ControllerBase
[HttpPost("deleteUserComment/{username}")]
[HttpPost("deleteComment/{slotType}/{slotId:int}")]
[EmailVerification]
public async Task<IActionResult> DeleteComment([FromQuery] int commentId, string? username, string? slotType, int slotId)
{
GameTokenEntity token = this.GetToken();

View file

@ -1,6 +1,7 @@
#nullable enable
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users;
using LBPUnion.ProjectLighthouse.StorableLists.Stores;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
@ -17,6 +18,7 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[Authorize]
[Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
[EmailVerification]
public class FriendsController : ControllerBase
{
private readonly DatabaseContext database;

View file

@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using LBPUnion.ProjectLighthouse.Types.Serialization;
@ -70,6 +71,7 @@ public class ClientConfigurationController : ControllerBase
[HttpPost("privacySettings")]
[Produces("text/xml")]
[EmailVerification]
public async Task<IActionResult> SetPrivacySetting()
{
UserEntity? user = await this.database.UserFromGameToken(this.GetToken());

View file

@ -2,6 +2,7 @@
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
@ -16,6 +17,7 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Matching;
[Authorize]
[Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
[EmailVerification]
public class EnterLevelController : ControllerBase
{
private readonly DatabaseContext database;

View file

@ -5,6 +5,7 @@ using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
using LBPUnion.ProjectLighthouse.Types.Logging;
@ -21,6 +22,7 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Matching;
[Authorize]
[Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
[EmailVerification]
public class MatchController : ControllerBase
{
private readonly DatabaseContext database;

View file

@ -3,7 +3,6 @@ using LBPUnion.ProjectLighthouse.Configuration;
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;
@ -13,6 +12,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Token;
using LBPUnion.ProjectLighthouse.Types.Filter;
using LBPUnion.ProjectLighthouse.Types.Logging;
using LBPUnion.ProjectLighthouse.Types.Mail;
using LBPUnion.ProjectLighthouse.Types.Notifications;
using LBPUnion.ProjectLighthouse.Types.Serialization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -55,16 +55,23 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.";
{
GameTokenEntity token = this.GetToken();
string username = await this.database.UsernameFromGameToken(token);
UserEntity? user = await this.database.UserFromGameToken(token);
if (user == null) return this.Forbid();
StringBuilder announceText = new(ServerConfiguration.Instance.AnnounceText);
announceText.Replace("%user", username);
announceText.Replace("%user", user.Username);
announceText.Replace("%id", token.UserId.ToString());
if (ServerConfiguration.Instance.Mail.RequireEmailVerification)
{
announceText.Insert(0,
RegisterStrings.EmailVerificationNotice.Translate(user.Language) + "\n\n");
}
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode)
{
announceText.Insert(0, BaseLayoutStrings.ReadOnlyWarn.Translate(LocalizationManager.DefaultLang) + "\n\n");
announceText.Insert(0, BaseLayoutStrings.ReadOnlyWarn.Translate(user.Language) + "\n\n");
}
#if DEBUG
@ -96,6 +103,15 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.";
StringBuilder builder = new();
UserEntity? user = await this.database.UserFromGameToken(token);
if (user?.EmailAddressVerified == false)
{
GameNotification verifyEmailNotification = new();
verifyEmailNotification.Type = NotificationType.ModerationNotification;
verifyEmailNotification.Text = RegisterStrings.EmailVerificationNotice.Translate(user.Language);
builder.AppendLine(LighthouseSerializer.Serialize(this.HttpContext.RequestServices, verifyEmailNotification));
}
foreach (NotificationEntity notification in notifications)
{
builder.AppendLine(LighthouseSerializer.Serialize(this.HttpContext.RequestServices,

View file

@ -5,6 +5,7 @@ using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Files;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.Types.Entities.Moderation;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
using LBPUnion.ProjectLighthouse.Types.Moderation.Reports;
@ -18,6 +19,7 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[Authorize]
[Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
[EmailVerification]
public class ReportController : ControllerBase
{
private readonly DatabaseContext database;

View file

@ -6,6 +6,7 @@ using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Files;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
@ -33,6 +34,7 @@ public class PhotosController : ControllerBase
}
[HttpPost("uploadPhoto")]
[EmailVerification]
public async Task<IActionResult> UploadPhoto()
{
GameTokenEntity token = this.GetToken();
@ -234,6 +236,7 @@ public class PhotosController : ControllerBase
}
[HttpPost("deletePhoto/{id:int}")]
[EmailVerification]
public async Task<IActionResult> DeletePhoto(int id)
{
GameTokenEntity token = this.GetToken();

View file

@ -1,9 +1,9 @@
#nullable enable
using System.Text;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Files;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Misc;
using LBPUnion.ProjectLighthouse.Types.Logging;
using LBPUnion.ProjectLighthouse.Types.Resources;
@ -52,6 +52,7 @@ public class ResourcesController : ControllerBase
[HttpPost("upload/{hash}/unattributed")]
[HttpPost("upload/{hash}")]
[EmailVerification]
public async Task<IActionResult> UploadResource(string hash)
{
string assetsDirectory = FileHelper.ResourcePath;

View file

@ -1,6 +1,7 @@
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
@ -41,6 +42,7 @@ public class LevelTagsController : ControllerBase
}
[HttpPost("tag/{slotType}/{id:int}")]
[EmailVerification]
public async Task<IActionResult> PostTag([FromForm(Name = "t")] string tagName, [FromRoute] string slotType, [FromRoute] int id)
{
GameTokenEntity token = this.GetToken();

View file

@ -2,6 +2,7 @@
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
using LBPUnion.ProjectLighthouse.Types.Serialization;
@ -41,6 +42,7 @@ public class PlaylistController : ControllerBase
}
[HttpPost("playlists/{playlistId:int}/delete")]
[EmailVerification]
public async Task<IActionResult> DeletePlaylist(int playlistId)
{
GameTokenEntity token = this.GetToken();
@ -60,6 +62,7 @@ public class PlaylistController : ControllerBase
[HttpPost("playlists/{playlistId:int}/slots")]
[HttpPost("playlists/{playlistId:int}/slots/{slotId:int}/delete")]
[HttpPost("playlists/{playlistId:int}/order_slots")]
[EmailVerification]
public async Task<IActionResult> UpdatePlaylist(int playlistId, int slotId)
{
GameTokenEntity token = this.GetToken();
@ -124,6 +127,7 @@ public class PlaylistController : ControllerBase
}
[HttpPost("playlists")]
[EmailVerification]
public async Task<IActionResult> CreatePlaylist()
{
GameTokenEntity token = this.GetToken();

View file

@ -6,6 +6,7 @@ using LBPUnion.ProjectLighthouse.Files;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Helpers;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
@ -24,6 +25,7 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
[Authorize]
[Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
[EmailVerification]
public class PublishController : ControllerBase
{
private readonly DatabaseContext database;

View file

@ -3,6 +3,7 @@ using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
@ -29,6 +30,7 @@ public class ReviewController : ControllerBase
// LBP1 rating
[HttpPost("rate/user/{slotId:int}")]
[EmailVerification]
public async Task<IActionResult> Rate(int slotId, int rating)
{
GameTokenEntity token = this.GetToken();
@ -58,6 +60,7 @@ public class ReviewController : ControllerBase
// LBP2 and beyond rating
[HttpPost("dpadrate/user/{slotId:int}")]
[EmailVerification]
public async Task<IActionResult> DPadRate(int slotId, int rating)
{
GameTokenEntity token = this.GetToken();
@ -89,6 +92,7 @@ public class ReviewController : ControllerBase
}
[HttpPost("postReview/user/{slotId:int}")]
[EmailVerification]
public async Task<IActionResult> PostReview(int slotId)
{
GameTokenEntity token = this.GetToken();
@ -202,6 +206,7 @@ public class ReviewController : ControllerBase
}
[HttpPost("rateReview/user/{slotId:int}/{username}")]
[EmailVerification]
public async Task<IActionResult> RateReview(int slotId, string username, int rating = 0)
{
GameTokenEntity token = this.GetToken();
@ -255,6 +260,7 @@ public class ReviewController : ControllerBase
}
[HttpPost("deleteReview/user/{slotId:int}/{username}")]
[EmailVerification]
public async Task<IActionResult> DeleteReview(int slotId, string username)
{
GameTokenEntity token = this.GetToken();

View file

@ -3,6 +3,7 @@ using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.StorableLists.Stores;
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
@ -42,6 +43,7 @@ public class ScoreController : ControllerBase
[HttpPost("scoreboard/{slotType}/{id:int}")]
[HttpPost("scoreboard/{slotType}/{id:int}/{childId:int}")]
[EmailVerification]
public async Task<IActionResult> SubmitScore(string slotType, int id, int childId)
{
GameTokenEntity token = this.GetToken();

View file

@ -6,6 +6,7 @@ using LBPUnion.ProjectLighthouse.Files;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Helpers;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users;
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
@ -63,6 +64,7 @@ public class UserController : ControllerBase
}
[HttpPost("updateUser")]
[EmailVerification]
public async Task<IActionResult> UpdateUser()
{
GameTokenEntity token = this.GetToken();
@ -171,6 +173,7 @@ public class UserController : ControllerBase
[HttpPost("update_my_pins")]
[Produces("text/json")]
[EmailVerification]
public async Task<IActionResult> UpdateMyPins()
{
UserEntity? user = await this.database.UserFromGameToken(this.GetToken());

View file

@ -0,0 +1,47 @@
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Middlewares;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
using Microsoft.AspNetCore.Http.Features;
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
public class EmailVerificationAttribute : Attribute;
public class EmailVerificationMiddleware: MiddlewareDBContext
{
public EmailVerificationMiddleware(RequestDelegate next) : base(next)
{
}
public override async Task InvokeAsync(HttpContext context, DatabaseContext database)
{
if (ServerConfiguration.Instance.Mail.RequireEmailVerification)
{
Endpoint? endpoint = context.Features.Get<IEndpointFeature>()?.Endpoint;
EmailVerificationAttribute? attribute = endpoint?.Metadata.GetMetadata<EmailVerificationAttribute>();
if (attribute != null)
{
GameTokenEntity? gameToken = await database.GameTokenFromRequest(context.Request);
if (gameToken == null)
{
context.Response.StatusCode = 403;
context.Abort();
return;
}
UserEntity? user = await database.UserFromGameToken(gameToken);
if (!user!.EmailAddressVerified)
{
context.Response.StatusCode = 401; // 403 will cause a re-auth
context.Abort();
return;
}
}
}
await this.next(context);
}
}

View file

@ -106,6 +106,7 @@ public class GameServerStartup
app.UseMiddleware<RequestLogMiddleware>();
app.UseMiddleware<RateLimitMiddleware>();
app.UseMiddleware<DigestMiddleware>(computeDigests);
app.UseMiddleware<EmailVerificationMiddleware>();
app.UseMiddleware<SetLastContactMiddleware>();
app.UseRouting();

View file

@ -17,4 +17,6 @@ public class MailConfiguration
public string Password { get; set; } = "";
public bool UseSSL { get; set; } = true;
public bool RequireEmailVerification { get; set; } = true;
}

View file

@ -11,7 +11,7 @@ public class ServerConfiguration : ConfigurationBase<ServerConfiguration>
// 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; } = 31;
public override int ConfigVersion { get; set; } = 32;
public override string ConfigName { get; set; } = "lighthouse.yml";
public string WebsiteListenUrl { get; set; } = "http://localhost:10060";