[feat] Разделение банов по серверам.

# Conflicts:
#	Content.Client/Administration/UI/BanList/BanListLine.xaml.cs
#	Content.Client/Administration/UI/BanList/Bans/BanListHeader.xaml
#	Content.Client/Administration/UI/BanList/Bans/BanListLine.xaml
#	Content.Client/Administration/UI/Tabs/AdminTab/BanWindow.xaml
#	Content.Client/Administration/UI/Tabs/AdminTab/BanWindow.xaml.cs
#	Content.IntegrationTests/Tests/Commands/PardonCommand.cs
#	Content.Server/Administration/Commands/BanCommand.cs
#	Content.Server/Administration/Commands/DepartmentBanCommand.cs
#	Content.Server/Administration/Commands/RoleBanCommand.cs
#	Content.Server/Administration/Managers/RoleBanManager.cs
#	Content.Server/Database/ServerDbManager.cs
#	Content.Server/Database/ServerDbPostgres.cs
#	Content.Server/Database/ServerDbSqlite.cs
This commit is contained in:
rhailrake
2023-04-30 14:27:27 +06:00
committed by Remuchi
parent 8bd6754472
commit 977e075086
42 changed files with 5825 additions and 328 deletions

View File

@@ -92,6 +92,7 @@ public sealed class BanListEui : BaseEui
}
line.BanningAdmin.Text = ban.BanningAdminName;
line.ServerName.Text = ban.ServerName == "unknown" ? "GLOBAL" : ban.ServerName;
}
private void OnLineIdsClicked<T>(IBanListLine<T> line) where T : SharedServerBan

View File

@@ -25,6 +25,11 @@
<Label Text="{Loc ban-list-header-banning-admin}"
SizeFlagsStretchRatio="2"
HorizontalExpand="True"/>
<cc:VSeparator/>
<Label Text="{Loc ban-list-header-server}"
SizeFlagsStretchRatio="2"
HorizontalExpand="True"/>
<cc:VSeparator/>
</BoxContainer>
</PanelContainer>
</ContainerButton>

View File

@@ -38,4 +38,11 @@
SizeFlagsStretchRatio="2"
HorizontalExpand="True"
ClipText="True"/>
<cc:VSeparator/>
<Label Name="ServerName"
Access="Public"
SizeFlagsStretchRatio="2"
HorizontalExpand="True"
ClipText="True"/>
<cc:VSeparator/>
</BoxContainer>

View File

@@ -10,4 +10,5 @@ public interface IBanListLine<T> where T : SharedServerBan
Label BanTime { get; }
Label Expires { get; }
Label BanningAdmin { get; }
Label ServerName { get; }
}

View File

@@ -29,6 +29,11 @@
<Label Text="{Loc ban-list-header-banning-admin}"
SizeFlagsStretchRatio="2"
HorizontalExpand="True"/>
<cc:VSeparator/>
<Label Text="{Loc ban-list-header-server}"
SizeFlagsStretchRatio="2"
HorizontalExpand="True"/>
<cc:VSeparator/>
</BoxContainer>
</PanelContainer>
</ContainerButton>

View File

@@ -43,4 +43,11 @@
SizeFlagsStretchRatio="2"
HorizontalExpand="True"
ClipText="True"/>
<cc:VSeparator/>
<Label Name="ServerName"
Access="Public"
SizeFlagsStretchRatio="2"
HorizontalExpand="True"
ClipText="True"/>
<cc:VSeparator/>
</BoxContainer>

View File

@@ -29,21 +29,12 @@
<Control MinWidth="50" />
<Label Name="ExpiresLabel" />
</BoxContainer>
<BoxContainer Orientation="Horizontal" Margin="4">
<OptionButton Name="TypeOption" />
<Control MinWidth="30" />
<Label Text="{Loc ban-panel-severity}" />
<OptionButton Name="SeverityOption" />
</BoxContainer>
<CheckBox Name="GlobalBanCheckbox" Margin="2" Text="Глобальный бан?" Pressed="False" />
<cc:HSeparator Margin="1"/>
<TextEdit Name="ReasonTextEdit" MinHeight="100" VerticalExpand="True" HorizontalExpand="True" />
</BoxContainer>
<!-- Player List -->
<cc:PlayerListControl Name="PlayerList" VerticalExpand="True" />
<!-- Role list (auto-generated) -->
<ScrollContainer>
<BoxContainer Name="RolesContainer" Orientation="Vertical" />
</ScrollContainer>
</TabContainer>
<Button Name="SubmitButton" Text="{Loc ban-panel-submit}" HorizontalExpand="True" />
</BoxContainer>

View File

