Use HWIDs for bans.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using Content.Server.Database;
|
||||
using Content.Shared.Administration;
|
||||
@@ -51,14 +53,16 @@ namespace Content.Server.Administration.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
var resolvedUid = await locator.LookupIdByNameOrIdAsync(target);
|
||||
if (resolvedUid == null)
|
||||
var located = await locator.LookupIdByNameOrIdAsync(target);
|
||||
if (located == null)
|
||||
{
|
||||
shell.WriteError("Unable to find a player with that name.");
|
||||
return;
|
||||
}
|
||||
|
||||
var targetUid = resolvedUid.Value;
|
||||
var targetUid = located.UserId;
|
||||
var targetHWid = located.LastHWId;
|
||||
var targetAddr = located.LastAddress;
|
||||
|
||||
if (player != null && player.UserId == targetUid)
|
||||
{
|
||||
@@ -72,7 +76,29 @@ namespace Content.Server.Administration.Commands
|
||||
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes);
|
||||
}
|
||||
|
||||
await dbMan.AddServerBanAsync(new ServerBanDef(null, targetUid, null, DateTimeOffset.Now, expires, reason, player?.UserId, null));
|
||||
(IPAddress, int)? addrRange = null;
|
||||
if (targetAddr != null)
|
||||
{
|
||||
if (targetAddr.IsIPv4MappedToIPv6)
|
||||
targetAddr = targetAddr.MapToIPv4();
|
||||
|
||||
// Ban /64 for IPv4, /32 for IPv4.
|
||||
var cidr = targetAddr.AddressFamily == AddressFamily.InterNetworkV6 ? 64 : 32;
|
||||
addrRange = (targetAddr, cidr);
|
||||
}
|
||||
|
||||
var banDef = new ServerBanDef(
|
||||
null,
|
||||
targetUid,
|
||||
addrRange,
|
||||
targetHWid,
|
||||
DateTimeOffset.Now,
|
||||
expires,
|
||||
reason,
|
||||
player?.UserId,
|
||||
null);
|
||||
|
||||
await dbMan.AddServerBanAsync(banDef);
|
||||
|
||||
var response = new StringBuilder($"Banned {target} with reason \"{reason}\"");
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Content.Server.Administration.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
var bans = await dbMan.GetServerBansAsync(null, targetUid);
|
||||
var bans = await dbMan.GetServerBansAsync(null, targetUid, null);
|
||||
|
||||
if (bans.Count == 0)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
@@ -15,6 +16,8 @@ using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Server.Administration
|
||||
{
|
||||
public sealed record LocatedPlayerData(NetUserId UserId, IPAddress? LastAddress, ImmutableArray<byte>? LastHWId);
|
||||
|
||||
/// <summary>
|
||||
/// Utilities for finding user IDs that extend to more than the server database.
|
||||
/// </summary>
|
||||
@@ -28,19 +31,13 @@ namespace Content.Server.Administration
|
||||
/// Look up a user ID by name globally.
|
||||
/// </summary>
|
||||
/// <returns>Null if the player does not exist.</returns>
|
||||
Task<NetUserId?> LookupIdByNameAsync(string playerName, CancellationToken cancel = default);
|
||||
Task<LocatedPlayerData?> LookupIdByNameAsync(string playerName, CancellationToken cancel = default);
|
||||
|
||||
/// <summary>
|
||||
/// If passed a GUID, runs <see cref="DoesPlayerExistAsync"/> and only returns it if the account exists.
|
||||
/// If passed a GUID, looks up the ID and tries to find HWId for it.
|
||||
/// If passed a player name, returns <see cref="LookupIdByNameAsync"/>.
|
||||
/// </summary>
|
||||
Task<NetUserId?> LookupIdByNameOrIdAsync(string playerName, CancellationToken cancel = default);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the specified user ID is an existing account, globally.
|
||||
/// </summary>
|
||||
/// <returns>True if the player account exists, false otherwise</returns>
|
||||
Task<bool> DoesPlayerExistAsync(NetUserId userId, CancellationToken cancel = default);
|
||||
Task<LocatedPlayerData?> LookupIdByNameOrIdAsync(string playerName, CancellationToken cancel = default);
|
||||
}
|
||||
|
||||
internal sealed class PlayerLocator : IPlayerLocator
|
||||
@@ -49,22 +46,27 @@ namespace Content.Server.Administration
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
|
||||
public async Task<NetUserId?> LookupIdByNameAsync(string playerName, CancellationToken cancel = default)
|
||||
public async Task<LocatedPlayerData?> LookupIdByNameAsync(string playerName, CancellationToken cancel = default)
|
||||
{
|
||||
// Check people currently on the server, the easiest case.
|
||||
if (_playerManager.TryGetSessionByUsername(playerName, out var session))
|
||||
return session.UserId;
|
||||
{
|
||||
var userId = session.UserId;
|
||||
var address = session.ConnectedClient.RemoteEndPoint.Address;
|
||||
var hwId = session.ConnectedClient.UserData.HWId;
|
||||
return new LocatedPlayerData(userId, address, hwId);
|
||||
}
|
||||
|
||||
// Check database for past players.
|
||||
var record = await _db.GetPlayerRecordByUserName(playerName, cancel);
|
||||
if (record != null)
|
||||
return record.UserId;
|
||||
return new LocatedPlayerData(record.UserId, record.LastSeenAddress, record.HWId);
|
||||
|
||||
// If all else fails, ask the auth server.
|
||||
var client = new HttpClient();
|
||||
var authServer = _configurationManager.GetCVar(CVars.AuthServer);
|
||||
var resp = await client.GetAsync($"{authServer}api/query/name?name={WebUtility.UrlEncode(playerName)}",
|
||||
cancel);
|
||||
var requestUri = $"{authServer}api/query/name?name={WebUtility.UrlEncode(playerName)}";
|
||||
using var resp = await client.GetAsync(requestUri, cancel);
|
||||
|
||||
if (resp.StatusCode == HttpStatusCode.NotFound)
|
||||
return null;
|
||||
@@ -83,45 +85,49 @@ namespace Content.Server.Administration
|
||||
return null;
|
||||
}
|
||||
|
||||
return new NetUserId(responseData.UserId);
|
||||
return new LocatedPlayerData(new NetUserId(responseData.UserId), null, null);
|
||||
}
|
||||
|
||||
public async Task<bool> DoesPlayerExistAsync(NetUserId userId, CancellationToken cancel = default)
|
||||
public async Task<LocatedPlayerData?> LookupIdAsync(NetUserId userId, CancellationToken cancel = default)
|
||||
{
|
||||
// Check people currently on the server, the easiest case.
|
||||
if (_playerManager.ValidSessionId(userId))
|
||||
return true;
|
||||
if (_playerManager.TryGetSessionById(userId, out var session))
|
||||
{
|
||||
var address = session.ConnectedClient.RemoteEndPoint.Address;
|
||||
var hwId = session.ConnectedClient.UserData.HWId;
|
||||
return new LocatedPlayerData(userId, address, hwId);
|
||||
}
|
||||
|
||||
// Check database for past players.
|
||||
var record = await _db.GetPlayerRecordByUserId(userId, cancel);
|
||||
if (record != null)
|
||||
return true;
|
||||
return new LocatedPlayerData(record.UserId, record.LastSeenAddress, record.HWId);
|
||||
|
||||
// If all else fails, ask the auth server.
|
||||
var client = new HttpClient();
|
||||
var authServer = _configurationManager.GetCVar(CVars.AuthServer);
|
||||
var requestUri = $"{authServer}api/query/userid?userid={WebUtility.UrlEncode(userId.UserId.ToString())}";
|
||||
var resp = await client.SendAsync(new HttpRequestMessage(HttpMethod.Head, requestUri), cancel);
|
||||
using var resp = await client.SendAsync(new HttpRequestMessage(HttpMethod.Head, requestUri), cancel);
|
||||
|
||||
if (resp.StatusCode == HttpStatusCode.NotFound)
|
||||
return false;
|
||||
return null;
|
||||
|
||||
if (!resp.IsSuccessStatusCode)
|
||||
{
|
||||
Logger.ErrorS("PlayerLocate", "Auth server returned bad response {StatusCode}!", resp.StatusCode);
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
return true;
|
||||
return new LocatedPlayerData(userId, null, null);
|
||||
}
|
||||
|
||||
public async Task<NetUserId?> LookupIdByNameOrIdAsync(string playerName, CancellationToken cancel = default)
|
||||
public async Task<LocatedPlayerData?> LookupIdByNameOrIdAsync(string playerName, CancellationToken cancel = default)
|
||||
{
|
||||
if (Guid.TryParse(playerName, out var guid))
|
||||
{
|
||||
var userId = new NetUserId(guid);
|
||||
|
||||
return await DoesPlayerExistAsync(userId, cancel) ? userId : null;
|
||||
return await LookupIdAsync(userId, cancel);
|
||||
}
|
||||
|
||||
return await LookupIdByNameAsync(playerName, cancel);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.Preferences;
|
||||
@@ -61,7 +62,14 @@ The ban reason is: ""{ban.Reason}""
|
||||
// Check if banned.
|
||||
var addr = e.IP.Address;
|
||||
var userId = e.UserId;
|
||||
var ban = await _db.GetServerBanAsync(addr, userId);
|
||||
ImmutableArray<byte>? hwId = e.UserData.HWId;
|
||||
if (hwId.Value.Length == 0)
|
||||
{
|
||||
// HWId not available for user's platform, don't look it up.
|
||||
hwId = null;
|
||||
}
|
||||
|
||||
var ban = await _db.GetServerBanAsync(addr, userId, hwId);
|
||||
if (ban != null)
|
||||
{
|
||||
var expires = "This is a permanent ban.";
|
||||
@@ -83,8 +91,8 @@ The ban reason is: ""{ban.Reason}""
|
||||
return;
|
||||
}
|
||||
|
||||
await _db.UpdatePlayerRecordAsync(userId, e.UserName, addr);
|
||||
await _db.AddConnectionLogAsync(userId, e.UserName, addr);
|
||||
await _db.UpdatePlayerRecordAsync(userId, e.UserName, addr, e.UserData.HWId);
|
||||
await _db.AddConnectionLogAsync(userId, e.UserName, addr, e.UserData.HWId);
|
||||
}
|
||||
|
||||
private async Task<NetUserId?> AssignUserIdCallback(string name)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Net;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
@@ -7,6 +8,7 @@ namespace Content.Server.Database
|
||||
public sealed class PlayerRecord
|
||||
{
|
||||
public NetUserId UserId { get; }
|
||||
public ImmutableArray<byte>? HWId { get; }
|
||||
public DateTimeOffset FirstSeenTime { get; }
|
||||
public string LastSeenUserName { get; }
|
||||
public DateTimeOffset LastSeenTime { get; }
|
||||
@@ -17,13 +19,15 @@ namespace Content.Server.Database
|
||||
DateTimeOffset firstSeenTime,
|
||||
string lastSeenUserName,
|
||||
DateTimeOffset lastSeenTime,
|
||||
IPAddress lastSeenAddress)
|
||||
IPAddress lastSeenAddress,
|
||||
ImmutableArray<byte>? hwId)
|
||||
{
|
||||
UserId = userId;
|
||||
FirstSeenTime = firstSeenTime;
|
||||
LastSeenUserName = lastSeenUserName;
|
||||
LastSeenTime = lastSeenTime;
|
||||
LastSeenAddress = lastSeenAddress;
|
||||
HWId = hwId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Net;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
@@ -11,6 +12,7 @@ namespace Content.Server.Database
|
||||
public int? Id { get; }
|
||||
public NetUserId? UserId { get; }
|
||||
public (IPAddress address, int cidrMask)? Address { get; }
|
||||
public ImmutableArray<byte>? HWId { get; }
|
||||
|
||||
public DateTimeOffset BanTime { get; }
|
||||
public DateTimeOffset? ExpirationTime { get; }
|
||||
@@ -22,15 +24,16 @@ namespace Content.Server.Database
|
||||
int? id,
|
||||
NetUserId? userId,
|
||||
(IPAddress, int)? address,
|
||||
ImmutableArray<byte>? hwId,
|
||||
DateTimeOffset banTime,
|
||||
DateTimeOffset? expirationTime,
|
||||
string reason,
|
||||
NetUserId? banningAdmin,
|
||||
ServerUnbanDef? unban)
|
||||
{
|
||||
if (userId == null && address == null)
|
||||
if (userId == null && address == null && hwId == null)
|
||||
{
|
||||
throw new ArgumentException("Must have a banned user, banned address, or both.");
|
||||
throw new ArgumentException("Must have at least one of banned user, banned address or hardware ID");
|
||||
}
|
||||
|
||||
if (address is {} addr && addr.Item1.IsIPv4MappedToIPv6)
|
||||
@@ -43,6 +46,7 @@ namespace Content.Server.Database
|
||||
Id = id;
|
||||
UserId = userId;
|
||||
Address = address;
|
||||
HWId = hwId;
|
||||
BanTime = banTime;
|
||||
ExpirationTime = expirationTime;
|
||||
Reason = reason;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
@@ -261,8 +262,12 @@ namespace Content.Server.Database
|
||||
/// </summary>
|
||||
/// <param name="address">The ip address of the user.</param>
|
||||
/// <param name="userId">The id of the user.</param>
|
||||
/// <param name="hwId">The HWId of the user.</param>
|
||||
/// <returns>The user's latest received un-pardoned ban, or null if none exist.</returns>
|
||||
public abstract Task<ServerBanDef?> GetServerBanAsync(IPAddress? address, NetUserId? userId);
|
||||
public abstract Task<ServerBanDef?> GetServerBanAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId);
|
||||
|
||||
/// <summary>
|
||||
/// Looks up an user's ban history.
|
||||
@@ -271,8 +276,12 @@ namespace Content.Server.Database
|
||||
/// </summary>
|
||||
/// <param name="address">The ip address of the user.</param>
|
||||
/// <param name="userId">The id of the user.</param>
|
||||
/// <param name="hwId">The HWId of the user.</param>
|
||||
/// <returns>The user's ban history.</returns>
|
||||
public abstract Task<List<ServerBanDef>> GetServerBansAsync(IPAddress? address, NetUserId? userId);
|
||||
public abstract Task<List<ServerBanDef>> GetServerBansAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId);
|
||||
|
||||
public abstract Task AddServerBanAsync(ServerBanDef serverBan);
|
||||
public abstract Task AddServerUnbanAsync(ServerUnbanDef serverUnban);
|
||||
@@ -280,14 +289,22 @@ namespace Content.Server.Database
|
||||
/*
|
||||
* PLAYER RECORDS
|
||||
*/
|
||||
public abstract Task UpdatePlayerRecord(NetUserId userId, string userName, IPAddress address);
|
||||
public abstract Task UpdatePlayerRecord(
|
||||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId);
|
||||
public abstract Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel);
|
||||
public abstract Task<PlayerRecord?> GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel);
|
||||
|
||||
/*
|
||||
* CONNECTION LOG
|
||||
*/
|
||||
public abstract Task AddConnectionLogAsync(NetUserId userId, string userName, IPAddress address);
|
||||
public abstract Task AddConnectionLogAsync(
|
||||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId);
|
||||
|
||||
/*
|
||||
* ADMIN STUFF
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
@@ -59,8 +60,12 @@ namespace Content.Server.Database
|
||||
/// </summary>
|
||||
/// <param name="address">The ip address of the user.</param>
|
||||
/// <param name="userId">The id of the user.</param>
|
||||
/// <param name="hwId">The hardware ID of the user.</param>
|
||||
/// <returns>The user's latest received un-pardoned ban, or null if none exist.</returns>
|
||||
Task<ServerBanDef?> GetServerBanAsync(IPAddress? address, NetUserId? userId);
|
||||
Task<ServerBanDef?> GetServerBanAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId);
|
||||
|
||||
/// <summary>
|
||||
/// Looks up an user's ban history.
|
||||
@@ -69,19 +74,31 @@ namespace Content.Server.Database
|
||||
/// </summary>
|
||||
/// <param name="address">The ip address of the user.</param>
|
||||
/// <param name="userId">The id of the user.</param>
|
||||
/// <param name="hwId">The HWId of the user.</param>
|
||||
/// <returns>The user's ban history.</returns>
|
||||
Task<List<ServerBanDef>> GetServerBansAsync(IPAddress? address, NetUserId? userId);
|
||||
Task<List<ServerBanDef>> GetServerBansAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId);
|
||||
|
||||
Task AddServerBanAsync(ServerBanDef serverBan);
|
||||
Task AddServerUnbanAsync(ServerUnbanDef serverBan);
|
||||
|
||||
// Player records
|
||||
Task UpdatePlayerRecordAsync(NetUserId userId, string userName, IPAddress address);
|
||||
Task UpdatePlayerRecordAsync(
|
||||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId);
|
||||
Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel = default);
|
||||
Task<PlayerRecord?> GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel = default);
|
||||
|
||||
// Connection log
|
||||
Task AddConnectionLogAsync(NetUserId userId, string userName, IPAddress address);
|
||||
Task AddConnectionLogAsync(
|
||||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId);
|
||||
|
||||
// Admins
|
||||
Task<Admin?> GetAdminDataForAsync(NetUserId userId, CancellationToken cancel = default);
|
||||
@@ -179,14 +196,20 @@ namespace Content.Server.Database
|
||||
return _db.GetServerBanAsync(id);
|
||||
}
|
||||
|
||||
public Task<ServerBanDef?> GetServerBanAsync(IPAddress? address, NetUserId? userId)
|
||||
public Task<ServerBanDef?> GetServerBanAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId)
|
||||
{
|
||||
return _db.GetServerBanAsync(address, userId);
|
||||
return _db.GetServerBanAsync(address, userId, hwId);
|
||||
}
|
||||
|
||||
public Task<List<ServerBanDef>> GetServerBansAsync(IPAddress? address, NetUserId? userId)
|
||||
public Task<List<ServerBanDef>> GetServerBansAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId)
|
||||
{
|
||||
return _db.GetServerBansAsync(address, userId);
|
||||
return _db.GetServerBansAsync(address, userId, hwId);
|
||||
}
|
||||
|
||||
public Task AddServerBanAsync(ServerBanDef serverBan)
|
||||
@@ -199,9 +222,13 @@ namespace Content.Server.Database
|
||||
return _db.AddServerUnbanAsync(serverUnban);
|
||||
}
|
||||
|
||||
public Task UpdatePlayerRecordAsync(NetUserId userId, string userName, IPAddress address)
|
||||
public Task UpdatePlayerRecordAsync(
|
||||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId)
|
||||
{
|
||||
return _db.UpdatePlayerRecord(userId, userName, address);
|
||||
return _db.UpdatePlayerRecord(userId, userName, address, hwId);
|
||||
}
|
||||
|
||||
public Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel = default)
|
||||
@@ -214,9 +241,13 @@ namespace Content.Server.Database
|
||||
return _db.GetPlayerRecordByUserId(userId, cancel);
|
||||
}
|
||||
|
||||
public Task AddConnectionLogAsync(NetUserId userId, string userName, IPAddress address)
|
||||
public Task AddConnectionLogAsync(
|
||||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId)
|
||||
{
|
||||
return _db.AddConnectionLogAsync(userId, userName, address);
|
||||
return _db.AddConnectionLogAsync(userId, userName, address, hwId);
|
||||
}
|
||||
|
||||
public Task<Admin?> GetAdminDataForAsync(NetUserId userId, CancellationToken cancel = default)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -48,7 +49,10 @@ namespace Content.Server.Database
|
||||
return ConvertBan(ban);
|
||||
}
|
||||
|
||||
public override async Task<ServerBanDef?> GetServerBanAsync(IPAddress? address, NetUserId? userId)
|
||||
public override async Task<ServerBanDef?> GetServerBanAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId)
|
||||
{
|
||||
if (address == null && userId == null)
|
||||
{
|
||||
@@ -57,73 +61,31 @@ namespace Content.Server.Database
|
||||
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
var query = db.PgDbContext.Ban
|
||||
.Include(p => p.Unban)
|
||||
.Where(p => p.Unban == null && (p.ExpirationTime == null || p.ExpirationTime.Value > DateTime.Now));
|
||||
|
||||
if (userId is { } uid)
|
||||
{
|
||||
if (address == null)
|
||||
{
|
||||
// Only have a user ID.
|
||||
query = query.Where(p => p.UserId == uid.UserId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Have both user ID and IP address.
|
||||
query = query.Where(p =>
|
||||
(p.Address != null && EF.Functions.ContainsOrEqual(p.Address.Value, address))
|
||||
|| p.UserId == uid.UserId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only have a connecting address.
|
||||
query = query.Where(
|
||||
p => p.Address != null && EF.Functions.ContainsOrEqual(p.Address.Value, address));
|
||||
}
|
||||
var query = MakeBanLookupQuery(address, userId, hwId, db)
|
||||
.Where(p => p.Unban == null && (p.ExpirationTime == null || p.ExpirationTime.Value > DateTime.Now))
|
||||
.OrderByDescending(b => b.BanTime);
|
||||
|
||||
var ban = await query.FirstOrDefaultAsync();
|
||||
|
||||
return ConvertBan(ban);
|
||||
}
|
||||
|
||||
public override async Task<List<ServerBanDef>> GetServerBansAsync(IPAddress? address, NetUserId? userId)
|
||||
public override async Task<List<ServerBanDef>> GetServerBansAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId)
|
||||
{
|
||||
if (address == null && userId == null)
|
||||
if (address == null && userId == null && hwId == null)
|
||||
{
|
||||
throw new ArgumentException("Address and userId cannot both be null");
|
||||
}
|
||||
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
var query = db.PgDbContext.Ban
|
||||
.Include(p => p.Unban).AsQueryable();
|
||||
|
||||
if (userId is { } uid)
|
||||
{
|
||||
if (address == null)
|
||||
{
|
||||
// Only have a user ID.
|
||||
query = query.Where(p => p.UserId == uid.UserId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Have both user ID and IP address.
|
||||
query = query.Where(p =>
|
||||
(p.Address != null && EF.Functions.ContainsOrEqual(p.Address.Value, address))
|
||||
|| p.UserId == uid.UserId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only have a connecting address.
|
||||
query = query.Where(
|
||||
p => p.Address != null && EF.Functions.ContainsOrEqual(p.Address.Value, address));
|
||||
}
|
||||
var query = MakeBanLookupQuery(address, userId, hwId, db);
|
||||
|
||||
var queryBans = await query.ToArrayAsync();
|
||||
var bans = new List<ServerBanDef>();
|
||||
var bans = new List<ServerBanDef>(queryBans.Length);
|
||||
|
||||
foreach (var ban in queryBans)
|
||||
{
|
||||
@@ -138,6 +100,45 @@ namespace Content.Server.Database
|
||||
return bans;
|
||||
}
|
||||
|
||||
private static IQueryable<PostgresServerBan> MakeBanLookupQuery(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
DbGuardImpl db)
|
||||
{
|
||||
IQueryable<PostgresServerBan>? query = null;
|
||||
|
||||
if (userId is { } uid)
|
||||
{
|
||||
var newQ = db.PgDbContext.Ban
|
||||
.Include(p => p.Unban)
|
||||
.Where(b => b.UserId == uid.UserId);
|
||||
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
|
||||
if (address != null)
|
||||
{
|
||||
var newQ = db.PgDbContext.Ban
|
||||
.Include(p => p.Unban)
|
||||
.Where(b => b.Address != null && EF.Functions.ContainsOrEqual(b.Address.Value, address));
|
||||
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
|
||||
if (hwId != null)
|
||||
{
|
||||
var newQ = db.PgDbContext.Ban
|
||||
.Include(p => p.Unban)
|
||||
.Where(b => b.HWId!.SequenceEqual(hwId));
|
||||
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
|
||||
query = query!.Distinct();
|
||||
return query;
|
||||
}
|
||||
|
||||
private static ServerBanDef? ConvertBan(PostgresServerBan? ban)
|
||||
{
|
||||
if (ban == null)
|
||||
@@ -163,6 +164,7 @@ namespace Content.Server.Database
|
||||
ban.Id,
|
||||
uid,
|
||||
ban.Address,
|
||||
ban.HWId == null ? null : ImmutableArray.Create(ban.HWId),
|
||||
ban.BanTime,
|
||||
ban.ExpirationTime,
|
||||
ban.Reason,
|
||||
@@ -196,6 +198,7 @@ namespace Content.Server.Database
|
||||
db.PgDbContext.Ban.Add(new PostgresServerBan
|
||||
{
|
||||
Address = serverBan.Address,
|
||||
HWId = serverBan.HWId?.ToArray(),
|
||||
Reason = serverBan.Reason,
|
||||
BanningAdmin = serverBan.BanningAdmin?.UserId,
|
||||
BanTime = serverBan.BanTime.UtcDateTime,
|
||||
@@ -220,7 +223,11 @@ namespace Content.Server.Database
|
||||
await db.PgDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public override async Task UpdatePlayerRecord(NetUserId userId, string userName, IPAddress address)
|
||||
public override async Task UpdatePlayerRecord(
|
||||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
@@ -237,6 +244,7 @@ namespace Content.Server.Database
|
||||
record.LastSeenTime = DateTime.UtcNow;
|
||||
record.LastSeenAddress = address;
|
||||
record.LastSeenUserName = userName;
|
||||
record.LastSeenHWId = hwId.ToArray();
|
||||
|
||||
await db.PgDbContext.SaveChangesAsync();
|
||||
}
|
||||
@@ -277,10 +285,15 @@ namespace Content.Server.Database
|
||||
new DateTimeOffset(record.FirstSeenTime),
|
||||
record.LastSeenUserName,
|
||||
new DateTimeOffset(record.LastSeenTime),
|
||||
record.LastSeenAddress);
|
||||
record.LastSeenAddress,
|
||||
record.LastSeenHWId?.ToImmutableArray());
|
||||
}
|
||||
|
||||
public override async Task AddConnectionLogAsync(NetUserId userId, string userName, IPAddress address)
|
||||
public override async Task AddConnectionLogAsync(
|
||||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
@@ -289,7 +302,8 @@ namespace Content.Server.Database
|
||||
Address = address,
|
||||
Time = DateTime.UtcNow,
|
||||
UserId = userId.UserId,
|
||||
UserName = userName
|
||||
UserName = userName,
|
||||
HWId = hwId.ToArray()
|
||||
});
|
||||
|
||||
await db.PgDbContext.SaveChangesAsync();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -58,7 +59,10 @@ namespace Content.Server.Database
|
||||
return ConvertBan(ban);
|
||||
}
|
||||
|
||||
public override async Task<ServerBanDef?> GetServerBanAsync(IPAddress? address, NetUserId? userId)
|
||||
public override async Task<ServerBanDef?> GetServerBanAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
@@ -69,23 +73,15 @@ namespace Content.Server.Database
|
||||
.Where(p => p.Unban == null && (p.ExpirationTime == null || p.ExpirationTime.Value > DateTime.UtcNow))
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var ban in bans)
|
||||
{
|
||||
if (address != null && ban.Address != null && address.IsInSubnet(ban.Address))
|
||||
{
|
||||
return ConvertBan(ban);
|
||||
}
|
||||
|
||||
if (userId is { } id && ban.UserId == id.UserId)
|
||||
{
|
||||
return ConvertBan(ban);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return bans.FirstOrDefault(b => BanMatches(b, address, userId, hwId)) is { } foundBan
|
||||
? ConvertBan(foundBan)
|
||||
: null;
|
||||
}
|
||||
|
||||
public override async Task<List<ServerBanDef>> GetServerBansAsync(IPAddress? address, NetUserId? userId)
|
||||
public override async Task<List<ServerBanDef>> GetServerBansAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
@@ -95,30 +91,34 @@ namespace Content.Server.Database
|
||||
.Include(p => p.Unban)
|
||||
.ToListAsync();
|
||||
|
||||
var bans = new List<ServerBanDef>();
|
||||
return queryBans
|
||||
.Where(b => BanMatches(b, address, userId, hwId))
|
||||
.Select(ConvertBan)
|
||||
.ToList()!;
|
||||
}
|
||||
|
||||
foreach (var ban in queryBans)
|
||||
private static bool BanMatches(
|
||||
SqliteServerBan ban,
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId)
|
||||
{
|
||||
if (address != null && ban.Address != null && address.IsInSubnet(ban.Address))
|
||||
{
|
||||
ServerBanDef? banDef = null;
|
||||
|
||||
if (address != null && ban.Address != null && address.IsInSubnet(ban.Address))
|
||||
{
|
||||
banDef = ConvertBan(ban);
|
||||
}
|
||||
else if (userId is { } id && ban.UserId == id.UserId)
|
||||
{
|
||||
banDef = ConvertBan(ban);
|
||||
}
|
||||
|
||||
if (banDef == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bans.Add(banDef);
|
||||
return true;
|
||||
}
|
||||
|
||||
return bans;
|
||||
if (userId is { } id && ban.UserId == id.UserId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hwId is { } hwIdVar && hwIdVar.AsSpan().SequenceEqual(ban.HWId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override async Task AddServerBanAsync(ServerBanDef serverBan)
|
||||
@@ -136,6 +136,7 @@ namespace Content.Server.Database
|
||||
Address = addrStr,
|
||||
Reason = serverBan.Reason,
|
||||
BanningAdmin = serverBan.BanningAdmin?.UserId,
|
||||
HWId = serverBan.HWId?.ToArray(),
|
||||
BanTime = serverBan.BanTime.UtcDateTime,
|
||||
ExpirationTime = serverBan.ExpirationTime?.UtcDateTime,
|
||||
UserId = serverBan.UserId?.UserId
|
||||
@@ -158,7 +159,11 @@ namespace Content.Server.Database
|
||||
await db.SqliteDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public override async Task UpdatePlayerRecord(NetUserId userId, string userName, IPAddress address)
|
||||
public override async Task UpdatePlayerRecord(
|
||||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
@@ -175,6 +180,7 @@ namespace Content.Server.Database
|
||||
record.LastSeenTime = DateTime.UtcNow;
|
||||
record.LastSeenAddress = address.ToString();
|
||||
record.LastSeenUserName = userName;
|
||||
record.LastSeenHWId = hwId.ToArray();
|
||||
|
||||
await db.SqliteDbContext.SaveChangesAsync();
|
||||
}
|
||||
@@ -215,8 +221,10 @@ namespace Content.Server.Database
|
||||
new DateTimeOffset(record.FirstSeenTime, TimeSpan.Zero),
|
||||
record.LastSeenUserName,
|
||||
new DateTimeOffset(record.LastSeenTime, TimeSpan.Zero),
|
||||
IPAddress.Parse(record.LastSeenAddress));
|
||||
IPAddress.Parse(record.LastSeenAddress),
|
||||
record.LastSeenHWId?.ToImmutableArray());
|
||||
}
|
||||
|
||||
private static ServerBanDef? ConvertBan(SqliteServerBan? ban)
|
||||
{
|
||||
if (ban == null)
|
||||
@@ -225,13 +233,13 @@ namespace Content.Server.Database
|
||||
}
|
||||
|
||||
NetUserId? uid = null;
|
||||
if (ban.UserId is {} guid)
|
||||
if (ban.UserId is { } guid)
|
||||
{
|
||||
uid = new NetUserId(guid);
|
||||
}
|
||||
|
||||
NetUserId? aUid = null;
|
||||
if (ban.BanningAdmin is {} aGuid)
|
||||
if (ban.BanningAdmin is { } aGuid)
|
||||
{
|
||||
aUid = new NetUserId(aGuid);
|
||||
}
|
||||
@@ -250,6 +258,7 @@ namespace Content.Server.Database
|
||||
ban.Id,
|
||||
uid,
|
||||
addrTuple,
|
||||
ban.HWId == null ? null : ImmutableArray.Create(ban.HWId),
|
||||
ban.BanTime,
|
||||
ban.ExpirationTime,
|
||||
ban.Reason,
|
||||
@@ -265,7 +274,7 @@ namespace Content.Server.Database
|
||||
}
|
||||
|
||||
NetUserId? aUid = null;
|
||||
if (unban.UnbanningAdmin is {} aGuid)
|
||||
if (unban.UnbanningAdmin is { } aGuid)
|
||||
{
|
||||
aUid = new NetUserId(aGuid);
|
||||
}
|
||||
@@ -276,7 +285,8 @@ namespace Content.Server.Database
|
||||
unban.UnbanTime);
|
||||
}
|
||||
|
||||
public override async Task AddConnectionLogAsync(NetUserId userId, string userName, IPAddress address)
|
||||
public override async Task AddConnectionLogAsync(NetUserId userId, string userName, IPAddress address,
|
||||
ImmutableArray<byte> hwId)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
@@ -285,7 +295,8 @@ namespace Content.Server.Database
|
||||
Address = address.ToString(),
|
||||
Time = DateTime.UtcNow,
|
||||
UserId = userId.UserId,
|
||||
UserName = userName
|
||||
UserName = userName,
|
||||
HWId = hwId.ToArray()
|
||||
});
|
||||
|
||||
await db.SqliteDbContext.SaveChangesAsync();
|
||||
|
||||
Reference in New Issue
Block a user