Compare commits
10 Commits
1094445d40
...
63067869d8
| Author | SHA1 | Date | |
|---|---|---|---|
| 63067869d8 | |||
| e57d6b07db | |||
| 7b022dae05 | |||
|
|
9330a883a5 | ||
|
|
d2c90a332a | ||
|
|
c664ecbe26 | ||
|
|
282fb4f83a | ||
|
|
4326a134c6 | ||
|
|
2e7672c440 | ||
|
|
a4c760af25 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,6 +9,7 @@
|
|||||||
*.user
|
*.user
|
||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
|
*/.idea/*
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.userprefs
|
*.userprefs
|
||||||
|
|||||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
|||||||
[submodule "MareAPI"]
|
[submodule "MareAPI"]
|
||||||
path = MareAPI
|
path = MareAPI
|
||||||
url = https://github.com/Penumbra-Sync/api.git
|
url = https://git.namazu.gay/friendlynamazu/api.git
|
||||||
|
|||||||
@@ -46,18 +46,18 @@ public abstract class AuthControllerBase : Controller
|
|||||||
if (await IsIdentBanned(dbContext, charaIdent))
|
if (await IsIdentBanned(dbContext, charaIdent))
|
||||||
{
|
{
|
||||||
Logger.LogWarning("Authenticate:IDENTBAN:{id}:{ident}", authResult.Uid, charaIdent);
|
Logger.LogWarning("Authenticate:IDENTBAN:{id}:{ident}", authResult.Uid, charaIdent);
|
||||||
return Unauthorized("Your XIV service account is banned from using the service.");
|
return Unauthorized("Your character is banned from using the service.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!authResult.Success && !authResult.TempBan)
|
if (!authResult.Success && !authResult.TempBan)
|
||||||
{
|
{
|
||||||
Logger.LogWarning("Authenticate:INVALID:{id}:{ident}", authResult?.Uid ?? "NOUID", charaIdent);
|
Logger.LogWarning("Authenticate:INVALID:{id}:{ident}", authResult?.Uid ?? "NOUID", charaIdent);
|
||||||
return Unauthorized("The provided secret key is invalid. Verify your Mare accounts existence and/or recover the secret key.");
|
return Unauthorized("The provided secret key is invalid. Verify your Namazu accounts existence and/or recover the secret key.");
|
||||||
}
|
}
|
||||||
if (!authResult.Success && authResult.TempBan)
|
if (!authResult.Success && authResult.TempBan)
|
||||||
{
|
{
|
||||||
Logger.LogWarning("Authenticate:TEMPBAN:{id}:{ident}", authResult.Uid ?? "NOUID", charaIdent);
|
Logger.LogWarning("Authenticate:TEMPBAN:{id}:{ident}", authResult.Uid ?? "NOUID", charaIdent);
|
||||||
return Unauthorized("Due to an excessive amount of failed authentication attempts you are temporarily banned. Check your Secret Key configuration and try connecting again in 5 minutes.");
|
return Unauthorized("Due to an excessive amount of failed authentication attempts you are temporarily locked out. Check your Secret Key configuration and try connecting again in 5 minutes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authResult.Permaban || authResult.MarkedForBan)
|
if (authResult.Permaban || authResult.MarkedForBan)
|
||||||
@@ -69,14 +69,14 @@ public abstract class AuthControllerBase : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogWarning("Authenticate:UIDBAN:{id}:{ident}", authResult.Uid, charaIdent);
|
Logger.LogWarning("Authenticate:UIDBAN:{id}:{ident}", authResult.Uid, charaIdent);
|
||||||
return Unauthorized("Your Mare account is banned from using the service.");
|
return Unauthorized("Your Namazu account is banned from using the service.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var existingIdent = await _redis.StringGetAsync("UID:" + authResult.Uid);
|
var existingIdent = await _redis.StringGetAsync("UID:" + authResult.Uid);
|
||||||
if (!string.IsNullOrEmpty(existingIdent))
|
if (!string.IsNullOrEmpty(existingIdent))
|
||||||
{
|
{
|
||||||
Logger.LogWarning("Authenticate:DUPLICATE:{id}:{ident}", authResult.Uid, charaIdent);
|
Logger.LogWarning("Authenticate:DUPLICATE:{id}:{ident}", authResult.Uid, charaIdent);
|
||||||
return Unauthorized("Already logged in to this Mare account. Reconnect in 60 seconds. If you keep seeing this issue, restart your game.");
|
return Unauthorized("Already logged in to this Namazu account. Reconnect in 60 seconds. If you keep seeing this issue, restart your game.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogInformation("Authenticate:SUCCESS:{id}:{ident}", authResult.Uid, charaIdent);
|
Logger.LogInformation("Authenticate:SUCCESS:{id}:{ident}", authResult.Uid, charaIdent);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public class JwtController : AuthControllerBase
|
|||||||
var userAuth = await dbContext.Auth.SingleAsync(u => u.UserUID == uid);
|
var userAuth = await dbContext.Auth.SingleAsync(u => u.UserUID == uid);
|
||||||
await EnsureBan(uid, userAuth.PrimaryUserUID, ident);
|
await EnsureBan(uid, userAuth.PrimaryUserUID, ident);
|
||||||
|
|
||||||
return Unauthorized("Your Mare account is banned.");
|
return Unauthorized("Your Namazu account is banned.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await IsIdentBanned(dbContext, ident))
|
if (await IsIdentBanned(dbContext, ident))
|
||||||
|
|||||||
@@ -147,9 +147,9 @@ public class OAuthController : AuthControllerBase
|
|||||||
var mareUser = await dbContext.LodeStoneAuth.Include(u => u.User).SingleOrDefaultAsync(u => u.DiscordId == discordUserId);
|
var mareUser = await dbContext.LodeStoneAuth.Include(u => u.User).SingleOrDefaultAsync(u => u.DiscordId == discordUserId);
|
||||||
if (mareUser == default)
|
if (mareUser == default)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Failed to get Mare user for {session}, DiscordId: {id}", reqId, discordUserId);
|
Logger.LogDebug("Failed to get Namazu user for {session}, DiscordId: {id}", reqId, discordUserId);
|
||||||
|
|
||||||
return BadRequest("Could not find a Mare user associated to this Discord account.");
|
return BadRequest("Could not find a Namazu user associated to this Discord account.");
|
||||||
}
|
}
|
||||||
|
|
||||||
JwtSecurityToken? jwt = null;
|
JwtSecurityToken? jwt = null;
|
||||||
|
|||||||
@@ -36,10 +36,6 @@ Global
|
|||||||
{326BFB1B-5571-47A6-8513-1FFDB32D53B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{326BFB1B-5571-47A6-8513-1FFDB32D53B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{326BFB1B-5571-47A6-8513-1FFDB32D53B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{326BFB1B-5571-47A6-8513-1FFDB32D53B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{326BFB1B-5571-47A6-8513-1FFDB32D53B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
{326BFB1B-5571-47A6-8513-1FFDB32D53B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{25A82A2A-35C2-4EE0-A0E8-DFDD77978DDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{25A82A2A-35C2-4EE0-A0E8-DFDD77978DDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{25A82A2A-35C2-4EE0-A0E8-DFDD77978DDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{25A82A2A-35C2-4EE0-A0E8-DFDD77978DDA}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{67B1461D-E215-4BA8-A64D-E1836724D5E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{67B1461D-E215-4BA8-A64D-E1836724D5E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{67B1461D-E215-4BA8-A64D-E1836724D5E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{67B1461D-E215-4BA8-A64D-E1836724D5E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{67B1461D-E215-4BA8-A64D-E1836724D5E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{67B1461D-E215-4BA8-A64D-E1836724D5E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
using MareSynchronosShared.Services;
|
using MareSynchronosShared.Services;
|
||||||
using MareSynchronosShared.Utils.Configuration;
|
using MareSynchronosShared.Utils.Configuration;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using System.Threading.RateLimiting;
|
||||||
|
|
||||||
namespace MareSynchronosServer.Hubs;
|
namespace MareSynchronosServer.Hubs;
|
||||||
|
|
||||||
public sealed class ConcurrencyFilter : IHubFilter, IDisposable
|
public sealed class ConcurrencyFilter : IHubFilter, IDisposable
|
||||||
{
|
{
|
||||||
private SemaphoreSlim _limiter;
|
private ConcurrencyLimiter _limiter;
|
||||||
private int _setLimit = 0;
|
private int _setLimit = 0;
|
||||||
private readonly IConfigurationService<ServerConfiguration> _config;
|
private readonly IConfigurationService<ServerConfiguration> _config;
|
||||||
private readonly CancellationTokenSource _cts = new();
|
private readonly CancellationTokenSource _cts = new();
|
||||||
@@ -19,14 +20,19 @@ public sealed class ConcurrencyFilter : IHubFilter, IDisposable
|
|||||||
_config = config;
|
_config = config;
|
||||||
_config.ConfigChangedEvent += OnConfigChange;
|
_config.ConfigChangedEvent += OnConfigChange;
|
||||||
|
|
||||||
RecreateSemaphore();
|
RecreateLimiter();
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var token = _cts.Token;
|
var token = _cts.Token;
|
||||||
while (!token.IsCancellationRequested)
|
while (!token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
mareMetrics.SetGaugeTo(MetricsAPI.GaugeHubConcurrency, _limiter?.CurrentCount ?? 0);
|
var stats = _limiter?.GetStatistics();
|
||||||
|
if (stats != null)
|
||||||
|
{
|
||||||
|
mareMetrics.SetGaugeTo(MetricsAPI.GaugeHubConcurrency, stats.CurrentAvailablePermits);
|
||||||
|
mareMetrics.SetGaugeTo(MetricsAPI.GaugeHubQueuedConcurrency, stats.CurrentQueuedCount);
|
||||||
|
}
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
|
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -34,18 +40,26 @@ public sealed class ConcurrencyFilter : IHubFilter, IDisposable
|
|||||||
|
|
||||||
private void OnConfigChange(object sender, EventArgs e)
|
private void OnConfigChange(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
RecreateSemaphore();
|
RecreateLimiter();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RecreateSemaphore()
|
private void RecreateLimiter()
|
||||||
{
|
{
|
||||||
var newLimit = _config.GetValueOrDefault(nameof(ServerConfiguration.HubExecutionConcurrencyFilter), 50);
|
var newLimit = _config.GetValueOrDefault(nameof(ServerConfiguration.HubExecutionConcurrencyFilter), 50);
|
||||||
if (newLimit != _setLimit)
|
|
||||||
|
if (newLimit == _setLimit && _limiter is not null)
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_setLimit = newLimit;
|
_setLimit = newLimit;
|
||||||
_limiter?.Dispose();
|
_limiter?.Dispose();
|
||||||
_limiter = new(initialCount: _setLimit, maxCount: _setLimit);
|
_limiter = new(new ConcurrencyLimiterOptions()
|
||||||
}
|
{
|
||||||
|
PermitLimit = newLimit,
|
||||||
|
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
|
||||||
|
QueueLimit = newLimit * 100,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask<object> InvokeMethodAsync(
|
public async ValueTask<object> InvokeMethodAsync(
|
||||||
@@ -56,15 +70,25 @@ public sealed class ConcurrencyFilter : IHubFilter, IDisposable
|
|||||||
return await next(invocationContext).ConfigureAwait(false);
|
return await next(invocationContext).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _limiter.WaitAsync(invocationContext.Context.ConnectionAborted).ConfigureAwait(false);
|
var ct = invocationContext.Context.ConnectionAborted;
|
||||||
|
RateLimitLease lease;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await next(invocationContext).ConfigureAwait(false);
|
lease = await _limiter.AcquireAsync(1, ct).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
catch (OperationCanceledException) when (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
_limiter.Release();
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lease.IsAcquired)
|
||||||
|
{
|
||||||
|
throw new HubException("Concurrency limit exceeded. Try again later.");
|
||||||
|
}
|
||||||
|
|
||||||
|
using (lease)
|
||||||
|
{
|
||||||
|
return await next(invocationContext).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +101,8 @@ public sealed class ConcurrencyFilter : IHubFilter, IDisposable
|
|||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
_cts.Cancel();
|
_cts.Cancel();
|
||||||
|
_limiter?.Dispose();
|
||||||
_config.ConfigChangedEvent -= OnConfigChange;
|
_config.ConfigChangedEvent -= OnConfigChange;
|
||||||
|
_cts.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,11 +159,11 @@ public partial class MareHub
|
|||||||
}
|
}
|
||||||
|
|
||||||
var gid = StringUtils.GenerateRandomString(12);
|
var gid = StringUtils.GenerateRandomString(12);
|
||||||
while (await DbContext.Groups.AnyAsync(g => g.GID == "MSS-" + gid).ConfigureAwait(false))
|
while (await DbContext.Groups.AnyAsync(g => g.GID == "NSS-" + gid).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
gid = StringUtils.GenerateRandomString(12);
|
gid = StringUtils.GenerateRandomString(12);
|
||||||
}
|
}
|
||||||
gid = "MSS-" + gid;
|
gid = "NSS-" + gid;
|
||||||
|
|
||||||
var passwd = StringUtils.GenerateRandomString(16);
|
var passwd = StringUtils.GenerateRandomString(16);
|
||||||
using var sha = SHA256.Create();
|
using var sha = SHA256.Create();
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
|
|||||||
var dbUser = await DbContext.Users.SingleAsync(f => f.UID == UserUID).ConfigureAwait(false);
|
var dbUser = await DbContext.Users.SingleAsync(f => f.UID == UserUID).ConfigureAwait(false);
|
||||||
dbUser.LastLoggedIn = DateTime.UtcNow;
|
dbUser.LastLoggedIn = DateTime.UtcNow;
|
||||||
|
|
||||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Information, "Welcome to Mare Synchronos \"" + _shardName + "\", Current Online Users: " + _systemInfoService.SystemInfoDto.OnlineUsers).ConfigureAwait(false);
|
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Information, "Welcome to Namazu Sync \"" + _shardName + "\", Current Online Users: " + _systemInfoService.SystemInfoDto.OnlineUsers).ConfigureAwait(false);
|
||||||
|
|
||||||
var defaultPermissions = await DbContext.UserDefaultPreferredPermissions.SingleOrDefaultAsync(u => u.UserUID == UserUID).ConfigureAwait(false);
|
var defaultPermissions = await DbContext.UserDefaultPreferredPermissions.SingleOrDefaultAsync(u => u.UserUID == UserUID).ConfigureAwait(false);
|
||||||
if (defaultPermissions == null)
|
if (defaultPermissions == null)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public class SignalRLimitFilter : IHubFilter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Optional method
|
// Optional method
|
||||||
public async Task OnConnectedAsync(HubLifetimeContext context, Func<HubLifetimeContext, Task> next)
|
/* public async Task OnConnectedAsync(HubLifetimeContext context, Func<HubLifetimeContext, Task> next)
|
||||||
{
|
{
|
||||||
await ConnectionLimiterSemaphore.WaitAsync().ConfigureAwait(false);
|
await ConnectionLimiterSemaphore.WaitAsync().ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
@@ -108,5 +108,5 @@ public class SignalRLimitFilter : IHubFilter
|
|||||||
{
|
{
|
||||||
DisconnectLimiterSemaphore.Release();
|
DisconnectLimiterSemaphore.Release();
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.0" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.6" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -303,7 +303,8 @@ public class Startup
|
|||||||
MetricsAPI.GaugeUserPairCacheUsers,
|
MetricsAPI.GaugeUserPairCacheUsers,
|
||||||
MetricsAPI.GaugeGposeLobbies,
|
MetricsAPI.GaugeGposeLobbies,
|
||||||
MetricsAPI.GaugeGposeLobbyUsers,
|
MetricsAPI.GaugeGposeLobbyUsers,
|
||||||
MetricsAPI.GaugeHubConcurrency
|
MetricsAPI.GaugeHubConcurrency,
|
||||||
|
MetricsAPI.GaugeHubQueuedConcurrency,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -184,9 +184,9 @@ internal class DiscordBot : IHostedService
|
|||||||
private async Task GenerateOrUpdateWizardMessage(SocketTextChannel channel, IUserMessage? prevMessage)
|
private async Task GenerateOrUpdateWizardMessage(SocketTextChannel channel, IUserMessage? prevMessage)
|
||||||
{
|
{
|
||||||
EmbedBuilder eb = new EmbedBuilder();
|
EmbedBuilder eb = new EmbedBuilder();
|
||||||
eb.WithTitle("Mare Services Bot Interaction Service");
|
eb.WithTitle("Namazu Services Bot Interaction Service");
|
||||||
eb.WithDescription("Press \"Start\" to interact with this bot!" + Environment.NewLine + Environment.NewLine
|
eb.WithDescription("Press \"Start\" to interact with this bot!" + Environment.NewLine + Environment.NewLine
|
||||||
+ "You can handle all of your Mare account needs in this server through the easy to use interactive bot prompt. Just follow the instructions!");
|
+ "You can handle all of your Namazu account needs in this server through the easy to use interactive bot prompt. Just follow the instructions!");
|
||||||
eb.WithThumbnailUrl("https://raw.githubusercontent.com/Penumbra-Sync/repo/main/MareSynchronos/images/icon.png");
|
eb.WithThumbnailUrl("https://raw.githubusercontent.com/Penumbra-Sync/repo/main/MareSynchronos/images/icon.png");
|
||||||
var cb = new ComponentBuilder();
|
var cb = new ComponentBuilder();
|
||||||
cb.WithButton("Start", style: ButtonStyle.Primary, customId: "wizard-captcha:true", emote: Emoji.Parse("➡️"));
|
cb.WithButton("Start", style: ButtonStyle.Primary, customId: "wizard-captcha:true", emote: Emoji.Parse("➡️"));
|
||||||
@@ -427,7 +427,7 @@ internal class DiscordBot : IHostedService
|
|||||||
var onlineUsers = await _connectionMultiplexer.GetServer(endPoint).KeysAsync(pattern: "UID:*").CountAsync().ConfigureAwait(false);
|
var onlineUsers = await _connectionMultiplexer.GetServer(endPoint).KeysAsync(pattern: "UID:*").CountAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
_logger.LogInformation("Users online: " + onlineUsers);
|
_logger.LogInformation("Users online: " + onlineUsers);
|
||||||
await _discordClient.SetActivityAsync(new Game("Mare for " + onlineUsers + " Users")).ConfigureAwait(false);
|
await _discordClient.SetActivityAsync(new Game("Namazu for " + onlineUsers + " Users")).ConfigureAwait(false);
|
||||||
await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
|
await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ public class MareModule : InteractionModuleBase
|
|||||||
if (primaryUser == null)
|
if (primaryUser == null)
|
||||||
{
|
{
|
||||||
eb.WithTitle("No account");
|
eb.WithTitle("No account");
|
||||||
eb.WithDescription("No Mare account was found associated to your Discord user");
|
eb.WithDescription("No Namazu account was found associated to your Discord user");
|
||||||
return eb;
|
return eb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ public class MareModule : InteractionModuleBase
|
|||||||
if (userInDb == null)
|
if (userInDb == null)
|
||||||
{
|
{
|
||||||
eb.WithTitle("No account");
|
eb.WithTitle("No account");
|
||||||
eb.WithDescription("The Discord user has no valid Mare account");
|
eb.WithDescription("The Discord user has no valid Namazu account");
|
||||||
return eb;
|
return eb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public partial class MareWizardModule
|
|||||||
eb.WithColor(Color.Blue);
|
eb.WithColor(Color.Blue);
|
||||||
eb.WithTitle("Recover");
|
eb.WithTitle("Recover");
|
||||||
eb.WithDescription("In case you have lost your secret key you can recover it here." + Environment.NewLine + Environment.NewLine
|
eb.WithDescription("In case you have lost your secret key you can recover it here." + Environment.NewLine + Environment.NewLine
|
||||||
+ "## ⚠️ **Once you recover your key, the previously used key will be invalidated. If you use Mare on multiple devices you will have to update the key everywhere you use it.** ⚠️" + Environment.NewLine + Environment.NewLine
|
+ "## ⚠️ **Once you recover your key, the previously used key will be invalidated. If you use Namazu on multiple devices you will have to update the key everywhere you use it.** ⚠️" + Environment.NewLine + Environment.NewLine
|
||||||
+ "Use the selection below to select the user account you want to recover." + Environment.NewLine + Environment.NewLine
|
+ "Use the selection below to select the user account you want to recover." + Environment.NewLine + Environment.NewLine
|
||||||
+ "- 1️⃣ is your primary account/UID" + Environment.NewLine
|
+ "- 1️⃣ is your primary account/UID" + Environment.NewLine
|
||||||
+ "- 2️⃣ are all your secondary accounts/UIDs" + Environment.NewLine
|
+ "- 2️⃣ are all your secondary accounts/UIDs" + Environment.NewLine
|
||||||
@@ -79,7 +79,7 @@ public partial class MareWizardModule
|
|||||||
+ Environment.NewLine
|
+ Environment.NewLine
|
||||||
+ "__NOTE: The Secret Key only contains the letters ABCDEF and numbers 0 - 9.__"
|
+ "__NOTE: The Secret Key only contains the letters ABCDEF and numbers 0 - 9.__"
|
||||||
+ Environment.NewLine + Environment.NewLine
|
+ Environment.NewLine + Environment.NewLine
|
||||||
+ "Enter this key in the Mare Synchronos Service Settings and reconnect to the service.");
|
+ "Enter this key in the Namazu Sync Service Settings and reconnect to the service.");
|
||||||
|
|
||||||
await db.Auth.AddAsync(auth).ConfigureAwait(false);
|
await db.Auth.AddAsync(auth).ConfigureAwait(false);
|
||||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|||||||
@@ -21,16 +21,44 @@ public partial class MareWizardModule
|
|||||||
_logger.LogInformation("{method}:{userId}", nameof(ComponentRegister), Context.Interaction.User.Id);
|
_logger.LogInformation("{method}:{userId}", nameof(ComponentRegister), Context.Interaction.User.Id);
|
||||||
|
|
||||||
EmbedBuilder eb = new();
|
EmbedBuilder eb = new();
|
||||||
eb.WithColor(Color.Blue);
|
// eb.WithColor(Color.Blue);
|
||||||
eb.WithTitle("Start Registration");
|
// eb.WithTitle("Start Registration");
|
||||||
eb.WithDescription("Here you can start the registration process with the Mare Synchronos server of this Discord." + Environment.NewLine + Environment.NewLine
|
// eb.WithDescription("Here you can start the registration process with the Namazu Sync server of this Discord." + Environment.NewLine + Environment.NewLine
|
||||||
+ "- Have your Lodestone URL ready (i.e. https://eu.finalfantasyxiv.com/lodestone/character/XXXXXXXXX)" + Environment.NewLine
|
// + "- Have your Lodestone URL ready (i.e. https://eu.finalfantasyxiv.com/lodestone/character/XXXXXXXXX)" + Environment.NewLine
|
||||||
+ " - The registration requires you to modify your Lodestone profile with a generated code for verification" + Environment.NewLine
|
// + " - The registration requires you to modify your Lodestone profile with a generated code for verification" + Environment.NewLine
|
||||||
+ "- Do not use this on mobile because you will need to be able to copy the generated secret key" + Environment.NewLine
|
// + "- Do not use this on mobile because you will need to be able to copy the generated secret key" + Environment.NewLine
|
||||||
+ "# Follow the bot instructions precisely. Slow down and read.");
|
// + "# Follow the bot instructions precisely. Slow down and read.");
|
||||||
|
// ComponentBuilder cb = new();
|
||||||
|
// AddHome(cb);
|
||||||
|
// cb.WithButton("Start Registration", "wizard-register-start", ButtonStyle.Primary, emote: new Emoji("🌒"));
|
||||||
|
|
||||||
|
var registerSuccess = false;
|
||||||
|
|
||||||
|
eb.WithColor(Color.Green);
|
||||||
|
using var db = await GetDbContext().ConfigureAwait(false);
|
||||||
|
string lodestoneAuth = await GenerateLodestoneAuth(Context.User.Id, null, db).ConfigureAwait(false);
|
||||||
|
var (uid, key) = await HandleAddUser(db).ConfigureAwait(false);
|
||||||
|
eb.WithTitle($"Registration successful, your UID: {uid}");
|
||||||
|
eb.WithDescription("This is your private secret key. Do not share this private secret key with anyone. **If you lose it, it is irrevocably lost.**"
|
||||||
|
+ Environment.NewLine + Environment.NewLine
|
||||||
|
+ $"**{key}**"
|
||||||
|
+ Environment.NewLine + Environment.NewLine
|
||||||
|
+ "Enter this key in Namazu Sync and hit save to connect to the service."
|
||||||
|
+ Environment.NewLine
|
||||||
|
+ "__NOTE: The Secret Key only contains the letters ABCDEF and numbers 0 - 9.__"
|
||||||
|
+ Environment.NewLine
|
||||||
|
+ " __NOTE: Secret keys are considered legacy. Using the suggested OAuth2 authentication, you do not need to use this Secret Key.__"
|
||||||
|
+ Environment.NewLine
|
||||||
|
+ "You should connect as soon as possible to not get caught by the automatic cleanup process."
|
||||||
|
+ Environment.NewLine
|
||||||
|
+ "Have fun."
|
||||||
|
);
|
||||||
ComponentBuilder cb = new();
|
ComponentBuilder cb = new();
|
||||||
AddHome(cb);
|
AddHome(cb);
|
||||||
cb.WithButton("Start Registration", "wizard-register-start", ButtonStyle.Primary, emote: new Emoji("🌒"));
|
if (registerSuccess) {
|
||||||
|
await _botServices.AddRegisteredRoleAsync(Context.Interaction.User).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
await ModifyInteraction(eb, cb).ConfigureAwait(false);
|
await ModifyInteraction(eb, cb).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,14 +161,14 @@ public partial class MareWizardModule
|
|||||||
eb.WithTitle($"Registration successful, your UID: {uid}");
|
eb.WithTitle($"Registration successful, your UID: {uid}");
|
||||||
eb.WithDescription("This is your private secret key. Do not share this private secret key with anyone. **If you lose it, it is irrevocably lost.**"
|
eb.WithDescription("This is your private secret key. Do not share this private secret key with anyone. **If you lose it, it is irrevocably lost.**"
|
||||||
+ Environment.NewLine + Environment.NewLine
|
+ Environment.NewLine + Environment.NewLine
|
||||||
+ "**__NOTE: Secret keys are considered legacy. Using the suggested OAuth2 authentication in Mare, you do not need to use this Secret Key.__**"
|
+ $"**{key}**"
|
||||||
+ Environment.NewLine + Environment.NewLine
|
+ Environment.NewLine + Environment.NewLine
|
||||||
+ $"||**`{key}`**||"
|
+ "Enter this key in Namazu Sync and hit save to connect to the service."
|
||||||
+ Environment.NewLine + Environment.NewLine
|
|
||||||
+ "If you want to continue using legacy authentication, enter this key in Mare Synchronos and hit save to connect to the service."
|
|
||||||
+ Environment.NewLine
|
+ Environment.NewLine
|
||||||
+ "__NOTE: The Secret Key only contains the letters ABCDEF and numbers 0 - 9.__"
|
+ "__NOTE: The Secret Key only contains the letters ABCDEF and numbers 0 - 9.__"
|
||||||
+ Environment.NewLine
|
+ Environment.NewLine
|
||||||
|
+ " __NOTE: Secret keys are considered legacy. Using the suggested OAuth2 authentication, you do not need to use this Secret Key.__"
|
||||||
|
+ Environment.NewLine
|
||||||
+ "You should connect as soon as possible to not get caught by the automatic cleanup process."
|
+ "You should connect as soon as possible to not get caught by the automatic cleanup process."
|
||||||
+ Environment.NewLine
|
+ Environment.NewLine
|
||||||
+ "Have fun.");
|
+ "Have fun.");
|
||||||
@@ -159,9 +187,9 @@ public partial class MareWizardModule
|
|||||||
+ Environment.NewLine + Environment.NewLine
|
+ Environment.NewLine + Environment.NewLine
|
||||||
+ "**Make sure your profile is set to public (All Users) for your character. The bot cannot read profiles with privacy settings set to \"logged in\" or \"private\".**"
|
+ "**Make sure your profile is set to public (All Users) for your character. The bot cannot read profiles with privacy settings set to \"logged in\" or \"private\".**"
|
||||||
+ Environment.NewLine + Environment.NewLine
|
+ Environment.NewLine + Environment.NewLine
|
||||||
+ "## You __need__ to enter following the code this bot provided onto your Lodestone in the character profile:"
|
+ "## You __need__ to enter following the code this bot provided onto your lodestone in the character profile:"
|
||||||
+ Environment.NewLine + Environment.NewLine
|
+ Environment.NewLine + Environment.NewLine
|
||||||
+ "**`" + verificationCode + "`**");
|
+ "**" + verificationCode + "**");
|
||||||
cb.WithButton("Cancel", "wizard-register", emote: new Emoji("❌"));
|
cb.WithButton("Cancel", "wizard-register", emote: new Emoji("❌"));
|
||||||
cb.WithButton("Retry", "wizard-register-verify:" + verificationCode, ButtonStyle.Primary, emote: new Emoji("🔁"));
|
cb.WithButton("Retry", "wizard-register-verify:" + verificationCode, ButtonStyle.Primary, emote: new Emoji("🔁"));
|
||||||
}
|
}
|
||||||
@@ -209,11 +237,11 @@ public partial class MareWizardModule
|
|||||||
+ Environment.NewLine
|
+ Environment.NewLine
|
||||||
+ "__NOTE: If the link does not lead you to your character edit profile page, you need to log in and set up your privacy settings!__"
|
+ "__NOTE: If the link does not lead you to your character edit profile page, you need to log in and set up your privacy settings!__"
|
||||||
+ Environment.NewLine + Environment.NewLine
|
+ Environment.NewLine + Environment.NewLine
|
||||||
+ $"**`{lodestoneAuth}`**"
|
+ $"**{lodestoneAuth}**"
|
||||||
+ Environment.NewLine + Environment.NewLine
|
+ Environment.NewLine + Environment.NewLine
|
||||||
+ $"**! THIS IS NOT THE KEY YOU HAVE TO ENTER IN MARE !**"
|
+ $"**! THIS IS NOT THE KEY YOU HAVE TO ENTER IN NAMAZU !**"
|
||||||
+ Environment.NewLine + Environment.NewLine
|
+ Environment.NewLine + Environment.NewLine
|
||||||
+ "Once added and saved, use the button below to Verify and finish registration and receive a secret key to use for Mare Synchronos."
|
+ "Once added and saved, use the button below to Verify and finish registration and receive a secret key to use for Namazu Sync."
|
||||||
+ Environment.NewLine
|
+ Environment.NewLine
|
||||||
+ "__You can delete the entry from your profile after verification.__"
|
+ "__You can delete the entry from your profile after verification.__"
|
||||||
+ Environment.NewLine + Environment.NewLine
|
+ Environment.NewLine + Environment.NewLine
|
||||||
|
|||||||
@@ -1,281 +0,0 @@
|
|||||||
using Discord.Interactions;
|
|
||||||
using Discord;
|
|
||||||
using MareSynchronosShared.Data;
|
|
||||||
using MareSynchronosShared.Utils;
|
|
||||||
using MareSynchronosShared.Models;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace MareSynchronosServices.Discord;
|
|
||||||
|
|
||||||
public partial class MareWizardModule
|
|
||||||
{
|
|
||||||
[ComponentInteraction("wizard-relink")]
|
|
||||||
public async Task ComponentRelink()
|
|
||||||
{
|
|
||||||
if (!(await ValidateInteraction().ConfigureAwait(false))) return;
|
|
||||||
|
|
||||||
_logger.LogInformation("{method}:{userId}", nameof(ComponentRelink), Context.Interaction.User.Id);
|
|
||||||
|
|
||||||
EmbedBuilder eb = new();
|
|
||||||
eb.WithTitle("Relink");
|
|
||||||
eb.WithColor(Color.Blue);
|
|
||||||
eb.WithDescription("Use this in case you already have a registered Mare account, but lost access to your previous Discord account." + Environment.NewLine + Environment.NewLine
|
|
||||||
+ "- Have your original registered Lodestone URL ready (i.e. https://eu.finalfantasyxiv.com/lodestone/character/XXXXXXXXX)" + Environment.NewLine
|
|
||||||
+ " - The relink process requires you to modify your Lodestone profile with a generated code for verification" + Environment.NewLine
|
|
||||||
+ "- Do not use this on mobile because you will need to be able to copy the generated secret key");
|
|
||||||
ComponentBuilder cb = new();
|
|
||||||
AddHome(cb);
|
|
||||||
cb.WithButton("Start Relink", "wizard-relink-start", ButtonStyle.Primary, emote: new Emoji("🔗"));
|
|
||||||
await ModifyInteraction(eb, cb).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[ComponentInteraction("wizard-relink-start")]
|
|
||||||
public async Task ComponentRelinkStart()
|
|
||||||
{
|
|
||||||
if (!(await ValidateInteraction().ConfigureAwait(false))) return;
|
|
||||||
|
|
||||||
_logger.LogInformation("{method}:{userId}", nameof(ComponentRelinkStart), Context.Interaction.User.Id);
|
|
||||||
|
|
||||||
using var db = await GetDbContext().ConfigureAwait(false);
|
|
||||||
db.LodeStoneAuth.RemoveRange(db.LodeStoneAuth.Where(u => u.DiscordId == Context.User.Id));
|
|
||||||
_botServices.DiscordVerifiedUsers.TryRemove(Context.User.Id, out _);
|
|
||||||
_botServices.DiscordRelinkLodestoneMapping.TryRemove(Context.User.Id, out _);
|
|
||||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
await RespondWithModalAsync<LodestoneModal>("wizard-relink-lodestone-modal").ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[ModalInteraction("wizard-relink-lodestone-modal")]
|
|
||||||
public async Task ModalRelink(LodestoneModal lodestoneModal)
|
|
||||||
{
|
|
||||||
if (!(await ValidateInteraction().ConfigureAwait(false))) return;
|
|
||||||
|
|
||||||
_logger.LogInformation("{method}:{userId}:{url}", nameof(ModalRelink), Context.Interaction.User.Id, lodestoneModal.LodestoneUrl);
|
|
||||||
|
|
||||||
EmbedBuilder eb = new();
|
|
||||||
eb.WithColor(Color.Purple);
|
|
||||||
var result = await HandleRelinkModalAsync(eb, lodestoneModal).ConfigureAwait(false);
|
|
||||||
ComponentBuilder cb = new();
|
|
||||||
cb.WithButton("Cancel", "wizard-relink", ButtonStyle.Secondary, emote: new Emoji("❌"));
|
|
||||||
if (result.Success) cb.WithButton("Verify", "wizard-relink-verify:" + result.LodestoneAuth + "," + result.UID, ButtonStyle.Primary, emote: new Emoji("✅"));
|
|
||||||
else cb.WithButton("Try again", "wizard-relink-start", ButtonStyle.Primary, emote: new Emoji("🔁"));
|
|
||||||
await ModifyModalInteraction(eb, cb).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[ComponentInteraction("wizard-relink-verify:*,*")]
|
|
||||||
public async Task ComponentRelinkVerify(string verificationCode, string uid)
|
|
||||||
{
|
|
||||||
if (!(await ValidateInteraction().ConfigureAwait(false))) return;
|
|
||||||
|
|
||||||
_logger.LogInformation("{method}:{userId}:{uid}:{verificationCode}", nameof(ComponentRelinkVerify), Context.Interaction.User.Id, uid, verificationCode);
|
|
||||||
|
|
||||||
|
|
||||||
_botServices.VerificationQueue.Enqueue(new KeyValuePair<ulong, Func<DiscordBotServices, Task>>(Context.User.Id,
|
|
||||||
(services) => HandleVerifyRelinkAsync(Context.User.Id, verificationCode, services)));
|
|
||||||
EmbedBuilder eb = new();
|
|
||||||
ComponentBuilder cb = new();
|
|
||||||
eb.WithColor(Color.Purple);
|
|
||||||
cb.WithButton("Cancel", "wizard-relink", ButtonStyle.Secondary, emote: new Emoji("❌"));
|
|
||||||
cb.WithButton("Check", "wizard-relink-verify-check:" + verificationCode + "," + uid, ButtonStyle.Primary, emote: new Emoji("❓"));
|
|
||||||
eb.WithTitle("Relink Verification Pending");
|
|
||||||
eb.WithDescription("Please wait until the bot verifies your registration." + Environment.NewLine
|
|
||||||
+ "Press \"Check\" to check if the verification has been already processed" + Environment.NewLine + Environment.NewLine
|
|
||||||
+ "__This will not advance automatically, you need to press \"Check\".__");
|
|
||||||
await ModifyInteraction(eb, cb).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[ComponentInteraction("wizard-relink-verify-check:*,*")]
|
|
||||||
public async Task ComponentRelinkVerifyCheck(string verificationCode, string uid)
|
|
||||||
{
|
|
||||||
if (!(await ValidateInteraction().ConfigureAwait(false))) return;
|
|
||||||
|
|
||||||
_logger.LogInformation("{method}:{userId}:{uid}:{verificationCode}", nameof(ComponentRelinkVerifyCheck), Context.Interaction.User.Id, uid, verificationCode);
|
|
||||||
|
|
||||||
EmbedBuilder eb = new();
|
|
||||||
ComponentBuilder cb = new();
|
|
||||||
bool stillEnqueued = _botServices.VerificationQueue.Any(k => k.Key == Context.User.Id);
|
|
||||||
bool verificationRan = _botServices.DiscordVerifiedUsers.TryGetValue(Context.User.Id, out bool verified);
|
|
||||||
bool relinkSuccess = false;
|
|
||||||
if (!verificationRan)
|
|
||||||
{
|
|
||||||
if (stillEnqueued)
|
|
||||||
{
|
|
||||||
eb.WithColor(Color.Gold);
|
|
||||||
eb.WithTitle("Your relink verification is still pending");
|
|
||||||
eb.WithDescription("Please try again and click Check in a few seconds");
|
|
||||||
cb.WithButton("Cancel", "wizard-relink", ButtonStyle.Secondary, emote: new Emoji("❌"));
|
|
||||||
cb.WithButton("Check", "wizard-relink-verify-check:" + verificationCode + "," + uid, ButtonStyle.Primary, emote: new Emoji("❓"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
eb.WithColor(Color.Red);
|
|
||||||
eb.WithTitle("Something went wrong");
|
|
||||||
eb.WithDescription("Your relink verification was processed but did not arrive properly. Please try to start the relink process from the start.");
|
|
||||||
cb.WithButton("Restart", "wizard-relink", ButtonStyle.Primary, emote: new Emoji("🔁"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (verified)
|
|
||||||
{
|
|
||||||
eb.WithColor(Color.Green);
|
|
||||||
using var db = await GetDbContext().ConfigureAwait(false);
|
|
||||||
var (_, key) = await HandleRelinkUser(db, uid).ConfigureAwait(false);
|
|
||||||
eb.WithTitle($"Relink successful, your UID is again: {uid}");
|
|
||||||
eb.WithDescription("This is your private secret key. Do not share this private secret key with anyone. **If you lose it, it is irrevocably lost.**"
|
|
||||||
+ Environment.NewLine + Environment.NewLine
|
|
||||||
+ $"||**`{key}`**||"
|
|
||||||
+ Environment.NewLine + Environment.NewLine
|
|
||||||
+ "Enter this key in Mare Synchronos and hit save to connect to the service."
|
|
||||||
+ Environment.NewLine + Environment.NewLine
|
|
||||||
+ "NOTE: If you are using OAuth2, you do not require to use this secret key."
|
|
||||||
+ Environment.NewLine
|
|
||||||
+ "Have fun.");
|
|
||||||
AddHome(cb);
|
|
||||||
|
|
||||||
relinkSuccess = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
eb.WithColor(Color.Gold);
|
|
||||||
eb.WithTitle("Failed to verify relink");
|
|
||||||
eb.WithDescription("The bot was not able to find the required verification code on your Lodestone profile." + Environment.NewLine + Environment.NewLine
|
|
||||||
+ "Please restart your relink process, make sure to save your profile _twice_ for it to be properly saved." + Environment.NewLine + Environment.NewLine
|
|
||||||
+ "**Make sure your profile is set to public (All Users) for your character. The bot cannot read profiles with privacy settings set to \"logged in\" or \"private\".**" + Environment.NewLine + Environment.NewLine
|
|
||||||
+ "The code the bot is looking for is" + Environment.NewLine + Environment.NewLine
|
|
||||||
+ "**`" + verificationCode + "`**");
|
|
||||||
cb.WithButton("Cancel", "wizard-relink", emote: new Emoji("❌"));
|
|
||||||
cb.WithButton("Retry", "wizard-relink-verify:" + verificationCode + "," + uid, ButtonStyle.Primary, emote: new Emoji("🔁"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await ModifyInteraction(eb, cb).ConfigureAwait(false);
|
|
||||||
if (relinkSuccess)
|
|
||||||
await _botServices.AddRegisteredRoleAsync(Context.Interaction.User).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<(bool Success, string LodestoneAuth, string UID)> HandleRelinkModalAsync(EmbedBuilder embed, LodestoneModal arg)
|
|
||||||
{
|
|
||||||
ulong userId = Context.User.Id;
|
|
||||||
|
|
||||||
var lodestoneId = ParseCharacterIdFromLodestoneUrl(arg.LodestoneUrl);
|
|
||||||
if (lodestoneId == null)
|
|
||||||
{
|
|
||||||
embed.WithTitle("Invalid Lodestone URL");
|
|
||||||
embed.WithDescription("The lodestone URL was not valid. It should have following format:" + Environment.NewLine
|
|
||||||
+ "https://eu.finalfantasyxiv.com/lodestone/character/YOUR_LODESTONE_ID/");
|
|
||||||
return (false, string.Empty, string.Empty);
|
|
||||||
}
|
|
||||||
// check if userid is already in db
|
|
||||||
var hashedLodestoneId = StringUtils.Sha256String(lodestoneId.ToString());
|
|
||||||
|
|
||||||
using var db = await GetDbContext().ConfigureAwait(false);
|
|
||||||
|
|
||||||
// check if discord id or lodestone id is banned
|
|
||||||
if (db.BannedRegistrations.Any(a => a.DiscordIdOrLodestoneAuth == hashedLodestoneId))
|
|
||||||
{
|
|
||||||
embed.WithTitle("Illegal operation");
|
|
||||||
embed.WithDescription("Your account is banned");
|
|
||||||
return (false, string.Empty, string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!db.LodeStoneAuth.Any(a => a.HashedLodestoneId == hashedLodestoneId))
|
|
||||||
{
|
|
||||||
// character already in db
|
|
||||||
embed.WithTitle("Impossible operation");
|
|
||||||
embed.WithDescription("This lodestone character does not exist in the database.");
|
|
||||||
return (false, string.Empty, string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedUser = await db.LodeStoneAuth.Include(u => u.User).SingleAsync(u => u.HashedLodestoneId == hashedLodestoneId).ConfigureAwait(false);
|
|
||||||
|
|
||||||
string lodestoneAuth = await GenerateLodestoneAuth(Context.User.Id, hashedLodestoneId, db).ConfigureAwait(false);
|
|
||||||
// check if lodestone id is already in db
|
|
||||||
embed.WithTitle("Authorize your character for relinking");
|
|
||||||
embed.WithDescription("Add following key to your character profile at https://na.finalfantasyxiv.com/lodestone/my/setting/profile/"
|
|
||||||
+ Environment.NewLine + Environment.NewLine
|
|
||||||
+ $"**`{lodestoneAuth}`**"
|
|
||||||
+ Environment.NewLine + Environment.NewLine
|
|
||||||
+ $"**! THIS IS NOT THE KEY YOU HAVE TO ENTER IN MARE !**"
|
|
||||||
+ Environment.NewLine
|
|
||||||
+ "__You can delete the entry from your profile after verification.__"
|
|
||||||
+ Environment.NewLine + Environment.NewLine
|
|
||||||
+ "The verification will expire in approximately 15 minutes. If you fail to verify the relink will be invalidated and you have to relink again.");
|
|
||||||
_botServices.DiscordRelinkLodestoneMapping[Context.User.Id] = lodestoneId.ToString();
|
|
||||||
|
|
||||||
return (true, lodestoneAuth, expectedUser.User.UID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleVerifyRelinkAsync(ulong userid, string authString, DiscordBotServices services)
|
|
||||||
{
|
|
||||||
using var req = new HttpClient();
|
|
||||||
|
|
||||||
services.DiscordVerifiedUsers.Remove(userid, out _);
|
|
||||||
if (services.DiscordRelinkLodestoneMapping.ContainsKey(userid))
|
|
||||||
{
|
|
||||||
var randomServer = services.LodestoneServers[random.Next(services.LodestoneServers.Length)];
|
|
||||||
var url = $"https://{randomServer}.finalfantasyxiv.com/lodestone/character/{services.DiscordRelinkLodestoneMapping[userid]}";
|
|
||||||
_logger.LogInformation("Verifying {userid} with URL {url}", userid, url);
|
|
||||||
using var response = await req.GetAsync(url).ConfigureAwait(false);
|
|
||||||
if (response.IsSuccessStatusCode || response.StatusCode == System.Net.HttpStatusCode.Forbidden)
|
|
||||||
{
|
|
||||||
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
|
||||||
if (content.Contains(authString))
|
|
||||||
{
|
|
||||||
services.DiscordVerifiedUsers[userid] = true;
|
|
||||||
_logger.LogInformation("Relink: Verified {userid} from lodestone {lodestone}", userid, services.DiscordRelinkLodestoneMapping[userid]);
|
|
||||||
await _botServices.LogToChannel($"<@{userid}> RELINK VERIFY: Success.").ConfigureAwait(false);
|
|
||||||
services.DiscordRelinkLodestoneMapping.TryRemove(userid, out _);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
services.DiscordVerifiedUsers[userid] = false;
|
|
||||||
_logger.LogInformation("Relink: Could not verify {userid} from lodestone {lodestone}, did not find authString: {authString}, status code was: {code}",
|
|
||||||
userid, services.DiscordRelinkLodestoneMapping[userid], authString, response.StatusCode);
|
|
||||||
await _botServices.LogToChannel($"<@{userid}> RELINK VERIFY: Failed: No Authstring ({authString}). (<{url}>)").ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogWarning("Could not verify {userid}, HttpStatusCode: {code}", userid, response.StatusCode);
|
|
||||||
await _botServices.LogToChannel($"<@{userid}> RELINK VERIFY: Failed: HttpStatusCode {response.StatusCode}. (<{url}>)").ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<(string, string)> HandleRelinkUser(MareDbContext db, string uid)
|
|
||||||
{
|
|
||||||
var oldLodestoneAuth = await db.LodeStoneAuth.Include(u => u.User).SingleOrDefaultAsync(u => u.User.UID == uid && u.DiscordId != Context.User.Id).ConfigureAwait(false);
|
|
||||||
var newLodestoneAuth = await db.LodeStoneAuth.Include(u => u.User).SingleOrDefaultAsync(u => u.DiscordId == Context.User.Id).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var user = oldLodestoneAuth.User;
|
|
||||||
|
|
||||||
var computedHash = StringUtils.Sha256String(StringUtils.GenerateRandomString(64) + DateTime.UtcNow.ToString());
|
|
||||||
var auth = new Auth()
|
|
||||||
{
|
|
||||||
HashedKey = StringUtils.Sha256String(computedHash),
|
|
||||||
User = user,
|
|
||||||
};
|
|
||||||
|
|
||||||
var previousAuth = await db.Auth.SingleOrDefaultAsync(u => u.UserUID == user.UID).ConfigureAwait(false);
|
|
||||||
if (previousAuth != null)
|
|
||||||
{
|
|
||||||
db.Remove(previousAuth);
|
|
||||||
}
|
|
||||||
|
|
||||||
newLodestoneAuth.LodestoneAuthString = null;
|
|
||||||
newLodestoneAuth.StartedAt = null;
|
|
||||||
newLodestoneAuth.User = user;
|
|
||||||
db.Update(newLodestoneAuth);
|
|
||||||
db.Remove(oldLodestoneAuth);
|
|
||||||
await db.Auth.AddAsync(auth).ConfigureAwait(false);
|
|
||||||
|
|
||||||
_botServices.Logger.LogInformation("User relinked: {userUID}", user.UID);
|
|
||||||
|
|
||||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
await _botServices.LogToChannel($"{Context.User.Mention} RELINK COMPLETE: => {user.UID}").ConfigureAwait(false);
|
|
||||||
|
|
||||||
return (user.UID, computedHash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -23,9 +23,9 @@ public partial class MareWizardModule
|
|||||||
eb.WithColor(Color.Blue);
|
eb.WithColor(Color.Blue);
|
||||||
eb.WithTitle("Secondary UID");
|
eb.WithTitle("Secondary UID");
|
||||||
eb.WithDescription("You can create secondary UIDs here. " + Environment.NewLine + Environment.NewLine
|
eb.WithDescription("You can create secondary UIDs here. " + Environment.NewLine + Environment.NewLine
|
||||||
+ "Secondary UIDs act as completely separate Mare accounts with their own pair list, joined syncshells, UID and so on." + Environment.NewLine
|
+ "Secondary UIDs act as completely separate Namazu accounts with their own pair list, joined syncshells, UID and so on." + Environment.NewLine
|
||||||
+ "Use this to create UIDs if you want to use Mare on two separate game instances at once or keep your alts private." + Environment.NewLine + Environment.NewLine
|
+ "Use this to create UIDs if you want to use Namazu on two separate game instances at once or keep your alts private." + Environment.NewLine + Environment.NewLine
|
||||||
+ "__Note:__ Creating a Secondary UID is _not_ necessary to use Mare for alts." + Environment.NewLine + Environment.NewLine
|
+ "__Note:__ Creating a Secondary UID is _not_ necessary to use Namazu for alts." + Environment.NewLine + Environment.NewLine
|
||||||
+ $"You currently have {secondaryUids} Secondary UIDs out of a maximum of 20.");
|
+ $"You currently have {secondaryUids} Secondary UIDs out of a maximum of 20.");
|
||||||
ComponentBuilder cb = new();
|
ComponentBuilder cb = new();
|
||||||
AddHome(cb);
|
AddHome(cb);
|
||||||
@@ -81,7 +81,7 @@ public partial class MareWizardModule
|
|||||||
|
|
||||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
embed.WithDescription("A secondary UID for you was created, use the information below and add the secret key to the Mare setings in the Service Settings tab.");
|
embed.WithDescription("A secondary UID for you was created, use the information below and add the secret key to the Namazu setings in the Service Settings tab.");
|
||||||
embed.AddField("UID", newUser.UID);
|
embed.AddField("UID", newUser.UID);
|
||||||
embed.AddField("Secret Key", computedHash);
|
embed.AddField("Secret Key", computedHash);
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public partial class MareWizardModule
|
|||||||
eb.WithColor(Color.Green);
|
eb.WithColor(Color.Green);
|
||||||
eb.WithTitle("Vanity UID successfully set");
|
eb.WithTitle("Vanity UID successfully set");
|
||||||
eb.WithDescription($"Your Vanity UID for \"{uid}\" was successfully changed to \"{desiredVanityUid}\"." + Environment.NewLine + Environment.NewLine
|
eb.WithDescription($"Your Vanity UID for \"{uid}\" was successfully changed to \"{desiredVanityUid}\"." + Environment.NewLine + Environment.NewLine
|
||||||
+ "For changes to take effect you need to reconnect to the Mare service.");
|
+ "For changes to take effect you need to reconnect to the Namazu service.");
|
||||||
await _botServices.LogToChannel($"{Context.User.Mention} VANITY UID SET: UID: {user.UID}, Vanity: {desiredVanityUid}").ConfigureAwait(false);
|
await _botServices.LogToChannel($"{Context.User.Mention} VANITY UID SET: UID: {user.UID}, Vanity: {desiredVanityUid}").ConfigureAwait(false);
|
||||||
AddHome(cb);
|
AddHome(cb);
|
||||||
}
|
}
|
||||||
@@ -195,7 +195,7 @@ public partial class MareWizardModule
|
|||||||
eb.WithColor(Color.Green);
|
eb.WithColor(Color.Green);
|
||||||
eb.WithTitle("Vanity Syncshell ID successfully set");
|
eb.WithTitle("Vanity Syncshell ID successfully set");
|
||||||
eb.WithDescription($"Your Vanity Syncshell ID for {gid} was successfully changed to \"{desiredVanityGid}\"." + Environment.NewLine + Environment.NewLine
|
eb.WithDescription($"Your Vanity Syncshell ID for {gid} was successfully changed to \"{desiredVanityGid}\"." + Environment.NewLine + Environment.NewLine
|
||||||
+ "For changes to take effect you need to reconnect to the Mare service.");
|
+ "For changes to take effect you need to reconnect to the Namazu service.");
|
||||||
AddHome(cb);
|
AddHome(cb);
|
||||||
await _botServices.LogToChannel($"{Context.User.Mention} VANITY GID SET: GID: {group.GID}, Vanity: {desiredVanityGid}").ConfigureAwait(false);
|
await _botServices.LogToChannel($"{Context.User.Mention} VANITY GID SET: GID: {group.GID}, Vanity: {desiredVanityGid}").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,12 +155,11 @@ public partial class MareWizardModule : InteractionModuleBase
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
EmbedBuilder eb = new();
|
EmbedBuilder eb = new();
|
||||||
eb.WithTitle("Welcome to the Mare Synchronos Service Bot for this server");
|
eb.WithTitle("Welcome to the Namazu Sync Service Bot for this server");
|
||||||
eb.WithDescription("Here is what you can do:" + Environment.NewLine + Environment.NewLine
|
eb.WithDescription("Here is what you can do:" + Environment.NewLine + Environment.NewLine
|
||||||
+ (!hasAccount ? string.Empty : ("- Check your account status press \"ℹ️ User Info\"" + Environment.NewLine))
|
+ (!hasAccount ? string.Empty : ("- Check your account status press \"ℹ️ User Info\"" + Environment.NewLine))
|
||||||
+ (hasAccount ? string.Empty : ("- Register a new Mare Account press \"🌒 Register\"" + Environment.NewLine))
|
+ (hasAccount ? string.Empty : ("- Register a new Namazu Account press \"🌒 Register\"" + Environment.NewLine))
|
||||||
+ (!hasAccount ? string.Empty : ("- You lost your secret key press \"🏥 Recover\"" + Environment.NewLine))
|
+ (!hasAccount ? string.Empty : ("- You lost your secret key press \"🏥 Recover\"" + Environment.NewLine))
|
||||||
+ (hasAccount ? string.Empty : ("- If you have changed your Discord account press \"🔗 Relink\"" + Environment.NewLine))
|
|
||||||
+ (!hasAccount ? string.Empty : ("- Create a secondary UIDs press \"2️⃣ Secondary UID\"" + Environment.NewLine))
|
+ (!hasAccount ? string.Empty : ("- Create a secondary UIDs press \"2️⃣ Secondary UID\"" + Environment.NewLine))
|
||||||
+ (!hasAccount ? string.Empty : ("- Set a Vanity UID press \"💅 Vanity IDs\"" + Environment.NewLine))
|
+ (!hasAccount ? string.Empty : ("- Set a Vanity UID press \"💅 Vanity IDs\"" + Environment.NewLine))
|
||||||
+ (!hasAccount ? string.Empty : (!isInAprilFoolsMode ? string.Empty : ("- Check your WorryCoin™ and MareToken© balance and add payment options" + Environment.NewLine)))
|
+ (!hasAccount ? string.Empty : (!isInAprilFoolsMode ? string.Empty : ("- Check your WorryCoin™ and MareToken© balance and add payment options" + Environment.NewLine)))
|
||||||
@@ -171,7 +170,6 @@ public partial class MareWizardModule : InteractionModuleBase
|
|||||||
if (!hasAccount)
|
if (!hasAccount)
|
||||||
{
|
{
|
||||||
cb.WithButton("Register", "wizard-register", ButtonStyle.Primary, new Emoji("🌒"));
|
cb.WithButton("Register", "wizard-register", ButtonStyle.Primary, new Emoji("🌒"));
|
||||||
cb.WithButton("Relink", "wizard-relink", ButtonStyle.Secondary, new Emoji("🔗"));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -49,4 +49,5 @@ public class MetricsAPI
|
|||||||
public const string GaugeGposeLobbies = "mare_gpose_lobbies";
|
public const string GaugeGposeLobbies = "mare_gpose_lobbies";
|
||||||
public const string GaugeGposeLobbyUsers = "mare_gpose_lobby_users";
|
public const string GaugeGposeLobbyUsers = "mare_gpose_lobby_users";
|
||||||
public const string GaugeHubConcurrency = "mare_free_concurrent_hub_calls";
|
public const string GaugeHubConcurrency = "mare_free_concurrent_hub_calls";
|
||||||
|
public const string GaugeHubQueuedConcurrency = "mare_free_concurrent_queued_hub_calls";
|
||||||
}
|
}
|
||||||
@@ -3,12 +3,15 @@ using MareSynchronosShared.Utils;
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace MareSynchronosShared.RequirementHandlers;
|
namespace MareSynchronosShared.RequirementHandlers;
|
||||||
public class ExistingUserRequirementHandler : AuthorizationHandler<ExistingUserRequirement>
|
public class ExistingUserRequirementHandler : AuthorizationHandler<ExistingUserRequirement>
|
||||||
{
|
{
|
||||||
private readonly IDbContextFactory<MareDbContext> _dbContextFactory;
|
private readonly IDbContextFactory<MareDbContext> _dbContextFactory;
|
||||||
private readonly ILogger<ExistingUserRequirementHandler> _logger;
|
private readonly ILogger<ExistingUserRequirementHandler> _logger;
|
||||||
|
private readonly static ConcurrentDictionary<string, (bool Exists, DateTime LastCheck)> _existingUserDict = [];
|
||||||
|
private readonly static ConcurrentDictionary<ulong, (bool Exists, DateTime LastCheck)> _existingDiscordDict = [];
|
||||||
|
|
||||||
public ExistingUserRequirementHandler(IDbContextFactory<MareDbContext> dbContext, ILogger<ExistingUserRequirementHandler> logger)
|
public ExistingUserRequirementHandler(IDbContextFactory<MareDbContext> dbContext, ILogger<ExistingUserRequirementHandler> logger)
|
||||||
{
|
{
|
||||||
@@ -17,22 +20,65 @@ public class ExistingUserRequirementHandler : AuthorizationHandler<ExistingUserR
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ExistingUserRequirement requirement)
|
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ExistingUserRequirement requirement)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var uid = context.User.Claims.SingleOrDefault(g => string.Equals(g.Type, MareClaimTypes.Uid, StringComparison.Ordinal))?.Value;
|
var uid = context.User.Claims.SingleOrDefault(g => string.Equals(g.Type, MareClaimTypes.Uid, StringComparison.Ordinal))?.Value;
|
||||||
if (uid == null) context.Fail();
|
if (uid == null)
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
_logger.LogWarning("Failed to find UID in claims");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var discordIdString = context.User.Claims.SingleOrDefault(g => string.Equals(g.Type, MareClaimTypes.DiscordId, StringComparison.Ordinal))?.Value;
|
var discordIdString = context.User.Claims.SingleOrDefault(g => string.Equals(g.Type, MareClaimTypes.DiscordId, StringComparison.Ordinal))?.Value;
|
||||||
if (discordIdString == null) context.Fail();
|
if (discordIdString == null)
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
_logger.LogWarning("Failed to find DiscordId in claims");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ulong.TryParse(discordIdString, out ulong discordId))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Failed to parse DiscordId");
|
||||||
|
context.Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
using var dbContext = await _dbContextFactory.CreateDbContextAsync().ConfigureAwait(false);
|
using var dbContext = await _dbContextFactory.CreateDbContextAsync().ConfigureAwait(false);
|
||||||
var user = await dbContext.Users.AsNoTracking().SingleOrDefaultAsync(b => b.UID == uid).ConfigureAwait(false);
|
|
||||||
if (user == null) context.Fail();
|
|
||||||
|
|
||||||
if (!ulong.TryParse(discordIdString, out ulong discordId)) context.Fail();
|
if (!_existingUserDict.TryGetValue(uid, out (bool Exists, DateTime LastCheck) existingUser)
|
||||||
|
|| DateTime.UtcNow.Subtract(existingUser.LastCheck).TotalHours > 1)
|
||||||
|
{
|
||||||
|
var userExists = await dbContext.Users.SingleOrDefaultAsync(context => context.UID == uid).ConfigureAwait(false) != null;
|
||||||
|
_existingUserDict[uid] = existingUser = (userExists, DateTime.UtcNow);
|
||||||
|
}
|
||||||
|
if (!existingUser.Exists)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Failed to find Mare User {User} in DB", uid);
|
||||||
|
context.Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var discordUser = await dbContext.LodeStoneAuth.AsNoTracking().SingleOrDefaultAsync(b => b.DiscordId == discordId).ConfigureAwait(false);
|
if (!_existingDiscordDict.TryGetValue(discordId, out (bool Exists, DateTime LastCheck) existingDiscordUser)
|
||||||
if (discordUser == null) context.Fail();
|
|| DateTime.UtcNow.Subtract(existingDiscordUser.LastCheck).TotalHours > 1)
|
||||||
|
{
|
||||||
|
var discordUserExists = await dbContext.LodeStoneAuth.AsNoTracking().SingleOrDefaultAsync(b => b.DiscordId == discordId).ConfigureAwait(false) != null;
|
||||||
|
_existingDiscordDict[discordId] = existingDiscordUser = (discordUserExists, DateTime.UtcNow);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existingDiscordUser.Exists)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Failed to find Discord User {User} in DB", discordId);
|
||||||
|
context.Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
context.Succeed(requirement);
|
context.Succeed(requirement);
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(e, "ExistingUserRequirementHandler failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -26,19 +26,34 @@ public class UserRequirementHandler : AuthorizationHandler<UserRequirement, HubI
|
|||||||
{
|
{
|
||||||
var uid = context.User.Claims.SingleOrDefault(g => string.Equals(g.Type, MareClaimTypes.Uid, StringComparison.Ordinal))?.Value;
|
var uid = context.User.Claims.SingleOrDefault(g => string.Equals(g.Type, MareClaimTypes.Uid, StringComparison.Ordinal))?.Value;
|
||||||
|
|
||||||
if (uid == null) context.Fail();
|
if (uid == null)
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
_logger.LogWarning("No user UID found in claims");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((requirement.Requirements & UserRequirements.Identified) is UserRequirements.Identified)
|
if ((requirement.Requirements & UserRequirements.Identified) is UserRequirements.Identified)
|
||||||
{
|
{
|
||||||
var ident = await _redis.GetAsync<string>("UID:" + uid).ConfigureAwait(false);
|
var ident = await _redis.GetAsync<string>("UID:" + uid).ConfigureAwait(false);
|
||||||
if (ident == RedisValue.EmptyString) context.Fail();
|
if (ident == RedisValue.EmptyString)
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
_logger.LogWarning("User {uid} not online", uid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((requirement.Requirements & UserRequirements.Administrator) is UserRequirements.Administrator)
|
if ((requirement.Requirements & UserRequirements.Administrator) is UserRequirements.Administrator)
|
||||||
{
|
{
|
||||||
using var dbContext = await _dbContextFactory.CreateDbContextAsync().ConfigureAwait(false);
|
using var dbContext = await _dbContextFactory.CreateDbContextAsync().ConfigureAwait(false);
|
||||||
var user = await dbContext.Users.AsNoTracking().SingleOrDefaultAsync(b => b.UID == uid).ConfigureAwait(false);
|
var user = await dbContext.Users.AsNoTracking().SingleOrDefaultAsync(b => b.UID == uid).ConfigureAwait(false);
|
||||||
if (user == null || !user.IsAdmin) context.Fail();
|
if (user == null || !user.IsAdmin)
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
_logger.LogWarning("Admin request for {uid} unauthenticated", uid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
_logger.LogInformation("Admin {uid} authenticated", uid);
|
_logger.LogInformation("Admin {uid} authenticated", uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +61,12 @@ public class UserRequirementHandler : AuthorizationHandler<UserRequirement, HubI
|
|||||||
{
|
{
|
||||||
using var dbContext = await _dbContextFactory.CreateDbContextAsync().ConfigureAwait(false);
|
using var dbContext = await _dbContextFactory.CreateDbContextAsync().ConfigureAwait(false);
|
||||||
var user = await dbContext.Users.AsNoTracking().SingleOrDefaultAsync(b => b.UID == uid).ConfigureAwait(false);
|
var user = await dbContext.Users.AsNoTracking().SingleOrDefaultAsync(b => b.UID == uid).ConfigureAwait(false);
|
||||||
if (user == null || !user.IsAdmin && !user.IsModerator) context.Fail();
|
if (user == null || !user.IsAdmin && !user.IsModerator)
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
_logger.LogWarning("Admin/Moderator for {uid} unauthenticated", uid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
_logger.LogInformation("Admin/Moderator {uid} authenticated", uid);
|
_logger.LogInformation("Admin/Moderator {uid} authenticated", uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user