@@ -1,17 +1,13 @@
using System.Linq;
using System.Net;
using System.Net.Sockets;
using Content.Client.Administration.UI.CustomControls;
using Content.Shared.Administration;
using Content.Shared.Database;
using Content.Shared.Roles;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -20,7 +16,7 @@ namespace Content.Client.Administration.UI.BanPanel;
[GenerateTypedNameReferences]
public sealed partial class BanPanel : DefaultWindow
{
public event Action<string?, (IPAddress, int)?, bool, byte[]?, bool, uint, string, NoteSeverity, string[]?, bool>? BanSubmitted;
public event Action<string?, (IPAddress, int)?, bool, byte[]?, bool, uint, string, NoteSeverity, bool, bool>? BanSubmitted;
public event Action<string>? PlayerChanged;
private string? PlayerUsername { get; set; }
private (IPAddress, int)? IpAddress { get; set; }
@@ -29,9 +25,6 @@ public sealed partial class BanPanel : DefaultWindow
private uint Multiplier { get; set; }
private bool HasBanFlag { get; set; }
private TimeSpan? ButtonResetOn { get; set; }
// This is less efficient than just holding a reference to the root control and enumerating children, but you
// have to know how the controls are nested, which makes the code more complicated.
private readonly List<CheckBox> _roleCheckboxes = new();
[Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -39,8 +32,7 @@ public sealed partial class BanPanel : DefaultWindow
{
BasicInfo,
//Text,
Players,
Roles
Players
}
private enum Multipliers
@@ -54,13 +46,6 @@ public sealed partial class BanPanel : DefaultWindow
Permanent
}
private enum Types
{
None,
Server,
Role
}
public BanPanel()
{
RobustXamlLoader.Load(this);
@@ -90,11 +75,6 @@ public sealed partial class BanPanel : DefaultWindow
HwidLine.Editable = HwidCheckbox.Pressed;
OnHwidChanged();
};
TypeOption.OnItemSelected += args =>
{
TypeOption.SelectId(args.Id);
OnTypeChanged();
};
LastConnCheckbox.OnPressed += args =>
{
IpLine.ModulateSelfOverride = null;
@@ -104,13 +84,6 @@ public sealed partial class BanPanel : DefaultWindow
};
SubmitButton.OnPressed += SubmitButtonOnOnPressed;
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-none"), (int) NoteSeverity.None);
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-low"), (int) NoteSeverity.Minor);
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-medium"), (int) NoteSeverity.Medium);
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-high"), (int) NoteSeverity.High);
SeverityOption.SelectId((int) NoteSeverity.Medium);
SeverityOption.OnItemSelected += args => SeverityOption.SelectId(args.Id);
MultiplierOption.AddItem(Loc.GetString("ban-panel-minutes"), (int) Multipliers.Minutes);
MultiplierOption.AddItem(Loc.GetString("ban-panel-hours"), (int) Multipliers.Hours);
MultiplierOption.AddItem(Loc.GetString("ban-panel-days"), (int) Multipliers.Days);
@@ -124,90 +97,8 @@ public sealed partial class BanPanel : DefaultWindow
Tabs.SetTabTitle((int) TabNumbers.BasicInfo, Loc.GetString("ban-panel-tabs-basic"));
//Tabs.SetTabTitle((int) TabNumbers.Text, Loc.GetString("ban-panel-tabs-reason"));
Tabs.SetTabTitle((int) TabNumbers.Players, Loc.GetString("ban-panel-tabs-players"));
Tabs.SetTabTitle((int) TabNumbers.Roles, Loc.GetString("ban-panel-tabs-role"));
Tabs.SetTabVisible((int) TabNumbers.Roles, false);
TypeOption.AddItem(Loc.GetString("ban-panel-select"), (int) Types.None);
TypeOption.AddItem(Loc.GetString("ban-panel-server"), (int) Types.Server);
TypeOption.AddItem(Loc.GetString("ban-panel-role"), (int) Types.Role);
ReasonTextEdit.Placeholder = new Rope.Leaf(Loc.GetString("ban-panel-reason"));
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
foreach (var proto in prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
{
CreateRoleGroup(proto.ID, proto.Roles, proto.Color);
}
CreateRoleGroup("Antagonist", prototypeManager.EnumeratePrototypes<AntagPrototype>().Select(p => p.ID), Color.Red);
}
private void CreateRoleGroup(string roleName, IEnumerable<string> roleList, Color color)
{
var outerContainer = new BoxContainer
{
Name = $"{roleName}GroupOuterBox",
HorizontalExpand = true,
VerticalExpand = true,
Orientation = BoxContainer.LayoutOrientation.Vertical,
Margin = new Thickness(4)
};
var departmentCheckbox = new CheckBox
{
Name = $"{roleName}GroupCheckbox",
Text = roleName,
Modulate = color,
HorizontalAlignment = HAlignment.Left
};
outerContainer.AddChild(departmentCheckbox);
var innerContainer = new BoxContainer
{
Name = $"{roleName}GroupInnerBox",
HorizontalExpand = true,
Orientation = BoxContainer.LayoutOrientation.Horizontal
};
departmentCheckbox.OnToggled += args =>
{
foreach (var child in innerContainer.Children)
{
if (child is CheckBox c)
{
c.Pressed = args.Pressed;
}
}
};
outerContainer.AddChild(innerContainer);
foreach (var role in roleList)
{
AddRoleCheckbox(role, innerContainer, departmentCheckbox);
}
RolesContainer.AddChild(new PanelContainer
{
PanelOverride = new StyleBoxFlat
{
BackgroundColor = color
}
});
RolesContainer.AddChild(outerContainer);
RolesContainer.AddChild(new HSeparator());
}
private void AddRoleCheckbox(string role, Control container, CheckBox header)
{
var roleCheckbox = new CheckBox
{
Name = $"{role}RoleCheckbox",
Text = role
};
roleCheckbox.OnToggled += args =>
{
if (args is { Pressed: true, Button.Parent: { } } && args.Button.Parent.Children.Where(e => e is CheckBox).All(e => ((CheckBox) e).Pressed))
header.Pressed = args.Pressed;
else
header.Pressed = false;
};
container.AddChild(roleCheckbox);
_roleCheckboxes.Add(roleCheckbox);
}
public void UpdateBanFlag(bool newFlag)
@@ -349,12 +240,6 @@ public sealed partial class BanPanel : DefaultWindow
Hwid = Convert.FromHexString(hwidString);
}
private void OnTypeChanged()
{
TypeOption.ModulateSelfOverride = null;
Tabs.SetTabVisible((int) TabNumbers.Roles, TypeOption.SelectedId == (int) Types.Role);
}
private void UpdateSubmitEnabled()
{
SubmitButton.Disabled = ErrorLevel != ErrorLevelEnum.None;
@@ -390,31 +275,6 @@ public sealed partial class BanPanel : DefaultWindow
private void SubmitButtonOnOnPressed(BaseButton.ButtonEventArgs obj)
{
string[]? roles = null;
if (TypeOption.SelectedId == (int) Types.Role)
{
var rolesList = new List<string>();
if (_roleCheckboxes.Count == 0)
throw new DebugAssertException("RoleCheckboxes was empty");
rolesList.AddRange(_roleCheckboxes.Where(c => c is { Pressed: true, Text: { } }).Select(c => c.Text!));
if (rolesList.Count == 0)
{
Tabs.CurrentTab = (int) TabNumbers.Roles;
return;
}
roles = rolesList.ToArray();
}
if (TypeOption.SelectedId == (int) Types.None)
{
TypeOption.ModulateSelfOverride = Color.Red;
Tabs.CurrentTab = (int) TabNumbers.BasicInfo;
return;
}
var reason = Rope.Collapse(ReasonTextEdit.TextRope);
if (string.IsNullOrWhiteSpace(reason))
{
@@ -437,9 +297,10 @@ public sealed partial class BanPanel : DefaultWindow
var player = PlayerCheckbox.Pressed ? PlayerUsername : null;
var useLastIp = IpCheckbox.Pressed && LastConnCheckbox.Pressed && IpAddress is null;
var useLastHwid = HwidCheckbox.Pressed && LastConnCheckbox.Pressed && Hwid is null;
var severity = (NoteSeverity) SeverityOption.SelectedId;
var erase = EraseCheckbox.Pressed;
BanSubmitted?.Invoke(player, IpAddress, useLastIp, Hwid, useLastHwid, (uint) (TimeEntered * Multiplier), reason, severity, roles, erase);
var isGlobalBan = GlobalBanCheckbox.Pressed;
BanSubmitted?.Invoke(player, IpAddress, useLastIp, Hwid, useLastHwid, (uint) (TimeEntered * Multiplier), reason, NoteSeverity.Medium, erase, isGlobalBan);
}
protected override void FrameUpdate(FrameEventArgs args)

View File

@@ -14,8 +14,8 @@ public sealed class BanPanelEui : BaseEui
{
BanPanel = new BanPanel();
BanPanel.OnClose += () => SendMessage(new CloseEuiMessage());
BanPanel.BanSubmitted += (player, ip, useLastIp, hwid, useLastHwid, minutes, reason, severity, roles, erase)
=> SendMessage(new BanPanelEuiStateMsg.CreateBanRequest(player, ip, useLastIp, hwid, useLastHwid, minutes, reason, severity, roles, erase));
BanPanel.BanSubmitted += (player, ip, useLastIp, hwid, useLastHwid, minutes, reason, severity, erase, isGlobalBan)
=> SendMessage(new BanPanelEuiStateMsg.CreateBanRequest(player, ip, useLastIp, hwid, useLastHwid, minutes, reason, severity, erase, isGlobalBan));
BanPanel.PlayerChanged += player => SendMessage(new BanPanelEuiStateMsg.GetPlayerInfoRequest(player));
}

View File

@@ -131,6 +131,8 @@ namespace Content.Client.Administration.UI
var title = string.IsNullOrWhiteSpace(popup.TitleEdit.Text) ? null : popup.TitleEdit.Text;
var adminServer = string.IsNullOrWhiteSpace(popup.ServerNameEdit.Text) ? "unknown" : popup.ServerNameEdit.Text;
if (popup.SourceData is { } src)
{
SendMessage(new UpdateAdmin
@@ -139,7 +141,8 @@ namespace Content.Client.Administration.UI
Title = title,
PosFlags = pos,
NegFlags = neg,
RankId = rank
RankId = rank,
AdminServer = adminServer
});
}
else
@@ -152,7 +155,8 @@ namespace Content.Client.Administration.UI
Title = title,
PosFlags = pos,
NegFlags = neg,
RankId = rank
RankId = rank,
AdminServer = adminServer
});
}
@@ -251,6 +255,11 @@ namespace Content.Client.Administration.UI
HorizontalAlignment = Control.HAlignment.Center,
});
al.AddChild(new Label
{
Text = admin.AdminServer,
});
var editButton = new Button { Text = Loc.GetString("permissions-eui-edit-title-button") };
editButton.OnPressed += _ => OnEditPressed(admin);
al.AddChild(editButton);
@@ -351,6 +360,7 @@ namespace Content.Client.Administration.UI
public readonly OptionButton RankButton;
public readonly Button SaveButton;
public readonly Button? RemoveButton;
public readonly LineEdit ServerNameEdit;
public readonly Dictionary<AdminFlags, (Button inherit, Button sub, Button plus)> FlagButtons
= new();
@@ -380,6 +390,7 @@ namespace Content.Client.Administration.UI
TitleEdit = new LineEdit { PlaceHolder = Loc.GetString("permissions-eui-edit-admin-window-title-edit-placeholder") };
RankButton = new OptionButton();
SaveButton = new Button { Text = Loc.GetString("permissions-eui-edit-admin-window-save-button"), HorizontalAlignment = HAlignment.Right };
ServerNameEdit = new LineEdit { PlaceHolder = Loc.GetString("permissions-eui-edit-admin-window-server-edit-placeholder") };
RankButton.AddItem(Loc.GetString("permissions-eui-edit-admin-window-no-rank-button"), NoRank);
foreach (var (rId, rank) in ui._ranks)
@@ -496,6 +507,7 @@ namespace Content.Client.Administration.UI
{
nameControl,
TitleEdit,
ServerNameEdit,
RankButton
}
},

