Code/SboxSuperuser/Engine/Commands/HostCommandDispatcher.cs

HostCommandDispatcher for the host/runtime. It validates and dispatches incoming command requests, enforces policy and permissions, queues execution (regular and lifecycle control queues), performs argument parsing, audit submission, and returns CommandResult values.

File Access
using SboxSuperuser.Core.Commands;
using SboxSuperuser.Engine.Authority;
using SboxSuperuser.Engine.Audit;

namespace SboxSuperuser.Engine.Commands;

internal sealed partial class HostCommandDispatcher : ICommandDispatcher, IDisposable
{
	private readonly HostPolicyService _policy;
	private readonly CommandRegistry _registry;
	private readonly Func<bool> _available;
	private readonly Func<IReadOnlyList<CommandPlayer>> _players;
	private readonly Action<Exception> _report;
	private readonly CancellationTokenSource _lifetime = new();
	private readonly RequestQueue<CommandResult> _queue;
	private readonly RequestQueue<CommandResult> _controlQueue;
	private bool _disposed;
	private readonly IAuditWriter _audit;

	internal HostCommandDispatcher( HostPolicyService policy, CommandRegistry registry, Func<bool> available,
		Func<IReadOnlyList<CommandPlayer>> players, Action<Exception> report, Func<DateTimeOffset> clock = null, IAuditWriter audit = null )
	{
		_policy = policy;
		_registry = registry;
		_available = available;
		_players = players;
		_report = report;
		_audit = audit;
		_queue = new RequestQueue<CommandResult>( clock ?? (() => DateTimeOffset.UtcNow), Result( CommandStatus.Cancelled, "Cancelled before execution." ) );
		_controlQueue = new RequestQueue<CommandResult>( clock ?? (() => DateTimeOffset.UtcNow),
			Result( CommandStatus.Cancelled, "Cancelled before execution." ), 8, 2, 128, 32 );
	}

	internal async Task<CommandResult> SubmitAsync( Func<ActorIdentity> authenticate, CommandOrigin origin, CommandRequest request,
		CancellationToken cancellation, CommandService service = null )
	{
		if ( !_available() || _disposed )
		{
			return Result( CommandStatus.Unsupported, "An eligible authoritative runtime on the main thread is required." );
		}

		if ( request is null )
		{
			return Result( CommandStatus.InvalidArguments, "A command request is required." );
		}

		using var linked = CancellationTokenSource.CreateLinkedTokenSource( cancellation, _lifetime.Token );
		var actor = authenticate();
		if ( actor is null )
		{
			return Result( CommandStatus.Denied, "The caller could not be authenticated." );
		}

		var context = new CommandContext( actor, origin, request.RequestId, linked.Token, new CommandExecutionServices( this, authenticate, service ) );
		return await DispatchAsync( context, request );
	}

	public async Task<CommandResult> DispatchAsync( CommandContext context, CommandRequest request )
	{
		var audit = new AuditSubmission();
		var result = await DispatchCoreAsync( context, request, audit );
		if ( _audit is not null && audit.Action is not null && !audit.Handled )
		{
			await _audit.RecordAsync( audit.Action, result.Status, CancellationToken.None );
		}

		return result;
	}

