[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.BanningAdmin.Text = ban.BanningAdminName;
line.ServerName.Text = ban.ServerName == "unknown" ? "GLOBAL" : ban.ServerName;
} }
private void OnLineIdsClicked<T>(IBanListLine<T> line) where T : SharedServerBan private void OnLineIdsClicked<T>(IBanListLine<T> line) where T : SharedServerBan

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,17 +1,13 @@
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using Content.Client.Administration.UI.CustomControls;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Roles;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -20,7 +16,7 @@ namespace Content.Client.Administration.UI.BanPanel;
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class BanPanel : DefaultWindow 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; public event Action<string>? PlayerChanged;
private string? PlayerUsername { get; set; } private string? PlayerUsername { get; set; }
private (IPAddress, int)? IpAddress { get; set; } private (IPAddress, int)? IpAddress { get; set; }
@@ -29,9 +25,6 @@ public sealed partial class BanPanel : DefaultWindow
private uint Multiplier { get; set; } private uint Multiplier { get; set; }
private bool HasBanFlag { get; set; } private bool HasBanFlag { get; set; }
private TimeSpan? ButtonResetOn { 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!; [Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -39,8 +32,7 @@ public sealed partial class BanPanel : DefaultWindow
{ {
BasicInfo, BasicInfo,
//Text, //Text,
Players, Players
Roles
} }
private enum Multipliers private enum Multipliers
@@ -54,13 +46,6 @@ public sealed partial class BanPanel : DefaultWindow
Permanent Permanent
} }
private enum Types
{
None,
Server,
Role
}
public BanPanel() public BanPanel()
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
@@ -90,11 +75,6 @@ public sealed partial class BanPanel : DefaultWindow
HwidLine.Editable = HwidCheckbox.Pressed; HwidLine.Editable = HwidCheckbox.Pressed;
OnHwidChanged(); OnHwidChanged();
}; };
TypeOption.OnItemSelected += args =>
{
TypeOption.SelectId(args.Id);
OnTypeChanged();
};
LastConnCheckbox.OnPressed += args => LastConnCheckbox.OnPressed += args =>
{ {
IpLine.ModulateSelfOverride = null; IpLine.ModulateSelfOverride = null;
@@ -104,13 +84,6 @@ public sealed partial class BanPanel : DefaultWindow
}; };
SubmitButton.OnPressed += SubmitButtonOnOnPressed; 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-minutes"), (int) Multipliers.Minutes);
MultiplierOption.AddItem(Loc.GetString("ban-panel-hours"), (int) Multipliers.Hours); MultiplierOption.AddItem(Loc.GetString("ban-panel-hours"), (int) Multipliers.Hours);
MultiplierOption.AddItem(Loc.GetString("ban-panel-days"), (int) Multipliers.Days); 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.BasicInfo, Loc.GetString("ban-panel-tabs-basic"));
//Tabs.SetTabTitle((int) TabNumbers.Text, Loc.GetString("ban-panel-tabs-reason")); //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.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")); 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) public void UpdateBanFlag(bool newFlag)
@@ -349,12 +240,6 @@ public sealed partial class BanPanel : DefaultWindow
Hwid = Convert.FromHexString(hwidString); Hwid = Convert.FromHexString(hwidString);
} }
private void OnTypeChanged()
{
TypeOption.ModulateSelfOverride = null;
Tabs.SetTabVisible((int) TabNumbers.Roles, TypeOption.SelectedId == (int) Types.Role);
}
private void UpdateSubmitEnabled() private void UpdateSubmitEnabled()
{ {
SubmitButton.Disabled = ErrorLevel != ErrorLevelEnum.None; SubmitButton.Disabled = ErrorLevel != ErrorLevelEnum.None;
@@ -390,31 +275,6 @@ public sealed partial class BanPanel : DefaultWindow
private void SubmitButtonOnOnPressed(BaseButton.ButtonEventArgs obj) 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); var reason = Rope.Collapse(ReasonTextEdit.TextRope);
if (string.IsNullOrWhiteSpace(reason)) if (string.IsNullOrWhiteSpace(reason))
{ {
@@ -437,9 +297,10 @@ public sealed partial class BanPanel : DefaultWindow
var player = PlayerCheckbox.Pressed ? PlayerUsername : null; var player = PlayerCheckbox.Pressed ? PlayerUsername : null;
var useLastIp = IpCheckbox.Pressed && LastConnCheckbox.Pressed && IpAddress is null; var useLastIp = IpCheckbox.Pressed && LastConnCheckbox.Pressed && IpAddress is null;
var useLastHwid = HwidCheckbox.Pressed && LastConnCheckbox.Pressed && Hwid is null; var useLastHwid = HwidCheckbox.Pressed && LastConnCheckbox.Pressed && Hwid is null;
var severity = (NoteSeverity) SeverityOption.SelectedId;
var erase = EraseCheckbox.Pressed; 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) protected override void FrameUpdate(FrameEventArgs args)

View File

@@ -14,8 +14,8 @@ public sealed class BanPanelEui : BaseEui
{ {
BanPanel = new BanPanel(); BanPanel = new BanPanel();
BanPanel.OnClose += () => SendMessage(new CloseEuiMessage()); BanPanel.OnClose += () => SendMessage(new CloseEuiMessage());
BanPanel.BanSubmitted += (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, roles, erase)); => SendMessage(new BanPanelEuiStateMsg.CreateBanRequest(player, ip, useLastIp, hwid, useLastHwid, minutes, reason, severity, erase, isGlobalBan));
BanPanel.PlayerChanged += player => SendMessage(new BanPanelEuiStateMsg.GetPlayerInfoRequest(player)); 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 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) if (popup.SourceData is { } src)
{ {
SendMessage(new UpdateAdmin SendMessage(new UpdateAdmin
@@ -139,7 +141,8 @@ namespace Content.Client.Administration.UI
Title = title, Title = title,
PosFlags = pos, PosFlags = pos,
NegFlags = neg, NegFlags = neg,
RankId = rank RankId = rank,
AdminServer = adminServer
}); });
} }
else else
@@ -152,7 +155,8 @@ namespace Content.Client.Administration.UI
Title = title, Title = title,
PosFlags = pos, PosFlags = pos,
NegFlags = neg, NegFlags = neg,
RankId = rank RankId = rank,
AdminServer = adminServer
}); });
} }
@@ -251,6 +255,11 @@ namespace Content.Client.Administration.UI
HorizontalAlignment = Control.HAlignment.Center, HorizontalAlignment = Control.HAlignment.Center,
}); });
al.AddChild(new Label
{
Text = admin.AdminServer,
});
var editButton = new Button { Text = Loc.GetString("permissions-eui-edit-title-button") }; var editButton = new Button { Text = Loc.GetString("permissions-eui-edit-title-button") };
editButton.OnPressed += _ => OnEditPressed(admin); editButton.OnPressed += _ => OnEditPressed(admin);
al.AddChild(editButton); al.AddChild(editButton);
@@ -351,6 +360,7 @@ namespace Content.Client.Administration.UI
public readonly OptionButton RankButton; public readonly OptionButton RankButton;
public readonly Button SaveButton; public readonly Button SaveButton;
public readonly Button? RemoveButton; public readonly Button? RemoveButton;
public readonly LineEdit ServerNameEdit;
public readonly Dictionary<AdminFlags, (Button inherit, Button sub, Button plus)> FlagButtons public readonly Dictionary<AdminFlags, (Button inherit, Button sub, Button plus)> FlagButtons
= new(); = new();
@@ -380,6 +390,7 @@ namespace Content.Client.Administration.UI
TitleEdit = new LineEdit { PlaceHolder = Loc.GetString("permissions-eui-edit-admin-window-title-edit-placeholder") }; TitleEdit = new LineEdit { PlaceHolder = Loc.GetString("permissions-eui-edit-admin-window-title-edit-placeholder") };
RankButton = new OptionButton(); RankButton = new OptionButton();
SaveButton = new Button { Text = Loc.GetString("permissions-eui-edit-admin-window-save-button"), HorizontalAlignment = HAlignment.Right }; 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); RankButton.AddItem(Loc.GetString("permissions-eui-edit-admin-window-no-rank-button"), NoRank);
foreach (var (rId, rank) in ui._ranks) foreach (var (rId, rank) in ui._ranks)
@@ -496,6 +507,7 @@ namespace Content.Client.Administration.UI
{ {
nameControl, nameControl,
TitleEdit, TitleEdit,
ServerNameEdit,
RankButton 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 () => Assert.Multiple(async () =>
{ {
// No bans should be returned // 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 // Direct id lookup returns a pardoned ban
Assert.That(await sDatabase.GetServerBanAsync(1), Is.Not.Null); 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 int? AdminRankId { get; set; }
public AdminRank? AdminRank { get; set; } public AdminRank? AdminRank { get; set; }
public List<AdminFlag> Flags { get; set; } = default!; public List<AdminFlag> Flags { get; set; } = default!;
public string? AdminServer { get; set; }
} }
public class AdminFlag public class AdminFlag
@@ -694,6 +695,7 @@ namespace Content.Server.Database
public bool Hidden { get; set; } public bool Hidden { get; set; }
public List<ServerBanHit> BanHits { get; set; } = null!; public List<ServerBanHit> BanHits { get; set; } = null!;
public string? ServerName { get; set; }
} }
/// <summary> /// <summary>
@@ -829,6 +831,7 @@ namespace Content.Server.Database
public bool Hidden { get; set; } public bool Hidden { get; set; }
public string RoleId { get; set; } = null!; public string RoleId { get; set; } = null!;
public string? ServerName { get; set; }
} }
[Table("server_role_unban")] [Table("server_role_unban")]

