From fc51b60cc13c42d721a7719f2d20c93ed7789bd3 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Mon, 10 Jan 2022 23:14:55 -0800 Subject: [PATCH] fix: Adds server access with protected accounts (#915) Adds a configuration file to specify protected accounts: _Distribution/Configuration/server-access.json_ ```json { "newPasswordOnReset": false, "protectedAccounts": ["admin"] } ``` Protected accounts are unbanned and reset to `AccessLevel.Owner` upon login. The option `newPasswordOnReset` will create a new password for a protected account if it needs to be reset. The password is a random GUID and logged to the console. _**Note:**_ If your player character was accidentally modified, simply make a new character to fix the old one. --- .../UOContent/Accounting/AccountHandler.cs | 9 +- Projects/UOContent/Misc/AccountPrompt.cs | 3 + Projects/UOContent/Misc/PacketThrottles.cs | 2 +- Projects/UOContent/Misc/ServerAccess.cs | 99 +++++++++++++++++++ 4 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 Projects/UOContent/Misc/ServerAccess.cs diff --git a/Projects/UOContent/Accounting/AccountHandler.cs b/Projects/UOContent/Accounting/AccountHandler.cs index 642660d05..583091ed7 100644 --- a/Projects/UOContent/Accounting/AccountHandler.cs +++ b/Projects/UOContent/Accounting/AccountHandler.cs @@ -241,13 +241,15 @@ namespace Server.Misc { res = DeleteResultType.CharBeingPlayed; } - else if (RestrictDeletion && Core.Now < m.Created + DeleteDelay) + else if (acct.AccessLevel == AccessLevel.Player && RestrictDeletion && Core.Now < m.Created + DeleteDelay) { res = DeleteResultType.CharTooYoung; } - else if (m.AccessLevel == AccessLevel.Player && + // Don't need to check current location, if netstate is null, they're logged out + else if ( + m.AccessLevel == AccessLevel.Player && Region.Find(m.LogoutLocation, m.LogoutMap).IsPartOf() - ) // Don't need to check current location, if netstate is null, they're logged out + ) { res = DeleteResultType.BadRequest; } @@ -265,7 +267,6 @@ namespace Server.Misc state.SendCharacterDeleteResult(res); state.SendCharacterListUpdate(acct); - } public static bool CanCreate(IPAddress ip) => diff --git a/Projects/UOContent/Misc/AccountPrompt.cs b/Projects/UOContent/Misc/AccountPrompt.cs index 0e537c0d5..1483f022e 100644 --- a/Projects/UOContent/Misc/AccountPrompt.cs +++ b/Projects/UOContent/Misc/AccountPrompt.cs @@ -27,6 +27,9 @@ namespace Server.Misc a.AccessLevel = AccessLevel.Owner; Console.WriteLine("Account created."); + + ServerAccess.AddProtectedAccount(a, true); + Console.WriteLine("Added {0} to the protected accounts list.", a.Username); } else { diff --git a/Projects/UOContent/Misc/PacketThrottles.cs b/Projects/UOContent/Misc/PacketThrottles.cs index d2b666ed9..dda06aab8 100644 --- a/Projects/UOContent/Misc/PacketThrottles.cs +++ b/Projects/UOContent/Misc/PacketThrottles.cs @@ -10,7 +10,7 @@ namespace Server.Network { // Delay in milliseconds private static readonly int[] Delays = new int[0x100]; - private static string ThrottlesConfiguration = "Configuration/throttles.json"; + private const string ThrottlesConfiguration = "Configuration/throttles.json"; public static void Initialize() { diff --git a/Projects/UOContent/Misc/ServerAccess.cs b/Projects/UOContent/Misc/ServerAccess.cs new file mode 100644 index 000000000..433055b86 --- /dev/null +++ b/Projects/UOContent/Misc/ServerAccess.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json.Serialization; +using Server.Accounting; +using Server.Json; +using Server.Logging; + +namespace Server.Misc; + +public static class ServerAccess +{ + private static readonly ILogger logger = LogFactory.GetLogger(typeof(ServerAccess)); + private const string _serverAccessConfigurationPath = "Configuration/server-access.json"; + public static ServerAccessConfiguration ServerAccessConfiguration { get; private set; } + + public static void SaveConfiguration() + { + var path = Path.Join(Core.BaseDirectory, _serverAccessConfigurationPath); + JsonConfig.Serialize(path, ServerAccessConfiguration); + } + + public static void AddProtectedAccount(Account acct, bool save = false) + { + ServerAccessConfiguration.ProtectedAccounts.Add(acct.Username.ToLower()); + + if (save) + { + SaveConfiguration(); + } + } + + public static void RemoveProtectedAccount(Account acct, bool save = false) + { + ServerAccessConfiguration.ProtectedAccounts.Remove(acct.Username.ToLower()); + + if (save) + { + SaveConfiguration(); + } + } + + public static void Configure() + { + var path = Path.Join(Core.BaseDirectory, _serverAccessConfigurationPath); + + if (!File.Exists(path)) + { + return; + } + + ServerAccessConfiguration = JsonConfig.Deserialize(path); + var protectedAccounts = string.Join(", ", ServerAccessConfiguration.ProtectedAccounts); + logger.Information("Protected accounts registered: {0}", protectedAccounts); + } + + public static void Initialize() + { + EventSink.AccountLogin += EventSink_ResetProtectedAccount; + } + + public static void EventSink_ResetProtectedAccount(AccountLoginEventArgs e) + { + var username = e.Username.ToLower(); + if (!ServerAccessConfiguration.ProtectedAccounts.Contains(username)) + { + return; + } + + var account = Accounts.GetAccount(username); + if (account is not { Banned: true, AccessLevel: >= AccessLevel.Owner }) + { + return; + } + + account.Banned = false; + account.AccessLevel = AccessLevel.Owner; + + logger.Warning("Protected account \"{0}\" has been reset.", username); + + if (ServerAccessConfiguration.NewPasswordOnReset) + { + var password = Guid.NewGuid().ToString(); + logger.Warning("Protected account \"{0}\" password reset to \"{1}\"", username, password); + account.SetPassword(password); + } + + e.Accepted = true; + } +} + +public record ServerAccessConfiguration +{ + [JsonPropertyName("newPasswordOnReset")] + public bool NewPasswordOnReset { get; init; } + + [JsonPropertyName("protectedAccounts")] + public HashSet ProtectedAccounts { get; init; } +}