Refactoring using Claims more, add Server Side Messaging (#20)

* add some refactoring based on claims, handle chara ident inside claim, fix discord userid in log

* improve authentication responses, add server side messaging

* update server to mainline api

Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
rootdarkarchon
2023-01-04 15:49:18 +01:00
committed by GitHub
parent 5f0c12ecfa
commit 74b7fcdf89
25 changed files with 350 additions and 204 deletions

Submodule MareAPI updated: 6645eaf63f...d361cfa3b9

View File

@@ -0,0 +1,3 @@
namespace MareSynchronosServer.Authentication;
public record SecretKeyAuthReply(bool Success, string Uid, bool TempBan);

View File

@@ -4,10 +4,8 @@ using MareSynchronosShared.Metrics;
using MareSynchronosShared.Services; using MareSynchronosShared.Services;
using MareSynchronosShared.Utils; using MareSynchronosShared.Utils;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace MareSynchronosShared.Authentication; namespace MareSynchronosServer.Authentication;
public class SecretKeyAuthenticatorService public class SecretKeyAuthenticatorService
{ {
@@ -16,7 +14,7 @@ public class SecretKeyAuthenticatorService
private readonly IConfigurationService<MareConfigurationAuthBase> _configurationService; private readonly IConfigurationService<MareConfigurationAuthBase> _configurationService;
private readonly ILogger<SecretKeyAuthenticatorService> _logger; private readonly ILogger<SecretKeyAuthenticatorService> _logger;
private readonly ConcurrentDictionary<string, SecretKeyAuthReply> _cachedPositiveResponses = new(StringComparer.Ordinal); private readonly ConcurrentDictionary<string, SecretKeyAuthReply> _cachedPositiveResponses = new(StringComparer.Ordinal);
private readonly ConcurrentDictionary<string, SecretKeyFailedAuthorization?> _failedAuthorizations = new(StringComparer.Ordinal); private readonly ConcurrentDictionary<string, SecretKeyFailedAuthorization> _failedAuthorizations = new(StringComparer.Ordinal);
public SecretKeyAuthenticatorService(MareMetrics metrics, IServiceScopeFactory serviceScopeFactory, IConfigurationService<MareConfigurationAuthBase> configuration, ILogger<SecretKeyAuthenticatorService> logger) public SecretKeyAuthenticatorService(MareMetrics metrics, IServiceScopeFactory serviceScopeFactory, IConfigurationService<MareConfigurationAuthBase> configuration, ILogger<SecretKeyAuthenticatorService> logger)
{ {
@@ -52,14 +50,14 @@ public class SecretKeyAuthenticatorService
_failedAuthorizations.Remove(ip, out _); _failedAuthorizations.Remove(ip, out _);
}); });
} }
return new(Success: false, Uid: null); return new(Success: false, Uid: null, TempBan: true);
} }
using var scope = _serviceScopeFactory.CreateScope(); using var scope = _serviceScopeFactory.CreateScope();
using var context = scope.ServiceProvider.GetService<MareDbContext>(); using var context = scope.ServiceProvider.GetService<MareDbContext>();
var authReply = await context.Auth.AsNoTracking().SingleOrDefaultAsync(u => u.HashedKey == hashedSecretKey).ConfigureAwait(false); var authReply = await context.Auth.AsNoTracking().SingleOrDefaultAsync(u => u.HashedKey == hashedSecretKey).ConfigureAwait(false);
SecretKeyAuthReply reply = new(authReply != null, authReply?.UserUID); SecretKeyAuthReply reply = new(authReply != null, authReply?.UserUID, false);
if (reply.Success) if (reply.Success)
{ {
@@ -99,6 +97,6 @@ public class SecretKeyAuthenticatorService
} }
} }
return new(Success: false, Uid: null); return new(Success: false, Uid: null, TempBan: false);
} }
} }

View File

@@ -1,4 +1,4 @@
namespace MareSynchronosShared.Authentication; namespace MareSynchronosServer.Authentication;
internal record SecretKeyFailedAuthorization internal record SecretKeyFailedAuthorization
{ {

View File

@@ -1,10 +1,14 @@
using MareSynchronos.API; using MareSynchronos.API;
using MareSynchronosServer.Authentication;
using MareSynchronosServer.Hubs;
using MareSynchronosServer.Services;
using MareSynchronosShared; using MareSynchronosShared;
using MareSynchronosShared.Authentication; using MareSynchronosShared.Data;
using MareSynchronosShared.Services; using MareSynchronosShared.Services;
using MareSynchronosShared.Utils; using MareSynchronosShared.Utils;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims; using System.Security.Claims;
@@ -17,14 +21,50 @@ namespace MareSynchronosServer.Controllers;
public class JwtController : Controller public class JwtController : Controller
{ {
private readonly IHttpContextAccessor _accessor; private readonly IHttpContextAccessor _accessor;
private readonly MareDbContext _mareDbContext;
private readonly SecretKeyAuthenticatorService _secretKeyAuthenticatorService; private readonly SecretKeyAuthenticatorService _secretKeyAuthenticatorService;
private readonly IConfigurationService<MareConfigurationAuthBase> _configuration; private readonly IConfigurationService<MareConfigurationAuthBase> _configuration;
private readonly IClientIdentificationService _clientIdentService;
public JwtController(IHttpContextAccessor accessor, SecretKeyAuthenticatorService secretKeyAuthenticatorService, IConfigurationService<MareConfigurationAuthBase> configuration) public JwtController(IHttpContextAccessor accessor, MareDbContext mareDbContext,
SecretKeyAuthenticatorService secretKeyAuthenticatorService,
IConfigurationService<MareConfigurationAuthBase> configuration,
IClientIdentificationService clientIdentService)
{ {
_accessor = accessor; _accessor = accessor;
_mareDbContext = mareDbContext;
_secretKeyAuthenticatorService = secretKeyAuthenticatorService; _secretKeyAuthenticatorService = secretKeyAuthenticatorService;
_configuration = configuration; _configuration = configuration;
_clientIdentService = clientIdentService;
}
[AllowAnonymous]
[HttpPost(MareAuth.AuthCreateIdent)]
public async Task<IActionResult> CreateToken(string auth, string charaIdent)
{
if (string.IsNullOrEmpty(auth)) return BadRequest("No Authkey");
if (string.IsNullOrEmpty(charaIdent)) return BadRequest("No CharaIdent");
var isBanned = await _mareDbContext.BannedUsers.AsNoTracking().AnyAsync(u => u.CharacterIdentification == charaIdent).ConfigureAwait(false);
if (isBanned) return Unauthorized("Your character is banned from using the service.");
var ip = _accessor.GetIpAddress();
var authResult = await _secretKeyAuthenticatorService.AuthorizeAsync(ip, auth);
if (!authResult.Success && !authResult.TempBan) return Unauthorized("The provided secret key is invalid. Verify your accounts existence and/or recover the secret key.");
if (!authResult.Success && authResult.TempBan) return Unauthorized("You are temporarily banned. Try connecting again later.");
var existingIdent = _clientIdentService.GetCharacterIdentForUid(authResult.Uid);
if (!string.IsNullOrEmpty(existingIdent)) return Unauthorized("Already logged in to this account.");
var token = CreateToken(new List<Claim>()
{
new Claim(MareClaimTypes.Uid, authResult.Uid),
new Claim(MareClaimTypes.CharaIdent, charaIdent)
});
return Content(token.RawData);
} }
[AllowAnonymous] [AllowAnonymous]
@@ -41,7 +81,7 @@ public class JwtController : Controller
var token = CreateToken(new List<Claim>() var token = CreateToken(new List<Claim>()
{ {
new Claim(ClaimTypes.NameIdentifier, authResult.Uid) new Claim(MareClaimTypes.Uid, authResult.Uid)
}); });
return Content(token.RawData); return Content(token.RawData);

View File

@@ -0,0 +1,7 @@
namespace MareSynchronosServer.Hubs;
public static class MareClaimTypes
{
public const string Uid = "uid";
public const string CharaIdent = "character_identification";
}

View File

@@ -58,5 +58,10 @@ namespace MareSynchronosServer.Hubs
{ {
throw new PlatformNotSupportedException("Calling clientside method on server not supported"); throw new PlatformNotSupportedException("Calling clientside method on server not supported");
} }
public Task Client_ReceiveServerMessage(MessageSeverity messageSeverity, string message)
{
throw new PlatformNotSupportedException("Calling clientside method on server not supported");
}
} }
} }

View File

