diff --git a/Application/Application.csproj b/Application/Application.csproj
index 5cd12b65..7e478c63 100644
--- a/Application/Application.csproj
+++ b/Application/Application.csproj
@@ -10,7 +10,7 @@
RaidMax
Forever None
IW4MAdmin
- IW4MAdmin is a complete server administration tool for IW4x and most Call of Duty® dedicated server
+ IW4MAdmin is a complete server administration tool for IW4x and most Call of Duty® dedicated servers
2019
https://github.com/RaidMax/IW4M-Admin/blob/master/LICENSE
https://raidmax.org/IW4MAdmin
diff --git a/Application/ApplicationManager.cs b/Application/ApplicationManager.cs
index da982303..4cae4cb5 100644
--- a/Application/ApplicationManager.cs
+++ b/Application/ApplicationManager.cs
@@ -376,7 +376,7 @@ namespace IW4MAdmin.Application
Commands.Add(new CPing());
Commands.Add(new CSetGravatar());
Commands.Add(new CNextMap());
- Commands.Add(new GenerateTokenCommand());
+ Commands.Add(new RequestTokenCommand());
foreach (Command C in SharedLibraryCore.Plugins.PluginImporter.ActiveCommands)
{
diff --git a/Application/EventParsers/BaseEventParser.cs b/Application/EventParsers/BaseEventParser.cs
index 5ded70a4..8931021f 100644
--- a/Application/EventParsers/BaseEventParser.cs
+++ b/Application/EventParsers/BaseEventParser.cs
@@ -87,7 +87,7 @@ namespace IW4MAdmin.Application.EventParsers
return new GameEvent()
{
Type = GameEvent.EventType.JoinTeam,
- Data = eventType,
+ Data = logLine,
Origin = origin,
Owner = server
};
@@ -203,7 +203,7 @@ namespace IW4MAdmin.Application.EventParsers
return new GameEvent()
{
Type = GameEvent.EventType.Damage,
- Data = eventType,
+ Data = logLine,
Origin = origin,
Target = target,
Owner = server
diff --git a/Application/Misc/TokenAuthentication.cs b/Application/Misc/TokenAuthentication.cs
index 41249d79..1fbe9957 100644
--- a/Application/Misc/TokenAuthentication.cs
+++ b/Application/Misc/TokenAuthentication.cs
@@ -1,4 +1,5 @@
-using SharedLibraryCore.Interfaces;
+using SharedLibraryCore.Helpers;
+using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Concurrent;
using System.Security.Cryptography;
@@ -10,16 +11,9 @@ namespace IW4MAdmin.Application.Misc
{
private readonly ConcurrentDictionary _tokens;
private readonly RNGCryptoServiceProvider _random;
- private readonly static TimeSpan _timeoutPeriod = new TimeSpan(0, 0, 30);
+ private readonly static TimeSpan _timeoutPeriod = new TimeSpan(0, 0, 120);
private const short TOKEN_LENGTH = 4;
- private class TokenState
- {
- public long NetworkId { get; set; }
- public DateTime RequestTime { get; set; } = DateTime.Now;
- public string Token { get; set; }
- }
-
public TokenAuthentication()
{
_tokens = new ConcurrentDictionary();
@@ -38,32 +32,44 @@ namespace IW4MAdmin.Application.Misc
return authorizeSuccessful;
}
- public string GenerateNextToken(long networkId)
+ public TokenState GenerateNextToken(long networkId)
{
TokenState state = null;
+
if (_tokens.ContainsKey(networkId))
{
state = _tokens[networkId];
- if ((DateTime.Now - state.RequestTime) < _timeoutPeriod)
+ if ((DateTime.Now - state.RequestTime) > _timeoutPeriod)
{
- return null;
+ _tokens.TryRemove(networkId, out TokenState _);
}
else
{
- _tokens.TryRemove(networkId, out TokenState _);
+ return state;
}
}
state = new TokenState()
{
NetworkId = networkId,
- Token = _generateToken()
+ Token = _generateToken(),
+ TokenDuration = _timeoutPeriod
};
_tokens.TryAdd(networkId, state);
- return state.Token;
+
+ // perform some housekeeping so we don't have built up tokens if they're not ever used
+ foreach (var (key, value) in _tokens)
+ {
+ if ((DateTime.Now - value.RequestTime) > _timeoutPeriod)
+ {
+ _tokens.TryRemove(key, out TokenState _);
+ }
+ }
+
+ return state;
}
public string _generateToken()
diff --git a/Plugins/Login/Commands/CLogin.cs b/Plugins/Login/Commands/CLogin.cs
index 509434ef..d92385f9 100644
--- a/Plugins/Login/Commands/CLogin.cs
+++ b/Plugins/Login/Commands/CLogin.cs
@@ -1,8 +1,5 @@
using SharedLibraryCore;
using SharedLibraryCore.Database.Models;
-using SharedLibraryCore.Objects;
-using System;
-using System.Collections.Generic;
using System.Threading.Tasks;
namespace IW4MAdmin.Plugins.Login.Commands
@@ -22,18 +19,22 @@ namespace IW4MAdmin.Plugins.Login.Commands
public override async Task ExecuteAsync(GameEvent E)
{
var client = E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId];
- string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(E.Data, client.PasswordSalt));
+ bool success = E.Owner.Manager.TokenAuthenticator.AuthorizeToken(E.Origin.NetworkId, E.Data);
- if (hashedPassword[0] == client.Password)
+ if (!success)
{
- Plugin.AuthorizedClients[E.Origin.ClientId] = true;
- E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS"]);
+ string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(E.Data, client.PasswordSalt));
+
+ if (hashedPassword[0] == client.Password)
+ {
+ success = true;
+ Plugin.AuthorizedClients[E.Origin.ClientId] = true;
+ }
}
- else
- {
+ _ = success ?
+ E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS"]) :
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL"]);
- }
}
}
}
diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs
index b72b8048..66e9a775 100644
--- a/Plugins/Stats/Helpers/StatManager.cs
+++ b/Plugins/Stats/Helpers/StatManager.cs
@@ -427,19 +427,20 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
public void AddDamageEvent(string eventLine, int attackerClientId, int victimClientId, long serverId)
{
- string regex = @"^(D);(.+);([0-9]+);(allies|axis);(.+);([0-9]+);(allies|axis);(.+);(.+);([0-9]+);(.+);(.+)$";
- var match = Regex.Match(eventLine, regex, RegexOptions.IgnoreCase);
+ // todo: maybe do something with this
+ //string regex = @"^(D);(.+);([0-9]+);(allies|axis);(.+);([0-9]+);(allies|axis);(.+);(.+);([0-9]+);(.+);(.+)$";
+ //var match = Regex.Match(eventLine, regex, RegexOptions.IgnoreCase);
- if (match.Success)
- {
- // this gives us what team the player is on
- var attackerStats = Servers[serverId].PlayerStats[attackerClientId];
- var victimStats = Servers[serverId].PlayerStats[victimClientId];
- IW4Info.Team victimTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[4].ToString());
- IW4Info.Team attackerTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[7].ToString());
- attackerStats.Team = attackerTeam;
- victimStats.Team = victimTeam;
- }
+ //if (match.Success)
+ //{
+ // // this gives us what team the player is on
+ // var attackerStats = Servers[serverId].PlayerStats[attackerClientId];
+ // var victimStats = Servers[serverId].PlayerStats[victimClientId];
+ // IW4Info.Team victimTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[4].ToString(), true);
+ // IW4Info.Team attackerTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[7].ToString(), true);
+ // attackerStats.Team = attackerTeam;
+ // victimStats.Team = victimTeam;
+ //}
}
///
diff --git a/SharedLibraryCore/Commands/GenerateTokenCommand.cs b/SharedLibraryCore/Commands/GenerateTokenCommand.cs
deleted file mode 100644
index 596837aa..00000000
--- a/SharedLibraryCore/Commands/GenerateTokenCommand.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using SharedLibraryCore.Database.Models;
-using System.Threading.Tasks;
-
-namespace SharedLibraryCore.Commands
-{
- public class GenerateTokenCommand : Command
- {
- public GenerateTokenCommand() :
- base("generatetoken", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GENERATETOKEN_DESC"], "gt", EFClient.Permission.Trusted, false)
- { }
-
- public override Task ExecuteAsync(GameEvent E)
- {
- string token = E.Owner.Manager.TokenAuthenticator.GenerateNextToken(E.Origin.NetworkId);
- var _event = token == null ?
- E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GENERATETOKEN_FAIL"]) :
- E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GENERATETOKEN_SUCCESS"]} {token}");
-
- return Task.CompletedTask;
- }
-}
-}
diff --git a/SharedLibraryCore/Commands/RequestTokenCommand.cs b/SharedLibraryCore/Commands/RequestTokenCommand.cs
new file mode 100644
index 00000000..6d0672ea
--- /dev/null
+++ b/SharedLibraryCore/Commands/RequestTokenCommand.cs
@@ -0,0 +1,20 @@
+using SharedLibraryCore.Database.Models;
+using System.Threading.Tasks;
+
+namespace SharedLibraryCore.Commands
+{
+ public class RequestTokenCommand : Command
+ {
+ public RequestTokenCommand() :
+ base("requesttoken", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GENERATETOKEN_DESC"], "rt", EFClient.Permission.Trusted, false)
+ { }
+
+ public override Task ExecuteAsync(GameEvent E)
+ {
+ var state = E.Owner.Manager.TokenAuthenticator.GenerateNextToken(E.Origin.NetworkId);
+ E.Origin.Tell(string.Format(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GENERATETOKEN_SUCCESS"], state.Token, $"{state.RemainingTime} {Utilities.CurrentLocalization.LocalizationIndex["GLOBAL_MINUTES"]}", E.Origin.ClientId));
+
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/SharedLibraryCore/Helpers/TokenState.cs b/SharedLibraryCore/Helpers/TokenState.cs
new file mode 100644
index 00000000..c5f8afb8
--- /dev/null
+++ b/SharedLibraryCore/Helpers/TokenState.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SharedLibraryCore.Helpers
+{
+ public sealed class TokenState
+ {
+ public long NetworkId { get; set; }
+ public DateTime RequestTime { get; set; } = DateTime.Now;
+ public TimeSpan TokenDuration { get; set; }
+ public string Token { get; set; }
+ public string RemainingTime => Math.Round(-(DateTime.Now - RequestTime).Subtract(TokenDuration).TotalMinutes, 1).ToString();
+ }
+}
diff --git a/SharedLibraryCore/Interfaces/ITokenAuthentication.cs b/SharedLibraryCore/Interfaces/ITokenAuthentication.cs
index f9752862..58d0ce3d 100644
--- a/SharedLibraryCore/Interfaces/ITokenAuthentication.cs
+++ b/SharedLibraryCore/Interfaces/ITokenAuthentication.cs
@@ -1,4 +1,5 @@
-using System;
+using SharedLibraryCore.Helpers;
+using System;
using System.Collections.Generic;
using System.Text;
@@ -11,7 +12,7 @@ namespace SharedLibraryCore.Interfaces
///
/// network id of the players to generate the token for
/// 4 character string token
- string GenerateNextToken(long networkId);
+ TokenState GenerateNextToken(long networkId);
///
/// authorizes given token
diff --git a/WebfrontCore/Controllers/AccountController.cs b/WebfrontCore/Controllers/AccountController.cs
index 60d94732..5cfdb3c8 100644
--- a/WebfrontCore/Controllers/AccountController.cs
+++ b/WebfrontCore/Controllers/AccountController.cs
@@ -21,9 +21,11 @@ namespace WebfrontCore.Controllers
try
{
var client = Manager.GetPrivilegedClients()[clientId];
- string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(password, client.PasswordSalt));
- if (hashedPassword[0] == client.Password)
+ // string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(password, client.PasswordSalt));
+ //if (hashedPassword[0] == client.Password)
+
+ if (Manager.TokenAuthenticator.AuthorizeToken(client.NetworkId, password))
{
var claims = new[]
{
@@ -61,12 +63,5 @@ namespace WebfrontCore.Controllers
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return RedirectToAction("Index", "Home");
}
-
- [HttpGet]
- [Authorize]
- public async Task GenerateLoginTokenAsync()
- {
- return Json(new { token = Manager.TokenAuthenticator.GenerateNextToken(Client.NetworkId) });
- }
}
}
diff --git a/WebfrontCore/Controllers/ActionController.cs b/WebfrontCore/Controllers/ActionController.cs
index 4a02388c..35b86cb9 100644
--- a/WebfrontCore/Controllers/ActionController.cs
+++ b/WebfrontCore/Controllers/ActionController.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore;
using WebfrontCore.ViewModels;
@@ -182,5 +183,25 @@ namespace WebfrontCore.Controllers
command = $"!setlevel @{targetId} {level}"
}));
}
+
+ public async Task GenerateLoginTokenForm()
+ {
+ var info = new ActionInfo()
+ {
+ ActionButtonLabel = "Generate",
+ Name = "GenerateLoginToken",
+ Action = "GenerateLoginTokenAsync",
+ Inputs = new List()
+ };
+
+ return View("_ActionForm", info);
+ }
+
+ [Authorize]
+ public string GenerateLoginTokenAsync()
+ {
+ var state = Manager.TokenAuthenticator.GenerateNextToken(Client.NetworkId);
+ return string.Format(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GENERATETOKEN_SUCCESS"], state.Token, $"{state.RemainingTime} {Utilities.CurrentLocalization.LocalizationIndex["GLOBAL_MINUTES"]}", Client.ClientId);
+ }
}
}
diff --git a/WebfrontCore/Views/Shared/_Layout.cshtml b/WebfrontCore/Views/Shared/_Layout.cshtml
index 249b2322..384f8e31 100644
--- a/WebfrontCore/Views/Shared/_Layout.cshtml
+++ b/WebfrontCore/Views/Shared/_Layout.cshtml
@@ -70,6 +70,7 @@
@class = "dropdown-item bg-dark text-muted text-center text-md-left",
title = "Logout of account"
})
+ @loc["WEBFRONT_ACTION_TOKEN"]
diff --git a/WebfrontCore/Views/Shared/_ValidationScriptsPartial.cshtml b/WebfrontCore/Views/Shared/_ValidationScriptsPartial.cshtml
deleted file mode 100644
index a699aafa..00000000
--- a/WebfrontCore/Views/Shared/_ValidationScriptsPartial.cshtml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/WebfrontCore/WebfrontCore.csproj b/WebfrontCore/WebfrontCore.csproj
index 56525595..2697ce1f 100644
--- a/WebfrontCore/WebfrontCore.csproj
+++ b/WebfrontCore/WebfrontCore.csproj
@@ -3,7 +3,7 @@
netcoreapp2.2
2.2.2
- true
+ false
true
true
2.6
@@ -12,7 +12,7 @@
RaidMax
Forever None
IW4MAdmin
- IW4MAdmin is a complete server administration tool for IW4x and most Call of Duty® dedicated server
+ IW4MAdmin is a complete server administration tool for IW4x and most Call of Duty® dedicated servers
2019
https://github.com/RaidMax/IW4M-Admin/blob/master/LICENSE
https://raidmax.org/IW4MAdmin
@@ -74,7 +74,5 @@
-
-
diff --git a/WebfrontCore/wwwroot/css/bootstrap-custom.css b/WebfrontCore/wwwroot/css/bootstrap-custom.css
index 51596636..c7a3f49b 100644
--- a/WebfrontCore/wwwroot/css/bootstrap-custom.css
+++ b/WebfrontCore/wwwroot/css/bootstrap-custom.css
@@ -6416,4 +6416,3 @@ form *, select {
position: fixed;
bottom: 1em;
right: 1em; }
-
diff --git a/WebfrontCore/wwwroot/css/profile.css b/WebfrontCore/wwwroot/css/profile.css
index 43996e6a..858edb75 100644
--- a/WebfrontCore/wwwroot/css/profile.css
+++ b/WebfrontCore/wwwroot/css/profile.css
@@ -178,7 +178,6 @@
}
.profile-action {
- padding-left: 0.25em;
cursor: pointer;
}