Implement config parsing, implement removing c2s/core event handlers in router, slightly change C2S/Core Event Handler API, Host Module API

This commit is contained in:
ChronosX88 2020-07-15 22:08:32 +04:00
parent fc609869eb
commit 1996546a73
11 changed files with 111 additions and 59 deletions

View File

@ -6,17 +6,19 @@ namespace Zirconium.Core
{ {
public class App public class App
{ {
public Config Config;
public SessionManager SessionManager { get; } public SessionManager SessionManager { get; }
public Router Router { get; } public Router Router { get; }
public ModuleManager ModuleManager { get; } public ModuleManager ModuleManager { get; }
public IHostModuleAPI HostModuleAPI { get; } public IHostModuleAPI HostModuleAPI { get; }
public AuthManager AuthManager { get; } public AuthManager AuthManager { get; }
public App() public App(Config config)
{ {
Config = config;
SessionManager = new SessionManager(); SessionManager = new SessionManager();
Router = new Router(this); Router = new Router(this);
HostModuleAPI = new HostModuleAPI(Router); HostModuleAPI = new HostModuleAPI(this, Router);
AuthManager = new AuthManager(this); AuthManager = new AuthManager(this);
Log.Info("Zirconium is initialized successfully"); Log.Info("Zirconium is initialized successfully");
} }

View File

@ -10,7 +10,7 @@ namespace Zirconium.Core
{ {
private App _app; private App _app;
private string _secretString; private string _secretString;
private const int TOKEN_EXPIRATION_TIME_HOURS = 24; private const long DEFAULT_TOKEN_EXPIRATION_TIME_HOURS = 24 * 3600000;
public AuthManager(App app) public AuthManager(App app)
{ {
@ -18,7 +18,7 @@ namespace Zirconium.Core
_secretString = Guid.NewGuid().ToString(); _secretString = Guid.NewGuid().ToString();
} }
public string CreateToken(string entityID, string deviceID) public string CreateToken(string entityID, string deviceID, long tokenExpirationMillis)
{ {
JWTPayload payload = new JWTPayload(); JWTPayload payload = new JWTPayload();
payload.DeviceID = deviceID; payload.DeviceID = deviceID;
@ -26,11 +26,15 @@ namespace Zirconium.Core
return new JwtBuilder() return new JwtBuilder()
.WithAlgorithm(new HMACSHA256Algorithm()) // symmetric .WithAlgorithm(new HMACSHA256Algorithm()) // symmetric
.WithSecret(_secretString) .WithSecret(_secretString)
.AddClaim("exp", DateTimeOffset.UtcNow.AddHours(TOKEN_EXPIRATION_TIME_HOURS).ToUnixTimeSeconds()) .AddClaim("exp", DateTimeOffset.UtcNow.AddMilliseconds(tokenExpirationMillis).ToUnixTimeSeconds())
.AddClaims(payload.ToDictionary()) .AddClaims(payload.ToDictionary())
.Encode(); .Encode();
} }
public string CreateToken(string entityID, string deviceID) {
return CreateToken(entityID, deviceID, DEFAULT_TOKEN_EXPIRATION_TIME_HOURS);
}
public JWTPayload ValidateToken(string token) public JWTPayload ValidateToken(string token)
{ {
var jsonPayload = new JwtBuilder() var jsonPayload = new JwtBuilder()

View File

@ -1,23 +1,17 @@
using YamlDotNet.Serialization;
namespace Zirconium.Core namespace Zirconium.Core
{ {
public class Config public class Config
{ {
// A list of enabled plugins (or extensions) in server // A list of enabled plugins (or extensions) in server
[YamlMember(Alias = "enabledPlugins")]
public string[] EnabledPlugins {get; set;} public string[] EnabledPlugins {get; set;}
// Server domain names (e.g. example.com) // Server domain names (e.g. example.com)
[YamlMember(Alias = "serverDomains")]
public string[] ServerDomains {get; set;} public string[] ServerDomains {get; set;}
// Path to directory with plugin assemblies // Path to directory with plugin assemblies
[YamlMember(Alias = "pluginsDirPath")]
public string PluginsDirPath {get; set;} public string PluginsDirPath {get; set;}
// ID of this server in terms of Cadmium federation network // ID of this server in terms of Cadmium federation network
[YamlMember(Alias = "serverID")]
public string ServerID {get; set;} public string ServerID {get; set;}
} }
} }

View File

@ -1,3 +1,4 @@
using System.Threading.Tasks;
using System; using System;
using WebSocketSharp.Server; using WebSocketSharp.Server;
using WebSocketSharp; using WebSocketSharp;
@ -42,8 +43,9 @@ namespace Zirconium.Core
Console.WriteLine($"Connection {ID} was created"); // TODO implement normal logging Console.WriteLine($"Connection {ID} was created"); // TODO implement normal logging
} }
public void SendMessage(string message) { public Task SendMessage(string message) {
this.Send(message.ToByteArray()); this.Send(message.ToByteArray());
return Task.CompletedTask;
} }
} }
} }

View File

@ -1,12 +1,43 @@
using System; using System;
using CommandLine;
using Nett;
using Zirconium.Core.Logging;
namespace Zirconium.Core namespace Zirconium.Core
{ {
public class RunOptions
{
[Option('c', "config", Required = true, HelpText = "Set config path")]
public string ConfigPath { get; set; }
}
class Program class Program
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
App app = new App(); string configPath = null;
Parser.Default.ParseArguments<RunOptions>(args)
.WithParsed<RunOptions>(o =>
{
configPath = o.ConfigPath;
})
.WithNotParsed(errs =>
{
foreach (var err in errs)
{
Log.Error($"Error occured when parsing run options - {err.Tag}");
}
Environment.Exit(1);
});
Config config = null;
try
{
config = Toml.ReadFile<Config>(configPath);
}
catch (Exception e)
{
Log.Fatal($"Error occured when parsing config - {e.Message}");
}
App app = new App(config);
} }
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using Newtonsoft.Json;
using Zirconium.Core.Models; using Zirconium.Core.Models;
using Zirconium.Core.Modules.Interfaces; using Zirconium.Core.Modules.Interfaces;
@ -6,56 +7,58 @@ namespace Zirconium.Core.Modules
{ {
public class HostModuleAPI : IHostModuleAPI public class HostModuleAPI : IHostModuleAPI
{ {
private App _app;
private Router _router; private Router _router;
public HostModuleAPI(Router router) public HostModuleAPI(App app, Router router)
{ {
_router = router; _router = router;
_app = app;
} }
public void FireEvent(CoreEvent coreEvent) public void FireEvent(CoreEvent coreEvent)
{ {
throw new NotImplementedException(); _router.RouteCoreEvent(coreEvent);
} }
public string GenerateAuthToken(string entityID, string deviceID, int tokenExpirationMillis) public string GenerateAuthToken(string entityID, string deviceID, int tokenExpirationMillis)
{ {
throw new NotImplementedException(); return _app.AuthManager.CreateToken(entityID, deviceID, tokenExpirationMillis);
} }
public string[] GetServerDomains() public string[] GetServerDomains()
{ {
throw new NotImplementedException(); return _app.Config.ServerDomains;
} }
public string GetServerID() public string GetServerID()
{ {
throw new NotImplementedException(); return _app.Config.ServerID;
} }
public void Hook(IC2SMessageHandler handler) public void Hook(IC2SMessageHandler handler)
{ {
throw new NotImplementedException(); _router.AddC2SHandler(handler.GetHandlingMessageType(), handler);
} }
public void HookCoreEvent(ICoreEventHandler handler) public void HookCoreEvent(ICoreEventHandler handler)
{ {
throw new NotImplementedException(); _router.AddCoreEventHandler(handler.GetHandlingEventType(), handler);
} }
public void SendMessage(string connID, BaseMessage message) public void SendMessage(ConnectionInfo connInfo, BaseMessage message)
{ {
throw new NotImplementedException(); connInfo.ConnectionHandler.SendMessage(JsonConvert.SerializeObject(message));
} }
public void Unhook(IC2SMessageHandler handler) public void Unhook(IC2SMessageHandler handler)
{ {
throw new NotImplementedException(); _router.RemoveC2SHandler(handler.GetHandlingMessageType(), handler);
} }
public void UnhookCoreEvent(ICoreEventHandler handler) public void UnhookCoreEvent(ICoreEventHandler handler)
{ {
throw new NotImplementedException(); _router.RemoveCoreEventHandler(handler.GetHandlingEventType(), handler);
} }
} }
} }

View File

@ -3,7 +3,8 @@ using Zirconium.Core.Models;
namespace Zirconium.Core.Modules.Interfaces namespace Zirconium.Core.Modules.Interfaces
{ {
public interface IC2SMessageHandler { public interface IC2SMessageHandler {
void HandleMessage(BaseMessage message); string GetHandlingMessageType();
void HandleMessage(ConnectionInfo connInfo, BaseMessage message);
bool IsAuthorizationRequired(); bool IsAuthorizationRequired();
string GetHandlerUniqueID(); string GetHandlerUniqueID();
} }

View File

@ -4,6 +4,7 @@ namespace Zirconium.Core.Modules.Interfaces
{ {
public interface ICoreEventHandler public interface ICoreEventHandler
{ {
string GetHandlingEventType();
void HandleEvent(CoreEvent coreEvent); void HandleEvent(CoreEvent coreEvent);
string GetHandlerUniqueID(); string GetHandlerUniqueID();
} }

View File

@ -12,6 +12,6 @@ namespace Zirconium.Core.Modules.Interfaces
string GenerateAuthToken(string entityID, string deviceID, int tokenExpirationMillis); string GenerateAuthToken(string entityID, string deviceID, int tokenExpirationMillis);
string[] GetServerDomains(); string[] GetServerDomains();
string GetServerID(); string GetServerID();
void SendMessage(string connID, BaseMessage message); void SendMessage(ConnectionInfo connInfo, BaseMessage message);
} }
} }

View File

@ -44,46 +44,43 @@ namespace Zirconium.Core
{ {
if (h.IsAuthorizationRequired()) if (h.IsAuthorizationRequired())
{ {
JWTPayload tokenPayload; string hash;
try using (SHA512 shaM = new SHA512Managed())
{ {
tokenPayload = _app.AuthManager.ValidateToken(message.AuthToken); hash = shaM.ComputeHash(message.AuthToken.ToByteArray()).ConvertToString();
} }
catch (Exception e) if (connInfo.LastTokenHash != hash)
{ {
Log.Warning(e.Message); JWTPayload tokenPayload;
try
var serializedMsg = JsonConvert.SerializeObject(
OtherUtils.GenerateProtocolError(
message,
"unauthorized",
"Unauthorized access",
new Dictionary<string, object>()
)
);
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(); tokenPayload = _app.AuthManager.ValidateToken(message.AuthToken);
} }
connInfo.LastTokenHash = hash; catch (Exception e)
// TODO implement comparing last token hash and if hash isn't changed then check payload {
// if payload already exists - then skip validating auth token Log.Warning(e.Message);
}
connInfo.LastTokenPayload = tokenPayload; var serializedMsg = JsonConvert.SerializeObject(
OtherUtils.GenerateProtocolError(
message,
"unauthorized",
"Unauthorized access",
new Dictionary<string, object>()
)
);
connInfo.ConnectionHandler.SendMessage(serializedMsg);
return;
}
connInfo.LastTokenHash = hash;
connInfo.LastTokenPayload = tokenPayload;
}
} }
Task.Run(() => Task.Run(() =>
{ {
// probably need to wrap whole foreach body, not only HandleMessage call - need to investigate // probably need to wrap whole foreach body, not only HandleMessage call - need to investigate
h.HandleMessage(message); h.HandleMessage(connInfo, message);
}); });
} }
} }
@ -91,11 +88,13 @@ namespace Zirconium.Core
public void RouteCoreEvent(CoreEvent coreEvent) public void RouteCoreEvent(CoreEvent coreEvent)
{ {
var handlers = _coreEventsHandlers[coreEvent.Name]; var handlers = _coreEventsHandlers[coreEvent.Name];
if (handlers == null) { if (handlers == null)
{
Log.Warning($"Drop core event {coreEvent.Name} because server hasn't proper handlers"); Log.Warning($"Drop core event {coreEvent.Name} because server hasn't proper handlers");
return; return;
} }
foreach (var h in handlers) { foreach (var h in handlers)
{
Task.Run(() => Task.Run(() =>
{ {
h.HandleEvent(coreEvent); h.HandleEvent(coreEvent);
@ -112,6 +111,13 @@ namespace Zirconium.Core
this._c2sMessageHandlers[messageType].Add(handler); this._c2sMessageHandlers[messageType].Add(handler);
} }
public void RemoveC2SHandler(string messageType, IC2SMessageHandler handler)
{
if (!this._c2sMessageHandlers[messageType].Remove(handler)) {
Log.Warning("attempt to remove c2s handler which doesn't exist in router");
}
}
public void AddCoreEventHandler(string eventType, ICoreEventHandler handler) public void AddCoreEventHandler(string eventType, ICoreEventHandler handler)
{ {
if (_coreEventsHandlers.GetValueOrDefault(eventType, null) == null) if (_coreEventsHandlers.GetValueOrDefault(eventType, null) == null)
@ -120,5 +126,12 @@ namespace Zirconium.Core
} }
this._coreEventsHandlers[eventType].Add(handler); this._coreEventsHandlers[eventType].Add(handler);
} }
public void RemoveCoreEventHandler(string eventType, ICoreEventHandler handler)
{
if (!this._coreEventsHandlers[eventType].Remove(handler)) {
Log.Warning("attempt to remove core handler which doesn't exist in router");
}
}
} }
} }

View File

@ -5,10 +5,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Colorful.Console" Version="1.2.10" /> <PackageReference Include="Colorful.Console" Version="1.2.10" />
<PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="websocketsharp.core" Version="1.0.0" /> <PackageReference Include="websocketsharp.core" Version="1.0.0" />
<PackageReference Include="YamlDotNet" Version="8.1.2" />
<PackageReference Include="JWT" Version="7.2.1" /> <PackageReference Include="JWT" Version="7.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.0" /> <PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.0" />
<PackageReference Include="Nett" Version="0.15.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>