diff --git a/MareSynchronosServer/MareSynchronos.API/API.cs b/MareSynchronosServer/MareSynchronos.API/API.cs index 50f9166d..e10aa581 100644 --- a/MareSynchronosServer/MareSynchronos.API/API.cs +++ b/MareSynchronosServer/MareSynchronos.API/API.cs @@ -6,69 +6,51 @@ namespace MareSynchronos.API { - public class API + public class Api { - public const int Version = 4; - } - - public class FilesHubAPI - { - public const string Path = "/files"; + public const int Version = 5; + public const string Path = "/mare"; - public const string SendAbortUpload = "AbortUpload"; - public const string InvokeSendFiles = "SendFiles"; - public const string InvokeIsUploadFinished = "IsUploadFinished"; - public const string SendUploadFileStreamAsync = "UploadFileStreamAsync"; - public const string InvokeGetFileSize = "GetFileSize"; - public const string StreamDownloadFileAsync = "StreamDownloadFileAsync"; - public const string SendDeleteAllFiles = "DeleteAllFiles"; - } + public const string SendFileAbortUpload = "AbortUpload"; + public const string InvokeFileSendFiles = "SendFiles"; + public const string InvokeFileIsUploadFinished = "IsUploadFinished"; + public const string SendFileUploadFileStreamAsync = "UploadFileStreamAsync"; + public const string InvokeFileGetFileSize = "GetFileSize"; + public const string StreamFileDownloadFileAsync = "StreamDownloadFileAsync"; + public const string SendFileDeleteAllFiles = "DeleteAllFiles"; - public class ConnectionHubAPI - { - public const string Path = "/heartbeat"; public const string InvokeHeartbeat = "Heartbeat"; public const string InvokeGetSystemInfo = "GetSystemInfo"; public const string OnUpdateSystemInfo = "OnUpdateSystemInfo"; - } - - public class AdminHubAPI - { - public const string Path = "/admin"; - - public const string InvokeGetOnlineUsers = "GetOnlineUsers"; - public const string InvokeGetBannedUsers = "GetBannedUsers"; - public const string SendUpdateOrAddBannedUser = "UpdateOrAddBannedUser"; - public const string SendDeleteBannedUser = "DeleteBannedUser"; - public const string InvokeGetForbiddenFiles = "GetForbiddenFiles"; - public const string SendUpdateOrAddForbiddenFile = "UpdateOrAddForbiddenFile"; - public const string SendDeleteForbiddenFile = "DeleteForbiddenFile"; - public const string SendChangeModeratorStatus = "ChangeModeratorStatus"; - - public const string OnForcedReconnect = "ForcedReconnect"; - public const string OnUpdateOrAddBannedUser = "UpdateOrAddBannedUser"; - public const string OnDeleteBannedUser = "DeleteBannedUser"; - public const string OnUpdateOrAddForbiddenFile = "UpdateOrAddForbiddenFile"; - public const string OnDeleteForbiddenFile = "DeleteForbiddenFile"; - } - - public class UserHubAPI - { - public const string Path = "/user"; - - public const string InvokeGetOnlineUsers = "GetOnlineUsers"; - public const string InvokeRegister = "Register"; - public const string InvokePushCharacterDataToVisibleClients = "PushCharacterDataToVisibleClients"; - public const string InvokeGetOnlineCharacters = "GetOnlineCharacters"; - public const string SendPairedClientAddition = "SendPairedClientAddition"; - public const string SendPairedClientRemoval = "SendPairedClientRemoval"; - public const string SendPairedClientPauseChange = "SendPairedClientPauseChange"; - public const string InvokeGetPairedClients = "GetPairedClients"; - public const string SendDeleteAccount = "DeleteAccount"; - public const string OnUpdateClientPairs = "UpdateClientPairs"; - public const string OnReceiveCharacterData = "ReceiveCharacterData"; - public const string OnRemoveOnlinePairedPlayer = "RemoveOnlinePairedPlayer"; - public const string OnAddOnlinePairedPlayer = "AddOnlinePairedPlayer"; + public const string InvokeAdminGetOnlineUsers = "AdminGetOnlineUsers"; + public const string InvokeAdminGetBannedUsers = "GetBannedUsers"; + public const string SendAdminUpdateOrAddBannedUser = "UpdateOrAddBannedUser"; + public const string SendAdminDeleteBannedUser = "DeleteBannedUser"; + public const string InvokeAdminGetForbiddenFiles = "GetForbiddenFiles"; + public const string SendAdminUpdateOrAddForbiddenFile = "UpdateOrAddForbiddenFile"; + public const string SendAdminDeleteForbiddenFile = "DeleteForbiddenFile"; + public const string SendAdminChangeModeratorStatus = "ChangeModeratorStatus"; + + public const string OnAdminForcedReconnect = "OnForcedReconnect"; + public const string OnAdminUpdateOrAddBannedUser = "OnUpdateOrAddBannedUser"; + public const string OnAdminDeleteBannedUser = "OnDeleteBannedUser"; + public const string OnAdminUpdateOrAddForbiddenFile = "OnUpdateOrAddForbiddenFile"; + public const string OnAdminDeleteForbiddenFile = "OnDeleteForbiddenFile"; + + public const string InvokeUserGetOnlineUsers = "GetOnlineUsers"; + public const string InvokeUserRegister = "Register"; + public const string InvokeUserPushCharacterDataToVisibleClients = "PushCharacterDataToVisibleClients"; + public const string InvokeUserGetOnlineCharacters = "GetOnlineCharacters"; + public const string SendUserPairedClientAddition = "SendPairedClientAddition"; + public const string SendUserPairedClientRemoval = "SendPairedClientRemoval"; + public const string SendUserPairedClientPauseChange = "SendPairedClientPauseChange"; + public const string InvokeUserGetPairedClients = "GetPairedClients"; + public const string SendUserDeleteAccount = "DeleteAccount"; + + public const string OnUserUpdateClientPairs = "UpdateClientPairs"; + public const string OnUserReceiveCharacterData = "ReceiveCharacterData"; + public const string OnUserRemoveOnlinePairedPlayer = "RemoveOnlinePairedPlayer"; + public const string OnUserAddOnlinePairedPlayer = "AddOnlinePairedPlayer"; } } diff --git a/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs b/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs index 428d4789..b69baf4a 100644 --- a/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs +++ b/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Data; -using System.Linq; using System.Security.Claims; using System.Security.Cryptography; using System.Text; @@ -32,7 +30,8 @@ protected override async Task HandleAuthenticateAsync() using var sha256 = SHA256.Create(); var hashedHeader = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(authHeader))).Replace("-", ""); - var uid = (await _mareDbContext.Users.AsNoTracking().FirstOrDefaultAsync(m => m.SecretKey == hashedHeader))?.UID; + var uid = (await _mareDbContext.Auth.Include("User").AsNoTracking() + .FirstOrDefaultAsync(m => m.HashedKey == hashedHeader))?.UserUID; if (uid == null) { diff --git a/MareSynchronosServer/MareSynchronosServer/Data/MareDbContext.cs b/MareSynchronosServer/MareSynchronosServer/Data/MareDbContext.cs index ebd86038..3b8cc997 100644 --- a/MareSynchronosServer/MareSynchronosServer/Data/MareDbContext.cs +++ b/MareSynchronosServer/MareSynchronosServer/Data/MareDbContext.cs @@ -1,8 +1,5 @@ -using System.Collections.Generic; -using MareSynchronos.API; -using MareSynchronosServer.Models; +using MareSynchronosServer.Models; using Microsoft.EntityFrameworkCore; -using Newtonsoft.Json; namespace MareSynchronosServer.Data { @@ -17,12 +14,16 @@ public MareDbContext(DbContextOptions options) : base(options) public DbSet ClientPairs { get; set; } public DbSet ForbiddenUploadEntries { get; set; } public DbSet BannedUsers { get; set; } + public DbSet Auth { 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("FileCaches"); + modelBuilder.Entity().HasIndex(c => c.UploaderUID); modelBuilder.Entity().ToTable("ClientPairs"); modelBuilder.Entity().HasKey(u => new { u.UserUID, u.OtherUserUID }); modelBuilder.Entity().HasIndex(c => c.UserUID); diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/BaseHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/BaseHub.cs deleted file mode 100644 index 434d88c7..00000000 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/BaseHub.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Linq; -using System.Security.Claims; -using System.Security.Cryptography; -using System.Threading.Tasks; -using MareSynchronosServer.Data; -using Microsoft.AspNetCore.SignalR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; - -namespace MareSynchronosServer.Hubs -{ - public abstract class BaseHub : Hub - { - protected readonly ILogger Logger; - protected MareDbContext DbContext { get; init; } - - protected BaseHub(MareDbContext context, ILogger logger) - { - Logger = logger; - DbContext = context; - } - - protected string AuthenticatedUserId => Context.User?.Claims?.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? "Unknown"; - - protected async Task GetAuthenticatedUserUntrackedAsync() - { - return await DbContext.Users.AsNoTrackingWithIdentityResolution().SingleAsync(u => u.UID == AuthenticatedUserId); - } - - public static string GenerateRandomString(int length, string allowableChars = null) - { - if (string.IsNullOrEmpty(allowableChars)) - allowableChars = @"ABCDEFGHJKLMNPQRSTUVWXYZ0123456789"; - - // Generate random data - var rnd = new byte[length]; - using (var rng = new RNGCryptoServiceProvider()) - rng.GetBytes(rnd); - - // 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/MareSynchronosServer/Hubs/ConnectionHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/ConnectionHub.cs deleted file mode 100644 index ca283a70..00000000 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/ConnectionHub.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Linq; -using System.Runtime.ConstrainedExecution; -using System.Security.Claims; -using System.Security.Cryptography.X509Certificates; -using System.Threading.Tasks; -using MareSynchronos.API; -using MareSynchronosServer.Data; -using Microsoft.AspNetCore.SignalR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; - -namespace MareSynchronosServer.Hubs -{ - public class ConnectionHub : BaseHub - { - private readonly SystemInfoService _systemInfoService; - - public ConnectionHub(MareDbContext mareDbContext, ILogger logger, SystemInfoService systemInfoService) : base(mareDbContext, logger) - { - _systemInfoService = systemInfoService; - } - - [HubMethodName(ConnectionHubAPI.InvokeHeartbeat)] - public async Task Heartbeat(string? characterIdentification) - { - var userId = Context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; - - Logger.LogInformation("Connection from " + userId + ", CI: " + characterIdentification); - - await Clients.Caller.SendAsync(ConnectionHubAPI.OnUpdateSystemInfo, _systemInfoService.SystemInfoDto); - - var isBanned = await DbContext.BannedUsers.AsNoTracking().AnyAsync(u => u.CharacterIdentification == characterIdentification); - - if (userId != null && !isBanned && !string.IsNullOrEmpty(characterIdentification)) - { - Logger.LogInformation("Connection from " + userId); - var user = (await DbContext.Users.SingleAsync(u => u.UID == userId)); - user.CharacterIdentification = characterIdentification; - await DbContext.SaveChangesAsync(); - return new ConnectionDto - { - ServerVersion = API.Version, - UID = userId, - IsModerator = user.IsModerator, - IsAdmin = user.IsAdmin - }; - } - - return new ConnectionDto() - { - ServerVersion = API.Version - }; - } - - [HubMethodName(ConnectionHubAPI.InvokeGetSystemInfo)] - public async Task GetSystemInfo() - { - return _systemInfoService.SystemInfoDto; - } - } -} diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/AdminHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs similarity index 53% rename from MareSynchronosServer/MareSynchronosServer/Hubs/AdminHub.cs rename to MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs index 7f427080..9371e92f 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/AdminHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs @@ -3,85 +3,79 @@ using System.Threading.Tasks; using MareSynchronos.API; using MareSynchronosServer.Authentication; -using MareSynchronosServer.Data; using MareSynchronosServer.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; namespace MareSynchronosServer.Hubs { - public class AdminHub : BaseHub + public partial class MareHub { - public AdminHub(MareDbContext context, ILogger logger) : base(context, logger) - { - } - - private bool IsAdmin => DbContext.Users.Single(b => b.UID == AuthenticatedUserId).IsAdmin; + private bool IsAdmin => _dbContext.Users.Single(b => b.UID == AuthenticatedUserId).IsAdmin; - private bool IsModerator => DbContext.Users.Single(b => b.UID == AuthenticatedUserId).IsModerator || IsAdmin; + private bool IsModerator => _dbContext.Users.Single(b => b.UID == AuthenticatedUserId).IsModerator || IsAdmin; - private List OnlineAdmins => DbContext.Users.Where(u => !string.IsNullOrEmpty(u.CharacterIdentification) && (u.IsModerator || u.IsAdmin)) + private List OnlineAdmins => _dbContext.Users.Where(u => !string.IsNullOrEmpty(u.CharacterIdentification) && (u.IsModerator || u.IsAdmin)) .Select(u => u.UID).ToList(); [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(AdminHubAPI.SendChangeModeratorStatus)] + [HubMethodName(Api.SendAdminChangeModeratorStatus)] 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); if (user == null) return; user.IsModerator = isModerator; - DbContext.Update(user); - await DbContext.SaveChangesAsync(); - await Clients.Users(user.UID).SendAsync(AdminHubAPI.OnForcedReconnect); + _dbContext.Update(user); + await _dbContext.SaveChangesAsync(); + await Clients.Users(user.UID).SendAsync(Api.OnAdminForcedReconnect); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(AdminHubAPI.SendDeleteBannedUser)] + [HubMethodName(Api.SendAdminDeleteBannedUser)] public async Task DeleteBannedUser(BannedUserDto dto) { 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); if (existingUser == null) { return; } - DbContext.Remove(existingUser); - await DbContext.SaveChangesAsync(); - await Clients.Users(OnlineAdmins).SendAsync(AdminHubAPI.OnDeleteBannedUser, dto); + _dbContext.Remove(existingUser); + await _dbContext.SaveChangesAsync(); + await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminDeleteBannedUser, dto); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(AdminHubAPI.SendDeleteForbiddenFile)] + [HubMethodName(Api.SendAdminDeleteForbiddenFile)] public async Task DeleteForbiddenFile(ForbiddenFileDto dto) { 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); if (existingFile == null) { return; } - DbContext.Remove(existingFile); - await DbContext.SaveChangesAsync(); - await Clients.Users(OnlineAdmins).SendAsync(AdminHubAPI.OnDeleteForbiddenFile, dto); + _dbContext.Remove(existingFile); + await _dbContext.SaveChangesAsync(); + await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminDeleteForbiddenFile, dto); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(AdminHubAPI.InvokeGetBannedUsers)] + [HubMethodName(Api.InvokeAdminGetBannedUsers)] public async Task> GetBannedUsers() { if (!IsModerator) return null; - return await DbContext.BannedUsers.AsNoTracking().Select(b => new BannedUserDto() + return await _dbContext.BannedUsers.AsNoTracking().Select(b => new BannedUserDto() { CharacterHash = b.CharacterIdentification, Reason = b.Reason @@ -89,12 +83,12 @@ public async Task> GetBannedUsers() } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(AdminHubAPI.InvokeGetForbiddenFiles)] + [HubMethodName(Api.InvokeAdminGetForbiddenFiles)] public async Task> GetForbiddenFiles() { if (!IsModerator) return null; - return await DbContext.ForbiddenUploadEntries.AsNoTracking().Select(b => new ForbiddenFileDto() + return await _dbContext.ForbiddenUploadEntries.AsNoTracking().Select(b => new ForbiddenFileDto() { Hash = b.Hash, ForbiddenBy = b.ForbiddenBy @@ -102,12 +96,12 @@ public async Task> GetForbiddenFiles() } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(AdminHubAPI.InvokeGetOnlineUsers)] - public async Task> GetOnlineUsers() + [HubMethodName(Api.InvokeUserGetOnlineUsers)] + public async Task> AdminGetOnlineUsers() { if (!IsModerator) return null; - return await DbContext.Users.AsNoTracking().Where(b => !string.IsNullOrEmpty(b.CharacterIdentification)).Select(b => new OnlineUserDto + return await _dbContext.Users.AsNoTracking().Where(b => !string.IsNullOrEmpty(b.CharacterIdentification)).Select(b => new OnlineUserDto { CharacterNameHash = b.CharacterIdentification, UID = b.UID, @@ -117,62 +111,62 @@ public async Task> GetOnlineUsers() } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(AdminHubAPI.SendUpdateOrAddBannedUser)] + [HubMethodName(Api.SendAdminUpdateOrAddBannedUser)] public async Task UpdateOrAddBannedUser(BannedUserDto dto) { 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); if (existingUser != null) { existingUser.Reason = dto.Reason; - DbContext.Update(existingUser); + _dbContext.Update(existingUser); } else { - await DbContext.BannedUsers.AddAsync(new Banned + await _dbContext.BannedUsers.AddAsync(new Banned { CharacterIdentification = dto.CharacterHash, Reason = dto.Reason }); } - await DbContext.SaveChangesAsync(); - await Clients.Users(OnlineAdmins).SendAsync(AdminHubAPI.OnUpdateOrAddBannedUser, dto); + await _dbContext.SaveChangesAsync(); + await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminUpdateOrAddBannedUser, dto); var bannedUser = - await DbContext.Users.SingleOrDefaultAsync(u => u.CharacterIdentification == dto.CharacterHash); + await _dbContext.Users.SingleOrDefaultAsync(u => u.CharacterIdentification == dto.CharacterHash); if (bannedUser != null) { - await Clients.User(bannedUser.UID).SendAsync(AdminHubAPI.OnForcedReconnect); + await Clients.User(bannedUser.UID).SendAsync(Api.OnAdminForcedReconnect); } } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(AdminHubAPI.SendUpdateOrAddForbiddenFile)] + [HubMethodName(Api.SendAdminUpdateOrAddForbiddenFile)] public async Task UpdateOrAddForbiddenFile(ForbiddenFileDto dto) { 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); if (existingForbiddenFile != null) { existingForbiddenFile.ForbiddenBy = dto.ForbiddenBy; - DbContext.Update(existingForbiddenFile); + _dbContext.Update(existingForbiddenFile); } else { - await DbContext.ForbiddenUploadEntries.AddAsync(new ForbiddenUploadEntry + await _dbContext.ForbiddenUploadEntries.AddAsync(new ForbiddenUploadEntry { Hash = dto.Hash, ForbiddenBy = dto.ForbiddenBy }); } - await DbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync(); - await Clients.Users(OnlineAdmins).SendAsync(AdminHubAPI.OnUpdateOrAddForbiddenFile, dto); + await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminUpdateOrAddForbiddenFile, dto); } } } diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/FilesHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs similarity index 57% rename from MareSynchronosServer/MareSynchronosServer/Hubs/FilesHub.cs rename to MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs index d7dd1804..f32a7e76 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/FilesHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Data; -using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.CompilerServices; @@ -10,60 +8,51 @@ using System.Threading.Tasks; using MareSynchronos.API; using MareSynchronosServer.Authentication; -using MareSynchronosServer.Data; using MareSynchronosServer.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace MareSynchronosServer.Hubs { - public class FilesHub : BaseHub + public partial class MareHub { - private readonly IConfiguration _configuration; - - public FilesHub(ILogger logger, MareDbContext context, IConfiguration configuration) : base(context, logger) - { - _configuration = configuration; - } - private string BasePath => _configuration["CacheDirectory"]; [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(FilesHubAPI.SendAbortUpload)] + [HubMethodName(Api.SendFileAbortUpload)] public async Task AbortUpload() { - Logger.LogInformation("User " + AuthenticatedUserId + " aborted upload"); + _logger.LogInformation("User " + AuthenticatedUserId + " aborted upload"); var userId = AuthenticatedUserId; - var notUploadedFiles = DbContext.Files.Where(f => !f.Uploaded && f.Uploader.UID == userId).ToList(); - DbContext.RemoveRange(notUploadedFiles); - await DbContext.SaveChangesAsync(); + var notUploadedFiles = _dbContext.Files.Where(f => !f.Uploaded && f.Uploader.UID == userId).ToList(); + _dbContext.RemoveRange(notUploadedFiles); + await _dbContext.SaveChangesAsync(); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(FilesHubAPI.SendDeleteAllFiles)] + [HubMethodName(Api.SendFileDeleteAllFiles)] public async Task DeleteAllFiles() { - Logger.LogInformation("User " + AuthenticatedUserId + " deleted all their files"); + _logger.LogInformation("User " + AuthenticatedUserId + " deleted all their files"); - 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(); foreach (var file in ownFiles) { File.Delete(Path.Combine(BasePath, file.Hash)); } - DbContext.Files.RemoveRange(ownFiles); - await DbContext.SaveChangesAsync(); + _dbContext.Files.RemoveRange(ownFiles); + await _dbContext.SaveChangesAsync(); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(FilesHubAPI.StreamDownloadFileAsync)] + [HubMethodName(Api.StreamFileDownloadFileAsync)] public async IAsyncEnumerable DownloadFileAsync(string hash, [EnumeratorCancellation] CancellationToken ct) { - Logger.LogInformation("User " + AuthenticatedUserId + " downloading file: " + hash); + _logger.LogInformation("User " + AuthenticatedUserId + " downloading file: " + hash); - var file = DbContext.Files.AsNoTracking() + var file = _dbContext.Files.AsNoTracking() .SingleOrDefault(f => f.Hash == hash); if (file == null) yield break; var chunkSize = 1024 * 512; // 512kb @@ -77,15 +66,15 @@ public async IAsyncEnumerable DownloadFileAsync(string hash, [Enumerator yield return readByteCount == chunkSize ? buffer.ToArray() : buffer.Take(readByteCount).ToArray(); } - Logger.LogInformation("User " + AuthenticatedUserId + " finished downloading file: " + hash); + _logger.LogInformation("User " + AuthenticatedUserId + " finished downloading file: " + hash); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(FilesHubAPI.InvokeGetFileSize)] + [HubMethodName(Api.InvokeFileGetFileSize)] public async Task GetFileSize(string hash) { - var file = await DbContext.Files.AsNoTracking().SingleOrDefaultAsync(f => f.Hash == hash); - var forbidden = DbContext.ForbiddenUploadEntries.AsNoTracking(). + var file = await _dbContext.Files.AsNoTracking().SingleOrDefaultAsync(f => f.Hash == hash); + var forbidden = _dbContext.ForbiddenUploadEntries.AsNoTracking(). SingleOrDefault(f => f.Hash == hash); var fileInfo = new FileInfo(Path.Combine(BasePath, hash)); long fileSize = 0; @@ -109,8 +98,8 @@ public async Task GetFileSize(string hash) if (!fileInfo.Exists && file != null) { - DbContext.Files.Remove(file); - await DbContext.SaveChangesAsync(); + _dbContext.Files.Remove(file); + await _dbContext.SaveChangesAsync(); } return response; @@ -118,30 +107,21 @@ public async Task GetFileSize(string hash) } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(FilesHubAPI.InvokeIsUploadFinished)] + [HubMethodName(Api.InvokeFileIsUploadFinished)] public async Task IsUploadFinished() { var userUid = AuthenticatedUserId; - return await DbContext.Files.AsNoTracking() + return await _dbContext.Files.AsNoTracking() .AnyAsync(f => f.Uploader.UID == userUid && !f.Uploaded); } - - public override Task OnDisconnectedAsync(Exception exception) - { - var userId = AuthenticatedUserId; - var notUploadedFiles = DbContext.Files.Where(f => !f.Uploaded && f.Uploader.UID == userId).ToList(); - DbContext.RemoveRange(notUploadedFiles); - DbContext.SaveChanges(); - return base.OnDisconnectedAsync(exception); - } - + [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(FilesHubAPI.InvokeSendFiles)] + [HubMethodName(Api.InvokeFileSendFiles)] public async Task> SendFiles(List fileListHashes) { fileListHashes = fileListHashes.Where(f => !string.IsNullOrEmpty(f)).Distinct().ToList(); - Logger.LogInformation("User " + AuthenticatedUserId + " sending files"); - var forbiddenFiles = await DbContext.ForbiddenUploadEntries.AsNoTracking().Where(f => fileListHashes.Contains(f.Hash)).ToListAsync(); + _logger.LogInformation("User " + AuthenticatedUserId + " sending files"); + var forbiddenFiles = await _dbContext.ForbiddenUploadEntries.AsNoTracking().Where(f => fileListHashes.Contains(f.Hash)).ToListAsync(); var filesToUpload = new List(); filesToUpload.AddRange(forbiddenFiles.Select(f => new UploadFileDto() { @@ -150,18 +130,18 @@ public async Task> SendFiles(List fileListHashes) IsForbidden = true })); fileListHashes.RemoveAll(f => filesToUpload.Any(u => u.Hash == f)); - var existingFiles = await DbContext.Files.AsNoTracking().Where(f => fileListHashes.Contains(f.Hash)).ToListAsync(); + var existingFiles = await _dbContext.Files.AsNoTracking().Where(f => fileListHashes.Contains(f.Hash)).ToListAsync(); foreach (var file in fileListHashes.Where(f => existingFiles.All(e => e.Hash != f) && filesToUpload.All(u => u.Hash != f))) { - Logger.LogInformation("User " + AuthenticatedUserId + " needs upload: " + file); + _logger.LogInformation("User " + AuthenticatedUserId + " needs upload: " + file); var userId = AuthenticatedUserId; - await DbContext.Files.AddAsync(new FileCache() + await _dbContext.Files.AddAsync(new FileCache() { Hash = file, Uploaded = false, - Uploader = DbContext.Users.Single(u => u.UID == userId) + Uploader = _dbContext.Users.Single(u => u.UID == userId) }); - await DbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync(); filesToUpload.Add(new UploadFileDto { Hash = file @@ -172,14 +152,14 @@ await DbContext.Files.AddAsync(new FileCache() } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(FilesHubAPI.SendUploadFileStreamAsync)] + [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); - var relatedFile = DbContext.Files.SingleOrDefault(f => f.Hash == hash && f.Uploader.UID == AuthenticatedUserId && f.Uploaded == false); + var relatedFile = _dbContext.Files.SingleOrDefault(f => f.Hash == hash && f.Uploader.UID == AuthenticatedUserId && f.Uploaded == false); if (relatedFile == null) return; - var forbiddenFile = DbContext.ForbiddenUploadEntries.SingleOrDefault(f => f.Hash == hash); + var forbiddenFile = _dbContext.ForbiddenUploadEntries.SingleOrDefault(f => f.Hash == hash); if (forbiddenFile != null) return; var uploadedFile = new List(); try @@ -193,8 +173,8 @@ public async Task UploadFileStreamAsync(string hash, IAsyncEnumerable fi { try { - DbContext.Files.Remove(relatedFile); - await DbContext.SaveChangesAsync(); + _dbContext.Files.Remove(relatedFile); + await _dbContext.SaveChangesAsync(); } catch { @@ -204,7 +184,7 @@ public async Task UploadFileStreamAsync(string hash, IAsyncEnumerable fi return; } - Logger.LogInformation("User " + AuthenticatedUserId + " upload finished: " + hash + ", size: " + uploadedFile.Count); + _logger.LogInformation("User " + AuthenticatedUserId + " upload finished: " + hash + ", size: " + uploadedFile.Count); try { @@ -214,24 +194,24 @@ public async Task UploadFileStreamAsync(string hash, IAsyncEnumerable fi var computedHashString = BitConverter.ToString(computedHash).Replace("-", ""); if (hash != computedHashString) { - Logger.LogWarning($"Computed file hash was not expected file hash. Computed: {computedHashString}, Expected {hash}"); - DbContext.Remove(relatedFile); - await DbContext.SaveChangesAsync(); + _logger.LogWarning($"Computed file hash was not expected file hash. Computed: {computedHashString}, Expected {hash}"); + _dbContext.Remove(relatedFile); + await _dbContext.SaveChangesAsync(); return; } await File.WriteAllBytesAsync(Path.Combine(BasePath, hash), uploadedFile.ToArray()); - relatedFile = DbContext.Files.Single(f => f.Hash == hash); + relatedFile = _dbContext.Files.Single(f => f.Hash == hash); relatedFile.Uploaded = true; - await DbContext.SaveChangesAsync(); - Logger.LogInformation("File " + hash + " added to DB"); + await _dbContext.SaveChangesAsync(); + _logger.LogInformation("File " + hash + " added to DB"); } catch (Exception ex) { - Logger.LogWarning(ex, "Upload failed"); - DbContext.Remove(relatedFile); - await DbContext.SaveChangesAsync(); + _logger.LogWarning(ex, "Upload failed"); + _dbContext.Remove(relatedFile); + await _dbContext.SaveChangesAsync(); } } diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/UserHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs similarity index 56% rename from MareSynchronosServer/MareSynchronosServer/Hubs/UserHub.cs rename to MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs index 2fc78d1d..739b29eb 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/UserHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs @@ -1,13 +1,11 @@ using System; using System.Collections.Generic; -using System.Data; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using MareSynchronos.API; using MareSynchronosServer.Authentication; -using MareSynchronosServer.Data; using MareSynchronosServer.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; @@ -16,73 +14,69 @@ namespace MareSynchronosServer.Hubs { - public class UserHub : BaseHub + public partial class MareHub { - public UserHub(ILogger logger, MareDbContext dbContext) : base(dbContext, logger) - { - } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(UserHubAPI.SendDeleteAccount)] + [HubMethodName(Api.SendUserDeleteAccount)] public async Task DeleteAccount() { - Logger.LogInformation("User " + AuthenticatedUserId + " deleted their account"); + _logger.LogInformation("User " + AuthenticatedUserId + " deleted their account"); string userid = AuthenticatedUserId; - var userEntry = await DbContext.Users.SingleAsync(u => u.UID == userid); - var ownPairData = DbContext.ClientPairs.Where(u => u.User.UID == userid); - DbContext.RemoveRange(ownPairData); - await DbContext.SaveChangesAsync(); - var otherPairData = DbContext.ClientPairs.Include(u => u.User).Where(u => u.OtherUser.UID == userid); + var userEntry = await _dbContext.Users.SingleAsync(u => u.UID == userid); + var ownPairData = _dbContext.ClientPairs.Where(u => u.User.UID == userid); + _dbContext.RemoveRange(ownPairData); + await _dbContext.SaveChangesAsync(); + var otherPairData = _dbContext.ClientPairs.Include(u => u.User).Where(u => u.OtherUser.UID == userid); foreach (var pair in otherPairData) { await Clients.User(pair.User.UID) - .SendAsync(UserHubAPI.OnUpdateClientPairs, new ClientPairDto() + .SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() { OtherUID = userid, IsRemoved = true }, userEntry.CharacterIdentification); } - DbContext.RemoveRange(otherPairData); - DbContext.Remove(userEntry); - await DbContext.SaveChangesAsync(); + _dbContext.RemoveRange(otherPairData); + _dbContext.Remove(userEntry); + await _dbContext.SaveChangesAsync(); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(UserHubAPI.InvokeGetOnlineCharacters)] + [HubMethodName(Api.InvokeUserGetOnlineCharacters)] public async Task> GetOnlineCharacters() { - Logger.LogInformation("User " + AuthenticatedUserId + " requested online characters"); + _logger.LogInformation("User " + AuthenticatedUserId + " requested online characters"); var ownUser = await GetAuthenticatedUserUntrackedAsync(); - var otherUsers = await DbContext.ClientPairs.AsNoTracking() + 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(); - var otherEntries = await DbContext.ClientPairs.AsNoTracking() + var otherEntries = await _dbContext.ClientPairs.AsNoTracking() .Include(u => u.User) .Where(u => otherUsers.Any(e => e == u.User) && u.OtherUser == ownUser && !u.IsPaused).ToListAsync(); - await Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync(UserHubAPI.OnAddOnlinePairedPlayer, ownUser.CharacterIdentification); + await Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync(Api.OnUserAddOnlinePairedPlayer, ownUser.CharacterIdentification); return otherEntries.Select(e => e.User.CharacterIdentification).Distinct().ToList(); } - [HubMethodName(UserHubAPI.InvokeGetOnlineUsers)] + [HubMethodName(Api.InvokeAdminGetOnlineUsers)] public async Task GetOnlineUsers() { - return await DbContext.Users.CountAsync(u => !string.IsNullOrEmpty(u.CharacterIdentification)); + return await _dbContext.Users.CountAsync(u => !string.IsNullOrEmpty(u.CharacterIdentification)); } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(UserHubAPI.InvokeGetPairedClients)] + [HubMethodName(Api.InvokeUserGetPairedClients)] public async Task> GetPairedClients() { string userid = AuthenticatedUserId; - var pairs = await DbContext.ClientPairs.AsNoTracking() + var pairs = await _dbContext.ClientPairs.AsNoTracking() .Include(u => u.OtherUser) .Include(u => u.User) .Where(w => w.User.UID == userid) @@ -100,42 +94,16 @@ public async Task> GetPairedClients() }).ToList(); } - public override async Task OnDisconnectedAsync(Exception exception) - { - var user = await DbContext.Users.AsNoTracking().SingleOrDefaultAsync(u => u.UID == AuthenticatedUserId); - if (user != null && !string.IsNullOrEmpty(user.CharacterIdentification)) - { - Logger.LogInformation("Disconnect from " + AuthenticatedUserId); - - var otherUsers = await DbContext.ClientPairs.AsNoTracking() - .Include(u => u.User) - .Include(u => u.OtherUser) - .Where(w => w.User.UID == user.UID && !w.IsPaused) - .Where(w => !string.IsNullOrEmpty(w.OtherUser.CharacterIdentification)) - .Select(e => e.OtherUser).ToListAsync(); - var otherEntries = await DbContext.ClientPairs.AsNoTracking().Include(u => u.User) - .Where(u => otherUsers.Any(e => e == u.User) && u.OtherUser.UID == user.UID && !u.IsPaused).ToListAsync(); - await Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync(UserHubAPI.OnRemoveOnlinePairedPlayer, user.CharacterIdentification); - (await DbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId)).CharacterIdentification = null; - await DbContext.SaveChangesAsync(); - - await Clients.All.SendAsync("UsersOnline", - await DbContext.Users.CountAsync(u => !string.IsNullOrEmpty(u.CharacterIdentification))); - } - - await base.OnDisconnectedAsync(exception); - } - [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(UserHubAPI.InvokePushCharacterDataToVisibleClients)] + [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.Count + " visible clients"); var user = await GetAuthenticatedUserUntrackedAsync(); - var senderPairedUsers = await DbContext.ClientPairs.AsNoTracking() + var senderPairedUsers = await _dbContext.ClientPairs.AsNoTracking() .Include(w => w.User) .Include(w => w.OtherUser) .Where(w => w.User.UID == user.UID && !w.IsPaused @@ -144,77 +112,81 @@ public async Task PushCharacterDataToVisibleClients(CharacterCacheDto characterC foreach (var pairedUser in senderPairedUsers) { - var isPaused = (await DbContext.ClientPairs.AsNoTracking() + var isPaused = (await _dbContext.ClientPairs.AsNoTracking() .FirstOrDefaultAsync(w => w.User.UID == pairedUser.UID && w.OtherUser.UID == user.UID))?.IsPaused ?? true; if (isPaused) continue; - await Clients.User(pairedUser.UID).SendAsync(UserHubAPI.OnReceiveCharacterData, characterCache, + await Clients.User(pairedUser.UID).SendAsync(Api.OnUserReceiveCharacterData, characterCache, user.CharacterIdentification); } } - [HubMethodName(UserHubAPI.InvokeRegister)] + [HubMethodName(Api.InvokeUserRegister)] public async Task Register() { using var sha256 = SHA256.Create(); - var computedHash = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(GenerateRandomString(64)))).Replace("-", ""); - var user = new Models.User - { - SecretKey = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(computedHash))) - .Replace("-", ""), - }; + var user = new User(); var hasValidUid = false; while (!hasValidUid) { var uid = GenerateRandomString(10); - if (DbContext.Users.Any(u => u.UID == uid)) continue; + if (_dbContext.Users.Any(u => u.UID == uid)) continue; user.UID = uid; hasValidUid = true; } // make the first registered user on the service to admin - if (!await DbContext.Users.AnyAsync()) + if (!await _dbContext.Users.AnyAsync()) { user.IsAdmin = true; } - DbContext.Users.Add(user); + 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 + }; + + _dbContext.Users.Add(user); + _dbContext.Auth.Add(auth); - Logger.LogInformation("User registered: " + user.UID); + _logger.LogInformation("User registered: " + user.UID); - await DbContext.SaveChangesAsync(); + await _dbContext.SaveChangesAsync(); return computedHash; } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(UserHubAPI.SendPairedClientAddition)] + [HubMethodName(Api.SendUserPairedClientAddition)] public async Task SendPairedClientAddition(string uid) { 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); - var otherUser = await DbContext.Users + var otherUser = await _dbContext.Users .SingleOrDefaultAsync(u => u.UID == uid); var existingEntry = - await DbContext.ClientPairs.AsNoTracking() + await _dbContext.ClientPairs.AsNoTracking() .FirstOrDefaultAsync(p => p.User.UID == AuthenticatedUserId && p.OtherUser.UID == uid); if (otherUser == null || existingEntry != null) return; - Logger.LogInformation("User " + AuthenticatedUserId + " adding " + uid + " to whitelist"); + _logger.LogInformation("User " + AuthenticatedUserId + " adding " + uid + " to whitelist"); ClientPair wl = new ClientPair() { IsPaused = false, OtherUser = otherUser, User = user }; - await DbContext.ClientPairs.AddAsync(wl); - await DbContext.SaveChangesAsync(); + await _dbContext.ClientPairs.AddAsync(wl); + await _dbContext.SaveChangesAsync(); var otherEntry = OppositeEntry(uid); await Clients.User(user.UID) - .SendAsync(UserHubAPI.OnUpdateClientPairs, new ClientPairDto() + .SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() { OtherUID = otherUser.UID, IsPaused = false, @@ -223,7 +195,7 @@ await Clients.User(user.UID) }, string.Empty); if (otherEntry != null) { - await Clients.User(uid).SendAsync(UserHubAPI.OnUpdateClientPairs, + await Clients.User(uid).SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() { OtherUID = user.UID, @@ -235,33 +207,33 @@ await Clients.User(uid).SendAsync(UserHubAPI.OnUpdateClientPairs, if (!string.IsNullOrEmpty(otherUser.CharacterIdentification)) { await Clients.User(user.UID) - .SendAsync(UserHubAPI.OnAddOnlinePairedPlayer, otherUser.CharacterIdentification); + .SendAsync(Api.OnUserAddOnlinePairedPlayer, otherUser.CharacterIdentification); await Clients.User(otherUser.UID) - .SendAsync(UserHubAPI.OnAddOnlinePairedPlayer, user.CharacterIdentification); + .SendAsync(Api.OnUserAddOnlinePairedPlayer, user.CharacterIdentification); } } } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(UserHubAPI.SendPairedClientPauseChange)] + [HubMethodName(Api.SendUserPairedClientPauseChange)] public async Task SendPairedClientPauseChange(string uid, bool isPaused) { if (uid == AuthenticatedUserId) return; - var user = await DbContext.Users.AsNoTracking() + var user = await _dbContext.Users.AsNoTracking() .SingleAsync(u => u.UID == AuthenticatedUserId); - var otherUser = await DbContext.Users.AsNoTracking() + var otherUser = await _dbContext.Users.AsNoTracking() .SingleOrDefaultAsync(u => u.UID == uid); if (otherUser == null) return; - Logger.LogInformation("User " + AuthenticatedUserId + " changed pause status with " + uid + " to " + isPaused); + _logger.LogInformation("User " + AuthenticatedUserId + " changed pause status with " + uid + " to " + isPaused); ClientPair wl = - await DbContext.ClientPairs.SingleOrDefaultAsync(w => w.User == user && w.OtherUser == otherUser); + await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.User == user && w.OtherUser == otherUser); wl.IsPaused = isPaused; - DbContext.Update(wl); - await DbContext.SaveChangesAsync(); + _dbContext.Update(wl); + await _dbContext.SaveChangesAsync(); var otherEntry = OppositeEntry(uid); await Clients.User(user.UID) - .SendAsync(UserHubAPI.OnUpdateClientPairs, new ClientPairDto() + .SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() { OtherUID = otherUser.UID, IsPaused = isPaused, @@ -270,7 +242,7 @@ await Clients.User(user.UID) }, otherUser.CharacterIdentification); if (otherEntry != null) { - await Clients.User(uid).SendAsync(UserHubAPI.OnUpdateClientPairs, new ClientPairDto() + await Clients.User(uid).SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() { OtherUID = user.UID, IsPaused = otherEntry.IsPaused, @@ -281,23 +253,23 @@ await Clients.User(user.UID) } [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)] - [HubMethodName(UserHubAPI.SendPairedClientRemoval)] + [HubMethodName(Api.SendUserPairedClientRemoval)] public async Task SendPairedClientRemoval(string uid) { 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); + var otherUser = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == uid); if (otherUser == null) return; - Logger.LogInformation("User " + AuthenticatedUserId + " removed " + uid + " from whitelist"); + _logger.LogInformation("User " + AuthenticatedUserId + " removed " + uid + " from whitelist"); ClientPair wl = - await DbContext.ClientPairs.SingleOrDefaultAsync(w => w.User == sender && w.OtherUser == otherUser); + await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.User == sender && w.OtherUser == otherUser); if (wl == null) return; - DbContext.ClientPairs.Remove(wl); - await DbContext.SaveChangesAsync(); + _dbContext.ClientPairs.Remove(wl); + await _dbContext.SaveChangesAsync(); var otherEntry = OppositeEntry(uid); await Clients.User(sender.UID) - .SendAsync(UserHubAPI.OnUpdateClientPairs, new ClientPairDto() + .SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() { OtherUID = otherUser.UID, IsRemoved = true @@ -307,10 +279,10 @@ await Clients.User(sender.UID) if (!string.IsNullOrEmpty(otherUser.CharacterIdentification)) { await Clients.User(sender.UID) - .SendAsync(UserHubAPI.OnRemoveOnlinePairedPlayer, otherUser.CharacterIdentification); + .SendAsync(Api.OnUserRemoveOnlinePairedPlayer, otherUser.CharacterIdentification); await Clients.User(otherUser.UID) - .SendAsync(UserHubAPI.OnRemoveOnlinePairedPlayer, sender.CharacterIdentification); - await Clients.User(otherUser.UID).SendAsync(UserHubAPI.OnUpdateClientPairs, new ClientPairDto() + .SendAsync(Api.OnUserRemoveOnlinePairedPlayer, sender.CharacterIdentification); + await Clients.User(otherUser.UID).SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() { OtherUID = sender.UID, IsPaused = otherEntry.IsPaused, @@ -322,6 +294,6 @@ await Clients.User(otherUser.UID) } private ClientPair OppositeEntry(string otherUID) => - DbContext.ClientPairs.AsNoTracking().SingleOrDefault(w => w.User.UID == otherUID && w.OtherUser.UID == AuthenticatedUserId); + _dbContext.ClientPairs.AsNoTracking().SingleOrDefault(w => w.User.UID == otherUID && w.OtherUser.UID == AuthenticatedUserId); } } diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs new file mode 100644 index 00000000..7869787a --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs @@ -0,0 +1,125 @@ +using System; +using System.Linq; +using System.Security.Claims; +using System.Security.Cryptography; +using System.Threading.Tasks; +using MareSynchronos.API; +using MareSynchronosServer.Data; +using Microsoft.AspNetCore.SignalR; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace MareSynchronosServer.Hubs +{ + public partial class MareHub : Hub + { + private readonly SystemInfoService _systemInfoService; + private readonly IConfiguration _configuration; + private readonly ILogger _logger; + private readonly MareDbContext _dbContext; + + public MareHub(MareDbContext mareDbContext, ILogger logger, SystemInfoService systemInfoService, IConfiguration configuration) + { + _systemInfoService = systemInfoService; + _configuration = configuration; + _logger = logger; + _dbContext = mareDbContext; + } + + [HubMethodName(Api.InvokeHeartbeat)] + public async Task Heartbeat(string? characterIdentification) + { + var userId = Context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; + + _logger.LogInformation("Connection from " + userId + ", CI: " + characterIdentification); + + await Clients.Caller.SendAsync(Api.OnUpdateSystemInfo, _systemInfoService.SystemInfoDto); + + var isBanned = await _dbContext.BannedUsers.AsNoTracking().AnyAsync(u => u.CharacterIdentification == characterIdentification); + + if (userId != null && !isBanned && !string.IsNullOrEmpty(characterIdentification)) + { + _logger.LogInformation("Connection from " + userId); + var user = (await _dbContext.Users.SingleAsync(u => u.UID == userId)); + user.CharacterIdentification = characterIdentification; + await _dbContext.SaveChangesAsync(); + return new ConnectionDto + { + ServerVersion = Api.Version, + UID = userId, + IsModerator = user.IsModerator, + IsAdmin = user.IsAdmin + }; + } + + return new ConnectionDto() + { + ServerVersion = Api.Version + }; + } + + [HubMethodName(Api.InvokeGetSystemInfo)] + public async Task GetSystemInfo() + { + return _systemInfoService.SystemInfoDto; + } + + public override async Task OnDisconnectedAsync(Exception exception) + { + var user = await _dbContext.Users.AsNoTracking().SingleOrDefaultAsync(u => u.UID == AuthenticatedUserId); + if (user != null && !string.IsNullOrEmpty(user.CharacterIdentification)) + { + _logger.LogInformation("Disconnect from " + AuthenticatedUserId); + + var otherUsers = await _dbContext.ClientPairs.AsNoTracking() + .Include(u => u.User) + .Include(u => u.OtherUser) + .Where(w => w.User.UID == user.UID && !w.IsPaused) + .Where(w => !string.IsNullOrEmpty(w.OtherUser.CharacterIdentification)) + .Select(e => e.OtherUser).ToListAsync(); + var otherEntries = await _dbContext.ClientPairs.AsNoTracking().Include(u => u.User) + .Where(u => otherUsers.Any(e => e == u.User) && u.OtherUser.UID == user.UID && !u.IsPaused).ToListAsync(); + await Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync(Api.OnUserRemoveOnlinePairedPlayer, user.CharacterIdentification); + + var notUploadedFiles = _dbContext.Files.Where(f => !f.Uploaded && f.Uploader.UID == user.UID).ToList(); + _dbContext.RemoveRange(notUploadedFiles); + + (await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId)).CharacterIdentification = null; + await _dbContext.SaveChangesAsync(); + + await Clients.All.SendAsync("UsersOnline", + await _dbContext.Users.CountAsync(u => !string.IsNullOrEmpty(u.CharacterIdentification))); + } + + await base.OnDisconnectedAsync(exception); + } + + public static string GenerateRandomString(int length, string allowableChars = null) + { + if (string.IsNullOrEmpty(allowableChars)) + allowableChars = @"ABCDEFGHJKLMNPQRSTUVWXYZ0123456789"; + + // Generate random data + var rnd = new byte[length]; + using (var rng = new RNGCryptoServiceProvider()) + rng.GetBytes(rnd); + + // 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() + { + return await _dbContext.Users.AsNoTrackingWithIdentityResolution().SingleAsync(u => u.UID == AuthenticatedUserId); + } + } +} diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220712102941_ClientPairKey.cs b/MareSynchronosServer/MareSynchronosServer/Migrations/20220712102941_ClientPairKey.cs index a8dd7e28..f812b375 100644 --- a/MareSynchronosServer/MareSynchronosServer/Migrations/20220712102941_ClientPairKey.cs +++ b/MareSynchronosServer/MareSynchronosServer/Migrations/20220712102941_ClientPairKey.cs @@ -24,7 +24,6 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "Id", table: "ClientPairs"); - migrationBuilder.AlterColumn( name: "UserUID", table: "ClientPairs", diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220713083057_MoveAuthToSeparateTable.Designer.cs b/MareSynchronosServer/MareSynchronosServer/Migrations/20220713083057_MoveAuthToSeparateTable.Designer.cs new file mode 100644 index 00000000..b17fa3bb --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServer/Migrations/20220713083057_MoveAuthToSeparateTable.Designer.cs @@ -0,0 +1,197 @@ +// +using System; +using MareSynchronosServer.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace MareSynchronosServer.Migrations +{ + [DbContext(typeof(MareDbContext))] + [Migration("20220713083057_MoveAuthToSeparateTable")] + partial class MoveAuthToSeparateTable + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("MareSynchronosServer.Models.Auth", b => + { + b.Property("HashedKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UserUID") + .HasColumnType("nvarchar(10)"); + + b.HasKey("HashedKey"); + + b.HasIndex("UserUID"); + + b.ToTable("Auth", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.Banned", b => + { + b.Property("CharacterIdentification") + .HasColumnType("nvarchar(450)"); + + b.Property("Reason") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("CharacterIdentification"); + + b.ToTable("BannedUsers", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.ClientPair", b => + { + b.Property("UserUID") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("OtherUserUID") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("AllowReceivingMessages") + .HasColumnType("bit"); + + b.Property("IsPaused") + .HasColumnType("bit"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("UserUID", "OtherUserUID"); + + b.HasIndex("OtherUserUID"); + + b.HasIndex("UserUID"); + + b.ToTable("ClientPairs", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.FileCache", b => + { + b.Property("Hash") + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.Property("Uploaded") + .HasColumnType("bit"); + + b.Property("UploaderUID") + .HasColumnType("nvarchar(10)"); + + b.HasKey("Hash"); + + b.HasIndex("UploaderUID"); + + b.ToTable("FileCaches", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.ForbiddenUploadEntry", b => + { + b.Property("Hash") + .HasColumnType("nvarchar(450)"); + + b.Property("ForbiddenBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("Hash"); + + b.ToTable("ForbiddenUploadEntries", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.User", b => + { + b.Property("UID") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("CharacterIdentification") + .HasColumnType("nvarchar(max)"); + + b.Property("IsAdmin") + .HasColumnType("bit"); + + b.Property("IsModerator") + .HasColumnType("bit"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("UID"); + + b.ToTable("Users", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.Auth", b => + { + b.HasOne("MareSynchronosServer.Models.User", "User") + .WithMany() + .HasForeignKey("UserUID"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.ClientPair", b => + { + b.HasOne("MareSynchronosServer.Models.User", "OtherUser") + .WithMany() + .HasForeignKey("OtherUserUID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MareSynchronosServer.Models.User", "User") + .WithMany() + .HasForeignKey("UserUID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OtherUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.FileCache", b => + { + b.HasOne("MareSynchronosServer.Models.User", "Uploader") + .WithMany() + .HasForeignKey("UploaderUID"); + + b.Navigation("Uploader"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220713083057_MoveAuthToSeparateTable.cs b/MareSynchronosServer/MareSynchronosServer/Migrations/20220713083057_MoveAuthToSeparateTable.cs new file mode 100644 index 00000000..10e52a3a --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServer/Migrations/20220713083057_MoveAuthToSeparateTable.cs @@ -0,0 +1,53 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MareSynchronosServer.Migrations +{ + public partial class MoveAuthToSeparateTable : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Auth", + columns: table => new + { + HashedKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + UserUID = table.Column(type: "nvarchar(10)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Auth", x => x.HashedKey); + table.ForeignKey( + name: "FK_Auth_Users_UserUID", + column: x => x.UserUID, + principalTable: "Users", + principalColumn: "UID"); + }); + + migrationBuilder.Sql("INSERT INTO Auth SELECT SecretKey, UID FROM Users"); + + migrationBuilder.DropColumn( + name: "SecretKey", + table: "Users"); + + migrationBuilder.CreateIndex( + name: "IX_Auth_UserUID", + table: "Auth", + column: "UserUID"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Auth"); + + migrationBuilder.AddColumn( + name: "SecretKey", + table: "Users", + type: "nvarchar(max)", + nullable: true); + + } + } +} diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220713104127_AddIndexes.Designer.cs b/MareSynchronosServer/MareSynchronosServer/Migrations/20220713104127_AddIndexes.Designer.cs new file mode 100644 index 00000000..ca4953f8 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServer/Migrations/20220713104127_AddIndexes.Designer.cs @@ -0,0 +1,200 @@ +// +using System; +using MareSynchronosServer.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace MareSynchronosServer.Migrations +{ + [DbContext(typeof(MareDbContext))] + [Migration("20220713104127_AddIndexes")] + partial class AddIndexes + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("MareSynchronosServer.Models.Auth", b => + { + b.Property("HashedKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UserUID") + .HasColumnType("nvarchar(10)"); + + b.HasKey("HashedKey"); + + b.HasIndex("UserUID"); + + b.ToTable("Auth", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.Banned", b => + { + b.Property("CharacterIdentification") + .HasColumnType("nvarchar(450)"); + + b.Property("Reason") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("CharacterIdentification"); + + b.ToTable("BannedUsers", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.ClientPair", b => + { + b.Property("UserUID") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("OtherUserUID") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("AllowReceivingMessages") + .HasColumnType("bit"); + + b.Property("IsPaused") + .HasColumnType("bit"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("UserUID", "OtherUserUID"); + + b.HasIndex("OtherUserUID"); + + b.HasIndex("UserUID"); + + b.ToTable("ClientPairs", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.FileCache", b => + { + b.Property("Hash") + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.Property("Uploaded") + .HasColumnType("bit"); + + b.Property("UploaderUID") + .HasColumnType("nvarchar(10)"); + + b.HasKey("Hash"); + + b.HasIndex("UploaderUID"); + + b.ToTable("FileCaches", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.ForbiddenUploadEntry", b => + { + b.Property("Hash") + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("ForbiddenBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("Hash"); + + b.ToTable("ForbiddenUploadEntries", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.User", b => + { + b.Property("UID") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("CharacterIdentification") + .HasColumnType("nvarchar(450)"); + + b.Property("IsAdmin") + .HasColumnType("bit"); + + b.Property("IsModerator") + .HasColumnType("bit"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("UID"); + + b.HasIndex("CharacterIdentification"); + + b.ToTable("Users", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.Auth", b => + { + b.HasOne("MareSynchronosServer.Models.User", "User") + .WithMany() + .HasForeignKey("UserUID"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.ClientPair", b => + { + b.HasOne("MareSynchronosServer.Models.User", "OtherUser") + .WithMany() + .HasForeignKey("OtherUserUID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MareSynchronosServer.Models.User", "User") + .WithMany() + .HasForeignKey("UserUID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OtherUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.FileCache", b => + { + b.HasOne("MareSynchronosServer.Models.User", "Uploader") + .WithMany() + .HasForeignKey("UploaderUID"); + + b.Navigation("Uploader"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220713104127_AddIndexes.cs b/MareSynchronosServer/MareSynchronosServer/Migrations/20220713104127_AddIndexes.cs new file mode 100644 index 00000000..82dc7425 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServer/Migrations/20220713104127_AddIndexes.cs @@ -0,0 +1,72 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MareSynchronosServer.Migrations +{ + public partial class AddIndexes : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "CharacterIdentification", + table: "Users", + type: "nvarchar(450)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.DropPrimaryKey( + name: "PK_ForbiddenUploadEntries", + table: "ForbiddenUploadEntries"); + + migrationBuilder.AlterColumn( + name: "Hash", + table: "ForbiddenUploadEntries", + type: "nvarchar(40)", + maxLength: 40, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(450)"); + + migrationBuilder.AddPrimaryKey( + name: "PK_ForbiddenUploadEntries", + table: "ForbiddenUploadEntries", + column: "Hash"); + + migrationBuilder.CreateIndex( + name: "IX_Users_CharacterIdentification", + table: "Users", + column: "CharacterIdentification"); + + migrationBuilder.Sql("ALTER DATABASE CURRENT SET ALLOW_SNAPSHOT_ISOLATION ON", true); + migrationBuilder.Sql("ALTER DATABASE CURRENT SET READ_COMMITTED_SNAPSHOT ON", true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Users_CharacterIdentification", + table: "Users"); + + migrationBuilder.AlterColumn( + name: "CharacterIdentification", + table: "Users", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(450)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Hash", + table: "ForbiddenUploadEntries", + type: "nvarchar(450)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + } + } +} diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/MareDbContextModelSnapshot.cs b/MareSynchronosServer/MareSynchronosServer/Migrations/MareDbContextModelSnapshot.cs index 5c797388..ae54deda 100644 --- a/MareSynchronosServer/MareSynchronosServer/Migrations/MareDbContextModelSnapshot.cs +++ b/MareSynchronosServer/MareSynchronosServer/Migrations/MareDbContextModelSnapshot.cs @@ -22,6 +22,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + modelBuilder.Entity("MareSynchronosServer.Models.Auth", b => + { + b.Property("HashedKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UserUID") + .HasColumnType("nvarchar(10)"); + + b.HasKey("HashedKey"); + + b.HasIndex("UserUID"); + + b.ToTable("Auth", (string)null); + }); + modelBuilder.Entity("MareSynchronosServer.Models.Banned", b => { b.Property("CharacterIdentification") @@ -97,7 +113,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("MareSynchronosServer.Models.ForbiddenUploadEntry", b => { b.Property("Hash") - .HasColumnType("nvarchar(450)"); + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); b.Property("ForbiddenBy") .HasColumnType("nvarchar(max)"); @@ -119,7 +136,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(10)"); b.Property("CharacterIdentification") - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(450)"); b.Property("IsAdmin") .HasColumnType("bit"); @@ -127,9 +144,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("IsModerator") .HasColumnType("bit"); - b.Property("SecretKey") - .HasColumnType("nvarchar(max)"); - b.Property("Timestamp") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -137,21 +151,32 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("UID"); + b.HasIndex("CharacterIdentification"); + b.ToTable("Users", (string)null); }); + modelBuilder.Entity("MareSynchronosServer.Models.Auth", b => + { + b.HasOne("MareSynchronosServer.Models.User", "User") + .WithMany() + .HasForeignKey("UserUID"); + + b.Navigation("User"); + }); + modelBuilder.Entity("MareSynchronosServer.Models.ClientPair", b => { b.HasOne("MareSynchronosServer.Models.User", "OtherUser") .WithMany() .HasForeignKey("OtherUserUID") - .OnDelete(DeleteBehavior.NoAction) + .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.HasOne("MareSynchronosServer.Models.User", "User") .WithMany() .HasForeignKey("UserUID") - .OnDelete(DeleteBehavior.NoAction) + .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.Navigation("OtherUser"); diff --git a/MareSynchronosServer/MareSynchronosServer/Models/Auth.cs b/MareSynchronosServer/MareSynchronosServer/Models/Auth.cs new file mode 100644 index 00000000..f154a73f --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServer/Models/Auth.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace MareSynchronosServer.Models +{ + public class Auth + { + [Key] + [MaxLength(64)] + public string HashedKey { get; set; } + + public string UserUID { get; set; } + public User User { get; set; } + } +} diff --git a/MareSynchronosServer/MareSynchronosServer/Models/FileCache.cs b/MareSynchronosServer/MareSynchronosServer/Models/FileCache.cs index b5a5e224..da48560f 100644 --- a/MareSynchronosServer/MareSynchronosServer/Models/FileCache.cs +++ b/MareSynchronosServer/MareSynchronosServer/Models/FileCache.cs @@ -1,6 +1,4 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; namespace MareSynchronosServer.Models { @@ -9,6 +7,7 @@ public class FileCache [Key] [MaxLength(40)] public string Hash { get; set; } + public string UploaderUID { get; set; } public User Uploader { get; set; } public bool Uploaded { get; set; } [Timestamp] diff --git a/MareSynchronosServer/MareSynchronosServer/Models/ForbiddenUploadEntry.cs b/MareSynchronosServer/MareSynchronosServer/Models/ForbiddenUploadEntry.cs index c5cf461f..bc0c34df 100644 --- a/MareSynchronosServer/MareSynchronosServer/Models/ForbiddenUploadEntry.cs +++ b/MareSynchronosServer/MareSynchronosServer/Models/ForbiddenUploadEntry.cs @@ -5,6 +5,7 @@ namespace MareSynchronosServer.Models public class ForbiddenUploadEntry { [Key] + [MaxLength(40)] public string Hash { get; set; } public string ForbiddenBy { get; set; } [Timestamp] diff --git a/MareSynchronosServer/MareSynchronosServer/Models/User.cs b/MareSynchronosServer/MareSynchronosServer/Models/User.cs index 5bc9a542..74308f71 100644 --- a/MareSynchronosServer/MareSynchronosServer/Models/User.cs +++ b/MareSynchronosServer/MareSynchronosServer/Models/User.cs @@ -1,5 +1,4 @@ -using System; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; namespace MareSynchronosServer.Models { @@ -8,7 +7,6 @@ public class User [Key] [MaxLength(10)] public string UID { get; set; } - public string SecretKey { get; set; } public string CharacterIdentification { get; set; } [Timestamp] public byte[] Timestamp { get; set; } diff --git a/MareSynchronosServer/MareSynchronosServer/Startup.cs b/MareSynchronosServer/MareSynchronosServer/Startup.cs index 1522baf5..5d1b949b 100644 --- a/MareSynchronosServer/MareSynchronosServer/Startup.cs +++ b/MareSynchronosServer/MareSynchronosServer/Startup.cs @@ -90,19 +90,10 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseEndpoints(endpoints => { - endpoints.MapHub(ConnectionHubAPI.Path, options => + endpoints.MapHub(Api.Path, options => { - options.Transports = HttpTransportType.WebSockets; - }); - endpoints.MapHub(UserHubAPI.Path, options => - { - options.Transports = HttpTransportType.WebSockets; - }); - endpoints.MapHub(AdminHubAPI.Path, options => options.Transports = HttpTransportType.WebSockets); - endpoints.MapHub(FilesHubAPI.Path, options => - { - options.ApplicationMaxBufferSize = long.MaxValue; - options.TransportMaxBufferSize = long.MaxValue; + options.ApplicationMaxBufferSize = 5242880; + options.TransportMaxBufferSize = 5242880; options.Transports = HttpTransportType.WebSockets; }); }); diff --git a/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs b/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs index ff11402e..9c8a1bd5 100644 --- a/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs +++ b/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs @@ -21,12 +21,12 @@ public class SystemInfoService : IHostedService, IDisposable private readonly ILogger _logger; private readonly IServiceProvider _services; private readonly IConfiguration _configuration; - private readonly IHubContext _hubContext; + private readonly IHubContext _hubContext; private Timer _timer; public SystemInfoDto SystemInfoDto { get; private set; } = new(); public SystemInfoService(ILogger logger, IServiceProvider services, - IConfiguration configuration, IHubContext hubContext) + IConfiguration configuration, IHubContext hubContext) { _logger = logger; _services = services; @@ -114,7 +114,7 @@ private void CalculateCpuUsage(object state) UploadedFiles = uploadedFiles }; - _hubContext.Clients.All.SendAsync(ConnectionHubAPI.OnUpdateSystemInfo, SystemInfoDto); + _hubContext.Clients.All.SendAsync(Api.OnUpdateSystemInfo, SystemInfoDto); _logger.LogInformation($"CPU:{cpuUsage:0.00}%, RAM Used:{(double)usedRAM / 1024 / 1024 / 1024:0.00}GB, Cache:{(double)localCacheSize / 1024 / 1024 / 1024:0.00}GB, Users:{loggedInUsers}, NetworkIn:{totalNetworkIn / 1024 / 1024:0.00}MB/s, NetworkOut:{totalNetworkOut / 1024 / 1024:0.00}MB/s"); }