View File

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

View File

@@ -52,7 +52,7 @@ public sealed class BanPanelEui : BaseEui
switch (msg) switch (msg)
{ {
case BanPanelEuiStateMsg.CreateBanRequest r: 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; break;
case BanPanelEuiStateMsg.GetPlayerInfoRequest r: case BanPanelEuiStateMsg.GetPlayerInfoRequest r:
ChangePlayer(r.PlayerUsername); 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)) if (!_admins.HasAdminFlag(Player, AdminFlags.Ban))
{ {
_sawmill.Warning($"{Player.Name} ({Player.UserId}) tried to create a ban with no ban flag"); _sawmill.Warning($"{Player.Name} ({Player.UserId}) tried to create a ban with no ban flag");
return; return;
} }
if (target == null && string.IsNullOrWhiteSpace(ipAddressString) && hwid == null) if (target == null && string.IsNullOrWhiteSpace(ipAddressString) && hwid == null)
{ {
_chat.DispatchServerMessage(Player, Loc.GetString("ban-panel-no-data")); _chat.DispatchServerMessage(Player, Loc.GetString("ban-panel-no-data"));
@@ -119,21 +120,7 @@ public sealed class BanPanelEui : BaseEui
targetHWid = useLastHwid ? located.LastHWId : hwid; targetHWid = useLastHwid ? located.LastHWId : hwid;
} }
if (roles?.Count > 0) if (erase && targetUid != null && _playerManager.TryGetSessionById(targetUid.Value, out var targetPlayer))
{
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))
{ {
try 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(); Close();
} }

