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:
2
MareAPI
2
MareAPI
Submodule MareAPI updated: 6645eaf63f...d361cfa3b9
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user