Refactoring Claims, add Server Side Messaging (#33)

* cache JWT by ApiUri, CharaIdent, SecretKey, refactor auth flow to new api

* add server side message handling

* update client to mainline api

Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
rootdarkarchon
2023-01-04 15:53:27 +01:00
committed by GitHub
parent 2404dc3f94
commit 9f5b4c189e
8 changed files with 92 additions and 36 deletions

Submodule MareAPI updated: 6645eaf63f...d361cfa3b9

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<Authors></Authors>
<Company></Company>
<Version>0.5.21</Version>
<Version>0.5.22</Version>
<Description></Description>
<Copyright></Copyright>
<PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl>

View File

@@ -14,7 +14,7 @@ using MareSynchronos.UI;
using MareSynchronos.Utils;
using Dalamud.Game.ClientState.Conditions;
using MareSynchronos.FileCache;
using Dalamud.Logging;
using Dalamud.Game.Gui;
namespace MareSynchronos;
@@ -23,6 +23,7 @@ public sealed class Plugin : IDalamudPlugin
private const string CommandName = "/mare";
private readonly ApiController _apiController;
private readonly CommandManager _commandManager;
private readonly ChatGui _chatGui;
private readonly Configuration _configuration;
private readonly PeriodicFileScanner _periodicFileScanner;
private readonly IntroUi _introUi;
@@ -44,7 +45,8 @@ public sealed class Plugin : IDalamudPlugin
public Plugin(DalamudPluginInterface pluginInterface, CommandManager commandManager,
Framework framework, ObjectTable objectTable, ClientState clientState, Condition condition)
Framework framework, ObjectTable objectTable, ClientState clientState, Condition condition,
ChatGui chatGui)
{
Logger.Debug("Launching " + Name);
_pluginInterface = pluginInterface;
@@ -59,7 +61,7 @@ public sealed class Plugin : IDalamudPlugin
_windowSystem = new WindowSystem("MareSynchronos");
// those can be initialized outside of game login
_dalamudUtil = new DalamudUtil(clientState, objectTable, framework, condition);
_dalamudUtil = new DalamudUtil(clientState, objectTable, framework, condition, chatGui);
_ipcManager = new IpcManager(_pluginInterface, _dalamudUtil);
_fileDialogManager = new FileDialogManager();

View File

@@ -494,7 +494,7 @@ public class CompactUi : Window, IDisposable
ImGui.AlignTextToFramePadding();
ImGui.TextColored(ImGuiColors.DalamudRed, "Not connected to any server");
}
if (printShard)
{
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().ItemSpacing.Y);
@@ -631,7 +631,7 @@ public class CompactUi : Window, IDisposable
return _apiController.ServerState switch
{
ServerState.Disconnected => "You are currently disconnected from the Mare Synchronos server.",
ServerState.Unauthorized => "The account associated with your used secret key is not present on the server anymore or you are tempbanned. To check the latter disconnect and reconnect in 15 minutes.",
ServerState.Unauthorized => "Server Response: " + _apiController.AuthFailureMessage,
ServerState.Offline => "Your selected Mare Synchronos server is currently offline.",
ServerState.VersionMisMatch =>
"Your plugin or the server you are connecting to is out of date. Please update your plugin now. If you already did so, contact the server provider to update their server to the latest version.",

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Game;
@@ -9,6 +8,8 @@ using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.Gui;
using Dalamud.Game.Text.SeStringHandling;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
@@ -29,6 +30,7 @@ public class DalamudUtil : IDisposable
private readonly ObjectTable _objectTable;
private readonly Framework _framework;
private readonly Condition _condition;
private readonly ChatGui _chatGui;
public event LogIn? LogIn;
public event LogOut? LogOut;
@@ -54,12 +56,13 @@ public class DalamudUtil : IDisposable
return false;
}
public DalamudUtil(ClientState clientState, ObjectTable objectTable, Framework framework, Condition condition)
public DalamudUtil(ClientState clientState, ObjectTable objectTable, Framework framework, Condition condition, ChatGui chatGui)
{
_clientState = clientState;
_objectTable = objectTable;
_framework = framework;
_condition = condition;
_chatGui = chatGui;
_clientState.Login += ClientStateOnLogin;
_clientState.Logout += ClientStateOnLogout;
_framework.Update += FrameworkOnUpdate;
@@ -70,6 +73,24 @@ public class DalamudUtil : IDisposable
}
}
public void PrintInfoChat(string message)
{
SeStringBuilder se = new SeStringBuilder().AddText("[Mare Synchronos] Info: ").AddItalics(message);
_chatGui.Print(se.BuiltString);
}
public void PrintWarnChat(string message)
{
SeStringBuilder se = new SeStringBuilder().AddText("[Mare Synchronos] ").AddUiForeground("Warning: " + message, 31).AddUiForegroundOff();
_chatGui.Print(se.BuiltString);
}
public void PrintErrorChat(string message)
{
SeStringBuilder se = new SeStringBuilder().AddText("[Mare Synchronos] ").AddUiForeground("Error: ", 534).AddItalicsOn().AddUiForeground(message, 534).AddUiForegroundOff().AddItalicsOff();
_chatGui.Print(se.BuiltString);
}
private void FrameworkOnUpdate(Framework framework)
{
if (_condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51] || IsInGpose)