View File

@@ -7,13 +7,11 @@ using Robust.Server.Player;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Console; using Robust.Shared.Console;
namespace Content.Server.Administration.Commands; namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.Ban)] [AdminCommand(AdminFlags.Ban)]
public sealed class BanCommand : LocalizedCommands public sealed class BanCommand : LocalizedCommands
{ {
[Dependency] private readonly IPlayerLocator _locator = default!; [Dependency] private readonly IPlayerLocator _locator = default!;
[Dependency] private readonly IBanManager _bans = default!; [Dependency] private readonly IBanManager _bans = default!;
[Dependency] private readonly IConfigurationManager _cfg = 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) public override async void Execute(IConsoleShell shell, string argStr, string[] args)
{ {
string target; var target = args[0];
string reason; var reason = args[1];
uint minutes; var minutes = 0u;
var isGlobalBan = false;
if (!Enum.TryParse(_cfg.GetCVar(CCVars.ServerBanDefaultSeverity), out NoteSeverity severity)) 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; severity = NoteSeverity.High;
} }
switch (args.Length) switch (args.Length)
{ {
case 2: case 2:
target = args[0];
reason = args[1];
minutes = 0;
break; break;
case 3: case 3:
target = args[0];
reason = args[1];
if (!uint.TryParse(args[2], out minutes)) if (!uint.TryParse(args[2], out minutes))
{ {
shell.WriteLine(Loc.GetString("cmd-ban-invalid-minutes", ("minutes", args[2]))); shell.WriteLine(Loc.GetString("cmd-ban-invalid-minutes", ("minutes", args[2])));
@@ -52,8 +48,6 @@ public sealed class BanCommand : LocalizedCommands
break; break;
case 4: case 4:
target = args[0];
reason = args[1];
if (!uint.TryParse(args[2], out minutes)) if (!uint.TryParse(args[2], out minutes))
{ {
@@ -69,6 +63,29 @@ public sealed class BanCommand : LocalizedCommands
return; 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; break;
default: default:
shell.WriteLine(Loc.GetString("cmd-ban-invalid-arguments")); shell.WriteLine(Loc.GetString("cmd-ban-invalid-arguments"));
@@ -88,22 +105,12 @@ public sealed class BanCommand : LocalizedCommands
var targetUid = located.UserId; var targetUid = located.UserId;
var targetHWid = located.LastHWId; 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) public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{ {
if (args.Length == 1)
{
var options = _playerManager.Sessions.Select(c => c.Name).OrderBy(c => c).ToArray();
return CompletionResult.FromHintOptions(options, LocalizationManager.GetString("cmd-ban-hint"));
}
if (args.Length == 2)
return CompletionResult.FromHint(LocalizationManager.GetString("cmd-ban-hint-reason"));
if (args.Length == 3)
{
var durations = new CompletionOption[] var durations = new CompletionOption[]
{ {
new("0", LocalizationManager.GetString("cmd-ban-hint-duration-1")), new("0", LocalizationManager.GetString("cmd-ban-hint-duration-1")),
@@ -114,11 +121,6 @@ public sealed class BanCommand : LocalizedCommands
new("43800", LocalizationManager.GetString("cmd-ban-hint-duration-6")), new("43800", LocalizationManager.GetString("cmd-ban-hint-duration-6")),
}; };
return CompletionResult.FromHintOptions(durations, LocalizationManager.GetString("cmd-ban-hint-duration"));
}
if (args.Length == 4)
{
var severities = new CompletionOption[] var severities = new CompletionOption[]
{ {
new("none", Loc.GetString("admin-note-editor-severity-none")), new("none", Loc.GetString("admin-note-editor-severity-none")),
@@ -127,9 +129,14 @@ public sealed class BanCommand : LocalizedCommands
new("high", Loc.GetString("admin-note-editor-severity-high")), new("high", Loc.GetString("admin-note-editor-severity-high")),
}; };
return CompletionResult.FromHintOptions(severities, Loc.GetString("cmd-ban-hint-severity")); return args.Length switch
} {
1 => CompletionResult.FromHintOptions(CompletionHelper.SessionNames(),
return CompletionResult.Empty; 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.Database;
using Content.Server.EUI; using Content.Server.EUI;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.CCVar;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Console; using Robust.Shared.Console;
namespace Content.Server.Administration.Commands; namespace Content.Server.Administration.Commands;
@@ -17,11 +19,13 @@ public sealed class BanListCommand : LocalizedCommands
[Dependency] private readonly IServerDbManager _dbManager = default!; [Dependency] private readonly IServerDbManager _dbManager = default!;
[Dependency] private readonly EuiManager _eui = default!; [Dependency] private readonly EuiManager _eui = default!;
[Dependency] private readonly IPlayerLocator _locator = default!; [Dependency] private readonly IPlayerLocator _locator = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
public override string Command => "banlist"; public override string Command => "banlist";
public override async void Execute(IConsoleShell shell, string argStr, string[] args) public override async void Execute(IConsoleShell shell, string argStr, string[] args)
{ {
var serverName = _cfg.GetCVar(CCVars.AdminLogsServerName);
if (args.Length != 1) if (args.Length != 1)
{ {
shell.WriteError(Help); shell.WriteError(Help);
@@ -38,7 +42,7 @@ public sealed class BanListCommand : LocalizedCommands
if (shell.Player is not { } player) 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) if (bans.Count == 0)
{ {
@@ -48,7 +52,7 @@ public sealed class BanListCommand : LocalizedCommands
foreach (var ban in bans) foreach (var ban in bans)
{ {
var msg = $"{ban.Id}: {ban.Reason}"; var msg = $"{ban.Id}: {ban.Reason} ({ban.ServerName})";
shell.WriteLine(msg); shell.WriteLine(msg);
} }

View File

@@ -23,10 +23,12 @@ public sealed class DepartmentBanCommand : IConsoleCommand
public async void Execute(IConsoleShell shell, string argStr, string[] args) public async void Execute(IConsoleShell shell, string argStr, string[] args)
{ {
string target;
string department; var target = args[0];
string reason; var department = args[1];
uint minutes; var reason = args[2];
var minutes = 0u;
var isGlobalBan = false;
if (!Enum.TryParse(_cfg.GetCVar(CCVars.DepartmentBanDefaultSeverity), out NoteSeverity severity)) 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."); 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) switch (args.Length)
{ {
case 3: case 3:
target = args[0];
department = args[1];
reason = args[2];
minutes = 0;
break; break;
case 4: case 4:
target = args[0]; if (!uint.TryParse(args[3], out minutes))
department = args[1]; {
reason = args[2]; shell.WriteError(Loc.GetString("cmd-roleban-minutes-parse", ("time", args[3]), ("help", Help)));
return;
}
break;
case 5:
if (!uint.TryParse(args[3], out minutes)) if (!uint.TryParse(args[3], out minutes))
{ {
shell.WriteError(Loc.GetString("cmd-roleban-minutes-parse", ("time", args[3]), ("help", Help))); shell.WriteError(Loc.GetString("cmd-roleban-minutes-parse", ("time", args[3]), ("help", Help)));
return; 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; break;
case 5: case 6:
target = args[0];
department = args[1];
reason = args[2];
if (!uint.TryParse(args[3], out minutes)) if (!uint.TryParse(args[3], out minutes))
{ {
shell.WriteError(Loc.GetString("cmd-roleban-minutes-parse", ("time", args[3]), ("help", Help))); shell.WriteError(Loc.GetString("cmd-roleban-minutes-parse", ("time", args[3]), ("help", Help)));
@@ -70,6 +72,11 @@ public sealed class DepartmentBanCommand : IConsoleCommand
return; return;
} }
if (!bool.TryParse(args[5], out isGlobalBan))
{
shell.WriteLine($"{args[5]} should be True or False.\n{Help}");
return;
}
break; break;
default: default:
shell.WriteError(Loc.GetString("cmd-roleban-arg-count")); shell.WriteError(Loc.GetString("cmd-roleban-arg-count"));
@@ -97,7 +104,7 @@ public sealed class DepartmentBanCommand : IConsoleCommand
var now = DateTimeOffset.UtcNow; var now = DateTimeOffset.UtcNow;
foreach (var job in departmentProto.Roles) 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")), 3 => CompletionResult.FromHint(Loc.GetString("cmd-roleban-hint-3")),
4 => CompletionResult.FromHintOptions(durOpts, Loc.GetString("cmd-roleban-hint-4")), 4 => CompletionResult.FromHintOptions(durOpts, Loc.GetString("cmd-roleban-hint-4")),
5 => CompletionResult.FromHintOptions(severities, Loc.GetString("cmd-roleban-hint-5")), 5 => CompletionResult.FromHintOptions(severities, Loc.GetString("cmd-roleban-hint-5")),
6 => CompletionResult.FromHint("<server>"),
_ => CompletionResult.Empty _ => CompletionResult.Empty
}; };
} }

View File

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

View File

@@ -374,6 +374,15 @@ namespace Content.Server.Administration.Managers
Flags = flags 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) if (dbData.Title != null)
{ {
data.Title = dbData.Title; 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 SawmillId = "admin.bans";
public const string JobPrefix = "Job:"; public const string JobPrefix = "Job:";
public const string UnknownServer = "unknown";
private readonly Dictionary<NetUserId, HashSet<ServerRoleBanDef>> _cachedRoleBans = new(); 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)) if (e.NewStatus != SessionStatus.Connected || _cachedRoleBans.ContainsKey(e.Session.UserId))
return; return;
var netChannel = e.Session.ConnectedClient; var netChannel = e.Session.Channel;
ImmutableArray<byte>? hwId = netChannel.UserData.HWId.Length == 0 ? null : netChannel.UserData.HWId; ImmutableArray<byte>? hwId = netChannel.UserData.HWId.Length == 0 ? null : netChannel.UserData.HWId;
await CacheDbRoleBans(e.Session.UserId, netChannel.RemoteEndPoint.Address, hwId); await CacheDbRoleBans(e.Session.UserId, netChannel.RemoteEndPoint.Address, hwId);
@@ -112,7 +113,7 @@ public sealed class BanManager : IBanManager, IPostInjectInit
} }
#region Server Bans #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; DateTimeOffset? expires = null;
if (minutes > 0) if (minutes > 0)
@@ -120,6 +121,13 @@ public sealed class BanManager : IBanManager, IPostInjectInit
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes.Value); expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes.Value);
} }
var serverName = _cfg.GetCVar(CCVars.AdminLogsServerName);
if (isGlobalBan)
{
serverName = UnknownServer;
}
_systems.TryGetEntitySystem<GameTicker>(out var ticker); _systems.TryGetEntitySystem<GameTicker>(out var ticker);
int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId; 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; 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, reason,
severity, severity,
banningAdmin, banningAdmin,
null); null,
serverName);
await _db.AddServerBanAsync(banDef); await _db.AddServerBanAsync(banDef);
var adminName = banningAdmin == null var adminName = banningAdmin == null
@@ -175,14 +184,14 @@ public sealed class BanManager : IBanManager, IPostInjectInit
return; return;
// If they are, kick them // If they are, kick them
var message = banDef.FormatBanMessage(_cfg, _localizationManager); var message = banDef.FormatBanMessage(_cfg, _localizationManager);
targetPlayer.ConnectedClient.Disconnect(message); targetPlayer.Channel.Disconnect(message);
} }
#endregion #endregion
#region Job Bans #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. // 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. // 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? _)) if (!_prototypeManager.TryIndex(role, out JobPrototype? _))
{ {
@@ -196,6 +205,13 @@ public sealed class BanManager : IBanManager, IPostInjectInit
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes.Value); expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes.Value);
} }
var serverName = _cfg.GetCVar(CCVars.AdminLogsServerName);
if (isGlobalBan)
{
serverName = UnknownServer;
}
_systems.TryGetEntitySystem(out GameTicker? ticker); _systems.TryGetEntitySystem(out GameTicker? ticker);
int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId; 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; 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, severity,
banningAdmin, banningAdmin,
null, null,
role); role,
serverName);
if (!await AddRoleBan(banDef)) if (!await AddRoleBan(banDef))
{ {
@@ -293,7 +310,7 @@ public sealed class BanManager : IBanManager, IPostInjectInit
}; };
_sawmill.Debug($"Sent rolebans to {pSession.Name}"); _sawmill.Debug($"Sent rolebans to {pSession.Name}");
_netManager.ServerSendMessage(bans, pSession.ConnectedClient); _netManager.ServerSendMessage(bans, pSession.Channel);
} }
public void PostInject() 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="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="severity">Severity of the resulting ban note</param>
/// <param name="reason">Reason for the ban</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>? GetRoleBans(NetUserId playerUserId);
public HashSet<string>? GetJobBans(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="reason">Reason for the ban</param>
/// <param name="minutes">Number of minutes to ban for. 0 and null mean permanent</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> /// <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> /// <summary>
/// Pardons a role ban for the specified target, username or GUID /// 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, Title = p.a.Title,
RankId = p.a.AdminRankId, RankId = p.a.AdminRankId,
UserId = new NetUserId(p.a.UserId), UserId = new NetUserId(p.a.UserId),
UserName = p.lastUserName UserName = p.lastUserName,
AdminServer = p.a.AdminServer
}).ToArray(), }).ToArray(),
AdminRanks = _adminRanks.ToDictionary(a => a.Id, a => new PermissionsEuiState.AdminRankData AdminRanks = _adminRanks.ToDictionary(a => a.Id, a => new PermissionsEuiState.AdminRankData
@@ -251,6 +252,7 @@ namespace Content.Server.Administration.UI
admin.Title = ua.Title; admin.Title = ua.Title;
admin.AdminRankId = ua.RankId; admin.AdminRankId = ua.RankId;
admin.AdminServer = ua.AdminServer;
admin.Flags = GenAdminFlagList(ua.PosFlags, ua.NegFlags); admin.Flags = GenAdminFlagList(ua.PosFlags, ua.NegFlags);
await _db.UpdateAdminAsync(admin); await _db.UpdateAdminAsync(admin);
@@ -265,8 +267,9 @@ namespace Content.Server.Administration.UI
var name = playerRecord?.LastSeenUserName ?? ua.UserId.ToString(); var name = playerRecord?.LastSeenUserName ?? ua.UserId.ToString();
var title = ua.Title ?? "<no title>"; var title = ua.Title ?? "<no title>";
var flags = AdminFlagsHelper.PosNegFlagsText(ua.PosFlags, ua.NegFlags); 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)) if (_playerManager.TryGetSessionById(ua.UserId, out var player))
{ {
@@ -333,15 +336,17 @@ namespace Content.Server.Administration.UI
Flags = GenAdminFlagList(ca.PosFlags, ca.NegFlags), Flags = GenAdminFlagList(ca.PosFlags, ca.NegFlags),
AdminRankId = ca.RankId, AdminRankId = ca.RankId,
UserId = userId.UserId, UserId = userId.UserId,
Title = ca.Title Title = ca.Title,
AdminServer = ca.AdminServer
}; };
await _db.AddAdminAsync(admin); await _db.AddAdminAsync(admin);
var title = ca.Title ?? "<no title>"; var title = ca.Title ?? "<no title>";
var flags = AdminFlagsHelper.PosNegFlagsText(ca.PosFlags, ca.NegFlags); 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)) if (_playerManager.TryGetSessionById(userId, out var player))
{ {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,5 +14,6 @@ public sealed record SharedServerRoleBan(
string Reason, string Reason,
string? BanningAdminName, string? BanningAdminName,
SharedServerUnban? Unban, SharedServerUnban? Unban,
string Role string Role,
) : SharedServerBan(Id, UserId, Address, HWId, BanTime, ExpirationTime, Reason, BanningAdminName, Unban); 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 uint Minutes { get; set; }
public string Reason { get; set; } public string Reason { get; set; }
public NoteSeverity Severity { get; set; } public NoteSeverity Severity { get; set; }
public string[]? Roles { get; set; }
public bool UseLastIp { get; set; } public bool UseLastIp { get; set; }
public bool UseLastHwid { get; set; } public bool UseLastHwid { get; set; }
public bool Erase { 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; Player = player;
IpAddress = ipAddress == null ? null : $"{ipAddress.Value.Item1}/{ipAddress.Value.Item2}"; IpAddress = ipAddress == null ? null : $"{ipAddress.Value.Item1}/{ipAddress.Value.Item2}";
@@ -44,8 +44,8 @@ public static class BanPanelEuiStateMsg
Minutes = minutes; Minutes = minutes;
Reason = reason; Reason = reason;
Severity = severity; Severity = severity;
Roles = roles;
Erase = erase; Erase = erase;
IsGlobalBan = isGlobalBan;
} }
} }

View File

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