	private async Task<CommandResult> DispatchCoreAsync( CommandContext context, CommandRequest request, AuditSubmission audit )
	{
		try
		{
			if ( context is null || request is null || context.RequestId != request.RequestId
				|| context.Services is not CommandExecutionServices services || !ReferenceEquals( services.Issuer, this ) )
			{
				return Result( CommandStatus.Denied, "Host-issued matching request context is required." );
			}

			var authorization = ValidateCaller( context, services );
			// Only a still-authenticated transport identity may be attributed in the journal.
			if ( authorization is null )
			{
				audit.Action = new AuditAction( context.Actor, context.Origin, request.RequestId,
					"command.unresolved", DateTimeOffset.UtcNow );
			}
			if ( authorization is not null )
			{
				return authorization;
			}

			if ( request.Command.Length > 96 || CommandTokenizer.ValidateTokens( request.Arguments ) is not null )
			{
				return Result( CommandStatus.InvalidArguments, "Command input exceeds its limits or contains unsupported characters." );
			}

			var key = new RequestKey( (int)context.Actor.Kind, context.Actor.SubjectId, (int)context.Origin, context.RequestId );
			var fingerprint = RequestFingerprint.Compute( request.Command, request.Arguments );
			var retry = _queue.FindRetry( key, fingerprint ) ?? _controlQueue.FindRetry( key, fingerprint );
			if ( retry is not null )
			{
				audit.Handled = retry.Admission != QueueAdmission.Conflict;
				return retry.Admission == QueueAdmission.Conflict
					? Result( CommandStatus.Conflict, "This request ID was already used with a different payload." ) : await retry.Result;
			}

			var command = _registry.Find( request.Command );
			if ( command is null )
			{
				return Result( CommandStatus.NotFound, "Command is not registered." );
			}

			audit.Action = CreateAuditAction( context, command.Descriptor, null );
			audit.Handled = !command.RecordsAudit;
			if ( context.Origin == CommandOrigin.Chat && command.Descriptor.Arguments.Any( argument =>
				argument.Audit == CommandArgumentAudit.Sensitive || AuditRedaction.SensitiveName( argument.Name ) ) )
			{
				return Result( CommandStatus.Denied, "Sensitive commands require a private non-chat transport." );
			}

			if ( !HasPermission( context, services, command.Descriptor.Permission ) )
			{
				return Result( CommandStatus.Denied, "Permission denied." );
			}
			if ( command.IsLifecycleControl && context.Actor.Kind != ActorKind.DedicatedConsole
				&& (context.Actor.Kind != ActorKind.Player || !_policy.IsOwner( context.Actor.SubjectId )) )
			{
				return Result( CommandStatus.Denied, "Module lifecycle requires current owner or dedicated console authority." );
			}

			var parsed = CommandArgumentParser.Parse( command.Descriptor, request.Arguments, _players() );
			if ( !parsed.IsSuccess )
			{
				return new CommandResult( CommandStatus.InvalidArguments, parsed.Error, parsed.PlayerChoices.Count == 0 ? null
					: new Dictionary<string, string> { [ "choices" ] = string.Join( ",", parsed.PlayerChoices.Select( player => player.SteamId ) ) } );
			}

			audit.Action = CreateAuditAction( context, command.Descriptor, parsed.Arguments );
			using var moduleCancellation = CancellationTokenSource.CreateLinkedTokenSource( context.Cancellation,
				command.Execution?.Cancellation ?? CancellationToken.None );
			var executionContext = new CommandContext( context.Actor, context.Origin, context.RequestId, moduleCancellation.Token, context.Services );
			Task<CommandResult> Execute() => ExecuteAuditedAsync( executionContext, services, request.Command, command, parsed.Arguments, audit );
			var queue = command.IsLifecycleControl ? _controlQueue : _queue;
			var admission = queue.Submit( key, fingerprint,
				() => command.Execution is null ? Execute() : command.Execution.RunAsync( Execute ), moduleCancellation.Token );
			return admission.Admission switch
			{
				QueueAdmission.Conflict => Result( CommandStatus.Conflict, "This request ID was already used with a different payload." ),
				QueueAdmission.Busy => Result( CommandStatus.Conflict, "Command capacity is full; retry later." ),
				_ => await admission.Result
			};
		}
		catch ( Exception error )
		{
			_report( error );
			return Result( CommandStatus.Unknown, "The command outcome could not be determined. See host diagnostics." );
		}
	}

	public void Dispose()
	{
		if ( _disposed )
		{
			return;
		}

		_disposed = true;
		_lifetime.Cancel();
		_lifetime.Dispose();
	}

	private static CommandResult Result( CommandStatus status, string message ) => new( status, message );
}