View File

@@ -0,0 +1,30 @@
<DefaultWindow
xmlns="https://spacestation14.io"
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
Title="{Loc Ban}" MinSize="425 325">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc Player}" MinWidth="100" />
<Control MinWidth="50" />
<LineEdit Name="PlayerNameLine" MinWidth="100" HorizontalExpand="True" />
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc Reason}" MinSize="100 0" />
<Control MinSize="50 0" />
<LineEdit Name="ReasonLine" MinSize="100 0" HorizontalExpand="True" />
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc Minutes}" MinWidth="100" />
<Control MinWidth="50" />
<LineEdit Name="MinutesLine" MinWidth="100" HorizontalExpand="True" PlaceHolder="{Loc 0 minutes for a permanent ban}" />
<Button Name="HourButton" Text="+1h (0)"/>
<Button Name="DayButton" Text="+1d (0)"/>
<Button Name="WeekButton" Text="+1w (0)"/>
<Button Name="MonthButton" Text="+1M (0)"/>
</BoxContainer>
<CheckBox Name="GlobalBan" Text="Global Ban"/>
<cc:PlayerListControl Name="PlayerList" VerticalExpand="True" />
<Control MinWidth="50" />
<Button Name="SubmitButton" Text="{Loc Ban}" />
</BoxContainer>
</DefaultWindow>

View File

@@ -0,0 +1,84 @@
using Content.Shared.Administration;
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.Console;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
using static Robust.Client.UserInterface.Controls.LineEdit;
namespace Content.Client.Administration.UI.Tabs.AdminTab
{
[GenerateTypedNameReferences]
[UsedImplicitly]
public sealed partial class BanWindow : DefaultWindow
{
public BanWindow()
{
RobustXamlLoader.Load(this);
PlayerNameLine.OnTextChanged += _ => OnPlayerNameChanged();
PlayerList.OnSelectionChanged += OnPlayerSelectionChanged;
SubmitButton.OnPressed += SubmitButtonOnOnPressed;
MinutesLine.OnTextChanged += UpdateButtonsText;
HourButton.OnPressed += _ => AddMinutes(60);
DayButton.OnPressed += _ => AddMinutes(1440);
WeekButton.OnPressed += _ => AddMinutes(10080);
MonthButton.OnPressed += _ => AddMinutes(43200);
}
private bool TryGetMinutes(string str, out uint minutes)
{
if(string.IsNullOrWhiteSpace(str))
{
minutes = 0;
return true;
}
return uint.TryParse(str, out minutes);
}
private void AddMinutes(uint add)
{
if (!TryGetMinutes(MinutesLine.Text, out var minutes))
return;
MinutesLine.Text = $"{minutes + add}";
UpdateButtons(minutes+add);
}
private void UpdateButtonsText(LineEditEventArgs obj)
{
if (!TryGetMinutes(obj.Text, out var minutes))
return;
UpdateButtons(minutes);
}
private void UpdateButtons(uint minutes)
{
HourButton.Text = $"+1h ({minutes / 60})";
DayButton.Text = $"+1d ({minutes / 1440})";
WeekButton.Text = $"+1w ({minutes / 10080})";
MonthButton.Text = $"+1M ({minutes / 43200})";
}
private void OnPlayerNameChanged()
{
SubmitButton.Disabled = string.IsNullOrEmpty(PlayerNameLine.Text);
}
public void OnPlayerSelectionChanged(PlayerInfo? player)
{
PlayerNameLine.Text = player?.Username ?? string.Empty;
OnPlayerNameChanged();
}
private void SubmitButtonOnOnPressed(BaseButton.ButtonEventArgs obj)
{
// Small verification if Player Name exists
IoCManager.Resolve<IClientConsoleHost>().ExecuteCommand(
$"ban \"{PlayerNameLine.Text}\" \"{CommandParsing.Escape(ReasonLine.Text)}\" \"{MinutesLine.Text}\" \"{GlobalBan.Pressed}\"");
}
}
}

View File

@@ -133,7 +133,7 @@ namespace Content.IntegrationTests.Tests.Commands
Assert.Multiple(async () =>
{
// No bans should be returned
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null), Is.Null);
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null, false), Is.Null);
// Direct id lookup returns a pardoned ban
Assert.That(await sDatabase.GetServerBanAsync(1), Is.Not.Null);

View File

@@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Content.Server.Database.Migrations.Postgres
{
public partial class AddYaicaServerNameYaicaSalted : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "server_name",
table: "server_role_ban",
type: "text",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "server_name",
table: "server_ban",
type: "text",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "server_name",
table: "server_role_ban");
migrationBuilder.DropColumn(
name: "server_name",
table: "server_ban");
}
}
}

View File

@@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Content.Server.Database.Migrations.Postgres
{
public partial class AddYaicaServerNameForAdmins : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "admin_server",
table: "admin",
type: "text",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "admin_server",
table: "admin");
}
}
}

View File

@@ -0,0 +1,36 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Content.Server.Database.Migrations.Sqlite
{
public partial class AddYaicaServerNameYaicaSalted : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "server_name",
table: "server_role_ban",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "server_name",
table: "server_ban",
type: "TEXT",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "server_name",
table: "server_role_ban");
migrationBuilder.DropColumn(
name: "server_name",
table: "server_ban");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Content.Server.Database.Migrations.Sqlite
{
public partial class AddYaicaServerNameForAdmins : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "admin_server",
table: "admin",
type: "TEXT",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "admin_server",
table: "admin");
}
}
}

View File

@@ -454,6 +454,7 @@ namespace Content.Server.Database
public int? AdminRankId { get; set; }
public AdminRank? AdminRank { get; set; }
public List<AdminFlag> Flags { get; set; } = default!;
public string? AdminServer { get; set; }
}
public class AdminFlag
@@ -694,6 +695,7 @@ namespace Content.Server.Database
public bool Hidden { get; set; }
public List<ServerBanHit> BanHits { get; set; } = null!;
public string? ServerName { get; set; }
}
/// <summary>
@@ -829,6 +831,7 @@ namespace Content.Server.Database
public bool Hidden { get; set; }
public string RoleId { get; set; } = null!;
public string? ServerName { get; set; }
}
[Table("server_role_unban")]

View File

@@ -78,7 +78,8 @@ public sealed class BanListEui : BaseEui
ban.BanningAdmin == null
? null
: (await _playerLocator.LookupIdAsync(ban.BanningAdmin.Value))?.Username,
unban
unban,
ban.ServerName
));
}
}
@@ -110,7 +111,8 @@ public sealed class BanListEui : BaseEui
? null
: (await _playerLocator.LookupIdAsync(ban.BanningAdmin.Value))?.Username,
unban,
ban.Role
ban.Role,
ban.ServerName ?? "unknown"
));
}
}

View File

