using System.Net; using System.Net.Sockets; using System.Threading.Tasks; using Content.Shared.Database; using Content.Shared.Roles; using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Prototypes; namespace Content.Server.Administration.Managers; public interface IBanManager { public void Initialize(); public void Restart(); /// /// Create a server ban in the database, blocking connection for matching players. /// void CreateServerBan(CreateServerBanInfo banInfo); /// /// Bans the specified target, address range and / or HWID. One of them must be non-null /// /// Target user, username or GUID, null for none /// The person who banned our target /// Address range, null for none /// H /// Number of minutes to ban for. 0 and null mean permanent /// Severity of the resulting ban note /// Reason for the ban [Obsolete("Use CreateServerBan(CreateBanInfo) instead")] public void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableTypedHwid? hwid, uint? minutes, NoteSeverity severity, string reason) { var info = new CreateServerBanInfo(reason); if (target != null) { ArgumentNullException.ThrowIfNull(targetUsername); info.AddUser(target.Value, targetUsername); } if (addressRange != null) info.AddAddressRange(addressRange.Value); if (hwid != null) info.AddHWId(hwid); if (minutes > 0) info.WithMinutes(minutes.Value); if (banningAdmin != null) info.WithBanningAdmin(banningAdmin.Value); info.WithSeverity(severity); CreateServerBan(info); } /// /// Gets a list of prefixed prototype IDs with the player's role bans. /// public HashSet? GetRoleBans(NetUserId playerUserId); /// /// Checks if the player is currently banned from any of the listed roles. /// /// The player. /// A list of valid antag prototype IDs. /// Returns True if an active role ban is found for this player for any of the listed roles. public bool IsRoleBanned(ICommonSession player, List> antags); /// /// Checks if the player is currently banned from any of the listed roles. /// /// The player. /// A list of valid job prototype IDs. /// Returns True if an active role ban is found for this player for any of the listed roles. public bool IsRoleBanned(ICommonSession player, List> jobs); /// /// Gets a list of prototype IDs with the player's job bans. /// public HashSet>? GetJobBans(NetUserId playerUserId); /// /// Gets a list of prototype IDs with the player's antag bans. /// public HashSet>? GetAntagBans(NetUserId playerUserId); /// /// Creates a role ban, preventing matching players from playing said roles. /// public void CreateRoleBan(CreateRoleBanInfo banInfo); /// /// Pardons a role ban by its ID. /// /// The id of the role ban to pardon. /// The admin, if any, that pardoned the role ban. /// The time at which this role ban was pardoned. public Task PardonRoleBan(int banId, NetUserId? unbanningAdmin, DateTimeOffset unbanTime); /// /// Sends role bans to the target /// /// Player's session public void SendRoleBans(ICommonSession pSession); } /// /// Base info to fill out in created ban records. /// /// /// [Access(typeof(BanManager), Other = AccessPermissions.Execute)] public abstract class CreateBanInfo { [Access(Other = AccessPermissions.Read)] public const int DefaultMaskIpv4 = 32; [Access(Other = AccessPermissions.Read)] public const int DefaultMaskIpv6 = 64; internal readonly HashSet<(NetUserId UserId, string UserName)> Users = []; internal readonly HashSet<(IPAddress Address, int Mask)> AddressRanges = []; internal readonly HashSet HWIds = []; internal readonly HashSet RoundIds = []; internal TimeSpan? Duration; internal NoteSeverity? Severity; internal string Reason; internal NetUserId? BanningAdmin; protected CreateBanInfo(string reason) { Reason = reason; } /// /// Add a user to be matched by the ban. /// /// /// Bans can target multiple users at once. /// /// The ID of the user. /// The name of the user (used for logging purposes). /// The current object, for easy chaining. public CreateBanInfo AddUser(NetUserId userId, string username) { Users.Add((userId, username)); return this; } /// /// Add an IP address to be matched by the ban. /// /// /// Bans can target multiple addresses at once. /// /// /// The IP address to add. If null, nothing is done. /// /// The current object, for easy chaining. public CreateBanInfo AddAddress(IPAddress? address) { if (address == null) return this; return AddAddressRange( address, address.AddressFamily == AddressFamily.InterNetwork ? DefaultMaskIpv4 : DefaultMaskIpv6); } /// /// Add an IP address range to be matched by the ban. /// /// /// Bans can target multiple address ranges at once. /// /// The current object, for easy chaining. public CreateBanInfo AddAddressRange((IPAddress Address, int Mask) addressRange) { return AddAddressRange(addressRange.Address, addressRange.Mask); } /// /// Add an IP address range to be matched by the ban. /// /// /// Bans can target multiple address ranges at once. /// /// The current object, for easy chaining. public CreateBanInfo AddAddressRange(IPAddress address, int mask) { AddressRanges.Add((address, mask)); return this; } /// /// Add a hardware IP (HWID) to be matched by the ban. /// /// /// Bans can target multiple HWIDs at once. /// /// /// The HWID to add. If null, nothing is done. /// /// The current object, for easy chaining. public CreateBanInfo AddHWId(ImmutableTypedHwid? hwId) { if (hwId != null) HWIds.Add(hwId); return this; } /// /// Add a relevant round ID to this ban. /// /// /// /// If not specified, the current round ID is used for the ban. /// Therefore, the first call to this function will replace the round ID, /// and further calls will add additional round IDs. /// /// /// Bans can target multiple round IDs at once. /// /// /// The current object, for easy chaining. public CreateBanInfo AddRoundId(int roundId) { RoundIds.Add(roundId); return this; } /// /// Set how long the ban will last, in minutes. /// /// /// If no duration is specified, the ban is permanent. /// /// The duration of the ban, in minutes. /// The current object, for easy chaining. /// /// Thrown if is not a positive number. /// public CreateBanInfo WithMinutes(int minutes) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(minutes); return WithMinutes((uint)minutes); } /// /// Set how long the ban will last, in minutes. /// /// /// If no duration is specified, the ban is permanent. /// /// The duration of the ban, in minutes. /// The current object, for easy chaining. /// /// Thrown if is not a positive number. /// public CreateBanInfo WithMinutes(uint minutes) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(minutes); return WithDuration(TimeSpan.FromMinutes(minutes)); } /// /// Set how long the ban will last. /// /// /// If no duration is specified, the ban is permanent. /// /// The duration of the ban. /// The current object, for easy chaining. /// /// Thrown if is not a positive amount of time. /// public CreateBanInfo WithDuration(TimeSpan duration) { if (duration <= TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(duration), "Duration must be greater than zero."); Duration = duration; return this; } /// /// Set the severity of the ban. /// /// /// If no severity is specified, the default is specified through server configuration. /// /// /// The current object, for easy chaining. public CreateBanInfo WithSeverity(NoteSeverity severity) { Severity = severity; return this; } /// /// Set the reason for the ban. /// /// /// This replaces the value given via the object constructor. /// /// The current object, for easy chaining. public CreateBanInfo WithReason(string reason) { Reason = reason; return this; } /// /// Specify the admin responsible for placing the ban. /// /// The current object, for easy chaining. public CreateBanInfo WithBanningAdmin(NetUserId? banningAdmin) { BanningAdmin = banningAdmin; return this; } } /// /// Stores info to create server ban records. /// /// [Access(typeof(BanManager), Other = AccessPermissions.Execute)] public sealed class CreateServerBanInfo : CreateBanInfo { /// The reason for the server ban. public CreateServerBanInfo(string reason) : base(reason) { } } /// /// Stores info to create role ban records. /// /// [Access(typeof(BanManager), Other = AccessPermissions.Execute)] public sealed class CreateRoleBanInfo : CreateBanInfo { internal readonly HashSet> AntagPrototypes = []; internal readonly HashSet> JobPrototypes = []; /// The reason for the role ban. public CreateRoleBanInfo(string reason) : base(reason) { } /// /// Add an antag role that will be unavailable for banned players. /// /// /// /// Bans can have multiple roles at once. /// /// /// While not checked in this function, adding a ban with invalid role IDs will cause a /// when actually creating the ban. /// /// /// The current object, for easy chaining. public CreateRoleBanInfo AddAntag(ProtoId protoId) { AntagPrototypes.Add(protoId); return this; } /// /// Add a job role that will be unavailable for banned players. /// /// /// /// Bans can have multiple roles at once. /// /// /// While not checked in this function, adding a ban with invalid role IDs will cause a /// when actually creating the ban. /// /// /// The current object, for easy chaining. public CreateRoleBanInfo AddJob(ProtoId protoId) { JobPrototypes.Add(protoId); return this; } }