Compare commits

...

4 Commits

Author SHA1 Message Date
RaidMax
bbcbc4c042 cleanup and enhance penalty handling 2021-10-31 11:57:32 -05:00
RaidMax
f3bead8eb5 reduce timeout when master api is down 2021-10-30 19:42:07 -05:00
RaidMax
7eb45f2bc9 fix issue with detecting bans on accounts with new ips when implicit linking is disabled 2021-10-20 11:16:35 -05:00
RaidMax
5837885653 post webfront url to master 2021-10-19 21:33:21 -05:00
12 changed files with 109 additions and 92 deletions

View File

@ -22,7 +22,7 @@ namespace IW4MAdmin.Application.API.Master
public int Uptime { get; set; } public int Uptime { get; set; }
/// <summary> /// <summary>
/// Specifices the version of the instance /// Specifies the version of the instance
/// </summary> /// </summary>
[JsonProperty("version")] [JsonProperty("version")]
[JsonConverter(typeof(BuildNumberJsonConverter))] [JsonConverter(typeof(BuildNumberJsonConverter))]
@ -33,5 +33,11 @@ namespace IW4MAdmin.Application.API.Master
/// </summary> /// </summary>
[JsonProperty("servers")] [JsonProperty("servers")]
public List<ApiServer> Servers { get; set; } public List<ApiServer> Servers { get; set; }
/// <summary>
/// Url IW4MAdmin is listening on
/// </summary>
[JsonProperty("webfront_url")]
public string WebfrontUrl { get; set; }
} }
} }

View File

