From 927e1aaceb851f1f8892e6f0028fbb9c8379e948 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Mon, 22 Aug 2022 10:46:37 +0200 Subject: [PATCH 1/3] potential optimizations (ConfigureAwait, Logging Templates) --- .../SecretKeyAuthenticationHandler.cs | 10 +- .../MareSynchronosServer/CleanupService.cs | 10 +- .../Discord/DiscordBot.cs | 70 ++++++------ .../Hubs/MareHub.Admin.cs | 44 ++++---- .../Hubs/MareHub.Files.cs | 65 ++++++----- .../MareSynchronosServer/Hubs/MareHub.User.cs | 102 +++++++++--------- .../MareSynchronosServer/Hubs/MareHub.cs | 26 ++--- .../MareSynchronosServer.csproj | 4 + .../MareSynchronosServer/SystemInfoService.cs | 2 +- .../Throttling/SignalRLimitFilter.cs | 26 ++--- .../Hubs/MareHubTest.cs | 2 +- 11 files changed, 182 insertions(+), 179 deletions(-) diff --git a/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs b/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs index 55371fb..3f26b89 100644 --- a/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs +++ b/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs @@ -113,7 +113,7 @@ namespace MareSynchronosServer.Authentication var token = failedAuth.ResetCts.Token; failedAuth.ResetTask = Task.Run(async () => { - await Task.Delay(TimeSpan.FromMinutes(tempBanMinutes), token); + await Task.Delay(TimeSpan.FromMinutes(tempBanMinutes), token).ConfigureAwait(false); if (token.IsCancellationRequested) return; FailedAuthorization fauth; lock (failedAuthLock) @@ -123,7 +123,7 @@ namespace MareSynchronosServer.Authentication fauth.Dispose(); }, token); - Logger.LogWarning("TempBan " + ip + " for authorization spam"); + Logger.LogWarning("TempBan {ip} for authorization spam", ip); return AuthenticateResult.Fail("Failed Authorization"); } } @@ -142,7 +142,7 @@ namespace MareSynchronosServer.Authentication lock (failedAuthLock) { - Logger.LogWarning("Failed authorization from " + ip); + Logger.LogWarning("Failed authorization from {ip}", ip); if (FailedAuthorizations.TryGetValue(ip, out var auth)) { auth.IncreaseFailedAttempts(); @@ -163,7 +163,7 @@ namespace MareSynchronosServer.Authentication if (string.IsNullOrEmpty(uid)) { uid = (await _mareDbContext.Auth.AsNoTracking() - .FirstOrDefaultAsync(m => m.HashedKey == hashedHeader))?.UserUID; + .FirstOrDefaultAsync(m => m.HashedKey == hashedHeader).ConfigureAwait(false))?.UserUID; if (uid == null) { @@ -172,7 +172,7 @@ namespace MareSynchronosServer.Authentication Authentications[hashedHeader] = unauthorized; } - Logger.LogWarning("Failed authorization from " + ip); + Logger.LogWarning("Failed authorization from {ip}", ip); lock (failedAuthLock) { if (FailedAuthorizations.TryGetValue(ip, out var auth)) diff --git a/MareSynchronosServer/MareSynchronosServer/CleanupService.cs b/MareSynchronosServer/MareSynchronosServer/CleanupService.cs index a8ccff6..401c496 100644 --- a/MareSynchronosServer/MareSynchronosServer/CleanupService.cs +++ b/MareSynchronosServer/MareSynchronosServer/CleanupService.cs @@ -49,7 +49,7 @@ namespace MareSynchronosServer using var scope = _services.CreateScope(); using var dbContext = scope.ServiceProvider.GetService()!; - _logger.LogInformation($"Cleaning up files older than {filesOlderThanDays} days"); + _logger.LogInformation("Cleaning up files older than {filesOlderThanDays} days", filesOlderThanDays); try { @@ -62,13 +62,13 @@ namespace MareSynchronosServer var fi = new FileInfo(fileName); if (!fi.Exists) { - _logger.LogInformation("File does not exist anymore: " + fileName); + _logger.LogInformation("File does not exist anymore: {fileName}", fileName); dbContext.Files.Remove(file); } else if (fi.LastAccessTime < prevTime) { MareMetrics.FilesTotalSize.Dec(fi.Length); - _logger.LogInformation("File outdated: " + fileName); + _logger.LogInformation("File outdated: {fileName}", fileName); dbContext.Files.Remove(file); fi.Delete(); } @@ -144,7 +144,7 @@ namespace MareSynchronosServer usersOlderThanDays = 14; } - _logger.LogInformation($"Cleaning up users older than {usersOlderThanDays} days"); + _logger.LogInformation("Cleaning up users older than {usersOlderThanDays} days", usersOlderThanDays); var allUsers = dbContext.Users.ToList(); List usersToRemove = new(); @@ -152,7 +152,7 @@ namespace MareSynchronosServer { if (user.LastLoggedIn < (DateTime.UtcNow - TimeSpan.FromDays(usersOlderThanDays))) { - _logger.LogInformation("User outdated: " + user.UID); + _logger.LogInformation("User outdated: {userUID}", user.UID); usersToRemove.Add(user); } } diff --git a/MareSynchronosServer/MareSynchronosServer/Discord/DiscordBot.cs b/MareSynchronosServer/MareSynchronosServer/Discord/DiscordBot.cs index 67dd217..de4df59 100644 --- a/MareSynchronosServer/MareSynchronosServer/Discord/DiscordBot.cs +++ b/MareSynchronosServer/MareSynchronosServer/Discord/DiscordBot.cs @@ -58,21 +58,21 @@ namespace MareSynchronosServer.Discord private async Task DiscordClient_SlashCommandExecuted(SocketSlashCommand arg) { - await semaphore.WaitAsync(); + await semaphore.WaitAsync().ConfigureAwait(false); try { if (arg.Data.Name == "register") { if (arg.Data.Options.FirstOrDefault(f => f.Name == "overwrite_old_account") != null) { - await DeletePreviousUserAccount(arg.User.Id); + await DeletePreviousUserAccount(arg.User.Id).ConfigureAwait(false); } var modal = new ModalBuilder(); modal.WithTitle("Verify with Lodestone"); modal.WithCustomId("register_modal"); modal.AddTextInput("Enter the Lodestone URL of your Character", "lodestoneurl", TextInputStyle.Short, "https://*.finalfantasyxiv.com/lodestone/character//", required: true); - await arg.RespondWithModalAsync(modal.Build()); + await arg.RespondWithModalAsync(modal.Build()).ConfigureAwait(false); } else if (arg.Data.Name == "verify") { @@ -81,23 +81,23 @@ namespace MareSynchronosServer.Discord { eb.WithTitle("Already queued for verfication"); eb.WithDescription("You are already queued for verification. Please wait."); - await arg.RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true); + await arg.RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true).ConfigureAwait(false); } else if (!DiscordLodestoneMapping.ContainsKey(arg.User.Id)) { eb.WithTitle("Cannot verify registration"); eb.WithDescription("You need to **/register** first before you can **/verify**"); - await arg.RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true); + await arg.RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true).ConfigureAwait(false); } else { - await arg.DeferAsync(ephemeral: true); + await arg.DeferAsync(ephemeral: true).ConfigureAwait(false); verificationQueue.Enqueue(arg); } } else { - await arg.RespondAsync("idk what you did to get here to start, just follow the instructions as provided.", ephemeral: true); + await arg.RespondAsync("idk what you did to get here to start, just follow the instructions as provided.", ephemeral: true).ConfigureAwait(false); } } finally @@ -110,7 +110,7 @@ namespace MareSynchronosServer.Discord { using var scope = services.CreateScope(); using var db = scope.ServiceProvider.GetService(); - var discordAuthedUser = await db.LodeStoneAuth.Include(u => u.User).FirstOrDefaultAsync(u => u.DiscordId == id); + var discordAuthedUser = await db.LodeStoneAuth.Include(u => u.User).FirstOrDefaultAsync(u => u.DiscordId == id).ConfigureAwait(false); if (discordAuthedUser != null) { if (discordAuthedUser.User != null) @@ -122,7 +122,7 @@ namespace MareSynchronosServer.Discord db.Remove(discordAuthedUser); } - await db.SaveChangesAsync(); + await db.SaveChangesAsync().ConfigureAwait(false); } } @@ -130,8 +130,8 @@ namespace MareSynchronosServer.Discord { if (arg.Data.CustomId == "register_modal") { - var embed = await HandleRegisterModalAsync(arg); - await arg.RespondAsync(embeds: new Embed[] { embed }, ephemeral: true); + var embed = await HandleRegisterModalAsync(arg).ConfigureAwait(false); + await arg.RespondAsync(embeds: new Embed[] { embed }, ephemeral: true).ConfigureAwait(false); } } @@ -147,10 +147,10 @@ namespace MareSynchronosServer.Discord if (lodestoneAuth != null && DiscordLodestoneMapping.ContainsKey(id)) { var randomServer = LodestoneServers[random.Next(LodestoneServers.Length)]; - var response = await req.GetAsync($"https://{randomServer}.finalfantasyxiv.com/lodestone/character/{DiscordLodestoneMapping[id]}"); + var response = await req.GetAsync($"https://{randomServer}.finalfantasyxiv.com/lodestone/character/{DiscordLodestoneMapping[id]}").ConfigureAwait(false); if (response.IsSuccessStatusCode) { - var content = await response.Content.ReadAsStringAsync(); + var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); if (content.Contains(lodestoneAuth.LodestoneAuthString)) { DiscordLodestoneMapping.TryRemove(id, out _); @@ -168,7 +168,7 @@ namespace MareSynchronosServer.Discord } // make the first registered user on the service to admin - if (!await db.Users.AnyAsync()) + if (!await db.Users.AnyAsync().ConfigureAwait(false)) { user.IsAdmin = true; } @@ -187,10 +187,10 @@ namespace MareSynchronosServer.Discord User = user, }; - db.Users.Add(user); - db.Auth.Add(auth); + await db.Users.AddAsync(user).ConfigureAwait(false); + await db.Auth.AddAsync(auth).ConfigureAwait(false); - logger.LogInformation("User registered: " + user.UID); + logger.LogInformation("User registered: {userUID}", user.UID); MareMetrics.UsersRegistered.Inc(); @@ -217,7 +217,7 @@ namespace MareSynchronosServer.Discord } } - await db.SaveChangesAsync(); + await db.SaveChangesAsync().ConfigureAwait(false); } else { @@ -270,7 +270,7 @@ namespace MareSynchronosServer.Discord } else { - string lodestoneAuth = await GenerateLodestoneAuth(arg.User.Id, hashedLodestoneId, db); + string lodestoneAuth = await GenerateLodestoneAuth(arg.User.Id, hashedLodestoneId, db).ConfigureAwait(false); // check if lodestone id is already in db embed.WithTitle("Authorize your character"); embed.WithDescription("Add following key to your character profile at https://na.finalfantasyxiv.com/lodestone/my/setting/profile/" @@ -303,7 +303,7 @@ namespace MareSynchronosServer.Discord }; dbContext.Add(lsAuth); - await dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync().ConfigureAwait(false); return auth; } @@ -345,8 +345,8 @@ namespace MareSynchronosServer.Discord try { - await discordClient.CreateGlobalApplicationCommandAsync(register.Build()); - await discordClient.CreateGlobalApplicationCommandAsync(verify.Build()); + await discordClient.CreateGlobalApplicationCommandAsync(register.Build()).ConfigureAwait(false); + await discordClient.CreateGlobalApplicationCommandAsync(verify.Build()).ConfigureAwait(false); } catch (Exception ex) { @@ -356,7 +356,7 @@ namespace MareSynchronosServer.Discord private Task Log(LogMessage msg) { - logger.LogInformation(msg.ToString()); + logger.LogInformation("{msg}", msg); return Task.CompletedTask; } @@ -367,8 +367,8 @@ namespace MareSynchronosServer.Discord { authToken = configuration.GetValue("DiscordBotToken"); - await discordClient.LoginAsync(TokenType.Bot, authToken); - await discordClient.StartAsync(); + await discordClient.LoginAsync(TokenType.Bot, authToken).ConfigureAwait(false); + await discordClient.StartAsync().ConfigureAwait(false); discordClient.Ready += DiscordClient_Ready; discordClient.SlashCommandExecuted += DiscordClient_SlashCommandExecuted; @@ -388,18 +388,18 @@ namespace MareSynchronosServer.Discord { try { - var dataEmbed = await HandleVerifyAsync(queueitem.User.Id); - await queueitem.FollowupAsync(embed: dataEmbed, ephemeral: true); + var dataEmbed = await HandleVerifyAsync(queueitem.User.Id).ConfigureAwait(false); + await queueitem.FollowupAsync(embed: dataEmbed, ephemeral: true).ConfigureAwait(false); logger.LogInformation("Sent login information to user"); } catch (Exception e) { - logger.LogError(e.Message); + logger.LogError(e, "Error during queue work"); } } - await Task.Delay(TimeSpan.FromSeconds(2), verificationTaskCts.Token); + await Task.Delay(TimeSpan.FromSeconds(2), verificationTaskCts.Token).ConfigureAwait(false); } } @@ -408,14 +408,14 @@ namespace MareSynchronosServer.Discord updateStatusCts = new(); while (!updateStatusCts.IsCancellationRequested) { - using var scope = services.CreateScope(); - using var db = scope.ServiceProvider.GetService(); + await using var scope = services.CreateAsyncScope(); + await using var db = scope.ServiceProvider.GetService(); var users = db.Users.Count(c => c.CharacterIdentification != null); - await discordClient.SetActivityAsync(new Game("Mare for " + users + " Users")); + await discordClient.SetActivityAsync(new Game("Mare for " + users + " Users")).ConfigureAwait(false); - await Task.Delay(TimeSpan.FromSeconds(15)); + await Task.Delay(TimeSpan.FromSeconds(15)).ConfigureAwait(false); } } @@ -424,8 +424,8 @@ namespace MareSynchronosServer.Discord verificationTaskCts?.Cancel(); updateStatusCts?.Cancel(); - await discordClient.LogoutAsync(); - await discordClient.StopAsync(); + await discordClient.LogoutAsync().ConfigureAwait(false); + await discordClient.StopAsync().ConfigureAwait(false); } } } diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs index 6877900..548204a 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs @@ -23,14 +23,14 @@ namespace MareSynchronosServer.Hubs public async Task ChangeModeratorStatus(string uid, bool isModerator) { if (!IsAdmin) return; - var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == uid); + var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == uid).ConfigureAwait(false); if (user == null) return; user.IsModerator = isModerator; _dbContext.Update(user); - await _dbContext.SaveChangesAsync(); - await Clients.Users(user.UID).SendAsync(Api.OnAdminForcedReconnect); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); + await Clients.Users(user.UID).SendAsync(Api.OnAdminForcedReconnect).ConfigureAwait(false); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] @@ -40,15 +40,15 @@ namespace MareSynchronosServer.Hubs if (!IsModerator || string.IsNullOrEmpty(dto.CharacterHash)) return; var existingUser = - await _dbContext.BannedUsers.SingleOrDefaultAsync(b => b.CharacterIdentification == dto.CharacterHash); + await _dbContext.BannedUsers.SingleOrDefaultAsync(b => b.CharacterIdentification == dto.CharacterHash).ConfigureAwait(false); if (existingUser == null) { return; } _dbContext.Remove(existingUser); - await _dbContext.SaveChangesAsync(); - await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminDeleteBannedUser, dto); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); + await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminDeleteBannedUser, dto).ConfigureAwait(false); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] @@ -58,15 +58,15 @@ namespace MareSynchronosServer.Hubs if (!IsAdmin || string.IsNullOrEmpty(dto.Hash)) return; var existingFile = - await _dbContext.ForbiddenUploadEntries.SingleOrDefaultAsync(b => b.Hash == dto.Hash); + await _dbContext.ForbiddenUploadEntries.SingleOrDefaultAsync(b => b.Hash == dto.Hash).ConfigureAwait(false); if (existingFile == null) { return; } _dbContext.Remove(existingFile); - await _dbContext.SaveChangesAsync(); - await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminDeleteForbiddenFile, dto); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); + await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminDeleteForbiddenFile, dto).ConfigureAwait(false); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] @@ -79,7 +79,7 @@ namespace MareSynchronosServer.Hubs { CharacterHash = b.CharacterIdentification, Reason = b.Reason - }).ToListAsync(); + }).ToListAsync().ConfigureAwait(false); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] @@ -92,7 +92,7 @@ namespace MareSynchronosServer.Hubs { Hash = b.Hash, ForbiddenBy = b.ForbiddenBy - }).ToListAsync(); + }).ToListAsync().ConfigureAwait(false); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] @@ -107,7 +107,7 @@ namespace MareSynchronosServer.Hubs UID = b.UID, IsModerator = b.IsModerator, IsAdmin = b.IsAdmin - }).ToListAsync(); + }).ToListAsync().ConfigureAwait(false); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] @@ -117,7 +117,7 @@ namespace MareSynchronosServer.Hubs if (!IsModerator || string.IsNullOrEmpty(dto.CharacterHash)) return; var existingUser = - await _dbContext.BannedUsers.SingleOrDefaultAsync(b => b.CharacterIdentification == dto.CharacterHash); + await _dbContext.BannedUsers.SingleOrDefaultAsync(b => b.CharacterIdentification == dto.CharacterHash).ConfigureAwait(false); if (existingUser != null) { existingUser.Reason = dto.Reason; @@ -129,16 +129,16 @@ namespace MareSynchronosServer.Hubs { CharacterIdentification = dto.CharacterHash, Reason = dto.Reason - }); + }).ConfigureAwait(false); } - await _dbContext.SaveChangesAsync(); - await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminUpdateOrAddBannedUser, dto); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); + await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminUpdateOrAddBannedUser, dto).ConfigureAwait(false); var bannedUser = - await _dbContext.Users.SingleOrDefaultAsync(u => u.CharacterIdentification == dto.CharacterHash); + await _dbContext.Users.SingleOrDefaultAsync(u => u.CharacterIdentification == dto.CharacterHash).ConfigureAwait(false); if (bannedUser != null) { - await Clients.User(bannedUser.UID).SendAsync(Api.OnAdminForcedReconnect); + await Clients.User(bannedUser.UID).SendAsync(Api.OnAdminForcedReconnect).ConfigureAwait(false); } } @@ -149,7 +149,7 @@ namespace MareSynchronosServer.Hubs if (!IsAdmin || string.IsNullOrEmpty(dto.Hash)) return; var existingForbiddenFile = - await _dbContext.ForbiddenUploadEntries.SingleOrDefaultAsync(b => b.Hash == dto.Hash); + await _dbContext.ForbiddenUploadEntries.SingleOrDefaultAsync(b => b.Hash == dto.Hash).ConfigureAwait(false); if (existingForbiddenFile != null) { existingForbiddenFile.ForbiddenBy = dto.ForbiddenBy; @@ -161,12 +161,12 @@ namespace MareSynchronosServer.Hubs { Hash = dto.Hash, ForbiddenBy = dto.ForbiddenBy - }); + }).ConfigureAwait(false); } - await _dbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); - await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminUpdateOrAddForbiddenFile, dto); + await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminUpdateOrAddForbiddenFile, dto).ConfigureAwait(false); } } } diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs index bc5027b..ecbfc69 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs @@ -25,20 +25,20 @@ namespace MareSynchronosServer.Hubs [HubMethodName(Api.SendFileAbortUpload)] public async Task AbortUpload() { - _logger.LogInformation("User " + AuthenticatedUserId + " aborted upload"); + _logger.LogInformation("User {AuthenticatedUserId} aborted upload", AuthenticatedUserId); var userId = AuthenticatedUserId; var notUploadedFiles = _dbContext.Files.Where(f => !f.Uploaded && f.Uploader.UID == userId).ToList(); _dbContext.RemoveRange(notUploadedFiles); - await _dbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] [HubMethodName(Api.SendFileDeleteAllFiles)] public async Task DeleteAllFiles() { - _logger.LogInformation("User " + AuthenticatedUserId + " deleted all their files"); + _logger.LogInformation("User {AuthenticatedUserId} deleted all their files", AuthenticatedUserId); - var ownFiles = await _dbContext.Files.Where(f => f.Uploaded && f.Uploader.UID == AuthenticatedUserId).ToListAsync(); + var ownFiles = await _dbContext.Files.Where(f => f.Uploaded && f.Uploader.UID == AuthenticatedUserId).ToListAsync().ConfigureAwait(false); foreach (var file in ownFiles) { var fi = new FileInfo(Path.Combine(BasePath, file.Hash)); @@ -50,16 +50,16 @@ namespace MareSynchronosServer.Hubs } } _dbContext.Files.RemoveRange(ownFiles); - await _dbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] [HubMethodName(Api.InvokeGetFilesSizes)] public async Task> GetFilesSizes(List hashes) { - var allFiles = await _dbContext.Files.Where(f => hashes.Contains(f.Hash)).ToListAsync(); + var allFiles = await _dbContext.Files.Where(f => hashes.Contains(f.Hash)).ToListAsync().ConfigureAwait(false); var forbiddenFiles = await _dbContext.ForbiddenUploadEntries. - Where(f => hashes.Contains(f.Hash)).ToListAsync(); + Where(f => hashes.Contains(f.Hash)).ToListAsync().ConfigureAwait(false); List response = new(); foreach (var hash in hashes) { @@ -90,7 +90,7 @@ namespace MareSynchronosServer.Hubs if (!fileInfo.Exists && downloadFile != null) { _dbContext.Files.Remove(downloadFile); - await _dbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); } } @@ -103,7 +103,7 @@ namespace MareSynchronosServer.Hubs { var userUid = AuthenticatedUserId; return await _dbContext.Files.AsNoTracking() - .AnyAsync(f => f.Uploader.UID == userUid && !f.Uploaded); + .AnyAsync(f => f.Uploader.UID == userUid && !f.Uploaded).ConfigureAwait(false); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] @@ -111,12 +111,11 @@ namespace MareSynchronosServer.Hubs public async Task> SendFiles(List fileListHashes) { var userSentHashes = new HashSet(fileListHashes.Distinct()); - _logger.LogInformation($"User {AuthenticatedUserId} sending files: {userSentHashes.Count}"); + _logger.LogInformation("User {AuthenticatedUserId} sending files: {count}", AuthenticatedUserId, userSentHashes.Count); var notCoveredFiles = new Dictionary(); - // Todo: Check if a select can directly transform to hashset - var forbiddenFiles = await _dbContext.ForbiddenUploadEntries.AsNoTracking().Where(f => userSentHashes.Contains(f.Hash)).ToDictionaryAsync(f => f.Hash, f => f); - var existingFiles = await _dbContext.Files.AsNoTracking().Where(f => userSentHashes.Contains(f.Hash)).ToDictionaryAsync(f => f.Hash, f => f); - var uploader = await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId); + var forbiddenFiles = await _dbContext.ForbiddenUploadEntries.AsNoTracking().Where(f => userSentHashes.Contains(f.Hash)).ToDictionaryAsync(f => f.Hash, f => f).ConfigureAwait(false); + var existingFiles = await _dbContext.Files.AsNoTracking().Where(f => userSentHashes.Contains(f.Hash)).ToDictionaryAsync(f => f.Hash, f => f).ConfigureAwait(false); + var uploader = await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false); List fileCachesToUpload = new(); foreach (var file in userSentHashes) @@ -137,7 +136,7 @@ namespace MareSynchronosServer.Hubs } if (existingFiles.ContainsKey(file)) { continue; } - _logger.LogInformation("User " + AuthenticatedUserId + " needs upload: " + file); + _logger.LogInformation("User {AuthenticatedUserId} needs upload: {file}", AuthenticatedUserId, file); var userId = AuthenticatedUserId; fileCachesToUpload.Add(new FileCache() { @@ -152,8 +151,8 @@ namespace MareSynchronosServer.Hubs }; } //Save bulk - await _dbContext.Files.AddRangeAsync(fileCachesToUpload); - await _dbContext.SaveChangesAsync(); + await _dbContext.Files.AddRangeAsync(fileCachesToUpload).ConfigureAwait(false); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); return notCoveredFiles.Values.ToList(); } @@ -161,7 +160,7 @@ namespace MareSynchronosServer.Hubs [HubMethodName(Api.SendFileUploadFileStreamAsync)] public async Task UploadFileStreamAsync(string hash, IAsyncEnumerable fileContent) { - _logger.LogInformation("User " + AuthenticatedUserId + " uploading file: " + hash); + _logger.LogInformation("User {AuthenticatedUserId} uploading file: {hash}", AuthenticatedUserId, hash); var relatedFile = _dbContext.Files.SingleOrDefault(f => f.Hash == hash && f.Uploader.UID == AuthenticatedUserId && f.Uploaded == false); if (relatedFile == null) return; @@ -173,23 +172,23 @@ namespace MareSynchronosServer.Hubs long length = 0; try { - await foreach (var chunk in fileContent) + await foreach (var chunk in fileContent.ConfigureAwait(false)) { length += chunk.Length; - await fileStream.WriteAsync(chunk); + await fileStream.WriteAsync(chunk).ConfigureAwait(false); } - await fileStream.FlushAsync(); - await fileStream.DisposeAsync(); + await fileStream.FlushAsync().ConfigureAwait(false); + await fileStream.DisposeAsync().ConfigureAwait(false); } catch { try { - await fileStream.FlushAsync(); - await fileStream.DisposeAsync(); + await fileStream.FlushAsync().ConfigureAwait(false); + await fileStream.DisposeAsync().ConfigureAwait(false); _dbContext.Files.Remove(relatedFile); - await _dbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); } catch { @@ -203,20 +202,20 @@ namespace MareSynchronosServer.Hubs return; } - _logger.LogInformation("User " + AuthenticatedUserId + " upload finished: " + hash + ", size: " + length); + _logger.LogInformation("User {AuthenticatedUserId} upload finished: {hash}, size: {length}", AuthenticatedUserId, hash, length); try { - var decodedFile = LZ4.LZ4Codec.Unwrap(await File.ReadAllBytesAsync(tempFileName)); + var decodedFile = LZ4.LZ4Codec.Unwrap(await File.ReadAllBytesAsync(tempFileName).ConfigureAwait(false)); using var sha1 = SHA1.Create(); using var ms = new MemoryStream(decodedFile); - var computedHash = await sha1.ComputeHashAsync(ms); + var computedHash = await sha1.ComputeHashAsync(ms).ConfigureAwait(false); var computedHashString = BitConverter.ToString(computedHash).Replace("-", ""); if (hash != computedHashString) { - _logger.LogWarning($"Computed file hash was not expected file hash. Computed: {computedHashString}, Expected {hash}"); + _logger.LogWarning("Computed file hash was not expected file hash. Computed: {computedHashString}, Expected {hash}", computedHashString, hash); _dbContext.Remove(relatedFile); - await _dbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); return; } @@ -228,14 +227,14 @@ namespace MareSynchronosServer.Hubs MareMetrics.FilesTotal.Inc(); MareMetrics.FilesTotalSize.Inc(length); - await _dbContext.SaveChangesAsync(); - _logger.LogInformation("File " + hash + " added to DB"); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); + _logger.LogInformation("File {hash} added to DB", hash); } catch (Exception ex) { _logger.LogWarning(ex, "Upload failed"); _dbContext.Remove(relatedFile); - await _dbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); } } } diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs index 2b267b1..a3ec2f0 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs @@ -18,14 +18,14 @@ namespace MareSynchronosServer.Hubs [HubMethodName(Api.SendUserDeleteAccount)] public async Task DeleteAccount() { - _logger.LogInformation("User " + AuthenticatedUserId + " deleted their account"); + _logger.LogInformation("User {AuthenticatedUserId} deleted their account", AuthenticatedUserId); string userid = AuthenticatedUserId; - var userEntry = await _dbContext.Users.SingleAsync(u => u.UID == userid); - var ownPairData = await _dbContext.ClientPairs.Where(u => u.User.UID == userid).ToListAsync(); - var auth = await _dbContext.Auth.SingleAsync(u => u.UserUID == userid); - var lodestone = await _dbContext.LodeStoneAuth.SingleOrDefaultAsync(a => a.User.UID == userid); + var userEntry = await _dbContext.Users.SingleAsync(u => u.UID == userid).ConfigureAwait(false); + var ownPairData = await _dbContext.ClientPairs.Where(u => u.User.UID == userid).ToListAsync().ConfigureAwait(false); + var auth = await _dbContext.Auth.SingleAsync(u => u.UserUID == userid).ConfigureAwait(false); + var lodestone = await _dbContext.LodeStoneAuth.SingleOrDefaultAsync(a => a.User.UID == userid).ConfigureAwait(false); if (lodestone != null) { @@ -34,7 +34,7 @@ namespace MareSynchronosServer.Hubs while (_dbContext.Files.Any(f => f.Uploader == userEntry)) { - await Task.Delay(1000); + await Task.Delay(1000).ConfigureAwait(false); } SecretKeyAuthenticationHandler.RemoveAuthentication(userid); @@ -43,9 +43,9 @@ namespace MareSynchronosServer.Hubs MareMetrics.PairsPaused.Dec(ownPairData.Count(c => c.IsPaused)); _dbContext.RemoveRange(ownPairData); - await _dbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); var otherPairData = await _dbContext.ClientPairs.Include(u => u.User) - .Where(u => u.OtherUser.UID == userid).ToListAsync(); + .Where(u => u.OtherUser.UID == userid).ToListAsync().ConfigureAwait(false); foreach (var pair in otherPairData) { await Clients.User(pair.User.UID) @@ -53,38 +53,38 @@ namespace MareSynchronosServer.Hubs { OtherUID = userid, IsRemoved = true - }, userEntry.CharacterIdentification); + }, userEntry.CharacterIdentification).ConfigureAwait(false); } - MareMetrics.Pairs.Dec(otherPairData.Count()); + MareMetrics.Pairs.Dec(otherPairData.Count); MareMetrics.PairsPaused.Dec(otherPairData.Count(c => c.IsPaused)); MareMetrics.UsersRegistered.Dec(); _dbContext.RemoveRange(otherPairData); _dbContext.Remove(userEntry); _dbContext.Remove(auth); - await _dbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] [HubMethodName(Api.InvokeUserGetOnlineCharacters)] public async Task> GetOnlineCharacters() { - _logger.LogInformation("User " + AuthenticatedUserId + " requested online characters"); + _logger.LogInformation("User {AuthenticatedUserId} requested online characters", AuthenticatedUserId); - var ownUser = await GetAuthenticatedUserUntrackedAsync(); + var ownUser = await GetAuthenticatedUserUntrackedAsync().ConfigureAwait(false); var otherUsers = await _dbContext.ClientPairs.AsNoTracking() - .Include(u => u.User) - .Include(u => u.OtherUser) - .Where(w => w.User.UID == ownUser.UID && !w.IsPaused) - .Where(w => !string.IsNullOrEmpty(w.OtherUser.CharacterIdentification)) - .Select(e => e.OtherUser).ToListAsync(); + .Include(u => u.User) + .Include(u => u.OtherUser) + .Where(w => w.User.UID == ownUser.UID && !w.IsPaused) + .Where(w => !string.IsNullOrEmpty(w.OtherUser.CharacterIdentification)) + .Select(e => e.OtherUser).ToListAsync().ConfigureAwait(false); var otherEntries = await _dbContext.ClientPairs.AsNoTracking() .Include(u => u.User) - .Where(u => otherUsers.Any(e => e == u.User) && u.OtherUser == ownUser && !u.IsPaused).ToListAsync(); + .Where(u => otherUsers.Any(e => e == u.User) && u.OtherUser == ownUser && !u.IsPaused).ToListAsync().ConfigureAwait(false); - await Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync(Api.OnUserAddOnlinePairedPlayer, ownUser.CharacterIdentification); + await Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync(Api.OnUserAddOnlinePairedPlayer, ownUser.CharacterIdentification).ConfigureAwait(false); return otherEntries.Select(e => e.User.CharacterIdentification).Distinct().ToList(); } @@ -117,7 +117,7 @@ namespace MareSynchronosServer.Hubs IsSynced = otherEntry != null }; - return (await query.ToListAsync()).Select(f => new ClientPairDto() + return (await query.ToListAsync().ConfigureAwait(false)).Select(f => new ClientPairDto() { IsPaused = f.IsPaused, OtherUID = f.OtherUserUID, @@ -130,9 +130,9 @@ namespace MareSynchronosServer.Hubs [HubMethodName(Api.InvokeUserPushCharacterDataToVisibleClients)] public async Task PushCharacterDataToVisibleClients(CharacterCacheDto characterCache, List visibleCharacterIds) { - _logger.LogInformation("User " + AuthenticatedUserId + " pushing character data to " + visibleCharacterIds.Count + " visible clients"); + _logger.LogInformation("User {AuthenticatedUserId} pushing character data to {visibleCharacterIds} visible clients", AuthenticatedUserId, visibleCharacterIds.Count); - var user = await GetAuthenticatedUserUntrackedAsync(); + var user = await GetAuthenticatedUserUntrackedAsync().ConfigureAwait(false); var query = from userToOther in _dbContext.ClientPairs @@ -154,9 +154,9 @@ namespace MareSynchronosServer.Hubs && visibleCharacterIds.Contains(userToOther.OtherUser.CharacterIdentification) select otherToUser.UserUID; - var otherEntries = await query.ToListAsync(); + var otherEntries = await query.ToListAsync().ConfigureAwait(false); - await Clients.Users(otherEntries).SendAsync(Api.OnUserReceiveCharacterData, characterCache, user.CharacterIdentification); + await Clients.Users(otherEntries).SendAsync(Api.OnUserReceiveCharacterData, characterCache, user.CharacterIdentification).ConfigureAwait(false); MareMetrics.UserPushData.Inc(); MareMetrics.UserPushDataTo.Inc(otherEntries.Count); @@ -168,24 +168,24 @@ namespace MareSynchronosServer.Hubs { if (uid == AuthenticatedUserId) return; uid = uid.Trim(); - var user = await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId); + var user = await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false); var otherUser = await _dbContext.Users - .SingleOrDefaultAsync(u => u.UID == uid); + .SingleOrDefaultAsync(u => u.UID == uid).ConfigureAwait(false); var existingEntry = await _dbContext.ClientPairs.AsNoTracking() .FirstOrDefaultAsync(p => - p.User.UID == AuthenticatedUserId && p.OtherUser.UID == uid); + p.User.UID == AuthenticatedUserId && p.OtherUser.UID == uid).ConfigureAwait(false); if (otherUser == null || existingEntry != null) return; - _logger.LogInformation("User " + AuthenticatedUserId + " adding " + uid + " to whitelist"); + _logger.LogInformation("User {AuthenticatedUserId} adding {uid} to whitelist", AuthenticatedUserId, uid); ClientPair wl = new ClientPair() { IsPaused = false, OtherUser = otherUser, User = user }; - await _dbContext.ClientPairs.AddAsync(wl); - await _dbContext.SaveChangesAsync(); + await _dbContext.ClientPairs.AddAsync(wl).ConfigureAwait(false); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); var otherEntry = OppositeEntry(uid); await Clients.User(user.UID) .SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() @@ -194,7 +194,7 @@ namespace MareSynchronosServer.Hubs IsPaused = false, IsPausedFromOthers = otherEntry?.IsPaused ?? false, IsSynced = otherEntry != null - }, string.Empty); + }, string.Empty).ConfigureAwait(false); if (otherEntry != null) { await Clients.User(uid).SendAsync(Api.OnUserUpdateClientPairs, @@ -204,14 +204,14 @@ namespace MareSynchronosServer.Hubs IsPaused = otherEntry.IsPaused, IsPausedFromOthers = false, IsSynced = true - }, user.CharacterIdentification); + }, user.CharacterIdentification).ConfigureAwait(false); if (!string.IsNullOrEmpty(otherUser.CharacterIdentification)) { await Clients.User(user.UID) - .SendAsync(Api.OnUserAddOnlinePairedPlayer, otherUser.CharacterIdentification); + .SendAsync(Api.OnUserAddOnlinePairedPlayer, otherUser.CharacterIdentification).ConfigureAwait(false); await Clients.User(otherUser.UID) - .SendAsync(Api.OnUserAddOnlinePairedPlayer, user.CharacterIdentification); + .SendAsync(Api.OnUserAddOnlinePairedPlayer, user.CharacterIdentification).ConfigureAwait(false); } } @@ -223,15 +223,15 @@ namespace MareSynchronosServer.Hubs public async Task SendPairedClientPauseChange(string otherUserUid, bool isPaused) { if (otherUserUid == AuthenticatedUserId) return; - ClientPair pair = await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == AuthenticatedUserId && w.OtherUserUID == otherUserUid); + ClientPair pair = await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == AuthenticatedUserId && w.OtherUserUID == otherUserUid).ConfigureAwait(false); if (pair == null) return; - _logger.LogInformation("User " + AuthenticatedUserId + " changed pause status with " + otherUserUid + " to " + isPaused); + _logger.LogInformation("User {AuthenticatedUserId} changed pause status with {otherUserUid} to {isPaused}", AuthenticatedUserId, otherUserUid, isPaused); pair.IsPaused = isPaused; _dbContext.Update(pair); - await _dbContext.SaveChangesAsync(); - var selfCharaIdent = (await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId)).CharacterIdentification; - var otherCharaIdent = (await _dbContext.Users.SingleAsync(u => u.UID == otherUserUid)).CharacterIdentification; + await _dbContext.SaveChangesAsync().ConfigureAwait(false); + var selfCharaIdent = (await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false)).CharacterIdentification; + var otherCharaIdent = (await _dbContext.Users.SingleAsync(u => u.UID == otherUserUid).ConfigureAwait(false)).CharacterIdentification; var otherEntry = OppositeEntry(otherUserUid); await Clients.User(AuthenticatedUserId) @@ -241,7 +241,7 @@ namespace MareSynchronosServer.Hubs IsPaused = isPaused, IsPausedFromOthers = otherEntry?.IsPaused ?? false, IsSynced = otherEntry != null - }, otherCharaIdent); + }, otherCharaIdent).ConfigureAwait(false); if (otherEntry != null) { await Clients.User(otherUserUid).SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() @@ -250,7 +250,7 @@ namespace MareSynchronosServer.Hubs IsPaused = otherEntry.IsPaused, IsPausedFromOthers = isPaused, IsSynced = true - }, selfCharaIdent); + }, selfCharaIdent).ConfigureAwait(false); } if (isPaused) @@ -269,37 +269,37 @@ namespace MareSynchronosServer.Hubs { if (uid == AuthenticatedUserId) return; - var sender = await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId); - var otherUser = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == uid); + var sender = await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false); + var otherUser = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == uid).ConfigureAwait(false); if (otherUser == null) return; - _logger.LogInformation("User " + AuthenticatedUserId + " removed " + uid + " from whitelist"); + _logger.LogInformation("User {AuthenticatedUserId} removed {uid} from whitelist", AuthenticatedUserId, uid); ClientPair wl = - await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.User == sender && w.OtherUser == otherUser); + await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.User == sender && w.OtherUser == otherUser).ConfigureAwait(false); if (wl == null) return; _dbContext.ClientPairs.Remove(wl); - await _dbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); var otherEntry = OppositeEntry(uid); await Clients.User(sender.UID) .SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() { OtherUID = otherUser.UID, IsRemoved = true - }, otherUser.CharacterIdentification); + }, otherUser.CharacterIdentification).ConfigureAwait(false); if (otherEntry != null) { if (!string.IsNullOrEmpty(otherUser.CharacterIdentification)) { await Clients.User(sender.UID) - .SendAsync(Api.OnUserRemoveOnlinePairedPlayer, otherUser.CharacterIdentification); + .SendAsync(Api.OnUserRemoveOnlinePairedPlayer, otherUser.CharacterIdentification).ConfigureAwait(false); await Clients.User(otherUser.UID) - .SendAsync(Api.OnUserRemoveOnlinePairedPlayer, sender.CharacterIdentification); + .SendAsync(Api.OnUserRemoveOnlinePairedPlayer, sender.CharacterIdentification).ConfigureAwait(false); await Clients.User(otherUser.UID).SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() { OtherUID = sender.UID, IsPaused = otherEntry.IsPaused, IsPausedFromOthers = false, IsSynced = false - }, sender.CharacterIdentification); + }, sender.CharacterIdentification).ConfigureAwait(false); } } diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs index abce674..79d4911 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs @@ -41,15 +41,15 @@ namespace MareSynchronosServer.Hubs var userId = Context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; - _logger.LogInformation("Connection from " + userId + ", CI: " + characterIdentification); + _logger.LogInformation("Connection from {userId}, CI: {characterIdentification}", userId, characterIdentification); - await Clients.Caller.SendAsync(Api.OnUpdateSystemInfo, _systemInfoService.SystemInfoDto); + await Clients.Caller.SendAsync(Api.OnUpdateSystemInfo, _systemInfoService.SystemInfoDto).ConfigureAwait(false); - var isBanned = await _dbContext.BannedUsers.AsNoTracking().AnyAsync(u => u.CharacterIdentification == characterIdentification); + var isBanned = await _dbContext.BannedUsers.AsNoTracking().AnyAsync(u => u.CharacterIdentification == characterIdentification).ConfigureAwait(false); if (!string.IsNullOrEmpty(userId) && !isBanned && !string.IsNullOrEmpty(characterIdentification)) { - var user = (await _dbContext.Users.SingleAsync(u => u.UID == userId)); + var user = (await _dbContext.Users.SingleAsync(u => u.UID == userId).ConfigureAwait(false)); if (!string.IsNullOrEmpty(user.CharacterIdentification) && characterIdentification != user.CharacterIdentification) { return new ConnectionDto() @@ -64,7 +64,7 @@ namespace MareSynchronosServer.Hubs user.LastLoggedIn = DateTime.UtcNow; user.CharacterIdentification = characterIdentification; - await _dbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); return new ConnectionDto { ServerVersion = Api.Version, @@ -82,7 +82,7 @@ namespace MareSynchronosServer.Hubs public override Task OnConnectedAsync() { - _logger.LogInformation("Connection from " + contextAccessor.GetIpAddress()); + _logger.LogInformation("Connection from {ip}", contextAccessor.GetIpAddress()); MareMetrics.Connections.Inc(); return base.OnConnectedAsync(); } @@ -91,12 +91,12 @@ namespace MareSynchronosServer.Hubs { MareMetrics.Connections.Dec(); - var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == AuthenticatedUserId); + var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false); if (user != null && !string.IsNullOrEmpty(user.CharacterIdentification)) { MareMetrics.AuthorizedConnections.Dec(); - _logger.LogInformation("Disconnect from " + AuthenticatedUserId); + _logger.LogInformation("Disconnect from {id}", AuthenticatedUserId); var query = from userToOther in _dbContext.ClientPairs @@ -114,17 +114,17 @@ namespace MareSynchronosServer.Hubs && !userToOther.IsPaused && !otherToUser.IsPaused select otherToUser.UserUID; - var otherEntries = await query.ToListAsync(); + var otherEntries = await query.ToListAsync().ConfigureAwait(false); - await Clients.Users(otherEntries).SendAsync(Api.OnUserRemoveOnlinePairedPlayer, user.CharacterIdentification); + await Clients.Users(otherEntries).SendAsync(Api.OnUserRemoveOnlinePairedPlayer, user.CharacterIdentification).ConfigureAwait(false); _dbContext.RemoveRange(_dbContext.Files.Where(f => !f.Uploaded && f.UploaderUID == user.UID)); user.CharacterIdentification = null; - await _dbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); } - await base.OnDisconnectedAsync(exception); + await base.OnDisconnectedAsync(exception).ConfigureAwait(false); } public static string GenerateRandomString(int length, string allowableChars = null) @@ -149,7 +149,7 @@ namespace MareSynchronosServer.Hubs protected async Task GetAuthenticatedUserUntrackedAsync() { - return await _dbContext.Users.AsNoTrackingWithIdentityResolution().SingleAsync(u => u.UID == AuthenticatedUserId); + return await _dbContext.Users.AsNoTrackingWithIdentityResolution().SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false); } } } diff --git a/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj b/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj index fb58143..c787afe 100644 --- a/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj +++ b/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj @@ -14,6 +14,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs b/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs index f8a06cf..5562c37 100644 --- a/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs +++ b/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs @@ -40,7 +40,7 @@ public class SystemInfoService : IHostedService, IDisposable private void PushSystemInfo(object state) { ThreadPool.GetAvailableThreads(out int workerThreads, out int ioThreads); - _logger.LogInformation($"ThreadPool: {workerThreads} workers available, {ioThreads} IO workers available"); + _logger.LogInformation("ThreadPool: {workerThreads} workers available, {ioThreads} IO workers available", workerThreads, ioThreads); MareMetrics.AvailableWorkerThreads.Set(workerThreads); MareMetrics.AvailableIOWorkerThreads.Set(ioThreads); diff --git a/MareSynchronosServer/MareSynchronosServer/Throttling/SignalRLimitFilter.cs b/MareSynchronosServer/MareSynchronosServer/Throttling/SignalRLimitFilter.cs index a90cca2..2631928 100644 --- a/MareSynchronosServer/MareSynchronosServer/Throttling/SignalRLimitFilter.cs +++ b/MareSynchronosServer/MareSynchronosServer/Throttling/SignalRLimitFilter.cs @@ -37,25 +37,25 @@ public class SignalRLimitFilter : IHubFilter HttpVerb = "ws", ClientId = invocationContext.Context.UserIdentifier }; - foreach (var rule in await _processor.GetMatchingRulesAsync(client)) + foreach (var rule in await _processor.GetMatchingRulesAsync(client).ConfigureAwait(false)) { - var counter = await _processor.ProcessRequestAsync(client, rule); + var counter = await _processor.ProcessRequestAsync(client, rule).ConfigureAwait(false); if (counter.Count > rule.Limit) { var authUserId = invocationContext.Context.User.Claims?.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? "Unknown"; var retry = counter.Timestamp.RetryAfterFrom(rule); - logger.LogWarning($"Method rate limit triggered from {ip}/{authUserId}: {invocationContext.HubMethodName}"); + logger.LogWarning("Method rate limit triggered from {ip}/{authUserId}: {method}", ip, authUserId, invocationContext.HubMethodName); throw new HubException($"call limit {retry}"); } } - return await next(invocationContext); + return await next(invocationContext).ConfigureAwait(false); } // Optional method public async Task OnConnectedAsync(HubLifetimeContext context, Func next) { - await ConnectionLimiterSemaphore.WaitAsync(); + await ConnectionLimiterSemaphore.WaitAsync().ConfigureAwait(false); var ip = accessor.GetIpAddress(); var client = new ClientRequestIdentity { @@ -63,13 +63,13 @@ public class SignalRLimitFilter : IHubFilter Path = "Connect", HttpVerb = "ws", }; - foreach (var rule in await _processor.GetMatchingRulesAsync(client)) + foreach (var rule in await _processor.GetMatchingRulesAsync(client).ConfigureAwait(false)) { - var counter = await _processor.ProcessRequestAsync(client, rule); + var counter = await _processor.ProcessRequestAsync(client, rule).ConfigureAwait(false); if (counter.Count > rule.Limit) { var retry = counter.Timestamp.RetryAfterFrom(rule); - logger.LogWarning($"Connection rate limit triggered from {ip}"); + logger.LogWarning("Connection rate limit triggered from {ip}", ip); ConnectionLimiterSemaphore.Release(); throw new HubException($"Connection rate limit {retry}"); } @@ -77,8 +77,8 @@ public class SignalRLimitFilter : IHubFilter try { - await Task.Delay(250); - await next(context); + await Task.Delay(250).ConfigureAwait(false); + await next(context).ConfigureAwait(false); } catch (Exception ex) { @@ -93,7 +93,7 @@ public class SignalRLimitFilter : IHubFilter public async Task OnDisconnectedAsync( HubLifetimeContext context, Exception exception, Func next) { - await DisconnectLimiterSemaphore.WaitAsync(); + await DisconnectLimiterSemaphore.WaitAsync().ConfigureAwait(false); if (exception != null) { logger.LogWarning(exception, "InitialException on OnDisconnectedAsync"); @@ -101,8 +101,8 @@ public class SignalRLimitFilter : IHubFilter try { - await next(context, exception); - await Task.Delay(250); + await next(context, exception).ConfigureAwait(false); + await Task.Delay(250).ConfigureAwait(false); } catch (Exception e) { diff --git a/MareSynchronosServer/MareSynchronosServerTest/Hubs/MareHubTest.cs b/MareSynchronosServer/MareSynchronosServerTest/Hubs/MareHubTest.cs index e51407a..73764ac 100644 --- a/MareSynchronosServer/MareSynchronosServerTest/Hubs/MareHubTest.cs +++ b/MareSynchronosServer/MareSynchronosServerTest/Hubs/MareHubTest.cs @@ -73,7 +73,7 @@ namespace MareSynchronosServerTest.Hubs { hub.Clients = clientsMock.Object; hub.Context = clientContextMock.Object; - await hub.OnDisconnectedAsync(new Exception("Test Exception")); + await hub.OnDisconnectedAsync(new Exception("Test Exception")).ConfigureAwait(false); clientsMock.Verify(x => x.Users(It.Is>(x => x.Count() == 2 && x[0] == "User2" && x[1] == "User3")), Times.Once); clientProxyMock.Verify(x => x.SendCoreAsync(It.IsAny(), It.Is(o => (string)o[0] == "Ident1"), It.IsAny()), Times.Once); From 6c243d0247dc427b55e7f94731390737c0c92512 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Mon, 22 Aug 2022 11:21:28 +0200 Subject: [PATCH 2/3] move shared content to shared project --- MareSynchronosServer/MareSynchronosServer.sln | 8 +++- .../MareSynchronosServer/CleanupService.cs | 6 +-- .../Data/MareDbContext.cs | 39 ------------------- .../Discord/DiscordBot.cs | 4 +- .../Hubs/MareHub.Admin.cs | 4 +- .../Hubs/MareHub.Files.cs | 4 +- .../MareSynchronosServer/Hubs/MareHub.User.cs | 4 +- .../MareSynchronosServer/Hubs/MareHub.cs | 7 ++-- .../IdBasedUserIdProvider.cs | 2 +- .../MareSynchronosServer.csproj | 7 +--- .../MareSynchronosServer/Program.cs | 3 +- .../MareSynchronosServer/Startup.cs | 4 +- .../MareSynchronosServer/SystemInfoService.cs | 2 +- .../Hubs/MareHubTest.cs | 6 +-- .../SecretKeyAuthenticationHandler.cs | 13 +++---- .../Data/MareDbContext.cs | 38 ++++++++++++++++++ .../Extensions.cs | 0 .../MareSynchronosShared.csproj | 25 ++++++++++++ .../Metrics/MareMetrics.cs | 2 +- .../20220731210149_InitialCreate.Designer.cs | 2 +- .../20220731210149_InitialCreate.cs | 0 ...731211419_RenameLowerSnakeCase.Designer.cs | 2 +- .../20220731211419_RenameLowerSnakeCase.cs | 0 ...0220801121419_AddLodestoneAuth.Designer.cs | 2 +- .../20220801121419_AddLodestoneAuth.cs | 0 ...ullableLodestoneAuthProperties.Designer.cs | 2 +- ...2103_AddNullableLodestoneAuthProperties.cs | 0 ...6103053_AddBannedRegistrations.Designer.cs | 2 +- .../20220806103053_AddBannedRegistrations.cs | 0 ...16170426_SetMaxLimitForStrings.Designer.cs | 2 +- .../20220816170426_SetMaxLimitForStrings.cs | 0 .../Migrations/MareDbContextModelSnapshot.cs | 2 +- .../Models/Auth.cs | 2 +- .../Models/Banned.cs | 2 +- .../Models/BannedRegistrations.cs | 2 +- .../Models/ClientPair.cs | 2 +- .../Models/FileCache.cs | 2 +- .../Models/ForbiddenUploadEntry.cs | 2 +- .../Models/LodeStoneAuth.cs | 5 +-- .../Models/User.cs | 5 +-- 40 files changed, 117 insertions(+), 97 deletions(-) delete mode 100644 MareSynchronosServer/MareSynchronosServer/Data/MareDbContext.cs rename MareSynchronosServer/MareSynchronosServer/{Authentication => }/IdBasedUserIdProvider.cs (88%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Authentication/SecretKeyAuthenticationHandler.cs (97%) create mode 100644 MareSynchronosServer/MareSynchronosShared/Data/MareDbContext.cs rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Extensions.cs (100%) create mode 100644 MareSynchronosServer/MareSynchronosShared/MareSynchronosShared.csproj rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Metrics/MareMetrics.cs (99%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Migrations/20220731210149_InitialCreate.Designer.cs (99%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Migrations/20220731210149_InitialCreate.cs (100%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Migrations/20220731211419_RenameLowerSnakeCase.Designer.cs (99%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Migrations/20220731211419_RenameLowerSnakeCase.cs (100%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Migrations/20220801121419_AddLodestoneAuth.Designer.cs (99%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Migrations/20220801121419_AddLodestoneAuth.cs (100%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Migrations/20220801122103_AddNullableLodestoneAuthProperties.Designer.cs (99%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Migrations/20220801122103_AddNullableLodestoneAuthProperties.cs (100%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Migrations/20220806103053_AddBannedRegistrations.Designer.cs (99%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Migrations/20220806103053_AddBannedRegistrations.cs (100%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Migrations/20220816170426_SetMaxLimitForStrings.Designer.cs (99%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Migrations/20220816170426_SetMaxLimitForStrings.cs (100%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Migrations/MareDbContextModelSnapshot.cs (99%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Models/Auth.cs (87%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Models/Banned.cs (88%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Models/BannedRegistrations.cs (84%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Models/ClientPair.cs (92%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Models/FileCache.cs (91%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Models/ForbiddenUploadEntry.cs (89%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Models/LodeStoneAuth.cs (78%) rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosShared}/Models/User.cs (81%) diff --git a/MareSynchronosServer/MareSynchronosServer.sln b/MareSynchronosServer/MareSynchronosServer.sln index 8f013f3..461354f 100644 --- a/MareSynchronosServer/MareSynchronosServer.sln +++ b/MareSynchronosServer/MareSynchronosServer.sln @@ -7,7 +7,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronosServer", "Mar EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronos.API", "..\MareAPI\MareSynchronosAPI\MareSynchronos.API.csproj", "{326BFB1B-5571-47A6-8513-1FFDB32D53B0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MareSynchronosServerTest", "MareSynchronosServerTest\MareSynchronosServerTest.csproj", "{25A82A2A-35C2-4EE0-A0E8-DFDD77978DDA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronosServerTest", "MareSynchronosServerTest\MareSynchronosServerTest.csproj", "{25A82A2A-35C2-4EE0-A0E8-DFDD77978DDA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MareSynchronosShared", "MareSynchronosShared\MareSynchronosShared.csproj", "{67B1461D-E215-4BA8-A64D-E1836724D5E6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,6 +29,10 @@ Global {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.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.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/MareSynchronosServer/MareSynchronosServer/CleanupService.cs b/MareSynchronosServer/MareSynchronosServer/CleanupService.cs index 401c496..5eff01d 100644 --- a/MareSynchronosServer/MareSynchronosServer/CleanupService.cs +++ b/MareSynchronosServer/MareSynchronosServer/CleanupService.cs @@ -4,10 +4,10 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MareSynchronosServer.Authentication; -using MareSynchronosServer.Data; using MareSynchronosServer.Metrics; -using MareSynchronosServer.Models; +using MareSynchronosShared.Authentication; +using MareSynchronosShared.Data; +using MareSynchronosShared.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; diff --git a/MareSynchronosServer/MareSynchronosServer/Data/MareDbContext.cs b/MareSynchronosServer/MareSynchronosServer/Data/MareDbContext.cs deleted file mode 100644 index 571436a..0000000 --- a/MareSynchronosServer/MareSynchronosServer/Data/MareDbContext.cs +++ /dev/null @@ -1,39 +0,0 @@ -using MareSynchronosServer.Models; -using Microsoft.EntityFrameworkCore; - -namespace MareSynchronosServer.Data -{ - public class MareDbContext : DbContext - { - public MareDbContext(DbContextOptions options) : base(options) - { - } - - public DbSet Users { get; set; } - public DbSet Files { get; set; } - public DbSet ClientPairs { get; set; } - public DbSet ForbiddenUploadEntries { get; set; } - public DbSet BannedUsers { get; set; } - public DbSet Auth { get; set; } - public DbSet LodeStoneAuth { get; set; } - public DbSet BannedRegistrations { get; set; } - - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity().ToTable("auth"); - modelBuilder.Entity().ToTable("users"); - modelBuilder.Entity().HasIndex(c => c.CharacterIdentification); - modelBuilder.Entity().ToTable("file_caches"); - modelBuilder.Entity().HasIndex(c => c.UploaderUID); - modelBuilder.Entity().ToTable("client_pairs"); - modelBuilder.Entity().HasKey(u => new { u.UserUID, u.OtherUserUID }); - modelBuilder.Entity().HasIndex(c => c.UserUID); - modelBuilder.Entity().HasIndex(c => c.OtherUserUID); - modelBuilder.Entity().ToTable("forbidden_upload_entries"); - modelBuilder.Entity().ToTable("banned_users"); - modelBuilder.Entity().ToTable("lodestone_auth"); - modelBuilder.Entity().ToTable("banned_registrations"); - } - } -} diff --git a/MareSynchronosServer/MareSynchronosServer/Discord/DiscordBot.cs b/MareSynchronosServer/MareSynchronosServer/Discord/DiscordBot.cs index de4df59..46b5887 100644 --- a/MareSynchronosServer/MareSynchronosServer/Discord/DiscordBot.cs +++ b/MareSynchronosServer/MareSynchronosServer/Discord/DiscordBot.cs @@ -1,9 +1,7 @@ using Discord; using Discord.WebSocket; -using MareSynchronosServer.Data; using MareSynchronosServer.Hubs; using MareSynchronosServer.Metrics; -using MareSynchronosServer.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -18,6 +16,8 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using MareSynchronosShared.Data; +using MareSynchronosShared.Models; namespace MareSynchronosServer.Discord { diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs index 548204a..23f5271 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs @@ -2,8 +2,8 @@ using System.Linq; using System.Threading.Tasks; using MareSynchronos.API; -using MareSynchronosServer.Authentication; -using MareSynchronosServer.Models; +using MareSynchronosShared.Authentication; +using MareSynchronosShared.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs index ecbfc69..2f31a0c 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs @@ -7,9 +7,9 @@ using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using MareSynchronos.API; -using MareSynchronosServer.Authentication; using MareSynchronosServer.Metrics; -using MareSynchronosServer.Models; +using MareSynchronosShared.Authentication; +using MareSynchronosShared.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs index a3ec2f0..961756c 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs @@ -2,9 +2,9 @@ using System.Linq; using System.Threading.Tasks; using MareSynchronos.API; -using MareSynchronosServer.Authentication; using MareSynchronosServer.Metrics; -using MareSynchronosServer.Models; +using MareSynchronosShared.Authentication; +using MareSynchronosShared.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs index 79d4911..e98aa6a 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs @@ -4,9 +4,10 @@ using System.Security.Claims; using System.Security.Cryptography; using System.Threading.Tasks; using MareSynchronos.API; -using MareSynchronosServer.Authentication; -using MareSynchronosServer.Data; using MareSynchronosServer.Metrics; +using MareSynchronosShared.Authentication; +using MareSynchronosShared.Data; +using MareSynchronosShared.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.SignalR; @@ -147,7 +148,7 @@ namespace MareSynchronosServer.Hubs protected string AuthenticatedUserId => Context.User?.Claims?.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? "Unknown"; - protected async Task GetAuthenticatedUserUntrackedAsync() + protected async Task GetAuthenticatedUserUntrackedAsync() { return await _dbContext.Users.AsNoTrackingWithIdentityResolution().SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false); } diff --git a/MareSynchronosServer/MareSynchronosServer/Authentication/IdBasedUserIdProvider.cs b/MareSynchronosServer/MareSynchronosServer/IdBasedUserIdProvider.cs similarity index 88% rename from MareSynchronosServer/MareSynchronosServer/Authentication/IdBasedUserIdProvider.cs rename to MareSynchronosServer/MareSynchronosServer/IdBasedUserIdProvider.cs index 05aef38..2da4d53 100644 --- a/MareSynchronosServer/MareSynchronosServer/Authentication/IdBasedUserIdProvider.cs +++ b/MareSynchronosServer/MareSynchronosServer/IdBasedUserIdProvider.cs @@ -2,7 +2,7 @@ using System.Security.Claims; using Microsoft.AspNetCore.SignalR; -namespace MareSynchronosServer.Authentication +namespace MareSynchronosServer { public class IdBasedUserIdProvider : IUserIdProvider { diff --git a/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj b/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj index c787afe..55c42ec 100644 --- a/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj +++ b/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj @@ -26,18 +26,13 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + diff --git a/MareSynchronosServer/MareSynchronosServer/Program.cs b/MareSynchronosServer/MareSynchronosServer/Program.cs index 264d8e4..d3c1aed 100644 --- a/MareSynchronosServer/MareSynchronosServer/Program.cs +++ b/MareSynchronosServer/MareSynchronosServer/Program.cs @@ -2,14 +2,13 @@ using System; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using System.Linq; -using MareSynchronosServer.Data; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using MareSynchronosServer.Metrics; -using MareSynchronosServer.Models; using System.Collections.Generic; +using MareSynchronosShared.Data; namespace MareSynchronosServer { diff --git a/MareSynchronosServer/MareSynchronosServer/Startup.cs b/MareSynchronosServer/MareSynchronosServer/Startup.cs index 9b862d1..a6b8a1d 100644 --- a/MareSynchronosServer/MareSynchronosServer/Startup.cs +++ b/MareSynchronosServer/MareSynchronosServer/Startup.cs @@ -5,8 +5,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using MareSynchronosServer.Authentication; -using MareSynchronosServer.Data; using MareSynchronosServer.Hubs; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http.Connections; @@ -18,6 +16,8 @@ using MareSynchronosServer.Discord; using AspNetCoreRateLimit; using MareSynchronosServer.Throttling; using Ben.Diagnostics; +using MareSynchronosShared.Authentication; +using MareSynchronosShared.Data; namespace MareSynchronosServer { diff --git a/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs b/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs index 5562c37..9c9ad62 100644 --- a/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs +++ b/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs @@ -3,9 +3,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using MareSynchronos.API; -using MareSynchronosServer.Data; using MareSynchronosServer.Hubs; using MareSynchronosServer.Metrics; +using MareSynchronosShared.Data; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; diff --git a/MareSynchronosServer/MareSynchronosServerTest/Hubs/MareHubTest.cs b/MareSynchronosServer/MareSynchronosServerTest/Hubs/MareHubTest.cs index 73764ac..4f8394e 100644 --- a/MareSynchronosServer/MareSynchronosServerTest/Hubs/MareHubTest.cs +++ b/MareSynchronosServer/MareSynchronosServerTest/Hubs/MareHubTest.cs @@ -1,6 +1,4 @@ -using MareSynchronosServer.Data; -using MareSynchronosServer.Hubs; -using MareSynchronosServer.Models; +using MareSynchronosServer.Hubs; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; @@ -13,6 +11,8 @@ using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; +using MareSynchronosShared.Data; +using MareSynchronosShared.Models; namespace MareSynchronosServerTest.Hubs { public class MareHubTest { diff --git a/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs b/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthenticationHandler.cs similarity index 97% rename from MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs rename to MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthenticationHandler.cs index 3f26b89..57ab121 100644 --- a/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs +++ b/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthenticationHandler.cs @@ -1,22 +1,19 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; +using System.Security.Claims; using System.Security.Cryptography; using System.Text; using System.Text.Encodings.Web; -using System.Threading; -using System.Threading.Tasks; -using MareSynchronosServer.Data; +using MareSynchronosServer; using MareSynchronosServer.Metrics; +using MareSynchronosShared.Data; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using ISystemClock = Microsoft.AspNetCore.Authentication.ISystemClock; -namespace MareSynchronosServer.Authentication +namespace MareSynchronosShared.Authentication { public class FailedAuthorization : IDisposable { diff --git a/MareSynchronosServer/MareSynchronosShared/Data/MareDbContext.cs b/MareSynchronosServer/MareSynchronosShared/Data/MareDbContext.cs new file mode 100644 index 0000000..b20ca3b --- /dev/null +++ b/MareSynchronosServer/MareSynchronosShared/Data/MareDbContext.cs @@ -0,0 +1,38 @@ +using MareSynchronosShared.Models; +using Microsoft.EntityFrameworkCore; + +namespace MareSynchronosShared.Data; + +public class MareDbContext : DbContext +{ + public MareDbContext(DbContextOptions options) : base(options) + { + } + + public DbSet Users { get; set; } + public DbSet Files { get; set; } + public DbSet ClientPairs { get; set; } + public DbSet ForbiddenUploadEntries { get; set; } + public DbSet BannedUsers { get; set; } + public DbSet Auth { get; set; } + public DbSet LodeStoneAuth { get; set; } + public DbSet BannedRegistrations { get; set; } + + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().ToTable("auth"); + modelBuilder.Entity().ToTable("users"); + modelBuilder.Entity().HasIndex(c => c.CharacterIdentification); + modelBuilder.Entity().ToTable("file_caches"); + modelBuilder.Entity().HasIndex(c => c.UploaderUID); + modelBuilder.Entity().ToTable("client_pairs"); + modelBuilder.Entity().HasKey(u => new { u.UserUID, u.OtherUserUID }); + modelBuilder.Entity().HasIndex(c => c.UserUID); + modelBuilder.Entity().HasIndex(c => c.OtherUserUID); + modelBuilder.Entity().ToTable("forbidden_upload_entries"); + modelBuilder.Entity().ToTable("banned_users"); + modelBuilder.Entity().ToTable("lodestone_auth"); + modelBuilder.Entity().ToTable("banned_registrations"); + } +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosServer/Extensions.cs b/MareSynchronosServer/MareSynchronosShared/Extensions.cs similarity index 100% rename from MareSynchronosServer/MareSynchronosServer/Extensions.cs rename to MareSynchronosServer/MareSynchronosShared/Extensions.cs diff --git a/MareSynchronosServer/MareSynchronosShared/MareSynchronosShared.csproj b/MareSynchronosServer/MareSynchronosShared/MareSynchronosShared.csproj new file mode 100644 index 0000000..f81b7f4 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosShared/MareSynchronosShared.csproj @@ -0,0 +1,25 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + diff --git a/MareSynchronosServer/MareSynchronosServer/Metrics/MareMetrics.cs b/MareSynchronosServer/MareSynchronosShared/Metrics/MareMetrics.cs similarity index 99% rename from MareSynchronosServer/MareSynchronosServer/Metrics/MareMetrics.cs rename to MareSynchronosServer/MareSynchronosShared/Metrics/MareMetrics.cs index a78798d..7cd69d2 100644 --- a/MareSynchronosServer/MareSynchronosServer/Metrics/MareMetrics.cs +++ b/MareSynchronosServer/MareSynchronosShared/Metrics/MareMetrics.cs @@ -1,6 +1,6 @@ using System.IO; using System.Linq; -using MareSynchronosServer.Data; +using MareSynchronosShared.Data; using Microsoft.Extensions.Configuration; using Prometheus; diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220731210149_InitialCreate.Designer.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20220731210149_InitialCreate.Designer.cs similarity index 99% rename from MareSynchronosServer/MareSynchronosServer/Migrations/20220731210149_InitialCreate.Designer.cs rename to MareSynchronosServer/MareSynchronosShared/Migrations/20220731210149_InitialCreate.Designer.cs index ddfded1..31ea23a 100644 --- a/MareSynchronosServer/MareSynchronosServer/Migrations/20220731210149_InitialCreate.Designer.cs +++ b/MareSynchronosServer/MareSynchronosShared/Migrations/20220731210149_InitialCreate.Designer.cs @@ -1,6 +1,6 @@ // using System; -using MareSynchronosServer.Data; +using MareSynchronosShared.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220731210149_InitialCreate.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20220731210149_InitialCreate.cs similarity index 100% rename from MareSynchronosServer/MareSynchronosServer/Migrations/20220731210149_InitialCreate.cs rename to MareSynchronosServer/MareSynchronosShared/Migrations/20220731210149_InitialCreate.cs diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220731211419_RenameLowerSnakeCase.Designer.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20220731211419_RenameLowerSnakeCase.Designer.cs similarity index 99% rename from MareSynchronosServer/MareSynchronosServer/Migrations/20220731211419_RenameLowerSnakeCase.Designer.cs rename to MareSynchronosServer/MareSynchronosShared/Migrations/20220731211419_RenameLowerSnakeCase.Designer.cs index 9c8495c..59c7501 100644 --- a/MareSynchronosServer/MareSynchronosServer/Migrations/20220731211419_RenameLowerSnakeCase.Designer.cs +++ b/MareSynchronosServer/MareSynchronosShared/Migrations/20220731211419_RenameLowerSnakeCase.Designer.cs @@ -1,6 +1,6 @@ // using System; -using MareSynchronosServer.Data; +using MareSynchronosShared.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220731211419_RenameLowerSnakeCase.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20220731211419_RenameLowerSnakeCase.cs similarity index 100% rename from MareSynchronosServer/MareSynchronosServer/Migrations/20220731211419_RenameLowerSnakeCase.cs rename to MareSynchronosServer/MareSynchronosShared/Migrations/20220731211419_RenameLowerSnakeCase.cs diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220801121419_AddLodestoneAuth.Designer.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20220801121419_AddLodestoneAuth.Designer.cs similarity index 99% rename from MareSynchronosServer/MareSynchronosServer/Migrations/20220801121419_AddLodestoneAuth.Designer.cs rename to MareSynchronosServer/MareSynchronosShared/Migrations/20220801121419_AddLodestoneAuth.Designer.cs index 0476cdc..5bab84a 100644 --- a/MareSynchronosServer/MareSynchronosServer/Migrations/20220801121419_AddLodestoneAuth.Designer.cs +++ b/MareSynchronosServer/MareSynchronosShared/Migrations/20220801121419_AddLodestoneAuth.Designer.cs @@ -1,6 +1,6 @@ // using System; -using MareSynchronosServer.Data; +using MareSynchronosShared.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220801121419_AddLodestoneAuth.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20220801121419_AddLodestoneAuth.cs similarity index 100% rename from MareSynchronosServer/MareSynchronosServer/Migrations/20220801121419_AddLodestoneAuth.cs rename to MareSynchronosServer/MareSynchronosShared/Migrations/20220801121419_AddLodestoneAuth.cs diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220801122103_AddNullableLodestoneAuthProperties.Designer.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20220801122103_AddNullableLodestoneAuthProperties.Designer.cs similarity index 99% rename from MareSynchronosServer/MareSynchronosServer/Migrations/20220801122103_AddNullableLodestoneAuthProperties.Designer.cs rename to MareSynchronosServer/MareSynchronosShared/Migrations/20220801122103_AddNullableLodestoneAuthProperties.Designer.cs index 54c6aa5..3b29f0d 100644 --- a/MareSynchronosServer/MareSynchronosServer/Migrations/20220801122103_AddNullableLodestoneAuthProperties.Designer.cs +++ b/MareSynchronosServer/MareSynchronosShared/Migrations/20220801122103_AddNullableLodestoneAuthProperties.Designer.cs @@ -1,6 +1,6 @@ // using System; -using MareSynchronosServer.Data; +using MareSynchronosShared.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220801122103_AddNullableLodestoneAuthProperties.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20220801122103_AddNullableLodestoneAuthProperties.cs similarity index 100% rename from MareSynchronosServer/MareSynchronosServer/Migrations/20220801122103_AddNullableLodestoneAuthProperties.cs rename to MareSynchronosServer/MareSynchronosShared/Migrations/20220801122103_AddNullableLodestoneAuthProperties.cs diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220806103053_AddBannedRegistrations.Designer.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20220806103053_AddBannedRegistrations.Designer.cs similarity index 99% rename from MareSynchronosServer/MareSynchronosServer/Migrations/20220806103053_AddBannedRegistrations.Designer.cs rename to MareSynchronosServer/MareSynchronosShared/Migrations/20220806103053_AddBannedRegistrations.Designer.cs index f7d9af7..82a1dcf 100644 --- a/MareSynchronosServer/MareSynchronosServer/Migrations/20220806103053_AddBannedRegistrations.Designer.cs +++ b/MareSynchronosServer/MareSynchronosShared/Migrations/20220806103053_AddBannedRegistrations.Designer.cs @@ -1,6 +1,6 @@ // using System; -using MareSynchronosServer.Data; +using MareSynchronosShared.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220806103053_AddBannedRegistrations.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20220806103053_AddBannedRegistrations.cs similarity index 100% rename from MareSynchronosServer/MareSynchronosServer/Migrations/20220806103053_AddBannedRegistrations.cs rename to MareSynchronosServer/MareSynchronosShared/Migrations/20220806103053_AddBannedRegistrations.cs diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220816170426_SetMaxLimitForStrings.Designer.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20220816170426_SetMaxLimitForStrings.Designer.cs similarity index 99% rename from MareSynchronosServer/MareSynchronosServer/Migrations/20220816170426_SetMaxLimitForStrings.Designer.cs rename to MareSynchronosServer/MareSynchronosShared/Migrations/20220816170426_SetMaxLimitForStrings.Designer.cs index 6852d4e..66a6821 100644 --- a/MareSynchronosServer/MareSynchronosServer/Migrations/20220816170426_SetMaxLimitForStrings.Designer.cs +++ b/MareSynchronosServer/MareSynchronosShared/Migrations/20220816170426_SetMaxLimitForStrings.Designer.cs @@ -1,6 +1,6 @@ // using System; -using MareSynchronosServer.Data; +using MareSynchronosShared.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220816170426_SetMaxLimitForStrings.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20220816170426_SetMaxLimitForStrings.cs similarity index 100% rename from MareSynchronosServer/MareSynchronosServer/Migrations/20220816170426_SetMaxLimitForStrings.cs rename to MareSynchronosServer/MareSynchronosShared/Migrations/20220816170426_SetMaxLimitForStrings.cs diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/MareDbContextModelSnapshot.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/MareDbContextModelSnapshot.cs similarity index 99% rename from MareSynchronosServer/MareSynchronosServer/Migrations/MareDbContextModelSnapshot.cs rename to MareSynchronosServer/MareSynchronosShared/Migrations/MareDbContextModelSnapshot.cs index 0e6cb67..b01ed50 100644 --- a/MareSynchronosServer/MareSynchronosServer/Migrations/MareDbContextModelSnapshot.cs +++ b/MareSynchronosServer/MareSynchronosShared/Migrations/MareDbContextModelSnapshot.cs @@ -1,6 +1,6 @@ // using System; -using MareSynchronosServer.Data; +using MareSynchronosShared.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; diff --git a/MareSynchronosServer/MareSynchronosServer/Models/Auth.cs b/MareSynchronosServer/MareSynchronosShared/Models/Auth.cs similarity index 87% rename from MareSynchronosServer/MareSynchronosServer/Models/Auth.cs rename to MareSynchronosServer/MareSynchronosShared/Models/Auth.cs index f154a73..1f8bf2c 100644 --- a/MareSynchronosServer/MareSynchronosServer/Models/Auth.cs +++ b/MareSynchronosServer/MareSynchronosShared/Models/Auth.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace MareSynchronosServer.Models +namespace MareSynchronosShared.Models { public class Auth { diff --git a/MareSynchronosServer/MareSynchronosServer/Models/Banned.cs b/MareSynchronosServer/MareSynchronosShared/Models/Banned.cs similarity index 88% rename from MareSynchronosServer/MareSynchronosServer/Models/Banned.cs rename to MareSynchronosServer/MareSynchronosShared/Models/Banned.cs index 63d3958..b56e02a 100644 --- a/MareSynchronosServer/MareSynchronosServer/Models/Banned.cs +++ b/MareSynchronosServer/MareSynchronosShared/Models/Banned.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace MareSynchronosServer.Models +namespace MareSynchronosShared.Models { public class Banned { diff --git a/MareSynchronosServer/MareSynchronosServer/Models/BannedRegistrations.cs b/MareSynchronosServer/MareSynchronosShared/Models/BannedRegistrations.cs similarity index 84% rename from MareSynchronosServer/MareSynchronosServer/Models/BannedRegistrations.cs rename to MareSynchronosServer/MareSynchronosShared/Models/BannedRegistrations.cs index f811399..b26283a 100644 --- a/MareSynchronosServer/MareSynchronosServer/Models/BannedRegistrations.cs +++ b/MareSynchronosServer/MareSynchronosShared/Models/BannedRegistrations.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace MareSynchronosServer.Models +namespace MareSynchronosShared.Models { public class BannedRegistrations { diff --git a/MareSynchronosServer/MareSynchronosServer/Models/ClientPair.cs b/MareSynchronosServer/MareSynchronosShared/Models/ClientPair.cs similarity index 92% rename from MareSynchronosServer/MareSynchronosServer/Models/ClientPair.cs rename to MareSynchronosServer/MareSynchronosShared/Models/ClientPair.cs index 0000630..8cabbb5 100644 --- a/MareSynchronosServer/MareSynchronosServer/Models/ClientPair.cs +++ b/MareSynchronosServer/MareSynchronosShared/Models/ClientPair.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace MareSynchronosServer.Models +namespace MareSynchronosShared.Models { public class ClientPair { diff --git a/MareSynchronosServer/MareSynchronosServer/Models/FileCache.cs b/MareSynchronosServer/MareSynchronosShared/Models/FileCache.cs similarity index 91% rename from MareSynchronosServer/MareSynchronosServer/Models/FileCache.cs rename to MareSynchronosServer/MareSynchronosShared/Models/FileCache.cs index d84f2a6..a239837 100644 --- a/MareSynchronosServer/MareSynchronosServer/Models/FileCache.cs +++ b/MareSynchronosServer/MareSynchronosShared/Models/FileCache.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace MareSynchronosServer.Models +namespace MareSynchronosShared.Models { public class FileCache { diff --git a/MareSynchronosServer/MareSynchronosServer/Models/ForbiddenUploadEntry.cs b/MareSynchronosServer/MareSynchronosShared/Models/ForbiddenUploadEntry.cs similarity index 89% rename from MareSynchronosServer/MareSynchronosServer/Models/ForbiddenUploadEntry.cs rename to MareSynchronosServer/MareSynchronosShared/Models/ForbiddenUploadEntry.cs index cf3e4ae..a558423 100644 --- a/MareSynchronosServer/MareSynchronosServer/Models/ForbiddenUploadEntry.cs +++ b/MareSynchronosServer/MareSynchronosShared/Models/ForbiddenUploadEntry.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace MareSynchronosServer.Models +namespace MareSynchronosShared.Models { public class ForbiddenUploadEntry { diff --git a/MareSynchronosServer/MareSynchronosServer/Models/LodeStoneAuth.cs b/MareSynchronosServer/MareSynchronosShared/Models/LodeStoneAuth.cs similarity index 78% rename from MareSynchronosServer/MareSynchronosServer/Models/LodeStoneAuth.cs rename to MareSynchronosServer/MareSynchronosShared/Models/LodeStoneAuth.cs index 976a52d..38448de 100644 --- a/MareSynchronosServer/MareSynchronosServer/Models/LodeStoneAuth.cs +++ b/MareSynchronosServer/MareSynchronosShared/Models/LodeStoneAuth.cs @@ -1,7 +1,6 @@ -using System; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; -namespace MareSynchronosServer.Models +namespace MareSynchronosShared.Models { public class LodeStoneAuth { diff --git a/MareSynchronosServer/MareSynchronosServer/Models/User.cs b/MareSynchronosServer/MareSynchronosShared/Models/User.cs similarity index 81% rename from MareSynchronosServer/MareSynchronosServer/Models/User.cs rename to MareSynchronosServer/MareSynchronosShared/Models/User.cs index ff4fcd2..2e5a31f 100644 --- a/MareSynchronosServer/MareSynchronosServer/Models/User.cs +++ b/MareSynchronosServer/MareSynchronosShared/Models/User.cs @@ -1,7 +1,6 @@ -using System; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; -namespace MareSynchronosServer.Models +namespace MareSynchronosShared.Models { public class User { From f9e4fd4f2d9c67d5983a54e1b0ff8e7a1a84b821 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Mon, 22 Aug 2022 14:24:47 +0200 Subject: [PATCH 3/3] minor refactoring --- MareSynchronosServer/MareSynchronosServer.sln | 14 +- .../Discord/DiscordBot.cs | 431 ----------------- .../{ => Hubs}/IdBasedUserIdProvider.cs | 2 +- .../Hubs/MareHub.Admin.cs | 16 +- .../Hubs/MareHub.Files.cs | 27 +- .../MareSynchronosServer/Hubs/MareHub.User.cs | 44 +- .../MareSynchronosServer/Hubs/MareHub.cs | 52 +- .../SignalRLimitFilter.cs | 18 +- .../MareSynchronosServer.csproj | 4 +- .../MareSynchronosServer/Program.cs | 3 - .../MareSynchronosServer/Startup.cs | 35 +- .../MareSynchronosServer/SystemInfoService.cs | 13 +- .../MareSynchronosServer/appsettings.json | 9 +- .../Authentication/FailedAuthorization.cs | 24 + .../SecretKeyAuthenticationHandler.cs | 165 +++++++ .../CleanupService.cs | 54 +-- .../Discord/DiscordBot.cs | 443 ++++++++++++++++++ .../MareSynchronosServices.csproj | 22 + .../Metrics/MareMetrics.cs | 82 ++++ .../MareSynchronosServices/Program.cs | 25 + .../Properties/launchSettings.json | 13 + .../Services/AuthenticationService.cs | 40 ++ .../Services/MetricsService.cs | 39 ++ .../MareSynchronosServices/Startup.cs | 55 +++ .../appsettings.Development.json | 8 + .../MareSynchronosServices/appsettings.json | 29 ++ .../SecretKeyAuthenticationHandler.cs | 217 --------- .../SecretKeyGrpcAuthenticationHandler.cs | 59 +++ .../MareSynchronosShared.csproj | 15 + .../Metrics/MareMetrics.cs | 58 --- .../Metrics/MetricsAPI.cs | 21 + .../Protos/mareservices.proto | 49 ++ .../MareSynchronosStaticFilesServer.csproj | 19 + .../Program.cs | 25 + .../Properties/launchSettings.json | 28 ++ .../Startup.cs | 57 +++ .../appsettings.Development.json | 9 + .../appsettings.json | 21 + 38 files changed, 1391 insertions(+), 854 deletions(-) delete mode 100644 MareSynchronosServer/MareSynchronosServer/Discord/DiscordBot.cs rename MareSynchronosServer/MareSynchronosServer/{ => Hubs}/IdBasedUserIdProvider.cs (90%) rename MareSynchronosServer/MareSynchronosServer/{Throttling => Hubs}/SignalRLimitFilter.cs (94%) create mode 100644 MareSynchronosServer/MareSynchronosServices/Authentication/FailedAuthorization.cs create mode 100644 MareSynchronosServer/MareSynchronosServices/Authentication/SecretKeyAuthenticationHandler.cs rename MareSynchronosServer/{MareSynchronosServer => MareSynchronosServices}/CleanupService.cs (77%) create mode 100644 MareSynchronosServer/MareSynchronosServices/Discord/DiscordBot.cs create mode 100644 MareSynchronosServer/MareSynchronosServices/MareSynchronosServices.csproj create mode 100644 MareSynchronosServer/MareSynchronosServices/Metrics/MareMetrics.cs create mode 100644 MareSynchronosServer/MareSynchronosServices/Program.cs create mode 100644 MareSynchronosServer/MareSynchronosServices/Properties/launchSettings.json create mode 100644 MareSynchronosServer/MareSynchronosServices/Services/AuthenticationService.cs create mode 100644 MareSynchronosServer/MareSynchronosServices/Services/MetricsService.cs create mode 100644 MareSynchronosServer/MareSynchronosServices/Startup.cs create mode 100644 MareSynchronosServer/MareSynchronosServices/appsettings.Development.json create mode 100644 MareSynchronosServer/MareSynchronosServices/appsettings.json delete mode 100644 MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthenticationHandler.cs create mode 100644 MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyGrpcAuthenticationHandler.cs delete mode 100644 MareSynchronosServer/MareSynchronosShared/Metrics/MareMetrics.cs create mode 100644 MareSynchronosServer/MareSynchronosShared/Metrics/MetricsAPI.cs create mode 100644 MareSynchronosServer/MareSynchronosShared/Protos/mareservices.proto create mode 100644 MareSynchronosServer/MareSynchronosStaticFilesServer/MareSynchronosStaticFilesServer.csproj create mode 100644 MareSynchronosServer/MareSynchronosStaticFilesServer/Program.cs create mode 100644 MareSynchronosServer/MareSynchronosStaticFilesServer/Properties/launchSettings.json create mode 100644 MareSynchronosServer/MareSynchronosStaticFilesServer/Startup.cs create mode 100644 MareSynchronosServer/MareSynchronosStaticFilesServer/appsettings.Development.json create mode 100644 MareSynchronosServer/MareSynchronosStaticFilesServer/appsettings.json diff --git a/MareSynchronosServer/MareSynchronosServer.sln b/MareSynchronosServer/MareSynchronosServer.sln index 461354f..4e7c962 100644 --- a/MareSynchronosServer/MareSynchronosServer.sln +++ b/MareSynchronosServer/MareSynchronosServer.sln @@ -9,7 +9,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronos.API", "..\Ma EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronosServerTest", "MareSynchronosServerTest\MareSynchronosServerTest.csproj", "{25A82A2A-35C2-4EE0-A0E8-DFDD77978DDA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MareSynchronosShared", "MareSynchronosShared\MareSynchronosShared.csproj", "{67B1461D-E215-4BA8-A64D-E1836724D5E6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronosShared", "MareSynchronosShared\MareSynchronosShared.csproj", "{67B1461D-E215-4BA8-A64D-E1836724D5E6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MareSynchronosStaticFilesServer", "MareSynchronosStaticFilesServer\MareSynchronosStaticFilesServer.csproj", "{3C7F43BB-FE4C-48BC-BF42-D24E70E8FCB7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MareSynchronosServices", "MareSynchronosServices\MareSynchronosServices.csproj", "{E29C8677-AB44-4950-9EB1-D8E70B710A56}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -33,6 +37,14 @@ Global {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.Build.0 = Release|Any CPU + {3C7F43BB-FE4C-48BC-BF42-D24E70E8FCB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C7F43BB-FE4C-48BC-BF42-D24E70E8FCB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C7F43BB-FE4C-48BC-BF42-D24E70E8FCB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C7F43BB-FE4C-48BC-BF42-D24E70E8FCB7}.Release|Any CPU.Build.0 = Release|Any CPU + {E29C8677-AB44-4950-9EB1-D8E70B710A56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E29C8677-AB44-4950-9EB1-D8E70B710A56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E29C8677-AB44-4950-9EB1-D8E70B710A56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E29C8677-AB44-4950-9EB1-D8E70B710A56}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/MareSynchronosServer/MareSynchronosServer/Discord/DiscordBot.cs b/MareSynchronosServer/MareSynchronosServer/Discord/DiscordBot.cs deleted file mode 100644 index 46b5887..0000000 --- a/MareSynchronosServer/MareSynchronosServer/Discord/DiscordBot.cs +++ /dev/null @@ -1,431 +0,0 @@ -using Discord; -using Discord.WebSocket; -using MareSynchronosServer.Hubs; -using MareSynchronosServer.Metrics; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Concurrent; -using System.Linq; -using System.Net.Http; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using MareSynchronosShared.Data; -using MareSynchronosShared.Models; - -namespace MareSynchronosServer.Discord -{ - public class DiscordBot : IHostedService - { - private readonly IServiceProvider services; - private readonly IConfiguration configuration; - private readonly ILogger logger; - private readonly Random random; - private string authToken = string.Empty; - DiscordSocketClient discordClient; - ConcurrentDictionary DiscordLodestoneMapping = new(); - private CancellationTokenSource verificationTaskCts; - private CancellationTokenSource updateStatusCts; - private readonly string[] LodestoneServers = new[] { "eu", "na", "jp", "fr", "de" }; - private readonly ConcurrentQueue verificationQueue = new(); - - private SemaphoreSlim semaphore; - - public DiscordBot(IServiceProvider services, IConfiguration configuration, ILogger logger) - { - this.services = services; - this.configuration = configuration; - this.logger = logger; - this.verificationQueue = new ConcurrentQueue(); - this.semaphore = new SemaphoreSlim(1); - - random = new(); - authToken = configuration.GetValue("DiscordBotToken"); - - discordClient = new(new DiscordSocketConfig() - { - DefaultRetryMode = RetryMode.AlwaysRetry - }); - - discordClient.Log += Log; - } - - private async Task DiscordClient_SlashCommandExecuted(SocketSlashCommand arg) - { - await semaphore.WaitAsync().ConfigureAwait(false); - try - { - if (arg.Data.Name == "register") - { - if (arg.Data.Options.FirstOrDefault(f => f.Name == "overwrite_old_account") != null) - { - await DeletePreviousUserAccount(arg.User.Id).ConfigureAwait(false); - } - - var modal = new ModalBuilder(); - modal.WithTitle("Verify with Lodestone"); - modal.WithCustomId("register_modal"); - modal.AddTextInput("Enter the Lodestone URL of your Character", "lodestoneurl", TextInputStyle.Short, "https://*.finalfantasyxiv.com/lodestone/character//", required: true); - await arg.RespondWithModalAsync(modal.Build()).ConfigureAwait(false); - } - else if (arg.Data.Name == "verify") - { - EmbedBuilder eb = new(); - if (verificationQueue.Any(u => u.User.Id == arg.User.Id)) - { - eb.WithTitle("Already queued for verfication"); - eb.WithDescription("You are already queued for verification. Please wait."); - await arg.RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true).ConfigureAwait(false); - } - else if (!DiscordLodestoneMapping.ContainsKey(arg.User.Id)) - { - eb.WithTitle("Cannot verify registration"); - eb.WithDescription("You need to **/register** first before you can **/verify**"); - await arg.RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true).ConfigureAwait(false); - } - else - { - await arg.DeferAsync(ephemeral: true).ConfigureAwait(false); - verificationQueue.Enqueue(arg); - } - } - else - { - await arg.RespondAsync("idk what you did to get here to start, just follow the instructions as provided.", ephemeral: true).ConfigureAwait(false); - } - } - finally - { - semaphore.Release(); - } - } - - private async Task DeletePreviousUserAccount(ulong id) - { - using var scope = services.CreateScope(); - using var db = scope.ServiceProvider.GetService(); - var discordAuthedUser = await db.LodeStoneAuth.Include(u => u.User).FirstOrDefaultAsync(u => u.DiscordId == id).ConfigureAwait(false); - if (discordAuthedUser != null) - { - if (discordAuthedUser.User != null) - { - CleanupService.PurgeUser(discordAuthedUser.User, db, configuration); - } - else - { - db.Remove(discordAuthedUser); - } - - await db.SaveChangesAsync().ConfigureAwait(false); - } - } - - private async Task DiscordClient_ModalSubmitted(SocketModal arg) - { - if (arg.Data.CustomId == "register_modal") - { - var embed = await HandleRegisterModalAsync(arg).ConfigureAwait(false); - await arg.RespondAsync(embeds: new Embed[] { embed }, ephemeral: true).ConfigureAwait(false); - } - } - - private async Task HandleVerifyAsync(ulong id) - { - var embedBuilder = new EmbedBuilder(); - - using var scope = services.CreateScope(); - var req = new HttpClient(); - using var db = scope.ServiceProvider.GetService(); - - var lodestoneAuth = db.LodeStoneAuth.SingleOrDefault(u => u.DiscordId == id); - if (lodestoneAuth != null && DiscordLodestoneMapping.ContainsKey(id)) - { - var randomServer = LodestoneServers[random.Next(LodestoneServers.Length)]; - var response = await req.GetAsync($"https://{randomServer}.finalfantasyxiv.com/lodestone/character/{DiscordLodestoneMapping[id]}").ConfigureAwait(false); - if (response.IsSuccessStatusCode) - { - var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - if (content.Contains(lodestoneAuth.LodestoneAuthString)) - { - DiscordLodestoneMapping.TryRemove(id, out _); - - using var sha256 = SHA256.Create(); - var user = new User(); - - var hasValidUid = false; - while (!hasValidUid) - { - var uid = MareHub.GenerateRandomString(10); - if (db.Users.Any(u => u.UID == uid)) continue; - user.UID = uid; - hasValidUid = true; - } - - // make the first registered user on the service to admin - if (!await db.Users.AnyAsync().ConfigureAwait(false)) - { - user.IsAdmin = true; - } - - if (configuration.GetValue("PurgeUnusedAccounts")) - { - var purgedDays = configuration.GetValue("PurgeUnusedAccountsPeriodInDays"); - user.LastLoggedIn = DateTime.UtcNow - TimeSpan.FromDays(purgedDays) + TimeSpan.FromDays(1); - } - - var computedHash = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(MareHub.GenerateRandomString(64)))).Replace("-", ""); - var auth = new Auth() - { - HashedKey = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(computedHash))) - .Replace("-", ""), - User = user, - }; - - await db.Users.AddAsync(user).ConfigureAwait(false); - await db.Auth.AddAsync(auth).ConfigureAwait(false); - - logger.LogInformation("User registered: {userUID}", user.UID); - - MareMetrics.UsersRegistered.Inc(); - - lodestoneAuth.StartedAt = null; - lodestoneAuth.User = user; - lodestoneAuth.LodestoneAuthString = null; - - embedBuilder.WithTitle("Registration successful"); - embedBuilder.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 - + $"**{computedHash}**" - + Environment.NewLine + Environment.NewLine - + "Enter this key in Mare Synchronos and hit save to connect to the service." - + Environment.NewLine - + "You should connect as soon as possible to not get caught by the automatic cleanup process." - + Environment.NewLine - + "Have fun."); - } - else - { - embedBuilder.WithTitle("Failed to verify your character"); - embedBuilder.WithDescription("Did not find requested authentication key on your profile. Make sure you have saved *twice*, then do **/verify** again."); - lodestoneAuth.StartedAt = DateTime.UtcNow; - } - } - - await db.SaveChangesAsync().ConfigureAwait(false); - } - else - { - embedBuilder.WithTitle("Your auth has expired or something else went wrong"); - embedBuilder.WithDescription("Start again with **/register**"); - DiscordLodestoneMapping.TryRemove(id, out _); - } - - return embedBuilder.Build(); - } - - private async Task HandleRegisterModalAsync(SocketModal arg) - { - var embed = new EmbedBuilder(); - - var lodestoneId = ParseCharacterIdFromLodestoneUrl(arg.Data.Components.Single(c => c.CustomId == "lodestoneurl").Value); - 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/"); - } - else - { - // check if userid is already in db - using var scope = services.CreateScope(); - using var sha256 = SHA256.Create(); - - var hashedLodestoneId = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(lodestoneId.ToString()))).Replace("-", ""); - - using var db = scope.ServiceProvider.GetService(); - - // check if discord id or lodestone id is banned - if (db.BannedRegistrations.Any(a => a.DiscordIdOrLodestoneAuth == arg.User.Id.ToString() || a.DiscordIdOrLodestoneAuth == hashedLodestoneId)) - { - embed.WithTitle("no"); - embed.WithDescription("your account is banned"); - } - else if (db.LodeStoneAuth.Any(a => a.DiscordId == arg.User.Id)) - { - // user already in db - embed.WithTitle("Registration failed"); - embed.WithDescription("You cannot register more than one lodestone character to your discord account."); - } - else if (db.LodeStoneAuth.Any(a => a.HashedLodestoneId == hashedLodestoneId)) - { - // character already in db - embed.WithTitle("Registration failed"); - embed.WithDescription("This lodestone character already exists in the Database. If you are the rightful owner for this character and lost your secret key generated with it, contact the developer."); - } - else - { - string lodestoneAuth = await GenerateLodestoneAuth(arg.User.Id, hashedLodestoneId, db).ConfigureAwait(false); - // check if lodestone id is already in db - embed.WithTitle("Authorize your character"); - 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 + Environment.NewLine - + "Once added and saved, use command **/verify** to finish registration and receive a secret key to use for Mare Synchronos." - + 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 registration will be invalidated and you have to **/register** again."); - DiscordLodestoneMapping[arg.User.Id] = lodestoneId.ToString(); - } - } - - return embed.Build(); - } - - private async Task GenerateLodestoneAuth(ulong discordid, string hashedLodestoneId, MareDbContext dbContext) - { - var auth = MareHub.GenerateRandomString(32); - LodeStoneAuth lsAuth = new LodeStoneAuth() - { - DiscordId = discordid, - HashedLodestoneId = hashedLodestoneId, - LodestoneAuthString = auth, - StartedAt = DateTime.UtcNow - }; - - dbContext.Add(lsAuth); - await dbContext.SaveChangesAsync().ConfigureAwait(false); - - return auth; - } - - private int? ParseCharacterIdFromLodestoneUrl(string lodestoneUrl) - { - var regex = new Regex(@"https:\/\/(na|eu|de|fr|jp)\.finalfantasyxiv\.com\/lodestone\/character\/\d+"); - var matches = regex.Match(lodestoneUrl); - var isLodestoneUrl = matches.Success; - if (!isLodestoneUrl || matches.Groups.Count < 1) return null; - - lodestoneUrl = matches.Groups[0].ToString(); - var stringId = lodestoneUrl.Split('/', StringSplitOptions.RemoveEmptyEntries).Last(); - if (!int.TryParse(stringId, out int lodestoneId)) - { - return null; - } - - return lodestoneId; - } - - private async Task DiscordClient_Ready() - { - var register = new SlashCommandBuilder() - .WithName("register") - .WithDescription("Registration for the Mare Synchronos server of this Discord") - .AddOption(new SlashCommandOptionBuilder() - .WithName("new_account") - .WithDescription("Starts the registration process for the Mare Synchronos server of this Discord") - .WithType(ApplicationCommandOptionType.SubCommand)) - .AddOption(new SlashCommandOptionBuilder() - .WithName("overwrite_old_account") - .WithDescription("Will forcefully overwrite your current character on the service, if present") - .WithType(ApplicationCommandOptionType.SubCommand)); - - var verify = new SlashCommandBuilder(); - verify.WithName("verify"); - verify.WithDescription("Finishes the registration process for the Mare Synchronos server of this Discord"); - - try - { - await discordClient.CreateGlobalApplicationCommandAsync(register.Build()).ConfigureAwait(false); - await discordClient.CreateGlobalApplicationCommandAsync(verify.Build()).ConfigureAwait(false); - } - catch (Exception ex) - { - logger.LogError(ex, "Failed to create command"); - } - } - - private Task Log(LogMessage msg) - { - logger.LogInformation("{msg}", msg); - - return Task.CompletedTask; - } - - public async Task StartAsync(CancellationToken cancellationToken) - { - if (!string.IsNullOrEmpty(authToken)) - { - authToken = configuration.GetValue("DiscordBotToken"); - - await discordClient.LoginAsync(TokenType.Bot, authToken).ConfigureAwait(false); - await discordClient.StartAsync().ConfigureAwait(false); - - discordClient.Ready += DiscordClient_Ready; - discordClient.SlashCommandExecuted += DiscordClient_SlashCommandExecuted; - discordClient.ModalSubmitted += DiscordClient_ModalSubmitted; - - _ = ProcessQueueWork(); - _ = UpdateStatusAsync(); - } - } - - private async Task ProcessQueueWork() - { - verificationTaskCts = new CancellationTokenSource(); - while (!verificationTaskCts.IsCancellationRequested) - { - if (verificationQueue.TryDequeue(out var queueitem)) - { - try - { - var dataEmbed = await HandleVerifyAsync(queueitem.User.Id).ConfigureAwait(false); - await queueitem.FollowupAsync(embed: dataEmbed, ephemeral: true).ConfigureAwait(false); - - logger.LogInformation("Sent login information to user"); - } - catch (Exception e) - { - logger.LogError(e, "Error during queue work"); - } - - } - await Task.Delay(TimeSpan.FromSeconds(2), verificationTaskCts.Token).ConfigureAwait(false); - } - } - - private async Task UpdateStatusAsync() - { - updateStatusCts = new(); - while (!updateStatusCts.IsCancellationRequested) - { - await using var scope = services.CreateAsyncScope(); - await using var db = scope.ServiceProvider.GetService(); - - var users = db.Users.Count(c => c.CharacterIdentification != null); - - await discordClient.SetActivityAsync(new Game("Mare for " + users + " Users")).ConfigureAwait(false); - - await Task.Delay(TimeSpan.FromSeconds(15)).ConfigureAwait(false); - } - } - - public async Task StopAsync(CancellationToken cancellationToken) - { - verificationTaskCts?.Cancel(); - updateStatusCts?.Cancel(); - - await discordClient.LogoutAsync().ConfigureAwait(false); - await discordClient.StopAsync().ConfigureAwait(false); - } - } -} diff --git a/MareSynchronosServer/MareSynchronosServer/IdBasedUserIdProvider.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/IdBasedUserIdProvider.cs similarity index 90% rename from MareSynchronosServer/MareSynchronosServer/IdBasedUserIdProvider.cs rename to MareSynchronosServer/MareSynchronosServer/Hubs/IdBasedUserIdProvider.cs index 2da4d53..ef08d56 100644 --- a/MareSynchronosServer/MareSynchronosServer/IdBasedUserIdProvider.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/IdBasedUserIdProvider.cs @@ -2,7 +2,7 @@ using System.Security.Claims; using Microsoft.AspNetCore.SignalR; -namespace MareSynchronosServer +namespace MareSynchronosServer.Hubs { public class IdBasedUserIdProvider : IUserIdProvider { diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs index 23f5271..4600284 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs @@ -18,7 +18,7 @@ namespace MareSynchronosServer.Hubs private List OnlineAdmins => _dbContext.Users.Where(u => !string.IsNullOrEmpty(u.CharacterIdentification) && (u.IsModerator || u.IsAdmin)) .Select(u => u.UID).ToList(); - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.SendAdminChangeModeratorStatus)] public async Task ChangeModeratorStatus(string uid, bool isModerator) { @@ -33,7 +33,7 @@ namespace MareSynchronosServer.Hubs await Clients.Users(user.UID).SendAsync(Api.OnAdminForcedReconnect).ConfigureAwait(false); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.SendAdminDeleteBannedUser)] public async Task DeleteBannedUser(BannedUserDto dto) { @@ -51,7 +51,7 @@ namespace MareSynchronosServer.Hubs await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminDeleteBannedUser, dto).ConfigureAwait(false); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.SendAdminDeleteForbiddenFile)] public async Task DeleteForbiddenFile(ForbiddenFileDto dto) { @@ -69,7 +69,7 @@ namespace MareSynchronosServer.Hubs await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminDeleteForbiddenFile, dto).ConfigureAwait(false); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.InvokeAdminGetBannedUsers)] public async Task> GetBannedUsers() { @@ -82,7 +82,7 @@ namespace MareSynchronosServer.Hubs }).ToListAsync().ConfigureAwait(false); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.InvokeAdminGetForbiddenFiles)] public async Task> GetForbiddenFiles() { @@ -95,7 +95,7 @@ namespace MareSynchronosServer.Hubs }).ToListAsync().ConfigureAwait(false); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.InvokeAdminGetOnlineUsers)] public async Task> AdminGetOnlineUsers() { @@ -110,7 +110,7 @@ namespace MareSynchronosServer.Hubs }).ToListAsync().ConfigureAwait(false); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.SendAdminUpdateOrAddBannedUser)] public async Task UpdateOrAddBannedUser(BannedUserDto dto) { @@ -142,7 +142,7 @@ namespace MareSynchronosServer.Hubs } } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.SendAdminUpdateOrAddForbiddenFile)] public async Task UpdateOrAddForbiddenFile(ForbiddenFileDto dto) { diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs index 2f31a0c..d4e7396 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs @@ -7,9 +7,10 @@ using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using MareSynchronos.API; -using MareSynchronosServer.Metrics; using MareSynchronosShared.Authentication; +using MareSynchronosShared.Metrics; using MareSynchronosShared.Models; +using MareSynchronosShared.Protos; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; @@ -21,7 +22,7 @@ namespace MareSynchronosServer.Hubs { private string BasePath => _configuration["CacheDirectory"]; - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.SendFileAbortUpload)] public async Task AbortUpload() { @@ -32,7 +33,7 @@ namespace MareSynchronosServer.Hubs await _dbContext.SaveChangesAsync().ConfigureAwait(false); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.SendFileDeleteAllFiles)] public async Task DeleteAllFiles() { @@ -44,8 +45,10 @@ namespace MareSynchronosServer.Hubs var fi = new FileInfo(Path.Combine(BasePath, file.Hash)); if (fi.Exists) { - MareMetrics.FilesTotalSize.Dec(fi.Length); - MareMetrics.FilesTotal.Dec(); + await _metricsClient.DecGaugeAsync(new GaugeRequest() + {GaugeName = MetricsAPI.GaugeFilesTotalSize, Value = fi.Length}).ConfigureAwait(false); + await _metricsClient.DecGaugeAsync(new GaugeRequest() + { GaugeName = MetricsAPI.GaugeFilesTotal, Value = 1}).ConfigureAwait(false); fi.Delete(); } } @@ -53,7 +56,7 @@ namespace MareSynchronosServer.Hubs await _dbContext.SaveChangesAsync().ConfigureAwait(false); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.InvokeGetFilesSizes)] public async Task> GetFilesSizes(List hashes) { @@ -97,7 +100,7 @@ namespace MareSynchronosServer.Hubs return response; } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.InvokeFileIsUploadFinished)] public async Task IsUploadFinished() { @@ -106,7 +109,7 @@ namespace MareSynchronosServer.Hubs .AnyAsync(f => f.Uploader.UID == userUid && !f.Uploaded).ConfigureAwait(false); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.InvokeFileSendFiles)] public async Task> SendFiles(List fileListHashes) { @@ -156,7 +159,7 @@ namespace MareSynchronosServer.Hubs return notCoveredFiles.Values.ToList(); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.SendFileUploadFileStreamAsync)] public async Task UploadFileStreamAsync(string hash, IAsyncEnumerable fileContent) { @@ -224,8 +227,10 @@ namespace MareSynchronosServer.Hubs relatedFile = _dbContext.Files.Single(f => f.Hash == hash); relatedFile.Uploaded = true; - MareMetrics.FilesTotal.Inc(); - MareMetrics.FilesTotalSize.Inc(length); + await _metricsClient.IncGaugeAsync(new GaugeRequest() + { GaugeName = MetricsAPI.GaugeFilesTotalSize, Value = length }).ConfigureAwait(false); + await _metricsClient.IncGaugeAsync(new GaugeRequest() + { GaugeName = MetricsAPI.GaugeFilesTotal, Value = 1 }).ConfigureAwait(false); await _dbContext.SaveChangesAsync().ConfigureAwait(false); _logger.LogInformation("File {hash} added to DB", hash); diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs index 961756c..8ad64de 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs @@ -2,9 +2,10 @@ using System.Linq; using System.Threading.Tasks; using MareSynchronos.API; -using MareSynchronosServer.Metrics; using MareSynchronosShared.Authentication; +using MareSynchronosShared.Metrics; using MareSynchronosShared.Models; +using MareSynchronosShared.Protos; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; @@ -14,7 +15,7 @@ namespace MareSynchronosServer.Hubs { public partial class MareHub { - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.SendUserDeleteAccount)] public async Task DeleteAccount() { @@ -37,10 +38,8 @@ namespace MareSynchronosServer.Hubs await Task.Delay(1000).ConfigureAwait(false); } - SecretKeyAuthenticationHandler.RemoveAuthentication(userid); + await _authServiceClient.RemoveAuthAsync(new RemoveAuthRequest() { Uid = userid }).ConfigureAwait(false); - MareMetrics.Pairs.Dec(ownPairData.Count); - MareMetrics.PairsPaused.Dec(ownPairData.Count(c => c.IsPaused)); _dbContext.RemoveRange(ownPairData); await _dbContext.SaveChangesAsync().ConfigureAwait(false); @@ -56,9 +55,12 @@ namespace MareSynchronosServer.Hubs }, userEntry.CharacterIdentification).ConfigureAwait(false); } - MareMetrics.Pairs.Dec(otherPairData.Count); - MareMetrics.PairsPaused.Dec(otherPairData.Count(c => c.IsPaused)); - MareMetrics.UsersRegistered.Dec(); + await _metricsClient.DecGaugeAsync(new GaugeRequest() + { GaugeName = MetricsAPI.GaugePairs, Value = ownPairData.Count + otherPairData.Count }).ConfigureAwait(false); + await _metricsClient.DecGaugeAsync(new GaugeRequest() + { GaugeName = MetricsAPI.GaugePairsPaused, Value = ownPairData.Count(c => c.IsPaused) }).ConfigureAwait(false); + await _metricsClient.DecGaugeAsync(new GaugeRequest() + { GaugeName = MetricsAPI.GaugeUsersRegistered, Value = 1 }).ConfigureAwait(false); _dbContext.RemoveRange(otherPairData); _dbContext.Remove(userEntry); @@ -66,7 +68,7 @@ namespace MareSynchronosServer.Hubs await _dbContext.SaveChangesAsync().ConfigureAwait(false); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.InvokeUserGetOnlineCharacters)] public async Task> GetOnlineCharacters() { @@ -88,7 +90,7 @@ namespace MareSynchronosServer.Hubs return otherEntries.Select(e => e.User.CharacterIdentification).Distinct().ToList(); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.InvokeUserGetPairedClients)] public async Task> GetPairedClients() { @@ -126,7 +128,7 @@ namespace MareSynchronosServer.Hubs }).ToList(); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.InvokeUserPushCharacterDataToVisibleClients)] public async Task PushCharacterDataToVisibleClients(CharacterCacheDto characterCache, List visibleCharacterIds) { @@ -158,11 +160,13 @@ namespace MareSynchronosServer.Hubs await Clients.Users(otherEntries).SendAsync(Api.OnUserReceiveCharacterData, characterCache, user.CharacterIdentification).ConfigureAwait(false); - MareMetrics.UserPushData.Inc(); - MareMetrics.UserPushDataTo.Inc(otherEntries.Count); + await _metricsClient.IncreaseCounterAsync(new IncreaseCounterRequest() + { CounterName = MetricsAPI.CounterUserPushData, Value = 1 }).ConfigureAwait(false); + await _metricsClient.IncreaseCounterAsync(new IncreaseCounterRequest() + { CounterName = MetricsAPI.CounterUserPushDataTo, Value = otherEntries.Count }).ConfigureAwait(false); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.SendUserPairedClientAddition)] public async Task SendPairedClientAddition(string uid) { @@ -215,10 +219,10 @@ namespace MareSynchronosServer.Hubs } } - MareMetrics.Pairs.Inc(); + await _metricsClient.IncGaugeAsync(new GaugeRequest() {GaugeName = MetricsAPI.GaugePairs, Value = 1}).ConfigureAwait(false); } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.SendUserPairedClientPauseChange)] public async Task SendPairedClientPauseChange(string otherUserUid, bool isPaused) { @@ -255,15 +259,15 @@ namespace MareSynchronosServer.Hubs if (isPaused) { - MareMetrics.PairsPaused.Inc(); + await _metricsClient.IncGaugeAsync(new GaugeRequest() { GaugeName = MetricsAPI.GaugePairsPaused, Value = 1 }).ConfigureAwait(false); } else { - MareMetrics.PairsPaused.Dec(); + await _metricsClient.DecGaugeAsync(new GaugeRequest() { GaugeName = MetricsAPI.GaugePairsPaused, Value = 1 }).ConfigureAwait(false); } } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] [HubMethodName(Api.SendUserPairedClientRemoval)] public async Task SendPairedClientRemoval(string uid) { @@ -303,7 +307,7 @@ namespace MareSynchronosServer.Hubs } } - MareMetrics.Pairs.Dec(); + await _metricsClient.DecGaugeAsync(new GaugeRequest() { GaugeName = MetricsAPI.GaugePairs, Value = 1 }).ConfigureAwait(false); } private ClientPair OppositeEntry(string otherUID) => diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs index e98aa6a..413702c 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs @@ -4,10 +4,11 @@ using System.Security.Claims; using System.Security.Cryptography; using System.Threading.Tasks; using MareSynchronos.API; -using MareSynchronosServer.Metrics; using MareSynchronosShared.Authentication; using MareSynchronosShared.Data; +using MareSynchronosShared.Metrics; using MareSynchronosShared.Models; +using MareSynchronosShared.Protos; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.SignalR; @@ -19,14 +20,19 @@ namespace MareSynchronosServer.Hubs { public partial class MareHub : Hub { + private readonly MetricsService.MetricsServiceClient _metricsClient; + private readonly AuthService.AuthServiceClient _authServiceClient; private readonly SystemInfoService _systemInfoService; private readonly IConfiguration _configuration; private readonly IHttpContextAccessor contextAccessor; private readonly ILogger _logger; private readonly MareDbContext _dbContext; - public MareHub(MareDbContext mareDbContext, ILogger logger, SystemInfoService systemInfoService, IConfiguration configuration, IHttpContextAccessor contextAccessor) + public MareHub(MetricsService.MetricsServiceClient metricsClient, AuthService.AuthServiceClient authServiceClient, + MareDbContext mareDbContext, ILogger logger, SystemInfoService systemInfoService, IConfiguration configuration, IHttpContextAccessor contextAccessor) { + _metricsClient = metricsClient; + _authServiceClient = authServiceClient; _systemInfoService = systemInfoService; _configuration = configuration; this.contextAccessor = contextAccessor; @@ -35,10 +41,10 @@ namespace MareSynchronosServer.Hubs } [HubMethodName(Api.InvokeHeartbeat)] - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] + [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] public async Task Heartbeat(string characterIdentification) { - MareMetrics.InitializedConnections.Inc(); + await _metricsClient.IncreaseCounterAsync(new() { CounterName = MetricsAPI.CounterInitializedConnections, Value = 1 }).ConfigureAwait(false); var userId = Context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; @@ -60,7 +66,7 @@ namespace MareSynchronosServer.Hubs } else if (string.IsNullOrEmpty(user.CharacterIdentification)) { - MareMetrics.AuthorizedConnections.Inc(); + await _metricsClient.IncGaugeAsync(new GaugeRequest() { GaugeName = MetricsAPI.GaugeAuthorizedConnections, Value = 1 }).ConfigureAwait(false); } user.LastLoggedIn = DateTime.UtcNow; @@ -81,32 +87,34 @@ namespace MareSynchronosServer.Hubs }; } - public override Task OnConnectedAsync() + public override async Task OnConnectedAsync() { _logger.LogInformation("Connection from {ip}", contextAccessor.GetIpAddress()); - MareMetrics.Connections.Inc(); - return base.OnConnectedAsync(); + await _metricsClient.IncGaugeAsync(new GaugeRequest() { GaugeName = MetricsAPI.GaugeConnections, Value = 1 }).ConfigureAwait(false); + await base.OnConnectedAsync().ConfigureAwait(false); } public override async Task OnDisconnectedAsync(Exception exception) { - MareMetrics.Connections.Dec(); + await _metricsClient.DecGaugeAsync(new GaugeRequest() { GaugeName = MetricsAPI.GaugeConnections, Value = 1 }).ConfigureAwait(false); var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false); if (user != null && !string.IsNullOrEmpty(user.CharacterIdentification)) { - MareMetrics.AuthorizedConnections.Dec(); + await _metricsClient.DecGaugeAsync(new GaugeRequest() { GaugeName = MetricsAPI.GaugeAuthorizedConnections, Value = 1 }).ConfigureAwait(false); _logger.LogInformation("Disconnect from {id}", AuthenticatedUserId); var query = from userToOther in _dbContext.ClientPairs join otherToUser in _dbContext.ClientPairs - on new { + on new + { user = userToOther.UserUID, other = userToOther.OtherUserUID - } equals new { + } equals new + { user = otherToUser.OtherUserUID, other = otherToUser.UserUID } @@ -118,7 +126,7 @@ namespace MareSynchronosServer.Hubs var otherEntries = await query.ToListAsync().ConfigureAwait(false); await Clients.Users(otherEntries).SendAsync(Api.OnUserRemoveOnlinePairedPlayer, user.CharacterIdentification).ConfigureAwait(false); - + _dbContext.RemoveRange(_dbContext.Files.Where(f => !f.Uploaded && f.UploaderUID == user.UID)); user.CharacterIdentification = null; @@ -128,24 +136,6 @@ namespace MareSynchronosServer.Hubs await base.OnDisconnectedAsync(exception).ConfigureAwait(false); } - public static string GenerateRandomString(int length, string allowableChars = null) - { - if (string.IsNullOrEmpty(allowableChars)) - allowableChars = @"ABCDEFGHJKLMNPQRSTUVWXYZ0123456789"; - - // Generate random data - var rnd = RandomNumberGenerator.GetBytes(length); - - // Generate the output string - var allowable = allowableChars.ToCharArray(); - var l = allowable.Length; - var chars = new char[length]; - for (var i = 0; i < length; i++) - chars[i] = allowable[rnd[i] % l]; - - return new string(chars); - } - protected string AuthenticatedUserId => Context.User?.Claims?.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? "Unknown"; protected async Task GetAuthenticatedUserUntrackedAsync() diff --git a/MareSynchronosServer/MareSynchronosServer/Throttling/SignalRLimitFilter.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/SignalRLimitFilter.cs similarity index 94% rename from MareSynchronosServer/MareSynchronosServer/Throttling/SignalRLimitFilter.cs rename to MareSynchronosServer/MareSynchronosServer/Hubs/SignalRLimitFilter.cs index 2631928..b79846d 100644 --- a/MareSynchronosServer/MareSynchronosServer/Throttling/SignalRLimitFilter.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/SignalRLimitFilter.cs @@ -1,22 +1,22 @@ -using AspNetCoreRateLimit; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.SignalR; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System; +using System; using System.Linq; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; +using AspNetCoreRateLimit; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; -namespace MareSynchronosServer.Throttling; +namespace MareSynchronosServer.Hubs; public class SignalRLimitFilter : IHubFilter { private readonly IRateLimitProcessor _processor; private readonly IHttpContextAccessor accessor; private readonly ILogger logger; - private static SemaphoreSlim ConnectionLimiterSemaphore = new(20); - private static SemaphoreSlim DisconnectLimiterSemaphore = new(20); + private static readonly SemaphoreSlim ConnectionLimiterSemaphore = new(20); + private static readonly SemaphoreSlim DisconnectLimiterSemaphore = new(20); public SignalRLimitFilter( IOptions options, IProcessingStrategy processing, IIpPolicyStore policyStore, IHttpContextAccessor accessor, ILogger logger) diff --git a/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj b/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj index 55c42ec..78e5a49 100644 --- a/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj +++ b/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj @@ -10,8 +10,9 @@ - + + @@ -27,7 +28,6 @@ - diff --git a/MareSynchronosServer/MareSynchronosServer/Program.cs b/MareSynchronosServer/MareSynchronosServer/Program.cs index d3c1aed..fa4eaaf 100644 --- a/MareSynchronosServer/MareSynchronosServer/Program.cs +++ b/MareSynchronosServer/MareSynchronosServer/Program.cs @@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using MareSynchronosServer.Metrics; using System.Collections.Generic; using MareSynchronosShared.Data; @@ -37,8 +36,6 @@ namespace MareSynchronosServer context.RemoveRange(unfinishedRegistrations); context.RemoveRange(looseFiles); context.SaveChanges(); - - MareMetrics.InitializeMetrics(context, services.GetRequiredService()); } if (args.Length == 0 || args[0] != "dry") diff --git a/MareSynchronosServer/MareSynchronosServer/Startup.cs b/MareSynchronosServer/MareSynchronosServer/Startup.cs index a6b8a1d..32ec88b 100644 --- a/MareSynchronosServer/MareSynchronosServer/Startup.cs +++ b/MareSynchronosServer/MareSynchronosServer/Startup.cs @@ -1,3 +1,4 @@ +using System; using MareSynchronos.API; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -9,15 +10,12 @@ using MareSynchronosServer.Hubs; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http.Connections; using Microsoft.AspNetCore.SignalR; -using Prometheus; -using Microsoft.Extensions.FileProviders; using Microsoft.AspNetCore.Authorization; -using MareSynchronosServer.Discord; using AspNetCoreRateLimit; -using MareSynchronosServer.Throttling; using Ben.Diagnostics; using MareSynchronosShared.Authentication; using MareSynchronosShared.Data; +using MareSynchronosShared.Protos; namespace MareSynchronosServer { @@ -47,6 +45,15 @@ namespace MareSynchronosServer services.AddSingleton(); services.AddTransient(_ => Configuration); + services.AddGrpcClient(c => + { + c.Address = new Uri(Configuration.GetValue("ServiceAddress")); + }); + services.AddGrpcClient(c => + { + c.Address = new Uri(Configuration.GetValue("ServiceAddress")); + }); + services.AddDbContextPool(options => { options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"), builder => @@ -56,15 +63,13 @@ namespace MareSynchronosServer options.EnableThreadSafetyChecks(false); }, Configuration.GetValue("DbContextPoolSize", 1024)); - services.AddHostedService(); services.AddHostedService(provider => provider.GetService()); - services.AddHostedService(); services.AddAuthentication(options => { - options.DefaultScheme = SecretKeyAuthenticationHandler.AuthScheme; + options.DefaultScheme = SecretKeyGrpcAuthenticationHandler.AuthScheme; }) - .AddScheme(SecretKeyAuthenticationHandler.AuthScheme, options => { }); + .AddScheme(SecretKeyGrpcAuthenticationHandler.AuthScheme, options => { }); services.AddAuthorization(options => options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()); services.AddSingleton(); @@ -98,27 +103,13 @@ namespace MareSynchronosServer app.UseIpRateLimiting(); - app.UseStaticFiles(); - app.UseHttpLogging(); - app.UseRouting(); - app.UseHttpMetrics(); app.UseWebSockets(); app.UseAuthentication(); app.UseAuthorization(); - var metricServer = new KestrelMetricServer(4980); - metricServer.Start(); - - app.UseStaticFiles(new StaticFileOptions() - { - FileProvider = new PhysicalFileProvider(Configuration["CacheDirectory"]), - RequestPath = "/cache", - ServeUnknownFileTypes = true - }); - app.UseEndpoints(endpoints => { endpoints.MapHub(Api.Path, options => diff --git a/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs b/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs index 9c9ad62..c8b8e85 100644 --- a/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs +++ b/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs @@ -4,8 +4,9 @@ using System.Threading; using System.Threading.Tasks; using MareSynchronos.API; using MareSynchronosServer.Hubs; -using MareSynchronosServer.Metrics; using MareSynchronosShared.Data; +using MareSynchronosShared.Metrics; +using MareSynchronosShared.Protos; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -41,11 +42,15 @@ public class SystemInfoService : IHostedService, IDisposable { ThreadPool.GetAvailableThreads(out int workerThreads, out int ioThreads); _logger.LogInformation("ThreadPool: {workerThreads} workers available, {ioThreads} IO workers available", workerThreads, ioThreads); - MareMetrics.AvailableWorkerThreads.Set(workerThreads); - MareMetrics.AvailableIOWorkerThreads.Set(ioThreads); using var scope = _services.CreateScope(); - using var db = scope.ServiceProvider.GetService(); + using var db = scope.ServiceProvider.GetService()!; + + var metricsServiceClient = scope.ServiceProvider.GetService()!; + _ = metricsServiceClient.SetGauge(new SetGaugeRequest() + { GaugeName = MetricsAPI.GaugeAvailableWorkerThreads, Value = workerThreads }); + _ = metricsServiceClient.SetGauge(new SetGaugeRequest() + { GaugeName = MetricsAPI.GaugeAvailableIOWorkerThreads, Value = ioThreads }); var users = db.Users.Count(c => c.CharacterIdentification != null); diff --git a/MareSynchronosServer/MareSynchronosServer/appsettings.json b/MareSynchronosServer/MareSynchronosServer/appsettings.json index 966a76f..9831f47 100644 --- a/MareSynchronosServer/MareSynchronosServer/appsettings.json +++ b/MareSynchronosServer/MareSynchronosServer/appsettings.json @@ -25,14 +25,9 @@ }, "DbContextPoolSize": 2000, "CdnFullUrl": "https:///cache/", - "FailedAuthForTempBan": 5, - "TempBanDurationInMinutes": 30, - "DiscordBotToken": "", - "UnusedFileRetentionPeriodInDays": 7, - "PurgeUnusedAccounts": true, - "PurgeUnusedAccountsPeriodInDays": 14, "CacheDirectory": "G:\\ServerTest", // do not delete this key and set it to the path where the files will be stored - "CacheSizeHardLimitInGiB": -1, + "ServicesUrl": "http://localhost:5002", + "AllowedHosts": "*", "Kestrel": { "Endpoints": { diff --git a/MareSynchronosServer/MareSynchronosServices/Authentication/FailedAuthorization.cs b/MareSynchronosServer/MareSynchronosServices/Authentication/FailedAuthorization.cs new file mode 100644 index 0000000..3521102 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/Authentication/FailedAuthorization.cs @@ -0,0 +1,24 @@ +namespace MareSynchronosServices.Authentication; + +public class FailedAuthorization : IDisposable +{ + private int failedAttempts = 1; + public int FailedAttempts => failedAttempts; + public Task ResetTask { get; set; } + public CancellationTokenSource? ResetCts { get; set; } + + public void Dispose() + { + try + { + ResetCts?.Cancel(); + ResetCts?.Dispose(); + } + catch { } + } + + public void IncreaseFailedAttempts() + { + Interlocked.Increment(ref failedAttempts); + } +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosServices/Authentication/SecretKeyAuthenticationHandler.cs b/MareSynchronosServer/MareSynchronosServices/Authentication/SecretKeyAuthenticationHandler.cs new file mode 100644 index 0000000..960eb60 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/Authentication/SecretKeyAuthenticationHandler.cs @@ -0,0 +1,165 @@ +using System.Security.Cryptography; +using System.Text; +using MareSynchronosServices.Metrics; +using MareSynchronosShared.Data; +using MareSynchronosShared.Metrics; +using MareSynchronosShared.Protos; +using Microsoft.EntityFrameworkCore; + +namespace MareSynchronosServices.Authentication; + +public class SecretKeyAuthenticationHandler +{ + private readonly ILogger logger; + private readonly MareMetrics metrics; + private const string Unauthorized = "Unauthorized"; + private readonly Dictionary authorizations = new(); + private readonly Dictionary failedAuthorizations = new(); + private readonly object authDictLock = new(); + private readonly object failedAuthLock = new(); + private readonly int failedAttemptsForTempBan; + private readonly int tempBanMinutes; + + public void ClearUnauthorizedUsers() + { + lock (authDictLock) + { + foreach (var item in authorizations.ToArray()) + { + if (item.Value == Unauthorized) + { + authorizations[item.Key] = string.Empty; + } + } + } + } + + public void RemoveAuthentication(string uid) + { + lock (authDictLock) + { + var authorization = authorizations.Where(u => u.Value == uid); + if (authorization.Any()) + { + authorizations.Remove(authorization.First().Key); + } + } + } + + public async Task AuthenticateAsync(MareDbContext mareDbContext, string ip, string secretKey) + { + metrics.IncCounter(MetricsAPI.CounterAuthenticationRequests); + + if (string.IsNullOrEmpty(secretKey)) + { + metrics.IncCounter(MetricsAPI.CounterAuthenticationFailures); + return new AuthReply() { Success = false, Uid = string.Empty }; + } + + lock (failedAuthLock) + { + if (failedAuthorizations.TryGetValue(ip, out var existingFailedAuthorization) && existingFailedAuthorization.FailedAttempts > failedAttemptsForTempBan) + { + metrics.IncCounter(MetricsAPI.CounterAuthenticationFailures); + + existingFailedAuthorization.ResetCts?.Cancel(); + existingFailedAuthorization.ResetCts?.Dispose(); + existingFailedAuthorization.ResetCts = new CancellationTokenSource(); + var token = existingFailedAuthorization.ResetCts.Token; + existingFailedAuthorization.ResetTask = Task.Run(async () => + { + await Task.Delay(TimeSpan.FromMinutes(tempBanMinutes), token).ConfigureAwait(false); + if (token.IsCancellationRequested) return; + FailedAuthorization? failedAuthorization; + lock (failedAuthLock) + { + failedAuthorizations.Remove(ip, out failedAuthorization); + } + failedAuthorization?.Dispose(); + }, token); + + logger.LogWarning("TempBan {ip} for authorization spam", ip); + return new AuthReply() { Success = false, Uid = string.Empty }; + } + } + + using var sha256 = SHA256.Create(); + var hashedHeader = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(secretKey))).Replace("-", ""); + + string uid; + lock (authDictLock) + { + if (authorizations.TryGetValue(hashedHeader, out uid)) + { + if (uid == Unauthorized) + { + metrics.IncCounter(MetricsAPI.CounterAuthenticationFailures); + + lock (failedAuthLock) + { + logger.LogWarning("Failed authorization from {ip}", ip); + if (failedAuthorizations.TryGetValue(ip, out var auth)) + { + auth.IncreaseFailedAttempts(); + } + else + { + failedAuthorizations[ip] = new FailedAuthorization(); + } + } + + return new AuthReply() { Success = false, Uid = string.Empty }; + } + + metrics.IncCounter(MetricsAPI.CounterAuthenticationRequests); + } + } + + if (string.IsNullOrEmpty(uid)) + { + uid = (await mareDbContext.Auth.AsNoTracking() + .FirstOrDefaultAsync(m => m.HashedKey == hashedHeader).ConfigureAwait(false))?.UserUID; + + if (uid == null) + { + lock (authDictLock) + { + authorizations[hashedHeader] = Unauthorized; + } + + logger.LogWarning("Failed authorization from {ip}", ip); + lock (failedAuthLock) + { + if (failedAuthorizations.TryGetValue(ip, out var auth)) + { + auth.IncreaseFailedAttempts(); + } + else + { + failedAuthorizations[ip] = new FailedAuthorization(); + } + } + + metrics.IncCounter(MetricsAPI.CounterAuthenticationFailures); + return new AuthReply() { Success = false, Uid = string.Empty }; + } + + lock (authDictLock) + { + authorizations[hashedHeader] = uid; + } + } + + metrics.IncCounter(MetricsAPI.CounterAuthenticationSuccesses); + + return new AuthReply() { Success = true, Uid = uid }; + } + + public SecretKeyAuthenticationHandler(IConfiguration configuration, ILogger logger, MareMetrics metrics) + { + this.logger = logger; + this.metrics = metrics; + failedAttemptsForTempBan = configuration.GetValue("FailedAuthForTempBan", 5); + tempBanMinutes = configuration.GetValue("TempBanDurationInMinutes", 30); + } +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosServer/CleanupService.cs b/MareSynchronosServer/MareSynchronosServices/CleanupService.cs similarity index 77% rename from MareSynchronosServer/MareSynchronosServer/CleanupService.cs rename to MareSynchronosServer/MareSynchronosServices/CleanupService.cs index 5eff01d..c64c97b 100644 --- a/MareSynchronosServer/MareSynchronosServer/CleanupService.cs +++ b/MareSynchronosServer/MareSynchronosServices/CleanupService.cs @@ -1,30 +1,26 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MareSynchronosServer.Metrics; -using MareSynchronosShared.Authentication; +using MareSynchronosServices.Authentication; using MareSynchronosShared.Data; +using MareSynchronosShared.Metrics; using MareSynchronosShared.Models; +using MareSynchronosShared.Protos; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; +using MetricsService = MareSynchronosShared.Protos.MetricsService; -namespace MareSynchronosServer +namespace MareSynchronosServices { public class CleanupService : IHostedService, IDisposable { + private readonly MetricsService.MetricsServiceClient _metricsClient; + private readonly SecretKeyAuthenticationHandler _authService; private readonly ILogger _logger; private readonly IServiceProvider _services; private readonly IConfiguration _configuration; private Timer _timer; - public CleanupService(ILogger logger, IServiceProvider services, IConfiguration configuration) + public CleanupService(MetricsService.MetricsServiceClient metricsClient, SecretKeyAuthenticationHandler authService, ILogger logger, IServiceProvider services, IConfiguration configuration) { + _metricsClient = metricsClient; + _authService = authService; _logger = logger; _services = services; _configuration = configuration; @@ -56,9 +52,10 @@ namespace MareSynchronosServer var prevTime = DateTime.Now.Subtract(TimeSpan.FromDays(filesOlderThanDays)); var allFiles = dbContext.Files.ToList(); + var cachedir = _configuration["CacheDirectory"]; foreach (var file in allFiles.Where(f => f.Uploaded)) { - var fileName = Path.Combine(_configuration["CacheDirectory"], file.Hash); + var fileName = Path.Combine(cachedir, file.Hash); var fi = new FileInfo(fileName); if (!fi.Exists) { @@ -67,7 +64,8 @@ namespace MareSynchronosServer } else if (fi.LastAccessTime < prevTime) { - MareMetrics.FilesTotalSize.Dec(fi.Length); + _metricsClient.DecGauge(new GaugeRequest() { GaugeName = MetricsAPI.GaugeFilesTotalSize, Value = fi.Length }); + _metricsClient.DecGauge(new GaugeRequest() { GaugeName = MetricsAPI.GaugeFilesTotal, Value = 1 }); _logger.LogInformation("File outdated: {fileName}", fileName); dbContext.Files.Remove(file); fi.Delete(); @@ -96,8 +94,8 @@ namespace MareSynchronosServer removedHashes.Add(oldestFile.Name.ToLower()); allLocalFiles.Remove(oldestFile); totalCacheSizeInBytes -= oldestFile.Length; - MareMetrics.FilesTotal.Dec(); - MareMetrics.FilesTotalSize.Dec(oldestFile.Length); + _metricsClient.DecGauge(new GaugeRequest() { GaugeName = MetricsAPI.GaugeFilesTotalSize, Value = oldestFile.Length }); + _metricsClient.DecGauge(new GaugeRequest() { GaugeName = MetricsAPI.GaugeFilesTotal, Value = 1 }); oldestFile.Delete(); } @@ -170,14 +168,14 @@ namespace MareSynchronosServer _logger.LogWarning(ex, "Error during user purge"); } - SecretKeyAuthenticationHandler.ClearUnauthorizedUsers(); + _authService.ClearUnauthorizedUsers(); _logger.LogInformation($"Cleanup complete"); dbContext.SaveChanges(); } - public static void PurgeUser(User user, MareDbContext dbContext, IConfiguration _configuration) + public void PurgeUser(User user, MareDbContext dbContext, IConfiguration _configuration) { var lodestone = dbContext.LodeStoneAuth.SingleOrDefault(a => a.User.UID == user.UID); @@ -186,7 +184,7 @@ namespace MareSynchronosServer dbContext.Remove(lodestone); } - SecretKeyAuthenticationHandler.RemoveAuthentication(user.UID); + _authService.RemoveAuthentication(user.UID); var auth = dbContext.Auth.Single(a => a.UserUID == user.UID); @@ -196,8 +194,8 @@ namespace MareSynchronosServer var fi = new FileInfo(Path.Combine(_configuration["CacheDirectory"], file.Hash)); if (fi.Exists) { - MareMetrics.FilesTotalSize.Dec(fi.Length); - MareMetrics.FilesTotal.Dec(); + _metricsClient.DecGauge(new GaugeRequest() { GaugeName = MetricsAPI.GaugeFilesTotalSize, Value = fi.Length }); + _metricsClient.DecGauge(new GaugeRequest() { GaugeName = MetricsAPI.GaugeFilesTotal, Value = 1 }); fi.Delete(); } } @@ -210,12 +208,10 @@ namespace MareSynchronosServer var otherPairData = dbContext.ClientPairs.Include(u => u.User) .Where(u => u.OtherUser.UID == user.UID).ToList(); - MareMetrics.Pairs.Dec(ownPairData.Count); - MareMetrics.PairsPaused.Dec(ownPairData.Count(c => c.IsPaused)); - MareMetrics.Pairs.Dec(otherPairData.Count); - MareMetrics.PairsPaused.Dec(otherPairData.Count(c => c.IsPaused)); - MareMetrics.UsersRegistered.Dec(); - + _metricsClient.DecGauge(new GaugeRequest() { GaugeName = MetricsAPI.GaugePairs, Value = ownPairData.Count + otherPairData.Count }); + _metricsClient.DecGauge(new GaugeRequest() { GaugeName = MetricsAPI.GaugePairsPaused, Value = ownPairData.Count(c => c.IsPaused) }); + _metricsClient.DecGauge(new GaugeRequest() { GaugeName = MetricsAPI.GaugeUsersRegistered, Value = 1 }); + dbContext.RemoveRange(otherPairData); dbContext.Remove(auth); dbContext.Remove(user); diff --git a/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBot.cs b/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBot.cs new file mode 100644 index 0000000..3fdc054 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBot.cs @@ -0,0 +1,443 @@ +using System.Collections.Concurrent; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using Discord; +using Discord.WebSocket; +using MareSynchronosServices.Metrics; +using MareSynchronosShared.Data; +using MareSynchronosShared.Metrics; +using MareSynchronosShared.Models; +using Microsoft.EntityFrameworkCore; + +namespace MareSynchronosServices.Discord; + +public class DiscordBot : IHostedService +{ + private readonly CleanupService cleanupService; + private readonly MareMetrics metrics; + private readonly IServiceProvider services; + private readonly IConfiguration configuration; + private readonly ILogger logger; + private readonly Random random; + private string authToken = string.Empty; + DiscordSocketClient discordClient; + ConcurrentDictionary DiscordLodestoneMapping = new(); + private CancellationTokenSource verificationTaskCts; + private CancellationTokenSource updateStatusCts; + private readonly string[] LodestoneServers = new[] { "eu", "na", "jp", "fr", "de" }; + private readonly ConcurrentQueue verificationQueue = new(); + + private SemaphoreSlim semaphore; + + public DiscordBot(CleanupService cleanupService, MareMetrics metrics, IServiceProvider services, IConfiguration configuration, ILogger logger) + { + this.cleanupService = cleanupService; + this.metrics = metrics; + this.services = services; + this.configuration = configuration; + this.logger = logger; + this.verificationQueue = new ConcurrentQueue(); + this.semaphore = new SemaphoreSlim(1); + + random = new(); + authToken = configuration.GetValue("DiscordBotToken"); + + discordClient = new(new DiscordSocketConfig() + { + DefaultRetryMode = RetryMode.AlwaysRetry + }); + + discordClient.Log += Log; + } + + private async Task DiscordClient_SlashCommandExecuted(SocketSlashCommand arg) + { + await semaphore.WaitAsync().ConfigureAwait(false); + try + { + if (arg.Data.Name == "register") + { + if (arg.Data.Options.FirstOrDefault(f => f.Name == "overwrite_old_account") != null) + { + await DeletePreviousUserAccount(arg.User.Id).ConfigureAwait(false); + } + + var modal = new ModalBuilder(); + modal.WithTitle("Verify with Lodestone"); + modal.WithCustomId("register_modal"); + modal.AddTextInput("Enter the Lodestone URL of your Character", "lodestoneurl", TextInputStyle.Short, "https://*.finalfantasyxiv.com/lodestone/character//", required: true); + await arg.RespondWithModalAsync(modal.Build()).ConfigureAwait(false); + } + else if (arg.Data.Name == "verify") + { + EmbedBuilder eb = new(); + if (verificationQueue.Any(u => u.User.Id == arg.User.Id)) + { + eb.WithTitle("Already queued for verfication"); + eb.WithDescription("You are already queued for verification. Please wait."); + await arg.RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true).ConfigureAwait(false); + } + else if (!DiscordLodestoneMapping.ContainsKey(arg.User.Id)) + { + eb.WithTitle("Cannot verify registration"); + eb.WithDescription("You need to **/register** first before you can **/verify**"); + await arg.RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true).ConfigureAwait(false); + } + else + { + await arg.DeferAsync(ephemeral: true).ConfigureAwait(false); + verificationQueue.Enqueue(arg); + } + } + else + { + await arg.RespondAsync("idk what you did to get here to start, just follow the instructions as provided.", ephemeral: true).ConfigureAwait(false); + } + } + finally + { + semaphore.Release(); + } + } + + private async Task DeletePreviousUserAccount(ulong id) + { + using var scope = services.CreateScope(); + using var db = scope.ServiceProvider.GetService(); + var discordAuthedUser = await db.LodeStoneAuth.Include(u => u.User).FirstOrDefaultAsync(u => u.DiscordId == id).ConfigureAwait(false); + if (discordAuthedUser != null) + { + if (discordAuthedUser.User != null) + { + cleanupService.PurgeUser(discordAuthedUser.User, db, configuration); + } + else + { + db.Remove(discordAuthedUser); + } + + await db.SaveChangesAsync().ConfigureAwait(false); + } + } + + private async Task DiscordClient_ModalSubmitted(SocketModal arg) + { + if (arg.Data.CustomId == "register_modal") + { + var embed = await HandleRegisterModalAsync(arg).ConfigureAwait(false); + await arg.RespondAsync(embeds: new Embed[] { embed }, ephemeral: true).ConfigureAwait(false); + } + } + + private async Task HandleVerifyAsync(ulong id) + { + var embedBuilder = new EmbedBuilder(); + + using var scope = services.CreateScope(); + var req = new HttpClient(); + using var db = scope.ServiceProvider.GetService(); + + var lodestoneAuth = db.LodeStoneAuth.SingleOrDefault(u => u.DiscordId == id); + if (lodestoneAuth != null && DiscordLodestoneMapping.ContainsKey(id)) + { + var randomServer = LodestoneServers[random.Next(LodestoneServers.Length)]; + var response = await req.GetAsync($"https://{randomServer}.finalfantasyxiv.com/lodestone/character/{DiscordLodestoneMapping[id]}").ConfigureAwait(false); + if (response.IsSuccessStatusCode) + { + var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + if (content.Contains(lodestoneAuth.LodestoneAuthString)) + { + DiscordLodestoneMapping.TryRemove(id, out _); + + using var sha256 = SHA256.Create(); + var user = new User(); + + var hasValidUid = false; + while (!hasValidUid) + { + var uid = GenerateRandomString(10); + if (db.Users.Any(u => u.UID == uid)) continue; + user.UID = uid; + hasValidUid = true; + } + + // make the first registered user on the service to admin + if (!await db.Users.AnyAsync().ConfigureAwait(false)) + { + user.IsAdmin = true; + } + + if (configuration.GetValue("PurgeUnusedAccounts")) + { + var purgedDays = configuration.GetValue("PurgeUnusedAccountsPeriodInDays"); + user.LastLoggedIn = DateTime.UtcNow - TimeSpan.FromDays(purgedDays) + TimeSpan.FromDays(1); + } + + var computedHash = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(GenerateRandomString(64)))).Replace("-", ""); + var auth = new Auth() + { + HashedKey = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(computedHash))) + .Replace("-", ""), + User = user, + }; + + await db.Users.AddAsync(user).ConfigureAwait(false); + await db.Auth.AddAsync(auth).ConfigureAwait(false); + + logger.LogInformation("User registered: {userUID}", user.UID); + + metrics.IncGaugeBy(MetricsAPI.GaugeUsersRegistered, 1); + + lodestoneAuth.StartedAt = null; + lodestoneAuth.User = user; + lodestoneAuth.LodestoneAuthString = null; + + embedBuilder.WithTitle("Registration successful"); + embedBuilder.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 + + $"**{computedHash}**" + + Environment.NewLine + Environment.NewLine + + "Enter this key in Mare Synchronos and hit save to connect to the service." + + Environment.NewLine + + "You should connect as soon as possible to not get caught by the automatic cleanup process." + + Environment.NewLine + + "Have fun."); + } + else + { + embedBuilder.WithTitle("Failed to verify your character"); + embedBuilder.WithDescription("Did not find requested authentication key on your profile. Make sure you have saved *twice*, then do **/verify** again."); + lodestoneAuth.StartedAt = DateTime.UtcNow; + } + } + + await db.SaveChangesAsync().ConfigureAwait(false); + } + else + { + embedBuilder.WithTitle("Your auth has expired or something else went wrong"); + embedBuilder.WithDescription("Start again with **/register**"); + DiscordLodestoneMapping.TryRemove(id, out _); + } + + return embedBuilder.Build(); + } + + private async Task HandleRegisterModalAsync(SocketModal arg) + { + var embed = new EmbedBuilder(); + + var lodestoneId = ParseCharacterIdFromLodestoneUrl(arg.Data.Components.Single(c => c.CustomId == "lodestoneurl").Value); + 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/"); + } + else + { + // check if userid is already in db + using var scope = services.CreateScope(); + using var sha256 = SHA256.Create(); + + var hashedLodestoneId = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(lodestoneId.ToString()))).Replace("-", ""); + + using var db = scope.ServiceProvider.GetService(); + + // check if discord id or lodestone id is banned + if (db.BannedRegistrations.Any(a => a.DiscordIdOrLodestoneAuth == arg.User.Id.ToString() || a.DiscordIdOrLodestoneAuth == hashedLodestoneId)) + { + embed.WithTitle("no"); + embed.WithDescription("your account is banned"); + } + else if (db.LodeStoneAuth.Any(a => a.DiscordId == arg.User.Id)) + { + // user already in db + embed.WithTitle("Registration failed"); + embed.WithDescription("You cannot register more than one lodestone character to your discord account."); + } + else if (db.LodeStoneAuth.Any(a => a.HashedLodestoneId == hashedLodestoneId)) + { + // character already in db + embed.WithTitle("Registration failed"); + embed.WithDescription("This lodestone character already exists in the Database. If you are the rightful owner for this character and lost your secret key generated with it, contact the developer."); + } + else + { + string lodestoneAuth = await GenerateLodestoneAuth(arg.User.Id, hashedLodestoneId, db).ConfigureAwait(false); + // check if lodestone id is already in db + embed.WithTitle("Authorize your character"); + 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 + Environment.NewLine + + "Once added and saved, use command **/verify** to finish registration and receive a secret key to use for Mare Synchronos." + + 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 registration will be invalidated and you have to **/register** again."); + DiscordLodestoneMapping[arg.User.Id] = lodestoneId.ToString(); + } + } + + return embed.Build(); + } + + private async Task GenerateLodestoneAuth(ulong discordid, string hashedLodestoneId, MareDbContext dbContext) + { + var auth = GenerateRandomString(32); + LodeStoneAuth lsAuth = new LodeStoneAuth() + { + DiscordId = discordid, + HashedLodestoneId = hashedLodestoneId, + LodestoneAuthString = auth, + StartedAt = DateTime.UtcNow + }; + + dbContext.Add(lsAuth); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + + return auth; + } + + private int? ParseCharacterIdFromLodestoneUrl(string lodestoneUrl) + { + var regex = new Regex(@"https:\/\/(na|eu|de|fr|jp)\.finalfantasyxiv\.com\/lodestone\/character\/\d+"); + var matches = regex.Match(lodestoneUrl); + var isLodestoneUrl = matches.Success; + if (!isLodestoneUrl || matches.Groups.Count < 1) return null; + + lodestoneUrl = matches.Groups[0].ToString(); + var stringId = lodestoneUrl.Split('/', StringSplitOptions.RemoveEmptyEntries).Last(); + if (!int.TryParse(stringId, out int lodestoneId)) + { + return null; + } + + return lodestoneId; + } + + private async Task DiscordClient_Ready() + { + var register = new SlashCommandBuilder() + .WithName("register") + .WithDescription("Registration for the Mare Synchronos server of this Discord") + .AddOption(new SlashCommandOptionBuilder() + .WithName("new_account") + .WithDescription("Starts the registration process for the Mare Synchronos server of this Discord") + .WithType(ApplicationCommandOptionType.SubCommand)) + .AddOption(new SlashCommandOptionBuilder() + .WithName("overwrite_old_account") + .WithDescription("Will forcefully overwrite your current character on the service, if present") + .WithType(ApplicationCommandOptionType.SubCommand)); + + var verify = new SlashCommandBuilder(); + verify.WithName("verify"); + verify.WithDescription("Finishes the registration process for the Mare Synchronos server of this Discord"); + + try + { + await discordClient.CreateGlobalApplicationCommandAsync(register.Build()).ConfigureAwait(false); + await discordClient.CreateGlobalApplicationCommandAsync(verify.Build()).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError(ex, "Failed to create command"); + } + } + + private Task Log(LogMessage msg) + { + logger.LogInformation("{msg}", msg); + + return Task.CompletedTask; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + if (!string.IsNullOrEmpty(authToken)) + { + authToken = configuration.GetValue("DiscordBotToken"); + + await discordClient.LoginAsync(TokenType.Bot, authToken).ConfigureAwait(false); + await discordClient.StartAsync().ConfigureAwait(false); + + discordClient.Ready += DiscordClient_Ready; + discordClient.SlashCommandExecuted += DiscordClient_SlashCommandExecuted; + discordClient.ModalSubmitted += DiscordClient_ModalSubmitted; + + _ = ProcessQueueWork(); + _ = UpdateStatusAsync(); + } + } + + private async Task ProcessQueueWork() + { + verificationTaskCts = new CancellationTokenSource(); + while (!verificationTaskCts.IsCancellationRequested) + { + if (verificationQueue.TryDequeue(out var queueitem)) + { + try + { + var dataEmbed = await HandleVerifyAsync(queueitem.User.Id).ConfigureAwait(false); + await queueitem.FollowupAsync(embed: dataEmbed, ephemeral: true).ConfigureAwait(false); + + logger.LogInformation("Sent login information to user"); + } + catch (Exception e) + { + logger.LogError(e, "Error during queue work"); + } + + } + await Task.Delay(TimeSpan.FromSeconds(2), verificationTaskCts.Token).ConfigureAwait(false); + } + } + + private async Task UpdateStatusAsync() + { + updateStatusCts = new(); + while (!updateStatusCts.IsCancellationRequested) + { + await using var scope = services.CreateAsyncScope(); + await using var db = scope.ServiceProvider.GetService(); + + var users = db.Users.Count(c => c.CharacterIdentification != null); + + await discordClient.SetActivityAsync(new Game("Mare for " + users + " Users")).ConfigureAwait(false); + + await Task.Delay(TimeSpan.FromSeconds(15)).ConfigureAwait(false); + } + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + verificationTaskCts?.Cancel(); + updateStatusCts?.Cancel(); + + await discordClient.LogoutAsync().ConfigureAwait(false); + await discordClient.StopAsync().ConfigureAwait(false); + } + + public static string GenerateRandomString(int length, string allowableChars = null) + { + if (string.IsNullOrEmpty(allowableChars)) + allowableChars = @"ABCDEFGHJKLMNPQRSTUVWXYZ0123456789"; + + // Generate random data + var rnd = RandomNumberGenerator.GetBytes(length); + + // Generate the output string + var allowable = allowableChars.ToCharArray(); + var l = allowable.Length; + var chars = new char[length]; + for (var i = 0; i < length; i++) + chars[i] = allowable[rnd[i] % l]; + + return new string(chars); + } +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosServices/MareSynchronosServices.csproj b/MareSynchronosServer/MareSynchronosServices/MareSynchronosServices.csproj new file mode 100644 index 0000000..7cddfee --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/MareSynchronosServices.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + + + diff --git a/MareSynchronosServer/MareSynchronosServices/Metrics/MareMetrics.cs b/MareSynchronosServer/MareSynchronosServices/Metrics/MareMetrics.cs new file mode 100644 index 0000000..85d0cce --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/Metrics/MareMetrics.cs @@ -0,0 +1,82 @@ +using MareSynchronosShared.Data; +using MareSynchronosShared.Metrics; + +using Prometheus; + +namespace MareSynchronosServices.Metrics; + +public class MareMetrics +{ + public MareMetrics(IServiceProvider services, IConfiguration configuration) + { + using var scope = services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetService(); + + gauges[MetricsAPI.GaugeUsersRegistered].IncTo(dbContext.Users.Count()); + gauges[MetricsAPI.GaugePairs].IncTo(dbContext.ClientPairs.Count()); + gauges[MetricsAPI.GaugePairsPaused].IncTo(dbContext.ClientPairs.Count(p => p.IsPaused)); + gauges[MetricsAPI.GaugeFilesTotal].IncTo(dbContext.Files.Count()); + gauges[MetricsAPI.GaugeFilesTotalSize].IncTo(Directory.EnumerateFiles(configuration["CacheDirectory"]).Sum(f => new FileInfo(f).Length)); + } + + private readonly Dictionary counters = new() + { + { MetricsAPI.CounterInitializedConnections, Prometheus.Metrics.CreateCounter(MetricsAPI.CounterInitializedConnections, "Initialized Connections") }, + { MetricsAPI.CounterUserPushData, Prometheus.Metrics.CreateCounter(MetricsAPI.CounterUserPushData, "Users pushing data") }, + { MetricsAPI.CounterUserPushDataTo, Prometheus.Metrics.CreateCounter(MetricsAPI.CounterUserPushDataTo, "Users Receiving Data") }, + { MetricsAPI.CounterAuthenticationRequests, Prometheus.Metrics.CreateCounter(MetricsAPI.CounterAuthenticationRequests, "Authentication Requests") }, + { MetricsAPI.CounterAuthenticationCacheHits, Prometheus.Metrics.CreateCounter(MetricsAPI.CounterAuthenticationCacheHits, "Authentication Requests Cache Hits") }, + { MetricsAPI.CounterAuthenticationFailures, Prometheus.Metrics.CreateCounter(MetricsAPI.CounterAuthenticationFailures, "Authentication Requests Failed") }, + { MetricsAPI.CounterAuthenticationSuccesses, Prometheus.Metrics.CreateCounter(MetricsAPI.CounterAuthenticationSuccesses, "Authentication Requests Success") }, + }; + + private readonly Dictionary gauges = new() + { + { MetricsAPI.GaugeConnections, Prometheus.Metrics.CreateGauge(MetricsAPI.GaugeConnections, "Unauthorized Connections") }, + { MetricsAPI.GaugeAuthorizedConnections, Prometheus.Metrics.CreateGauge(MetricsAPI.GaugeConnections, "Authorized Connections") }, + { MetricsAPI.GaugeAvailableIOWorkerThreads, Prometheus.Metrics.CreateGauge(MetricsAPI.GaugeConnections, "Available Threadpool IO Workers") }, + { MetricsAPI.GaugeAvailableWorkerThreads, Prometheus.Metrics.CreateGauge(MetricsAPI.GaugeConnections, "Aavailable Threadpool Workers") }, + { MetricsAPI.GaugeUsersRegistered, Prometheus.Metrics.CreateGauge(MetricsAPI.GaugeConnections, "Total Registrations") }, + { MetricsAPI.GaugePairs, Prometheus.Metrics.CreateGauge(MetricsAPI.GaugeConnections, "Total Pairs") }, + { MetricsAPI.GaugePairsPaused, Prometheus.Metrics.CreateGauge(MetricsAPI.GaugeConnections, "Total Paused Pairs") }, + { MetricsAPI.GaugeFilesTotal, Prometheus.Metrics.CreateGauge(MetricsAPI.GaugeConnections, "Total uploaded files") }, + { MetricsAPI.GaugeFilesTotalSize, Prometheus.Metrics.CreateGauge(MetricsAPI.GaugeConnections, "Total uploaded files (bytes)") }, + }; + + public void SetGaugeTo(string gaugeName, double value) + { + if (gauges.ContainsKey(gaugeName)) + { + gauges[gaugeName].IncTo(value); + } + } + + public void IncGaugeBy(string gaugeName, double value) + { + if (gauges.ContainsKey(gaugeName)) + { + gauges[gaugeName].Inc(value); + } + } + + public void DecGaugeBy(string gaugeName, double value) + { + if (gauges.ContainsKey(gaugeName)) + { + gauges[gaugeName].Dec(value); + } + } + + public void IncCounter(string counterName) + { + IncCounterBy(counterName, 1); + } + + public void IncCounterBy(string counterName, double value) + { + if (counters.ContainsKey(counterName)) + { + counters[counterName].Inc(value); + } + } +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosServices/Program.cs b/MareSynchronosServer/MareSynchronosServices/Program.cs new file mode 100644 index 0000000..09607b5 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/Program.cs @@ -0,0 +1,25 @@ +using MareSynchronosServices; +using MareSynchronosServices.Metrics; +using MareSynchronosShared.Data; +using Microsoft.EntityFrameworkCore; + +public class Program +{ + public static void Main(string[] args) + { + var hostBuilder = CreateHostBuilder(args); + var host = hostBuilder.Build(); + + host.Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseSystemd() + .UseConsoleLifetime() + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseContentRoot(AppContext.BaseDirectory); + webBuilder.UseStartup(); + }); +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosServices/Properties/launchSettings.json b/MareSynchronosServer/MareSynchronosServices/Properties/launchSettings.json new file mode 100644 index 0000000..ea24f06 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "MareSynchronosServices": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5294;https://localhost:7294", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/MareSynchronosServer/MareSynchronosServices/Services/AuthenticationService.cs b/MareSynchronosServer/MareSynchronosServices/Services/AuthenticationService.cs new file mode 100644 index 0000000..a720c61 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/Services/AuthenticationService.cs @@ -0,0 +1,40 @@ +using Grpc.Core; +using MareSynchronosServices.Authentication; +using MareSynchronosShared.Data; +using MareSynchronosShared.Protos; + +namespace MareSynchronosServices.Services +{ + public class AuthenticationService : AuthService.AuthServiceBase + { + private readonly ILogger _logger; + private readonly MareDbContext _dbContext; + private readonly SecretKeyAuthenticationHandler _authHandler; + + public AuthenticationService(ILogger logger, MareDbContext dbContext, SecretKeyAuthenticationHandler authHandler) + { + _logger = logger; + _dbContext = dbContext; + _authHandler = authHandler; + } + + public override async Task Authorize(AuthRequest request, ServerCallContext context) + { + return await _authHandler.AuthenticateAsync(_dbContext, request.Ip, request.SecretKey); + } + + public override Task RemoveAuth(RemoveAuthRequest request, ServerCallContext context) + { + _logger.LogInformation("Removing Authentication for {uid}", request.Uid); + _authHandler.RemoveAuthentication(request.Uid); + return Task.FromResult(new Empty()); + } + + public override Task ClearUnauthorized(Empty request, ServerCallContext context) + { + _logger.LogInformation("Clearing unauthorized users"); + _authHandler.ClearUnauthorizedUsers(); + return Task.FromResult(new Empty()); + } + } +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosServices/Services/MetricsService.cs b/MareSynchronosServer/MareSynchronosServices/Services/MetricsService.cs new file mode 100644 index 0000000..26bae42 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/Services/MetricsService.cs @@ -0,0 +1,39 @@ +using Grpc.Core; +using MareSynchronosServices.Metrics; +using MareSynchronosShared.Protos; + +namespace MareSynchronosServices.Services; + +public class MetricsService : MareSynchronosShared.Protos.MetricsService.MetricsServiceBase +{ + private readonly MareMetrics metrics; + + public MetricsService(MareMetrics metrics) + { + this.metrics = metrics; + } + + public override Task IncreaseCounter(IncreaseCounterRequest request, ServerCallContext context) + { + metrics.IncCounterBy(request.CounterName, request.Value); + return Task.FromResult(new Empty()); + } + + public override Task SetGauge(SetGaugeRequest request, ServerCallContext context) + { + metrics.SetGaugeTo(request.GaugeName, request.Value); + return Task.FromResult(new Empty()); + } + + public override Task DecGauge(GaugeRequest request, ServerCallContext context) + { + metrics.DecGaugeBy(request.GaugeName, request.Value); + return Task.FromResult(new Empty()); + } + + public override Task IncGauge(GaugeRequest request, ServerCallContext context) + { + metrics.IncGaugeBy(request.GaugeName, request.Value); + return Task.FromResult(new Empty()); + } +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosServices/Startup.cs b/MareSynchronosServer/MareSynchronosServices/Startup.cs new file mode 100644 index 0000000..0095df9 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/Startup.cs @@ -0,0 +1,55 @@ +using MareSynchronosServer; +using MareSynchronosServices.Authentication; +using MareSynchronosServices.Discord; +using MareSynchronosServices.Metrics; +using MareSynchronosServices.Services; +using MareSynchronosShared.Authentication; +using MareSynchronosShared.Data; +using Microsoft.EntityFrameworkCore; +using Prometheus; + +namespace MareSynchronosServices; + +public class Startup +{ + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddDbContextPool(options => + { + options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"), builder => + { + builder.MigrationsHistoryTable("_efmigrationshistory", "public"); + }).UseSnakeCaseNamingConvention(); + options.EnableThreadSafetyChecks(false); + }, Configuration.GetValue("DbContextPoolSize", 1024)); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddTransient(_ => Configuration); + services.AddHostedService(provider => provider.GetService()); + services.AddHostedService(); + services.AddGrpc(); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseRouting(); + + var metricServer = new KestrelMetricServer(4980); + metricServer.Start(); + + app.UseEndpoints(endpoints => + { + endpoints.MapGrpcService(); + endpoints.MapGrpcService(); + }); + } +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosServices/appsettings.Development.json b/MareSynchronosServer/MareSynchronosServices/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/MareSynchronosServer/MareSynchronosServices/appsettings.json b/MareSynchronosServer/MareSynchronosServices/appsettings.json new file mode 100644 index 0000000..fdc0173 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/appsettings.json @@ -0,0 +1,29 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Host=localhost;Port=5432;Database=mare;Username=postgres" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "Kestrel": { + "Endpoints": { + "Http": { + "Protocols": "Http2", + "Url": "http://+:5002" + } + } + }, + "DbContextPoolSize": 1024, + "DiscordBotToken": "", + "UnusedFileRetentionPeriodInDays": 7, + "PurgeUnusedAccounts": true, + "PurgeUnusedAccountsPeriodInDays": 14, + "CacheSizeHardLimitInGiB": -1, + "CacheDirectory": "G:\\ServerTest", // do not delete this key and set it to the path where the files will be stored + "FailedAuthForTempBan": 5, + "TempBanDurationInMinutes": 30, + "AllowedHosts": "*" +} diff --git a/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthenticationHandler.cs b/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthenticationHandler.cs deleted file mode 100644 index 57ab121..0000000 --- a/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthenticationHandler.cs +++ /dev/null @@ -1,217 +0,0 @@ -using System.Security.Claims; -using System.Security.Cryptography; -using System.Text; -using System.Text.Encodings.Web; -using MareSynchronosServer; -using MareSynchronosServer.Metrics; -using MareSynchronosShared.Data; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using ISystemClock = Microsoft.AspNetCore.Authentication.ISystemClock; - -namespace MareSynchronosShared.Authentication -{ - public class FailedAuthorization : IDisposable - { - private int failedAttempts = 1; - public int FailedAttempts => failedAttempts; - public Task ResetTask { get; set; } - public CancellationTokenSource? ResetCts { get; set; } - - public void Dispose() - { - try - { - ResetCts?.Cancel(); - ResetCts?.Dispose(); - } - catch { } - } - - public void IncreaseFailedAttempts() - { - Interlocked.Increment(ref failedAttempts); - } - } - - public class SecretKeyAuthenticationHandler : AuthenticationHandler - { - private readonly IHttpContextAccessor _accessor; - private readonly MareDbContext _mareDbContext; - private readonly IConfiguration _configuration; - public const string AuthScheme = "SecretKeyAuth"; - private const string unauthorized = "Unauthorized"; - public static readonly Dictionary Authentications = new(); - private static readonly Dictionary FailedAuthorizations = new(); - private static readonly object authDictLock = new(); - private static readonly object failedAuthLock = new(); - private readonly int failedAttemptsForTempBan; - private readonly int tempBanMinutes; - - public static void ClearUnauthorizedUsers() - { - lock (authDictLock) - { - foreach (var item in Authentications.ToArray()) - { - if (item.Value == unauthorized) - { - Authentications[item.Key] = string.Empty; - } - } - } - } - - public static void RemoveAuthentication(string uid) - { - lock (authDictLock) - { - var auth = Authentications.Where(u => u.Value == uid); - if (auth.Any()) - { - Authentications.Remove(auth.First().Key); - } - } - } - - protected override async Task HandleAuthenticateAsync() - { - MareMetrics.AuthenticationRequests.Inc(); - - if (!Request.Headers.ContainsKey("Authorization")) - { - MareMetrics.AuthenticationFailures.Inc(); - return AuthenticateResult.Fail("Failed Authorization"); - } - - var authHeader = Request.Headers["Authorization"].ToString(); - - if (string.IsNullOrEmpty(authHeader)) - { - MareMetrics.AuthenticationFailures.Inc(); - return AuthenticateResult.Fail("Failed Authorization"); - } - - var ip = _accessor.GetIpAddress(); - - lock (failedAuthLock) - { - if (FailedAuthorizations.TryGetValue(ip, out var failedAuth) && failedAuth.FailedAttempts > failedAttemptsForTempBan) - { - MareMetrics.AuthenticationFailures.Inc(); - - failedAuth.ResetCts?.Cancel(); - failedAuth.ResetCts?.Dispose(); - failedAuth.ResetCts = new CancellationTokenSource(); - var token = failedAuth.ResetCts.Token; - failedAuth.ResetTask = Task.Run(async () => - { - await Task.Delay(TimeSpan.FromMinutes(tempBanMinutes), token).ConfigureAwait(false); - if (token.IsCancellationRequested) return; - FailedAuthorization fauth; - lock (failedAuthLock) - { - FailedAuthorizations.Remove(ip, out fauth); - } - fauth.Dispose(); - }, token); - - Logger.LogWarning("TempBan {ip} for authorization spam", ip); - return AuthenticateResult.Fail("Failed Authorization"); - } - } - - using var sha256 = SHA256.Create(); - var hashedHeader = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(authHeader))).Replace("-", ""); - - string uid; - lock (authDictLock) - { - if (Authentications.TryGetValue(hashedHeader, out uid)) - { - if (uid == unauthorized) - { - MareMetrics.AuthenticationFailures.Inc(); - - lock (failedAuthLock) - { - Logger.LogWarning("Failed authorization from {ip}", ip); - if (FailedAuthorizations.TryGetValue(ip, out var auth)) - { - auth.IncreaseFailedAttempts(); - } - else - { - FailedAuthorizations[ip] = new FailedAuthorization(); - } - } - - return AuthenticateResult.Fail("Failed Authorization"); - } - - MareMetrics.AuthenticationCacheHits.Inc(); - } - } - - if (string.IsNullOrEmpty(uid)) - { - uid = (await _mareDbContext.Auth.AsNoTracking() - .FirstOrDefaultAsync(m => m.HashedKey == hashedHeader).ConfigureAwait(false))?.UserUID; - - if (uid == null) - { - lock (authDictLock) - { - Authentications[hashedHeader] = unauthorized; - } - - Logger.LogWarning("Failed authorization from {ip}", ip); - lock (failedAuthLock) - { - if (FailedAuthorizations.TryGetValue(ip, out var auth)) - { - auth.IncreaseFailedAttempts(); - } - else - { - FailedAuthorizations[ip] = new FailedAuthorization(); - } - } - - MareMetrics.AuthenticationFailures.Inc(); - return AuthenticateResult.Fail("Failed Authorization"); - } - else - { - Authentications[hashedHeader] = uid; - } - } - - var claims = new List { - new Claim(ClaimTypes.NameIdentifier, uid) - }; - - var identity = new ClaimsIdentity(claims, nameof(SecretKeyAuthenticationHandler)); - var principal = new ClaimsPrincipal(identity); - var ticket = new AuthenticationTicket(principal, Scheme.Name); - - MareMetrics.AuthenticationSuccesses.Inc(); - - return AuthenticateResult.Success(ticket); - } - - public SecretKeyAuthenticationHandler(IOptionsMonitor options, IHttpContextAccessor accessor, - MareDbContext mareDbContext, IConfiguration configuration, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) - { - _accessor = accessor; - _mareDbContext = mareDbContext; - _configuration = configuration; - failedAttemptsForTempBan = _configuration.GetValue("FailedAuthForTempBan", 5); - tempBanMinutes = _configuration.GetValue("TempBanDurationInMinutes", 30); - } - } -} diff --git a/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyGrpcAuthenticationHandler.cs b/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyGrpcAuthenticationHandler.cs new file mode 100644 index 0000000..6a9ad18 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyGrpcAuthenticationHandler.cs @@ -0,0 +1,59 @@ +using System.Security.Claims; +using System.Security.Cryptography; +using System.Text; +using System.Text.Encodings.Web; +using MareSynchronosServer; +using MareSynchronosShared.Data; +using MareSynchronosShared.Metrics; +using MareSynchronosShared.Protos; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using ISystemClock = Microsoft.AspNetCore.Authentication.ISystemClock; + +namespace MareSynchronosShared.Authentication +{ + + public class SecretKeyGrpcAuthenticationHandler : AuthenticationHandler + { + public const string AuthScheme = "SecretKeyGrpcAuth"; + + private readonly AuthService.AuthServiceClient _authClient; + private readonly IHttpContextAccessor _accessor; + + public SecretKeyGrpcAuthenticationHandler(IHttpContextAccessor accessor, AuthService.AuthServiceClient authClient, + IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) + { + this._authClient = authClient; + _accessor = accessor; + } + + protected override async Task HandleAuthenticateAsync() + { + Request.Headers.TryGetValue("Authorization", out var authHeader); + var ip = _accessor.GetIpAddress(); + + var authResult = await _authClient.AuthorizeAsync(new AuthRequest() {Ip = ip, SecretKey = authHeader}); + + if (!authResult.Success) + { + return AuthenticateResult.Fail("Failed Authorization"); + } + + string uid = authResult.Uid; + + var claims = new List + { + new(ClaimTypes.NameIdentifier, uid) + }; + + var identity = new ClaimsIdentity(claims, nameof(SecretKeyGrpcAuthenticationHandler)); + var principal = new ClaimsPrincipal(identity); + var ticket = new AuthenticationTicket(principal, Scheme.Name); + + return AuthenticateResult.Success(ticket); + } + } +} diff --git a/MareSynchronosServer/MareSynchronosShared/MareSynchronosShared.csproj b/MareSynchronosServer/MareSynchronosShared/MareSynchronosShared.csproj index f81b7f4..afba71f 100644 --- a/MareSynchronosServer/MareSynchronosShared/MareSynchronosShared.csproj +++ b/MareSynchronosServer/MareSynchronosShared/MareSynchronosShared.csproj @@ -6,10 +6,19 @@ enable + + + + + + + + + @@ -22,4 +31,10 @@ + + + Both + + + diff --git a/MareSynchronosServer/MareSynchronosShared/Metrics/MareMetrics.cs b/MareSynchronosServer/MareSynchronosShared/Metrics/MareMetrics.cs deleted file mode 100644 index 7cd69d2..0000000 --- a/MareSynchronosServer/MareSynchronosShared/Metrics/MareMetrics.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.IO; -using System.Linq; -using MareSynchronosShared.Data; -using Microsoft.Extensions.Configuration; -using Prometheus; - -namespace MareSynchronosServer.Metrics -{ - public class MareMetrics - { - public static readonly Counter InitializedConnections = - Prometheus.Metrics.CreateCounter("mare_initialized_connections", "Initialized Connections"); - public static readonly Gauge Connections = - Prometheus.Metrics.CreateGauge("mare_unauthorized_connections", "Unauthorized Connections"); - public static readonly Gauge AuthorizedConnections = - Prometheus.Metrics.CreateGauge("mare_authorized_connections", "Authorized Connections"); - public static readonly Gauge AvailableWorkerThreads = Prometheus.Metrics.CreateGauge("mare_available_threadpool", "Available Threadpool Workers"); - public static readonly Gauge AvailableIOWorkerThreads = Prometheus.Metrics.CreateGauge("mare_available_threadpool_io", "Available Threadpool IO Workers"); - - public static readonly Gauge UsersRegistered = Prometheus.Metrics.CreateGauge("mare_users_registered", "Total Registrations"); - - public static readonly Gauge Pairs = Prometheus.Metrics.CreateGauge("mare_pairs", "Total Pairs"); - public static readonly Gauge PairsPaused = Prometheus.Metrics.CreateGauge("mare_pairs_paused", "Total Paused Pairs"); - - public static readonly Gauge FilesTotal = Prometheus.Metrics.CreateGauge("mare_files", "Total uploaded files"); - public static readonly Gauge FilesTotalSize = - Prometheus.Metrics.CreateGauge("mare_files_size", "Total uploaded files (bytes)"); - - public static readonly Counter UserPushData = Prometheus.Metrics.CreateCounter("mare_user_push", "Users pushing data"); - public static readonly Counter UserPushDataTo = - Prometheus.Metrics.CreateCounter("mare_user_push_to", "Users Receiving Data"); - - public static readonly Counter UserDownloadedFiles = - Prometheus.Metrics.CreateCounter("mare_user_downloaded_files", "Total Downloaded Files by Users"); - public static readonly Counter UserDownloadedFilesSize = - Prometheus.Metrics.CreateCounter("mare_user_downloaded_files_size", "Total Downloaded Files Size by Users"); - - public static readonly Gauge - CPUUsage = Prometheus.Metrics.CreateGauge("mare_cpu_usage", "Total calculated CPU usage in %"); - public static readonly Gauge RAMUsage = - Prometheus.Metrics.CreateGauge("mare_ram_usage", "Total calculated RAM usage in bytes for Mare + MSSQL"); - public static readonly Gauge NetworkOut = Prometheus.Metrics.CreateGauge("mare_network_out", "Network out in byte/s"); - public static readonly Gauge NetworkIn = Prometheus.Metrics.CreateGauge("mare_network_in", "Network in in byte/s"); - public static readonly Counter AuthenticationRequests = Prometheus.Metrics.CreateCounter("mare_auth_requests", "Mare Authentication Requests"); - public static readonly Counter AuthenticationCacheHits = Prometheus.Metrics.CreateCounter("mare_auth_requests_cachehit", "Mare Authentication Requests Cache Hits"); - public static readonly Counter AuthenticationFailures = Prometheus.Metrics.CreateCounter("mare_auth_requests_fail", "Mare Authentication Requests Failed"); - public static readonly Counter AuthenticationSuccesses = Prometheus.Metrics.CreateCounter("mare_auth_requests_success", "Mare Authentication Requests Success"); - - public static void InitializeMetrics(MareDbContext dbContext, IConfiguration configuration) - { - UsersRegistered.IncTo(dbContext.Users.Count()); - Pairs.IncTo(dbContext.ClientPairs.Count()); - PairsPaused.IncTo(dbContext.ClientPairs.Count(p => p.IsPaused)); - FilesTotal.IncTo(dbContext.Files.Count()); - FilesTotalSize.IncTo(Directory.EnumerateFiles(configuration["CacheDirectory"]).Sum(f => new FileInfo(f).Length)); - } - } -} diff --git a/MareSynchronosServer/MareSynchronosShared/Metrics/MetricsAPI.cs b/MareSynchronosServer/MareSynchronosShared/Metrics/MetricsAPI.cs new file mode 100644 index 0000000..1d420e9 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosShared/Metrics/MetricsAPI.cs @@ -0,0 +1,21 @@ +namespace MareSynchronosShared.Metrics; + +public class MetricsAPI +{ + public const string CounterInitializedConnections = "mare_initialized_connections"; + public const string GaugeConnections = "mare_unauthorized_connections"; + public const string GaugeAuthorizedConnections = "mare_authorized_connections"; + public const string GaugeAvailableWorkerThreads = "mare_available_threadpool"; + public const string GaugeAvailableIOWorkerThreads = "mare_available_threadpool_io"; + public const string GaugeUsersRegistered = "mare_users_registered"; + public const string GaugePairs = "mare_pairs"; + public const string GaugePairsPaused = "mare_pairs_paused"; + public const string GaugeFilesTotal = "mare_files"; + public const string GaugeFilesTotalSize = "mare_files_size"; + public const string CounterUserPushData = "mare_user_push"; + public const string CounterUserPushDataTo = "mare_user_push_to"; + public const string CounterAuthenticationRequests = "mare_auth_requests"; + public const string CounterAuthenticationCacheHits = "mare_auth_requests_cachehit"; + public const string CounterAuthenticationFailures = "mare_auth_requests_fail"; + public const string CounterAuthenticationSuccesses = "mare_auth_requests_success"; +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosShared/Protos/mareservices.proto b/MareSynchronosServer/MareSynchronosShared/Protos/mareservices.proto new file mode 100644 index 0000000..96e405d --- /dev/null +++ b/MareSynchronosServer/MareSynchronosShared/Protos/mareservices.proto @@ -0,0 +1,49 @@ +syntax = "proto3"; + +option csharp_namespace = "MareSynchronosShared.Protos"; + +package mareservices; + +service AuthService { + rpc Authorize (AuthRequest) returns (AuthReply); + rpc RemoveAuth (RemoveAuthRequest) returns (Empty); + rpc ClearUnauthorized (Empty) returns (Empty); +} + +service MetricsService { + rpc IncreaseCounter (IncreaseCounterRequest) returns (Empty); + rpc SetGauge (SetGaugeRequest) returns (Empty); + rpc DecGauge (GaugeRequest) returns (Empty); + rpc IncGauge (GaugeRequest) returns (Empty); +} + +message Empty { } + +message GaugeRequest { + string gaugeName = 1; + double value = 2; +} + +message SetGaugeRequest { + string gaugeName = 1; + double value = 2; +} + +message IncreaseCounterRequest { + string counterName = 1; + double value = 2; +} + +message RemoveAuthRequest { + string uid = 1; +} + +message AuthRequest { + string ip = 1; + string secretKey = 2; +} + +message AuthReply { + bool success = 1; + string uid = 2; +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosStaticFilesServer/MareSynchronosStaticFilesServer.csproj b/MareSynchronosServer/MareSynchronosStaticFilesServer/MareSynchronosStaticFilesServer.csproj new file mode 100644 index 0000000..31bc086 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosStaticFilesServer/MareSynchronosStaticFilesServer.csproj @@ -0,0 +1,19 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + diff --git a/MareSynchronosServer/MareSynchronosStaticFilesServer/Program.cs b/MareSynchronosServer/MareSynchronosStaticFilesServer/Program.cs new file mode 100644 index 0000000..663da32 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosStaticFilesServer/Program.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Http.Connections; +using Microsoft.AspNetCore.SignalR; + +namespace MareSynchronosStaticFilesServer; + +public class Program +{ + public static void Main(string[] args) + { + var hostBuilder = CreateHostBuilder(args); + var host = hostBuilder.Build(); + + host.Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseSystemd() + .UseConsoleLifetime() + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseContentRoot(AppContext.BaseDirectory); + webBuilder.UseStartup(); + }); +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosStaticFilesServer/Properties/launchSettings.json b/MareSynchronosServer/MareSynchronosStaticFilesServer/Properties/launchSettings.json new file mode 100644 index 0000000..6ba3599 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosStaticFilesServer/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:21378", + "sslPort": 44331 + } + }, + "profiles": { + "MareSynchronosStaticFilesServer": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7094;http://localhost:5094", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/MareSynchronosServer/MareSynchronosStaticFilesServer/Startup.cs b/MareSynchronosServer/MareSynchronosStaticFilesServer/Startup.cs new file mode 100644 index 0000000..c1f99a9 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosStaticFilesServer/Startup.cs @@ -0,0 +1,57 @@ +using MareSynchronosShared.Authentication; +using MareSynchronosShared.Data; +using MareSynchronosShared.Models; +using MareSynchronosShared.Protos; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.FileProviders; + +namespace MareSynchronosStaticFilesServer; + +public class Startup +{ + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddHttpContextAccessor(); + + services.AddTransient(_ => Configuration); + + services.AddGrpcClient(c => + { + c.Address = new Uri(Configuration.GetValue("ServiceAddress")); + }); + + services.AddAuthentication(options => + { + options.DefaultScheme = SecretKeyGrpcAuthenticationHandler.AuthScheme; + }) + .AddScheme(SecretKeyGrpcAuthenticationHandler.AuthScheme, options => { }); + services.AddAuthorization(options => options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseStaticFiles(); + app.UseHttpLogging(); + + app.UseRouting(); + + app.UseAuthentication(); + app.UseAuthorization(); + + app.UseStaticFiles(new StaticFileOptions() + { + FileProvider = new PhysicalFileProvider(Configuration["CacheDirectory"]), + RequestPath = "/cache", + ServeUnknownFileTypes = true + }); + } +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosStaticFilesServer/appsettings.Development.json b/MareSynchronosServer/MareSynchronosStaticFilesServer/appsettings.Development.json new file mode 100644 index 0000000..770d3e9 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosStaticFilesServer/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "DetailedErrors": true, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/MareSynchronosServer/MareSynchronosStaticFilesServer/appsettings.json b/MareSynchronosServer/MareSynchronosStaticFilesServer/appsettings.json new file mode 100644 index 0000000..7eb3997 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosStaticFilesServer/appsettings.json @@ -0,0 +1,21 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Host=localhost;Port=5432;Database=mare;Username=postgres" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://+:5001" + } + } + }, + "AllowedHosts": "*", + "CacheDirectory": "G:\\ServerTest", // do not delete this key and set it to the path where the files will be stored + "ServicesUrl": "http://localhost:5002" +}