View File

@@ -30,12 +30,24 @@ internal class Logger : ILogger
}
}
public static void Error(string msg, Exception ex)
{
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";
PluginLog.Error($"[{caller}] {msg} {Environment.NewLine} Exception: {ex.Message} {Environment.NewLine} {ex.StackTrace}");
}
public static void Warn(string msg, Exception ex)
{
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";
PluginLog.Warning($"[{caller}] {msg} {Environment.NewLine} Exception: {ex.Message} {Environment.NewLine} {ex.StackTrace}");
}
public static void Error(string msg)
{
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";
PluginLog.Error($"[{caller}] {msg}");
}
public static void Warn(string warn)
{
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";

View File

@@ -78,6 +78,12 @@ public partial class ApiController
_mareHub!.On(nameof(Client_AdminUpdateOrAddForbiddenFile), act);
}
public void OnReceiveServerMessage(Action<MessageSeverity, string> act)
{
if (_initialized) return;
_mareHub!.On(nameof(Client_ReceiveServerMessage), act);
}
public Task Client_UserUpdateClientPairs(ClientPairDto dto)
{
var entry = PairedClients.SingleOrDefault(e => string.Equals(e.OtherUID, dto.OtherUID, System.StringComparison.Ordinal));
@@ -213,4 +219,25 @@ public partial class ApiController
return Task.CompletedTask;
}
public Task Client_ReceiveServerMessage(MessageSeverity severity, string message)
{
switch (severity)
{
case MessageSeverity.Error:
Logger.Error(message);
_dalamudUtil.PrintErrorChat(message);
break;
case MessageSeverity.Warning:
Logger.Warn(message);
_dalamudUtil.PrintWarnChat(message);
break;
case MessageSeverity.Information:
Logger.Info(message);
_dalamudUtil.PrintInfoChat(message);
break;
}
return Task.CompletedTask;
}
}

View File