@@ -52,7 +52,7 @@ public sealed class BanPanelEui : BaseEui
switch (msg)
{
case BanPanelEuiStateMsg.CreateBanRequest r:
BanPlayer(r.Player, r.IpAddress, r.UseLastIp, r.Hwid?.ToImmutableArray(), r.UseLastHwid, r.Minutes, r.Severity, r.Reason, r.Roles, r.Erase);
BanPlayer(r.Player, r.IpAddress, r.UseLastIp, r.Hwid?.ToImmutableArray(), r.UseLastHwid, r.Minutes, r.Severity, r.Reason, r.Erase, r.IsGlobalBan);
break;
case BanPanelEuiStateMsg.GetPlayerInfoRequest r:
ChangePlayer(r.PlayerUsername);
@@ -60,13 +60,14 @@ public sealed class BanPanelEui : BaseEui
}
}
private async void BanPlayer(string? target, string? ipAddressString, bool useLastIp, ImmutableArray<byte>? hwid, bool useLastHwid, uint minutes, NoteSeverity severity, string reason, IReadOnlyCollection<string>? roles, bool erase)
private async void BanPlayer(string? target, string? ipAddressString, bool useLastIp, ImmutableArray<byte>? hwid, bool useLastHwid, uint minutes, NoteSeverity severity, string reason, bool erase, bool isGlobalBan)
{
if (!_admins.HasAdminFlag(Player, AdminFlags.Ban))
{
_sawmill.Warning($"{Player.Name} ({Player.UserId}) tried to create a ban with no ban flag");
return;
}
if (target == null && string.IsNullOrWhiteSpace(ipAddressString) && hwid == null)
{
_chat.DispatchServerMessage(Player, Loc.GetString("ban-panel-no-data"));
@@ -119,21 +120,7 @@ public sealed class BanPanelEui : BaseEui
targetHWid = useLastHwid ? located.LastHWId : hwid;
}
if (roles?.Count > 0)
{
var now = DateTimeOffset.UtcNow;
foreach (var role in roles)
{
_banManager.CreateRoleBan(targetUid, target, Player.UserId, addressRange, targetHWid, role, minutes, severity, reason, now);
}
Close();
return;
}
if (erase &&
targetUid != null &&
_playerManager.TryGetSessionById(targetUid.Value, out var targetPlayer))
if (erase && targetUid != null && _playerManager.TryGetSessionById(targetUid.Value, out var targetPlayer))
{
try
{
@@ -146,7 +133,7 @@ public sealed class BanPanelEui : BaseEui
}
}
_banManager.CreateServerBan(targetUid, target, Player.UserId, addressRange, targetHWid, minutes, severity, reason);
_banManager.CreateServerBan(targetUid, target, Player.UserId, addressRange, targetHWid, minutes, severity, reason, isGlobalBan);
Close();
}

View File

@@ -7,13 +7,11 @@ using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.Ban)]
public sealed class BanCommand : LocalizedCommands
{
[Dependency] private readonly IPlayerLocator _locator = default!;
[Dependency] private readonly IBanManager _bans = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
@@ -23,26 +21,24 @@ public sealed class BanCommand : LocalizedCommands
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
{
string target;
string reason;
uint minutes;
var target = args[0];
var reason = args[1];
var minutes = 0u;
var isGlobalBan = false;
if (!Enum.TryParse(_cfg.GetCVar(CCVars.ServerBanDefaultSeverity), out NoteSeverity severity))
{
Logger.WarningS("admin.server_ban", "Server ban severity could not be parsed from config! Defaulting to high.");
Logger.WarningS("admin.server_ban",
"Server ban severity could not be parsed from config! Defaulting to high.");
severity = NoteSeverity.High;
}
switch (args.Length)
{
case 2:
target = args[0];
reason = args[1];
minutes = 0;
break;
case 3:
target = args[0];
reason = args[1];
if (!uint.TryParse(args[2], out minutes))
{
shell.WriteLine(Loc.GetString("cmd-ban-invalid-minutes", ("minutes", args[2])));
@@ -52,8 +48,6 @@ public sealed class BanCommand : LocalizedCommands
break;
case 4:
target = args[0];
reason = args[1];
if (!uint.TryParse(args[2], out minutes))
{
@@ -69,6 +63,29 @@ public sealed class BanCommand : LocalizedCommands
return;
}
break;
case 5:
if (!uint.TryParse(args[2], out minutes))
{
shell.WriteLine(Loc.GetString("cmd-ban-invalid-minutes", ("minutes", args[2])));
shell.WriteLine(Help);
return;
}
if (!Enum.TryParse(args[3], ignoreCase: true, out severity))
{
shell.WriteLine(Loc.GetString("cmd-ban-invalid-severity", ("severity", args[3])));
shell.WriteLine(Help);
return;
}
if (!bool.TryParse(args[4], out isGlobalBan))
{
shell.WriteLine(Loc.GetString($"{args[4]} should be True or False.\n{Help}"));
shell.WriteLine(Help);
return;
}
break;
default:
shell.WriteLine(Loc.GetString("cmd-ban-invalid-arguments"));
@@ -88,48 +105,38 @@ public sealed class BanCommand : LocalizedCommands
var targetUid = located.UserId;
var targetHWid = located.LastHWId;
_bans.CreateServerBan(targetUid, target, player?.UserId, null, targetHWid, minutes, severity, reason);
_bans.CreateServerBan(targetUid, target, player?.UserId, null, targetHWid, minutes, severity, reason, isGlobalBan);
}
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (args.Length == 1)
var durations = new CompletionOption[]
{
var options = _playerManager.Sessions.Select(c => c.Name).OrderBy(c => c).ToArray();
return CompletionResult.FromHintOptions(options, LocalizationManager.GetString("cmd-ban-hint"));
}
new("0", LocalizationManager.GetString("cmd-ban-hint-duration-1")),
new("1440", LocalizationManager.GetString("cmd-ban-hint-duration-2")),
new("4320", LocalizationManager.GetString("cmd-ban-hint-duration-3")),
new("10080", LocalizationManager.GetString("cmd-ban-hint-duration-4")),
new("20160", LocalizationManager.GetString("cmd-ban-hint-duration-5")),
new("43800", LocalizationManager.GetString("cmd-ban-hint-duration-6")),
};
if (args.Length == 2)
return CompletionResult.FromHint(LocalizationManager.GetString("cmd-ban-hint-reason"));
if (args.Length == 3)
var severities = new CompletionOption[]
{
var durations = new CompletionOption[]
{
new("0", LocalizationManager.GetString("cmd-ban-hint-duration-1")),
new("1440", LocalizationManager.GetString("cmd-ban-hint-duration-2")),
new("4320", LocalizationManager.GetString("cmd-ban-hint-duration-3")),
new("10080", LocalizationManager.GetString("cmd-ban-hint-duration-4")),
new("20160", LocalizationManager.GetString("cmd-ban-hint-duration-5")),
new("43800", LocalizationManager.GetString("cmd-ban-hint-duration-6")),
};
new("none", Loc.GetString("admin-note-editor-severity-none")),
new("minor", Loc.GetString("admin-note-editor-severity-low")),
new("medium", Loc.GetString("admin-note-editor-severity-medium")),
new("high", Loc.GetString("admin-note-editor-severity-high")),
};
return CompletionResult.FromHintOptions(durations, LocalizationManager.GetString("cmd-ban-hint-duration"));
}
if (args.Length == 4)
return args.Length switch
{
var severities = new CompletionOption[]
{
new("none", Loc.GetString("admin-note-editor-severity-none")),
new("minor", Loc.GetString("admin-note-editor-severity-low")),
new("medium", Loc.GetString("admin-note-editor-severity-medium")),
new("high", Loc.GetString("admin-note-editor-severity-high")),
};
return CompletionResult.FromHintOptions(severities, Loc.GetString("cmd-ban-hint-severity"));
}
return CompletionResult.Empty;
1 => CompletionResult.FromHintOptions(CompletionHelper.SessionNames(),
LocalizationManager.GetString("cmd-ban-hint")),
2 => CompletionResult.FromHint(LocalizationManager.GetString("cmd-ban-hint-reason")),
3 => CompletionResult.FromHintOptions(durations, LocalizationManager.GetString("cmd-ban-hint-duration")),
4 => CompletionResult.FromHintOptions(severities, Loc.GetString("cmd-ban-hint-severity")),
_ => args.Length == 5 ? CompletionResult.FromHint("<server>") : CompletionResult.Empty
};
}
}

View File

