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 Config Config;
public SessionManager SessionManager { get; }
public Router Router { get; }
public ModuleManager ModuleManager { get; }
public IHostModuleAPI HostModuleAPI { get; }
public AuthManager AuthManager { get; }
public App()
public App(Config config)
{
Config = config;
SessionManager = new SessionManager();
Router = new Router(this);
HostModuleAPI = new HostModuleAPI(Router);
HostModuleAPI = new HostModuleAPI(this, Router);
AuthManager = new AuthManager(this);
Log.Info("Zirconium is initialized successfully");
}

View File

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

View File

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

View File

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

View File

@ -1,12 +1,43 @@
using System;
using CommandLine;
using Nett;
using Zirconium.Core.Logging;
namespace Zirconium.Core
{
public class RunOptions
{
[Option('c', "config", Required = true, HelpText = "Set config path")]
public string ConfigPath { get; set; }
}
class Program
{
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 Newtonsoft.Json;
using Zirconium.Core.Models;
using Zirconium.Core.Modules.Interfaces;
@ -6,56 +7,58 @@ namespace Zirconium.Core.Modules
{
public class HostModuleAPI : IHostModuleAPI
{
private App _app;
private Router _router;
public HostModuleAPI(Router router)
public HostModuleAPI(App app, Router router)
{
_router = router;
_app = app;
}
public void FireEvent(CoreEvent coreEvent)
{
throw new NotImplementedException();
_router.RouteCoreEvent(coreEvent);
}
public string GenerateAuthToken(string entityID, string deviceID, int tokenExpirationMillis)
{
throw new NotImplementedException();
return _app.AuthManager.CreateToken(entityID, deviceID, tokenExpirationMillis);
}
public string[] GetServerDomains()
{
throw new NotImplementedException();
return _app.Config.ServerDomains;
}
public string GetServerID()
{
throw new NotImplementedException();
return _app.Config.ServerID;
}
public void Hook(IC2SMessageHandler handler)
{
throw new NotImplementedException();
_router.AddC2SHandler(handler.GetHandlingMessageType(), 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)
{
throw new NotImplementedException();
_router.RemoveC2SHandler(handler.GetHandlingMessageType(), 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
{
public interface IC2SMessageHandler {
void HandleMessage(BaseMessage message);
string GetHandlingMessageType();
void HandleMessage(ConnectionInfo connInfo, BaseMessage message);
bool IsAuthorizationRequired();
string GetHandlerUniqueID();
}

View File

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

View File

@ -12,6 +12,6 @@ namespace Zirconium.Core.Modules.Interfaces
string GenerateAuthToken(string entityID, string deviceID, int tokenExpirationMillis);
string[] GetServerDomains();
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())
{
JWTPayload tokenPayload;
try
string hash;
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);
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())
JWTPayload tokenPayload;
try
{
hash = shaM.ComputeHash(message.AuthToken.ToByteArray()).ConvertToString();
tokenPayload = _app.AuthManager.ValidateToken(message.AuthToken);
}
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
}
catch (Exception e)
{
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(() =>
{
{
// 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)
{
var handlers = _coreEventsHandlers[coreEvent.Name];
if (handlers == null) {
if (handlers == null)
{
Log.Warning($"Drop core event {coreEvent.Name} because server hasn't proper handlers");
return;
}
foreach (var h in handlers) {
foreach (var h in handlers)
{
Task.Run(() =>
{
h.HandleEvent(coreEvent);
@ -112,6 +111,13 @@ namespace Zirconium.Core
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)
{
if (_coreEventsHandlers.GetValueOrDefault(eventType, null) == null)
@ -120,5 +126,12 @@ namespace Zirconium.Core
}
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>
<ItemGroup>
<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="YamlDotNet" Version="8.1.2" />
<PackageReference Include="JWT" Version="7.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.0" />
<PackageReference Include="Nett" Version="0.15.0" />
</ItemGroup>
</Project>