@@ -12,7 +12,6 @@ using MareSynchronos.FileCache;
using MareSynchronos.Utils;
using MareSynchronos.WebAPI.Utils;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Logging;
@@ -20,6 +19,8 @@ namespace MareSynchronos.WebAPI;
public delegate void SimpleStringDelegate(string str);
public record JwtCache(string ApiUrl, string CharaIdent, string SecretKey);
public partial class ApiController : IDisposable, IMareHubClient
{
public const string MainServer = "Lunae Crescere Incipientis (Central Server EU)";
@@ -31,8 +32,8 @@ public partial class ApiController : IDisposable, IMareHubClient
private readonly DalamudUtil _dalamudUtil;
private readonly FileCacheManager _fileDbManager;
private CancellationTokenSource _connectionCancellationTokenSource;
private Dictionary<string, string> _jwtToken = new(StringComparer.Ordinal);
private KeyValuePair<string, string> AuthorizationJwtHeader => new("Authorization", "Bearer " + _jwtToken[SecretKey]);
private Dictionary<JwtCache, string> _jwtToken = new();
private KeyValuePair<string, string> AuthorizationJwtHeader => new("Authorization", "Bearer " + _jwtToken.GetValueOrDefault(new JwtCache(ApiUri, _dalamudUtil.PlayerNameHashed, SecretKey), string.Empty));
private HubConnection? _mareHub;
@@ -41,6 +42,7 @@ public partial class ApiController : IDisposable, IMareHubClient
private ConnectionDto? _connectionDto;
public ServerInfoDto ServerInfo => _connectionDto?.ServerInfo ?? new ServerInfoDto();
public string AuthFailureMessage { get; private set; } = string.Empty;
public SystemInfoDto SystemInfoDto { get; private set; } = new();
public bool IsModerator => (_connectionDto?.IsAdmin ?? false) || (_connectionDto?.IsModerator ?? false);
@@ -167,15 +169,17 @@ public partial class ApiController : IDisposable, IMareHubClient
continue;
}
AuthFailureMessage = string.Empty;
await StopConnection(token).ConfigureAwait(false);
try
{
Logger.Debug("Building connection");
if (!_jwtToken.TryGetValue(SecretKey, out var jwtToken) || forceGetToken)
if (!_jwtToken.TryGetValue(new JwtCache(ApiUri, _dalamudUtil.PlayerNameHashed, SecretKey), out var jwtToken) || forceGetToken)
{
Logger.Debug("Requesting new JWT token");
Logger.Debug("Requesting new JWT");
using HttpClient httpClient = new();
var postUri = new Uri(new Uri(ApiUri
.Replace("wss://", "https://", StringComparison.OrdinalIgnoreCase)
@@ -184,11 +188,13 @@ public partial class ApiController : IDisposable, IMareHubClient
var auth = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(SecretKey))).Replace("-", "", StringComparison.OrdinalIgnoreCase);
var result = await httpClient.PostAsync(postUri, new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("auth", auth)
new KeyValuePair<string, string>("auth", auth),
new KeyValuePair<string, string>("charaIdent", _dalamudUtil.PlayerNameHashed)
})).ConfigureAwait(false);
AuthFailureMessage = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
result.EnsureSuccessStatusCode();
_jwtToken[SecretKey] = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
Logger.Debug("JWT Token Success");
_jwtToken[new JwtCache(ApiUri, _dalamudUtil.PlayerNameHashed, SecretKey)] = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
Logger.Debug("JWT Success");
}
while (!_dalamudUtil.IsPlayerPresent && !token.IsCancellationRequested)
@@ -203,9 +209,10 @@ public partial class ApiController : IDisposable, IMareHubClient
await _mareHub.StartAsync(token).ConfigureAwait(false);
OnReceiveServerMessage((sev, msg) => Client_ReceiveServerMessage(sev, msg));
OnUpdateSystemInfo((dto) => Client_UpdateSystemInfo(dto));
_connectionDto = await Heartbeat(_dalamudUtil.PlayerNameHashed).ConfigureAwait(false);
_connectionDto = await GetConnectionDto().ConfigureAwait(false);
ServerState = ServerState.Connected;
@@ -225,24 +232,6 @@ public partial class ApiController : IDisposable, IMareHubClient
_mareHub.Reconnected += MareHubOnReconnected;
}
}
catch (HubException ex)
{
if (ex.Message.Contains("unauthorized", StringComparison.OrdinalIgnoreCase))
{
Logger.Warn(ex.Message);
ServerState = ServerState.Unauthorized;
await StopConnection(token).ConfigureAwait(false);
return;
}
Logger.Warn(ex.GetType().ToString());
Logger.Warn(ex.Message);
Logger.Warn(ex.StackTrace ?? string.Empty);
ServerState = ServerState.RateLimited;
await StopConnection(token).ConfigureAwait(false);
return;
}
catch (HttpRequestException ex)
{
Logger.Warn(ex.GetType().ToString());
@@ -423,6 +412,11 @@ public partial class ApiController : IDisposable, IMareHubClient
return await _mareHub!.InvokeAsync<ConnectionDto>(nameof(Heartbeat), characterIdentification).ConfigureAwait(false);
}
public async Task<ConnectionDto> GetConnectionDto()
{
return await _mareHub!.InvokeAsync<ConnectionDto>(nameof(GetConnectionDto)).ConfigureAwait(false);
}
public async Task<bool> CheckClientHealth()
{
return await _mareHub!.InvokeAsync<bool>(nameof(CheckClientHealth)).ConfigureAwait(false);