@@ -21,8 +21,7 @@ public partial class MareHub
public async Task FilesAbortUpload() public async Task FilesAbortUpload()
{ {
_logger.LogCallInfo(); _logger.LogCallInfo();
var userId = AuthenticatedUserId; var notUploadedFiles = _dbContext.Files.Where(f => !f.Uploaded && f.Uploader.UID == UserUID).ToList();
var notUploadedFiles = _dbContext.Files.Where(f => !f.Uploaded && f.Uploader.UID == userId).ToList();
_dbContext.RemoveRange(notUploadedFiles); _dbContext.RemoveRange(notUploadedFiles);
await _dbContext.SaveChangesAsync().ConfigureAwait(false); await _dbContext.SaveChangesAsync().ConfigureAwait(false);
} }
@@ -32,7 +31,7 @@ public partial class MareHub
{ {
_logger.LogCallInfo(); _logger.LogCallInfo();
var ownFiles = await _dbContext.Files.Where(f => f.Uploaded && f.Uploader.UID == AuthenticatedUserId).ToListAsync().ConfigureAwait(false); var ownFiles = await _dbContext.Files.Where(f => f.Uploaded && f.Uploader.UID == UserUID).ToListAsync().ConfigureAwait(false);
var request = new DeleteFilesRequest(); var request = new DeleteFilesRequest();
request.Hash.AddRange(ownFiles.Select(f => f.Hash)); request.Hash.AddRange(ownFiles.Select(f => f.Hash));
Metadata headers = new Metadata() Metadata headers = new Metadata()
@@ -81,9 +80,8 @@ public partial class MareHub
public async Task<bool> FilesIsUploadFinished() public async Task<bool> FilesIsUploadFinished()
{ {
_logger.LogCallInfo(); _logger.LogCallInfo();
var userUid = AuthenticatedUserId;
return await _dbContext.Files.AsNoTracking() return await _dbContext.Files.AsNoTracking()
.AnyAsync(f => f.Uploader.UID == userUid && !f.Uploaded).ConfigureAwait(false); .AnyAsync(f => f.Uploader.UID == UserUID && !f.Uploaded).ConfigureAwait(false);
} }
[Authorize(Policy = "Identified")] [Authorize(Policy = "Identified")]
@@ -94,7 +92,7 @@ public partial class MareHub
var notCoveredFiles = new Dictionary<string, UploadFileDto>(StringComparer.Ordinal); var notCoveredFiles = new Dictionary<string, UploadFileDto>(StringComparer.Ordinal);
var forbiddenFiles = await _dbContext.ForbiddenUploadEntries.AsNoTracking().Where(f => userSentHashes.Contains(f.Hash)).AsNoTracking().ToDictionaryAsync(f => f.Hash, f => f).ConfigureAwait(false); var forbiddenFiles = await _dbContext.ForbiddenUploadEntries.AsNoTracking().Where(f => userSentHashes.Contains(f.Hash)).AsNoTracking().ToDictionaryAsync(f => f.Hash, f => f).ConfigureAwait(false);
var existingFiles = await _dbContext.Files.AsNoTracking().Where(f => userSentHashes.Contains(f.Hash)).AsNoTracking().ToDictionaryAsync(f => f.Hash, f => f).ConfigureAwait(false); var existingFiles = await _dbContext.Files.AsNoTracking().Where(f => userSentHashes.Contains(f.Hash)).AsNoTracking().ToDictionaryAsync(f => f.Hash, f => f).ConfigureAwait(false);
var uploader = await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false); var uploader = await _dbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false);
List<FileCache> fileCachesToUpload = new(); List<FileCache> fileCachesToUpload = new();
foreach (var file in userSentHashes) foreach (var file in userSentHashes)
@@ -117,7 +115,6 @@ public partial class MareHub
_logger.LogCallInfo(MareHubLogger.Args(file, "Missing")); _logger.LogCallInfo(MareHubLogger.Args(file, "Missing"));
var userId = AuthenticatedUserId;
fileCachesToUpload.Add(new FileCache() fileCachesToUpload.Add(new FileCache()
{ {
Hash = file, Hash = file,
@@ -143,7 +140,7 @@ public partial class MareHub
await _uploadSemaphore.WaitAsync(Context.ConnectionAborted).ConfigureAwait(false); await _uploadSemaphore.WaitAsync(Context.ConnectionAborted).ConfigureAwait(false);
var relatedFile = _dbContext.Files.SingleOrDefault(f => f.Hash == hash && f.Uploader.UID == AuthenticatedUserId && !f.Uploaded); var relatedFile = _dbContext.Files.SingleOrDefault(f => f.Hash == hash && f.Uploader.UID == UserUID && !f.Uploaded);
if (relatedFile == null) if (relatedFile == null)
{ {
_uploadSemaphore.Release(); _uploadSemaphore.Release();
@@ -226,7 +223,7 @@ public partial class MareHub
{ {
FileData = ByteString.CopyFrom(data, 0, readBytes), FileData = ByteString.CopyFrom(data, 0, readBytes),
Hash = computedHashString, Hash = computedHashString,
Uploader = AuthenticatedUserId Uploader = UserUID
}).ConfigureAwait(false); }).ConfigureAwait(false);
} }
await streamingCall.RequestStream.CompleteAsync().ConfigureAwait(false); await streamingCall.RequestStream.CompleteAsync().ConfigureAwait(false);

View File

@@ -1,7 +1,6 @@
using MareSynchronosShared.Models; using MareSynchronosShared.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using MareSynchronosServer.Utils; using MareSynchronosServer.Utils;
using System.Security.Claims;
namespace MareSynchronosServer.Hubs; namespace MareSynchronosServer.Hubs;
@@ -9,7 +8,7 @@ public partial class MareHub
{ {
private async Task<List<PausedEntry>> GetAllPairedClientsWithPauseState(string? uid = null) private async Task<List<PausedEntry>> GetAllPairedClientsWithPauseState(string? uid = null)
{ {
uid ??= AuthenticatedUserId; uid ??= UserUID;
var query = await (from userPair in _dbContext.ClientPairs var query = await (from userPair in _dbContext.ClientPairs
join otherUserPair in _dbContext.ClientPairs on userPair.OtherUserUID equals otherUserPair.UserUID join otherUserPair in _dbContext.ClientPairs on userPair.OtherUserUID equals otherUserPair.UserUID
@@ -47,7 +46,7 @@ public partial class MareHub
private async Task<List<string>> GetAllPairedUnpausedUsers(string? uid = null) private async Task<List<string>> GetAllPairedUnpausedUsers(string? uid = null)
{ {
uid ??= AuthenticatedUserId; uid ??= UserUID;
var ret = await GetAllPairedClientsWithPauseState(uid).ConfigureAwait(false); var ret = await GetAllPairedClientsWithPauseState(uid).ConfigureAwait(false);
return ret.Where(k => !k.IsPaused).Select(k => k.UID).ToList(); return ret.Where(k => !k.IsPaused).Select(k => k.UID).ToList();
} }
@@ -68,11 +67,12 @@ public partial class MareHub
return usersToSendDataTo; return usersToSendDataTo;
} }
public string AuthenticatedUserId => Context.User?.Claims?.SingleOrDefault(c => string.Equals(c.Type, ClaimTypes.NameIdentifier, StringComparison.Ordinal))?.Value ?? "Unknown"; public string UserUID => Context.User?.Claims?.SingleOrDefault(c => string.Equals(c.Type, MareClaimTypes.Uid, StringComparison.Ordinal))?.Value ?? throw new Exception("No UID in Claims");
public string UserCharaIdent => Context.User?.Claims?.SingleOrDefault(c => string.Equals(c.Type, MareClaimTypes.CharaIdent, StringComparison.Ordinal))?.Value ?? throw new Exception("No Chara Ident in Claims");
private async Task UserGroupLeave(GroupPair groupUserPair, List<PausedEntry> allUserPairs, string userIdent, string? uid = null) private async Task UserGroupLeave(GroupPair groupUserPair, List<PausedEntry> allUserPairs, string userIdent, string? uid = null)
{ {
uid ??= AuthenticatedUserId; uid ??= UserUID;
var userPair = allUserPairs.SingleOrDefault(p => string.Equals(p.UID, groupUserPair.GroupUserUID, StringComparison.Ordinal)); var userPair = allUserPairs.SingleOrDefault(p => string.Equals(p.UID, groupUserPair.GroupUserUID, StringComparison.Ordinal));
if (userPair != null) if (userPair != null)
{ {
@@ -106,7 +106,7 @@ public partial class MareHub
private async Task<(bool IsValid, GroupPair ReferredPair)> TryValidateUserInGroup(string gid, string? uid = null) private async Task<(bool IsValid, GroupPair ReferredPair)> TryValidateUserInGroup(string gid, string? uid = null)
{ {
uid ??= AuthenticatedUserId; uid ??= UserUID;
var groupPair = await _dbContext.GroupPairs.Include(c => c.GroupUser) var groupPair = await _dbContext.GroupPairs.Include(c => c.GroupUser)
.SingleOrDefaultAsync(g => g.GroupGID == gid && (g.GroupUserUID == uid || g.GroupUser.Alias == uid)).ConfigureAwait(false); .SingleOrDefaultAsync(g => g.GroupGID == gid && (g.GroupUserUID == uid || g.GroupUser.Alias == uid)).ConfigureAwait(false);
@@ -122,7 +122,7 @@ public partial class MareHub
if (isOwnerResult.ReferredGroup == null) return (false, null); if (isOwnerResult.ReferredGroup == null) return (false, null);
var groupPairSelf = await _dbContext.GroupPairs.SingleOrDefaultAsync(g => g.GroupGID == gid && g.GroupUserUID == AuthenticatedUserId).ConfigureAwait(false); var groupPairSelf = await _dbContext.GroupPairs.SingleOrDefaultAsync(g => g.GroupGID == gid && g.GroupUserUID == UserUID).ConfigureAwait(false);
if (groupPairSelf == null || !groupPairSelf.IsModerator) return (false, null); if (groupPairSelf == null || !groupPairSelf.IsModerator) return (false, null);
return (true, isOwnerResult.ReferredGroup); return (true, isOwnerResult.ReferredGroup);
@@ -133,6 +133,6 @@ public partial class MareHub
var group = await _dbContext.Groups.SingleOrDefaultAsync(g => g.GID == gid).ConfigureAwait(false); var group = await _dbContext.Groups.SingleOrDefaultAsync(g => g.GID == gid).ConfigureAwait(false);
if (group == null) return (false, null); if (group == null) return (false, null);
return (string.Equals(group.OwnerUID, AuthenticatedUserId, StringComparison.Ordinal), group); return (string.Equals(group.OwnerUID, UserUID, StringComparison.Ordinal), group);
} }
} }

View File

@@ -15,8 +15,8 @@ public partial class MareHub
public async Task<GroupCreatedDto> GroupCreate() public async Task<GroupCreatedDto> GroupCreate()
{ {
_logger.LogCallInfo(); _logger.LogCallInfo();
var existingGroupsByUser = await _dbContext.Groups.CountAsync(u => u.OwnerUID == AuthenticatedUserId).ConfigureAwait(false); var existingGroupsByUser = await _dbContext.Groups.CountAsync(u => u.OwnerUID == UserUID).ConfigureAwait(false);
var existingJoinedGroups = await _dbContext.GroupPairs.CountAsync(u => u.GroupUserUID == AuthenticatedUserId).ConfigureAwait(false); var existingJoinedGroups = await _dbContext.GroupPairs.CountAsync(u => u.GroupUserUID == UserUID).ConfigureAwait(false);
if (existingGroupsByUser >= _maxExistingGroupsByUser || existingJoinedGroups >= _maxJoinedGroupsByUser) if (existingGroupsByUser >= _maxExistingGroupsByUser || existingJoinedGroups >= _maxJoinedGroupsByUser)
{ {
throw new System.Exception($"Max groups for user is {_maxExistingGroupsByUser}, max joined groups is {_maxJoinedGroupsByUser}."); throw new System.Exception($"Max groups for user is {_maxExistingGroupsByUser}, max joined groups is {_maxJoinedGroupsByUser}.");
@@ -38,13 +38,13 @@ public partial class MareHub
GID = gid, GID = gid,
HashedPassword = hashedPw, HashedPassword = hashedPw,
InvitesEnabled = true, InvitesEnabled = true,
OwnerUID = AuthenticatedUserId OwnerUID = UserUID
}; };
GroupPair initialPair = new() GroupPair initialPair = new()
{ {
GroupGID = newGroup.GID, GroupGID = newGroup.GID,
GroupUserUID = AuthenticatedUserId, GroupUserUID = UserUID,
IsPaused = false, IsPaused = false,
IsPinned = true IsPinned = true
}; };
@@ -53,9 +53,9 @@ public partial class MareHub
await _dbContext.GroupPairs.AddAsync(initialPair).ConfigureAwait(false); await _dbContext.GroupPairs.AddAsync(initialPair).ConfigureAwait(false);
await _dbContext.SaveChangesAsync().ConfigureAwait(false); await _dbContext.SaveChangesAsync().ConfigureAwait(false);
var self = await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false); var self = await _dbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false);
await Clients.User(AuthenticatedUserId).Client_GroupChange(new GroupDto() await Clients.User(UserUID).Client_GroupChange(new GroupDto()
{ {
GID = newGroup.GID, GID = newGroup.GID,
OwnedBy = string.IsNullOrEmpty(self.Alias) ? self.UID : self.Alias, OwnedBy = string.IsNullOrEmpty(self.Alias) ? self.UID : self.Alias,
@@ -78,7 +78,7 @@ public partial class MareHub
{ {
_logger.LogCallInfo(); _logger.LogCallInfo();
var groups = await _dbContext.GroupPairs.Include(g => g.Group).Include(g => g.Group.Owner).Where(g => g.GroupUserUID == AuthenticatedUserId).AsNoTracking().ToListAsync().ConfigureAwait(false); var groups = await _dbContext.GroupPairs.Include(g => g.Group).Include(g => g.Group.Owner).Where(g => g.GroupUserUID == UserUID).AsNoTracking().ToListAsync().ConfigureAwait(false);
return groups.Select(g => new GroupDto() return groups.Select(g => new GroupDto()
{ {
@@ -99,7 +99,7 @@ public partial class MareHub
var (inGroup, _) = await TryValidateUserInGroup(gid).ConfigureAwait(false); var (inGroup, _) = await TryValidateUserInGroup(gid).ConfigureAwait(false);
if (!inGroup) return new List<GroupPairDto>(); if (!inGroup) return new List<GroupPairDto>();
var allPairs = await _dbContext.GroupPairs.Include(g => g.GroupUser).Where(g => g.GroupGID == gid && g.GroupUserUID != AuthenticatedUserId).AsNoTracking().ToListAsync().ConfigureAwait(false); var allPairs = await _dbContext.GroupPairs.Include(g => g.GroupUser).Where(g => g.GroupGID == gid && g.GroupUserUID != UserUID).AsNoTracking().ToListAsync().ConfigureAwait(false);
return allPairs.Select(p => new GroupPairDto() return allPairs.Select(p => new GroupPairDto()
{ {
GroupGID = gid, GroupGID = gid,
@@ -160,14 +160,16 @@ public partial class MareHub
[Authorize(Policy = "Identified")] [Authorize(Policy = "Identified")]
public async Task<bool> GroupJoin(string gid, string password) public async Task<bool> GroupJoin(string gid, string password)
{ {
gid = gid.Trim();
_logger.LogCallInfo(MareHubLogger.Args(gid)); _logger.LogCallInfo(MareHubLogger.Args(gid));
var group = await _dbContext.Groups.Include(g => g.Owner).AsNoTracking().SingleOrDefaultAsync(g => g.GID == gid || g.Alias == gid).ConfigureAwait(false); var group = await _dbContext.Groups.Include(g => g.Owner).AsNoTracking().SingleOrDefaultAsync(g => g.GID == gid || g.Alias == gid).ConfigureAwait(false);
var existingPair = await _dbContext.GroupPairs.AsNoTracking().SingleOrDefaultAsync(g => g.GroupGID == gid && g.GroupUserUID == AuthenticatedUserId).ConfigureAwait(false); var existingPair = await _dbContext.GroupPairs.AsNoTracking().SingleOrDefaultAsync(g => g.GroupGID == gid && g.GroupUserUID == UserUID).ConfigureAwait(false);
var hashedPw = StringUtils.Sha256String(password); var hashedPw = StringUtils.Sha256String(password);
var existingUserCount = await _dbContext.GroupPairs.AsNoTracking().CountAsync(g => g.GroupGID == gid).ConfigureAwait(false); var existingUserCount = await _dbContext.GroupPairs.AsNoTracking().CountAsync(g => g.GroupGID == gid).ConfigureAwait(false);
var joinedGroups = await _dbContext.GroupPairs.CountAsync(g => g.GroupUserUID == AuthenticatedUserId).ConfigureAwait(false); var joinedGroups = await _dbContext.GroupPairs.CountAsync(g => g.GroupUserUID == UserUID).ConfigureAwait(false);
var isBanned = await _dbContext.GroupBans.AnyAsync(g => g.GroupGID == gid && g.BannedUserUID == AuthenticatedUserId).ConfigureAwait(false); var isBanned = await _dbContext.GroupBans.AnyAsync(g => g.GroupGID == gid && g.BannedUserUID == UserUID).ConfigureAwait(false);
var groupGid = group?.GID ?? string.Empty; var groupGid = group?.GID ?? string.Empty;
var oneTimeInvite = await _dbContext.GroupTempInvites.SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.Invite == hashedPw).ConfigureAwait(false); var oneTimeInvite = await _dbContext.GroupTempInvites.SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.Invite == hashedPw).ConfigureAwait(false);
@@ -189,7 +191,7 @@ public partial class MareHub
GroupPair newPair = new() GroupPair newPair = new()
{ {
GroupGID = group.GID, GroupGID = group.GID,
GroupUserUID = AuthenticatedUserId GroupUserUID = UserUID
}; };
await _dbContext.GroupPairs.AddAsync(newPair).ConfigureAwait(false); await _dbContext.GroupPairs.AddAsync(newPair).ConfigureAwait(false);
@@ -197,7 +199,7 @@ public partial class MareHub
_logger.LogCallInfo(MareHubLogger.Args(gid, "Success")); _logger.LogCallInfo(MareHubLogger.Args(gid, "Success"));
await Clients.User(AuthenticatedUserId).Client_GroupChange(new GroupDto() await Clients.User(UserUID).Client_GroupChange(new GroupDto()
{ {
GID = group.GID, GID = group.GID,
OwnedBy = string.IsNullOrEmpty(group.Owner.Alias) ? group.Owner.UID : group.Owner.Alias, OwnedBy = string.IsNullOrEmpty(group.Owner.Alias) ? group.Owner.UID : group.Owner.Alias,
@@ -207,15 +209,15 @@ public partial class MareHub
InvitesEnabled = true InvitesEnabled = true
}).ConfigureAwait(false); }).ConfigureAwait(false);
var self = _dbContext.Users.Single(u => u.UID == AuthenticatedUserId); var self = _dbContext.Users.Single(u => u.UID == UserUID);
var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == group.GID && p.GroupUserUID != AuthenticatedUserId).ToListAsync().ConfigureAwait(false); var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == group.GID && p.GroupUserUID != UserUID).ToListAsync().ConfigureAwait(false);
await Clients.Users(groupPairs.Select(p => p.GroupUserUID)).Client_GroupUserChange(new GroupPairDto() await Clients.Users(groupPairs.Select(p => p.GroupUserUID)).Client_GroupUserChange(new GroupPairDto()
{ {
GroupGID = group.GID, GroupGID = group.GID,
IsPaused = false, IsPaused = false,
IsRemoved = false, IsRemoved = false,
UserUID = AuthenticatedUserId, UserUID = UserUID,
UserAlias = self.Alias, UserAlias = self.Alias,
IsPinned = false, IsPinned = false,
IsModerator = false, IsModerator = false,
@@ -223,7 +225,6 @@ public partial class MareHub
var allUserPairs = await GetAllPairedClientsWithPauseState().ConfigureAwait(false); var allUserPairs = await GetAllPairedClientsWithPauseState().ConfigureAwait(false);
var userIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId);
foreach (var groupUserPair in groupPairs) foreach (var groupUserPair in groupPairs)
{ {
var userPair = allUserPairs.Single(p => string.Equals(p.UID, groupUserPair.GroupUserUID, StringComparison.Ordinal)); var userPair = allUserPairs.Single(p => string.Equals(p.UID, groupUserPair.GroupUserUID, StringComparison.Ordinal));
@@ -234,8 +235,8 @@ public partial class MareHub
var groupUserIdent = _clientIdentService.GetCharacterIdentForUid(groupUserPair.GroupUserUID); var groupUserIdent = _clientIdentService.GetCharacterIdentForUid(groupUserPair.GroupUserUID);
if (!string.IsNullOrEmpty(groupUserIdent)) if (!string.IsNullOrEmpty(groupUserIdent))
{ {
await Clients.User(AuthenticatedUserId).Client_UserChangePairedPlayer(groupUserIdent, true).ConfigureAwait(false); await Clients.User(UserUID).Client_UserChangePairedPlayer(groupUserIdent, true).ConfigureAwait(false);
await Clients.User(groupUserPair.GroupUserUID).Client_UserChangePairedPlayer(userIdent, true).ConfigureAwait(false); await Clients.User(groupUserPair.GroupUserUID).Client_UserChangePairedPlayer(UserCharaIdent, true).ConfigureAwait(false);
} }
} }
@@ -292,18 +293,18 @@ public partial class MareHub
var group = await _dbContext.Groups.SingleOrDefaultAsync(g => g.GID == gid).ConfigureAwait(false); var group = await _dbContext.Groups.SingleOrDefaultAsync(g => g.GID == gid).ConfigureAwait(false);
var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == group.GID).ToListAsync().ConfigureAwait(false); var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == group.GID).ToListAsync().ConfigureAwait(false);
var groupPairsWithoutSelf = groupPairs.Where(p => !string.Equals(p.GroupUserUID, AuthenticatedUserId, StringComparison.Ordinal)).ToList(); var groupPairsWithoutSelf = groupPairs.Where(p => !string.Equals(p.GroupUserUID, UserUID, StringComparison.Ordinal)).ToList();
_dbContext.GroupPairs.Remove(groupPair); _dbContext.GroupPairs.Remove(groupPair);
await _dbContext.SaveChangesAsync().ConfigureAwait(false); await _dbContext.SaveChangesAsync().ConfigureAwait(false);
await Clients.User(AuthenticatedUserId).Client_GroupChange(new GroupDto() await Clients.User(UserUID).Client_GroupChange(new GroupDto()
{ {
GID = group.GID, GID = group.GID,
IsDeleted = true IsDeleted = true
}).ConfigureAwait(false); }).ConfigureAwait(false);
bool ownerHasLeft = string.Equals(group.OwnerUID, AuthenticatedUserId, StringComparison.Ordinal); bool ownerHasLeft = string.Equals(group.OwnerUID, UserUID, StringComparison.Ordinal);
if (ownerHasLeft) if (ownerHasLeft)
{ {
if (!groupPairsWithoutSelf.Any()) if (!groupPairsWithoutSelf.Any())
@@ -352,15 +353,14 @@ public partial class MareHub
{ {
GroupGID = group.GID, GroupGID = group.GID,
IsRemoved = true, IsRemoved = true,
UserUID = AuthenticatedUserId, UserUID = UserUID,
}).ConfigureAwait(false); }).ConfigureAwait(false);
var allUserPairs = await GetAllPairedClientsWithPauseState().ConfigureAwait(false); var allUserPairs = await GetAllPairedClientsWithPauseState().ConfigureAwait(false);
var userIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId);
foreach (var groupUserPair in groupPairsWithoutSelf) foreach (var groupUserPair in groupPairsWithoutSelf)
{ {
await UserGroupLeave(groupUserPair, allUserPairs, userIdent).ConfigureAwait(false); await UserGroupLeave(groupUserPair, allUserPairs, UserCharaIdent).ConfigureAwait(false);
} }
} }
@@ -377,15 +377,15 @@ public partial class MareHub
_logger.LogCallInfo(MareHubLogger.Args(gid, isPaused, "Success")); _logger.LogCallInfo(MareHubLogger.Args(gid, isPaused, "Success"));
var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == gid && p.GroupUserUID != AuthenticatedUserId).AsNoTracking().ToListAsync().ConfigureAwait(false); var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == gid && p.GroupUserUID != UserUID).AsNoTracking().ToListAsync().ConfigureAwait(false);
await Clients.Users(groupPairs.Select(p => p.GroupUserUID)).Client_GroupUserChange(new GroupPairDto() await Clients.Users(groupPairs.Select(p => p.GroupUserUID)).Client_GroupUserChange(new GroupPairDto()
{ {
GroupGID = gid, GroupGID = gid,
IsPaused = isPaused, IsPaused = isPaused,
UserUID = AuthenticatedUserId, UserUID = UserUID,
}).ConfigureAwait(false); }).ConfigureAwait(false);
await Clients.User(AuthenticatedUserId).Client_GroupChange(new GroupDto await Clients.User(UserUID).Client_GroupChange(new GroupDto
{ {
GID = gid, GID = gid,
IsPaused = isPaused IsPaused = isPaused
@@ -393,7 +393,6 @@ public partial class MareHub
var allUserPairs = await GetAllPairedClientsWithPauseState().ConfigureAwait(false); var allUserPairs = await GetAllPairedClientsWithPauseState().ConfigureAwait(false);
var userIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId);
foreach (var groupUserPair in groupPairs) foreach (var groupUserPair in groupPairs)
{ {
var userPair = allUserPairs.SingleOrDefault(p => string.Equals(p.UID, groupUserPair.GroupUserUID, StringComparison.Ordinal)); var userPair = allUserPairs.SingleOrDefault(p => string.Equals(p.UID, groupUserPair.GroupUserUID, StringComparison.Ordinal));
@@ -407,8 +406,8 @@ public partial class MareHub
var groupUserIdent = _clientIdentService.GetCharacterIdentForUid(groupUserPair.GroupUserUID); var groupUserIdent = _clientIdentService.GetCharacterIdentForUid(groupUserPair.GroupUserUID);
if (!string.IsNullOrEmpty(groupUserIdent)) if (!string.IsNullOrEmpty(groupUserIdent))
{ {
await Clients.User(AuthenticatedUserId).Client_UserChangePairedPlayer(groupUserIdent, !isPaused).ConfigureAwait(false); await Clients.User(UserUID).Client_UserChangePairedPlayer(groupUserIdent, !isPaused).ConfigureAwait(false);
await Clients.User(groupUserPair.GroupUserUID).Client_UserChangePairedPlayer(userIdent, !isPaused).ConfigureAwait(false); await Clients.User(groupUserPair.GroupUserUID).Client_UserChangePairedPlayer(UserCharaIdent, !isPaused).ConfigureAwait(false);
} }
} }
} }
@@ -471,7 +470,7 @@ public partial class MareHub
var alias = string.IsNullOrEmpty(groupPair.GroupUser.Alias) ? "-" : groupPair.GroupUser.Alias; var alias = string.IsNullOrEmpty(groupPair.GroupUser.Alias) ? "-" : groupPair.GroupUser.Alias;
var ban = new GroupBan() var ban = new GroupBan()
{ {
BannedByUID = AuthenticatedUserId, BannedByUID = UserUID,
BannedReason = $"{reason} (Alias at time of ban: {alias})", BannedReason = $"{reason} (Alias at time of ban: {alias})",
BannedOn = DateTime.UtcNow, BannedOn = DateTime.UtcNow,
BannedUserUID = uid, BannedUserUID = uid,
@@ -574,7 +573,7 @@ public partial class MareHub
var ownedShells = await _dbContext.Groups.CountAsync(g => g.OwnerUID == uid).ConfigureAwait(false); var ownedShells = await _dbContext.Groups.CountAsync(g => g.OwnerUID == uid).ConfigureAwait(false);
if (ownedShells >= _maxExistingGroupsByUser) return; if (ownedShells >= _maxExistingGroupsByUser) return;
var prevOwner = await _dbContext.GroupPairs.SingleOrDefaultAsync(g => g.GroupGID == gid && g.GroupUserUID == AuthenticatedUserId).ConfigureAwait(false); var prevOwner = await _dbContext.GroupPairs.SingleOrDefaultAsync(g => g.GroupGID == gid && g.GroupUserUID == UserUID).ConfigureAwait(false);
prevOwner.IsPinned = false; prevOwner.IsPinned = false;
group.Owner = newOwnerPair.GroupUser; group.Owner = newOwnerPair.GroupUser;
group.Alias = null; group.Alias = null;

View File

@@ -16,13 +16,11 @@ public partial class MareHub
{ {
_logger.LogCallInfo(); _logger.LogCallInfo();
string userid = AuthenticatedUserId; var userEntry = await _dbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false);
var userEntry = await _dbContext.Users.SingleAsync(u => u.UID == userid).ConfigureAwait(false); var ownPairData = await _dbContext.ClientPairs.Where(u => u.User.UID == UserUID).ToListAsync().ConfigureAwait(false);
var charaIdent = _clientIdentService.GetCharacterIdentForUid(userid); var auth = await _dbContext.Auth.SingleAsync(u => u.UserUID == UserUID).ConfigureAwait(false);
var ownPairData = await _dbContext.ClientPairs.Where(u => u.User.UID == userid).ToListAsync().ConfigureAwait(false); var lodestone = await _dbContext.LodeStoneAuth.SingleOrDefaultAsync(a => a.User.UID == UserUID).ConfigureAwait(false);
var auth = await _dbContext.Auth.SingleAsync(u => u.UserUID == userid).ConfigureAwait(false); var groupPairs = await _dbContext.GroupPairs.Where(g => g.GroupUserUID == UserUID).ToListAsync().ConfigureAwait(false);
var lodestone = await _dbContext.LodeStoneAuth.SingleOrDefaultAsync(a => a.User.UID == userid).ConfigureAwait(false);
var groupPairs = await _dbContext.GroupPairs.Where(g => g.GroupUserUID == userid).ToListAsync().ConfigureAwait(false);
if (lodestone != null) if (lodestone != null)
{ {
@@ -37,12 +35,12 @@ public partial class MareHub
_dbContext.ClientPairs.RemoveRange(ownPairData); _dbContext.ClientPairs.RemoveRange(ownPairData);
await _dbContext.SaveChangesAsync().ConfigureAwait(false); await _dbContext.SaveChangesAsync().ConfigureAwait(false);
var otherPairData = await _dbContext.ClientPairs.Include(u => u.User) var otherPairData = await _dbContext.ClientPairs.Include(u => u.User)
.Where(u => u.OtherUser.UID == userid).AsNoTracking().ToListAsync().ConfigureAwait(false); .Where(u => u.OtherUser.UID == UserUID).AsNoTracking().ToListAsync().ConfigureAwait(false);
foreach (var pair in otherPairData) foreach (var pair in otherPairData)
{ {
await Clients.User(pair.User.UID).Client_UserUpdateClientPairs(new ClientPairDto() await Clients.User(pair.User.UID).Client_UserUpdateClientPairs(new ClientPairDto()
{ {
OtherUID = userid, OtherUID = UserUID,
IsRemoved = true IsRemoved = true
}).ConfigureAwait(false); }).ConfigureAwait(false);
} }
@@ -65,9 +63,7 @@ public partial class MareHub
{ {
_logger.LogCallInfo(); _logger.LogCallInfo();
var ownIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId); var usersToSendOnlineTo = await SendOnlineToAllPairedUsers(UserCharaIdent).ConfigureAwait(false);
var usersToSendOnlineTo = await SendOnlineToAllPairedUsers(ownIdent).ConfigureAwait(false);
return usersToSendOnlineTo.Select(e => _clientIdentService.GetCharacterIdentForUid(e)).Where(t => !string.IsNullOrEmpty(t)).Distinct(StringComparer.Ordinal).ToList(); return usersToSendOnlineTo.Select(e => _clientIdentService.GetCharacterIdentForUid(e)).Where(t => !string.IsNullOrEmpty(t)).Distinct(StringComparer.Ordinal).ToList();
} }
@@ -76,7 +72,6 @@ public partial class MareHub
{ {
_logger.LogCallInfo(); _logger.LogCallInfo();
string userid = AuthenticatedUserId;
var query = var query =
from userToOther in _dbContext.ClientPairs from userToOther in _dbContext.ClientPairs
join otherToUser in _dbContext.ClientPairs join otherToUser in _dbContext.ClientPairs
@@ -92,7 +87,7 @@ public partial class MareHub
} into leftJoin } into leftJoin
from otherEntry in leftJoin.DefaultIfEmpty() from otherEntry in leftJoin.DefaultIfEmpty()
where where
userToOther.UserUID == userid userToOther.UserUID == UserUID
select new select new
{ {
userToOther.OtherUser.Alias, userToOther.OtherUser.Alias,
@@ -140,22 +135,24 @@ public partial class MareHub
} }
} }
if (hadInvalidData) throw new HubException("Invalid data provided, contact the appropriate mod creator to resolve those issues" if (hadInvalidData)
{
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "One or more of your supplied mods were rejected from the server. Consult /xllog for more information.").ConfigureAwait(false);
throw new HubException("Invalid data provided, contact the appropriate mod creator to resolve those issues"
+ Environment.NewLine + Environment.NewLine
+ string.Join(Environment.NewLine, invalidGamePaths.Select(p => "Invalid Game Path: " + p)) + string.Join(Environment.NewLine, invalidGamePaths.Select(p => "Invalid Game Path: " + p))
+ Environment.NewLine + Environment.NewLine
+ string.Join(Environment.NewLine, invalidFileSwapPaths.Select(p => "Invalid FileSwap Path: " + p))); + string.Join(Environment.NewLine, invalidFileSwapPaths.Select(p => "Invalid FileSwap Path: " + p)));
}
var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false); var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
var allPairedUsersDict = allPairedUsers.ToDictionary(f => f, f => _clientIdentService.GetCharacterIdentForUid(f), StringComparer.Ordinal) var allPairedUsersDict = allPairedUsers.ToDictionary(f => f, f => _clientIdentService.GetCharacterIdentForUid(f), StringComparer.Ordinal)
.Where(f => visibleCharacterIds.Contains(f.Value, StringComparer.Ordinal)); .Where(f => visibleCharacterIds.Contains(f.Value, StringComparer.Ordinal));
var ownIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId);
_logger.LogCallInfo(MareHubLogger.Args(visibleCharacterIds.Count, allPairedUsersDict.Count())); _logger.LogCallInfo(MareHubLogger.Args(visibleCharacterIds.Count, allPairedUsersDict.Count()));
await Clients.Users(allPairedUsersDict.Select(f => f.Key)).Client_UserReceiveCharacterData(characterCache, ownIdent).ConfigureAwait(false); await Clients.Users(allPairedUsersDict.Select(f => f.Key)).Client_UserReceiveCharacterData(characterCache, UserCharaIdent).ConfigureAwait(false);
_mareMetrics.IncCounter(MetricsAPI.CounterUserPushData); _mareMetrics.IncCounter(MetricsAPI.CounterUserPushData);
_mareMetrics.IncCounter(MetricsAPI.CounterUserPushDataTo, allPairedUsersDict.Count()); _mareMetrics.IncCounter(MetricsAPI.CounterUserPushDataTo, allPairedUsersDict.Count());
@@ -168,18 +165,22 @@ public partial class MareHub
// don't allow adding yourself or nothing // don't allow adding yourself or nothing
uid = uid.Trim(); uid = uid.Trim();
if (string.Equals(uid, AuthenticatedUserId, StringComparison.Ordinal) || string.IsNullOrWhiteSpace(uid)) return; if (string.Equals(uid, UserUID, StringComparison.Ordinal) || string.IsNullOrWhiteSpace(uid)) return;
// grab other user, check if it exists and if a pair already exists // grab other user, check if it exists and if a pair already exists
var otherUser = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == uid || u.Alias == uid).ConfigureAwait(false); var otherUser = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == uid || u.Alias == uid).ConfigureAwait(false);
var existingEntry = var existingEntry =
await _dbContext.ClientPairs.AsNoTracking() await _dbContext.ClientPairs.AsNoTracking()
.FirstOrDefaultAsync(p => .FirstOrDefaultAsync(p =>
p.User.UID == AuthenticatedUserId && p.OtherUserUID == uid).ConfigureAwait(false); p.User.UID == UserUID && p.OtherUserUID == uid).ConfigureAwait(false);
if (otherUser == null || existingEntry != null) return; if (otherUser == null || existingEntry != null)
{
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Warning, $"Cannot pair with {uid}, either already paired or UID does not exist").ConfigureAwait(false);
return;
}
// grab self create new client pair and save // grab self create new client pair and save
var user = await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false); var user = await _dbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false);
_logger.LogCallInfo(MareHubLogger.Args(uid, "Success")); _logger.LogCallInfo(MareHubLogger.Args(uid, "Success"));
@@ -239,8 +240,8 @@ public partial class MareHub
{ {
_logger.LogCallInfo(MareHubLogger.Args(otherUserUid, isPaused)); _logger.LogCallInfo(MareHubLogger.Args(otherUserUid, isPaused));
if (string.Equals(otherUserUid, AuthenticatedUserId, StringComparison.Ordinal)) return; if (string.Equals(otherUserUid, UserUID, StringComparison.Ordinal)) return;
ClientPair pair = await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == AuthenticatedUserId && w.OtherUserUID == otherUserUid).ConfigureAwait(false); ClientPair pair = await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == otherUserUid).ConfigureAwait(false);
if (pair == null) return; if (pair == null) return;
pair.IsPaused = isPaused; pair.IsPaused = isPaused;
@@ -251,7 +252,7 @@ public partial class MareHub
var otherEntry = OppositeEntry(otherUserUid); var otherEntry = OppositeEntry(otherUserUid);
await Clients.User(AuthenticatedUserId).Client_UserUpdateClientPairs( await Clients.User(UserUID).Client_UserUpdateClientPairs(
new ClientPairDto() new ClientPairDto()
{ {
OtherUID = otherUserUid, OtherUID = otherUserUid,
@@ -263,19 +264,18 @@ public partial class MareHub
{ {
await Clients.User(otherUserUid).Client_UserUpdateClientPairs(new ClientPairDto() await Clients.User(otherUserUid).Client_UserUpdateClientPairs(new ClientPairDto()
{ {
OtherUID = AuthenticatedUserId, OtherUID = UserUID,
IsPaused = otherEntry.IsPaused, IsPaused = otherEntry.IsPaused,
IsPausedFromOthers = isPaused, IsPausedFromOthers = isPaused,
IsSynced = true IsSynced = true
}).ConfigureAwait(false); }).ConfigureAwait(false);
var selfCharaIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId);
var otherCharaIdent = _clientIdentService.GetCharacterIdentForUid(pair.OtherUserUID); var otherCharaIdent = _clientIdentService.GetCharacterIdentForUid(pair.OtherUserUID);
if (selfCharaIdent == null || otherCharaIdent == null || otherEntry.IsPaused) return; if (UserCharaIdent == null || otherCharaIdent == null || otherEntry.IsPaused) return;
await Clients.User(AuthenticatedUserId).Client_UserChangePairedPlayer(otherCharaIdent, !isPaused).ConfigureAwait(false); await Clients.User(UserUID).Client_UserChangePairedPlayer(otherCharaIdent, !isPaused).ConfigureAwait(false);
await Clients.User(otherUserUid).Client_UserChangePairedPlayer(selfCharaIdent, !isPaused).ConfigureAwait(false); await Clients.User(otherUserUid).Client_UserChangePairedPlayer(UserCharaIdent, !isPaused).ConfigureAwait(false);
} }
} }
@@ -284,11 +284,11 @@ public partial class MareHub
{ {
_logger.LogCallInfo(MareHubLogger.Args(otherUserUid)); _logger.LogCallInfo(MareHubLogger.Args(otherUserUid));
if (string.Equals(otherUserUid, AuthenticatedUserId, StringComparison.Ordinal)) return; if (string.Equals(otherUserUid, UserUID, StringComparison.Ordinal)) return;
// check if client pair even exists // check if client pair even exists
ClientPair callerPair = ClientPair callerPair =
await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == AuthenticatedUserId && w.OtherUserUID == otherUserUid).ConfigureAwait(false); await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == otherUserUid).ConfigureAwait(false);
bool callerHadPaused = callerPair.IsPaused; bool callerHadPaused = callerPair.IsPaused;
if (callerPair == null) return; if (callerPair == null) return;
@@ -298,7 +298,7 @@ public partial class MareHub
_logger.LogCallInfo(MareHubLogger.Args(otherUserUid, "Success")); _logger.LogCallInfo(MareHubLogger.Args(otherUserUid, "Success"));
await Clients.User(AuthenticatedUserId) await Clients.User(UserUID)
.Client_UserUpdateClientPairs(new ClientPairDto() .Client_UserUpdateClientPairs(new ClientPairDto()
{ {
OtherUID = otherUserUid, OtherUID = otherUserUid,
@@ -317,7 +317,7 @@ public partial class MareHub
await Clients.User(otherUserUid).Client_UserUpdateClientPairs( await Clients.User(otherUserUid).Client_UserUpdateClientPairs(
new ClientPairDto() new ClientPairDto()
{ {
OtherUID = AuthenticatedUserId, OtherUID = UserUID,
IsPausedFromOthers = false, IsPausedFromOthers = false,
IsSynced = false IsSynced = false
}).ConfigureAwait(false); }).ConfigureAwait(false);
@@ -336,20 +336,18 @@ public partial class MareHub
// if neither user had paused each other and either is not in an unpaused group with each other, change state to offline // if neither user had paused each other and either is not in an unpaused group with each other, change state to offline
if (!callerHadPaused && !otherHadPaused && isPausedInGroup) if (!callerHadPaused && !otherHadPaused && isPausedInGroup)
{ {
var userIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId); await Clients.User(UserUID).Client_UserChangePairedPlayer(otherIdent, false).ConfigureAwait(false);
await Clients.User(AuthenticatedUserId).Client_UserChangePairedPlayer(otherIdent, false).ConfigureAwait(false); await Clients.User(otherUserUid).Client_UserChangePairedPlayer(UserCharaIdent, false).ConfigureAwait(false);
await Clients.User(otherUserUid).Client_UserChangePairedPlayer(userIdent, false).ConfigureAwait(false);
} }
// if the caller had paused other but not the other has paused the caller and they are in an unpaused group together, change state to online // if the caller had paused other but not the other has paused the caller and they are in an unpaused group together, change state to online
if (callerHadPaused && !otherHadPaused && !isPausedInGroup) if (callerHadPaused && !otherHadPaused && !isPausedInGroup)
{ {
var userIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId); await Clients.User(UserUID).Client_UserChangePairedPlayer(otherIdent, true).ConfigureAwait(false);
await Clients.User(AuthenticatedUserId).Client_UserChangePairedPlayer(otherIdent, true).ConfigureAwait(false); await Clients.User(otherUserUid).Client_UserChangePairedPlayer(UserCharaIdent, true).ConfigureAwait(false);
await Clients.User(otherUserUid).Client_UserChangePairedPlayer(userIdent, true).ConfigureAwait(false);
} }
} }
private ClientPair OppositeEntry(string otherUID) => 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 == UserUID);
} }

View File

@@ -1,5 +1,4 @@
using System.Security.Claims; using MareSynchronos.API;
using MareSynchronos.API;
using MareSynchronosServer.Services; using MareSynchronosServer.Services;
using MareSynchronosServer.Utils; using MareSynchronosServer.Utils;
using MareSynchronosShared; using MareSynchronosShared;
@@ -10,7 +9,6 @@ using MareSynchronosShared.Services;
using MareSynchronosShared.Utils; using MareSynchronosShared.Utils;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
namespace MareSynchronosServer.Hubs; namespace MareSynchronosServer.Hubs;
@@ -51,57 +49,40 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
_dbContext = mareDbContext; _dbContext = mareDbContext;
} }
[Authorize(Policy = "Authenticated")] [Authorize(Policy = "Identified")]
public async Task<ConnectionDto> Heartbeat(string characterIdentification) public async Task<ConnectionDto> GetConnectionDto()
{ {
_logger.LogCallInfo();
_mareMetrics.IncCounter(MetricsAPI.CounterInitializedConnections); _mareMetrics.IncCounter(MetricsAPI.CounterInitializedConnections);
var userId = Context.User!.Claims.SingleOrDefault(c => string.Equals(c.Type, ClaimTypes.NameIdentifier, StringComparison.Ordinal))?.Value;
_logger.LogCallInfo(MareHubLogger.Args(characterIdentification));
await Clients.Caller.Client_UpdateSystemInfo(_systemInfoService.SystemInfoDto).ConfigureAwait(false); await Clients.Caller.Client_UpdateSystemInfo(_systemInfoService.SystemInfoDto).ConfigureAwait(false);
var isBanned = await _dbContext.BannedUsers.AsNoTracking().AnyAsync(u => u.CharacterIdentification == characterIdentification).ConfigureAwait(false); var dbUser = _dbContext.Users.SingleOrDefault(f => f.UID == UserUID);
dbUser.LastLoggedIn = DateTime.UtcNow;
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
if (!string.IsNullOrEmpty(userId) && !isBanned && !string.IsNullOrEmpty(characterIdentification)) await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Information, "Welcome to Mare Synchronos \"" + _shardName + "\", Current Online Users: " + _systemInfoService.SystemInfoDto.OnlineUsers).ConfigureAwait(false);
return new ConnectionDto()
{ {
var user = (await _dbContext.Users.SingleAsync(u => u.UID == userId).ConfigureAwait(false)); ServerVersion = IMareHub.ApiVersion,
var existingIdent = _clientIdentService.GetCharacterIdentForUid(userId); UID = string.IsNullOrEmpty(dbUser.Alias) ? dbUser.UID : dbUser.Alias,
if (!string.IsNullOrEmpty(existingIdent) && !string.Equals(characterIdentification, existingIdent, StringComparison.Ordinal)) IsAdmin = dbUser.IsAdmin,
IsModerator = dbUser.IsModerator,
ServerInfo = new ServerInfoDto()
{ {
_logger.LogCallWarning(MareHubLogger.Args(characterIdentification, "Failure", "LoggedIn")); MaxGroupsCreatedByUser = _maxExistingGroupsByUser,
ShardName = _shardName,
return new ConnectionDto() MaxGroupsJoinedByUser = _maxJoinedGroupsByUser,
{ MaxGroupUserCount = _maxGroupUserCount
ServerVersion = IMareHub.ApiVersion
};
} }
};
}
user.LastLoggedIn = DateTime.UtcNow; [Authorize(Policy = "Authenticated")]
_clientIdentService.MarkUserOnline(user.UID, characterIdentification); public async Task<ConnectionDto> Heartbeat(string characterIdentification)
await _dbContext.SaveChangesAsync().ConfigureAwait(false); {
_logger.LogCallInfo(MareHubLogger.Args(characterIdentification, "Success"));
return new ConnectionDto
{
ServerVersion = IMareHub.ApiVersion,
UID = string.IsNullOrEmpty(user.Alias) ? user.UID : user.Alias,
IsModerator = user.IsModerator,
IsAdmin = user.IsAdmin,
ServerInfo = new ServerInfoDto()
{
MaxGroupsCreatedByUser = _maxExistingGroupsByUser,
ShardName = _shardName,
MaxGroupsJoinedByUser = _maxJoinedGroupsByUser,
MaxGroupUserCount = _maxGroupUserCount
}
};
}
_logger.LogCallWarning(MareHubLogger.Args(characterIdentification, "Failure"));
return new ConnectionDto() return new ConnectionDto()
{ {
ServerVersion = IMareHub.ApiVersion ServerVersion = IMareHub.ApiVersion
@@ -109,21 +90,31 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
} }
[Authorize(Policy = "Authenticated")] [Authorize(Policy = "Authenticated")]
public Task<bool> CheckClientHealth() public async Task<bool> CheckClientHealth()
{ {
var needsReconnect = !_clientIdentService.IsOnCurrentServer(AuthenticatedUserId); var needsReconnect = !_clientIdentService.IsOnCurrentServer(UserUID);
if (needsReconnect) if (needsReconnect)
{ {
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Warning, "Internal server state corruption detected, reconnecting you automatically to fix the issue.").ConfigureAwait(false);
_logger.LogCallWarning(MareHubLogger.Args(needsReconnect)); _logger.LogCallWarning(MareHubLogger.Args(needsReconnect));
} }
return Task.FromResult(needsReconnect);
return needsReconnect;
} }
[Authorize(Policy = "Authenticated")] [Authorize(Policy = "Authenticated")]
public override async Task OnConnectedAsync() public override async Task OnConnectedAsync()
{ {
_logger.LogCallInfo(MareHubLogger.Args(_contextAccessor.GetIpAddress()));
_mareMetrics.IncGauge(MetricsAPI.GaugeConnections); _mareMetrics.IncGauge(MetricsAPI.GaugeConnections);
try
{
_logger.LogCallInfo(MareHubLogger.Args(_contextAccessor.GetIpAddress(), UserCharaIdent));
_clientIdentService.MarkUserOnline(UserUID, UserCharaIdent);
}
catch { }
await base.OnConnectedAsync().ConfigureAwait(false); await base.OnConnectedAsync().ConfigureAwait(false);
} }
@@ -132,19 +123,18 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
{ {
_mareMetrics.DecGauge(MetricsAPI.GaugeConnections); _mareMetrics.DecGauge(MetricsAPI.GaugeConnections);
var userCharaIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId); try
if (!string.IsNullOrEmpty(userCharaIdent))
{ {
_logger.LogCallInfo(); _logger.LogCallInfo(MareHubLogger.Args(_contextAccessor.GetIpAddress(), UserCharaIdent));
_clientIdentService.MarkUserOffline(AuthenticatedUserId);
await SendOfflineToAllPairedUsers(userCharaIdent).ConfigureAwait(false); _clientIdentService.MarkUserOffline(UserUID);
_dbContext.RemoveRange(_dbContext.Files.Where(f => !f.Uploaded && f.UploaderUID == AuthenticatedUserId)); await SendOfflineToAllPairedUsers(UserCharaIdent).ConfigureAwait(false);
_dbContext.RemoveRange(_dbContext.Files.Where(f => !f.Uploaded && f.UploaderUID == UserUID));
await _dbContext.SaveChangesAsync().ConfigureAwait(false); await _dbContext.SaveChangesAsync().ConfigureAwait(false);
} }
catch { }
await base.OnDisconnectedAsync(exception).ConfigureAwait(false); await base.OnDisconnectedAsync(exception).ConfigureAwait(false);
} }

View File

@@ -1,5 +1,4 @@
using System.Security.Claims; using AspNetCoreRateLimit;
using AspNetCoreRateLimit;
using MareSynchronosShared; using MareSynchronosShared;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@@ -37,7 +36,7 @@ public class SignalRLimitFilter : IHubFilter
var counter = await _processor.ProcessRequestAsync(client, rule).ConfigureAwait(false); var counter = await _processor.ProcessRequestAsync(client, rule).ConfigureAwait(false);
if (counter.Count > rule.Limit) if (counter.Count > rule.Limit)
{ {
var authUserId = invocationContext.Context.User.Claims?.SingleOrDefault(c => string.Equals(c.Type, ClaimTypes.NameIdentifier, StringComparison.Ordinal))?.Value ?? "Unknown"; var authUserId = invocationContext.Context.User.Claims?.SingleOrDefault(c => string.Equals(c.Type, MareClaimTypes.Uid, StringComparison.Ordinal))?.Value ?? "Unknown";
var retry = counter.Timestamp.RetryAfterFrom(rule); var retry = counter.Timestamp.RetryAfterFrom(rule);
logger.LogWarning("Method rate limit triggered from {ip}/{authUserId}: {method}", ip, authUserId, invocationContext.HubMethodName); logger.LogWarning("Method rate limit triggered from {ip}/{authUserId}: {method}", ip, authUserId, invocationContext.HubMethodName);
throw new HubException($"call limit {retry}"); throw new HubException($"call limit {retry}");

View File

@@ -1,9 +1,9 @@
using System.Security.Claims; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using MareSynchronosShared.Data; using MareSynchronosShared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using MareSynchronosServer.Services; using MareSynchronosServer.Services;
using MareSynchronosServer.Hubs;
namespace MareSynchronosServer.RequirementHandlers; namespace MareSynchronosServer.RequirementHandlers;
@@ -22,7 +22,7 @@ public class UserRequirementHandler : AuthorizationHandler<UserRequirement, HubI
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, UserRequirement requirement, HubInvocationContext resource) protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, UserRequirement requirement, HubInvocationContext resource)
{ {
var uid = context.User.Claims.SingleOrDefault(g => string.Equals(g.Type, ClaimTypes.NameIdentifier, StringComparison.Ordinal))?.Value; var uid = context.User.Claims.SingleOrDefault(g => string.Equals(g.Type, MareClaimTypes.Uid, StringComparison.Ordinal))?.Value;
if (uid == null) context.Fail(); if (uid == null) context.Fail();

View File

@@ -0,0 +1,46 @@
using Grpc.Core;
using MareSynchronos.API;
using MareSynchronosServer.Hubs;
using MareSynchronosShared.Protos;
using Microsoft.AspNetCore.SignalR;
using static MareSynchronosShared.Protos.ClientMessageService;
namespace MareSynchronosServer.Services;
public class GrpcClientMessageService : ClientMessageServiceBase
{
private readonly ILogger<GrpcClientMessageService> _logger;
private readonly IHubContext<MareHub, IMareHub> _hubContext;
public GrpcClientMessageService(ILogger<GrpcClientMessageService> logger, IHubContext<MareHub, IMareHub> hubContext)
{
_logger = logger;
_hubContext = hubContext;
}
public override async Task<Empty> SendClientMessage(ClientMessage request, ServerCallContext context)
{
bool hasUid = !string.IsNullOrEmpty(request.Uid);
var severity = request.Type switch
{
MessageType.Info => MessageSeverity.Information,
MessageType.Warning => MessageSeverity.Warning,
MessageType.Error => MessageSeverity.Error,
_ => MessageSeverity.Information,
};
if (!hasUid)
{
_logger.LogInformation("Sending Message of severity {severity} to all online users: {message}", severity, request.Message);
await _hubContext.Clients.All.Client_ReceiveServerMessage(severity, request.Message).ConfigureAwait(false);
}
else
{
_logger.LogInformation("Sending Message of severity {severity} to user {uid}: {message}", severity, request.Uid, request.Message);
await _hubContext.Clients.User(request.Uid).Client_ReceiveServerMessage(severity, request.Message).ConfigureAwait(false);
}
return new Empty();
}
}

View File

@@ -94,9 +94,10 @@ public class GrpcClientIdentificationService : GrpcBaseService, IClientIdentific
public void MarkUserOffline(string uid) public void MarkUserOffline(string uid)
{ {
_metrics.SetGaugeTo(MetricsAPI.GaugeAuthorizedConnections, OnlineClients.Count);
if (OnlineClients.TryRemove(uid, out var uidWithIdent)) if (OnlineClients.TryRemove(uid, out var uidWithIdent))
{ {
_metrics.SetGaugeTo(MetricsAPI.GaugeAuthorizedConnections, OnlineClients.Count);
_identChangeQueue.Enqueue(new IdentChange() _identChangeQueue.Enqueue(new IdentChange()
{ {
IsOnline = false, IsOnline = false,
@@ -172,6 +173,7 @@ public class GrpcClientIdentificationService : GrpcBaseService, IClientIdentific
_logger.LogInformation("Starting Receive Online Client Data stream"); _logger.LogInformation("Starting Receive Online Client Data stream");
await foreach (var cur in stream.ResponseStream.ReadAllAsync(cts).ConfigureAwait(false)) await foreach (var cur in stream.ResponseStream.ReadAllAsync(cts).ConfigureAwait(false))
{ {
OnlineClients.Remove(cur.UidWithIdent.Uid.Uid, out _);
if (cur.IsOnline) if (cur.IsOnline)
{ {
RemoteCachedIdents[cur.UidWithIdent.Uid.Uid] = cur.UidWithIdent; RemoteCachedIdents[cur.UidWithIdent.Uid.Uid] = cur.UidWithIdent;

View File

@@ -47,14 +47,14 @@ public class SystemInfoService : IHostedService, IDisposable
_mareMetrics.SetGaugeTo(MetricsAPI.GaugeAvailableWorkerThreads, workerThreads); _mareMetrics.SetGaugeTo(MetricsAPI.GaugeAvailableWorkerThreads, workerThreads);
_mareMetrics.SetGaugeTo(MetricsAPI.GaugeAvailableIOWorkerThreads, ioThreads); _mareMetrics.SetGaugeTo(MetricsAPI.GaugeAvailableIOWorkerThreads, ioThreads);
var onlineUsers = (int)_clientIdentService.GetOnlineUsers().Result;
SystemInfoDto = new SystemInfoDto()
{
OnlineUsers = onlineUsers,
};
if (_config.IsMain) if (_config.IsMain)
{ {
var onlineUsers = (int)_clientIdentService.GetOnlineUsers().Result;
SystemInfoDto = new SystemInfoDto()
{
OnlineUsers = onlineUsers,
};
_logger.LogInformation("Sending System Info, Online Users: {onlineUsers}", onlineUsers); _logger.LogInformation("Sending System Info, Online Users: {onlineUsers}", onlineUsers);
_hubContext.Clients.All.Client_UpdateSystemInfo(SystemInfoDto); _hubContext.Clients.All.Client_UpdateSystemInfo(SystemInfoDto);

View File

@@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using AspNetCoreRateLimit; using AspNetCoreRateLimit;
using MareSynchronosShared.Authentication;
using MareSynchronosShared.Data; using MareSynchronosShared.Data;
using MareSynchronosShared.Protos; using MareSynchronosShared.Protos;
using Grpc.Net.Client.Configuration; using Grpc.Net.Client.Configuration;
@@ -22,6 +21,7 @@ using Grpc.Net.ClientFactory;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using System.Text; using System.Text;
using MareSynchronosServer.Authentication;
namespace MareSynchronosServer; namespace MareSynchronosServer;
@@ -79,7 +79,7 @@ public class Startup
services.AddSingleton<IUserIdProvider, IdBasedUserIdProvider>(); services.AddSingleton<IUserIdProvider, IdBasedUserIdProvider>();
services.AddHostedService(provider => provider.GetService<SystemInfoService>()); services.AddHostedService(provider => provider.GetService<SystemInfoService>());
// configure services based on main server status // configure services based on main server status
ConfigureIdentityServices(services, mareConfig, isMainServer); ConfigureServicesBasedOnShardType(services, mareConfig, isMainServer);
if (isMainServer) if (isMainServer)
{ {
@@ -210,7 +210,7 @@ public class Startup
})); }));
} }
private static void ConfigureIdentityServices(IServiceCollection services, IConfigurationSection mareConfig, bool isMainServer) private static void ConfigureServicesBasedOnShardType(IServiceCollection services, IConfigurationSection mareConfig, bool isMainServer)
{ {
if (!isMainServer) if (!isMainServer)
{ {
@@ -325,6 +325,7 @@ public class Startup
{ {
endpoints.MapGrpcService<GrpcIdentityService>().AllowAnonymous(); endpoints.MapGrpcService<GrpcIdentityService>().AllowAnonymous();
endpoints.MapGrpcService<GrpcConfigurationService<ServerConfiguration>>().AllowAnonymous(); endpoints.MapGrpcService<GrpcConfigurationService<ServerConfiguration>>().AllowAnonymous();
endpoints.MapGrpcService<GrpcClientMessageService>().AllowAnonymous();
} }
endpoints.MapHealthChecks("/health").AllowAnonymous(); endpoints.MapHealthChecks("/health").AllowAnonymous();

View File

@@ -1,4 +1,4 @@
using System.Security.Claims; using MareSynchronosServer.Hubs;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
namespace MareSynchronosServer.Utils; namespace MareSynchronosServer.Utils;
@@ -7,6 +7,6 @@ public class IdBasedUserIdProvider : IUserIdProvider
{ {
public string GetUserId(HubConnectionContext context) public string GetUserId(HubConnectionContext context)
{ {
return context.User!.Claims.SingleOrDefault(c => string.Equals(c.Type, ClaimTypes.NameIdentifier, StringComparison.Ordinal))?.Value; return context.User!.Claims.SingleOrDefault(c => string.Equals(c.Type, MareClaimTypes.Uid, StringComparison.Ordinal))?.Value;
} }
} }

View File

@@ -22,12 +22,12 @@ public class MareHubLogger
public void LogCallInfo(object[] args = null, [CallerMemberName] string methodName = "") public void LogCallInfo(object[] args = null, [CallerMemberName] string methodName = "")
{ {
string formattedArgs = args != null && args.Length != 0 ? "|" + string.Join(":", args) : string.Empty; string formattedArgs = args != null && args.Length != 0 ? "|" + string.Join(":", args) : string.Empty;
_logger.LogInformation("{uid}:{method}{args}", _hub.AuthenticatedUserId, methodName, formattedArgs); _logger.LogInformation("{uid}:{method}{args}", _hub.UserUID, methodName, formattedArgs);
} }
public void LogCallWarning(object[] args = null, [CallerMemberName] string methodName = "") public void LogCallWarning(object[] args = null, [CallerMemberName] string methodName = "")
{ {
string formattedArgs = args != null && args.Length != 0 ? "|" + string.Join(":", args) : string.Empty; string formattedArgs = args != null && args.Length != 0 ? "|" + string.Join(":", args) : string.Empty;
_logger.LogWarning("{uid}:{method}{args}", _hub.AuthenticatedUserId, methodName, formattedArgs); _logger.LogWarning("{uid}:{method}{args}", _hub.UserUID, methodName, formattedArgs);
} }
} }

View File

@@ -6,11 +6,11 @@ using Microsoft.EntityFrameworkCore;
using Discord.WebSocket; using Discord.WebSocket;
using Prometheus; using Prometheus;
using MareSynchronosShared.Models; using MareSynchronosShared.Models;
using MareSynchronosShared.Metrics;
using MareSynchronosShared.Utils; using MareSynchronosShared.Utils;
using MareSynchronosShared.Services; using MareSynchronosShared.Services;
using static MareSynchronosShared.Protos.IdentificationService; using static MareSynchronosShared.Protos.IdentificationService;
using static System.Formats.Asn1.AsnWriter; using Grpc.Net.ClientFactory;
using MareSynchronosShared.Protos;
namespace MareSynchronosServices.Discord; namespace MareSynchronosServices.Discord;
@@ -30,23 +30,26 @@ public class MareModule : InteractionModuleBase
private readonly DiscordBotServices _botServices; private readonly DiscordBotServices _botServices;
private readonly IdentificationServiceClient _identificationServiceClient; private readonly IdentificationServiceClient _identificationServiceClient;
private readonly IConfigurationService<ServerConfiguration> _mareClientConfigurationService; private readonly IConfigurationService<ServerConfiguration> _mareClientConfigurationService;
private readonly GrpcClientFactory _grpcClientFactory;
private Random random = new(); private Random random = new();
public MareModule(ILogger<MareModule> logger, IServiceProvider services, DiscordBotServices botServices, public MareModule(ILogger<MareModule> logger, IServiceProvider services, DiscordBotServices botServices,
IdentificationServiceClient identificationServiceClient, IConfigurationService<ServerConfiguration> mareClientConfigurationService) IdentificationServiceClient identificationServiceClient, IConfigurationService<ServerConfiguration> mareClientConfigurationService,
GrpcClientFactory grpcClientFactory)
{ {
_logger = logger; _logger = logger;
_services = services; _services = services;
_botServices = botServices; _botServices = botServices;
_identificationServiceClient = identificationServiceClient; _identificationServiceClient = identificationServiceClient;
_mareClientConfigurationService = mareClientConfigurationService; _mareClientConfigurationService = mareClientConfigurationService;
_grpcClientFactory = grpcClientFactory;
} }
[SlashCommand("register", "Starts the registration process for the Mare Synchronos server of this Discord")] [SlashCommand("register", "Starts the registration process for the Mare Synchronos server of this Discord")]
public async Task Register([Summary("overwrite", "Overwrites your old account")] bool overwrite = false) public async Task Register([Summary("overwrite", "Overwrites your old account")] bool overwrite = false)
{ {
_logger.LogInformation("SlashCommand:{userId}:{Method}:{params}", _logger.LogInformation("SlashCommand:{userId}:{Method}:{params}",
Context.Client.CurrentUser.Id, nameof(Register), Context.Interaction.User.Id, nameof(Register),
string.Join(",", new[] { $"{nameof(overwrite)}:{overwrite}" })); string.Join(",", new[] { $"{nameof(overwrite)}:{overwrite}" }));
await TryRespondAsync(async () => await TryRespondAsync(async () =>
@@ -64,7 +67,7 @@ public class MareModule : InteractionModuleBase
public async Task SetVanityUid([Summary("vanity_uid", "Desired Vanity UID")] string vanityUid) public async Task SetVanityUid([Summary("vanity_uid", "Desired Vanity UID")] string vanityUid)
{ {
_logger.LogInformation("SlashCommand:{userId}:{Method}:{params}", _logger.LogInformation("SlashCommand:{userId}:{Method}:{params}",
Context.Client.CurrentUser.Id, nameof(SetVanityUid), Context.Interaction.User.Id, nameof(SetVanityUid),
string.Join(",", new[] { $"{nameof(vanityUid)}:{vanityUid}" })); string.Join(",", new[] { $"{nameof(vanityUid)}:{vanityUid}" }));
await TryRespondAsync(async () => await TryRespondAsync(async () =>
@@ -83,7 +86,7 @@ public class MareModule : InteractionModuleBase
[Summary("vanity_syncshell_id", "Desired Vanity Syncshell ID")] string vanityId) [Summary("vanity_syncshell_id", "Desired Vanity Syncshell ID")] string vanityId)
{ {
_logger.LogInformation("SlashCommand:{userId}:{Method}:{params}", _logger.LogInformation("SlashCommand:{userId}:{Method}:{params}",
Context.Client.CurrentUser.Id, nameof(SetSyncshellVanityId), Context.Interaction.User.Id, nameof(SetSyncshellVanityId),
string.Join(",", new[] { $"{nameof(syncshellId)}:{syncshellId}", $"{nameof(vanityId)}:{vanityId}" })); string.Join(",", new[] { $"{nameof(syncshellId)}:{syncshellId}", $"{nameof(vanityId)}:{vanityId}" }));
await TryRespondAsync(async () => await TryRespondAsync(async () =>
@@ -100,7 +103,7 @@ public class MareModule : InteractionModuleBase
public async Task Verify() public async Task Verify()
{ {
_logger.LogInformation("SlashCommand:{userId}:{Method}", _logger.LogInformation("SlashCommand:{userId}:{Method}",
Context.Client.CurrentUser.Id, nameof(Verify)); Context.Interaction.User.Id, nameof(Verify));
await TryRespondAsync(async () => await TryRespondAsync(async () =>
{ {
EmbedBuilder eb = new(); EmbedBuilder eb = new();
@@ -128,7 +131,7 @@ public class MareModule : InteractionModuleBase
public async Task VerifyRelink() public async Task VerifyRelink()
{ {
_logger.LogInformation("SlashCommand:{userId}:{Method}", _logger.LogInformation("SlashCommand:{userId}:{Method}",
Context.Client.CurrentUser.Id, nameof(VerifyRelink)); Context.Interaction.User.Id, nameof(VerifyRelink));
await TryRespondAsync(async () => await TryRespondAsync(async () =>
{ {
EmbedBuilder eb = new(); EmbedBuilder eb = new();
@@ -156,7 +159,7 @@ public class MareModule : InteractionModuleBase
public async Task Recover() public async Task Recover()
{ {
_logger.LogInformation("SlashCommand:{userId}:{Method}", _logger.LogInformation("SlashCommand:{userId}:{Method}",
Context.Client.CurrentUser.Id, nameof(Recover)); Context.Interaction.User.Id, nameof(Recover));
await RespondWithModalAsync<LodestoneModal>("recover_modal").ConfigureAwait(false); await RespondWithModalAsync<LodestoneModal>("recover_modal").ConfigureAwait(false);
} }
@@ -166,7 +169,7 @@ public class MareModule : InteractionModuleBase
[Summary("uid", "ADMIN ONLY: UID to check for")] string? uid = null) [Summary("uid", "ADMIN ONLY: UID to check for")] string? uid = null)
{ {
_logger.LogInformation("SlashCommand:{userId}:{Method}", _logger.LogInformation("SlashCommand:{userId}:{Method}",
Context.Client.CurrentUser.Id, nameof(UserInfo)); Context.Interaction.User.Id, nameof(UserInfo));
await TryRespondAsync(async () => await TryRespondAsync(async () =>
{ {
@@ -182,7 +185,7 @@ public class MareModule : InteractionModuleBase
public async Task Relink() public async Task Relink()
{ {
_logger.LogInformation("SlashCommand:{userId}:{Method}", _logger.LogInformation("SlashCommand:{userId}:{Method}",
Context.Client.CurrentUser.Id, nameof(Relink)); Context.Interaction.User.Id, nameof(Relink));
await RespondWithModalAsync<LodestoneModal>("relink_modal").ConfigureAwait(false); await RespondWithModalAsync<LodestoneModal>("relink_modal").ConfigureAwait(false);
} }
@@ -190,7 +193,7 @@ public class MareModule : InteractionModuleBase
public async Task UserAdd([Summary("desired_uid", "Desired UID")] string desiredUid) public async Task UserAdd([Summary("desired_uid", "Desired UID")] string desiredUid)
{ {
_logger.LogInformation("SlashCommand:{userId}:{Method}:{params}", _logger.LogInformation("SlashCommand:{userId}:{Method}:{params}",
Context.Client.CurrentUser.Id, nameof(UserAdd), Context.Interaction.User.Id, nameof(UserAdd),
string.Join(",", new[] { $"{nameof(desiredUid)}:{desiredUid}" })); string.Join(",", new[] { $"{nameof(desiredUid)}:{desiredUid}" }));
await TryRespondAsync(async () => await TryRespondAsync(async () =>
@@ -201,11 +204,51 @@ public class MareModule : InteractionModuleBase
}); });
} }
[SlashCommand("message", "ADMIN ONLY: sends a message to clients")]
public async Task SendMessageToClients([Summary("message", "Message to send")] string message,
[Summary("severity", "Severity of the message")] MareSynchronosShared.Protos.MessageType messageType = MareSynchronosShared.Protos.MessageType.Info,
[Summary("uid", "User ID to the person to send the message to")] string? uid = null)
{
_logger.LogInformation("SlashCommand:{userId}:{Method}:{message}:{type}:{uid}", Context.Interaction.User.Id, nameof(SendMessageToClients), message, messageType, uid);
using var scope = _services.CreateScope();
using var db = scope.ServiceProvider.GetService<MareDbContext>();
if (!(await db.LodeStoneAuth.Include(u => u.User).SingleOrDefaultAsync(a => a.DiscordId == Context.Interaction.User.Id))?.User?.IsAdmin ?? true)
{
await RespondAsync("No permission", ephemeral: true).ConfigureAwait(false);
return;
}
if (!string.IsNullOrEmpty(uid) && !await db.Users.AnyAsync(u => u.UID == uid))
{
await RespondAsync("Specified UID does not exist", ephemeral: true).ConfigureAwait(false);
return;
}
try
{
var client = _grpcClientFactory.CreateClient<ClientMessageService.ClientMessageServiceClient>("MessageClient");
await client.SendClientMessageAsync(new ClientMessage()
{
Message = message,
Type = messageType,
Uid = uid ?? string.Empty
});
await RespondAsync("Message sent", ephemeral: true).ConfigureAwait(false);
}
catch (Exception ex)
{
await RespondAsync("Failed to send message: " + ex.ToString(), ephemeral: true).ConfigureAwait(false);
}
}
[ModalInteraction("recover_modal")] [ModalInteraction("recover_modal")]
public async Task RecoverModal(LodestoneModal modal) public async Task RecoverModal(LodestoneModal modal)
{ {
_logger.LogInformation("Modal:{userId}:{Method}", _logger.LogInformation("Modal:{userId}:{Method}",
Context.Client.CurrentUser.Id, nameof(RecoverModal)); Context.Interaction.User.Id, nameof(RecoverModal));
await TryRespondAsync(async () => await TryRespondAsync(async () =>
{ {
@@ -218,7 +261,7 @@ public class MareModule : InteractionModuleBase
public async Task RegisterModal(LodestoneModal modal) public async Task RegisterModal(LodestoneModal modal)
{ {
_logger.LogInformation("Modal:{userId}:{Method}", _logger.LogInformation("Modal:{userId}:{Method}",
Context.Client.CurrentUser.Id, nameof(RegisterModal)); Context.Interaction.User.Id, nameof(RegisterModal));
await TryRespondAsync(async () => await TryRespondAsync(async () =>
{ {
@@ -231,7 +274,7 @@ public class MareModule : InteractionModuleBase
public async Task RelinkModal(LodestoneModal modal) public async Task RelinkModal(LodestoneModal modal)
{ {
_logger.LogInformation("Modal:{userId}:{Method}", _logger.LogInformation("Modal:{userId}:{Method}",
Context.Client.CurrentUser.Id, nameof(RelinkModal)); Context.Interaction.User.Id, nameof(RelinkModal));
await TryRespondAsync(async () => await TryRespondAsync(async () =>
{ {

View File

@@ -68,6 +68,18 @@ public class Startup
}; };
}); });
services.AddGrpcClient<ClientMessageService.ClientMessageServiceClient>("MessageClient", c =>
{
c.Address = new Uri(mareConfig.GetValue<string>(nameof(ServicesConfiguration.MainServerGrpcAddress)));
}).ConfigureChannel(c =>
{
c.ServiceConfig = new ServiceConfig { MethodConfigs = { noRetryConfig } };
c.HttpHandler = new SocketsHttpHandler()
{
EnableMultipleHttp2Connections = true
};
});
services.Configure<ServicesConfiguration>(Configuration.GetRequiredSection("MareSynchronos")); services.Configure<ServicesConfiguration>(Configuration.GetRequiredSection("MareSynchronos"));
services.Configure<ServerConfiguration>(Configuration.GetRequiredSection("MareSynchronos")); services.Configure<ServerConfiguration>(Configuration.GetRequiredSection("MareSynchronos"));
services.Configure<MareConfigurationAuthBase>(Configuration.GetRequiredSection("MareSynchronos")); services.Configure<MareConfigurationAuthBase>(Configuration.GetRequiredSection("MareSynchronos"));

View File

@@ -1,3 +0,0 @@
namespace MareSynchronosShared.Authentication;
public record SecretKeyAuthReply(bool Success, string? Uid);

View File

@@ -23,6 +23,22 @@ service ConfigurationService {
rpc GetConfigurationEntry (KeyMessage) returns (ValueMessage); rpc GetConfigurationEntry (KeyMessage) returns (ValueMessage);
} }
service ClientMessageService {
rpc SendClientMessage (ClientMessage) returns (Empty);
}
message ClientMessage {
MessageType type = 1;
string message = 2;
string uid = 3;
}
enum MessageType {
INFO = 0;
WARNING = 1;
ERROR = 2;
}
message KeyMessage { message KeyMessage {
string key = 1; string key = 1;
string default = 2; string default = 2;

View File

@@ -1,12 +1,10 @@
using Grpc.Net.Client.Configuration; using Grpc.Net.Client.Configuration;
using Grpc.Net.ClientFactory; using Grpc.Net.ClientFactory;
using MareSynchronosShared.Authentication;
using MareSynchronosShared.Data; using MareSynchronosShared.Data;
using MareSynchronosShared.Metrics; using MareSynchronosShared.Metrics;
using MareSynchronosShared.Protos; using MareSynchronosShared.Protos;
using MareSynchronosShared.Services; using MareSynchronosShared.Services;
using MareSynchronosShared.Utils; using MareSynchronosShared.Utils;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -45,10 +43,6 @@ public class Startup
services.AddSingleton(m => new MareMetrics(m.GetService<ILogger<MareMetrics>>(), new List<string> services.AddSingleton(m => new MareMetrics(m.GetService<ILogger<MareMetrics>>(), new List<string>
{ {
MetricsAPI.CounterAuthenticationCacheHits,
MetricsAPI.CounterAuthenticationFailures,
MetricsAPI.CounterAuthenticationRequests,
MetricsAPI.CounterAuthenticationSuccesses
}, new List<string> }, new List<string>
{ {
MetricsAPI.GaugeFilesTotalSize, MetricsAPI.GaugeFilesTotalSize,
@@ -64,7 +58,6 @@ public class Startup
services.AddHostedService(m => m.GetService<FileStatisticsService>()); services.AddHostedService(m => m.GetService<FileStatisticsService>());
services.AddHostedService<FileCleanupService>(); services.AddHostedService<FileCleanupService>();
services.AddSingleton<SecretKeyAuthenticatorService>();
services.AddDbContextPool<MareDbContext>(options => services.AddDbContextPool<MareDbContext>(options =>
{ {
options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"), builder => options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"), builder =>