@@ -3,7 +3,9 @@ using Content.Server.Administration.BanList;
using Content.Server.Database;
using Content.Server.EUI;
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands;
@@ -17,11 +19,13 @@ public sealed class BanListCommand : LocalizedCommands
[Dependency] private readonly IServerDbManager _dbManager = default!;
[Dependency] private readonly EuiManager _eui = default!;
[Dependency] private readonly IPlayerLocator _locator = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
public override string Command => "banlist";
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
{
var serverName = _cfg.GetCVar(CCVars.AdminLogsServerName);
if (args.Length != 1)
{
shell.WriteError(Help);
@@ -38,7 +42,7 @@ public sealed class BanListCommand : LocalizedCommands
if (shell.Player is not { } player)
{
var bans = await _dbManager.GetServerBansAsync(data.LastAddress, data.UserId, data.LastHWId, false);
var bans = await _dbManager.GetServerBansAsync(data.LastAddress, data.UserId, data.LastHWId, false, serverName);
if (bans.Count == 0)
{
@@ -48,7 +52,7 @@ public sealed class BanListCommand : LocalizedCommands
foreach (var ban in bans)
{
var msg = $"{ban.Id}: {ban.Reason}";
var msg = $"{ban.Id}: {ban.Reason} ({ban.ServerName})";
shell.WriteLine(msg);
}

View File

@@ -23,10 +23,12 @@ public sealed class DepartmentBanCommand : IConsoleCommand
public async void Execute(IConsoleShell shell, string argStr, string[] args)
{
string target;
string department;
string reason;
uint minutes;
var target = args[0];
var department = args[1];
var reason = args[2];
var minutes = 0u;
var isGlobalBan = false;
if (!Enum.TryParse(_cfg.GetCVar(CCVars.DepartmentBanDefaultSeverity), out NoteSeverity severity))
{
Logger.WarningS("admin.department_ban", "Department ban severity could not be parsed from config! Defaulting to medium.");
@@ -36,28 +38,28 @@ public sealed class DepartmentBanCommand : IConsoleCommand
switch (args.Length)
{
case 3:
target = args[0];
department = args[1];
reason = args[2];
minutes = 0;
break;
case 4:
target = args[0];
department = args[1];
reason = args[2];
if (!uint.TryParse(args[3], out minutes))
{
shell.WriteError(Loc.GetString("cmd-roleban-minutes-parse", ("time", args[3]), ("help", Help)));
return;
}
break;
case 5:
if (!uint.TryParse(args[3], out minutes))
{
shell.WriteError(Loc.GetString("cmd-roleban-minutes-parse", ("time", args[3]), ("help", Help)));
return;
}
if (!Enum.TryParse(args[4], ignoreCase: true, out severity))
{
shell.WriteLine(Loc.GetString("cmd-roleban-severity-parse", ("severity", args[4]), ("help", Help)));
return;
}
break;
case 5:
target = args[0];
department = args[1];
reason = args[2];
case 6:
if (!uint.TryParse(args[3], out minutes))
{
shell.WriteError(Loc.GetString("cmd-roleban-minutes-parse", ("time", args[3]), ("help", Help)));
@@ -70,6 +72,11 @@ public sealed class DepartmentBanCommand : IConsoleCommand
return;
}
if (!bool.TryParse(args[5], out isGlobalBan))
{
shell.WriteLine($"{args[5]} should be True or False.\n{Help}");
return;
}
break;
default:
shell.WriteError(Loc.GetString("cmd-roleban-arg-count"));
@@ -97,7 +104,7 @@ public sealed class DepartmentBanCommand : IConsoleCommand
var now = DateTimeOffset.UtcNow;
foreach (var job in departmentProto.Roles)
{
_banManager.CreateRoleBan(targetUid, located.Username, shell.Player?.UserId, null, targetHWid, job, minutes, severity, reason, now);
_banManager.CreateRoleBan(targetUid, located.Username, shell.Player?.UserId, null, targetHWid, job, minutes, severity, reason, now, isGlobalBan);
}
}
@@ -130,6 +137,7 @@ public sealed class DepartmentBanCommand : IConsoleCommand
3 => CompletionResult.FromHint(Loc.GetString("cmd-roleban-hint-3")),
4 => CompletionResult.FromHintOptions(durOpts, Loc.GetString("cmd-roleban-hint-4")),
5 => CompletionResult.FromHintOptions(severities, Loc.GetString("cmd-roleban-hint-5")),
6 => CompletionResult.FromHint("<server>"),
_ => CompletionResult.Empty
};
}

View File

@@ -1,12 +1,11 @@
using System.Linq;
using System.Text;
using Content.Server.Administration.Managers;
using Content.Server.Administration.Managers;
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Content.Shared.Database;
using Content.Shared.Roles;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.Ban)]
@@ -22,10 +21,11 @@ public sealed class RoleBanCommand : IConsoleCommand
public async void Execute(IConsoleShell shell, string argStr, string[] args)
{
string target;
string job;
string reason;
uint minutes;
var target = args[0];
var job = args[1];
var reason = args[2];
var minutes = 0u;
var isGlobalBan = false;
if (!Enum.TryParse(_cfg.GetCVar(CCVars.RoleBanDefaultSeverity), out NoteSeverity severity))
{
Logger.WarningS("admin.role_ban", "Role ban severity could not be parsed from config! Defaulting to medium.");
@@ -35,16 +35,8 @@ public sealed class RoleBanCommand : IConsoleCommand
switch (args.Length)
{
case 3:
target = args[0];
job = args[1];
reason = args[2];
minutes = 0;
break;
case 4:
target = args[0];
job = args[1];
reason = args[2];
if (!uint.TryParse(args[3], out minutes))
{
shell.WriteError(Loc.GetString("cmd-roleban-minutes-parse", ("time", args[3]), ("help", Help)));
@@ -53,22 +45,33 @@ public sealed class RoleBanCommand : IConsoleCommand
break;
case 5:
target = args[0];
job = args[1];
reason = args[2];
if (!uint.TryParse(args[3], out minutes))
{
shell.WriteError(Loc.GetString("cmd-roleban-minutes-parse", ("time", args[3]), ("help", Help)));
return;
}
if (!Enum.TryParse(args[4], ignoreCase: true, out severity))
{
shell.WriteLine(Loc.GetString("cmd-roleban-severity-parse", ("severity", args[4]), ("help", Help)));
return;
}
break;
case 6:
if (!uint.TryParse(args[3], out minutes))
{
shell.WriteError(Loc.GetString("cmd-roleban-minutes-parse", ("time", args[3]), ("help", Help)));
return;
}
if (!Enum.TryParse(args[4], ignoreCase: true, out severity))
{
shell.WriteLine(Loc.GetString("cmd-roleban-severity-parse", ("severity", args[4]), ("help", Help)));
return;
}
if (!bool.TryParse(args[5], out isGlobalBan))
{
shell.WriteLine($"{args[5]} should be True or False.\n{Help}");
return;
}
break;
default:
shell.WriteError(Loc.GetString("cmd-roleban-arg-count"));
@@ -85,8 +88,7 @@ public sealed class RoleBanCommand : IConsoleCommand
var targetUid = located.UserId;
var targetHWid = located.LastHWId;
_bans.CreateRoleBan(targetUid, located.Username, shell.Player?.UserId, null, targetHWid, job, minutes, severity, reason, DateTimeOffset.UtcNow);
_bans.CreateRoleBan(targetUid, located.Username, shell.Player?.UserId, null, targetHWid, job, minutes, severity, reason, DateTimeOffset.UtcNow, isGlobalBan);
}
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
@@ -118,6 +120,7 @@ public sealed class RoleBanCommand : IConsoleCommand
3 => CompletionResult.FromHint(Loc.GetString("cmd-roleban-hint-3")),
4 => CompletionResult.FromHintOptions(durOpts, Loc.GetString("cmd-roleban-hint-4")),
5 => CompletionResult.FromHintOptions(severities, Loc.GetString("cmd-roleban-hint-5")),
6 => CompletionResult.FromHint("<server>"),
_ => CompletionResult.Empty
};
}

View File

@@ -374,6 +374,15 @@ namespace Content.Server.Administration.Managers
Flags = flags
};
var currentServerName = _cfg.GetCVar(CCVars.AdminLogsServerName);
// я ебался в зад, поймите
if (!data.HasFlag(AdminFlags.Permissions) && !data.HasFlag(AdminFlags.Host) &&
dbData.AdminServer != null && dbData.AdminServer != "unknown" && currentServerName != "unknown"
&& currentServerName != dbData.AdminServer)
{
return null;
}
if (dbData.Title != null)
{
data.Title = dbData.Title;

View File

@@ -37,6 +37,7 @@ public sealed class BanManager : IBanManager, IPostInjectInit
public const string SawmillId = "admin.bans";
public const string JobPrefix = "Job:";
public const string UnknownServer = "unknown";
private readonly Dictionary<NetUserId, HashSet<ServerRoleBanDef>> _cachedRoleBans = new();
@@ -52,7 +53,7 @@ public sealed class BanManager : IBanManager, IPostInjectInit
if (e.NewStatus != SessionStatus.Connected || _cachedRoleBans.ContainsKey(e.Session.UserId))
return;
var netChannel = e.Session.ConnectedClient;
var netChannel = e.Session.Channel;
ImmutableArray<byte>? hwId = netChannel.UserData.HWId.Length == 0 ? null : netChannel.UserData.HWId;
await CacheDbRoleBans(e.Session.UserId, netChannel.RemoteEndPoint.Address, hwId);
@@ -112,7 +113,7 @@ public sealed class BanManager : IBanManager, IPostInjectInit
}
#region Server Bans
public async void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, uint? minutes, NoteSeverity severity, string reason)
public async void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, uint? minutes, NoteSeverity severity, string reason, bool isGlobalBan)
{
DateTimeOffset? expires = null;
if (minutes > 0)
@@ -120,6 +121,13 @@ public sealed class BanManager : IBanManager, IPostInjectInit
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes.Value);
}
var serverName = _cfg.GetCVar(CCVars.AdminLogsServerName);
if (isGlobalBan)
{
serverName = UnknownServer;
}
_systems.TryGetEntitySystem<GameTicker>(out var ticker);
int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
var playtime = target == null ? TimeSpan.Zero : (await _db.GetPlayTimes(target.Value)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero;
@@ -136,7 +144,8 @@ public sealed class BanManager : IBanManager, IPostInjectInit
reason,
severity,
banningAdmin,
null);
null,
serverName);
await _db.AddServerBanAsync(banDef);
var adminName = banningAdmin == null
@@ -175,14 +184,14 @@ public sealed class BanManager : IBanManager, IPostInjectInit
return;
// If they are, kick them
var message = banDef.FormatBanMessage(_cfg, _localizationManager);
targetPlayer.ConnectedClient.Disconnect(message);
targetPlayer.Channel.Disconnect(message);
}
#endregion
#region Job Bans
// If you are trying to remove timeOfBan, please don't. It's there because the note system groups role bans by time, reason and banning admin.
// Removing it will clutter the note list. Please also make sure that department bans are applied to roles with the same DateTimeOffset.
public async void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan)
public async void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan, bool isGlobalBan)
{
if (!_prototypeManager.TryIndex(role, out JobPrototype? _))
{
@@ -196,6 +205,13 @@ public sealed class BanManager : IBanManager, IPostInjectInit
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes.Value);
}
var serverName = _cfg.GetCVar(CCVars.AdminLogsServerName);
if (isGlobalBan)
{
serverName = UnknownServer;
}
_systems.TryGetEntitySystem(out GameTicker? ticker);
int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
var playtime = target == null ? TimeSpan.Zero : (await _db.GetPlayTimes(target.Value)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero;
@@ -213,7 +229,8 @@ public sealed class BanManager : IBanManager, IPostInjectInit
severity,
banningAdmin,
null,
role);
role,
serverName);
if (!await AddRoleBan(banDef))
{
@@ -293,7 +310,7 @@ public sealed class BanManager : IBanManager, IPostInjectInit
};
_sawmill.Debug($"Sent rolebans to {pSession.Name}");
_netManager.ServerSendMessage(bans, pSession.ConnectedClient);
_netManager.ServerSendMessage(bans, pSession.Channel);
}
public void PostInject()

