diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Login/LoginController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Login/LoginController.cs index 077f1b00..000b84aa 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Login/LoginController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Login/LoginController.cs @@ -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.Helpers; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Tickets; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; @@ -212,6 +213,14 @@ public class LoginController : ControllerBase return this.Forbid(); } + if (ServerConfiguration.Instance.Authentication.RequirePatchworkUserAgent) + { + if (!PatchworkHelper.IsValidPatchworkUserAgent(this.Request.Headers.UserAgent.ToString())) + { + return this.Forbid(); + } + } + Logger.Success($"Successfully logged in user {user.Username} as {token.GameVersion} client", LogArea.Login); user.LastLogin = TimeHelper.TimestampMillis; diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Login/LogoutController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Login/LogoutController.cs index 89140f71..a12f9d64 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Login/LogoutController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Login/LogoutController.cs @@ -38,5 +38,4 @@ public class LogoutController : ControllerBase return this.Ok(); } - } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs index 42b1d1b7..cccc9b48 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs @@ -1,4 +1,4 @@ -using System.Text; +using System.Text; using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; diff --git a/ProjectLighthouse.Servers.GameServer/Helpers/PatchworkHelper.cs b/ProjectLighthouse.Servers.GameServer/Helpers/PatchworkHelper.cs new file mode 100644 index 00000000..3a7f604f --- /dev/null +++ b/ProjectLighthouse.Servers.GameServer/Helpers/PatchworkHelper.cs @@ -0,0 +1,24 @@ +using LBPUnion.ProjectLighthouse.Configuration; +using System.Text.RegularExpressions; + +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Helpers; + +public static partial class PatchworkHelper +{ + private static readonly int requiredMajor = ServerConfiguration.Instance.Authentication.PatchworkMajorVersionMinimum; + private static readonly int requiredMinor = ServerConfiguration.Instance.Authentication.PatchworkMinorVersionMinimum; + + [GeneratedRegex(@"^PatchworkLBP[123V] (\d{1,5})\.(\d{1,5})$")] + private static partial Regex PatchworkUserAgentRegex(); + + public static bool IsValidPatchworkUserAgent(string userAgent) + { + Match result = PatchworkUserAgentRegex().Match(userAgent); + if (!result.Success) return false; + + if (!int.TryParse(result.Groups[1].Value, out int major) || !int.TryParse(result.Groups[2].Value, out int minor)) + return false; + + return major >= requiredMajor && minor >= requiredMinor; + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Tests.GameApiTests/Unit/PatchworkUserAgentTests.cs b/ProjectLighthouse.Tests.GameApiTests/Unit/PatchworkUserAgentTests.cs new file mode 100644 index 00000000..5567fc91 --- /dev/null +++ b/ProjectLighthouse.Tests.GameApiTests/Unit/PatchworkUserAgentTests.cs @@ -0,0 +1,44 @@ +using LBPUnion.ProjectLighthouse.Servers.GameServer.Helpers; +using Xunit; + +namespace ProjectLighthouse.Tests.GameApiTests.Unit; + +[Trait("Category", "Unit")] +public class PatchworkUserAgentTests +{ + [Fact] + public void CanValidatePatchworkUserAgents() + { + string[] validUserAgents = { + "PatchworkLBP1 1.0", + "PatchworkLBP2 2.0", + "PatchworkLBP3 3.0", + "PatchworkLBPV 4.0", + "PatchworkLBP1 1.5", + }; + + string[] invalidUserAgents = { + // Matching + "patchworklbp1 1.0", // Case sensitive + "ptchwrklbp1 1.0", // Misspelled + "PatchworkLBP1 1", // Missing major/minor + "PatchworkLBP1 1.000001", // Major/minor too long + + // Data + "PatchworkLBP1 0.5", // Version number too low + "PatchworkLBP1 A.0" // Int cannot be parsed + }; + + bool result; + foreach (string userAgent in validUserAgents) + { + result = PatchworkHelper.IsValidPatchworkUserAgent(userAgent); + Assert.True(result, $"Valid user agent: \"{userAgent}\" was evaluated as {result}."); + } + foreach (string userAgent in invalidUserAgents) + { + result = PatchworkHelper.IsValidPatchworkUserAgent(userAgent); + Assert.False(result, $"Invalid user agent: \"{userAgent}\" was evaluated as {result}."); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Configuration/ConfigurationCategories/AuthenticationConfiguration.cs b/ProjectLighthouse/Configuration/ConfigurationCategories/AuthenticationConfiguration.cs index 4c3abb36..057485ec 100644 --- a/ProjectLighthouse/Configuration/ConfigurationCategories/AuthenticationConfiguration.cs +++ b/ProjectLighthouse/Configuration/ConfigurationCategories/AuthenticationConfiguration.cs @@ -9,5 +9,11 @@ public class AuthenticationConfiguration public bool AllowRPCNSignup { get; set; } = true; public bool AllowPSNSignup { get; set; } = true; + + // Require use of Zaprit's "Patchwork" prx plugin's user agent when connecting to the server + // Major and minor version minimums can be left alone if patchwork is not required + public bool RequirePatchworkUserAgent { get; set; } = false; + public int PatchworkMajorVersionMinimum { get; set; } = 1; + public int PatchworkMinorVersionMinimum { get; set; } = 0; } \ No newline at end of file diff --git a/ProjectLighthouse/Configuration/ServerConfiguration.cs b/ProjectLighthouse/Configuration/ServerConfiguration.cs index ba9f7f4a..d9c66dee 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; } = 27; + public override int ConfigVersion { get; set; } = 30; public override string ConfigName { get; set; } = "lighthouse.yml"; public string WebsiteListenUrl { get; set; } = "http://localhost:10060"; @@ -31,7 +31,6 @@ public class ServerConfiguration : ConfigurationBase public bool CheckForUnsafeFiles { get; set; } = true; public bool LogChatFiltering { get; set; } = false; public bool LogChatMessages { get; set; } = false; - public AuthenticationConfiguration Authentication { get; set; } = new(); public CaptchaConfiguration Captcha { get; set; } = new(); public DigestKeyConfiguration DigestKey { get; set; } = new();