improve ban handling edge cases

This commit is contained in:
RaidMax 2022-03-25 11:28:15 -05:00
parent f90cdbef16
commit 557cc1614f
4 changed files with 95 additions and 14 deletions

View File

@ -11,6 +11,7 @@ using SharedLibraryCore.Dtos.Meta.Responses;
using SharedLibraryCore.Helpers; using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using SharedLibraryCore.QueryHelper; using SharedLibraryCore.QueryHelper;
using SharedLibraryCore.Services;
using ILogger = Microsoft.Extensions.Logging.ILogger; using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.Meta namespace IW4MAdmin.Application.Meta
@ -19,7 +20,8 @@ namespace IW4MAdmin.Application.Meta
/// implementation of IResourceQueryHelper /// implementation of IResourceQueryHelper
/// used to pull in penalties applied to a given client id /// used to pull in penalties applied to a given client id
/// </summary> /// </summary>
public class ReceivedPenaltyResourceQueryHelper : IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse> public class
ReceivedPenaltyResourceQueryHelper : IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse>
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IDatabaseContextFactory _contextFactory; private readonly IDatabaseContextFactory _contextFactory;
@ -33,7 +35,8 @@ namespace IW4MAdmin.Application.Meta
_appConfig = appConfig; _appConfig = appConfig;
} }
public async Task<ResourceQueryHelperResult<ReceivedPenaltyResponse>> QueryResource(ClientPaginationRequest query) public async Task<ResourceQueryHelperResult<ReceivedPenaltyResponse>> QueryResource(
ClientPaginationRequest query)
{ {
var linkedPenaltyType = Utilities.LinkedPenaltyTypes(); var linkedPenaltyType = Utilities.LinkedPenaltyTypes();
await using var ctx = _contextFactory.CreateContext(enableTracking: false); await using var ctx = _contextFactory.CreateContext(enableTracking: false);
@ -48,13 +51,20 @@ namespace IW4MAdmin.Application.Meta
linkedPenaltyType.Contains(_penalty.Type) && _penalty.LinkId == linkId.AliasLinkId); linkedPenaltyType.Contains(_penalty.Type) && _penalty.LinkId == linkId.AliasLinkId);
IQueryable<EFPenalty> iqIpLinkedPenalties = null; IQueryable<EFPenalty> iqIpLinkedPenalties = null;
IQueryable<EFPenalty> identifierPenalties = null;
if (!_appConfig.EnableImplicitAccountLinking) if (!_appConfig.EnableImplicitAccountLinking)
{ {
var usedIps = await ctx.Aliases.AsNoTracking() var usedIps = await ctx.Aliases.AsNoTracking()
.Where(alias => (alias.LinkId == linkId.AliasLinkId || alias.AliasId == linkId.CurrentAliasId) && alias.IPAddress != null) .Where(alias =>
(alias.LinkId == linkId.AliasLinkId || alias.AliasId == linkId.CurrentAliasId) &&
alias.IPAddress != null)
.Select(alias => alias.IPAddress).ToListAsync(); .Select(alias => alias.IPAddress).ToListAsync();
identifierPenalties = ctx.PenaltyIdentifiers.AsNoTracking().Where(identifier =>
identifier.IPv4Address != null && usedIps.Contains(identifier.IPv4Address))
.Select(id => id.Penalty);
var aliasedIds = await ctx.Aliases.AsNoTracking().Where(alias => usedIps.Contains(alias.IPAddress)) var aliasedIds = await ctx.Aliases.AsNoTracking().Where(alias => usedIps.Contains(alias.IPAddress))
.Select(alias => alias.LinkId) .Select(alias => alias.LinkId)
.ToListAsync(); .ToListAsync();
@ -71,6 +81,11 @@ namespace IW4MAdmin.Application.Meta
iqAllPenalties = iqPenalties.Union(iqIpLinkedPenalties); iqAllPenalties = iqPenalties.Union(iqIpLinkedPenalties);
} }
if (identifierPenalties != null)
{
iqAllPenalties = iqPenalties.Union(identifierPenalties);
}
var penalties = await iqAllPenalties var penalties = await iqAllPenalties
.Where(_penalty => _penalty.When < query.Before) .Where(_penalty => _penalty.When < query.Before)
.OrderByDescending(_penalty => _penalty.When) .OrderByDescending(_penalty => _penalty.When)
@ -97,7 +112,7 @@ namespace IW4MAdmin.Application.Meta
{ {
// todo: maybe actually count // todo: maybe actually count
RetrievedResultCount = penalties.Count, RetrievedResultCount = penalties.Count,
Results = penalties Results = penalties.Distinct()
}; };
} }
} }

View File

@ -153,6 +153,7 @@ namespace SharedLibraryCore.Configuration
public bool EnablePrivilegedUserPrivacy { get; set; } public bool EnablePrivilegedUserPrivacy { get; set; }
[ConfigurationIgnore] public bool EnableImplicitAccountLinking { get; set; } = false; [ConfigurationIgnore] public bool EnableImplicitAccountLinking { get; set; } = false;
[ConfigurationIgnore] public TimeSpan RecentAliasIpLinkTimeLimit { get; set; } = TimeSpan.FromDays(7);
[ConfigurationIgnore] public TimeSpan MaxClientHistoryTime { get; set; } = TimeSpan.FromHours(12); [ConfigurationIgnore] public TimeSpan MaxClientHistoryTime { get; set; } = TimeSpan.FromHours(12);

View File