View File

@@ -22,7 +22,7 @@ public interface IBanManager
/// <param name="minutes">Number of minutes to ban for. 0 and null mean permanent</param>
/// <param name="severity">Severity of the resulting ban note</param>
/// <param name="reason">Reason for the ban</param>
public void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, uint? minutes, NoteSeverity severity, string reason);
public void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, uint? minutes, NoteSeverity severity, string reason, bool isGlobalBan);
public HashSet<string>? GetRoleBans(NetUserId playerUserId);
public HashSet<string>? GetJobBans(NetUserId playerUserId);
@@ -35,7 +35,7 @@ public interface IBanManager
/// <param name="reason">Reason for the ban</param>
/// <param name="minutes">Number of minutes to ban for. 0 and null mean permanent</param>
/// <param name="timeOfBan">Time when the ban was applied, used for grouping role bans</param>
public void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan);
public void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan, bool isGlobalBan);
/// <summary>
/// Pardons a role ban for the specified target, username or GUID

View File

@@ -73,7 +73,8 @@ namespace Content.Server.Administration.UI
Title = p.a.Title,
RankId = p.a.AdminRankId,
UserId = new NetUserId(p.a.UserId),
UserName = p.lastUserName
UserName = p.lastUserName,
AdminServer = p.a.AdminServer
}).ToArray(),
AdminRanks = _adminRanks.ToDictionary(a => a.Id, a => new PermissionsEuiState.AdminRankData
@@ -251,6 +252,7 @@ namespace Content.Server.Administration.UI
admin.Title = ua.Title;
admin.AdminRankId = ua.RankId;
admin.AdminServer = ua.AdminServer;
admin.Flags = GenAdminFlagList(ua.PosFlags, ua.NegFlags);
await _db.UpdateAdminAsync(admin);
@@ -265,8 +267,9 @@ namespace Content.Server.Administration.UI
var name = playerRecord?.LastSeenUserName ?? ua.UserId.ToString();
var title = ua.Title ?? "<no title>";
var flags = AdminFlagsHelper.PosNegFlagsText(ua.PosFlags, ua.NegFlags);
var server = ua.AdminServer ?? "<all servers>";
Logger.InfoS("admin.perms", $"{Player} updated admin {name} to {title}/{rankName}/{flags}");
Logger.InfoS("admin.perms", $"{Player} updated admin {name} to {title}/{rankName}/{flags}/{server}");
if (_playerManager.TryGetSessionById(ua.UserId, out var player))
{
@@ -333,15 +336,17 @@ namespace Content.Server.Administration.UI
Flags = GenAdminFlagList(ca.PosFlags, ca.NegFlags),
AdminRankId = ca.RankId,
UserId = userId.UserId,
Title = ca.Title
Title = ca.Title,
AdminServer = ca.AdminServer
};
await _db.AddAdminAsync(admin);
var title = ca.Title ?? "<no title>";
var flags = AdminFlagsHelper.PosNegFlagsText(ca.PosFlags, ca.NegFlags);
var server = ca.AdminServer ?? "<all servers>";
Logger.InfoS("admin.perms", $"{Player} added admin {name} as {title}/{rankName}/{flags}");
Logger.InfoS("admin.perms", $"{Player} added admin {name} as {title}/{rankName}/{flags}/{server}");
if (_playerManager.TryGetSessionById(userId, out var player))
{

View File

@@ -5,7 +5,6 @@ using Content.Shared.Database;
using Robust.Shared.Configuration;
using Robust.Shared.Network;
namespace Content.Server.Database
{
public sealed class ServerBanDef
@@ -24,8 +23,9 @@ namespace Content.Server.Database
public NetUserId? BanningAdmin { get; }
public ServerUnbanDef? Unban { get; }
public ServerBanDef(
int? id,
public string ServerName { get; }
public ServerBanDef(int? id,
NetUserId? userId,
(IPAddress, int)? address,
ImmutableArray<byte>? hwId,
@@ -36,7 +36,8 @@ namespace Content.Server.Database
string reason,
NoteSeverity severity,
NetUserId? banningAdmin,
ServerUnbanDef? unban)
ServerUnbanDef? unban,
string serverName)
{
if (userId == null && address == null && hwId == null)
{
@@ -62,6 +63,7 @@ namespace Content.Server.Database
Severity = severity;
BanningAdmin = banningAdmin;
Unban = unban;
ServerName = serverName;
}
public string FormatBanMessage(IConfigurationManager cfg, ILocalizationManager loc)

View File

@@ -21,6 +21,7 @@ namespace Content.Server.Database
{
public abstract class ServerDbBase
{
protected const string GlobalServerName = "unknown";
private readonly ISawmill _opsLog;
/// <param name="opsLog">Sawmill to trace log database operations to.</param>
@@ -337,7 +338,8 @@ namespace Content.Server.Database
public abstract Task<ServerBanDef?> GetServerBanAsync(
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId);
ImmutableArray<byte>? hwId,
string serverName = GlobalServerName);
/// <summary>
/// Looks up an user's ban history.
@@ -353,7 +355,8 @@ namespace Content.Server.Database
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId,
bool includeUnbanned);
bool includeUnbanned,
string serverName = GlobalServerName);
public abstract Task AddServerBanAsync(ServerBanDef serverBan);
public abstract Task AddServerUnbanAsync(ServerUnbanDef serverUnban);
@@ -445,7 +448,8 @@ namespace Content.Server.Database
public abstract Task<List<ServerRoleBanDef>> GetServerRoleBansAsync(IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId,
bool includeUnbanned);
bool includeUnbanned,
string serverName = GlobalServerName);
public abstract Task<ServerRoleBanDef> AddServerRoleBanAsync(ServerRoleBanDef serverRoleBan);
public abstract Task AddServerRoleUnbanAsync(ServerRoleUnbanDef serverRoleUnban);
@@ -660,6 +664,7 @@ namespace Content.Server.Database
existing.Flags = admin.Flags;
existing.Title = admin.Title;
existing.AdminRankId = admin.AdminRankId;
existing.AdminServer = admin.AdminServer;
await db.DbContext.SaveChangesAsync(cancel);
}

View File

@@ -24,6 +24,7 @@ namespace Content.Server.Database
{
public interface IServerDbManager
{
const string GlobalServerName = "unknown";
void Init();
void Shutdown();
@@ -68,7 +69,8 @@ namespace Content.Server.Database
Task<ServerBanDef?> GetServerBanAsync(
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId);
ImmutableArray<byte>? hwId,
string serverName = GlobalServerName);
/// <summary>
/// Looks up an user's ban history.
@@ -83,7 +85,8 @@ namespace Content.Server.Database
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId,
bool includeUnbanned=true);
bool includeUnbanned=true,
string serverName = GlobalServerName);
Task AddServerBanAsync(ServerBanDef serverBan);
Task AddServerUnbanAsync(ServerUnbanDef serverBan);
@@ -137,7 +140,8 @@ namespace Content.Server.Database
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId,
bool includeUnbanned = true);
bool includeUnbanned = true,
string serverName = GlobalServerName);
Task<ServerRoleBanDef> AddServerRoleBanAsync(ServerRoleBanDef serverBan);
Task AddServerRoleUnbanAsync(ServerRoleUnbanDef serverBan);
@@ -281,6 +285,8 @@ namespace Content.Server.Database
public sealed class ServerDbManager : IServerDbManager
{
private const string GlobalServerName = "unknown";
public static readonly Counter DbReadOpsMetric = Metrics.CreateCounter(
"db_read_ops",
"Amount of read operations processed by the database manager.");
@@ -395,20 +401,22 @@ namespace Content.Server.Database
public Task<ServerBanDef?> GetServerBanAsync(
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId)
ImmutableArray<byte>? hwId,
string serverName = GlobalServerName)
{
DbReadOpsMetric.Inc();
return RunDbCommand(() => _db.GetServerBanAsync(address, userId, hwId));
return RunDbCommand(() => _db.GetServerBanAsync(address, userId, hwId, serverName));
}
public Task<List<ServerBanDef>> GetServerBansAsync(
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId,
bool includeUnbanned=true)
bool includeUnbanned=true,
string serverName = GlobalServerName)
{
DbReadOpsMetric.Inc();
return RunDbCommand(() => _db.GetServerBansAsync(address, userId, hwId, includeUnbanned));
return RunDbCommand(() => _db.GetServerBansAsync(address, userId, hwId, includeUnbanned, serverName));
}
public Task AddServerBanAsync(ServerBanDef serverBan)
@@ -452,10 +460,11 @@ namespace Content.Server.Database
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId,
bool includeUnbanned = true)
bool includeUnbanned=true,
string serverName = GlobalServerName)
{
DbReadOpsMetric.Inc();
return RunDbCommand(() => _db.GetServerRoleBansAsync(address, userId, hwId, includeUnbanned));
return RunDbCommand(() => _db.GetServerRoleBansAsync(address, userId, hwId, includeUnbanned, serverName));
}
public Task<ServerRoleBanDef> AddServerRoleBanAsync(ServerRoleBanDef serverRoleBan)