@ -605,6 +605,11 @@ namespace IW4MAdmin.Application
return _servers.SelectMany(s => s.Clients).ToList().Where(p => p != null).ToList(); return _servers.SelectMany(s => s.Clients).ToList().Where(p => p != null).ToList();
} }
public EFClient FindActiveClient(EFClient client) =>client.ClientNumber < 0 ?
GetActiveClients()
.FirstOrDefault(c => c.NetworkId == client.NetworkId) ?? client :
client;
public ClientService GetClientService() public ClientService GetClientService()
{ {
return ClientSvc; return ClientSvc;

View File

@ -725,11 +725,11 @@ namespace IW4MAdmin
private async Task OnClientUpdate(EFClient origin) private async Task OnClientUpdate(EFClient origin)
{ {
var client = GetClientsAsList().FirstOrDefault(_client => _client.Equals(origin)); var client = Manager.GetActiveClients().FirstOrDefault(c => c.NetworkId == origin.NetworkId);
if (client == null) if (client == null)
{ {
ServerLogger.LogWarning("{origin} expected to exist in client list for update, but they do not", origin.ToString()); ServerLogger.LogWarning("{Origin} expected to exist in client list for update, but they do not", origin.ToString());
return; return;
} }
@ -755,10 +755,10 @@ namespace IW4MAdmin
} }
} }
else if ((client.IPAddress != null && client.State == ClientState.Disconnecting) || else if (client.IPAddress != null && client.State == ClientState.Disconnecting ||
client.Level == Permission.Banned) client.Level == Permission.Banned)
{ {
ServerLogger.LogWarning("{client} state is Unknown (probably kicked), but they are still connected. trying to kick again...", origin.ToString()); ServerLogger.LogWarning("{Client} state is Unknown (probably kicked), but they are still connected. trying to kick again...", origin.ToString());
await client.CanConnect(client.IPAddress, Manager.GetApplicationSettings().Configuration().EnableImplicitAccountLinking); await client.CanConnect(client.IPAddress, Manager.GetApplicationSettings().Configuration().EnableImplicitAccountLinking);
} }
} }
@ -1321,12 +1321,9 @@ namespace IW4MAdmin
public override async Task Warn(string reason, EFClient targetClient, EFClient targetOrigin) public override async Task Warn(string reason, EFClient targetClient, EFClient targetOrigin)
{ {
// ensure player gets warned if command not performed on them in game // ensure player gets warned if command not performed on them in game
targetClient = targetClient.ClientNumber < 0 ? var activeClient = Manager.FindActiveClient(targetClient);
Manager.GetActiveClients()
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
targetClient;
var newPenalty = new EFPenalty() var newPenalty = new EFPenalty
{ {
Type = EFPenalty.PenaltyType.Warning, Type = EFPenalty.PenaltyType.Warning,
Expires = DateTime.UtcNow, Expires = DateTime.UtcNow,
@ -1336,31 +1333,28 @@ namespace IW4MAdmin
Link = targetClient.AliasLink Link = targetClient.AliasLink
}; };
ServerLogger.LogDebug("Creating warn penalty for {targetClient}", targetClient.ToString()); ServerLogger.LogDebug("Creating warn penalty for {TargetClient}", targetClient.ToString());
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger); await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
if (targetClient.IsIngame) if (activeClient.IsIngame)
{ {
if (targetClient.Warnings >= 4) if (activeClient.Warnings >= 4)
{ {
targetClient.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient(this)); activeClient.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient(this));
return; return;
} }
// todo: move to translation sheet var message = loc["COMMANDS_WARNING_FORMAT"]
string message = $"^1{loc["SERVER_WARNING"]} ^7[^3{targetClient.Warnings}^7]: ^3{targetClient.Name}^7, {reason}"; .FormatExt(activeClient.Warnings, activeClient.Name, reason);
targetClient.CurrentServer.Broadcast(message); activeClient.CurrentServer.Broadcast(message);
} }
} }
public override async Task Kick(string reason, EFClient targetClient, EFClient originClient, EFPenalty previousPenalty) public override async Task Kick(string reason, EFClient targetClient, EFClient originClient, EFPenalty previousPenalty)
{ {
targetClient = targetClient.ClientNumber < 0 ? var activeClient = Manager.FindActiveClient(targetClient);
Manager.GetActiveClients()
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
targetClient;
var newPenalty = new EFPenalty() var newPenalty = new EFPenalty
{ {
Type = EFPenalty.PenaltyType.Kick, Type = EFPenalty.PenaltyType.Kick,
Expires = DateTime.UtcNow, Expires = DateTime.UtcNow,
@ -1370,77 +1364,64 @@ namespace IW4MAdmin
Link = targetClient.AliasLink Link = targetClient.AliasLink
}; };
ServerLogger.LogDebug("Creating kick penalty for {targetClient}", targetClient.ToString()); ServerLogger.LogDebug("Creating kick penalty for {TargetClient}", targetClient.ToString());
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger); await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
if (targetClient.IsIngame) if (activeClient.IsIngame)
{ {
var e = new GameEvent() var gameEvent = new GameEvent
{ {
Type = GameEvent.EventType.PreDisconnect, Type = GameEvent.EventType.PreDisconnect,
Origin = targetClient, Origin = activeClient,
Owner = this Owner = this
}; };
Manager.AddEvent(e); Manager.AddEvent(gameEvent);
var temporalClientId = targetClient.GetAdditionalProperty<string>("ConnectionClientId");
var parsedClientId = string.IsNullOrEmpty(temporalClientId) ? (int?)null : int.Parse(temporalClientId);
var clientNumber = parsedClientId ?? targetClient.ClientNumber;
var formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick, var formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick,
clientNumber, activeClient.TemporalClientNumber,
_messageFormatter.BuildFormattedMessage(RconParser.Configuration, _messageFormatter.BuildFormattedMessage(RconParser.Configuration,
newPenalty, newPenalty,
previousPenalty)); previousPenalty));
await targetClient.CurrentServer.ExecuteCommandAsync(formattedKick); ServerLogger.LogDebug("Executing tempban kick command for {ActiveClient}", activeClient.ToString());
await activeClient.CurrentServer.ExecuteCommandAsync(formattedKick);
} }
} }
public override async Task TempBan(string Reason, TimeSpan length, EFClient targetClient, EFClient originClient) public override async Task TempBan(string reason, TimeSpan length, EFClient targetClient, EFClient originClient)
{ {
// ensure player gets kicked if command not performed on them in the same server // ensure player gets kicked if command not performed on them in the same server
targetClient = targetClient.ClientNumber < 0 ? var activeClient = Manager.FindActiveClient(targetClient);
Manager.GetActiveClients()
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
targetClient;
var newPenalty = new EFPenalty() var newPenalty = new EFPenalty
{ {
Type = EFPenalty.PenaltyType.TempBan, Type = EFPenalty.PenaltyType.TempBan,
Expires = DateTime.UtcNow + length, Expires = DateTime.UtcNow + length,
Offender = targetClient, Offender = targetClient,
Offense = Reason, Offense = reason,
Punisher = originClient, Punisher = originClient,
Link = targetClient.AliasLink Link = targetClient.AliasLink
}; };
ServerLogger.LogDebug("Creating tempban penalty for {targetClient}", targetClient.ToString()); ServerLogger.LogDebug("Creating tempban penalty for {TargetClient}", targetClient.ToString());
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger); await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
if (targetClient.IsIngame) if (activeClient.IsIngame)
{ {
var temporalClientId = targetClient.GetAdditionalProperty<string>("ConnectionClientId");
var parsedClientId = string.IsNullOrEmpty(temporalClientId) ? (int?)null : int.Parse(temporalClientId);
var clientNumber = parsedClientId ?? targetClient.ClientNumber;
var formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick, var formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick,
clientNumber, activeClient.TemporalClientNumber,
_messageFormatter.BuildFormattedMessage(RconParser.Configuration, newPenalty)); _messageFormatter.BuildFormattedMessage(RconParser.Configuration, newPenalty));
ServerLogger.LogDebug("Executing tempban kick command for {targetClient}", targetClient.ToString()); ServerLogger.LogDebug("Executing tempban kick command for {ActiveClient}", activeClient.ToString());
await targetClient.CurrentServer.ExecuteCommandAsync(formattedKick); await activeClient.CurrentServer.ExecuteCommandAsync(formattedKick);
} }
} }
public override async Task Ban(string reason, EFClient targetClient, EFClient originClient, bool isEvade = false) public override async Task Ban(string reason, EFClient targetClient, EFClient originClient, bool isEvade = false)
{ {
// ensure player gets kicked if command not performed on them in the same server // ensure player gets kicked if command not performed on them in the same server
targetClient = targetClient.ClientNumber < 0 ? var activeClient = Manager.FindActiveClient(targetClient);
Manager.GetActiveClients()
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
targetClient;
EFPenalty newPenalty = new EFPenalty() var newPenalty = new EFPenalty
{ {
Type = EFPenalty.PenaltyType.Ban, Type = EFPenalty.PenaltyType.Ban,
Expires = null, Expires = null,
@ -1451,46 +1432,42 @@ namespace IW4MAdmin
IsEvadedOffense = isEvade IsEvadedOffense = isEvade
}; };
ServerLogger.LogDebug("Creating ban penalty for {targetClient}", targetClient.ToString()); ServerLogger.LogDebug("Creating ban penalty for {TargetClient}", targetClient.ToString());
targetClient.SetLevel(Permission.Banned, originClient); activeClient.SetLevel(Permission.Banned, originClient);
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger); await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
if (targetClient.IsIngame) if (activeClient.IsIngame)
{ {
ServerLogger.LogDebug("Attempting to kicking newly banned client {targetClient}", targetClient.ToString()); ServerLogger.LogDebug("Attempting to kicking newly banned client {ActiveClient}", activeClient.ToString());
var temporalClientId = targetClient.GetAdditionalProperty<string>("ConnectionClientId");
var parsedClientId = string.IsNullOrEmpty(temporalClientId) ? (int?)null : int.Parse(temporalClientId);
var clientNumber = parsedClientId ?? targetClient.ClientNumber;
var formattedString = string.Format(RconParser.Configuration.CommandPrefixes.Kick, var formattedString = string.Format(RconParser.Configuration.CommandPrefixes.Kick,
clientNumber, activeClient.TemporalClientNumber,
_messageFormatter.BuildFormattedMessage(RconParser.Configuration, newPenalty)); _messageFormatter.BuildFormattedMessage(RconParser.Configuration, newPenalty));
await targetClient.CurrentServer.ExecuteCommandAsync(formattedString); await activeClient.CurrentServer.ExecuteCommandAsync(formattedString);
} }
} }
override public async Task Unban(string reason, EFClient Target, EFClient Origin) public override async Task Unban(string reason, EFClient targetClient, EFClient originClient)
{ {
var unbanPenalty = new EFPenalty() var unbanPenalty = new EFPenalty
{ {
Type = EFPenalty.PenaltyType.Unban, Type = EFPenalty.PenaltyType.Unban,
Expires = DateTime.Now, Expires = DateTime.Now,
Offender = Target, Offender = targetClient,
Offense = reason, Offense = reason,
Punisher = Origin, Punisher = originClient,
When = DateTime.UtcNow, When = DateTime.UtcNow,
Active = true, Active = true,
Link = Target.AliasLink Link = targetClient.AliasLink
}; };
ServerLogger.LogDebug("Creating unban penalty for {targetClient}", Target.ToString()); ServerLogger.LogDebug("Creating unban penalty for {targetClient}", targetClient.ToString());
Target.SetLevel(Permission.User, Origin); targetClient.SetLevel(Permission.User, originClient);
await Manager.GetPenaltyService().RemoveActivePenalties(Target.AliasLink.AliasLinkId); await Manager.GetPenaltyService().RemoveActivePenalties(targetClient.AliasLink.AliasLinkId);
await Manager.GetPenaltyService().Create(unbanPenalty); await Manager.GetPenaltyService().Create(unbanPenalty);
} }
override public void InitializeTokens() public override void InitializeTokens()
{ {
Manager.GetMessageTokens().Add(new MessageToken("TOTALPLAYERS", (Server s) => Task.Run(async () => (await Manager.GetClientService().GetTotalClientsAsync()).ToString()))); Manager.GetMessageTokens().Add(new MessageToken("TOTALPLAYERS", (Server s) => Task.Run(async () => (await Manager.GetClientService().GetTotalClientsAsync()).ToString())));
Manager.GetMessageTokens().Add(new MessageToken("VERSION", (Server s) => Task.FromResult(Application.Program.Version.ToString()))); Manager.GetMessageTokens().Add(new MessageToken("VERSION", (Server s) => Task.FromResult(Application.Program.Version.ToString())));

View File

@ -19,6 +19,7 @@ using SharedLibraryCore.Services;
using Stats.Dtos; using Stats.Dtos;
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -342,7 +343,12 @@ namespace IW4MAdmin.Application
var masterUri = Utilities.IsDevelopment var masterUri = Utilities.IsDevelopment
? new Uri("http://127.0.0.1:8080") ? new Uri("http://127.0.0.1:8080")
: appConfig?.MasterUrl ?? new ApplicationConfiguration().MasterUrl; : appConfig?.MasterUrl ?? new ApplicationConfiguration().MasterUrl;
var masterRestClient = RestClient.For<IMasterApi>(masterUri); var httpClient = new HttpClient
{
BaseAddress = masterUri,
Timeout = TimeSpan.FromSeconds(15)
};
var masterRestClient = RestClient.For<IMasterApi>(httpClient);
var translationLookup = Configure.Initialize(Utilities.DefaultLogger, masterRestClient, appConfig); var translationLookup = Configure.Initialize(Utilities.DefaultLogger, masterRestClient, appConfig);
if (appConfig == null) if (appConfig == null)

View File

@ -179,7 +179,8 @@ namespace IW4MAdmin.Application.Misc
Id = s.EndPoint, Id = s.EndPoint,
Port = (short)s.Port, Port = (short)s.Port,
IPAddress = s.IP IPAddress = s.IP
}).ToList() }).ToList(),
WebfrontUrl = _appConfig.WebfrontUrl
}; };
Response<ResultMessage> response = null; Response<ResultMessage> response = null;

