From e56307bc31af13203efb00a1e428ef7da6e3280c Mon Sep 17 00:00:00 2001 From: Jared Goodwin Date: Sun, 12 Jan 2020 13:55:47 -0800 Subject: [PATCH] Begin stubbling out device alert service. --- Agent/Program.cs | 8 +- Agent/Services/Bash.cs | 4 +- Agent/Services/CMD.cs | 128 +++++++++++++------------- Agent/Services/ConfigService.cs | 35 +++++++ Agent/Services/DeviceSocket.cs | 33 +++---- Agent/Services/PSCore.cs | 2 +- Agent/Services/Updater.cs | 8 +- Agent/Services/Utilities.cs | 65 ------------- Agent/Services/WindowsPS.cs | 4 +- Server/Data/DataService.cs | 11 --- Server/Services/DeviceAlertService.cs | 38 ++++++++ Server/Services/DeviceSocketHub.cs | 53 ++++++----- Shared/Models/ConnectionInfo.cs | 1 + Shared/Models/DeviceAlert.cs | 10 ++ Shared/Models/RemotelyUser.cs | 9 +- 15 files changed, 207 insertions(+), 202 deletions(-) create mode 100644 Agent/Services/ConfigService.cs delete mode 100644 Agent/Services/Utilities.cs create mode 100644 Server/Services/DeviceAlertService.cs create mode 100644 Shared/Models/DeviceAlert.cs diff --git a/Agent/Program.cs b/Agent/Program.cs index 38ad287d..0d6e2ad1 100644 --- a/Agent/Program.cs +++ b/Agent/Program.cs @@ -10,6 +10,7 @@ using System.ServiceProcess; using System.Threading.Tasks; using System.Diagnostics; using System.Threading; +using Microsoft.Extensions.DependencyInjection; namespace Remotely.Agent { @@ -17,12 +18,17 @@ namespace Remotely.Agent { public static bool IsDebug { get; set; } + public static IServiceProvider Services { get; set; } + public static void Main(string[] args) { try { + // TODO: Replace static services with scoped instances in IoC container. + var serviceCollection = new ServiceCollection(); + Services = serviceCollection.BuildServiceProvider(); - Task.Run(()=> { Init(args); }); + Task.Run(() => { Init(args); }); Thread.Sleep(Timeout.Infinite); diff --git a/Agent/Services/Bash.cs b/Agent/Services/Bash.cs index 622a786e..ac5c809e 100644 --- a/Agent/Services/Bash.cs +++ b/Agent/Services/Bash.cs @@ -120,7 +120,7 @@ namespace Remotely.Agent.Services var partialResult = new GenericCommandResult() { CommandContextID = LastInputID, - DeviceID = Utilities.GetConnectionInfo().DeviceID, + DeviceID = ConfigService.GetConnectionInfo().DeviceID, CommandType = "Bash", StandardOutput = StandardOut, ErrorOutput = "WARNING: The command execution froze and was forced to return before finishing. " + @@ -136,7 +136,7 @@ namespace Remotely.Agent.Services return new GenericCommandResult() { CommandContextID = LastInputID, - DeviceID = Utilities.GetConnectionInfo().DeviceID, + DeviceID = ConfigService.GetConnectionInfo().DeviceID, CommandType = "Bash", StandardOutput = StandardOut, ErrorOutput = ErrorOut diff --git a/Agent/Services/CMD.cs b/Agent/Services/CMD.cs index 2c8b32e6..b038ab4f 100644 --- a/Agent/Services/CMD.cs +++ b/Agent/Services/CMD.cs @@ -11,44 +11,6 @@ namespace Remotely.Agent.Services { public class CMD { - private static ConcurrentDictionary Sessions { get; set; } = new ConcurrentDictionary(); - private string ConnectionID { get; set; } - - private string LastInputID { get; set; } - private bool OutputDone { get; set; } - private string StandardOut { get; set; } - private string ErrorOut { get; set; } - private System.Timers.Timer ProcessIdleTimeout { get; set; } - public static CMD GetCurrent(string connectionID) - { - if (Sessions.ContainsKey(connectionID)) - { - var bash = Sessions[connectionID]; - bash.ProcessIdleTimeout.Stop(); - bash.ProcessIdleTimeout.Start(); - return bash; - } - else - { - var cmd = new CMD(); - cmd.ConnectionID = connectionID; - cmd.ProcessIdleTimeout = new System.Timers.Timer(600000); // 10 minutes. - cmd.ProcessIdleTimeout.AutoReset = false; - cmd.ProcessIdleTimeout.Elapsed += cmd.ProcessIdleTimeout_Elapsed; - Sessions.AddOrUpdate(connectionID, cmd, (id, c) => cmd); - cmd.ProcessIdleTimeout.Start(); - return cmd; - } - } - - private void ProcessIdleTimeout_Elapsed(object sender, System.Timers.ElapsedEventArgs e) - { - Sessions.Remove(ConnectionID, out var outResult); - outResult.CMDProc.Kill(); - } - - private Process CMDProc { get; } - private CMD() { var psi = new ProcessStartInfo("cmd.exe"); @@ -70,24 +32,34 @@ namespace Remotely.Agent.Services CMDProc.BeginOutputReadLine(); } - private void CMDProc_OutputDataReceived(object sender, DataReceivedEventArgs e) - { - if (e?.Data?.Contains(LastInputID) == true) - { - OutputDone = true; - } - else if (!OutputDone) - { - StandardOut += e.Data + Environment.NewLine; - } + private static ConcurrentDictionary Sessions { get; set; } = new ConcurrentDictionary(); + private Process CMDProc { get; } + private string ConnectionID { get; set; } - } - - private void CMDProc_ErrorDataReceived(object sender, DataReceivedEventArgs e) + private string ErrorOut { get; set; } + private string LastInputID { get; set; } + private bool OutputDone { get; set; } + private System.Timers.Timer ProcessIdleTimeout { get; set; } + private string StandardOut { get; set; } + public static CMD GetCurrent(string connectionID) { - if (e?.Data != null) + if (Sessions.ContainsKey(connectionID)) { - ErrorOut += e.Data + Environment.NewLine; + var bash = Sessions[connectionID]; + bash.ProcessIdleTimeout.Stop(); + bash.ProcessIdleTimeout.Start(); + return bash; + } + else + { + var cmd = new CMD(); + cmd.ConnectionID = connectionID; + cmd.ProcessIdleTimeout = new System.Timers.Timer(600000); // 10 minutes. + cmd.ProcessIdleTimeout.AutoReset = false; + cmd.ProcessIdleTimeout.Elapsed += cmd.ProcessIdleTimeout_Elapsed; + Sessions.AddOrUpdate(connectionID, cmd, (id, c) => cmd); + cmd.ProcessIdleTimeout.Start(); + return cmd; } } @@ -112,7 +84,40 @@ namespace Remotely.Agent.Services } return GenerateCompletedResult(); } - + + } + + private void CMDProc_ErrorDataReceived(object sender, DataReceivedEventArgs e) + { + if (e?.Data != null) + { + ErrorOut += e.Data + Environment.NewLine; + } + } + + private void CMDProc_OutputDataReceived(object sender, DataReceivedEventArgs e) + { + if (e?.Data?.Contains(LastInputID) == true) + { + OutputDone = true; + } + else if (!OutputDone) + { + StandardOut += e.Data + Environment.NewLine; + } + + } + + private GenericCommandResult GenerateCompletedResult() + { + return new GenericCommandResult() + { + CommandContextID = LastInputID, + DeviceID = ConfigService.GetConnectionInfo().DeviceID, + CommandType = "CMD", + StandardOutput = StandardOut, + ErrorOutput = ErrorOut + }; } private GenericCommandResult GeneratePartialResult() @@ -121,7 +126,7 @@ namespace Remotely.Agent.Services var partialResult = new GenericCommandResult() { CommandContextID = LastInputID, - DeviceID = Utilities.GetConnectionInfo().DeviceID, + DeviceID = ConfigService.GetConnectionInfo().DeviceID, CommandType = "CMD", StandardOutput = StandardOut, ErrorOutput = "WARNING: The command execution froze and was forced to return before finishing. " + @@ -132,17 +137,10 @@ namespace Remotely.Agent.Services return partialResult; } - private GenericCommandResult GenerateCompletedResult() + private void ProcessIdleTimeout_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { - return new GenericCommandResult() - { - CommandContextID = LastInputID, - DeviceID = Utilities.GetConnectionInfo().DeviceID, - CommandType = "CMD", - StandardOutput = StandardOut, - ErrorOutput = ErrorOut - }; + Sessions.Remove(ConnectionID, out var outResult); + outResult.CMDProc.Kill(); } - } } diff --git a/Agent/Services/ConfigService.cs b/Agent/Services/ConfigService.cs new file mode 100644 index 00000000..5c21cfd8 --- /dev/null +++ b/Agent/Services/ConfigService.cs @@ -0,0 +1,35 @@ +using Remotely.Shared.Models; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; + +namespace Remotely.Agent.Services +{ + public static class ConfigService + { + private static ConnectionInfo connectionInfo; + public static ConnectionInfo GetConnectionInfo() + { + if (connectionInfo == null) + { + if (!File.Exists("ConnectionInfo.json")) + { + Logger.Write(new Exception("No connection info available. Please create ConnectionInfo.json file with appropriate values.")); + return null; + } + connectionInfo = JsonConvert.DeserializeObject(File.ReadAllText("ConnectionInfo.json")); + } + + return connectionInfo; + } + + + public static void SaveConnectionInfo(ConnectionInfo connectionInfo) + { + File.WriteAllText("ConnectionInfo.json", JsonConvert.SerializeObject(connectionInfo)); + } + } +} diff --git a/Agent/Services/DeviceSocket.cs b/Agent/Services/DeviceSocket.cs index 06b3f128..d409a09c 100644 --- a/Agent/Services/DeviceSocket.cs +++ b/Agent/Services/DeviceSocket.cs @@ -27,7 +27,7 @@ namespace Remotely.Agent.Services public static async Task Connect() { - ConnectionInfo = Utilities.GetConnectionInfo(); + ConnectionInfo = ConfigService.GetConnectionInfo(); HubConnection = new HubConnectionBuilder() .WithUrl(ConnectionInfo.Host + "/DeviceHub") @@ -47,18 +47,15 @@ namespace Remotely.Agent.Services IsServerVerified = true; ConnectionInfo.ServerVerificationToken = Guid.NewGuid().ToString(); await HubConnection.InvokeAsync("SetServerVerificationToken", ConnectionInfo.ServerVerificationToken); - Utilities.SaveConnectionInfo(ConnectionInfo); + ConfigService.SaveConnectionInfo(ConnectionInfo); } else { await HubConnection.InvokeAsync("SendServerVerificationToken"); } - if (HeartbeatTimer != null) - { - HeartbeatTimer.Stop(); - } - HeartbeatTimer = new Timer(300000); + HeartbeatTimer?.Dispose(); + HeartbeatTimer = new Timer(TimeSpan.FromMinutes(5).TotalMilliseconds); HeartbeatTimer.Elapsed += HeartbeatTimer_Elapsed; HeartbeatTimer.Start(); } @@ -171,7 +168,7 @@ namespace Remotely.Agent.Services hubConnection.On("TransferFiles", async (string transferID, List fileIDs, string requesterID) => { Logger.Write($"File transfer started by {requesterID}."); - var connectionInfo = Utilities.GetConnectionInfo(); + var connectionInfo = ConfigService.GetConnectionInfo(); var sharedFilePath = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(),"RemotelySharedFiles")).FullName; foreach (var fileID in fileIDs) @@ -201,7 +198,7 @@ namespace Remotely.Agent.Services await HubConnection.InvokeAsync("TransferCompleted", transferID, requesterID); }); hubConnection.On("DeployScript", async (string mode, string fileID, string commandContextID, string requesterID) => { - var connectionInfo = Utilities.GetConnectionInfo(); + var connectionInfo = ConfigService.GetConnectionInfo(); var sharedFilePath = Directory.CreateDirectory(Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Remotely", @@ -253,11 +250,11 @@ namespace Remotely.Agent.Services if (Program.IsDebug) { - Process.Start(rcBinaryPath, $"-mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {Utilities.GetConnectionInfo().Host} -desktop default"); + Process.Start(rcBinaryPath, $"-mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {ConfigService.GetConnectionInfo().Host} -desktop default"); } else { - var result = Win32Interop.OpenInteractiveProcess(rcBinaryPath + $" -mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {Utilities.GetConnectionInfo().Host} -desktop default", "default", true, out _); + var result = Win32Interop.OpenInteractiveProcess(rcBinaryPath + $" -mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {ConfigService.GetConnectionInfo().Host} -desktop default", "default", true, out _); if (!result) { await hubConnection.InvokeAsync("DisplayMessage", "Remote control failed to start on target device.", "Failed to start remote control.", requesterID); @@ -272,7 +269,7 @@ namespace Remotely.Agent.Services var psi = new ProcessStartInfo() { FileName = "sudo", - Arguments = $"-u {username} {rcBinaryPath} -mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {Utilities.GetConnectionInfo().Host} -desktop default & disown" + Arguments = $"-u {username} {rcBinaryPath} -mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {ConfigService.GetConnectionInfo().Host} -desktop default & disown" }; psi.Environment.Add("DISPLAY", ":0"); psi.Environment.Add("XAUTHORITY", $"{homeDir}/.Xauthority"); @@ -304,7 +301,7 @@ namespace Remotely.Agent.Services Logger.Write("Restarting screen caster."); if (Program.IsDebug) { - var proc = Process.Start(rcBinaryPath, $"-mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {Utilities.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)}"); + var proc = Process.Start(rcBinaryPath, $"-mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {ConfigService.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)}"); var stopwatch = Stopwatch.StartNew(); while (stopwatch.Elapsed.TotalSeconds < 10) { @@ -317,7 +314,7 @@ namespace Remotely.Agent.Services } else { - var result = Win32Interop.OpenInteractiveProcess(rcBinaryPath + $" -mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {Utilities.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)}", "default", true, out var procInfo); + var result = Win32Interop.OpenInteractiveProcess(rcBinaryPath + $" -mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {ConfigService.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)}", "default", true, out var procInfo); if (result) { @@ -332,7 +329,7 @@ namespace Remotely.Agent.Services if (Process.GetProcessesByName(Path.GetFileNameWithoutExtension(rcBinaryPath))?.Where(x=>x.Id == procInfo.dwProcessId)?.Any() != true) { Logger.Write("Restarting screen caster after failed relaunch."); - result = Win32Interop.OpenInteractiveProcess(rcBinaryPath + $" -mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {Utilities.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)}", "default", true, out procInfo); + result = Win32Interop.OpenInteractiveProcess(rcBinaryPath + $" -mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {ConfigService.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)}", "default", true, out procInfo); } } } @@ -353,7 +350,7 @@ namespace Remotely.Agent.Services var psi = new ProcessStartInfo() { FileName = "sudo", - Arguments = $"-u {username} {rcBinaryPath} -mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {Utilities.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)} & disown" + Arguments = $"-u {username} {rcBinaryPath} -mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {ConfigService.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)} & disown" }; psi.Environment.Add("DISPLAY", ":0"); psi.Environment.Add("XAUTHORITY", $"{homeDir}/.Xauthority"); @@ -375,7 +372,7 @@ namespace Remotely.Agent.Services hubConnection.On("ServerVerificationToken", (string verificationToken) => { - if (verificationToken == Utilities.GetConnectionInfo().ServerVerificationToken) + if (verificationToken == ConfigService.GetConnectionInfo().ServerVerificationToken) { IsServerVerified = true; if (!Program.IsDebug) @@ -394,7 +391,7 @@ namespace Remotely.Agent.Services private static void SendResultsViaAjax(string resultType, object result) { - var targetURL = Utilities.GetConnectionInfo().Host + $"/API/Commands/{resultType}"; + var targetURL = ConfigService.GetConnectionInfo().Host + $"/API/Commands/{resultType}"; var webRequest = WebRequest.CreateHttp(targetURL); webRequest.Method = "POST"; diff --git a/Agent/Services/PSCore.cs b/Agent/Services/PSCore.cs index a9203af8..0f16691e 100644 --- a/Agent/Services/PSCore.cs +++ b/Agent/Services/PSCore.cs @@ -72,7 +72,7 @@ namespace Remotely.Agent.Services return new PSCoreCommandResult() { CommandContextID = commandID, - DeviceID = Utilities.GetConnectionInfo().DeviceID, + DeviceID = ConfigService.GetConnectionInfo().DeviceID, DebugOutput = debugOut, ErrorOutput = errorOut, VerboseOutput = verboseOut, diff --git a/Agent/Services/Updater.cs b/Agent/Services/Updater.cs index 0af15636..ad00c00d 100644 --- a/Agent/Services/Updater.cs +++ b/Agent/Services/Updater.cs @@ -20,7 +20,7 @@ namespace Remotely.Agent.Services try { var wc = new WebClient(); - var response = new HttpClient().GetAsync(Utilities.GetConnectionInfo().Host + $"/API/CoreVersion/").Result; + var response = new HttpClient().GetAsync(ConfigService.GetConnectionInfo().Host + $"/API/CoreVersion/").Result; var latestVersion = response.Content.ReadAsStringAsync().Result; var thisVersion = FileVersionInfo.GetVersionInfo("Remotely_Agent.dll").FileVersion.ToString().Trim(); if (thisVersion != latestVersion) @@ -37,7 +37,7 @@ namespace Remotely.Agent.Services { Directory.Delete(tempFolder, true); } - wc.DownloadFile(new Uri(Utilities.GetConnectionInfo().Host + $"/Downloads/{fileName}"), tempFile); + wc.DownloadFile(new Uri(ConfigService.GetConnectionInfo().Host + $"/Downloads/{fileName}"), tempFile); Logger.Write($"Service Updater: Extracting files."); @@ -187,10 +187,8 @@ namespace Remotely.Agent.Services { throw new Exception("Unsupported operating system."); } - var response = await new HttpClient().GetAsync(Utilities.GetConnectionInfo().Host + $"/API/ScreenCastVersion/{platform}"); + var response = await new HttpClient().GetAsync(ConfigService.GetConnectionInfo().Host + $"/API/ScreenCastVersion/{platform}"); return await response.Content.ReadAsStringAsync(); } - - } } diff --git a/Agent/Services/Utilities.cs b/Agent/Services/Utilities.cs deleted file mode 100644 index 930ad82a..00000000 --- a/Agent/Services/Utilities.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Remotely.Shared.Models; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; - -namespace Remotely.Agent.Services -{ - public static class Utilities - { - - - public static ConnectionInfo GetConnectionInfo() - { - if (!File.Exists("ConnectionInfo.json")) - { - Logger.Write(new Exception("No connection info available. Please create ConnectionInfo.json file with appropriate values.")); - } - return JsonConvert.DeserializeObject(File.ReadAllText("ConnectionInfo.json")); - } - - public static string AppDataDir - { - get - { - return Directory.CreateDirectory( - Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), - "Remotely") - ).FullName; - } - } - - public static void KillProcs() - { - var procs = Process.GetProcessesByName("Agent_Win").Where(proc => proc.Id != Process.GetCurrentProcess().Id); - foreach (var proc in procs) - { - proc.Kill(); - } - } - - public static Dictionary ProcessArgs(string[] args) - { - var argDict = new Dictionary(); - var argString = String.Join(" ", args).ToLower(); - var argArray = argString.Split("-".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); - foreach (var arg in argArray) - { - var split = arg.Split(" ".ToCharArray(), 2, StringSplitOptions.RemoveEmptyEntries); - var key = split[0]; - var value = split.Length == 2 ? split[1] : ""; - argDict.Add(key, value); - } - return argDict; - } - - public static void SaveConnectionInfo(ConnectionInfo connectionInfo) - { - File.WriteAllText("ConnectionInfo.json", JsonConvert.SerializeObject(connectionInfo)); - } - } -} diff --git a/Agent/Services/WindowsPS.cs b/Agent/Services/WindowsPS.cs index a563217c..5ec7fb3e 100644 --- a/Agent/Services/WindowsPS.cs +++ b/Agent/Services/WindowsPS.cs @@ -118,7 +118,7 @@ namespace Remotely.Agent.Services var partialResult = new GenericCommandResult() { CommandContextID = LastInputID, - DeviceID = Utilities.GetConnectionInfo().DeviceID, + DeviceID = ConfigService.GetConnectionInfo().DeviceID, CommandType = "WinPS", StandardOutput = StandardOut, ErrorOutput = "WARNING: The command execution froze and was forced to return before finishing. " + @@ -134,7 +134,7 @@ namespace Remotely.Agent.Services return new GenericCommandResult() { CommandContextID = LastInputID, - DeviceID = Utilities.GetConnectionInfo().DeviceID, + DeviceID = ConfigService.GetConnectionInfo().DeviceID, CommandType = "WinPS", StandardOutput = StandardOut, ErrorOutput = ErrorOut diff --git a/Server/Data/DataService.cs b/Server/Data/DataService.cs index b0c491f2..7d7e4f3a 100644 --- a/Server/Data/DataService.cs +++ b/Server/Data/DataService.cs @@ -392,17 +392,6 @@ namespace Remotely.Server.Data .UserOptions; } - public List GetUsersWithAccessToDevice(IEnumerable userIDs, Device device) - { - var targetDevice = RemotelyContext.Devices - .FirstOrDefault(x => x.ID == device.ID && x.OrganizationID == device.OrganizationID); - - var targetUsers = RemotelyContext.Users.Where(x => - x.OrganizationID == device.OrganizationID && - userIDs.Contains(x.Id)).ToList(); - - return targetUsers.ToList(); - } public bool JoinViaInvitation(string userName, string inviteID) { var invite = RemotelyContext.InviteLinks diff --git a/Server/Services/DeviceAlertService.cs b/Server/Services/DeviceAlertService.cs new file mode 100644 index 00000000..ca1f7828 --- /dev/null +++ b/Server/Services/DeviceAlertService.cs @@ -0,0 +1,38 @@ +using Remotely.Server.Data; +using Remotely.Shared.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Remotely.Server.Services +{ + public class DeviceAlertService + { + public DeviceAlertService(DataService dataService, ApplicationConfig appConfig) + { + DataService = dataService; + AppConfig = appConfig; + } + + public DataService DataService { get; } + public ApplicationConfig AppConfig { get; } + + public bool ShouldSendAlert(Device device, out DeviceAlert alert) + { + if (true) // Check + { + //DataService.AddDeviceAlert(alert); + //TrySendAlertEmail(alert); + //TrySendAlertWebRequest(alert); + alert = new DeviceAlert(); + return true; + } + else + { + alert = null; + return false; + } + } + } +} diff --git a/Server/Services/DeviceSocketHub.cs b/Server/Services/DeviceSocketHub.cs index 9f71b344..cbe4e302 100644 --- a/Server/Services/DeviceSocketHub.cs +++ b/Server/Services/DeviceSocketHub.cs @@ -14,14 +14,19 @@ namespace Remotely.Server.Services { public class DeviceSocketHub : Hub { - public DeviceSocketHub(DataService dataService, IHubContext browserHub, IHubContext rcBrowserHub) + public DeviceSocketHub(DataService dataService, + IHubContext browserHub, + IHubContext rcBrowserHub, + DeviceAlertService deviceAlertService) { DataService = dataService; BrowserHub = browserHub; RCBrowserHub = rcBrowserHub; + DeviceAlertService = deviceAlertService; } public static ConcurrentDictionary ServiceConnections { get; } = new ConcurrentDictionary(); + public DeviceAlertService DeviceAlertService { get; } public IHubContext RCBrowserHub { get; } private IHubContext BrowserHub { get; } private DataService DataService { get; } @@ -79,14 +84,10 @@ namespace Remotely.Server.Services Device = updatedDevice; ServiceConnections.AddOrUpdate(Context.ConnectionId, Device, (id, d) => Device); - var onlineOrganizationUsers = BrowserSocketHub.ConnectionIdToUserLookup - .Where(x => x.Value.OrganizationID == Device.OrganizationID); - - var authorizedUsers = DataService.GetUsersWithAccessToDevice(onlineOrganizationUsers.Select(x => x.Value.Id), Device); - var connectionIds = onlineOrganizationUsers - .Where(onlineUser => authorizedUsers.Any(authorizedUser => authorizedUser.Id == onlineUser.Value.Id)) - .Select(x => x.Key) - .ToList(); + var connectionIds = BrowserSocketHub.ConnectionIdToUserLookup + .Where(x => x.Value.OrganizationID == Device.OrganizationID) + .Select(x => x.Key) + .ToList(); await BrowserHub.Clients.Clients(connectionIds).SendAsync("DeviceCameOnline", Device); } @@ -107,16 +108,17 @@ namespace Remotely.Server.Services { DataService.AddOrUpdateDevice(device, out var updatedDevice); Device = updatedDevice; - var onlineOrganizationUsers = BrowserSocketHub.ConnectionIdToUserLookup - .Where(x => x.Value.OrganizationID == Device.OrganizationID); - - var authorizedUsers = DataService.GetUsersWithAccessToDevice(onlineOrganizationUsers.Select(x=>x.Value.Id), Device); - var connectionIds = onlineOrganizationUsers - .Where(onlineUser => authorizedUsers.Any(authorizedUser => authorizedUser.Id == onlineUser.Value.Id)) - .Select(x => x.Key) - .ToList(); + var connectionIds = BrowserSocketHub.ConnectionIdToUserLookup + .Where(x => x.Value.OrganizationID == Device.OrganizationID) + .Select(x => x.Key) + .ToList(); await BrowserHub.Clients.Clients(connectionIds).SendAsync("DeviceHeartbeat", Device); + + if (DeviceAlertService.ShouldSendAlert(Device, out var alert)) + { + await BrowserHub.Clients.Clients(connectionIds).SendAsync("DeviceAlert", alert); + } } public async Task DisplayMessage(string consoleMessage, string popupMessage, string requesterID) @@ -135,18 +137,19 @@ namespace Remotely.Server.Services Device.IsOnline = false; - var onlineOrganizationUsers = BrowserSocketHub.ConnectionIdToUserLookup - .Where(x => x.Value.OrganizationID == Device.OrganizationID); - - var authorizedUsers = DataService.GetUsersWithAccessToDevice(onlineOrganizationUsers.Select(x => x.Value.Id), Device); - var connectionIds = onlineOrganizationUsers - .Where(onlineUser => authorizedUsers.Any(authorizedUser => authorizedUser.Id == onlineUser.Value.Id)) - .Select(x => x.Key) - .ToList(); + var connectionIds = BrowserSocketHub.ConnectionIdToUserLookup + .Where(x => x.Value.OrganizationID == Device.OrganizationID) + .Select(x => x.Key) + .ToList(); await BrowserHub.Clients.Clients(connectionIds).SendAsync("DeviceWentOffline", Device); ServiceConnections.Remove(Context.ConnectionId, out _); + + if (DeviceAlertService.ShouldSendAlert(Device, out var alert)) + { + await BrowserHub.Clients.Clients(connectionIds).SendAsync("DeviceAlert", alert); + } } await base.OnDisconnectedAsync(exception); diff --git a/Shared/Models/ConnectionInfo.cs b/Shared/Models/ConnectionInfo.cs index 669533b0..278edefe 100644 --- a/Shared/Models/ConnectionInfo.cs +++ b/Shared/Models/ConnectionInfo.cs @@ -25,5 +25,6 @@ namespace Remotely.Shared.Models } public string OrganizationID { get; set; } public string ServerVerificationToken { get; set; } + } } diff --git a/Shared/Models/DeviceAlert.cs b/Shared/Models/DeviceAlert.cs new file mode 100644 index 00000000..9e4a2491 --- /dev/null +++ b/Shared/Models/DeviceAlert.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Remotely.Shared.Models +{ + public class DeviceAlert + { + } +} diff --git a/Shared/Models/RemotelyUser.cs b/Shared/Models/RemotelyUser.cs index 5b02e3bd..0cd66dcf 100644 --- a/Shared/Models/RemotelyUser.cs +++ b/Shared/Models/RemotelyUser.cs @@ -10,15 +10,10 @@ namespace Remotely.Shared.Models { public class RemotelyUser : IdentityUser { - public RemotelyUser() - { - UserOptions = new RemotelyUserOptions(); - Organization = new Organization(); - } - public RemotelyUserOptions UserOptions { get; set; } + public RemotelyUserOptions UserOptions { get; set; } = new RemotelyUserOptions(); [JsonIgnore] - public virtual Organization Organization { get; set; } + public virtual Organization Organization { get; set; } = new Organization(); public string OrganizationID { get; set; } public bool IsAdministrator { get; set; } = true;