Fixed DeviceCameOnline and Heartbeat events from overwriting device fields. Replaced SignalR groups with static user collections to perform online user lookups. Added navigation properties to authorization queries in DataService.

This commit is contained in:
Jared Goodwin 2019-07-13 01:44:02 -07:00
parent 3ed8650556
commit 652e5e502c
6 changed files with 170 additions and 90 deletions

View File

@ -39,16 +39,30 @@ namespace Remotely.Server.Data
RemotelyContext.SaveChanges();
}
public bool AddOrUpdateDevice(Device device)
public bool AddOrUpdateDevice(Device device, out Device updatedDevice)
{
var existingDevice = RemotelyContext.Devices.Find(device.ID);
if (existingDevice != null)
{
device.ServerVerificationToken = existingDevice.ServerVerificationToken;
RemotelyContext.Entry(existingDevice).CurrentValues.SetValues(device);
existingDevice.CurrentUser = device.CurrentUser;
existingDevice.DeviceName = device.DeviceName;
existingDevice.Drives = device.Drives;
existingDevice.FreeMemory = device.FreeMemory;
existingDevice.FreeStorage = device.FreeStorage;
existingDevice.Is64Bit = device.Is64Bit;
existingDevice.IsOnline = true;
existingDevice.LastOnline = DateTime.Now;
existingDevice.OSArchitecture = device.OSArchitecture;
existingDevice.OSDescription = device.OSDescription;
existingDevice.Platform = device.Platform;
existingDevice.ProcessorCount = device.ProcessorCount;
existingDevice.TotalMemory = device.TotalMemory;
existingDevice.TotalStorage = device.TotalStorage;
updatedDevice = existingDevice;
}
else
{
updatedDevice = device;
if (!RemotelyContext.Organizations.Any(x => x.ID == device.OrganizationID))
{
WriteEvent(new EventLog()
@ -95,6 +109,8 @@ namespace Remotely.Server.Data
{
var targetDevice = RemotelyContext.Devices
.Include(x=>x.DevicePermissionLinks)
.ThenInclude(x=>x.PermissionGroup)
.ThenInclude(x=>x.UserPermissionLinks)
.FirstOrDefault(x => x.ID == deviceID && x.OrganizationID == remotelyUser.OrganizationID);
return remotelyUser.IsAdministrator ||
@ -102,12 +118,34 @@ namespace Remotely.Server.Data
targetDevice.DevicePermissionLinks.Any(x => x.PermissionGroup.UserPermissionLinks.Any(y => y.RemotelyUserID == remotelyUser.Id));
}
public List<RemotelyUser> GetUsersWithAccessToDevice(IEnumerable<string> userIDs, Device device)
{
var targetDevice = RemotelyContext.Devices
.Include(x => x.DevicePermissionLinks)
.ThenInclude(x => x.PermissionGroup)
.ThenInclude(x => x.UserPermissionLinks)
.FirstOrDefault(x => x.ID == device.ID && x.OrganizationID == device.OrganizationID);
var authorizedUsers = RemotelyContext.Users.Where(x =>
x.OrganizationID == device.OrganizationID &&
userIDs.Contains(x.Id) &&
(
targetDevice.DevicePermissionLinks.Count == 0 ||
x.IsAdministrator ||
targetDevice.DevicePermissionLinks.Any(y => y.PermissionGroup.UserPermissionLinks.Any(z=>z.RemotelyUserID == x.Id))
));
return authorizedUsers.ToList();
}
public bool DoesUserHaveAccessToDevice(string deviceID, string remotelyUserID)
{
var remotelyUser = RemotelyContext.Users.Find(remotelyUserID);
var targetDevice = RemotelyContext.Devices
.Include(x => x.DevicePermissionLinks)
.ThenInclude(x => x.PermissionGroup)
.ThenInclude(x => x.UserPermissionLinks)
.FirstOrDefault(x => x.ID == deviceID && x.OrganizationID == remotelyUser.OrganizationID);
return remotelyUser.IsAdministrator ||

View File

@ -32,8 +32,9 @@ namespace Remotely.Server.Services
AppConfig = appConfig;
}
private ApplicationConfig AppConfig { get; }
private DataService DataService { get; }
public static ConcurrentDictionary<string, RemotelyUser> ConnectionIdToUserLookup { get; } = new ConcurrentDictionary<string, RemotelyUser>();
private ApplicationConfig AppConfig { get; }
private DataService DataService { get; }
private IHubContext<DeviceSocketHub> DeviceHub { get; }
private RemotelyUser RemotelyUser
{
@ -59,71 +60,79 @@ namespace Remotely.Server.Services
DataService.AddPermissionToDevices(RemotelyUser.Id, deviceIDs, groupName);
await Clients.Caller.SendAsync("DisplayConsoleMessage", "Group added.");
}
public async Task DeployScript(string fileID, string mode, string[] deviceIDs)
{
deviceIDs = DataService.FilterDeviceIDsByUserPermission(deviceIDs, RemotelyUser);
var connections = GetActiveClientConnections(deviceIDs);
var commandContext = new CommandContext()
{
CommandMode = mode,
CommandText = Encoding.UTF8.GetString(DataService.GetSharedFiled(fileID).FileContents),
SenderConnectionID = Context.ConnectionId,
SenderUserID = Context.UserIdentifier,
TargetDeviceIDs = connections.Select(x => x.Value.ID).ToArray(),
OrganizationID = RemotelyUser.OrganizationID
};
DataService.AddOrUpdateCommandContext(commandContext);
await Clients.Caller.SendAsync("CommandContextCreated", commandContext);
foreach (var connection in connections)
{
await DeviceHub.Clients.Client(connection.Key).SendAsync("DeployScript", mode, fileID, commandContext.ID, Context.ConnectionId);
}
}
public async Task ExecuteCommandOnClient(string mode, string command, string[] deviceIDs)
{
deviceIDs = DataService.FilterDeviceIDsByUserPermission(deviceIDs, RemotelyUser);
var connections = GetActiveClientConnections(deviceIDs);
var commandContext = new CommandContext()
{
CommandMode = mode,
CommandText = command,
SenderConnectionID = Context.ConnectionId,
SenderUserID = Context.UserIdentifier,
TargetDeviceIDs = connections.Select(x => x.Value.ID).ToArray(),
OrganizationID = RemotelyUser.Organization.ID
};
DataService.AddOrUpdateCommandContext(commandContext);
await Clients.Caller.SendAsync("CommandContextCreated", commandContext);
foreach (var connection in connections)
{
await DeviceHub.Clients.Client(connection.Key).SendAsync("ExecuteCommand", mode, command, commandContext.ID, Context.ConnectionId);
}
}
public async Task GetGroups(string[] deviceIDs)
{
deviceIDs = DataService.FilterDeviceIDsByUserPermission(deviceIDs, RemotelyUser);
var result = DataService.GetDevicesAndPermissions(RemotelyUser.Id, deviceIDs);
await Clients.Caller.SendAsync("GetGroupsResult", result);
}
public async Task DeployScript(string fileID, string mode, string[] deviceIDs)
{
deviceIDs = DataService.FilterDeviceIDsByUserPermission(deviceIDs, RemotelyUser);
var connections = GetActiveClientConnections(deviceIDs);
var commandContext = new CommandContext()
{
CommandMode = mode,
CommandText = Encoding.UTF8.GetString(DataService.GetSharedFiled(fileID).FileContents),
SenderConnectionID = Context.ConnectionId,
SenderUserID = Context.UserIdentifier,
TargetDeviceIDs = connections.Select(x => x.Value.ID).ToArray(),
OrganizationID = RemotelyUser.OrganizationID
};
DataService.AddOrUpdateCommandContext(commandContext);
await Clients.Caller.SendAsync("CommandContextCreated", commandContext);
foreach (var connection in connections)
{
await DeviceHub.Clients.Client(connection.Key).SendAsync("DeployScript", mode, fileID, commandContext.ID, Context.ConnectionId);
}
}
public async Task ExecuteCommandOnClient(string mode, string command, string[] deviceIDs)
{
deviceIDs = DataService.FilterDeviceIDsByUserPermission(deviceIDs, RemotelyUser);
var connections = GetActiveClientConnections(deviceIDs);
var commandContext = new CommandContext()
{
CommandMode = mode,
CommandText = command,
SenderConnectionID = Context.ConnectionId,
SenderUserID = Context.UserIdentifier,
TargetDeviceIDs = connections.Select(x => x.Value.ID).ToArray(),
OrganizationID = RemotelyUser.Organization.ID
};
DataService.AddOrUpdateCommandContext(commandContext);
await Clients.Caller.SendAsync("CommandContextCreated", commandContext);
foreach (var connection in connections)
{
await DeviceHub.Clients.Client(connection.Key).SendAsync("ExecuteCommand", mode, command, commandContext.ID, Context.ConnectionId);
}
}
public override async Task OnConnectedAsync()
{
RemotelyUser = DataService.GetUserAndPermissionsByID(Context?.UserIdentifier);
if (IsConnectionValid()?.Result == false)
RemotelyUser = DataService.GetUserAndPermissionsByID(Context.UserIdentifier);
if (await IsConnectionValid() == false)
{
return;
}
await Groups.AddToGroupAsync(Context.ConnectionId, RemotelyUser.Organization.ID);
while (!ConnectionIdToUserLookup.TryAdd(Context.ConnectionId, RemotelyUser))
{
DataService.WriteEvent("Retrying ConnectionIdToUserLookup.TryAdd in BrowserSocketHub.");
await Task.Delay(100);
}
await Clients.Caller.SendAsync("UserOptions", RemotelyUser.UserOptions);
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, RemotelyUser.Organization.ID);
await base.OnDisconnectedAsync(exception);
while (!ConnectionIdToUserLookup.TryRemove(Context.ConnectionId, out _))
{
DataService.WriteEvent("Retrying ConnectionIdToUserLookup.TryRemove in BrowserSocketHub.");
await Task.Delay(100);
}
await base.OnDisconnectedAsync(exception);
}
public async Task RemoteControl(string deviceID)
@ -142,8 +151,15 @@ namespace Remotely.Server.Services
}
}
public async Task RemoveGroup(string[] deviceIDs, string groupName)
{
public async Task RemoveDevices(string[] deviceIDs)
{
var filterDevices = DataService.FilterDeviceIDsByUserPermission(deviceIDs, RemotelyUser);
DataService.RemoveDevices(filterDevices);
await Clients.Caller.SendAsync("RefreshDeviceList");
}
public async Task RemoveGroup(string[] deviceIDs, string groupName)
{
groupName = groupName.Trim();
deviceIDs = DataService.FilterDeviceIDsByUserPermission(deviceIDs, RemotelyUser);
if (!DataService.DoesGroupExist(RemotelyUser.Id, groupName))
@ -154,12 +170,6 @@ namespace Remotely.Server.Services
DataService.RemovePermissionFromDevices(RemotelyUser.Id, deviceIDs, groupName);
await Clients.Caller.SendAsync("DisplayConsoleMessage", "Group removed.");
}
public async Task RemoveDevices(string[] deviceIDs)
{
var filterDevices = DataService.FilterDeviceIDsByUserPermission(deviceIDs, RemotelyUser);
DataService.RemoveDevices(filterDevices);
await Clients.Caller.SendAsync("RefreshDeviceList");
}
public async Task TransferFiles(List<string> fileIDs, string transferID, string[] deviceIDs)
{
DataService.WriteEvent(new EventLog()

View File

@ -73,24 +73,26 @@ namespace Remotely.Server.Services
Context.Abort();
return;
}
device.IsOnline = true;
device.LastOnline = DateTime.Now;
Device = device;
if (DataService.AddOrUpdateDevice(device))
if (DataService.AddOrUpdateDevice(device, out var updatedDevice))
{
var failCount = 0;
while (!ServiceConnections.TryAdd(Context.ConnectionId, device))
Device = updatedDevice;
while (!ServiceConnections.TryAdd(Context.ConnectionId, Device))
{
if (failCount > 3)
{
Context.Abort();
return;
}
failCount++;
await Task.Delay(1000);
DataService.WriteEvent("Retrying ServiceConnections.TryAdd in DeviceSocketHub.");
await Task.Delay(100);
}
await this.Groups.AddToGroupAsync(this.Context.ConnectionId, device.OrganizationID);
await BrowserHub.Clients.Group(Device.OrganizationID).SendAsync("DeviceCameOnline", 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();
await BrowserHub.Clients.Clients(connectionIds).SendAsync("DeviceCameOnline", Device);
}
else
{
@ -107,11 +109,18 @@ namespace Remotely.Server.Services
public async Task DeviceHeartbeat(Device device)
{
device.IsOnline = true;
device.LastOnline = DateTime.Now;
Device = device;
DataService.AddOrUpdateDevice(device);
await BrowserHub.Clients.Group(Device.OrganizationID).SendAsync("DeviceHeartbeat", Device);
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();
await BrowserHub.Clients.Clients(connectionIds).SendAsync("DeviceHeartbeat", Device);
}
public async Task DisplayConsoleMessage(string message, string requesterID)
@ -127,12 +136,24 @@ namespace Remotely.Server.Services
if (Device != null)
{
DataService.DeviceDisconnected(Device.ID);
await this.Groups.RemoveFromGroupAsync(this.Context.ConnectionId, Device.OrganizationID);
Device.IsOnline = false;
await BrowserHub.Clients.Group(Device.OrganizationID).SendAsync("DeviceWentOffline", Device);
while (!ServiceConnections.TryRemove(Context.ConnectionId, out var 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();
await BrowserHub.Clients.Clients(connectionIds).SendAsync("DeviceWentOffline", Device);
while (!ServiceConnections.TryRemove(Context.ConnectionId, out _))
{
await Task.Delay(1000);
DataService.WriteEvent("Retrying ServiceConnections.TryRemove in DeviceSocketHub.");
await Task.Delay(100);
}
}

View File

@ -116,23 +116,28 @@ namespace Remotely.Server.Services
await RCDeviceHub.Clients.Client(ScreenCasterID).SendAsync("MouseWheel", deltaX, deltaY, Context.ConnectionId);
}
public override Task OnConnectedAsync()
public override async Task OnConnectedAsync()
{
if (Context.User.Identity.IsAuthenticated)
{
var user = DataService.GetUserByName(Context.User.Identity.Name);
OrganizationConnectionList.TryAdd(Context.ConnectionId, user);
while (!OrganizationConnectionList.TryAdd(Context.ConnectionId, user))
{
DataService.WriteEvent("Retrying OrganizationConnectionList.TryAdd in RCBrowserSocketHub.");
await Task.Delay(100);
}
}
return base.OnConnectedAsync();
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
if (Context.User.Identity.IsAuthenticated)
{
while (!OrganizationConnectionList.TryRemove(Context.ConnectionId, out var user))
while (!OrganizationConnectionList.TryRemove(Context.ConnectionId, out _))
{
await Task.Delay(1000);
DataService.WriteEvent("Retrying OrganizationConnectionList.TryRemove in RCBrowserSocketHub.");
await Task.Delay(100);
}
}
await RCDeviceHub.Clients.Client(ScreenCasterID).SendAsync("ViewerDisconnected", Context.ConnectionId);

View File

@ -127,6 +127,7 @@ namespace Remotely.Server.Services
};
while (!SessionInfoList.TryAdd(Context.ConnectionId, SessionInfo))
{
DataService.WriteEvent("Retrying SessionInfoList.TryAdd in RCDeviceSocketHub.");
await Task.Delay(100);
}
@ -136,6 +137,7 @@ namespace Remotely.Server.Services
{
while (!SessionInfoList.TryRemove(Context.ConnectionId, out _))
{
DataService.WriteEvent("Retrying SessionInfoList.TryRemove in RCDeviceSocketHub.");
await Task.Delay(100);
}

View File

@ -89,6 +89,10 @@ namespace Remotely.Server.Services
}
}
}
catch (Exception ex)
{
DataService.WriteEvent(ex);
}
finally
{
IsProcessing = false;