View File

@ -83,6 +83,7 @@ namespace SharedLibraryCore.Commands
var found = await Manager.GetClientService().Get(dbID); var found = await Manager.GetClientService().Get(dbID);
if (found != null) if (found != null)
{ {
found = Manager.FindActiveClient(found);
E.Target = found; E.Target = found;
E.Target.CurrentServer = E.Owner; E.Target.CurrentServer = E.Owner;
E.Data = String.Join(" ", Args.Skip(1)); E.Data = String.Join(" ", Args.Skip(1));

View File

@ -7,7 +7,6 @@ using System.Threading;
using System.Collections; using System.Collections;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using Microsoft.Extensions.Logging;
namespace SharedLibraryCore.Interfaces namespace SharedLibraryCore.Interfaces
{ {
@ -23,6 +22,7 @@ namespace SharedLibraryCore.Interfaces
IList<IManagerCommand> GetCommands(); IList<IManagerCommand> GetCommands();
IList<Helpers.MessageToken> GetMessageTokens(); IList<Helpers.MessageToken> GetMessageTokens();
IList<EFClient> GetActiveClients(); IList<EFClient> GetActiveClients();
EFClient FindActiveClient(EFClient client);
IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings(); IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings();
ClientService GetClientService(); ClientService GetClientService();
PenaltyService GetPenaltyService(); PenaltyService GetPenaltyService();

View File

@ -683,6 +683,17 @@ namespace SharedLibraryCore.Database.Models
set => SetAdditionalProperty(EFMeta.ClientTag, value); set => SetAdditionalProperty(EFMeta.ClientTag, value);
} }
[NotMapped]
public int TemporalClientNumber
{
get
{
var temporalClientId = GetAdditionalProperty<string>("ConnectionClientId");
var parsedClientId = string.IsNullOrEmpty(temporalClientId) ? (int?) null : int.Parse(temporalClientId);
return parsedClientId ?? ClientNumber;
}
}
[NotMapped] [NotMapped]
private readonly SemaphoreSlim _processingEvent; private readonly SemaphoreSlim _processingEvent;

View File

@ -218,9 +218,9 @@ namespace SharedLibraryCore
/// <summary> /// <summary>
/// Temporarily ban a player ( default 1 hour ) from the server /// Temporarily ban a player ( default 1 hour ) from the server
/// </summary> /// </summary>
/// <param name="Reason">Reason for banning the player</param> /// <param name="reason">Reason for banning the player</param>
/// <param name="Target">The player to ban</param> /// <param name="Target">The player to ban</param>
abstract public Task TempBan(String Reason, TimeSpan length, EFClient Target, EFClient Origin); abstract public Task TempBan(String reason, TimeSpan length, EFClient Target, EFClient Origin);
/// <summary> /// <summary>
/// Perm ban a player from the server /// Perm ban a player from the server
@ -236,9 +236,9 @@ namespace SharedLibraryCore
/// Unban a player by npID / GUID /// Unban a player by npID / GUID
/// </summary> /// </summary>
/// <param name="npID">npID of the player</param> /// <param name="npID">npID of the player</param>
/// <param name="Target">I don't remember what this is for</param> /// <param name="targetClient">I don't remember what this is for</param>
/// <returns></returns> /// <returns></returns>
abstract public Task Unban(string reason, EFClient Target, EFClient Origin); abstract public Task Unban(string reason, EFClient targetClient, EFClient originClient);
/// <summary> /// <summary>
/// Change the current searver map /// Change the current searver map

View File

@ -162,15 +162,29 @@ namespace SharedLibraryCore.Services
.Where(p => p.LinkId == linkId) .Where(p => p.LinkId == linkId)
.Where(filter); .Where(filter);
var iqIpPenalties = _appConfig.EnableImplicitAccountLinking IQueryable<EFPenalty> iqIpPenalties;
? context.Aliases
if (_appConfig.EnableImplicitAccountLinking)
{
iqIpPenalties = context.Aliases
.Where(a => a.IPAddress != null && a.IPAddress == ip) .Where(a => a.IPAddress != null && a.IPAddress == ip)
.SelectMany(a => a.Link.ReceivedPenalties) .SelectMany(a => a.Link.ReceivedPenalties)
.Where(filter)
: context.Penalties.Where(penalty =>
penalty.Offender.CurrentAlias.IPAddress != null &&
penalty.Offender.CurrentAlias.IPAddress == ip)
.Where(filter); .Where(filter);
}
else
{
var aliasIps = await context.Aliases.Where(alias => alias.LinkId == linkId && alias.IPAddress != null)
.Select(alias => alias.IPAddress)
.ToListAsync();
if (ip != null)
{
aliasIps.Add(ip);
}
iqIpPenalties = context.Penalties
.Where(penalty => aliasIps.Contains(penalty.Offender.CurrentAlias.IPAddress))
.Where(filter);
}
var activePenalties = (await iqLinkPenalties.ToListAsync()) var activePenalties = (await iqLinkPenalties.ToListAsync())
.Union(await iqIpPenalties.ToListAsync()) .Union(await iqIpPenalties.ToListAsync())

View File

@ -202,6 +202,6 @@
}); });
</script> </script>
@await RenderSectionAsync("scripts", required: false) @await RenderSectionAsync("scripts", required: false)
@Html.Raw(ViewBag.ScriptInjection); @Html.Raw(ViewBag.ScriptInjection)
</body> </body>
</html> </html>

