mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
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:
parent
3ed8650556
commit
652e5e502c
@ -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 ||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -89,6 +89,10 @@ namespace Remotely.Server.Services
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DataService.WriteEvent(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsProcessing = false;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user