Files
space-station-14/Content.Server/Database/BanDef.cs
Pieter-Jan Briers 29b7fc4463 Stable to master (#42599)
Ban database refactor (#42495)

* Ban DB refactor seems to work at a basic level for PostgreSQL

* New ban creation API

Supports all the new functionality (multiple players/addresses/hwids/roles/rounds per ban).

* Make the migration irreversible

* Re-implement ban notifications

The server ID check is no longer done as admins may want to place bans spanning multiple rounds irrelevant of the source server.

* Fix some split query warnings

* Implement migration on SQLite

* More comments

* Remove required from ban reason

SS14.Admin changes would like this

* More missing AsSplitQuery() calls

* Fix missing ban type filter

* Fix old CreateServerBan API with permanent time

* Fix department and role ban commands with permanent time

* Re-add banhits navigation property

Dropped this on accident, SS14.Admin needs it.

* More ban API fixes.

* Don't fetch ban exemption info for role bans

Not relevant, reduces query performance

* Regenerate migrations

* Fix adminnotes command for players that never connected

Would blow up handling null player records. Not a new bug introduced by the refactor, but I ran into it.

* Great shame... I accidentally committed submodule update...

* Update GDPR scripts

* Fix sandbox violation

* Fix bans with duplicate info causing DB exceptions

Most notably happened with role bans, as multiple departments may include the same role.
2026-01-23 15:34:23 +01:00

129 lines
4.7 KiB
C#

using System.Collections.Immutable;
using System.Linq;
using System.Net;
using Content.Shared.CCVar;
using Content.Shared.Database;
using Robust.Shared.Configuration;
using Robust.Shared.Network;
namespace Content.Server.Database
{
public sealed class BanDef
{
public int? Id { get; }
public BanType Type { get; }
public ImmutableArray<NetUserId> UserIds { get; }
public ImmutableArray<(IPAddress address, int cidrMask)> Addresses { get; }
public ImmutableArray<ImmutableTypedHwid> HWIds { get; }
public DateTimeOffset BanTime { get; }
public DateTimeOffset? ExpirationTime { get; }
public ImmutableArray<int> RoundIds { get; }
public TimeSpan PlaytimeAtNote { get; }
public string Reason { get; }
public NoteSeverity Severity { get; set; }
public NetUserId? BanningAdmin { get; }
public UnbanDef? Unban { get; }
public ServerBanExemptFlags ExemptFlags { get; }
public ImmutableArray<BanRoleDef>? Roles { get; }
public BanDef(
int? id,
BanType type,
ImmutableArray<NetUserId> userIds,
ImmutableArray<(IPAddress address, int cidrMask)> addresses,
ImmutableArray<ImmutableTypedHwid> hwIds,
DateTimeOffset banTime,
DateTimeOffset? expirationTime,
ImmutableArray<int> roundIds,
TimeSpan playtimeAtNote,
string reason,
NoteSeverity severity,
NetUserId? banningAdmin,
UnbanDef? unban,
ServerBanExemptFlags exemptFlags = default,
ImmutableArray<BanRoleDef>? roles = null)
{
if (userIds.Length == 0 && addresses.Length == 0 && hwIds.Length == 0)
{
throw new ArgumentException("Must have at least one of banned user, banned address or hardware ID");
}
addresses = addresses.Select(address =>
{
if (address is { address.IsIPv4MappedToIPv6: true } addr)
{
// Fix IPv6-mapped IPv4 addresses
// So that IPv4 addresses are consistent between separate-socket and dual-stack socket modes.
address = (addr.address.MapToIPv4(), addr.cidrMask - 96);
}
return address;
})
.ToImmutableArray();
Id = id;
Type = type;
UserIds = userIds;
Addresses = addresses;
HWIds = hwIds;
BanTime = banTime;
ExpirationTime = expirationTime;
RoundIds = roundIds;
PlaytimeAtNote = playtimeAtNote;
Reason = reason;
Severity = severity;
BanningAdmin = banningAdmin;
Unban = unban;
ExemptFlags = exemptFlags;
switch (Type)
{
case BanType.Server:
if (roles != null)
throw new ArgumentException("Cannot specify roles for server ban types", nameof(roles));
break;
case BanType.Role:
if (roles is not { Length: > 0 })
throw new ArgumentException("Must specify roles for server ban types", nameof(roles));
if (exemptFlags != 0)
throw new ArgumentException("Role bans cannot have exempt flags", nameof(exemptFlags));
break;
default:
throw new ArgumentOutOfRangeException(nameof(type));
}
Roles = roles;
}
public string FormatBanMessage(IConfigurationManager cfg, ILocalizationManager loc)
{
string expires;
if (ExpirationTime is { } expireTime)
{
var duration = expireTime - BanTime;
var utc = expireTime.ToUniversalTime();
expires = loc.GetString("ban-expires", ("duration", duration.TotalMinutes.ToString("N0")), ("time", utc.ToString("f")));
}
else
{
var appeal = cfg.GetCVar(CCVars.InfoLinksAppeal);
expires = !string.IsNullOrWhiteSpace(appeal)
? loc.GetString("ban-banned-permanent-appeal", ("link", appeal))
: loc.GetString("ban-banned-permanent");
}
return $"""
{loc.GetString("ban-banned-1")}
{loc.GetString("ban-banned-2", ("reason", Reason))}
{expires}
{loc.GetString("ban-banned-3")}
""";
}
}
}