diff --git a/src/Zirconium/Core/App.cs b/src/Zirconium/Core/App.cs index 8c69dbd..aea8416 100644 --- a/src/Zirconium/Core/App.cs +++ b/src/Zirconium/Core/App.cs @@ -1,5 +1,6 @@ using Zirconium.Core.Modules; using Zirconium.Core.Modules.Interfaces; +using Zirconium.Core.Logging; namespace Zirconium.Core { @@ -9,12 +10,15 @@ namespace Zirconium.Core public Router Router { get; } public ModuleManager ModuleManager { get; } public IHostModuleAPI HostModuleAPI { get; } + public AuthManager AuthManager { get; } public App() { SessionManager = new SessionManager(); Router = new Router(this); HostModuleAPI = new HostModuleAPI(Router); + AuthManager = new AuthManager(this); + Log.Info("Zirconium is initialized successfully"); } } } \ No newline at end of file diff --git a/src/Zirconium/Core/ConnectionHandler.cs b/src/Zirconium/Core/ConnectionHandler.cs index f498bf5..4429e9b 100644 --- a/src/Zirconium/Core/ConnectionHandler.cs +++ b/src/Zirconium/Core/ConnectionHandler.cs @@ -2,6 +2,7 @@ using System; using WebSocketSharp.Server; using WebSocketSharp; using Zirconium.Core.Models; +using Zirconium.Utils; namespace Zirconium.Core { @@ -36,9 +37,14 @@ namespace Zirconium.Core var ip = Context.UserEndPoint.Address; var connInfo = new ConnectionInfo(); connInfo.ClientAddress = ip; + connInfo.ConnectionHandler = this; _app.SessionManager.AddSession(ID, connInfo); Console.WriteLine($"Connection {ID} was created"); // TODO implement normal logging } + + public void SendMessage(string message) { + this.Send(message.ToByteArray()); + } } } diff --git a/src/Zirconium/Core/Main.cs b/src/Zirconium/Core/Main.cs index 7b3ba5c..9f8ba21 100644 --- a/src/Zirconium/Core/Main.cs +++ b/src/Zirconium/Core/Main.cs @@ -6,7 +6,7 @@ namespace Zirconium.Core { static void Main(string[] args) { - Console.WriteLine("Hello World! Zirconium"); + App app = new App(); } } } diff --git a/src/Zirconium/Core/Models/ConnectionInfo.cs b/src/Zirconium/Core/Models/ConnectionInfo.cs index 448002c..6aa03e5 100644 --- a/src/Zirconium/Core/Models/ConnectionInfo.cs +++ b/src/Zirconium/Core/Models/ConnectionInfo.cs @@ -7,5 +7,6 @@ namespace Zirconium.Core.Models public string LastTokenHash { get; set; } public JWTPayload LastTokenPayload { get; set; } public IPAddress ClientAddress { get; set; } + public ConnectionHandler ConnectionHandler { get; set; } } } \ No newline at end of file diff --git a/src/Zirconium/Core/Router.cs b/src/Zirconium/Core/Router.cs index 2dfb06b..1a4b403 100644 --- a/src/Zirconium/Core/Router.cs +++ b/src/Zirconium/Core/Router.cs @@ -1,7 +1,12 @@ +using System.Threading.Tasks; +using System; using System.Collections.Generic; using Zirconium.Core.Models; using Zirconium.Core.Modules.Interfaces; using Zirconium.Utils; +using Zirconium.Core.Logging; +using Newtonsoft.Json; +using System.Security.Cryptography; namespace Zirconium.Core { @@ -18,19 +23,99 @@ namespace Zirconium.Core _coreEventsHandlers = new Dictionary>(); } - public void RouteMessage(ConnectionInfo connInfo, BaseMessage message) { + public void RouteMessage(ConnectionInfo connInfo, BaseMessage message) + { + var handlers = _c2sMessageHandlers[message.MessageType]; + if (handlers == null) + { + Log.Warning($"Drop message with type {message.MessageType} because server hasn't proper handlers"); + var serializedMsg = JsonConvert.SerializeObject( + OtherUtils.GenerateProtocolError( + message, + "unhandled", + $"Server doesn't implement message type {message.MessageType}", + new Dictionary() + ) + ); + connInfo.ConnectionHandler.SendMessage(serializedMsg); + return; + } + foreach (var h in handlers) + { + if (h.IsAuthorizationRequired()) + { + JWTPayload tokenPayload; + try + { + tokenPayload = _app.AuthManager.ValidateToken(message.AuthToken); + } + catch (Exception e) + { + Log.Warning(e.Message); + var serializedMsg = JsonConvert.SerializeObject( + OtherUtils.GenerateProtocolError( + message, + "unauthorized", + "Unauthorized access", + new Dictionary() + ) + ); + connInfo.ConnectionHandler.SendMessage(serializedMsg); + return; + } + + if (connInfo.LastTokenHash == "" || connInfo.LastTokenHash == null) + { + string hash; + using (SHA512 shaM = new SHA512Managed()) + { + hash = shaM.ComputeHash(message.AuthToken.ToByteArray()).ConvertToString(); + } + connInfo.LastTokenHash = hash; + // TODO implement comparing last token hash and if hash isn't changed then check payload + // if payload already exists - then skip validating auth token + } + + connInfo.LastTokenPayload = tokenPayload; + } + + Task.Run(() => + { + // probably need to wrap whole foreach body, not only HandleMessage call - need to investigate + h.HandleMessage(message); + }); + } } - public void AddC2SHandler(string messageType, IC2SMessageHandler handler) { - if (_c2sMessageHandlers.GetValueOrDefault(messageType, null) == null) { + public void RouteCoreEvent(CoreEvent coreEvent) + { + var handlers = _coreEventsHandlers[coreEvent.Name]; + if (handlers == null) { + Log.Warning($"Drop core event {coreEvent.Name} because server hasn't proper handlers"); + return; + } + foreach (var h in handlers) { + Task.Run(() => + { + h.HandleEvent(coreEvent); + }); + } + } + + public void AddC2SHandler(string messageType, IC2SMessageHandler handler) + { + if (_c2sMessageHandlers.GetValueOrDefault(messageType, null) == null) + { _c2sMessageHandlers[messageType] = new List(); } this._c2sMessageHandlers[messageType].Add(handler); } - public void AddCoreEventHandler(string eventType, ICoreEventHandler handler) { - if (_coreEventsHandlers.GetValueOrDefault(eventType, null) == null) { + public void AddCoreEventHandler(string eventType, ICoreEventHandler handler) + { + if (_coreEventsHandlers.GetValueOrDefault(eventType, null) == null) + { _coreEventsHandlers[eventType] = new List(); } this._coreEventsHandlers[eventType].Add(handler); diff --git a/src/Zirconium/Utils/OtherUtils.cs b/src/Zirconium/Utils/OtherUtils.cs new file mode 100644 index 0000000..25201b2 --- /dev/null +++ b/src/Zirconium/Utils/OtherUtils.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using Zirconium.Core.Models; + +namespace Zirconium.Utils +{ + public static class OtherUtils + { + public static BaseMessage GenerateProtocolError(BaseMessage parentMessage, string errCode, string errText, IDictionary errPayload) + { + ProtocolError err = new ProtocolError(); + err.ErrCode = errCode; + err.ErrText = errText; + err.ErrPayload = errPayload; + + BaseMessage msg = new BaseMessage(parentMessage, true); + msg.Ok = false; + msg.Payload = err.ToDictionary(); + return msg; + } + } +} \ No newline at end of file