View File

@ -78,10 +78,6 @@
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Remove="Views\Plugins\**" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Remove="Views\Plugins\**" /> <EmbeddedResource Remove="Views\Plugins\**" />
</ItemGroup> </ItemGroup>
@ -96,7 +92,7 @@
<Exec Command="if $(ConfigurationName) == Debug ( &#xD;&#xA;powershell -Command wget https://raw.githubusercontent.com/iconic/open-iconic/master/font/css/open-iconic-bootstrap.scss -o $(ProjectDir)wwwroot\lib\open-iconic\font\css\open-iconic-bootstrap.scss&#xD;&#xA;echo d | xcopy /f /y $(ProjectDir)wwwroot\lib\open-iconic\font\fonts $(ProjectDir)wwwroot\font\&#xD;&#xA;)" /> <Exec Command="if $(ConfigurationName) == Debug ( &#xD;&#xA;powershell -Command wget https://raw.githubusercontent.com/iconic/open-iconic/master/font/css/open-iconic-bootstrap.scss -o $(ProjectDir)wwwroot\lib\open-iconic\font\css\open-iconic-bootstrap.scss&#xD;&#xA;echo d | xcopy /f /y $(ProjectDir)wwwroot\lib\open-iconic\font\fonts $(ProjectDir)wwwroot\font\&#xD;&#xA;)" />
</Target> </Target>
<Target Name="MyPreCompileTarget" BeforeTargets="Build"> <Target Name="MyPreCompileTarget" BeforeTargets="Build" Condition="'$(Configuration)'!='Debug'">
<Exec Command="dotnet bundle" /> <Exec Command="dotnet bundle" />
</Target> </Target>
</Project> </Project>