diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Login/LoginController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Login/LoginController.cs index 077f1b006..000b84aa7 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Login/LoginController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Login/LoginController.cs @@ -3,6 +3,7 @@ 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 async Task Login() 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 89140f71c..a12f9d64a 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Login/LogoutController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Login/LogoutController.cs @@ -38,5 +38,4 @@ public async Task OnPost() 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 42b1d1b7b..cccc9b485 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 000000000..3a7f604f5 --- /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.Servers.Website/Controllers/AutoDiscoverController.cs b/ProjectLighthouse.Servers.Website/Controllers/AutoDiscoverController.cs new file mode 100644 index 000000000..fab168c4c --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Controllers/AutoDiscoverController.cs @@ -0,0 +1,26 @@ +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Servers.Website.Types; +using Microsoft.AspNetCore.Mvc; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers; + +[ApiController] +public class AutoDiscoverController: ControllerBase +{ + [ResponseCache(Duration = 86400)] + [HttpGet("autodiscover")] + [Produces("application/json")] + public IActionResult AutoDiscover() + { + AutoDiscoverResponse resp = new() + { + Version = 3, + Url = ServerConfiguration.Instance.GameApiExternalUrl, + ServerBrand = ServerConfiguration.Instance.Customization.ServerName, + UsesCustomDigestKey = false, + BannerImageUrl = null, + ServerDescription = ServerConfiguration.Instance.Customization.ServerDescription, + }; + return this.Ok(resp); + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Types/AutoDiscoverResponse.cs b/ProjectLighthouse.Servers.Website/Types/AutoDiscoverResponse.cs new file mode 100644 index 000000000..e309ed63c --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Types/AutoDiscoverResponse.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Types; + +public record AutoDiscoverResponse +{ + [JsonProperty("version")] + public required uint Version { get; set; } + [JsonProperty("serverBrand")] + public required string ServerBrand { get; set; } + [JsonProperty("serverDescription")] + public required string ServerDescription { get; set; } + [JsonProperty("url")] + public required string Url { get; set; } + [JsonProperty("bannerImageUrl")] + public string? BannerImageUrl { get; set; } + [JsonProperty("usesCustomDigestKey")] + public required bool UsesCustomDigestKey { get; set; } +} diff --git a/ProjectLighthouse.Tests.GameApiTests/Unit/PatchworkUserAgentTests.cs b/ProjectLighthouse.Tests.GameApiTests/Unit/PatchworkUserAgentTests.cs new file mode 100644 index 000000000..5567fc911 --- /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 4c3abb368..057485ece 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/ConfigurationCategories/CustomizationConfiguration.cs b/ProjectLighthouse/Configuration/ConfigurationCategories/CustomizationConfiguration.cs index ee6b17e42..3d026f519 100644 --- a/ProjectLighthouse/Configuration/ConfigurationCategories/CustomizationConfiguration.cs +++ b/ProjectLighthouse/Configuration/ConfigurationCategories/CustomizationConfiguration.cs @@ -4,4 +4,5 @@ public class CustomizationConfiguration { public string ServerName { get; set; } = "Project Lighthouse"; public string EnvironmentName { get; set; } = "project-lighthouse"; + public string ServerDescription { get; set; } = "A Project Lighthouse Server"; } \ No newline at end of file diff --git a/ProjectLighthouse/Configuration/ServerConfiguration.cs b/ProjectLighthouse/Configuration/ServerConfiguration.cs index ba9f7f4a1..d145495c6 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; } = 31; public override string ConfigName { get; set; } = "lighthouse.yml"; public string WebsiteListenUrl { get; set; } = "http://localhost:10060";