From 91930de98c0c68e1ee68d425b341e14d45945e9b Mon Sep 17 00:00:00 2001 From: Zekins Date: Wed, 3 Jul 2024 15:04:02 +0300 Subject: [PATCH 1/4] VPN blocker --- .../Tabs/PanicBunkerTab/PanicBunkerTab.xaml | 15 ++- .../PanicBunkerTab/PanicBunkerTab.xaml.cs | 41 +++++-- .../Commands/PanicBunkerCommand.cs | 28 ++--- Content.Server/Administration/ServerApi.cs | 102 +---------------- .../Administration/Systems/AdminSystem.cs | 48 ++++++-- .../Connection/ConnectionManager.cs | 108 +++++++++++++----- .../Commands/PanicBunkerCommand.cs | 33 ++++++ .../PlayTimeTrackingManager.cs | 3 +- .../Events/PanicBunkerChangedEvent.cs | 7 +- Content.Shared/CCVar/CCVars.cs | 13 ++- Content.Shared/Corvax/CCCVars/CCCVars.cs | 17 +++ Resources/ConfigPresets/Corvax/mrp.toml | 9 ++ .../ui/tabs/panicbunker-tab.ftl | 4 +- .../administration/commands/panicbunker.ftl | 2 - Resources/Locale/ru-RU/generic.ftl | 1 + 15 files changed, 252 insertions(+), 179 deletions(-) create mode 100644 Content.Server/Corvax/Administration/Commands/PanicBunkerCommand.cs create mode 100644 Content.Shared/Corvax/CCCVars/CCCVars.cs diff --git a/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml b/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml index 89827d06424..7f8cbbfb111 100644 --- a/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml +++ b/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml @@ -1,4 +1,4 @@ - diff --git a/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml.cs b/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml.cs index f6212cd5ee1..c967ea48b13 100644 --- a/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml.cs +++ b/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml.cs @@ -1,4 +1,5 @@ -using Content.Shared.Administration.Events; +using Content.Corvax.Interfaces.Shared; +using Content.Shared.Administration.Events; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface; using Robust.Client.UserInterface.XAML; @@ -12,7 +13,7 @@ public sealed partial class PanicBunkerTab : Control [Dependency] private readonly IConsoleHost _console = default!; private string _minAccountAge; - private string _minOverallHours; + private string _minOverallMinutes; public PanicBunkerTab() { @@ -25,9 +26,17 @@ public PanicBunkerTab() MinAccountAge.OnFocusExit += args => SendMinAccountAge(args.Text); _minAccountAge = MinAccountAge.Text; - MinOverallHours.OnTextEntered += args => SendMinOverallHours(args.Text); - MinOverallHours.OnFocusExit += args => SendMinOverallHours(args.Text); - _minOverallHours = MinOverallHours.Text; + MinOverallMinutes.OnTextEntered += args => SendMinOverallMinutes(args.Text); + MinOverallMinutes.OnFocusExit += args => SendMinOverallMinutes(args.Text); + _minOverallMinutes = MinOverallMinutes.Text; + // Corvax-VPNGuard-Start + var haveSecrets = IoCManager.Instance!.TryResolveType(out _); // TODO: Probably need better way to detect Secrets module + if (haveSecrets) + { + VPNContainer.Visible = true; + DenyVPN.OnPressed += _ => SendDenyVpn(DenyVPN.Pressed); + } + // Corvax-VPNGuard-End } private void SendMinAccountAge(string text) @@ -42,17 +51,24 @@ private void SendMinAccountAge(string text) _console.ExecuteCommand($"panicbunker_min_account_age {minutes}"); } - private void SendMinOverallHours(string text) + private void SendMinOverallMinutes(string text) { if (string.IsNullOrWhiteSpace(text) || - text == _minOverallHours || - !int.TryParse(text, out var hours)) + text == _minOverallMinutes || + !int.TryParse(text, out var minutes)) { return; } - _console.ExecuteCommand($"panicbunker_min_overall_hours {hours}"); + _console.ExecuteCommand($"panicbunker_min_overall_minutes {minutes}"); + } + + // Corvax-VPNGuard-Start + private void SendDenyVpn(bool deny) + { + _console.ExecuteCommand($"panicbunker_deny_vpn {deny}"); } + // Corvax-VPNGuard-End public void UpdateStatus(PanicBunkerStatus status) { @@ -68,10 +84,11 @@ public void UpdateStatus(PanicBunkerStatus status) CountDeadminnedButton.Pressed = status.CountDeadminnedAdmins; ShowReasonButton.Pressed = status.ShowReason; - MinAccountAge.Text = status.MinAccountAgeHours.ToString(); + MinAccountAge.Text = status.MinAccountAgeMinutes.ToString(); _minAccountAge = MinAccountAge.Text; - MinOverallHours.Text = status.MinOverallHours.ToString(); - _minOverallHours = MinOverallHours.Text; + MinOverallMinutes.Text = status.MinOverallMinutes.ToString(); + _minOverallMinutes = MinOverallMinutes.Text; + DenyVPN.Pressed = status.DenyVpn; // Corvax-VPNGuard } } diff --git a/Content.Server/Administration/Commands/PanicBunkerCommand.cs b/Content.Server/Administration/Commands/PanicBunkerCommand.cs index de3f3cbaea2..ff3f2987f93 100644 --- a/Content.Server/Administration/Commands/PanicBunkerCommand.cs +++ b/Content.Server/Administration/Commands/PanicBunkerCommand.cs @@ -1,4 +1,4 @@ -using Content.Shared.Administration; +using Content.Shared.Administration; using Content.Shared.CCVar; using Robust.Shared.Configuration; using Robust.Shared.Console; @@ -139,54 +139,54 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) if (args.Length == 0) { var current = _cfg.GetCVar(CCVars.PanicBunkerMinAccountAge); - shell.WriteLine(Loc.GetString("panicbunker-command-min-account-age-is", ("hours", current / 60))); + shell.WriteLine(Loc.GetString("panicbunker-command-min-account-age-is", ("minutes", current))); } if (args.Length > 1) { - shell.WriteError(Loc.GetString("shell-need-between-arguments",("lower", 0), ("upper", 1))); + shell.WriteError(Loc.GetString("shell-need-between-arguments", ("lower", 0), ("upper", 1))); return; } - if (!int.TryParse(args[0], out var hours)) + if (!int.TryParse(args[0], out var minutes)) { shell.WriteError(Loc.GetString("shell-argument-must-be-number")); return; } - _cfg.SetCVar(CCVars.PanicBunkerMinAccountAge, hours * 60); - shell.WriteLine(Loc.GetString("panicbunker-command-min-account-age-set", ("hours", hours))); + _cfg.SetCVar(CCVars.PanicBunkerMinAccountAge, minutes); + shell.WriteLine(Loc.GetString("panicbunker-command-min-account-age-set", ("minutes", minutes))); } } [AdminCommand(AdminFlags.Server)] -public sealed class PanicBunkerMinOverallHoursCommand : LocalizedCommands +public sealed class PanicBunkerMinOverallMinutesCommand : LocalizedCommands { [Dependency] private readonly IConfigurationManager _cfg = default!; - public override string Command => "panicbunker_min_overall_hours"; + public override string Command => "panicbunker_min_overall_minutes"; public override void Execute(IConsoleShell shell, string argStr, string[] args) { if (args.Length == 0) { - var current = _cfg.GetCVar(CCVars.PanicBunkerMinOverallHours); - shell.WriteLine(Loc.GetString("panicbunker-command-min-overall-hours-is", ("minutes", current))); + var current = _cfg.GetCVar(CCVars.PanicBunkerMinOverallMinutes); + shell.WriteLine(Loc.GetString("panicbunker-command-min-overall-minutes-is", ("minutes", current))); } if (args.Length > 1) { - shell.WriteError(Loc.GetString("shell-need-between-arguments",("lower", 0), ("upper", 1))); + shell.WriteError(Loc.GetString("shell-need-between-arguments", ("lower", 0), ("upper", 1))); return; } - if (!int.TryParse(args[0], out var hours)) + if (!int.TryParse(args[0], out var minutes)) { shell.WriteError(Loc.GetString("shell-argument-must-be-number")); return; } - _cfg.SetCVar(CCVars.PanicBunkerMinOverallHours, hours); - shell.WriteLine(Loc.GetString("panicbunker-command-overall-hours-age-set", ("hours", hours))); + _cfg.SetCVar(CCVars.PanicBunkerMinOverallMinutes, minutes); + shell.WriteLine(Loc.GetString("panicbunker-command-overall-minutes-age-set", ("minutes", minutes))); } } diff --git a/Content.Server/Administration/ServerApi.cs b/Content.Server/Administration/ServerApi.cs index 2f7bcbe48e2..a4b60b88998 100644 --- a/Content.Server/Administration/ServerApi.cs +++ b/Content.Server/Administration/ServerApi.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Net; using System.Net.Http; using System.Security.Cryptography; @@ -8,9 +8,7 @@ using System.Threading.Tasks; using Content.Server.Administration.Systems; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Presets; -using Content.Server.GameTicking.Rules.Components; using Content.Server.Maps; using Content.Server.RoundEnd; using Content.Shared.Administration.Managers; @@ -41,7 +39,7 @@ public sealed partial class ServerApi : IPostInjectInit CCVars.PanicBunkerCountDeadminnedAdmins.Name, CCVars.PanicBunkerShowReason.Name, CCVars.PanicBunkerMinAccountAge.Name, - CCVars.PanicBunkerMinOverallHours.Name, + CCVars.PanicBunkerMinOverallMinutes.Name, CCVars.PanicBunkerCustomReason.Name, ]; @@ -67,8 +65,6 @@ void IPostInjectInit.PostInject() _sawmill = _logManager.GetSawmill("serverApi"); // Get - RegisterHandler(HttpMethod.Get, "/admin/info", InfoHandler); //frontier - not sure why this action needs an actor - RegisterHandler(HttpMethod.Get, "/admin/game_rules", GetGameRules); RegisterHandler(HttpMethod.Get, "/admin/presets", GetPresets); // Post @@ -76,8 +72,6 @@ void IPostInjectInit.PostInject() RegisterActorHandler(HttpMethod.Post, "/admin/actions/round/end", ActionRoundEnd); RegisterActorHandler(HttpMethod.Post, "/admin/actions/round/restartnow", ActionRoundRestartNow); RegisterActorHandler(HttpMethod.Post, "/admin/actions/kick", ActionKick); - RegisterActorHandler(HttpMethod.Post, "/admin/actions/add_game_rule", ActionAddGameRule); - RegisterActorHandler(HttpMethod.Post, "/admin/actions/end_game_rule", ActionEndGameRule); RegisterActorHandler(HttpMethod.Post, "/admin/actions/force_preset", ActionForcePreset); RegisterActorHandler(HttpMethod.Post, "/admin/actions/set_motd", ActionForceMotd); RegisterActorHandler(HttpMethod.Patch, "/admin/actions/panic_bunker", ActionPanicPunker); @@ -427,98 +421,6 @@ await context.RespondJsonAsync(new PresetResponse }); } - /// - /// Returns an array containing all game rules. - /// - private async Task GetGameRules(IStatusHandlerContext context) - { - var gameRules = new List(); - foreach (var gameRule in _prototypeManager.EnumeratePrototypes()) - { - if (gameRule.Abstract) - continue; - - if (gameRule.HasComponent(_componentFactory)) - gameRules.Add(gameRule.ID); - } - - await context.RespondJsonAsync(new GameruleResponse - { - GameRules = gameRules - }); - } - - - /// - /// Handles fetching information. - /// - private async Task InfoHandler(IStatusHandlerContext context) //frontier - we had an actor here and never used it so we drop it for now until im compelled to re-add it - { - /* - Information to display - Round number - Connected players - Active admins - Active game rules - Active game preset - Active map - MOTD - Panic bunker status - */ - - var info = await RunOnMainThread(() => - { - var ticker = _entitySystemManager.GetEntitySystem(); - var adminSystem = _entitySystemManager.GetEntitySystem(); - - var players = new List(); - - foreach (var player in _playerManager.Sessions) - { - var adminData = _adminManager.GetAdminData(player, true); - - players.Add(new InfoResponse.Player - { - UserId = player.UserId.UserId, - Name = player.Name, - IsAdmin = adminData != null, - IsDeadminned = !adminData?.Active ?? false - }); - } - - InfoResponse.MapInfo? mapInfo = null; - if (_gameMapManager.GetSelectedMap() is { } mapPrototype) - { - mapInfo = new InfoResponse.MapInfo - { - Id = mapPrototype.ID, - Name = mapPrototype.MapName - }; - } - - var gameRules = new List(); - foreach (var addedGameRule in ticker.GetActiveGameRules()) - { - var meta = _entityManager.MetaQuery.GetComponent(addedGameRule); - gameRules.Add(meta.EntityPrototype?.ID ?? meta.EntityPrototype?.Name ?? "Unknown"); - } - - var panicBunkerCVars = PanicBunkerCVars.ToDictionary(c => c, c => _config.GetCVar(c)); - return new InfoResponse - { - Players = players, - RoundId = ticker.RoundId, - Map = mapInfo, - PanicBunker = panicBunkerCVars, - GamePreset = ticker.CurrentPreset?.ID, - GameRules = gameRules, - MOTD = _config.GetCVar(CCVars.MOTD) - }; - }); - - await context.RespondJsonAsync(info); - } - #endregion private async Task CheckAccess(IStatusHandlerContext context) diff --git a/Content.Server/Administration/Systems/AdminSystem.cs b/Content.Server/Administration/Systems/AdminSystem.cs index 546b0b0decf..573645f0f06 100644 --- a/Content.Server/Administration/Systems/AdminSystem.cs +++ b/Content.Server/Administration/Systems/AdminSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Bank.Components; using Content.Shared.Bank.Events; using Content.Shared.CCVar; +using Content.Shared.Corvax.CCCVars; using Content.Shared.GameTicking; using Content.Shared.Hands.Components; using Content.Shared.IdentityManagement; @@ -72,14 +73,21 @@ public override void Initialize() _playerManager.PlayerStatusChanged += OnPlayerStatusChanged; _adminManager.OnPermsChanged += OnAdminPermsChanged; + _playTime.SessionPlayTimeUpdated += OnSessionPlayTimeUpdated; + // Panic Bunker Settings Subs.CVar(_config, CCVars.PanicBunkerEnabled, OnPanicBunkerChanged, true); Subs.CVar(_config, CCVars.PanicBunkerDisableWithAdmins, OnPanicBunkerDisableWithAdminsChanged, true); Subs.CVar(_config, CCVars.PanicBunkerEnableWithoutAdmins, OnPanicBunkerEnableWithoutAdminsChanged, true); Subs.CVar(_config, CCVars.PanicBunkerCountDeadminnedAdmins, OnPanicBunkerCountDeadminnedAdminsChanged, true); - Subs.CVar(_config, CCVars.PanicBunkerShowReason, OnShowReasonChanged, true); + Subs.CVar(_config, CCVars.PanicBunkerShowReason, OnPanicBunkerShowReasonChanged, true); Subs.CVar(_config, CCVars.PanicBunkerMinAccountAge, OnPanicBunkerMinAccountAgeChanged, true); - Subs.CVar(_config, CCVars.PanicBunkerMinOverallHours, OnPanicBunkerMinOverallHoursChanged, true); + Subs.CVar(_config, CCVars.PanicBunkerMinOverallMinutes, OnPanicBunkerMinOverallMinutesChanged, true); + Subs.CVar(_config, CCCVars.PanicBunkerDenyVPN, OnPanicBunkerDenyVpnChanged, true); // Corvax-VPNGuard + + /* + * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future. + */ SubscribeLocalEvent(OnIdentityChanged); SubscribeLocalEvent(OnPlayerAttached); @@ -87,8 +95,6 @@ public override void Initialize() SubscribeLocalEvent(OnRoleEvent); SubscribeLocalEvent(OnRoleEvent); SubscribeLocalEvent(OnRoundRestartCleanup); - SubscribeLocalEvent(OnBalanceChanged); - } private void OnRoundRestartCleanup(RoundRestartCleanupEvent ev) @@ -287,7 +293,7 @@ private void OnPanicBunkerCountDeadminnedAdminsChanged(bool enabled) UpdatePanicBunker(); } - private void OnShowReasonChanged(bool enabled) + private void OnPanicBunkerShowReasonChanged(bool enabled) { PanicBunker.ShowReason = enabled; SendPanicBunkerStatusAll(); @@ -295,15 +301,23 @@ private void OnShowReasonChanged(bool enabled) private void OnPanicBunkerMinAccountAgeChanged(int minutes) { - PanicBunker.MinAccountAgeHours = minutes / 60; + PanicBunker.MinAccountAgeMinutes = minutes; + SendPanicBunkerStatusAll(); + } + + private void OnPanicBunkerMinOverallMinutesChanged(int minutes) + { + PanicBunker.MinOverallMinutes = minutes; SendPanicBunkerStatusAll(); } - private void OnPanicBunkerMinOverallHoursChanged(int hours) + // Corvax-VPNGuard-Start + private void OnPanicBunkerDenyVpnChanged(bool deny) { - PanicBunker.MinOverallHours = hours; + PanicBunker.DenyVpn = deny; SendPanicBunkerStatusAll(); } + // Corvax-VPNGuard-End private void UpdatePanicBunker() { @@ -312,6 +326,19 @@ private void UpdatePanicBunker() : _adminManager.ActiveAdmins; var hasAdmins = admins.Any(); + // TODO Fix order dependent Cvars + // Please for the sake of my sanity don't make cvars & order dependent. + // Just make a bool field on the system instead of having some cvars automatically modify other cvars. + // + // I.e., this: + // /sudo cvar game.panic_bunker.enabled true + // /sudo cvar game.panic_bunker.disable_with_admins true + // and this: + // /sudo cvar game.panic_bunker.disable_with_admins true + // /sudo cvar game.panic_bunker.enabled true + // + // should have the same effect, but currently setting the disable_with_admins can modify enabled. + if (hasAdmins && PanicBunker.DisableWithAdmins) { _config.SetCVar(CCVars.PanicBunkerEnabled, false); @@ -403,5 +430,10 @@ keyStorage.Key is { } key && _gameTicker.SpawnObserver(player); } + + private void OnSessionPlayTimeUpdated(ICommonSession session) + { + UpdatePlayerList(session); + } } } diff --git a/Content.Server/Connection/ConnectionManager.cs b/Content.Server/Connection/ConnectionManager.cs index 01fc192a231..021db93c7f1 100644 --- a/Content.Server/Connection/ConnectionManager.cs +++ b/Content.Server/Connection/ConnectionManager.cs @@ -1,21 +1,28 @@ using System.Collections.Immutable; +using System.Linq; using System.Runtime.InteropServices; -using System.Text.Json.Nodes; using System.Threading.Tasks; -using Content.Server._NF.Auth; -using Content.Server.Administration; using Content.Corvax.Interfaces.Server; +using Content.Corvax.Interfaces.Shared; +using Content.Server.Chat.Managers; using Content.Server.Database; using Content.Server.GameTicking; using Content.Server.Preferences.Managers; using Content.Shared.CCVar; +using Content.Shared.Corvax.CCCVars; using Content.Shared.GameTicking; using Content.Shared.Players.PlayTimeTracking; using Robust.Server.Player; using Robust.Shared.Configuration; +using Robust.Shared.Enums; using Robust.Shared.Network; +using Robust.Shared.Player; using Robust.Shared.Timing; +/* + * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future. + */ + namespace Content.Server.Connection { public interface IConnectionManager @@ -50,11 +57,9 @@ public sealed class ConnectionManager : IConnectionManager [Dependency] private readonly ServerDbEntryManager _serverDbEntry = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly ILogManager _logManager = default!; - - private IServerSponsorsManager? _sponsorsMgr; // Corvax-Sponsors - - //frontier - [Dependency] private readonly MiniAuthManager _authManager = default!; + [Dependency] private readonly IChatManager _chatManager = default!; + private ISharedSponsorsManager? _sponsorsMgr; // Corvax-Sponsors + private IServerVPNGuardManager? _vpnGuardMgr; // Corvax-VPNGuard private readonly Dictionary _temporaryBypasses = []; private ISawmill _sawmill = default!; @@ -66,6 +71,7 @@ public void Initialize() IoCManager.Instance!.TryResolveType(out _sponsorsMgr); // Corvax-Sponsors _netMgr.Connecting += NetMgrOnConnecting; _netMgr.AssignUserIdCallback = AssignUserIdCallback; + _plyMgr.PlayerStatusChanged += PlayerStatusChanged; // Approval-based IP bans disabled because they don't play well with Happy Eyeballs. // _netMgr.HandleApprovalCallback = HandleApproval; } @@ -134,6 +140,46 @@ private async Task NetMgrOnConnecting(NetConnectingArgs e) } } + private async void PlayerStatusChanged(object? sender, SessionStatusEventArgs args) + { + if (args.NewStatus == SessionStatus.Connected) + { + AdminAlertIfSharedConnection(args.Session); + } + } + + private void AdminAlertIfSharedConnection(ICommonSession newSession) + { + var playerThreshold = _cfg.GetCVar(CCVars.AdminAlertMinPlayersSharingConnection); + if (playerThreshold < 0) + return; + + var addr = newSession.Channel.RemoteEndPoint.Address; + + var otherConnectionsFromAddress = _plyMgr.Sessions.Where(session => + session.Status is SessionStatus.Connected or SessionStatus.InGame + && session.Channel.RemoteEndPoint.Address.Equals(addr) + && session.UserId != newSession.UserId) + .ToList(); + + var otherConnectionCount = otherConnectionsFromAddress.Count; + if (otherConnectionCount + 1 < playerThreshold) // Add one for the total, not just others, using the address + return; + + var username = newSession.Name; + var otherUsernames = string.Join(", ", + otherConnectionsFromAddress.Select(session => session.Name)); + + _chatManager.SendAdminAlert(Loc.GetString("admin-alert-shared-connection", + ("player", username), + ("otherCount", otherConnectionCount), + ("otherList", otherUsernames))); + } + + /* + * TODO: Jesus H Christ what is this utter mess of a function + * TODO: Break this apart into is constituent steps. + */ private async Task<(ConnectionDenyReason, string, List? bansHit)?> ShouldDeny( NetConnectingArgs e) { @@ -191,9 +237,9 @@ private async Task NetMgrOnConnecting(NetConnectingArgs e) ("reason", Loc.GetString("panic-bunker-account-reason-account", ("minutes", minMinutesAge)))), null); } - var minOverallHours = _cfg.GetCVar(CCVars.PanicBunkerMinOverallHours); + var minOverallMinutes = _cfg.GetCVar(CCVars.PanicBunkerMinOverallMinutes); var overallTime = (await _db.GetPlayTimes(e.UserId)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall); - var haveMinOverallTime = overallTime != null && overallTime.TimeSpent.TotalHours > minOverallHours; + var haveMinOverallTime = overallTime != null && overallTime.TimeSpent.TotalMinutes > minOverallMinutes; // Use the custom reason if it exists & they don't have the minimum time if (customReason != string.Empty && !haveMinOverallTime && !bypassAllowed) @@ -205,10 +251,27 @@ private async Task NetMgrOnConnecting(NetConnectingArgs e) { return (ConnectionDenyReason.Panic, Loc.GetString("panic-bunker-account-denied-reason", - ("reason", Loc.GetString("panic-bunker-account-reason-overall", ("hours", minOverallHours)))), null); + ("reason", Loc.GetString("panic-bunker-account-reason-overall", ("minutes", minOverallMinutes)))), null); + } + + // Corvax-VPNGuard-Start + if (_vpnGuardMgr == null) // "lazyload" because of problems with dependency resolve order + IoCManager.Instance!.TryResolveType(out _vpnGuardMgr); + + var denyVpn = false; + if (_cfg.GetCVar(CCCVars.PanicBunkerDenyVPN) && _vpnGuardMgr != null) + { + denyVpn = await _vpnGuardMgr.IsConnectionVpn(e.IP.Address); + if (denyVpn) + { + return (ConnectionDenyReason.Panic, + Loc.GetString("panic-bunker-account-denied-reason", + ("reason", Loc.GetString("panic-bunker-account-reason-vpn"))), null); + } } + // Corvax-VPNGuard-End - if (!validAccountAge || !haveMinOverallTime && !bypassAllowed) + if ((!validAccountAge || !haveMinOverallTime || denyVpn) && !bypassAllowed) // Corvax-VPNGuard { return (ConnectionDenyReason.Panic, Loc.GetString("panic-bunker-account-denied"), null); } @@ -220,7 +283,7 @@ private async Task NetMgrOnConnecting(NetConnectingArgs e) var adminBypass = _cfg.GetCVar(CCVars.AdminBypassMaxPlayers) && adminData != null; // Corvax-Queue-Start var isQueueEnabled = IoCManager.Instance!.TryResolveType(out var mgr) && mgr.IsEnabled; - if (_plyMgr.PlayerCount >= _cfg.GetCVar(CCVars.SoftMaxPlayers) && !isPrivileged && !isQueueEnabled) + if ((_plyMgr.PlayerCount >= _cfg.GetCVar(CCVars.SoftMaxPlayers) && !adminBypass) && !wasInGame && !isQueueEnabled) // Corvax-Queue-End { return (ConnectionDenyReason.Full, Loc.GetString("soft-player-cap-full"), null); @@ -243,21 +306,6 @@ private async Task NetMgrOnConnecting(NetConnectingArgs e) } } - //Frontier - //This is our little chunk that serves as a dAuth. It takes in a comma separated list of IP:PORT, and checks - //the requesting player against the list of players logged in to other servers. It is intended to be failsafe. - //In the case of Admins, it shares the same bypass setting as the soft_max_player_limit - if (!_cfg.GetCVar(CCVars.AllowMultiConnect) && !adminBypass) - { - var serverListString = _cfg.GetCVar(CCVars.ServerAuthList); - var serverList = serverListString.Split(","); - foreach (var server in serverList) - { - if (await _authManager.IsPlayerConnected(server, userId)) - return (ConnectionDenyReason.Connected, Loc.GetString("multiauth-already-connected"), null); - } - } - // end Frontier return null; } @@ -287,8 +335,8 @@ private bool HasTemporaryBypass(NetUserId user) // Corvax-Queue-Start: Make these conditions in one place, for checks in the connection and in the queue public async Task HavePrivilegedJoin(NetUserId userId) { - var adminBypass = await _dbManager.GetAdminDataForAsync(userId) != null; - var havePriorityJoin = _sponsorsMgr != null && _sponsorsMgr.HavePriorityJoin(userId); // Corvax-Sponsors + var adminBypass = _cfg.GetCVar(CCVars.AdminBypassMaxPlayers) && await _dbManager.GetAdminDataForAsync(userId) != null; + var havePriorityJoin = _sponsorsMgr != null && _sponsorsMgr.HaveServerPriorityJoin(userId); // Corvax-Sponsors var wasInGame = EntitySystem.TryGet(out var ticker) && ticker.PlayerGameStatuses.TryGetValue(userId, out var status) && status == PlayerGameStatus.JoinedGame; diff --git a/Content.Server/Corvax/Administration/Commands/PanicBunkerCommand.cs b/Content.Server/Corvax/Administration/Commands/PanicBunkerCommand.cs new file mode 100644 index 00000000000..1db98128a19 --- /dev/null +++ b/Content.Server/Corvax/Administration/Commands/PanicBunkerCommand.cs @@ -0,0 +1,33 @@ +using Content.Server.Administration; +using Content.Shared.Administration; +using Content.Shared.Corvax.CCCVars; +using Robust.Shared.Configuration; +using Robust.Shared.Console; + +namespace Content.Server.Corvax.Administration.Commands; + +[AdminCommand(AdminFlags.Server)] +public sealed class PanicBunkerDenyVpnCommand : LocalizedCommands +{ + [Dependency] private readonly IConfigurationManager _cfg = default!; + + public override string Command => "panicbunker_deny_vpn"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 1) + { + shell.WriteError(Loc.GetString("shell-need-exactly-one-argument")); + return; + } + + if (!bool.TryParse(args[0], out var deny)) + { + shell.WriteError(Loc.GetString("shell-argument-must-be-boolean")); + return; + } + + _cfg.SetCVar(CCCVars.PanicBunkerDenyVPN, deny); + shell.WriteLine(Loc.GetString(deny ? "panicbunker-command-deny-vpn-enabled" : "panicbunker-command-deny-vpn-disabled")); + } +} diff --git a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs index 638ef125e49..c68fc221415 100644 --- a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs +++ b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs @@ -81,6 +81,8 @@ public sealed partial class PlayTimeTrackingManager : ISharedPlaytimeManager public event CalcPlayTimeTrackersCallback? CalcTrackers; + public event Action? SessionPlayTimeUpdated; + public void Initialize() { _sawmill = Logger.GetSawmill("play_time"); @@ -183,7 +185,6 @@ public void FlushAllTrackers() FlushSingleTracker(data, time); } } - /// /// Flush time tracker information for a player, /// so APIs like return up-to-date info. diff --git a/Content.Shared/Administration/Events/PanicBunkerChangedEvent.cs b/Content.Shared/Administration/Events/PanicBunkerChangedEvent.cs index f809b67bc8d..b360d808a3d 100644 --- a/Content.Shared/Administration/Events/PanicBunkerChangedEvent.cs +++ b/Content.Shared/Administration/Events/PanicBunkerChangedEvent.cs @@ -1,4 +1,4 @@ -using Robust.Shared.Serialization; +using Robust.Shared.Serialization; namespace Content.Shared.Administration.Events; @@ -10,8 +10,9 @@ public sealed class PanicBunkerStatus public bool EnableWithoutAdmins; public bool CountDeadminnedAdmins; public bool ShowReason; - public int MinAccountAgeHours; - public int MinOverallHours; + public int MinAccountAgeMinutes; + public int MinOverallMinutes; + public bool DenyVpn; // Corvax-VPNGuard } [Serializable, NetSerializable] diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 93a8d83c168..fa0455488eb 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -290,8 +290,8 @@ public static readonly CVarDef /// /// Minimal overall played time. /// - public static readonly CVarDef PanicBunkerMinOverallHours = - CVarDef.Create("game.panic_bunker.min_overall_hours", 10, CVar.SERVERONLY); + public static readonly CVarDef PanicBunkerMinOverallMinutes = + CVarDef.Create("game.panic_bunker.min_overall_minutes", 600, CVar.SERVERONLY); /// /// A custom message that will be used for connections denied to the panic bunker @@ -780,6 +780,15 @@ public static readonly CVarDef public static readonly CVarDef ServerBanErasePlayer = CVarDef.Create("admin.server_ban_erase_player", false, CVar.ARCHIVE | CVar.SERVER | CVar.REPLICATED); + /// + /// Minimum players sharing a connection required to create an alert. -1 to disable the alert. + /// + /// + /// If you set this to 0 or 1 then it will alert on every connection, so probably don't do that. + /// + public static readonly CVarDef AdminAlertMinPlayersSharingConnection = + CVarDef.Create("admin.alert.min_players_sharing_connection", -1, CVar.SERVERONLY); + /// /// Minimum explosion intensity to create an admin alert message. -1 to disable the alert. /// diff --git a/Content.Shared/Corvax/CCCVars/CCCVars.cs b/Content.Shared/Corvax/CCCVars/CCCVars.cs new file mode 100644 index 00000000000..898e59d35a9 --- /dev/null +++ b/Content.Shared/Corvax/CCCVars/CCCVars.cs @@ -0,0 +1,17 @@ +using Robust.Shared.Configuration; + +namespace Content.Shared.Corvax.CCCVars; + +/// +/// Corvax modules console variables +/// +[CVarDefs] +// ReSharper disable once InconsistentNaming +public sealed class CCCVars +{ + /// + /// Deny any VPN connections. + /// + public static readonly CVarDef PanicBunkerDenyVPN = + CVarDef.Create("game.panic_bunker.deny_vpn", false, CVar.SERVERONLY); +} diff --git a/Resources/ConfigPresets/Corvax/mrp.toml b/Resources/ConfigPresets/Corvax/mrp.toml index e4b7507656e..fc04ffc3db4 100644 --- a/Resources/ConfigPresets/Corvax/mrp.toml +++ b/Resources/ConfigPresets/Corvax/mrp.toml @@ -19,3 +19,12 @@ timerrestart = 30 map_enabled = false preset_enabled = false restart_enabled = false + +[game.panic_bunker] +enabled = true +show_reason = true +min_account_age = 0 +min_overall_minutes = 0 +deny_vpn = true +enable_without_admins = true +disable_with_admins = true diff --git a/Resources/Locale/ru-RU/administration/ui/tabs/panicbunker-tab.ftl b/Resources/Locale/ru-RU/administration/ui/tabs/panicbunker-tab.ftl index 3a90e14e4a7..56f7255995f 100644 --- a/Resources/Locale/ru-RU/administration/ui/tabs/panicbunker-tab.ftl +++ b/Resources/Locale/ru-RU/administration/ui/tabs/panicbunker-tab.ftl @@ -11,7 +11,7 @@ admin-ui-panic-bunker-count-deadminned-admins-tooltip = Count deadminned admins admin-ui-panic-bunker-show-reason = Show Reason admin-ui-panic-bunker-show-reason-tooltip = Show the user why they were blocked from connecting by the panic bunker. admin-ui-panic-bunker-min-account-age = Min. Account Age -admin-ui-panic-bunker-min-overall-hours = Min. Overall Playtime +admin-ui-panic-bunker-min-overall-minutes = Min. Overall Playtime admin-ui-panic-bunker-is-enabled = The panic bunker is currently enabled. admin-ui-panic-bunker-enabled-admin-alert = The panic bunker has been enabled. -admin-ui-panic-bunker-disabled-admin-alert = The panic bunker has been disabled. +admin-ui-panic-bunker-disabled-admin-alert = The panic bunker has been disabled. \ No newline at end of file diff --git a/Resources/Locale/ru-RU/corvax/administration/commands/panicbunker.ftl b/Resources/Locale/ru-RU/corvax/administration/commands/panicbunker.ftl index bd25c9d5512..27f9c8da369 100644 --- a/Resources/Locale/ru-RU/corvax/administration/commands/panicbunker.ftl +++ b/Resources/Locale/ru-RU/corvax/administration/commands/panicbunker.ftl @@ -1,4 +1,2 @@ -cmd-panicbunker_deny_vpn-desc = Включает или отключает запрет доступа через VPN-соединения. -cmd-panicbunker_deny_vpn-help = Использование: panicbunker_min_overall_hours panicbunker-command-deny-vpn-enabled = Бункер теперь будет блокировать подключения через VPN. panicbunker-command-deny-vpn-disabled = Бункер больше не будет блокировать подключения через VPN. diff --git a/Resources/Locale/ru-RU/generic.ftl b/Resources/Locale/ru-RU/generic.ftl index b74a568d6a7..3e9a08c08ce 100644 --- a/Resources/Locale/ru-RU/generic.ftl +++ b/Resources/Locale/ru-RU/generic.ftl @@ -8,5 +8,6 @@ generic-unknown-title = Неизвестно generic-error = ошибка generic-invalid = недействительно generic-hours = часов +generic-minutes = минут generic-playtime-title = Игровое время generic-confirm = Подтвердить From ef6cf6a6530a90e4a242daf8d6b7a7cd8b1e7f7e Mon Sep 17 00:00:00 2001 From: Zekins Date: Wed, 3 Jul 2024 15:19:20 +0300 Subject: [PATCH 2/4] Frontier fix --- Content.Server/Administration/ServerApi.cs | 92 +++++++++++++++++++ .../Connection/ConnectionManager.cs | 22 ++++- 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/Content.Server/Administration/ServerApi.cs b/Content.Server/Administration/ServerApi.cs index a4b60b88998..51555be3e5d 100644 --- a/Content.Server/Administration/ServerApi.cs +++ b/Content.Server/Administration/ServerApi.cs @@ -8,7 +8,9 @@ using System.Threading.Tasks; using Content.Server.Administration.Systems; using Content.Server.GameTicking; +using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Presets; +using Content.Server.GameTicking.Rules.Components; using Content.Server.Maps; using Content.Server.RoundEnd; using Content.Shared.Administration.Managers; @@ -65,6 +67,8 @@ void IPostInjectInit.PostInject() _sawmill = _logManager.GetSawmill("serverApi"); // Get + RegisterHandler(HttpMethod.Get, "/admin/info", InfoHandler); //frontier - not sure why this action needs an actor + RegisterHandler(HttpMethod.Get, "/admin/game_rules", GetGameRules); RegisterHandler(HttpMethod.Get, "/admin/presets", GetPresets); // Post @@ -72,6 +76,8 @@ void IPostInjectInit.PostInject() RegisterActorHandler(HttpMethod.Post, "/admin/actions/round/end", ActionRoundEnd); RegisterActorHandler(HttpMethod.Post, "/admin/actions/round/restartnow", ActionRoundRestartNow); RegisterActorHandler(HttpMethod.Post, "/admin/actions/kick", ActionKick); + RegisterActorHandler(HttpMethod.Post, "/admin/actions/add_game_rule", ActionAddGameRule); + RegisterActorHandler(HttpMethod.Post, "/admin/actions/end_game_rule", ActionEndGameRule); RegisterActorHandler(HttpMethod.Post, "/admin/actions/force_preset", ActionForcePreset); RegisterActorHandler(HttpMethod.Post, "/admin/actions/set_motd", ActionForceMotd); RegisterActorHandler(HttpMethod.Patch, "/admin/actions/panic_bunker", ActionPanicPunker); @@ -421,6 +427,92 @@ await context.RespondJsonAsync(new PresetResponse }); } + /// + /// Returns an array containing all game rules. + /// + private async Task GetGameRules(IStatusHandlerContext context) + { + var gameRules = new List(); + foreach (var gameRule in _prototypeManager.EnumeratePrototypes()) + { + if (gameRule.Abstract) + continue; + + if (gameRule.HasComponent(_componentFactory)) + gameRules.Add(gameRule.ID); + } + + await context.RespondJsonAsync(new GameruleResponse + { + GameRules = gameRules + }); + } + + + /// + /// Handles fetching information. + /// + private async Task InfoHandler(IStatusHandlerContext context) //frontier - we had an actor here and never used it so we drop it for now until im compelled to re-add it + { + /* + Information to display + Round number + Connected players + Active admins + Active game rules + Active game preset + Active map + MOTD + Panic bunker status + */ + + var info = await RunOnMainThread(() => + { + var ticker = _entitySystemManager.GetEntitySystem(); + var adminSystem = _entitySystemManager.GetEntitySystem(); + var players = new List(); + foreach (var player in _playerManager.Sessions) + { + var adminData = _adminManager.GetAdminData(player, true); + players.Add(new InfoResponse.Player + { + UserId = player.UserId.UserId, + Name = player.Name, + IsAdmin = adminData != null, + IsDeadminned = !adminData?.Active ?? false + }); + } + InfoResponse.MapInfo? mapInfo = null; + if (_gameMapManager.GetSelectedMap() is { } mapPrototype) + { + mapInfo = new InfoResponse.MapInfo + { + Id = mapPrototype.ID, + Name = mapPrototype.MapName + }; + } + var gameRules = new List(); + foreach (var addedGameRule in ticker.GetActiveGameRules()) + { + var meta = _entityManager.MetaQuery.GetComponent(addedGameRule); + gameRules.Add(meta.EntityPrototype?.ID ?? meta.EntityPrototype?.Name ?? "Unknown"); + } + var panicBunkerCVars = PanicBunkerCVars.ToDictionary(c => c, c => _config.GetCVar(c)); + return new InfoResponse + { + Players = players, + RoundId = ticker.RoundId, + Map = mapInfo, + PanicBunker = panicBunkerCVars, + GamePreset = ticker.CurrentPreset?.ID, + GameRules = gameRules, + MOTD = _config.GetCVar(CCVars.MOTD) + }; + }); + + await context.RespondJsonAsync(info); + } + #endregion private async Task CheckAccess(IStatusHandlerContext context) diff --git a/Content.Server/Connection/ConnectionManager.cs b/Content.Server/Connection/ConnectionManager.cs index 021db93c7f1..d721c69740e 100644 --- a/Content.Server/Connection/ConnectionManager.cs +++ b/Content.Server/Connection/ConnectionManager.cs @@ -1,6 +1,7 @@ using System.Collections.Immutable; using System.Linq; using System.Runtime.InteropServices; +using System.Text.Json.Nodes; using System.Threading.Tasks; using Content.Corvax.Interfaces.Server; using Content.Corvax.Interfaces.Shared; @@ -8,6 +9,8 @@ using Content.Server.Database; using Content.Server.GameTicking; using Content.Server.Preferences.Managers; +using Content.Server._NF.Auth; +using Content.Server.Administration; using Content.Shared.CCVar; using Content.Shared.Corvax.CCCVars; using Content.Shared.GameTicking; @@ -58,6 +61,9 @@ public sealed class ConnectionManager : IConnectionManager [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IChatManager _chatManager = default!; + //frontier + [Dependency] private readonly MiniAuthManager _authManager = default!; + private ISharedSponsorsManager? _sponsorsMgr; // Corvax-Sponsors private IServerVPNGuardManager? _vpnGuardMgr; // Corvax-VPNGuard @@ -305,7 +311,21 @@ session.Status is SessionStatus.Connected or SessionStatus.InGame return (ConnectionDenyReason.Whitelist, msg, null); } } - + //Frontier + //This is our little chunk that serves as a dAuth. It takes in a comma separated list of IP:PORT, and checks + //the requesting player against the list of players logged in to other servers. It is intended to be failsafe. + //In the case of Admins, it shares the same bypass setting as the soft_max_player_limit + if (!_cfg.GetCVar(CCVars.AllowMultiConnect) && !adminBypass) + { + var serverListString = _cfg.GetCVar(CCVars.ServerAuthList); + var serverList = serverListString.Split(","); + foreach (var server in serverList) + { + if (await _authManager.IsPlayerConnected(server, userId)) + return (ConnectionDenyReason.Connected, Loc.GetString("multiauth-already-connected"), null); + } + } + // end Frontier return null; } From 97a0f8e50476d836a83bf4ce0c45245ff1f9d63b Mon Sep 17 00:00:00 2001 From: Zekins <136648667+Zekins3366@users.noreply.github.com> Date: Sun, 14 Jul 2024 12:23:22 +0300 Subject: [PATCH 3/4] =?UTF-8?q?=D0=92=D0=B8=D0=B7=D0=B8=D0=B1=D0=B8=D0=BB?= =?UTF-8?q?=D0=B8=D1=82=D0=B8=20=D0=BE=D0=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml b/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml index 7f8cbbfb111..523e55af5f5 100644 --- a/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml +++ b/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml @@ -39,7 +39,7 @@