Implement BlacklistedRange exempt flag (#29258)
* Implement a new kind of ip range ban that only applies to new players * Put determining whether a player record exists to its own function * Make BlacklistedRange bans get bypassed by any ban exemption * Stop trying to get another DbGuard while already having one This does break with convention on the functions in that area but considering the use of this function it's probably fine? I could alternatively just move the place it's called from. Also I was suppossed to wait for tests to finish locally just to be sure, but nah. I am pushing this now
This commit is contained in:
@@ -693,6 +693,14 @@ namespace Content.Server.Database
|
||||
/// Intended use is for users with shared connections. This should not be used as an alternative to <see cref="Datacenter"/>.
|
||||
/// </remarks>
|
||||
IP = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Ban is an IP range that is only applied for first time joins.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Intended for use with residential IP ranges that are often used maliciously.
|
||||
/// </remarks>
|
||||
BlacklistedRange = 1 << 2,
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
|
||||
@@ -625,6 +625,11 @@ namespace Content.Server.Database
|
||||
return record == null ? null : MakePlayerRecord(record);
|
||||
}
|
||||
|
||||
protected async Task<bool> PlayerRecordExists(DbGuard db, NetUserId userId)
|
||||
{
|
||||
return await db.DbContext.Player.AnyAsync(p => p.UserId == userId);
|
||||
}
|
||||
|
||||
[return: NotNullIfNotNull(nameof(player))]
|
||||
protected PlayerRecord? MakePlayerRecord(Player? player)
|
||||
{
|
||||
|
||||
@@ -78,7 +78,8 @@ namespace Content.Server.Database
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
var exempt = await GetBanExemptionCore(db, userId);
|
||||
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned: false, exempt)
|
||||
var newPlayer = userId == null || !await PlayerRecordExists(db, userId.Value);
|
||||
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned: false, exempt, newPlayer)
|
||||
.OrderByDescending(b => b.BanTime);
|
||||
|
||||
var ban = await query.FirstOrDefaultAsync();
|
||||
@@ -98,7 +99,8 @@ namespace Content.Server.Database
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
var exempt = await GetBanExemptionCore(db, userId);
|
||||
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned, exempt);
|
||||
var newPlayer = !await db.PgDbContext.Player.AnyAsync(p => p.UserId == userId);
|
||||
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned, exempt, newPlayer);
|
||||
|
||||
var queryBans = await query.ToArrayAsync();
|
||||
var bans = new List<ServerBanDef>(queryBans.Length);
|
||||
@@ -122,7 +124,8 @@ namespace Content.Server.Database
|
||||
ImmutableArray<byte>? hwId,
|
||||
DbGuardImpl db,
|
||||
bool includeUnbanned,
|
||||
ServerBanExemptFlags? exemptFlags)
|
||||
ServerBanExemptFlags? exemptFlags,
|
||||
bool newPlayer)
|
||||
{
|
||||
DebugTools.Assert(!(address == null && userId == null && hwId == null));
|
||||
|
||||
@@ -141,7 +144,9 @@ namespace Content.Server.Database
|
||||
{
|
||||
var newQ = db.PgDbContext.Ban
|
||||
.Include(p => p.Unban)
|
||||
.Where(b => b.Address != null && EF.Functions.ContainsOrEqual(b.Address.Value, address));
|
||||
.Where(b => b.Address != null
|
||||
&& EF.Functions.ContainsOrEqual(b.Address.Value, address)
|
||||
&& !(b.ExemptFlags.HasFlag(ServerBanExemptFlags.BlacklistedRange) && !newPlayer));
|
||||
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
@@ -167,6 +172,9 @@ namespace Content.Server.Database
|
||||
|
||||
if (exemptFlags is { } exempt)
|
||||
{
|
||||
if (exempt != ServerBanExemptFlags.None)
|
||||
exempt |= ServerBanExemptFlags.BlacklistedRange; // Any kind of exemption should bypass BlacklistedRange
|
||||
|
||||
query = query.Where(b => (b.ExemptFlags & exempt) == 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -86,11 +86,13 @@ namespace Content.Server.Database
|
||||
|
||||
var exempt = await GetBanExemptionCore(db, userId);
|
||||
|
||||
var newPlayer = userId == null || !await PlayerRecordExists(db, userId.Value);
|
||||
|
||||
// SQLite can't do the net masking stuff we need to match IP address ranges.
|
||||
// So just pull down the whole list into memory.
|
||||
var bans = await GetAllBans(db.SqliteDbContext, includeUnbanned: false, exempt);
|
||||
|
||||
return bans.FirstOrDefault(b => BanMatches(b, address, userId, hwId, exempt)) is { } foundBan
|
||||
return bans.FirstOrDefault(b => BanMatches(b, address, userId, hwId, exempt, newPlayer)) is { } foundBan
|
||||
? ConvertBan(foundBan)
|
||||
: null;
|
||||
}
|
||||
@@ -103,12 +105,14 @@ namespace Content.Server.Database
|
||||
|
||||
var exempt = await GetBanExemptionCore(db, userId);
|
||||
|
||||
var newPlayer = !await db.SqliteDbContext.Player.AnyAsync(p => p.UserId == userId);
|
||||
|
||||
// SQLite can't do the net masking stuff we need to match IP address ranges.
|
||||
// So just pull down the whole list into memory.
|
||||
var queryBans = await GetAllBans(db.SqliteDbContext, includeUnbanned, exempt);
|
||||
|
||||
return queryBans
|
||||
.Where(b => BanMatches(b, address, userId, hwId, exempt))
|
||||
.Where(b => BanMatches(b, address, userId, hwId, exempt, newPlayer))
|
||||
.Select(ConvertBan)
|
||||
.ToList()!;
|
||||
}
|
||||
@@ -137,10 +141,18 @@ namespace Content.Server.Database
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ServerBanExemptFlags? exemptFlags)
|
||||
ServerBanExemptFlags? exemptFlags,
|
||||
bool newPlayer)
|
||||
{
|
||||
// Any flag to bypass BlacklistedRange bans.
|
||||
var exemptFromBlacklistedRange = exemptFlags != null && exemptFlags.Value != ServerBanExemptFlags.None;
|
||||
|
||||
if (!exemptFlags.GetValueOrDefault(ServerBanExemptFlags.None).HasFlag(ServerBanExemptFlags.IP)
|
||||
&& address != null && ban.Address is not null && address.IsInSubnet(ban.Address.ToTuple().Value))
|
||||
&& address != null
|
||||
&& ban.Address is not null
|
||||
&& address.IsInSubnet(ban.Address.ToTuple().Value)
|
||||
&& (!ban.ExemptFlags.HasFlag(ServerBanExemptFlags.BlacklistedRange) ||
|
||||
newPlayer && !exemptFromBlacklistedRange))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user