Remotely/Agent/Services/ScriptExecutor.cs
Jared Goodwin 3ef4cdf81a
Extract remote control functionality into separate library. (#539)
* Convert server to new single-file startup model.

* Add remote control implementations.

* Implement IViewerAuthorizer.

* Update hub endpoints.

* Implement HubEventHandler.

* Implement ViewerHubDataProvider.

* Implement page data provider.

* Implement RCL and refactor.

* Update submodule.

* Replace submodule with NuGet.

* Update copy URL.

* Update NuGet.

* Remove deprecated WebRTC.

* Remove deprecated WebRTC.

* Update Immense.RemoteControl

* Building out desktop projects.

* Bring more services into submodule.

* Update submodule.

* Update submodule.

* Refactoring for module.

* Update submodule.

* Update submodule

* Got Windows desktop app running.

* Refactor for submodule changes.

* FIx unattended session start.

* Switch desktop app out of console mode.

* Fix tests.

* Update publishing.

* Remove ClickOnce middleware.

* Remove ClickOnce remnants.

* Update submodule

* Add some logging.

* Update Linux path.

* Update submodule.

* Add cleanup service for unattended sessions that failed to start.

* Update submodule.

* Fix chat.

* Add ValidateExecutableReferencesMatchSelfContained property.

* Add other submodule projects.  Align checkbox.

* Update submodule.  Reduce deserialization in the browser, resulting in faster renders.

* Update submodule.

* Update submodule.

* Update submodule.

* Update submodule.

* Add orgId back for branding.

* Get branding loading in desktop apps.

* Update submodule.

* Create log dir.

* Refactor version check on config page.

* Update submodule.

* Update submodule.

* Change submodule URL.

* Correct namespace.

* Update submodule.

* Checkout submodules recursively.
2022-12-23 06:39:12 -08:00

176 lines
6.2 KiB
C#

using Microsoft.AspNetCore.SignalR.Client;
using Remotely.Shared;
using Remotely.Shared.Enums;
using Remotely.Shared.Models;
using Remotely.Shared.Utilities;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;
namespace Remotely.Agent.Services
{
public class ScriptExecutor
{
public ScriptExecutor(ConfigService configService)
{
ConfigService = configService;
}
private ConfigService ConfigService { get; }
public async Task RunCommandFromApi(ScriptingShell shell,
string requestID,
string command,
string senderUsername,
string authToken,
HubConnection hubConnection)
{
try
{
var result = ExecuteScriptContent(shell, requestID, command, TimeSpan.FromMinutes(AppConstants.ScriptRunExpirationMinutes));
result.InputType = ScriptInputType.Api;
result.SenderUserName = senderUsername;
await SendResultsToApi(result, authToken);
await hubConnection.SendAsync("ScriptResultViaApi", requestID);
}
catch (Exception ex)
{
Logger.Write(ex);
}
}
public async Task RunCommandFromTerminal(ScriptingShell shell,
string command,
string authToken,
string senderUsername,
string senderConnectionID,
ScriptInputType scriptInputType,
TimeSpan timeout,
HubConnection hubConnection)
{
try
{
var result = ExecuteScriptContent(shell, senderConnectionID, command, timeout);
result.InputType = scriptInputType;
result.SenderUserName = senderUsername;
var responseResult = await SendResultsToApi(result, authToken);
if (responseResult is null)
{
return;
}
await hubConnection.SendAsync("ScriptResult", responseResult.ID);
}
catch (Exception ex)
{
Logger.Write(ex);
await hubConnection.SendAsync("DisplayMessage",
"There was an error executing the command. It has been logged on the client device.",
"Error executing command.",
"bg-danger",
senderConnectionID);
}
}
public async Task RunScript(Guid savedScriptId,
int scriptRunId,
string initiator,
ScriptInputType scriptInputType,
string authToken)
{
try
{
Logger.Write($"Script run started. Script ID: {savedScriptId}. Script Run: {scriptRunId}. Initiator: {initiator}.");
var connectionInfo = ConfigService.GetConnectionInfo();
var url = $"{connectionInfo.Host}/API/SavedScripts/{savedScriptId}";
using var hc = new HttpClient();
hc.DefaultRequestHeaders.Add("Authorization", authToken);
var savedScript = await hc.GetFromJsonAsync<SavedScript>(url);
var result = ExecuteScriptContent(savedScript.Shell,
Guid.NewGuid().ToString(),
savedScript.Content,
TimeSpan.FromMinutes(AppConstants.ScriptRunExpirationMinutes));
result.SenderUserName = initiator;
result.ScriptRunId = scriptRunId;
result.InputType = scriptInputType;
result.SavedScriptId = savedScriptId;
var responseResult = await SendResultsToApi(result, authToken);
}
catch (Exception ex)
{
Logger.Write(ex);
}
}
// TODO: Async/await.
private ScriptResult ExecuteScriptContent(ScriptingShell shell,
string terminalSessionId,
string command,
TimeSpan timeout)
{
switch (shell)
{
case ScriptingShell.PSCore:
return PSCore.GetCurrent(terminalSessionId).WriteInput(command);
case ScriptingShell.WinPS:
if (EnvironmentHelper.IsWindows)
{
return ExternalScriptingShell
.GetCurrent(ScriptingShell.WinPS, terminalSessionId)
.WriteInput(command, timeout);
}
break;
case ScriptingShell.CMD:
if (EnvironmentHelper.IsWindows)
{
return ExternalScriptingShell
.GetCurrent(ScriptingShell.CMD, terminalSessionId)
.WriteInput(command, timeout);
}
break;
case ScriptingShell.Bash:
return ExternalScriptingShell
.GetCurrent(ScriptingShell.Bash, terminalSessionId)
.WriteInput(command, timeout);
default:
break;
}
return null;
}
private async Task<ScriptResult> SendResultsToApi(object result, string authToken)
{
var targetURL = ConfigService.GetConnectionInfo().Host + $"/API/ScriptResults";
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", authToken);
using var response = await httpClient.PostAsJsonAsync(targetURL, result);
if (!response.IsSuccessStatusCode)
{
Logger.Write($"Failed to send script results. Status Code: {response.StatusCode}");
return default;
}
var content = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<ScriptResult>(content, JsonSerializerHelper.CaseInsensitiveOptions);
}
}
}