From 698a9eddf7fcd3e87dc8a653367422c834cfa502 Mon Sep 17 00:00:00 2001 From: CakeAndBanana Date: Mon, 20 Oct 2025 02:30:40 +0200 Subject: [PATCH] Added new jwt claim for country, Moved models to correct folder instead of inside Lightlesshub.Groups --- .../Controllers/AuthControllerBase.cs | 3 +- .../Services/GeoIPService.cs | 40 ++++++++++++++++++- .../Hubs/LightlessHub.Functions.cs | 4 +- .../Hubs/LightlessHub.Groups.cs | 1 + .../Hubs/LightlessHub.User.cs | 23 ++--------- .../LightlessSyncServer/Hubs/LightlessHub.cs | 2 +- .../Models/BroadcastRedisEntry.cs | 12 ++++++ .../Models/PairingPayload.cs | 8 ++++ .../LightlessSyncServer/Program.cs | 1 - .../Services/SystemInfoService.cs | 3 +- .../Utils/MareClaimTypes.cs | 1 + 11 files changed, 71 insertions(+), 27 deletions(-) create mode 100644 LightlessSyncServer/LightlessSyncServer/Models/BroadcastRedisEntry.cs create mode 100644 LightlessSyncServer/LightlessSyncServer/Models/PairingPayload.cs diff --git a/LightlessSyncServer/LightlessSyncAuthService/Controllers/AuthControllerBase.cs b/LightlessSyncServer/LightlessSyncAuthService/Controllers/AuthControllerBase.cs index b47f38d..27a44a0 100644 --- a/LightlessSyncServer/LightlessSyncAuthService/Controllers/AuthControllerBase.cs +++ b/LightlessSyncServer/LightlessSyncAuthService/Controllers/AuthControllerBase.cs @@ -106,7 +106,8 @@ public abstract class AuthControllerBase : Controller new Claim(LightlessClaimTypes.CharaIdent, charaIdent), new Claim(LightlessClaimTypes.Alias, alias), new Claim(LightlessClaimTypes.Expires, DateTime.UtcNow.AddHours(6).Ticks.ToString(CultureInfo.InvariantCulture)), - new Claim(LightlessClaimTypes.Continent, await _geoIPProvider.GetCountryFromIP(HttpAccessor)) + new Claim(LightlessClaimTypes.Continent, await _geoIPProvider.GetContinentFromIP(HttpAccessor)), + new Claim(LightlessClaimTypes.Country, await _geoIPProvider.GetCountryFromIP(HttpAccessor)), }); return Content(token.RawData); diff --git a/LightlessSyncServer/LightlessSyncAuthService/Services/GeoIPService.cs b/LightlessSyncServer/LightlessSyncAuthService/Services/GeoIPService.cs index 2ea048f..f3b0c4b 100644 --- a/LightlessSyncServer/LightlessSyncAuthService/Services/GeoIPService.cs +++ b/LightlessSyncServer/LightlessSyncAuthService/Services/GeoIPService.cs @@ -2,6 +2,9 @@ using LightlessSyncShared.Services; using LightlessSyncShared.Utils.Configuration; using MaxMind.GeoIP2; +using MaxMind.GeoIP2.Model; +using Microsoft.AspNetCore.Http; +using System.Security.Claims; namespace LightlessSyncAuthService.Services; @@ -23,7 +26,7 @@ public class GeoIPService : IHostedService _lightlessConfiguration = lightlessConfiguration; } - public async Task GetCountryFromIP(IHttpContextAccessor httpContextAccessor) + public async Task GetContinentFromIP(IHttpContextAccessor httpContextAccessor) { if (!_useGeoIP) { @@ -41,6 +44,7 @@ public class GeoIPService : IHostedService if (_dbReader!.TryCity(ip, out var response)) { string? continent = response?.Continent.Code; + if (!string.IsNullOrEmpty(continent) && string.Equals(continent, "NA", StringComparison.Ordinal) && response?.Location.Longitude != null) @@ -140,4 +144,38 @@ public class GeoIPService : IHostedService _dbReader?.Dispose(); return Task.CompletedTask; } + + internal async Task GetCountryFromIP(IHttpContextAccessor httpContextAccessor) + { + if (!_useGeoIP) + { + return "*"; + } + + try + { + var ip = httpContextAccessor.GetIpAddress(); + + using CancellationTokenSource waitCts = new(); + waitCts.CancelAfter(TimeSpan.FromSeconds(5)); + while (_processingReload) await Task.Delay(100, waitCts.Token).ConfigureAwait(false); + + if (_dbReader!.TryCity(ip, out var response)) + { + string? country = response?.Country.IsoCode; + + if (!string.IsNullOrEmpty(country) + && response?.Location.Longitude != null) + { + return country; + } + } + return "*"; + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Error handling Geo IP country in request"); + return "*"; + } + } } diff --git a/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.Functions.cs b/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.Functions.cs index 82e757a..c5fd851 100644 --- a/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.Functions.cs +++ b/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.Functions.cs @@ -1,5 +1,6 @@ using LightlessSync.API.Data; using LightlessSync.API.Dto.Group; +using LightlessSyncServer.Models; using LightlessSyncServer.Utils; using LightlessSyncShared.Metrics; using LightlessSyncShared.Models; @@ -8,7 +9,6 @@ using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using StackExchange.Redis; using System.Text.Json; -using System.Threading; namespace LightlessSyncServer.Hubs; @@ -20,6 +20,8 @@ public partial class LightlessHub public string Continent => Context.User?.Claims?.SingleOrDefault(c => string.Equals(c.Type, LightlessClaimTypes.Continent, StringComparison.Ordinal))?.Value ?? "UNK"; + public string Country => Context.User?.Claims?.SingleOrDefault(c => string.Equals(c.Type, LightlessClaimTypes.Country, StringComparison.Ordinal))?.Value ?? "UNK"; + private async Task DeleteUser(User user) { var ownPairData = await DbContext.ClientPairs.Where(u => u.User.UID == user.UID).ToListAsync().ConfigureAwait(false); diff --git a/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.Groups.cs b/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.Groups.cs index fe6ac8f..ef51ed4 100644 --- a/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.Groups.cs +++ b/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.Groups.cs @@ -3,6 +3,7 @@ using LightlessSync.API.Data.Enum; using LightlessSync.API.Data.Extensions; using LightlessSync.API.Dto.Group; using LightlessSync.API.Dto.User; +using LightlessSyncServer.Models; using LightlessSyncServer.Utils; using LightlessSyncShared.Models; using LightlessSyncShared.Utils; diff --git a/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.User.cs b/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.User.cs index 838118c..6dc3634 100644 --- a/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.User.cs +++ b/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.User.cs @@ -3,6 +3,7 @@ using LightlessSync.API.Data.Enum; using LightlessSync.API.Data.Extensions; using LightlessSync.API.Dto.Group; using LightlessSync.API.Dto.User; +using LightlessSyncServer.Models; using LightlessSyncServer.Utils; using LightlessSyncShared.Metrics; using LightlessSyncShared.Models; @@ -204,8 +205,8 @@ public partial class LightlessHub return; } - var sender = await _pairService.TryAddPairAsync(UserUID, payload.UID); - var receiver = await _pairService.TryAddPairAsync(payload.UID, UserUID); + var sender = await _pairService.TryAddPairAsync(UserUID, payload.UID).ConfigureAwait(false); + var receiver = await _pairService.TryAddPairAsync(payload.UID, UserUID).ConfigureAwait(false); var user = await DbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false); var otherUser = await DbContext.Users.SingleAsync(u => u.UID == payload.UID).ConfigureAwait(false); @@ -304,7 +305,6 @@ public partial class LightlessHub } } - private async Task NotifyBroadcastOwnerOfPairRequest(string targetHashedCid) { var myHashedCid = UserCharaIdent; @@ -360,23 +360,6 @@ public partial class LightlessHub await Clients.User(entry.OwnerUID).Client_ReceiveBroadcastPairRequest(dto).ConfigureAwait(false); } - private class PairingPayload - { - public string UID { get; set; } = string.Empty; - public string HashedCid { get; set; } = string.Empty; - public DateTime Timestamp { get; set; } - } - - public class BroadcastRedisEntry - { - public string HashedCID { get; set; } = string.Empty; - public string OwnerUID { get; set; } = string.Empty; - public string? GID { get; set; } - - public bool OwnedBy(string userUid) => !string.IsNullOrEmpty(userUid) && string.Equals(OwnerUID, userUid, StringComparison.Ordinal); - - public bool HasOwner() => !string.IsNullOrEmpty(OwnerUID); - } [Authorize(Policy = "Identified")] public async Task SetBroadcastStatus(bool enabled, GroupBroadcastRequestDto? groupDto = null) diff --git a/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.cs b/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.cs index 318fc99..69e874a 100644 --- a/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.cs +++ b/LightlessSyncServer/LightlessSyncServer/Hubs/LightlessHub.cs @@ -162,7 +162,7 @@ public partial class LightlessHub : Hub, ILightlessHub else { _lightlessMetrics.IncGaugeWithLabels(MetricsAPI.GaugeConnections, labels: Continent); - + _lightlessMetrics.IncGaugeWithLabels(MetricsAPI.GaugeConnections, labels: Country); try { _logger.LogCallInfo(LightlessHubLogger.Args(_contextAccessor.GetIpAddress(), Context.ConnectionId, UserCharaIdent)); diff --git a/LightlessSyncServer/LightlessSyncServer/Models/BroadcastRedisEntry.cs b/LightlessSyncServer/LightlessSyncServer/Models/BroadcastRedisEntry.cs new file mode 100644 index 0000000..b5fe478 --- /dev/null +++ b/LightlessSyncServer/LightlessSyncServer/Models/BroadcastRedisEntry.cs @@ -0,0 +1,12 @@ +namespace LightlessSyncServer.Models; + +public class BroadcastRedisEntry() +{ + public string? GID { get; set; } + public string HashedCID { get; set; } = string.Empty; + public string OwnerUID { get; set; } = string.Empty; + + public bool OwnedBy(string userUid) => !string.IsNullOrEmpty(userUid) && string.Equals(OwnerUID, userUid, StringComparison.Ordinal); + + public bool HasOwner() => !string.IsNullOrEmpty(OwnerUID); +} \ No newline at end of file diff --git a/LightlessSyncServer/LightlessSyncServer/Models/PairingPayload.cs b/LightlessSyncServer/LightlessSyncServer/Models/PairingPayload.cs new file mode 100644 index 0000000..89cda50 --- /dev/null +++ b/LightlessSyncServer/LightlessSyncServer/Models/PairingPayload.cs @@ -0,0 +1,8 @@ +namespace LightlessSyncServer.Models; + +public class PairingPayload +{ + public string UID { get; set; } = string.Empty; + public string HashedCid { get; set; } = string.Empty; + public DateTime Timestamp { get; set; } +} \ No newline at end of file diff --git a/LightlessSyncServer/LightlessSyncServer/Program.cs b/LightlessSyncServer/LightlessSyncServer/Program.cs index 426d520..d55e08a 100644 --- a/LightlessSyncServer/LightlessSyncServer/Program.cs +++ b/LightlessSyncServer/LightlessSyncServer/Program.cs @@ -41,7 +41,6 @@ public class Program metrics.SetGaugeTo(MetricsAPI.GaugeUsersRegistered, context.Users.AsNoTracking().Count()); metrics.SetGaugeTo(MetricsAPI.GaugePairs, context.ClientPairs.AsNoTracking().Count()); metrics.SetGaugeTo(MetricsAPI.GaugePairsPaused, context.Permissions.AsNoTracking().Where(p=>p.IsPaused).Count()); - } if (args.Length == 0 || !string.Equals(args[0], "dry", StringComparison.Ordinal)) diff --git a/LightlessSyncServer/LightlessSyncServer/Services/SystemInfoService.cs b/LightlessSyncServer/LightlessSyncServer/Services/SystemInfoService.cs index 255fdb0..8778613 100644 --- a/LightlessSyncServer/LightlessSyncServer/Services/SystemInfoService.cs +++ b/LightlessSyncServer/LightlessSyncServer/Services/SystemInfoService.cs @@ -1,15 +1,14 @@ using LightlessSync.API.Dto; using LightlessSync.API.SignalR; using LightlessSyncServer.Hubs; +using LightlessSyncServer.Models; using LightlessSyncShared.Data; using LightlessSyncShared.Metrics; using LightlessSyncShared.Services; using LightlessSyncShared.Utils.Configuration; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; -using StackExchange.Redis; using StackExchange.Redis.Extensions.Core.Abstractions; -using static LightlessSyncServer.Hubs.LightlessHub; namespace LightlessSyncServer.Services; diff --git a/LightlessSyncServer/LightlessSyncShared/Utils/MareClaimTypes.cs b/LightlessSyncServer/LightlessSyncShared/Utils/MareClaimTypes.cs index f832cfa..2c172da 100644 --- a/LightlessSyncServer/LightlessSyncShared/Utils/MareClaimTypes.cs +++ b/LightlessSyncServer/LightlessSyncShared/Utils/MareClaimTypes.cs @@ -8,6 +8,7 @@ public static class LightlessClaimTypes public const string Internal = "internal"; public const string Expires = "expiration_date"; public const string Continent = "continent"; + public const string Country = "country"; public const string DiscordUser = "discord_user"; public const string DiscordId = "discord_user_id"; public const string OAuthLoginToken = "oauth_login_token";