Swap chat host/client roles.

This commit is contained in:
Jared 2021-01-22 17:31:55 -08:00 committed by Jared Goodwin
parent 1c81aaa523
commit 45b17b8a9a
18 changed files with 99 additions and 44 deletions

View File

@ -8,7 +8,7 @@
<OutputType>WinExe</OutputType>
<RootNamespace>Remotely.Agent.Installer.Win</RootNamespace>
<AssemblyName>Remotely_Installer</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
</startup>
</configuration>

View File

@ -5,6 +5,6 @@ namespace Remotely.Agent.Models
public class ChatSession
{
public int ProcessID { get; set; }
public NamedPipeClientStream PipeStream { get; set; }
public NamedPipeServerStream PipeStream { get; set; }
}
}

View File

@ -44,7 +44,7 @@ namespace Remotely.Agent
builder.AddConsole().AddDebug();
});
serviceCollection.AddSingleton<AgentSocket>();
serviceCollection.AddScoped<ChatClientService>();
serviceCollection.AddScoped<ChatHostService>();
serviceCollection.AddTransient<Bash>();
serviceCollection.AddTransient<CMD>();
serviceCollection.AddTransient<PSCore>();

View File

@ -26,7 +26,7 @@ namespace Remotely.Agent.Services
Uninstaller uninstaller,
CommandExecutor commandExecutor,
ScriptRunner scriptRunner,
ChatClientService chatService,
ChatHostService chatService,
IAppLauncher appLauncher,
IUpdater updater)
{
@ -40,7 +40,7 @@ namespace Remotely.Agent.Services
}
public bool IsConnected => HubConnection?.State == HubConnectionState.Connected;
private IAppLauncher AppLauncher { get; }
private ChatClientService ChatService { get; }
private ChatHostService ChatService { get; }
private CommandExecutor CommandExecutor { get; }
private ConfigService ConfigService { get; }
private ConnectionInfo ConnectionInfo { get; set; }

View File