@ -693,13 +693,14 @@ namespace SharedLibraryCore.Database.Models
if (Level != Permission.Banned) if (Level != Permission.Banned)
{ {
Utilities.DefaultLogger.LogInformation( Utilities.DefaultLogger.LogInformation(
"Client {client} has a ban penalty, but they're using a new GUID, we we're updating their level and kicking them", "Client {Client} has a ban penalty, but they're using a new GUID, we we're updating their level and kicking them",
ToString()); ToString());
await SetLevel(Permission.Banned, autoKickClient).WaitAsync(Utilities.DefaultCommandTimeout, await SetLevel(Permission.Banned, autoKickClient).WaitAsync(Utilities.DefaultCommandTimeout,
CurrentServer.Manager.CancellationToken); CurrentServer.Manager.CancellationToken);
} }
Utilities.DefaultLogger.LogInformation("Kicking {client} because they are banned", ToString()); Utilities.DefaultLogger.LogInformation("Kicking {Client} because they are banned", ToString());
Kick(loc["WEBFRONT_PENALTY_LIST_BANNED_REASON"], autoKickClient, banPenalty); Kick(loc["WEBFRONT_PENALTY_LIST_BANNED_REASON"], autoKickClient, banPenalty);
return false; return false;
} }
@ -732,6 +733,34 @@ namespace SharedLibraryCore.Database.Models
ToString()); ToString());
Unflag(Utilities.CurrentLocalization.LocalizationIndex["SERVER_AUTOFLAG_UNFLAG"], autoKickClient); Unflag(Utilities.CurrentLocalization.LocalizationIndex["SERVER_AUTOFLAG_UNFLAG"], autoKickClient);
} }
if (Level != Permission.Banned)
{
return true;
}
// we want to see if they've recently used a banned IP
var recentIPPenalties= await CurrentServer.Manager.GetPenaltyService().ActivePenaltiesByRecentIdentifiers(AliasLinkId);
var recentBanPenalty =
recentIPPenalties.FirstOrDefault(penalty => penalty.Type == EFPenalty.PenaltyType.Ban);
if (recentBanPenalty is null || !IPAddress.HasValue)
{
Utilities.DefaultLogger.LogInformation(
"Setting {Client} level to user because they are banned but no direct penalties or recent penalty identifiers exist for them",
ToString());
await SetLevel(Permission.User, autoKickClient).WaitAsync(Utilities.DefaultCommandTimeout,
CurrentServer.Manager.CancellationToken);
return true;
}
Utilities.DefaultLogger.LogInformation("Updating penalty for {Client} because they recently used a banned IP", this);
await CurrentServer.Manager.GetPenaltyService()
.CreatePenaltyIdentifier(recentBanPenalty.PenaltyId, NetworkId, IPAddress.Value);
Utilities.DefaultLogger.LogInformation("Kicking {Client} because they are banned", ToString());
Kick(loc["WEBFRONT_PENALTY_LIST_BANNED_REASON"], autoKickClient, recentBanPenalty);
} }
return true; return true;

View File

@ -59,6 +59,20 @@ namespace SharedLibraryCore.Services
return newEntity; return newEntity;
} }
public async Task CreatePenaltyIdentifier(int penaltyId, long networkId, int ipv4Address)
{
await using var context = _contextFactory.CreateContext();
var penaltyIdentifiers = new EFPenaltyIdentifier
{
PenaltyId = penaltyId,
NetworkId = networkId,
IPv4Address = ipv4Address
};
context.PenaltyIdentifiers.Add(penaltyIdentifiers);
await context.SaveChangesAsync();
}
public Task<EFPenalty> Delete(EFPenalty entity) public Task<EFPenalty> Delete(EFPenalty entity)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@ -172,12 +186,34 @@ namespace SharedLibraryCore.Services
public async Task<List<EFPenalty>> GetActivePenaltiesByIdentifier(int? ip, long networkId) public async Task<List<EFPenalty>> GetActivePenaltiesByIdentifier(int? ip, long networkId)
{ {
await using var context = _contextFactory.CreateContext(false); await using var context = _contextFactory.CreateContext(false);
var activePenaltiesIds = context.PenaltyIdentifiers.Where(identifier => var activePenaltiesIds = context.PenaltyIdentifiers.Where(identifier =>
identifier.IPv4Address != null && identifier.IPv4Address == ip || identifier.NetworkId == networkId) identifier.IPv4Address != null && identifier.IPv4Address == ip || identifier.NetworkId == networkId)
.Where(FilterById); .Where(FilterById);
return await activePenaltiesIds.Select(ids => ids.Penalty).ToListAsync(); return await activePenaltiesIds.Select(ids => ids.Penalty).ToListAsync();
} }
public async Task<List<EFPenalty>> ActivePenaltiesByRecentIdentifiers(int linkId)
{
await using var context = _contextFactory.CreateContext(false);
var recentlyUsedIps = await context.Aliases.Where(alias => alias.LinkId == linkId)
.Where(alias => alias.IPAddress != null)
.Where(alias => alias.DateAdded >= DateTime.UtcNow - _appConfig.RecentAliasIpLinkTimeLimit)
.Select(alias => alias.IPAddress).ToListAsync();
if (!recentlyUsedIps.Any())
{
return new List<EFPenalty>();
}
var activePenaltiesIds = context.PenaltyIdentifiers
.Where(identifier => recentlyUsedIps.Contains(identifier.IPv4Address))
.Where(FilterById);
return await activePenaltiesIds.Select(ids => ids.Penalty).ToListAsync();
}
public virtual async Task RemoveActivePenalties(int aliasLinkId, long networkId, int? ipAddress = null) public virtual async Task RemoveActivePenalties(int aliasLinkId, long networkId, int? ipAddress = null)
{ {
await using var context = _contextFactory.CreateContext(); await using var context = _contextFactory.CreateContext();