Client command perms cleanup. (#9599)

This commit is contained in:
Pieter-Jan Briers
2022-07-10 15:43:44 +02:00
committed by GitHub
parent d3c1c9b330
commit 221c23000e
5 changed files with 144 additions and 93 deletions

View File

@@ -1,13 +1,8 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Client.Console; using Robust.Client.Console;
using Robust.Shared.Console; using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Reflection; using Robust.Shared.Utility;
namespace Content.Client.Administration.Managers namespace Content.Client.Administration.Managers
{ {
@@ -15,10 +10,13 @@ namespace Content.Client.Administration.Managers
{ {
[Dependency] private readonly IClientNetManager _netMgr = default!; [Dependency] private readonly IClientNetManager _netMgr = default!;
[Dependency] private readonly IClientConGroupController _conGroup = default!; [Dependency] private readonly IClientConGroupController _conGroup = default!;
[Dependency] private readonly IResourceManager _res = default!;
private AdminData? _adminData; private AdminData? _adminData;
private readonly HashSet<string> _availableCommands = new(); private readonly HashSet<string> _availableCommands = new();
private readonly AdminCommandPermissions _localCommandPermissions = new();
public event Action? AdminStatusUpdated; public event Action? AdminStatusUpdated;
public bool IsActive() public bool IsActive()
@@ -33,6 +31,16 @@ namespace Content.Client.Administration.Managers
public bool CanCommand(string cmdName) public bool CanCommand(string cmdName)
{ {
if (_adminData != null && _adminData.HasFlag(AdminFlags.Host))
{
// Host can execute all commands when connected.
// Kind of a shortcut to avoid pains during development.
return true;
}
if (_localCommandPermissions.CanCommand(cmdName, _adminData))
return true;
return _availableCommands.Contains(cmdName); return _availableCommands.Contains(cmdName);
} }
@@ -59,6 +67,12 @@ namespace Content.Client.Administration.Managers
public void Initialize() public void Initialize()
{ {
_netMgr.RegisterNetMessage<MsgUpdateAdminStatus>(UpdateMessageRx); _netMgr.RegisterNetMessage<MsgUpdateAdminStatus>(UpdateMessageRx);
// Load flags for engine commands, since those don't have the attributes.
if (_res.TryContentFileRead(new ResourcePath("/clientCommandPerms.yml"), out var efs))
{
_localCommandPermissions.LoadPermissionsFromStream(efs);
}
} }
private void UpdateMessageRx(MsgUpdateAdminStatus message) private void UpdateMessageRx(MsgUpdateAdminStatus message)

View File

@@ -1,4 +1,3 @@
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -15,7 +14,6 @@ using Robust.Shared.ContentPack;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Content.Server.Administration.Managers namespace Content.Server.Administration.Managers
@@ -42,10 +40,7 @@ namespace Content.Server.Administration.Managers
public IEnumerable<IPlayerSession> AllAdmins => _admins.Select(p => p.Key); public IEnumerable<IPlayerSession> AllAdmins => _admins.Select(p => p.Key);
// If a command isn't in this list it's server-console only. private readonly AdminCommandPermissions _commandPermissions = new();
// if a command is in but the flags value is null it's available to everybody.
private readonly HashSet<string> _anyCommands = new();
private readonly Dictionary<string, AdminFlags[]> _adminCommands = new();
public bool IsAdmin(IPlayerSession session, bool includeDeAdmin = false) public bool IsAdmin(IPlayerSession session, bool includeDeAdmin = false)
{ {
@@ -180,63 +175,19 @@ namespace Content.Server.Administration.Managers
if (flagsReq.Length != 0) if (flagsReq.Length != 0)
{ {
_adminCommands.Add(cmdName, flagsReq); _commandPermissions.AdminCommands.Add(cmdName, flagsReq);
} }
else else
{ {
_anyCommands.Add(cmdName); _commandPermissions.AnyCommands.Add(cmdName);
} }
} }
// Load flags for engine commands, since those don't have the attributes. // Load flags for engine commands, since those don't have the attributes.
if (_res.TryContentFileRead(new ResourcePath("/engineCommandPerms.yml"), out var efs)) if (_res.TryContentFileRead(new ResourcePath("/engineCommandPerms.yml"), out var efs))
{ {
LoadPermissionsFromStream(efs); _commandPermissions.LoadPermissionsFromStream(efs);
} }
// Load flags for client-only commands, those don't have the flag attributes, only "AnyCommand".
if (_res.TryContentFileRead(new ResourcePath("/clientCommandPerms.yml"), out var cfs))
{
LoadPermissionsFromStream(cfs);
}
}
private void LoadPermissionsFromStream(Stream fs)
{
using var reader = new StreamReader(fs, EncodingHelpers.UTF8);
var yStream = new YamlStream();
yStream.Load(reader);
var root = (YamlSequenceNode) yStream.Documents[0].RootNode;
foreach (var child in root)
{
var map = (YamlMappingNode) child;
var commands = map.GetNode<YamlSequenceNode>("Commands").Select(p => p.AsString());
if (map.TryGetNode("Flags", out var flagsNode))
{
var flagNames = flagsNode.AsString().Split(",", StringSplitOptions.RemoveEmptyEntries);
var flags = AdminFlagsHelper.NamesToFlags(flagNames);
foreach (var cmd in commands)
{
if (!_adminCommands.TryGetValue(cmd, out var exFlags))
{
_adminCommands.Add(cmd, new[] {flags});
}
else
{
var newArr = new AdminFlags[exFlags.Length + 1];
exFlags.CopyTo(newArr, 0);
exFlags[^1] = flags;
_adminCommands[cmd] = newArr;
}
}
}
else
{
_anyCommands.UnionWith(commands);
}
}
} }
public void PromoteHost(IPlayerSession player) public void PromoteHost(IPlayerSession player)
@@ -257,13 +208,13 @@ namespace Content.Server.Administration.Managers
{ {
var msg = new MsgUpdateAdminStatus(); var msg = new MsgUpdateAdminStatus();
var commands = new List<string>(_anyCommands); var commands = new List<string>(_commandPermissions.AnyCommands);
if (_admins.TryGetValue(session, out var adminData)) if (_admins.TryGetValue(session, out var adminData))
{ {
msg.Admin = adminData.Data; msg.Admin = adminData.Data;
commands.AddRange(_adminCommands commands.AddRange(_commandPermissions.AdminCommands
.Where(p => p.Value.Any(f => adminData.Data.HasFlag(f))) .Where(p => p.Value.Any(f => adminData.Data.HasFlag(f)))
.Select(p => p.Key)); .Select(p => p.Key));
} }
@@ -400,13 +351,13 @@ namespace Content.Server.Administration.Managers
public bool CanCommand(IPlayerSession session, string cmdName) public bool CanCommand(IPlayerSession session, string cmdName)
{ {
if (_anyCommands.Contains(cmdName)) if (_commandPermissions.AnyCommands.Contains(cmdName))
{ {
// Anybody can use this command. // Anybody can use this command.
return true; return true;
} }
if (!_adminCommands.TryGetValue(cmdName, out var flagsReq)) if (!_commandPermissions.AdminCommands.TryGetValue(cmdName, out var flagsReq))
{ {
// Server-console only. // Server-console only.
return false; return false;

View File

@@ -0,0 +1,83 @@
using System.IO;
using System.Linq;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Content.Shared.Administration;
public sealed class AdminCommandPermissions
{
// Commands executable by anybody.
public readonly HashSet<string> AnyCommands = new();
// Commands only executable by admins with one of the given flag masks.
public readonly Dictionary<string, AdminFlags[]> AdminCommands = new();
public void LoadPermissionsFromStream(Stream fs)
{
using var reader = new StreamReader(fs, EncodingHelpers.UTF8);
var yStream = new YamlStream();
yStream.Load(reader);
var root = (YamlSequenceNode) yStream.Documents[0].RootNode;
foreach (var child in root)
{
var map = (YamlMappingNode) child;
var commands = map.GetNode<YamlSequenceNode>("Commands").Select(p => p.AsString());
if (map.TryGetNode("Flags", out var flagsNode))
{
var flagNames = flagsNode.AsString().Split(",", StringSplitOptions.RemoveEmptyEntries);
var flags = AdminFlagsHelper.NamesToFlags(flagNames);
foreach (var cmd in commands)
{
if (!AdminCommands.TryGetValue(cmd, out var exFlags))
{
AdminCommands.Add(cmd, new[] {flags});
}
else
{
var newArr = new AdminFlags[exFlags.Length + 1];
exFlags.CopyTo(newArr, 0);
exFlags[^1] = flags;
AdminCommands[cmd] = newArr;
}
}
}
else
{
AnyCommands.UnionWith(commands);
}
}
}
public bool CanCommand(string cmdName, AdminData? admin)
{
if (AnyCommands.Contains(cmdName))
{
// Anybody can use this command.
return true;
}
if (!AdminCommands.TryGetValue(cmdName, out var flagsReq))
{
// Server-console only.
return false;
}
if (admin == null)
{
// Player isn't an admin.
return false;
}
foreach (var flagReq in flagsReq)
{
if (admin.HasFlag(flagReq))
{
return true;
}
}
return false;
}
}

View File

@@ -1,3 +1,31 @@
# Available to everybody
- Commands:
- disconnect
- help
- list
- quit
- hardquit
- svbind
- bind
- exec # macro moment
- cls
- vram
- monitor
- setmonitor
- keyinfo
- setclipboard
- getclipboard
- net_graph
- net_watchent
- net_draw_interp
- devwindow
- fill
- dumpentities
- ">"
- gcf
- gc
- gc_mode
- Flags: DEBUG - Flags: DEBUG
Commands: Commands:
- atvrange - atvrange

View File

@@ -1,32 +1,3 @@
# Available to everybody
- Commands:
- disconnect
- help
- list
- quit
- hardquit
- cvar
- svbind
- bind
- exec # macro moment
- clear
- vram
- monitor
- setmonitor
- keyinfo
- setclipboard
- getclipboard
- gcf
- net_graph
- net_watchent
- net_draw_interp
- devwindow
- fill
- dumpentities
- getcomponentregistration
- fuck
- ">"
- Flags: VAREDIT - Flags: VAREDIT
Commands: Commands:
- addcomp - addcomp
@@ -141,6 +112,10 @@
- scsi - scsi
- csi - csi
- lsasm - lsasm
- cvar
- gcf
- getcomponentregistration
- fuck
- Flags: QUERY - Flags: QUERY
Commands: Commands: