From cd2bbfb3d4d4cdcd0ef86b4c613be0a69656ce28 Mon Sep 17 00:00:00 2001 From: RaidMax Date: Fri, 13 Apr 2018 23:51:38 -0500 Subject: [PATCH] Added login plugin --- Application/Manager.cs | 1 + Application/RconParsers/T6MRConParser.cs | 2 +- Application/Server.cs | 48 ++++++++---- IW4MAdmin.sln | 21 +++++- Plugins/Login/Commands/CLogin.cs | 37 +++++++++ Plugins/Login/Configuration.cs | 18 +++++ Plugins/Login/Login.csproj | 22 ++++++ Plugins/Login/Plugin.cs | 75 +++++++++++++++++++ SharedLibraryCore/Commands/NativeCommands.cs | 33 +++++++- SharedLibraryCore/Event.cs | 2 + .../Exceptions/AuthorizationException.cs | 11 +++ SharedLibraryCore/Services/PenaltyService.cs | 6 +- WebfrontCore/Controllers/ConsoleController.cs | 3 +- 13 files changed, 256 insertions(+), 23 deletions(-) create mode 100644 Plugins/Login/Commands/CLogin.cs create mode 100644 Plugins/Login/Configuration.cs create mode 100644 Plugins/Login/Login.csproj create mode 100644 Plugins/Login/Plugin.cs create mode 100644 SharedLibraryCore/Exceptions/AuthorizationException.cs diff --git a/Application/Manager.cs b/Application/Manager.cs index 661be03a6..3c91eb48a 100644 --- a/Application/Manager.cs +++ b/Application/Manager.cs @@ -233,6 +233,7 @@ namespace IW4MAdmin.Application Commands.Add(new CPruneAdmins()); Commands.Add(new CKillServer()); Commands.Add(new CSetPassword()); + Commands.Add(new CPing()); foreach (Command C in SharedLibraryCore.Plugins.PluginImporter.ActiveCommands) Commands.Add(C); diff --git a/Application/RconParsers/T6MRConParser.cs b/Application/RconParsers/T6MRConParser.cs index feb1b1ff1..bb9564ae1 100644 --- a/Application/RconParsers/T6MRConParser.cs +++ b/Application/RconParsers/T6MRConParser.cs @@ -82,7 +82,7 @@ namespace Application.RconParsers { String responseLine = statusLine; - if (Regex.Matches(responseLine, @"^\d+", RegexOptions.IgnoreCase).Count > 0) // its a client line! + if (Regex.Matches(responseLine, @"^ *\d+", RegexOptions.IgnoreCase).Count > 0) // its a client line! { String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); int clientId = -1; diff --git a/Application/Server.cs b/Application/Server.cs index fe934c5e5..da4a006c4 100644 --- a/Application/Server.cs +++ b/Application/Server.cs @@ -16,6 +16,7 @@ using SharedLibraryCore.Configuration; using IW4MAdmin.Application.Misc; using Application.RconParsers; using Application.EventParsers; +using SharedLibraryCore.Exceptions; namespace IW4MAdmin { @@ -31,7 +32,7 @@ namespace IW4MAdmin int id = Math.Abs($"{IP}:{Port.ToString()}".Select(a => (int)a).Sum()); // this is a nasty fix for get hashcode being changed - switch(id) + switch (id) { case 765: return 886229536; @@ -227,7 +228,7 @@ namespace IW4MAdmin if (C == null) { await E.Origin.Tell("You entered an unknown command"); - throw new SharedLibraryCore.Exceptions.CommandException($"{E.Origin} entered unknown command \"{CommandString}\""); + throw new CommandException($"{E.Origin} entered unknown command \"{CommandString}\""); } E.Data = E.Data.RemoveWords(1); @@ -236,14 +237,14 @@ namespace IW4MAdmin if (E.Origin.Level < C.Permission) { await E.Origin.Tell("You do not have access to that command"); - throw new SharedLibraryCore.Exceptions.CommandException($"{E.Origin} does not have access to \"{C.Name}\""); + throw new CommandException($"{E.Origin} does not have access to \"{C.Name}\""); } if (Args.Length < (C.RequiredArgumentCount)) { await E.Origin.Tell($"Not enough arguments supplied"); await E.Origin.Tell(C.Syntax); - throw new SharedLibraryCore.Exceptions.CommandException($"{E.Origin} did not supply enough arguments for \"{C.Name}\""); + throw new CommandException($"{E.Origin} did not supply enough arguments for \"{C.Name}\""); } if (C.RequiresTarget || Args.Length > 0) @@ -291,7 +292,7 @@ namespace IW4MAdmin if (matchingPlayers.Count > 1) { await E.Origin.Tell("Multiple players match that name"); - throw new SharedLibraryCore.Exceptions.CommandException($"{E.Origin} had multiple players found for {C.Name}"); + throw new CommandException($"{E.Origin} had multiple players found for {C.Name}"); } else if (matchingPlayers.Count == 1) { @@ -305,7 +306,7 @@ namespace IW4MAdmin { await E.Origin.Tell($"Not enough arguments supplied!"); await E.Origin.Tell(C.Syntax); - throw new SharedLibraryCore.Exceptions.CommandException($"{E.Origin} did not supply enough arguments for \"{C.Name}\""); + throw new CommandException($"{E.Origin} did not supply enough arguments for \"{C.Name}\""); } } } @@ -318,7 +319,7 @@ namespace IW4MAdmin await E.Origin.Tell("Multiple players match that name"); foreach (var p in matchingPlayers) await E.Origin.Tell($"[^3{p.ClientNumber}^7] {p.Name}"); - throw new SharedLibraryCore.Exceptions.CommandException($"{E.Origin} had multiple players found for {C.Name}"); + throw new CommandException($"{E.Origin} had multiple players found for {C.Name}"); } else if (matchingPlayers.Count == 1) { @@ -335,7 +336,7 @@ namespace IW4MAdmin { await E.Origin.Tell($"Not enough arguments supplied!"); await E.Origin.Tell(C.Syntax); - throw new SharedLibraryCore.Exceptions.CommandException($"{E.Origin} did not supply enough arguments for \"{C.Name}\""); + throw new CommandException($"{E.Origin} did not supply enough arguments for \"{C.Name}\""); } } } @@ -343,7 +344,7 @@ namespace IW4MAdmin if (E.Target == null && C.RequiresTarget) { await E.Origin.Tell("Unable to find specified player."); - throw new SharedLibraryCore.Exceptions.CommandException($"{E.Origin} specified invalid player for \"{C.Name}\""); + throw new CommandException($"{E.Origin} specified invalid player for \"{C.Name}\""); } } E.Data = E.Data.Trim(); @@ -397,7 +398,7 @@ namespace IW4MAdmin } // when the server has lost connection - catch (SharedLibraryCore.Exceptions.NetworkException) + catch (NetworkException) { Throttled = true; return ClientNum; @@ -459,7 +460,7 @@ namespace IW4MAdmin LastPoll = DateTime.Now; } - catch (SharedLibraryCore.Exceptions.NetworkException e) + catch (NetworkException e) { ConnectionErrors++; if (ConnectionErrors == 1) @@ -553,7 +554,7 @@ namespace IW4MAdmin return true; } //#if !DEBUG - catch (SharedLibraryCore.Exceptions.NetworkException) + catch (NetworkException) { Logger.WriteError($"Could not communicate with {IP}:{Port}"); return false; @@ -649,7 +650,7 @@ namespace IW4MAdmin // patch for T6M:PLUTONIUM mainPath = (GameName == Game.T6M) ? $"t6r{Path.DirectorySeparatorChar}data" : mainPath; #if DEBUG - basepath.Value = @"\\192.168.88.253\Call of Duty Black Ops II"; + // basepath.Value = @"\\192.168.88.253\Call of Duty Black Ops II"; #endif string logPath = (game.Value == "" || onelog?.Value == 1) ? $"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{mainPath}{Path.DirectorySeparatorChar}{logfile.Value}" : @@ -717,7 +718,7 @@ namespace IW4MAdmin C = await ValidateCommand(E); } - catch (SharedLibraryCore.Exceptions.CommandException e) + catch (CommandException e) { Logger.WriteInfo(e.Message); } @@ -731,9 +732,28 @@ namespace IW4MAdmin try { + if (!E.Remote && E.Origin.Level != Player.Permission.Console) + { + await ExecuteEvent(new GameEvent() + { + Type = GameEvent.EventType.Command, + Data = string.Empty, + Origin = E.Origin, + Target = E.Target, + Owner = this, + Extra = C, + Remote = E.Remote + }); + } + await C.ExecuteAsync(E); } + catch (AuthorizationException e) + { + await E.Origin.Tell($"You are not authorized to execute that command - {e.Message}"); + } + catch (Exception Except) { Logger.WriteError(String.Format("A command request \"{0}\" generated an error.", C.Name)); diff --git a/IW4MAdmin.sln b/IW4MAdmin.sln index dbeba8e55..2e76d4cf0 100644 --- a/IW4MAdmin.sln +++ b/IW4MAdmin.sln @@ -24,7 +24,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stats", "Plugins\Stats\Stat EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Welcome", "Plugins\Welcome\Welcome.csproj", "{179140D3-97AA-4CB4-8BF6-A0C73CA75701}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProfanityDeterment", "Plugins\ProfanityDeterment\ProfanityDeterment.csproj", "{958FF7EC-0226-4E85-A85B-B84EC768197D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProfanityDeterment", "Plugins\ProfanityDeterment\ProfanityDeterment.csproj", "{958FF7EC-0226-4E85-A85B-B84EC768197D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Login", "Plugins\Login\Login.csproj", "{D9F2ED28-6FA5-40CA-9912-E7A849147AB1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -150,6 +152,22 @@ Global {958FF7EC-0226-4E85-A85B-B84EC768197D}.Release|x64.Build.0 = Release|Any CPU {958FF7EC-0226-4E85-A85B-B84EC768197D}.Release|x86.ActiveCfg = Release|Any CPU {958FF7EC-0226-4E85-A85B-B84EC768197D}.Release|x86.Build.0 = Release|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Debug|x64.ActiveCfg = Debug|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Debug|x64.Build.0 = Debug|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Debug|x86.ActiveCfg = Debug|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Debug|x86.Build.0 = Debug|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|Any CPU.Build.0 = Release|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|x64.ActiveCfg = Release|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|x64.Build.0 = Release|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|x86.ActiveCfg = Release|Any CPU + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -159,6 +177,7 @@ Global {98BE4A81-8AFD-4957-83F7-009D353C6BCB} = {26E8B310-269E-46D4-A612-24601F16065F} {179140D3-97AA-4CB4-8BF6-A0C73CA75701} = {26E8B310-269E-46D4-A612-24601F16065F} {958FF7EC-0226-4E85-A85B-B84EC768197D} = {26E8B310-269E-46D4-A612-24601F16065F} + {D9F2ED28-6FA5-40CA-9912-E7A849147AB1} = {26E8B310-269E-46D4-A612-24601F16065F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {84F8F8E0-1F73-41E0-BD8D-BB6676E2EE87} diff --git a/Plugins/Login/Commands/CLogin.cs b/Plugins/Login/Commands/CLogin.cs new file mode 100644 index 000000000..332bbbfc3 --- /dev/null +++ b/Plugins/Login/Commands/CLogin.cs @@ -0,0 +1,37 @@ +using SharedLibraryCore; +using SharedLibraryCore.Objects; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace IW4MAdmin.Plugins.Login.Commands +{ + public class CLogin : Command + { + public CLogin() : base("login", "login using password", "l", Player.Permission.Trusted, false, new CommandArgument[] + { + new CommandArgument() + { + Name = "password", + Required = true + } + }){ } + + 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)); + + if (hashedPassword[0] == client.Password) + { + Plugin.AuthorizedClients[E.Origin.ClientId] = true; + await E.Origin.Tell("You are now logged in"); + } + + else + { + await E.Origin.Tell("Your password is incorrect"); + } + } + } +} diff --git a/Plugins/Login/Configuration.cs b/Plugins/Login/Configuration.cs new file mode 100644 index 000000000..d7f3b99bb --- /dev/null +++ b/Plugins/Login/Configuration.cs @@ -0,0 +1,18 @@ +using SharedLibraryCore; +using SharedLibraryCore.Interfaces; + +namespace IW4MAdmin.Plugins.Login +{ + class Configuration : IBaseConfiguration + { + public bool RequirePrivilegedClientLogin { get; set; } + + public IBaseConfiguration Generate() + { + RequirePrivilegedClientLogin = Utilities.PromptBool("Require privileged client login"); + return this; + } + + public string Name() => "LoginConfiguration"; + } +} diff --git a/Plugins/Login/Login.csproj b/Plugins/Login/Login.csproj new file mode 100644 index 000000000..d42397a7f --- /dev/null +++ b/Plugins/Login/Login.csproj @@ -0,0 +1,22 @@ + + + + Library + netcoreapp2.0 + + + RaidMax.IW4MAdmin.Plugins.Login + RaidMax + Forever None + Login Plugin for IW4MAdmin + + + + + + + + + + + diff --git a/Plugins/Login/Plugin.cs b/Plugins/Login/Plugin.cs new file mode 100644 index 000000000..c012b9a79 --- /dev/null +++ b/Plugins/Login/Plugin.cs @@ -0,0 +1,75 @@ +using System.Collections.Concurrent; +using System.Reflection; +using System.Threading.Tasks; +using SharedLibraryCore; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Exceptions; +using SharedLibraryCore.Interfaces; + +namespace IW4MAdmin.Plugins.Login +{ + public class Plugin : IPlugin + { + public string Name => "Login"; + + public float Version => Assembly.GetExecutingAssembly().GetName().Version.Major + Assembly.GetExecutingAssembly().GetName().Version.Minor / 10.0f; + + public string Author => "RaidMax"; + + public static ConcurrentDictionary AuthorizedClients { get; private set; } + private Configuration Config; + + public Task OnEventAsync(GameEvent E, Server S) + { + if (E.Remote || Config.RequirePrivilegedClientLogin == false) + return Task.CompletedTask; + + if (E.Type == GameEvent.EventType.Connect) + { + AuthorizedClients.TryAdd(E.Origin.ClientId, false); + } + + if (E.Type == GameEvent.EventType.Disconnect) + { + AuthorizedClients.TryRemove(E.Origin.ClientId, out bool value); + } + + if (E.Type == GameEvent.EventType.Command) + { + if (((Command)E.Extra).Name == new SharedLibraryCore.Commands.CSetPassword().Name && + E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId].Password == null) + return Task.CompletedTask; + + if (((Command)E.Extra).Name == new Commands.CLogin().Name) + return Task.CompletedTask; + + if (!AuthorizedClients[E.Origin.ClientId]) + throw new AuthorizationException("not logged in"); + } + + return Task.CompletedTask; + } + + public async Task OnLoadAsync(IManager manager) + { + AuthorizedClients = new ConcurrentDictionary(); + + var cfg = new BaseConfigurationHandler("LoginPluginSettings"); + if (cfg.Configuration() == null) + { + cfg.Set((Configuration)new Configuration().Generate()); + await cfg.Save(); + } + + Config = cfg.Configuration(); + } + + public Task OnTickAsync(Server S) => Utilities.CompletedTask; + + public Task OnUnloadAsync() + { + AuthorizedClients.Clear(); + return Utilities.CompletedTask; + } + } +} diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs index 90ca1c48c..093a9a493 100644 --- a/SharedLibraryCore/Commands/NativeCommands.cs +++ b/SharedLibraryCore/Commands/NativeCommands.cs @@ -937,7 +937,7 @@ namespace SharedLibraryCore.Commands public class CPruneAdmins : Command { - public CPruneAdmins() : base("prune", "demote any admins that have not connected recently (defaults to 30 days)", "p", Player.Permission.Owner, false, new CommandArgument[] + public CPruneAdmins() : base("prune", "demote any admins that have not connected recently (defaults to 30 days)", "pa", Player.Permission.Owner, false, new CommandArgument[] { new CommandArgument() { @@ -1070,4 +1070,35 @@ namespace SharedLibraryCore.Commands } } } + + + public class CPing : Command + { + public CPing() : base("ping", "get client's ping", "pi", Player.Permission.User, false, new CommandArgument[] + { + new CommandArgument() + { + Name = "client", + Required = false + } + }){} + + public override async Task ExecuteAsync(GameEvent E) + { + if (E.Message.IsBroadcastCommand()) + { + if (E.Target == null) + await E.Owner.Broadcast($"{E.Origin.Name}'s ping is ^5{E.Origin.Ping}^7ms"); + else + await E.Owner.Broadcast($"{E.Target.Name}'s ping is ^5{E.Origin.Ping}^7ms"); + } + else + { + if (E.Target == null) + await E.Origin.Tell($"Your ping is ^5{E.Origin.Ping}^7ms"); + else + await E.Origin.Tell($"{E.Target.Name}'s ping is ^5{E.Origin.Ping}^7ms"); + } + } + } } diff --git a/SharedLibraryCore/Event.cs b/SharedLibraryCore/Event.cs index 326e9c3b3..fe8fb262c 100644 --- a/SharedLibraryCore/Event.cs +++ b/SharedLibraryCore/Event.cs @@ -31,6 +31,7 @@ namespace SharedLibraryCore //FROM PLAYER Report, Flag, + Command, // FROM GAME Script, @@ -57,5 +58,6 @@ namespace SharedLibraryCore public Player Target; public Server Owner; public Boolean Remote = false; + public object Extra { get; set; } } } diff --git a/SharedLibraryCore/Exceptions/AuthorizationException.cs b/SharedLibraryCore/Exceptions/AuthorizationException.cs new file mode 100644 index 000000000..695a156f2 --- /dev/null +++ b/SharedLibraryCore/Exceptions/AuthorizationException.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SharedLibraryCore.Exceptions +{ + public class AuthorizationException : Exception + { + public AuthorizationException(string message) : base (message) { } + } +} diff --git a/SharedLibraryCore/Services/PenaltyService.cs b/SharedLibraryCore/Services/PenaltyService.cs index 2f52bfda2..bc34f0e61 100644 --- a/SharedLibraryCore/Services/PenaltyService.cs +++ b/SharedLibraryCore/Services/PenaltyService.cs @@ -133,10 +133,6 @@ namespace SharedLibraryCore.Services { using (var context = new DatabaseContext()) { - /*context.Configuration.LazyLoadingEnabled = false; - context.Configuration.ProxyCreationEnabled = false; - context.Configuration.AutoDetectChangesEnabled = false;*/ - if (victim) { var now = DateTime.UtcNow; @@ -162,7 +158,7 @@ namespace SharedLibraryCore.Services PunisherId = penalty.PunisherId, Offense = penalty.Offense, Type = penalty.Type.ToString(), - TimeRemaining = now > penalty.Expires ? "" : penalty.Expires.ToString() + TimeRemaining = now > penalty.Expires ? "" : penalty.Expires.ToString() }, When = penalty.When, Sensitive = penalty.Type == Objects.Penalty.PenaltyType.Flag diff --git a/WebfrontCore/Controllers/ConsoleController.cs b/WebfrontCore/Controllers/ConsoleController.cs index 0723cbda7..c21434665 100644 --- a/WebfrontCore/Controllers/ConsoleController.cs +++ b/WebfrontCore/Controllers/ConsoleController.cs @@ -42,7 +42,8 @@ namespace WebfrontCore.Controllers Type = GameEvent.EventType.Say, Data = command, Origin = client, - Owner = server + Owner = server, + Remote = true }; await server.ExecuteEvent(remoteEvent);