View File

@@ -67,7 +67,8 @@ namespace Content.Server.Database
public override async Task<ServerBanDef?> GetServerBanAsync(
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId)
ImmutableArray<byte>? hwId,
string serverName = GlobalServerName)
{
if (address == null && userId == null && hwId == null)
{
@@ -77,7 +78,7 @@ namespace Content.Server.Database
await using var db = await GetDbImpl();
var exempt = await GetBanExemptionCore(db, userId);
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned: false, exempt)
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned: false, exempt, serverName)
.OrderByDescending(b => b.BanTime);
var ban = await query.FirstOrDefaultAsync();
@@ -87,7 +88,7 @@ namespace Content.Server.Database
public override async Task<List<ServerBanDef>> GetServerBansAsync(IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId, bool includeUnbanned)
ImmutableArray<byte>? hwId, bool includeUnbanned, string serverName = GlobalServerName)
{
if (address == null && userId == null && hwId == null)
{
@@ -97,7 +98,7 @@ namespace Content.Server.Database
await using var db = await GetDbImpl();
var exempt = await GetBanExemptionCore(db, userId);
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned, exempt);
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned, exempt, serverName);
var queryBans = await query.ToArrayAsync();
var bans = new List<ServerBanDef>(queryBans.Length);
@@ -121,7 +122,8 @@ namespace Content.Server.Database
ImmutableArray<byte>? hwId,
DbGuardImpl db,
bool includeUnbanned,
ServerBanExemptFlags? exemptFlags)
ServerBanExemptFlags? exemptFlags,
string serverName = GlobalServerName)
{
DebugTools.Assert(!(address == null && userId == null && hwId == null));
@@ -158,6 +160,9 @@ namespace Content.Server.Database
query != null,
"At least one filter item (IP/UserID/HWID) must have been given to make query not null.");
query = query.Where(p =>
p.ServerName == serverName || p.ServerName == "unknown" || string.IsNullOrEmpty(p.ServerName) || serverName == GlobalServerName);
if (!includeUnbanned)
{
query = query.Where(p =>
@@ -205,7 +210,8 @@ namespace Content.Server.Database
ban.Reason,
ban.Severity,
aUid,
unbanDef);
unbanDef,
ban.ServerName ??= "unknown");
}
private static ServerUnbanDef? ConvertUnban(ServerUnban? unban)
@@ -242,7 +248,8 @@ namespace Content.Server.Database
ExpirationTime = serverBan.ExpirationTime?.UtcDateTime,
RoundId = serverBan.RoundId,
PlaytimeAtNote = serverBan.PlaytimeAtNote,
PlayerUserId = serverBan.UserId?.UserId
PlayerUserId = serverBan.UserId?.UserId,
ServerName = serverBan.ServerName
});
await db.PgDbContext.SaveChangesAsync();
@@ -281,7 +288,8 @@ namespace Content.Server.Database
public override async Task<List<ServerRoleBanDef>> GetServerRoleBansAsync(IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId,
bool includeUnbanned)
bool includeUnbanned,
string serverName = GlobalServerName)
{
if (address == null && userId == null && hwId == null)
{
@@ -290,7 +298,7 @@ namespace Content.Server.Database
await using var db = await GetDbImpl();
var query = MakeRoleBanLookupQuery(address, userId, hwId, db, includeUnbanned)
var query = MakeRoleBanLookupQuery(address, userId, hwId, db, includeUnbanned, serverName)
.OrderByDescending(b => b.BanTime);
return await QueryRoleBans(query);
@@ -319,7 +327,8 @@ namespace Content.Server.Database
NetUserId? userId,
ImmutableArray<byte>? hwId,
DbGuardImpl db,
bool includeUnbanned)
bool includeUnbanned,
string serverName = GlobalServerName)
{
IQueryable<ServerRoleBan>? query = null;
@@ -350,6 +359,9 @@ namespace Content.Server.Database
query = query == null ? newQ : query.Union(newQ);
}
query = query?.Where(p =>
p.ServerName == serverName || p.ServerName == "unknown" || string.IsNullOrEmpty(p.ServerName) || serverName == GlobalServerName);
if (!includeUnbanned)
{
query = query?.Where(p =>
@@ -395,7 +407,8 @@ namespace Content.Server.Database
ban.Severity,
aUid,
unbanDef,
ban.RoleId);
ban.RoleId,
ban.ServerName ??= "unknown");
}
private static ServerRoleUnbanDef? ConvertRoleUnban(ServerRoleUnban? unban)
@@ -434,6 +447,7 @@ namespace Content.Server.Database
PlaytimeAtNote = serverRoleBan.PlaytimeAtNote,
PlayerUserId = serverRoleBan.UserId?.UserId,
RoleId = serverRoleBan.Role,
ServerName = serverRoleBan.ServerName
};
db.PgDbContext.RoleBan.Add(ban);

