diff --git a/Server/API/RemoteControlController.cs b/Server/API/RemoteControlController.cs index f70fb2b5..2a105c8c 100644 --- a/Server/API/RemoteControlController.cs +++ b/Server/API/RemoteControlController.cs @@ -83,22 +83,26 @@ namespace Remotely.Server.API { return BadRequest("There are already the maximum amount of active remote control sessions for your organization."); } + + var existingSessions = RCDeviceSocketHub.SessionInfoList.Where(x => x.Value.MachineName == targetDevice.Value.DeviceName); + await DeviceHub.Clients.Client(targetDevice.Key).SendAsync("RemoteControl", Request.HttpContext.Connection.Id, targetDevice.Key); var stopWatch = Stopwatch.StartNew(); - while (!RCDeviceSocketHub.MachineNameToSessionIDLookup.ContainsKey(targetDevice.Value.DeviceName) && stopWatch.Elapsed.TotalSeconds < 5) + + while (!RCDeviceSocketHub.SessionInfoList.Values.Any(x=>x.MachineName == targetDevice.Value.DeviceName && !existingSessions.Any(y=>y.Key != x.RCSocketID)) && stopWatch.Elapsed.TotalSeconds < 5) { await Task.Delay(10); } - if (!RCDeviceSocketHub.MachineNameToSessionIDLookup.ContainsKey(targetDevice.Value.DeviceName)) + if (!RCDeviceSocketHub.SessionInfoList.Values.Any(x => x.MachineName == targetDevice.Value.DeviceName && !existingSessions.Any(y => y.Key != x.RCSocketID))) { return StatusCode(500, "The remote control process failed to start in time on the remote device."); } else { - var rcSessionID = RCDeviceSocketHub.MachineNameToSessionIDLookup[targetDevice.Value.DeviceName]; - return Ok($"{HttpContext.Request.Scheme}://{Request.Host}/RemoteControl?clientID={rcSessionID}&serviceID={targetDevice.Key}"); + var rcSession = RCDeviceSocketHub.SessionInfoList.Values.FirstOrDefault(x=>x.MachineName == targetDevice.Value.DeviceName && !existingSessions.Any(y=>y.Key != x.RCSocketID)); + return Ok($"{HttpContext.Request.Scheme}://{Request.Host}/RemoteControl?clientID={rcSession.RCSocketID}&serviceID={targetDevice.Key}"); } } else diff --git a/Server/Data/DataService.cs b/Server/Data/DataService.cs index 6f6b7062..e9d06a9a 100644 --- a/Server/Data/DataService.cs +++ b/Server/Data/DataService.cs @@ -109,6 +109,19 @@ namespace Remotely.Server.Data targetDevice.DevicePermissionLinks.Any(x => x.PermissionGroup.UserPermissionLinks.Any(y => y.RemotelyUserID == remotelyUser.Id)); } + public bool DoesUserHaveAccessToDevice(string deviceID, string remotelyUserID) + { + var remotelyUser = RemotelyContext.Users.Find(remotelyUserID); + + var targetDevice = RemotelyContext.Devices + .Include(x => x.DevicePermissionLinks) + .FirstOrDefault(x => x.ID == deviceID && x.OrganizationID == remotelyUser.OrganizationID); + + return remotelyUser.IsAdministrator || + targetDevice.DevicePermissionLinks.Count == 0 || + targetDevice.DevicePermissionLinks.Any(x => x.PermissionGroup.UserPermissionLinks.Any(y => y.RemotelyUserID == remotelyUser.Id)); + } + public string[] FilterDeviceIDsByUserPermission(string[] deviceIDs, RemotelyUser remotelyUser) { return RemotelyContext.Devices.Where(x => diff --git a/Server/Models/RCSessionInfo.cs b/Server/Models/RCSessionInfo.cs new file mode 100644 index 00000000..7a2f48d8 --- /dev/null +++ b/Server/Models/RCSessionInfo.cs @@ -0,0 +1,19 @@ +using Remotely.Shared.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Remotely.Server.Models +{ + public class RCSessionInfo + { + public string AttendedSessionID { get; set; } + public string MachineName { get; set; } + public RemoteControlMode Mode { get; set; } + public string RCSocketID { get; set; } + public string RequesterName { get; set; } + public string ServiceID { get; set; } + public DateTime StartTime { get; set; } + } +} diff --git a/Server/Services/DeviceSocketHub.cs b/Server/Services/DeviceSocketHub.cs index 3709d7ca..6d8fbf87 100644 --- a/Server/Services/DeviceSocketHub.cs +++ b/Server/Services/DeviceSocketHub.cs @@ -21,7 +21,7 @@ namespace Remotely.Server.Services RCBrowserHub = rcBrowserHub; } - public static ConcurrentDictionary ServiceConnections { get; set; } = new ConcurrentDictionary(); + public static ConcurrentDictionary ServiceConnections { get; } = new ConcurrentDictionary(); public IHubContext RCBrowserHub { get; } private IHubContext BrowserHub { get; } private DataService DataService { get; } diff --git a/Server/Services/RCBrowserSocketHub.cs b/Server/Services/RCBrowserSocketHub.cs index 9377191a..81e9716c 100644 --- a/Server/Services/RCBrowserSocketHub.cs +++ b/Server/Services/RCBrowserSocketHub.cs @@ -167,13 +167,13 @@ namespace Remotely.Server.Services { if ((RemoteControlMode)remoteControlMode == RemoteControlMode.Normal) { - if (!RCDeviceSocketHub.AttendedSessionList.ContainsKey(screenCasterID)) + if (!RCDeviceSocketHub.SessionInfoList.Any(x => x.Value.AttendedSessionID == screenCasterID)) { await Clients.Caller.SendAsync("SessionIDNotFound"); return; } - screenCasterID = RCDeviceSocketHub.AttendedSessionList[screenCasterID]; + screenCasterID = RCDeviceSocketHub.SessionInfoList.First(x => x.Value.AttendedSessionID == screenCasterID).Value.RCSocketID; } DataService.WriteEvent(new EventLog() @@ -194,7 +194,12 @@ namespace Remotely.Server.Services RequesterName = requesterName; if (Mode == RemoteControlMode.Unattended) { - await RCDeviceHub.Clients.Client(screenCasterID).SendAsync("GetScreenCast", Context.ConnectionId, requesterName); + var serviceID = RCDeviceSocketHub.SessionInfoList.FirstOrDefault(x => x.Value.RCSocketID == screenCasterID).Value?.ServiceID; + var deviceID = DeviceSocketHub.ServiceConnections[serviceID].ID; + if (Context.User.Identity.IsAuthenticated && DataService.DoesUserHaveAccessToDevice(deviceID, Context.UserIdentifier)) + { + await RCDeviceHub.Clients.Client(screenCasterID).SendAsync("GetScreenCast", Context.ConnectionId, requesterName); + } } else { diff --git a/Server/Services/RCDeviceSocketHub.cs b/Server/Services/RCDeviceSocketHub.cs index 95d312a3..0810fadc 100644 --- a/Server/Services/RCDeviceSocketHub.cs +++ b/Server/Services/RCDeviceSocketHub.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Hosting; using System.IO; using System.Drawing; using Remotely.Shared.Models; +using Remotely.Server.Models; namespace Remotely.Server.Services { @@ -29,9 +30,7 @@ namespace Remotely.Server.Services AppConfig = appConfig; } - public static ConcurrentDictionary AttendedSessionList { get; set; } = new ConcurrentDictionary(); - - public static ConcurrentDictionary MachineNameToSessionIDLookup { get; set; } = new ConcurrentDictionary(); + public static ConcurrentDictionary SessionInfoList { get; } = new ConcurrentDictionary(); public ApplicationConfig AppConfig { get; } public RemoteControlSessionRecorder RCSessionRecorder { get; } private IHubContext BrowserHub { get; } @@ -53,65 +52,30 @@ namespace Remotely.Server.Services Context.Items["CurrentScreenSize"] = value; } } + private RCSessionInfo SessionInfo + { + get + { + if (Context.Items.ContainsKey("SessionInfo")) + { + return (RCSessionInfo)Context.Items["SessionInfo"]; + } + else + { + return null; + } + } + set + { + Context.Items["SessionInfo"] = value; + } + } + private DataService DataService { get; } private IHubContext DeviceHub { get; } - private string MachineName - { - get - { - if (Context.Items.ContainsKey("MachineName")) - { - return Context.Items["MachineName"] as string; - } - else - { - return null; - } - } - set - { - Context.Items["MachineName"] = value; - } - } private IHubContext RCBrowserHub { get; } - private string ServiceID - { - get - { - if (Context.Items.ContainsKey("ServiceID")) - { - return Context.Items["ServiceID"] as string; - } - else - { - return null; - } - } - set - { - Context.Items["ServiceID"] = value; - } - } - private DateTime StartTime - { - get - { - if (Context.Items.ContainsKey("StartTime")) - { - return (DateTime)Context.Items["StartTime"]; - } - else - { - return DateTime.Now; - } - } - set - { - Context.Items["StartTime"] = value; - } - } private List ViewerList { @@ -127,7 +91,7 @@ namespace Remotely.Server.Services public async Task CtrlAltDel() { - await DeviceHub.Clients.Client(ServiceID).SendAsync("CtrlAltDel"); + await DeviceHub.Clients.Client(SessionInfo.ServiceID).SendAsync("CtrlAltDel"); } public async Task GetSessionID() { @@ -139,10 +103,7 @@ namespace Remotely.Server.Services } Context.Items["SessionID"] = sessionID; - while (!AttendedSessionList.TryAdd(sessionID, Context.ConnectionId)) - { - await Task.Delay(1000); - } + SessionInfoList[Context.ConnectionId].AttendedSessionID = sessionID; await Clients.Caller.SendAsync("SessionID", sessionID); } @@ -157,44 +118,46 @@ namespace Remotely.Server.Services await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("RelaunchedScreenCasterReady", Context.ConnectionId); } - public override Task OnConnectedAsync() + public override async Task OnConnectedAsync() { - StartTime = DateTime.Now; - return base.OnConnectedAsync(); + SessionInfo = new RCSessionInfo() + { + RCSocketID = Context.ConnectionId, + StartTime = DateTime.Now + }; + while (!SessionInfoList.TryAdd(Context.ConnectionId, SessionInfo)) + { + await Task.Delay(100); + } + + await base.OnConnectedAsync(); } public override async Task OnDisconnectedAsync(Exception exception) - { - if (Context.Items.ContainsKey("SessionID") && AttendedSessionList.ContainsKey(Context.Items["SessionID"].ToString())) + { + while (!SessionInfoList.TryRemove(Context.ConnectionId, out _)) + { + await Task.Delay(100); + } + + if (SessionInfo.Mode == Shared.Enums.RemoteControlMode.Unattended) { - while (!AttendedSessionList.TryRemove(Context.Items["SessionID"].ToString(), out var value)) - { - await Task.Delay(1000); - } await RCBrowserHub.Clients.Clients(ViewerList).SendAsync("ScreenCasterDisconnected"); } - else + else if (SessionInfo.Mode == Shared.Enums.RemoteControlMode.Unattended) { if (ViewerList.Count > 0) { await RCBrowserHub.Clients.Clients(ViewerList).SendAsync("Reconnecting"); - await DeviceHub.Clients.Client(ServiceID).SendAsync("RestartScreenCaster", ViewerList, ServiceID, Context.ConnectionId); + await DeviceHub.Clients.Client(SessionInfo.ServiceID).SendAsync("RestartScreenCaster", ViewerList, SessionInfo.ServiceID, Context.ConnectionId); } } - if (!string.IsNullOrWhiteSpace(MachineName) && MachineNameToSessionIDLookup.ContainsKey(MachineName)) - { - while (!MachineNameToSessionIDLookup.TryRemove(MachineName, out _)) - { - await Task.Delay(1000); - } - } await base.OnDisconnectedAsync(exception); } public void ReceiveDeviceInfo(string serviceID, string machineName) { - ServiceID = serviceID; - MachineName = machineName; - MachineNameToSessionIDLookup[MachineName] = Context.ConnectionId; + SessionInfo.ServiceID = serviceID; + SessionInfo.MachineName = machineName; } public async Task SendMachineName(string machineName, string viewerID) { @@ -219,7 +182,8 @@ namespace Remotely.Server.Services { if (AppConfig.RecordRemoteControlSessions) { - RCSessionRecorder.SaveFrame(captureBytes, left, top, CurrentScreenSize.Width, CurrentScreenSize.Height, rcBrowserHubConnectionID, MachineName, StartTime); + RCSessionRecorder.SaveFrame(captureBytes, left, top, CurrentScreenSize.Width, CurrentScreenSize.Height, + rcBrowserHubConnectionID, SessionInfo.MachineName, SessionInfo.StartTime); } return RCBrowserHub.Clients.Client(rcBrowserHubConnectionID).SendAsync("ScreenCapture", captureBytes, left, top, width, height, captureTime); diff --git a/Server/appsettings.json b/Server/appsettings.json index 512c671b..a0b6bcd9 100644 --- a/Server/appsettings.json +++ b/Server/appsettings.json @@ -1,7 +1,7 @@ { "ConnectionStrings": { "SQLite": "DataSource=Server.db", - "SQLServer": "Server=(localdb)\\mssqllocaldb;Database=aspnet-Server-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true", + "SQLServer": "Server=(localdb)\\mssqllocaldb;Database=Remotely-Server-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true", "PostgreSQL": "Host=localhost;Database=;Username=;Password=" }, "Logging": { diff --git a/Server/wwwroot/scripts/Enums/RemoteControlMode.js b/Server/wwwroot/scripts/Enums/RemoteControlMode.js index de8b04ee..3c97fbbc 100644 --- a/Server/wwwroot/scripts/Enums/RemoteControlMode.js +++ b/Server/wwwroot/scripts/Enums/RemoteControlMode.js @@ -1,6 +1,7 @@ export var RemoteControlMode; (function (RemoteControlMode) { - RemoteControlMode[RemoteControlMode["Unattended"] = 0] = "Unattended"; - RemoteControlMode[RemoteControlMode["Normal"] = 1] = "Normal"; + RemoteControlMode[RemoteControlMode["None"] = 0] = "None"; + RemoteControlMode[RemoteControlMode["Unattended"] = 1] = "Unattended"; + RemoteControlMode[RemoteControlMode["Normal"] = 2] = "Normal"; })(RemoteControlMode || (RemoteControlMode = {})); //# sourceMappingURL=RemoteControlMode.js.map \ No newline at end of file diff --git a/Server/wwwroot/scripts/Enums/RemoteControlMode.js.map b/Server/wwwroot/scripts/Enums/RemoteControlMode.js.map index 74d0257b..74eaece6 100644 --- a/Server/wwwroot/scripts/Enums/RemoteControlMode.js.map +++ b/Server/wwwroot/scripts/Enums/RemoteControlMode.js.map @@ -1 +1 @@ -{"version":3,"file":"RemoteControlMode.js","sourceRoot":"","sources":["RemoteControlMode.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,iBAGX;AAHD,WAAY,iBAAiB;IACzB,qEAAU,CAAA;IACV,6DAAM,CAAA;AACV,CAAC,EAHW,iBAAiB,KAAjB,iBAAiB,QAG5B"} \ No newline at end of file +{"version":3,"file":"RemoteControlMode.js","sourceRoot":"","sources":["RemoteControlMode.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,iBAIX;AAJD,WAAY,iBAAiB;IACzB,yDAAI,CAAA;IACJ,qEAAU,CAAA;IACV,6DAAM,CAAA;AACV,CAAC,EAJW,iBAAiB,KAAjB,iBAAiB,QAI5B"} \ No newline at end of file diff --git a/Server/wwwroot/scripts/Enums/RemoteControlMode.ts b/Server/wwwroot/scripts/Enums/RemoteControlMode.ts index a5a1e1a6..cba8a2d0 100644 --- a/Server/wwwroot/scripts/Enums/RemoteControlMode.ts +++ b/Server/wwwroot/scripts/Enums/RemoteControlMode.ts @@ -1,4 +1,5 @@ export enum RemoteControlMode { + None, Unattended, Normal } \ No newline at end of file diff --git a/Shared/Enums/RemoteControlMode.cs b/Shared/Enums/RemoteControlMode.cs index eb83b71d..d1e6ecf2 100644 --- a/Shared/Enums/RemoteControlMode.cs +++ b/Shared/Enums/RemoteControlMode.cs @@ -6,6 +6,7 @@ namespace Remotely.Shared.Enums { public enum RemoteControlMode { + None, Unattended, Normal }