You can now ban people that aren't on the server. Or that have never JOINED the server in the first place. Amazing!

This commit is contained in:
Pieter-Jan Briers
2021-03-08 02:19:19 +01:00
parent cf48e3a2d8
commit 1818b561f8
3 changed files with 144 additions and 15 deletions

View File

@@ -5,7 +5,6 @@ using Content.Shared.Administration;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Network;
#nullable enable #nullable enable
@@ -16,12 +15,13 @@ namespace Content.Server.Administration.Commands
{ {
public string Command => "ban"; public string Command => "ban";
public string Description => "Bans somebody"; public string Description => "Bans somebody";
public string Help => $"Usage: {Command} <name or user ID> <reason> <duration in minutes, or 0 for permanent ban>"; public string Help => $"Usage: {Command} <name or user ID> <reason> [duration in minutes, leave out or 0 for permanent ban]";
public async void Execute(IConsoleShell shell, string argStr, string[] args) public async void Execute(IConsoleShell shell, string argStr, string[] args)
{ {
var player = shell.Player as IPlayerSession; var player = shell.Player as IPlayerSession;
var plyMgr = IoCManager.Resolve<IPlayerManager>(); var plyMgr = IoCManager.Resolve<IPlayerManager>();
var locator = IoCManager.Resolve<IPlayerLocator>();
var dbMan = IoCManager.Resolve<IServerDbManager>(); var dbMan = IoCManager.Resolve<IServerDbManager>();
string target; string target;
@@ -51,22 +51,15 @@ namespace Content.Server.Administration.Commands
return; return;
} }
NetUserId targetUid; var resolvedUid = await locator.LookupIdByNameOrIdAsync(target);
if (resolvedUid == null)
if (plyMgr.TryGetSessionByUsername(target, out var targetSession))
{ {
targetUid = targetSession.UserId; shell.WriteError("Unable to find a player with that name.");
}
else if (Guid.TryParse(target, out var targetGuid))
{
targetUid = new NetUserId(targetGuid);
}
else
{
shell.WriteLine("Unable to find user with that name.");
return; return;
} }
var targetUid = resolvedUid.Value;
if (player != null && player.UserId == targetUid) if (player != null && player.UserId == targetUid)
{ {
shell.WriteLine("You can't ban yourself!"); shell.WriteLine("You can't ban yourself!");
@@ -81,7 +74,7 @@ namespace Content.Server.Administration.Commands
await dbMan.AddServerBanAsync(new ServerBanDef(null, targetUid, null, DateTimeOffset.Now, expires, reason, player?.UserId, null)); await dbMan.AddServerBanAsync(new ServerBanDef(null, targetUid, null, DateTimeOffset.Now, expires, reason, player?.UserId, null));
var response = new StringBuilder($"Banned {targetUid} with reason \"{reason}\""); var response = new StringBuilder($"Banned {target} with reason \"{reason}\"");
response.Append(expires == null ? response.Append(expires == null ?
" permanently." " permanently."

View File

@@ -0,0 +1,135 @@
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading;
using System.Threading.Tasks;
using Content.Server.Database;
using JetBrains.Annotations;
using Robust.Server.Player;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Network;
namespace Content.Server.Administration
{
/// <summary>
/// Utilities for finding user IDs that extend to more than the server database.
/// </summary>
/// <remarks>
/// Methods in this class will check connected clients, server database
/// AND the authentication server for lookups, in that order.
/// </remarks>
public interface IPlayerLocator
{
/// <summary>
/// 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);
/// <summary>
/// If passed a GUID, runs <see cref="DoesPlayerExistAsync"/> and only returns it if the account exists.
/// 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);
}
internal sealed class PlayerLocator : IPlayerLocator
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IServerDbManager _db = default!;
public async Task<NetUserId?> 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;
// Check database for past players.
var record = await _db.GetPlayerRecordByUserName(playerName, cancel);
if (record != null)
return record.UserId;
// 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);
if (resp.StatusCode == HttpStatusCode.NotFound)
return null;
if (!resp.IsSuccessStatusCode)
{
Logger.ErrorS("PlayerLocate", "Auth server returned bad response {StatusCode}!", resp.StatusCode);
return null;
}
var responseData = await resp.Content.ReadFromJsonAsync<UserDataResponse>(cancellationToken: cancel);
if (responseData == null)
{
Logger.ErrorS("PlayerLocate", "Auth server returned null response!");
return null;
}
return new NetUserId(responseData.UserId);
}
public async Task<bool> DoesPlayerExistAsync(NetUserId userId, CancellationToken cancel = default)
{
// Check people currently on the server, the easiest case.
if (_playerManager.ValidSessionId(userId))
return true;
// Check database for past players.
var record = await _db.GetPlayerRecordByUserId(userId, cancel);
if (record != null)
return true;
// 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);
if (resp.StatusCode == HttpStatusCode.NotFound)
return false;
if (!resp.IsSuccessStatusCode)
{
Logger.ErrorS("PlayerLocate", "Auth server returned bad response {StatusCode}!", resp.StatusCode);
return false;
}
return true;
}
public async Task<NetUserId?> 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 LookupIdByNameAsync(playerName, cancel);
}
[UsedImplicitly]
private sealed record UserDataResponse(string UserName, Guid UserId)
{
}
}
}

View File

@@ -62,6 +62,7 @@ namespace Content.Server
IoCManager.Register<IHolidayManager, HolidayManager>(); IoCManager.Register<IHolidayManager, HolidayManager>();
IoCManager.Register<IVoteManager, VoteManager>(); IoCManager.Register<IVoteManager, VoteManager>();
IoCManager.Register<INpcBehaviorManager, NpcBehaviorManager>(); IoCManager.Register<INpcBehaviorManager, NpcBehaviorManager>();
IoCManager.Register<IPlayerLocator, PlayerLocator>();
} }
} }
} }