Files
ClubPenguinServer/MareSynchronosServer/MareSynchronosServer/Hubs/User.cs

343 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
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;
using Microsoft.EntityFrameworkCore;
namespace MareSynchronosServer.Hubs
{
public class User : BaseHub
{
public User(MareDbContext dbContext) : base(dbContext)
{
}
public async Task<string> 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 hasValidUid = false;
while (!hasValidUid)
{
var uid = GenerateRandomString(10);
if (DbContext.Users.Any(u => u.UID == uid)) continue;
user.UID = uid;
hasValidUid = true;
}
DbContext.Users.Add(user);
await DbContext.SaveChangesAsync();
return computedHash;
}
private Whitelist OppositeEntry(string otherUID) =>
DbContext.Whitelists.SingleOrDefault(w => w.User.UID == otherUID && w.OtherUser.UID == AuthenticatedUserId);
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
public string GetUID()
{
return AuthenticatedUserId;
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
public async Task SendVisibilityList(List<string> currentVisibilityList)
{
Stopwatch st = Stopwatch.StartNew();
var cid = DbContext.Users.Single(u => u.UID == AuthenticatedUserId).CharacterIdentification;
var visibilities = DbContext.Visibilities.Where(v => v.CID == cid).ToList();
foreach (var visibility in currentVisibilityList.Where(visibility => visibilities.All(v => v.OtherCID != visibility)))
{
await DbContext.Visibilities.AddAsync(new Visibility { CID = cid, OtherCID = visibility });
}
foreach (var visibility in visibilities.Where(v => currentVisibilityList.Contains(v.OtherCID)))
{
DbContext.Visibilities.Remove(visibility);
}
await DbContext.SaveChangesAsync();
st.Stop();
Debug.WriteLine("Visibility update took " + st.Elapsed);
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
public async Task GetCharacterData(Dictionary<string, int> visibleCharacterWithJobs)
{
var uid = AuthenticatedUserId;
Dictionary<string, CharacterCacheDto> ret = new();
var whitelistEntriesHavingThisUser = DbContext.Whitelists
.Include(w => w.User)
.Include(w => w.OtherUser)
.Where(w => w.OtherUser.UID == uid && !w.IsPaused && visibleCharacterWithJobs.Keys.Contains(w.User.CharacterIdentification))
.ToList();
foreach (var whiteListEntry in whitelistEntriesHavingThisUser)
{
bool isNotPaused = await DbContext.Whitelists.AnyAsync(w =>
!w.IsPaused && w.User.UID == uid && w.OtherUser.UID == whiteListEntry.User.UID);
if (!isNotPaused) continue;
var dictEntry = visibleCharacterWithJobs[whiteListEntry.User.CharacterIdentification];
var cachedChar = await
DbContext.CharacterData.SingleOrDefaultAsync(c => c.UserId == whiteListEntry.User.UID && c.JobId == dictEntry);
if (cachedChar != null)
{
await Clients.User(uid).SendAsync("ReceiveCharacterData", new CharacterCacheDto()
{
FileReplacements = cachedChar.EquipmentData,
Hash = cachedChar.Hash,
JobId = cachedChar.JobId,
GlamourerData = cachedChar.GlamourerData
},
whiteListEntry.User.CharacterIdentification);
}
}
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
public async Task PushCharacterData(CharacterCacheDto characterCache, List<string> visibleCharacterIds)
{
var uid = AuthenticatedUserId;
var whitelistEntriesHavingThisUser = DbContext.Whitelists
.Include(w => w.User)
.Include(w => w.OtherUser)
.Where(w => w.OtherUser.UID == uid && !w.IsPaused
&& visibleCharacterIds.Contains(w.User.CharacterIdentification)).ToList();
var existingCharacterData =
await DbContext.CharacterData.SingleOrDefaultAsync(s =>
s.UserId == uid && s.JobId == characterCache.JobId);
if (existingCharacterData != null && existingCharacterData.Hash != characterCache.Hash)
{
existingCharacterData.EquipmentData =
characterCache.FileReplacements;
existingCharacterData.Hash = characterCache.Hash;
existingCharacterData.GlamourerData = characterCache.GlamourerData;
DbContext.CharacterData.Update(existingCharacterData);
}
else if (existingCharacterData == null)
{
CharacterData data = new CharacterData
{
UserId = AuthenticatedUserId,
EquipmentData = characterCache.FileReplacements,
Hash = characterCache.Hash,
GlamourerData = characterCache.GlamourerData,
JobId = characterCache.JobId
};
await DbContext.CharacterData.AddAsync(data);
await DbContext.SaveChangesAsync();
}
if ((existingCharacterData != null && existingCharacterData.Hash != characterCache.Hash) || existingCharacterData == null)
{
foreach (var whitelistEntry in whitelistEntriesHavingThisUser)
{
var ownEntry = DbContext.Whitelists.SingleOrDefault(w =>
w.User.UID == uid && w.OtherUser.UID == whitelistEntry.User.UID);
if (ownEntry == null || ownEntry.IsPaused) continue;
await Clients.User(whitelistEntry.User.UID).SendAsync("ReceiveCharacterData", characterCache,
whitelistEntry.OtherUser.CharacterIdentification);
}
}
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
public async Task<List<string>> SendCharacterNameHash(string characterNameHash)
{
var ownUser = DbContext.Users.Single(u => u.UID == AuthenticatedUserId);
ownUser.CharacterIdentification = characterNameHash;
await DbContext.SaveChangesAsync();
var otherUsers = await DbContext.Whitelists
.Include(u => u.User)
.Include(u => u.OtherUser)
.Where(w => w.User == ownUser)
.Where(w => !string.IsNullOrEmpty(w.OtherUser.CharacterIdentification))
.Select(e => e.OtherUser).ToListAsync();
var otherEntries = await DbContext.Whitelists.Include(u => u.User)
.Where(u => otherUsers.Any(e => e == u.User) && u.OtherUser == ownUser).ToListAsync();
await Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync("AddOnlineWhitelistedPlayer", characterNameHash);
return otherEntries.Select(e => e.User.CharacterIdentification).ToList();
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
public async Task SendWhitelistAddition(string uid)
{
var user = await DbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId);
var otherUser = await DbContext.Users.SingleOrDefaultAsync(u => u.UID == uid);
if (otherUser == null) return;
Whitelist wl = new Whitelist()
{
IsPaused = false,
OtherUser = otherUser,
User = user
};
await DbContext.Whitelists.AddAsync(wl);
await DbContext.SaveChangesAsync();
var otherEntry = OppositeEntry(uid);
await Clients.User(user.UID)
.SendAsync("UpdateWhitelist", new WhitelistDto()
{
OtherUID = otherUser.UID,
IsPaused = false,
IsPausedFromOthers = otherEntry?.IsPaused ?? false,
IsSynced = otherEntry != null
}, otherUser.CharacterIdentification);
if (otherEntry != null)
{
if (string.IsNullOrEmpty(otherUser.CharacterIdentification))
{
await Clients.User(user.UID)
.SendAsync("AddOnlineWhitelistedPlayer", otherUser.CharacterIdentification);
await Clients.User(otherUser.UID)
.SendAsync("AddOnlineWhitelistedPlayer", user.CharacterIdentification);
}
await Clients.User(uid).SendAsync("UpdateWhitelist",
new WhitelistDto()
{
OtherUID = user.UID,
IsPaused = otherEntry.IsPaused,
IsPausedFromOthers = false,
IsSynced = true
}, user.CharacterIdentification);
}
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
public async Task SendWhitelistRemoval(string uid)
{
var user = await DbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId);
var otherUser = await DbContext.Users.SingleOrDefaultAsync(u => u.UID == uid);
if (otherUser == null) return;
Whitelist wl =
await DbContext.Whitelists.SingleOrDefaultAsync(w => w.User == user && w.OtherUser == otherUser);
DbContext.Whitelists.Remove(wl);
await DbContext.SaveChangesAsync();
var otherEntry = OppositeEntry(uid);
await Clients.User(user.UID)
.SendAsync("UpdateWhitelist", new WhitelistDto()
{
OtherUID = otherUser.UID,
IsPaused = false,
IsPausedFromOthers = otherEntry?.IsPaused ?? false,
IsSynced = otherEntry != null
}, otherUser.CharacterIdentification);
if (otherEntry != null)
{
if (string.IsNullOrEmpty(otherUser.CharacterIdentification))
{
await Clients.User(user.UID)
.SendAsync("RemoveOnlineWhitelistedPlayer", otherUser.CharacterIdentification);
await Clients.User(otherUser.UID)
.SendAsync("RemoveOnlineWhitelistedPlayer", user.CharacterIdentification);
}
await Clients.User(uid).SendAsync("UpdateWhitelist", new WhitelistDto()
{
OtherUID = user.UID,
IsPaused = otherEntry.IsPaused,
IsPausedFromOthers = false,
IsSynced = false
}, user.CharacterIdentification);
}
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
public async Task SendWhitelistPauseChange(string uid, bool isPaused)
{
var user = DbContext.Users.Single(u => u.UID == AuthenticatedUserId);
var otherUser = await DbContext.Users.SingleOrDefaultAsync(u => u.UID == uid);
if (otherUser == null) return;
Whitelist wl =
await DbContext.Whitelists.SingleOrDefaultAsync(w => w.User == user && w.OtherUser == otherUser);
wl.IsPaused = isPaused;
DbContext.Update(wl);
await DbContext.SaveChangesAsync();
var otherEntry = OppositeEntry(uid);
await Clients.User(user.UID)
.SendAsync("UpdateWhitelist", new WhitelistDto()
{
OtherUID = otherUser.UID,
IsPaused = isPaused,
IsPausedFromOthers = otherEntry?.IsPaused ?? false,
IsSynced = otherEntry != null
}, otherUser.CharacterIdentification);
if (otherEntry != null)
{
await Clients.User(uid).SendAsync("UpdateWhitelist", new WhitelistDto()
{
OtherUID = user.UID,
IsPaused = otherEntry.IsPaused,
IsPausedFromOthers = isPaused,
IsSynced = true
}, user.CharacterIdentification);
}
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
public async Task<List<WhitelistDto>> GetWhitelist()
{
string userid = AuthenticatedUserId;
var user = GetAuthenticatedUser();
return DbContext.Whitelists
.Include(u => u.OtherUser)
.Include(u => u.User)
.Where(w => w.User.UID == userid)
.ToList()
.Select(w =>
{
var otherEntry = OppositeEntry(w.OtherUser.UID);
return new WhitelistDto
{
IsPaused = w.IsPaused,
OtherUID = w.OtherUser.UID,
IsSynced = otherEntry != null,
IsPausedFromOthers = otherEntry?.IsPaused ?? false,
};
}).ToList();
}
public override Task OnDisconnectedAsync(Exception exception)
{
var user = DbContext.Users.SingleOrDefault(u => u.UID == AuthenticatedUserId);
if (user != null)
{
var otherUsers = DbContext.Whitelists
.Include(u => u.User)
.Include(u => u.OtherUser)
.Where(w => w.User == user)
.Where(w => !string.IsNullOrEmpty(w.OtherUser.CharacterIdentification))
.Select(e => e.OtherUser).ToList();
var otherEntries = DbContext.Whitelists.Include(u => u.User)
.Where(u => otherUsers.Any(e => e == u.User) && u.OtherUser == user).ToList();
_ = Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync("RemoveOnlineWhitelistedPlayer", user.CharacterIdentification);
var outdatedVisibilities = DbContext.Visibilities.Where(v => v.CID == user.CharacterIdentification);
DbContext.RemoveRange(outdatedVisibilities);
var outdatedCharacterData = DbContext.CharacterData.Where(v => v.UserId == user.UID);
DbContext.RemoveRange(outdatedCharacterData);
user.CharacterIdentification = null;
DbContext.SaveChanges();
}
return base.OnDisconnectedAsync(exception);
}
}
}