View File

@@ -79,7 +79,8 @@ namespace Content.Server.Database
public override async Task<ServerBanDef?> GetServerBanAsync(
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId)
ImmutableArray<byte>? hwId,
string serverName = GlobalServerName)
{
await using var db = await GetDbImpl();
@@ -89,14 +90,14 @@ namespace Content.Server.Database
// So just pull down the whole list into memory.
var bans = await GetAllBans(db.SqliteDbContext, includeUnbanned: false, exempt);
return bans.FirstOrDefault(b => BanMatches(b, address, userId, hwId, exempt)) is { } foundBan
return bans.FirstOrDefault(b => BanMatches(b, address, userId, hwId, exempt, serverName)) is { } foundBan
? ConvertBan(foundBan)
: null;
}
public override async Task<List<ServerBanDef>> GetServerBansAsync(IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId, bool includeUnbanned)
ImmutableArray<byte>? hwId, bool includeUnbanned, string serverName = GlobalServerName)
{
await using var db = await GetDbImpl();
@@ -107,7 +108,7 @@ namespace Content.Server.Database
var queryBans = await GetAllBans(db.SqliteDbContext, includeUnbanned, exempt);
return queryBans
.Where(b => BanMatches(b, address, userId, hwId, exempt))
.Where(b => BanMatches(b, address, userId, hwId, exempt, serverName))
.Select(ConvertBan)
.ToList()!;
}
@@ -136,8 +137,14 @@ namespace Content.Server.Database
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId,
ServerBanExemptFlags? exemptFlags)
ServerBanExemptFlags? exemptFlags,
string serverName = GlobalServerName)
{
if (serverName != GlobalServerName && ban.ServerName != GlobalServerName && serverName != ban.ServerName)
{
return false;
}
if (!exemptFlags.GetValueOrDefault(ServerBanExemptFlags.None).HasFlag(ServerBanExemptFlags.IP)
&& address != null && ban.Address is not null && address.IsInSubnet(ban.Address.Value))
{
@@ -167,7 +174,8 @@ namespace Content.Server.Database
ExpirationTime = serverBan.ExpirationTime?.UtcDateTime,
RoundId = serverBan.RoundId,
PlaytimeAtNote = serverBan.PlaytimeAtNote,
PlayerUserId = serverBan.UserId?.UserId
PlayerUserId = serverBan.UserId?.UserId,
ServerName = serverBan.ServerName
});
await db.SqliteDbContext.SaveChangesAsync();
@@ -205,7 +213,8 @@ namespace Content.Server.Database
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId,
bool includeUnbanned)
bool includeUnbanned,
string serverName = GlobalServerName)
{
await using var db = await GetDbImpl();
@@ -214,7 +223,7 @@ namespace Content.Server.Database
var queryBans = await GetAllRoleBans(db.SqliteDbContext, includeUnbanned);
return queryBans
.Where(b => RoleBanMatches(b, address, userId, hwId))
.Where(b => RoleBanMatches(b, address, userId, hwId, serverName))
.Select(ConvertRoleBan)
.ToList()!;
}
@@ -237,8 +246,14 @@ namespace Content.Server.Database
ServerRoleBan ban,
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId)
ImmutableArray<byte>? hwId,
string serverName)
{
if (serverName != GlobalServerName && ban.ServerName != GlobalServerName && serverName != ban.ServerName)
{
return false;
}
if (address != null && ban.Address is not null && address.IsInSubnet(ban.Address.Value))
{
return true;
@@ -269,6 +284,7 @@ namespace Content.Server.Database
PlaytimeAtNote = serverBan.PlaytimeAtNote,
PlayerUserId = serverBan.UserId?.UserId,
RoleId = serverBan.Role,
ServerName = serverBan.ServerName
};
db.SqliteDbContext.RoleBan.Add(ban);
@@ -326,7 +342,8 @@ namespace Content.Server.Database
ban.Severity,
aUid,
unban,
ban.RoleId);
ban.RoleId,
ban.ServerName ??= GlobalServerName);
}
private static ServerRoleUnbanDef? ConvertRoleUnban(ServerRoleUnban? unban)
@@ -395,7 +412,8 @@ namespace Content.Server.Database
ban.Reason,
ban.Severity,
aUid,
unban);
unban,
ban.ServerName ??= GlobalServerName);
}
private static ServerUnbanDef? ConvertUnban(ServerUnban? unban)

View File

@@ -22,6 +22,8 @@ public sealed class ServerRoleBanDef
public ServerRoleUnbanDef? Unban { get; }
public string Role { get; }
public string? ServerName { get; }
public ServerRoleBanDef(
int? id,
NetUserId? userId,
@@ -35,7 +37,8 @@ public sealed class ServerRoleBanDef
NoteSeverity severity,
NetUserId? banningAdmin,
ServerRoleUnbanDef? unban,
string role)
string role,
string serverName)
{
if (userId == null && address == null && hwId == null)
{
@@ -62,5 +65,6 @@ public sealed class ServerRoleBanDef
BanningAdmin = banningAdmin;
Unban = unban;
Role = role;
ServerName = serverName;
}
}

View File

@@ -13,5 +13,6 @@ public record SharedServerBan(
DateTime? ExpirationTime,
string Reason,
string? BanningAdminName,
SharedServerUnban? Unban
SharedServerUnban? Unban,
string ServerName
);

View File

@@ -14,5 +14,6 @@ public sealed record SharedServerRoleBan(
string Reason,
string? BanningAdminName,
SharedServerUnban? Unban,
string Role
) : SharedServerBan(Id, UserId, Address, HWId, BanTime, ExpirationTime, Reason, BanningAdminName, Unban);
string Role,
string ServerName
) : SharedServerBan(Id, UserId, Address, HWId, BanTime, ExpirationTime, Reason, BanningAdminName, Unban, ServerName);

View File

@@ -29,12 +29,12 @@ public static class BanPanelEuiStateMsg
public uint Minutes { get; set; }
public string Reason { get; set; }
public NoteSeverity Severity { get; set; }
public string[]? Roles { get; set; }
public bool UseLastIp { get; set; }
public bool UseLastHwid { get; set; }
public bool Erase { get; set; }
public bool IsGlobalBan { get; set; }
public CreateBanRequest(string? player, (IPAddress, int)? ipAddress, bool useLastIp, byte[]? hwid, bool useLastHwid, uint minutes, string reason, NoteSeverity severity, string[]? roles, bool erase)
public CreateBanRequest(string? player, (IPAddress, int)? ipAddress, bool useLastIp, byte[]? hwid, bool useLastHwid, uint minutes, string reason, NoteSeverity severity, bool erase, bool isGlobalBan)
{
Player = player;
IpAddress = ipAddress == null ? null : $"{ipAddress.Value.Item1}/{ipAddress.Value.Item2}";
@@ -44,8 +44,8 @@ public static class BanPanelEuiStateMsg
Minutes = minutes;
Reason = reason;
Severity = severity;
Roles = roles;
Erase = erase;
IsGlobalBan = isGlobalBan;
}
}

View File

@@ -21,6 +21,7 @@ namespace Content.Shared.Administration
public AdminFlags PosFlags;
public AdminFlags NegFlags;
public int? RankId;
public string? AdminServer;
}
[Serializable, NetSerializable]
@@ -41,6 +42,7 @@ namespace Content.Shared.Administration
public AdminFlags PosFlags;
public AdminFlags NegFlags;
public int? RankId;
public string? AdminServer;
}
[Serializable, NetSerializable]
@@ -57,6 +59,7 @@ namespace Content.Shared.Administration
public AdminFlags PosFlags;
public AdminFlags NegFlags;
public int? RankId;
public string? AdminServer;
}