219 lines
8.6 KiB
C#
219 lines
8.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Threading.Tasks;
|
|
using Data.Abstractions;
|
|
using Data.Models;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using SharedLibraryCore.Configuration;
|
|
using SharedLibraryCore.Dtos;
|
|
using SharedLibraryCore.Interfaces;
|
|
|
|
namespace SharedLibraryCore.Services
|
|
{
|
|
public class PenaltyService : IEntityService<EFPenalty>
|
|
{
|
|
private readonly ApplicationConfiguration _appConfig;
|
|
private readonly IDatabaseContextFactory _contextFactory;
|
|
|
|
public PenaltyService(IDatabaseContextFactory contextFactory, ApplicationConfiguration appConfig)
|
|
{
|
|
_contextFactory = contextFactory;
|
|
_appConfig = appConfig;
|
|
}
|
|
|
|
public virtual async Task<EFPenalty> Create(EFPenalty newEntity)
|
|
{
|
|
await using var context = _contextFactory.CreateContext();
|
|
var penalty = new EFPenalty
|
|
{
|
|
Active = true,
|
|
OffenderId = newEntity.Offender.ClientId,
|
|
PunisherId = newEntity.Punisher.ClientId,
|
|
LinkId = newEntity.Link?.AliasLinkId,
|
|
Type = newEntity.Type,
|
|
Expires = newEntity.Expires,
|
|
Offense = newEntity.Offense,
|
|
When = DateTime.UtcNow,
|
|
AutomatedOffense = newEntity.AutomatedOffense ??
|
|
newEntity.Punisher.AdministeredPenalties?.FirstOrDefault()?.AutomatedOffense,
|
|
IsEvadedOffense = newEntity.IsEvadedOffense
|
|
};
|
|
|
|
if (LinkedPenalties.Contains(newEntity.Type))
|
|
{
|
|
var penaltyIdentifiers = new EFPenaltyIdentifier
|
|
{
|
|
Penalty = penalty,
|
|
NetworkId = newEntity.Offender.NetworkId,
|
|
IPv4Address = newEntity.Offender.CurrentAlias.IPAddress
|
|
};
|
|
|
|
context.PenaltyIdentifiers.Add(penaltyIdentifiers);
|
|
}
|
|
|
|
context.Penalties.Add(penalty);
|
|
await context.SaveChangesAsync();
|
|
|
|
return newEntity;
|
|
}
|
|
|
|
public Task<EFPenalty> Delete(EFPenalty entity)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public async Task<IList<EFPenalty>> Find(Func<EFPenalty, bool> expression)
|
|
{
|
|
throw await Task.FromResult(new Exception());
|
|
}
|
|
|
|
public Task<EFPenalty> Get(int entityID)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public Task<EFPenalty> GetUnique(long entityProperty)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public Task<EFPenalty> Update(EFPenalty entity)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public async Task<IList<PenaltyInfo>> GetRecentPenalties(int count, int offset,
|
|
EFPenalty.PenaltyType showOnly = EFPenalty.PenaltyType.Any, bool ignoreAutomated = true)
|
|
{
|
|
await using var context = _contextFactory.CreateContext(false);
|
|
var iqPenalties = context.Penalties
|
|
.Where(p => showOnly == EFPenalty.PenaltyType.Any
|
|
? p.Type != EFPenalty.PenaltyType.Any
|
|
: p.Type == showOnly)
|
|
.Where(_penalty => !ignoreAutomated || _penalty.PunisherId != 1)
|
|
.OrderByDescending(p => p.When)
|
|
.Skip(offset)
|
|
.Take(count)
|
|
.Select(_penalty => new PenaltyInfo
|
|
{
|
|
Id = _penalty.PenaltyId,
|
|
Offense = _penalty.Offense,
|
|
AutomatedOffense = _penalty.AutomatedOffense,
|
|
OffenderId = _penalty.OffenderId,
|
|
OffenderName = _penalty.Offender.CurrentAlias.Name,
|
|
PunisherId = _penalty.PunisherId,
|
|
PunisherName = _penalty.Punisher.CurrentAlias.Name,
|
|
PunisherLevel = _penalty.Punisher.Level,
|
|
PenaltyType = _penalty.Type,
|
|
Expires = _penalty.Expires,
|
|
TimePunished = _penalty.When,
|
|
IsEvade = _penalty.IsEvadedOffense
|
|
});
|
|
|
|
return await iqPenalties.ToListAsync();
|
|
}
|
|
|
|
private static readonly EFPenalty.PenaltyType[] LinkedPenalties =
|
|
{ EFPenalty.PenaltyType.Ban, EFPenalty.PenaltyType.Flag };
|
|
|
|
private static readonly Expression<Func<EFPenalty, bool>> Filter = p =>
|
|
LinkedPenalties.Contains(p.Type) && p.Active && (p.Expires == null || p.Expires > DateTime.UtcNow);
|
|
|
|
private static readonly Expression<Func<EFPenaltyIdentifier, bool>> FilterById = pi =>
|
|
LinkedPenalties.Contains(pi.Penalty.Type) && pi.Penalty.Active &&
|
|
(pi.Penalty.Expires == null || pi.Penalty.Expires > DateTime.UtcNow);
|
|
|
|
public async Task<List<EFPenalty>> GetActivePenaltiesAsync(int linkId, int currentAliasId, long networkId,
|
|
int? ip = null)
|
|
{
|
|
var penaltiesByIdentifier = await GetActivePenaltiesByIdentifier(ip, networkId);
|
|
|
|
if (penaltiesByIdentifier.Any())
|
|
{
|
|
return penaltiesByIdentifier;
|
|
}
|
|
|
|
await using var context = _contextFactory.CreateContext(false);
|
|
|
|
IQueryable<EFPenalty> iqIpPenalties;
|
|
|
|
if (_appConfig.EnableImplicitAccountLinking)
|
|
{
|
|
iqIpPenalties = context.Aliases
|
|
.Where(a => a.IPAddress != null && a.IPAddress == ip)
|
|
.SelectMany(a => a.Link.ReceivedPenalties)
|
|
.Where(Filter);
|
|
}
|
|
else
|
|
{
|
|
var usedIps = await context.Aliases.AsNoTracking()
|
|
.Where(alias =>
|
|
(alias.LinkId == linkId || alias.AliasId == currentAliasId) && alias.IPAddress != null)
|
|
.Select(alias => alias.IPAddress).ToListAsync();
|
|
|
|
var aliasedIds = await context.Aliases.AsNoTracking().Where(alias => usedIps.Contains(alias.IPAddress))
|
|
.Select(alias => alias.LinkId)
|
|
.ToListAsync();
|
|
|
|
iqIpPenalties = context.Penalties.AsNoTracking()
|
|
.Where(penalty => aliasedIds.Contains(penalty.LinkId ?? -1) || penalty.LinkId == linkId)
|
|
.Where(Filter);
|
|
}
|
|
|
|
var activeIpPenalties = await iqIpPenalties.ToListAsync();
|
|
var activePenalties = activeIpPenalties.Distinct();
|
|
|
|
// this is a bit more performant in memory (ordering)
|
|
return activePenalties.OrderByDescending(p => p.When).ToList();
|
|
}
|
|
|
|
public async Task<List<EFPenalty>> GetActivePenaltiesByIdentifier(int? ip, long networkId)
|
|
{
|
|
await using var context = _contextFactory.CreateContext(false);
|
|
var activePenaltiesIds = context.PenaltyIdentifiers.Where(identifier =>
|
|
identifier.IPv4Address != null && identifier.IPv4Address == ip || identifier.NetworkId == networkId)
|
|
.Where(FilterById);
|
|
return await activePenaltiesIds.Select(ids => ids.Penalty).ToListAsync();
|
|
}
|
|
|
|
public virtual async Task RemoveActivePenalties(int aliasLinkId, long networkId, int? ipAddress = null)
|
|
{
|
|
await using var context = _contextFactory.CreateContext();
|
|
var now = DateTime.UtcNow;
|
|
|
|
var activePenalties = await GetActivePenaltiesByIdentifier(ipAddress, networkId);
|
|
|
|
if (activePenalties.Any())
|
|
{
|
|
var ids = activePenalties.Select(penalty => penalty.PenaltyId);
|
|
await context.Penalties.Where(penalty => ids.Contains(penalty.PenaltyId))
|
|
.ForEachAsync(penalty =>
|
|
{
|
|
penalty.Active = false;
|
|
penalty.Expires = now;
|
|
});
|
|
await context.SaveChangesAsync();
|
|
return;
|
|
}
|
|
|
|
var penaltiesByLink = context.Penalties
|
|
.Where(p => p.LinkId == aliasLinkId)
|
|
.Where(p => p.Expires > now || p.Expires == null);
|
|
|
|
var penaltiesByIp = context.Penalties
|
|
.Where(p => p.Offender.CurrentAlias.IPAddress != null && p.Offender.CurrentAlias.IPAddress == null)
|
|
.Where(p => p.Expires > now || p.Expires == null);
|
|
|
|
await penaltiesByLink.Union(penaltiesByIp).Distinct().ForEachAsync(p =>
|
|
{
|
|
p.Active = false;
|
|
p.Expires = now;
|
|
});
|
|
|
|
await context.SaveChangesAsync();
|
|
}
|
|
}
|
|
}
|