@ -14,9 +14,9 @@ using System.Threading.Tasks;
namespace Remotely.Agent.Services
{
public class ChatClientService
public class ChatHostService
{
public ChatClientService(IAppLauncher appLauncher)
public ChatHostService(IAppLauncher appLauncher)
{
AppLauncher = appLauncher;
}
@ -71,15 +71,20 @@ namespace Remotely.Agent.Services
Logger.Write($"Chat app did not start successfully.");
return;
}
var serverStream = new NamedPipeServerStream("Remotely_Chat" + senderConnectionID,
PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous);
var clientPipe = new NamedPipeClientStream(".", "Remotely_Chat" + senderConnectionID, PipeDirection.InOut, PipeOptions.Asynchronous);
clientPipe.Connect(15000);
if (!clientPipe.IsConnected)
var cts = new CancellationTokenSource(15000);
await serverStream.WaitForConnectionAsync(cts.Token);
if (!serverStream.IsConnected)
{
Logger.Write("Failed to connect to chat host.");
Logger.Write("Failed to connect to chat client.");
return;
}
chatSession = new ChatSession() { PipeStream = clientPipe, ProcessID = procID };
chatSession = new ChatSession() { PipeStream = serverStream, ProcessID = procID };
_ = Task.Run(async () => { await ReadFromStream(chatSession.PipeStream, senderConnectionID, hubConnection); });
ChatClients.Add(senderConnectionID, chatSession, CacheItemPolicy);
}
@ -108,11 +113,11 @@ namespace Remotely.Agent.Services
}
}
private async Task ReadFromStream(NamedPipeClientStream clientPipe, string senderConnectionID, HubConnection hubConnection)
private async Task ReadFromStream(NamedPipeServerStream clientPipe, string senderConnectionID, HubConnection hubConnection)
{
using var sr = new StreamReader(clientPipe, leaveOpen: true);
while (clientPipe.IsConnected)
{
using var sr = new StreamReader(clientPipe, leaveOpen: true);
var messageJson = await sr.ReadLineAsync();
if (!string.IsNullOrWhiteSpace(messageJson))
{

View File

@ -2,7 +2,7 @@
namespace Remotely.Desktop.Core.Interfaces
{
public interface IChatHostService
public interface IChatClientService
{
Task StartChat(string requesterID, string organizationName);
}

View File

@ -13,29 +13,29 @@ using System.Threading.Tasks;
namespace Remotely.Desktop.Core.Services
{
public class ChatHostService : IChatHostService
public class ChatClientService : IChatClientService
{
private readonly IChatUiService _chatUiService;
public ChatHostService(IChatUiService chatUiService)
public ChatClientService(IChatUiService chatUiService)
{
_chatUiService = chatUiService;
}
private NamedPipeServerStream NamedPipeStream { get; set; }
private NamedPipeClientStream NamedPipeStream { get; set; }
private StreamReader Reader { get; set; }
private StreamWriter Writer { get; set; }
public async Task StartChat(string requesterID, string organizationName)
{
NamedPipeStream = new NamedPipeServerStream("Remotely_Chat" + requesterID, PipeDirection.InOut, 10, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
NamedPipeStream = new NamedPipeClientStream(".", "Remotely_Chat" + requesterID, PipeDirection.InOut, PipeOptions.Asynchronous);
Writer = new StreamWriter(NamedPipeStream);
Reader = new StreamReader(NamedPipeStream);
var cts = new CancellationTokenSource(10000);
try
{
await NamedPipeStream.WaitForConnectionAsync(cts.Token);
await NamedPipeStream.ConnectAsync(15_000);
}
catch (TaskCanceledException)
{
@ -43,6 +43,12 @@ namespace Remotely.Desktop.Core.Services
Environment.Exit(0);
}
if (!NamedPipeStream.IsConnected)
{
Logger.Write("Failed to connect to chat host.");
return;
}
_chatUiService.ChatWindowClosed += OnChatWindowClosed;
_chatUiService.ShowChatWindow(organizationName, Writer);
@ -54,7 +60,6 @@ namespace Remotely.Desktop.Core.Services
{
try
{
NamedPipeStream?.Disconnect();
NamedPipeStream?.Dispose();
}
catch { }

View File

@ -55,7 +55,7 @@ namespace Remotely.Desktop.Linux
if (Conductor.Mode == Core.Enums.AppMode.Chat)
{
await Services.GetRequiredService<IChatHostService>().StartChat(Conductor.RequesterID, Conductor.OrganizationName);
await Services.GetRequiredService<IChatClientService>().StartChat(Conductor.RequesterID, Conductor.OrganizationName);
}
else if (Conductor.Mode == Core.Enums.AppMode.Unattended)
{
@ -107,7 +107,7 @@ namespace Remotely.Desktop.Linux
serviceCollection.AddSingleton<ICasterSocket, CasterSocket>();
serviceCollection.AddSingleton<IdleTimer>();
serviceCollection.AddSingleton<Conductor>();
serviceCollection.AddSingleton<IChatHostService, ChatHostService>();
serviceCollection.AddSingleton<IChatClientService, ChatClientService>();
serviceCollection.AddSingleton<IChatUiService, ChatUiServiceLinux>();
serviceCollection.AddTransient<IScreenCapturer, ScreenCapturerLinux>();
serviceCollection.AddTransient<Viewer>();

View File

@ -61,7 +61,7 @@ namespace Remotely.Desktop.Win
StartUiThreads(false);
_ = Task.Run(async () =>
{
var chatService = Services.GetRequiredService<IChatHostService>();
var chatService = Services.GetRequiredService<IChatClientService>();
await chatService.StartChat(Conductor.RequesterID, Conductor.OrganizationName);
});
}
@ -105,7 +105,7 @@ namespace Remotely.Desktop.Win
serviceCollection.AddSingleton<ICasterSocket, CasterSocket>();
serviceCollection.AddSingleton<IdleTimer>();
serviceCollection.AddSingleton<Conductor>();
serviceCollection.AddSingleton<IChatHostService, ChatHostService>();
serviceCollection.AddSingleton<IChatClientService, ChatClientService>();
serviceCollection.AddSingleton<IChatUiService, ChatUiServiceWin>();
serviceCollection.AddTransient<IScreenCapturer, ScreenCapturerWin>();
serviceCollection.AddTransient<Viewer>();

View File

@ -139,6 +139,13 @@
<br />
<span asp-validation-for="AppSettingsInput.MaxConcurrentUpdates" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AppSettingsInput.MessageOfTheDay" class="control-label"></label>
<br />
<input asp-for="AppSettingsInput.MessageOfTheDay" class="form-control" />
<br />
<span asp-validation-for="AppSettingsInput.MessageOfTheDay" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AppSettingsInput.RedirectToHttps"></label>
<br />

View File

@ -199,6 +199,8 @@ namespace Remotely.Server.Areas.Identity.Pages.Account.Manage
[Display(Name = "Max Organizations")]
public int MaxOrganizationCount { get; set; }
[Display(Name = "Message of the Day")]
public string MessageOfTheDay { get; set; }
[Display(Name = "Redirect To HTTPS")]
public bool RedirectToHttps { get; set; }

View File

@ -1,7 +1,9 @@
@page
@model IndexModel
@inject Microsoft.AspNetCore.Hosting.IWebHostEnvironment HostEnv
@{
ViewData["Title"] = "Home";
}
@if (!User.Identity.IsAuthenticated)
@ -10,6 +12,15 @@
}
else
{
if (!string.IsNullOrWhiteSpace(Model.Motd))
{
<div id="motdAlert" class="alert alert-info alert-dismissible mr-5" role="alert" hidden>
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
<span id="motdContentSpan">@Model.Motd</span>
</div>
}
<partial name="_LoadingWheel" model="Model" />
<partial name="_IndexLoggedIn" model="Model" />
}

View File

@ -1,10 +1,13 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Remotely.Server.Services;
using Remotely.Shared.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@ -12,53 +15,58 @@ namespace Remotely.Server.Pages
{
public class IndexModel : PageModel
{
private readonly IApplicationConfig _appConfig;
private readonly IDataService _dataService;
private readonly SignInManager<RemotelyUser> _signInManager;
public IndexModel(IDataService dataService,
SignInManager<RemotelyUser> signInManager,
IApplicationConfig appConfig)
{
DataService = dataService;
SignInManager = signInManager;
AppConfig = appConfig;
_dataService = dataService;
_signInManager = signInManager;
_appConfig = appConfig;
}
public string DefaultPrompt { get; set; }
public List<SelectListItem> DeviceGroups { get; set; } = new List<SelectListItem>();
public List<Alert> Alerts { get; set; } = new List<Alert>();
private IApplicationConfig AppConfig { get; }
private IDataService DataService { get; }
private SignInManager<RemotelyUser> SignInManager { get; }
public string DefaultPrompt { get; set; }
public string Motd { get; set; }
public List<SelectListItem> DeviceGroups { get; set; } = new List<SelectListItem>();
public async Task<IActionResult> OnGet()
{
if (User?.Identity?.IsAuthenticated == true)
{
var user = DataService.GetUserByName(User.Identity.Name);
var user = _dataService.GetUserByName(User.Identity.Name);
if (user is null)
{
await SignInManager.SignOutAsync();
await _signInManager.SignOutAsync();
return RedirectToPage();
}
if (AppConfig.Require2FA && !user.TwoFactorEnabled)
if (_appConfig.Require2FA && !user.TwoFactorEnabled)
{
return RedirectToPage("TwoFactorRequired");
}
DefaultPrompt = DataService.GetDefaultPrompt(User.Identity.Name);
var groups = DataService.GetDeviceGroups(User.Identity.Name);
DefaultPrompt = _dataService.GetDefaultPrompt(User.Identity.Name);
var groups = _dataService.GetDeviceGroups(User.Identity.Name);
if (groups?.Any() == true)
{
DeviceGroups.AddRange(groups.Select(x => new SelectListItem(x.Name, x.ID)));
}
var alerts = DataService.GetAlerts(user.Id);
var alerts = _dataService.GetAlerts(user.Id);
if (alerts.Any())
{
Alerts.AddRange(alerts);
}
Motd = _appConfig.MessageOfTheDay;
}
else
{
DefaultPrompt = DataService.GetDefaultPrompt();
DefaultPrompt = _dataService.GetDefaultPrompt();
}
return Page();

View File

@ -16,6 +16,7 @@ namespace Remotely.Server.Services
string[] KnownProxies { get; }
int MaxConcurrentUpdates { get; }
int MaxOrganizationCount { get; }
string MessageOfTheDay { get; }
bool RedirectToHttps { get; }
bool RemoteControlNotifyUser { get; }
bool RemoteControlRequiresAuthentication { get; }
@ -58,6 +59,7 @@ namespace Remotely.Server.Services
public string[] KnownProxies => Config.GetSection("ApplicationOptions:KnownProxies").Get<string[]>() ?? new string[0];
public int MaxConcurrentUpdates => int.Parse(Config["ApplicationOptions:MaxConcurrentUpdates"] ?? "10");
public int MaxOrganizationCount => int.Parse(Config["ApplicationOptions:MaxOrganizationCount"] ?? "1");
public string MessageOfTheDay => Config["ApplicationOptions:MessageOfTheDay"];
public bool RedirectToHttps => bool.Parse(Config["ApplicationOptions:RedirectToHttps"] ?? "false");
public bool RemoteControlNotifyUser => bool.Parse(Config["ApplicationOptions:RemoteControlNotifyUser"] ?? "true");
public bool RemoteControlRequiresAuthentication => bool.Parse(Config["ApplicationOptions:RemoteControlRequiresAuthentication"] ?? "true");

View File

@ -32,6 +32,7 @@
"KnownProxies": [],
"MaxConcurrentUpdates": 10,
"MaxOrganizationCount": 1,
"MessageOfTheDay": "",
"RedirectToHttps": false,
"RemoteControlNotifyUser": true,
"RemoteControlSessionLimit": 3,

View File

@ -26,7 +26,19 @@ export const MainApp = {
HubConnection: BrowserHubConnection,
UserSettings: UserSettings,
Sound: Sound,
GetMotd() {
if (!UI.MotdContentSpan.innerHTML || localStorage["remotely-motd"] == UI.MotdContentSpan.innerHTML) {
return;
}
UI.MotdAlert.querySelector("button").addEventListener("click", () => {
localStorage["remotely-motd"] = UI.MotdContentSpan.innerHTML;
});
UI.MotdAlert.removeAttribute("hidden");
},
Init() {
MainApp.GetMotd();
UI.ConsoleTextArea.focus();
ApplyInputEventHandlers();
BrowserHubConnection.Connect();

View File

@ -25,4 +25,6 @@ export var RightPaginationButton = document.querySelector("#rightPaginationButto
export var TabContentWrapper = document.getElementById("tabContentWrapper") as HTMLDivElement;
export var ToastsWrapper = document.getElementById("toastsWrapper") as HTMLDivElement;
export var TotalDevicesCount = document.querySelector("#totalDevicesSpan") as HTMLSpanElement;
export var TotalPagesSpan = document.querySelector("#totalPagesSpan") as HTMLSpanElement;
export var TotalPagesSpan = document.querySelector("#totalPagesSpan") as HTMLSpanElement;
export var MotdAlert = document.getElementById("motdAlert") as HTMLDivElement;
export var MotdContentSpan = document.getElementById("motdContentSpan") as HTMLSpanElement;