diff --git a/Server/API/AgentUpdateController.cs b/Server/API/AgentUpdateController.cs index 91bbf032..51c18482 100644 --- a/Server/API/AgentUpdateController.cs +++ b/Server/API/AgentUpdateController.cs @@ -21,7 +21,6 @@ namespace Remotely.Server.API private IWebHostEnvironment HostingEnv { get; } private DataService DataService { get; } - // GET: api/ [HttpGet("[action]")] public string CurrentVersion() { diff --git a/Server/API/ClientDownloadsController.cs b/Server/API/ClientDownloadsController.cs index f28e45ef..4024dd00 100644 --- a/Server/API/ClientDownloadsController.cs +++ b/Server/API/ClientDownloadsController.cs @@ -29,18 +29,12 @@ namespace Remotely.Server.API private DataService DataService { get; set; } private IWebHostEnvironment HostEnv { get; set; } - [Authorize] + [ServiceFilter(typeof(ApiAuthorizationFilter))] [HttpGet("{platformID}")] public async Task Get(string platformID) { - var user = DataService.GetUserByName(User.Identity.Name); - return await GetInstallFile(user.OrganizationID, platformID); - } - - [HttpGet("{organizationID}/{platformID}")] - public async Task Get(string organizationID, string platformID) - { - return await GetInstallFile(organizationID, platformID); + Request.Headers.TryGetValue("OrganizationID", out var orgID); + return await GetInstallFile(orgID, platformID); } private async Task GetInstallFile(string organizationID, string platformID) @@ -51,9 +45,6 @@ namespace Remotely.Server.API byte[] fileBytes; switch (platformID) { - // TODO: Remove x64/x86 and PS files after a few releases. - case "Win10-x64": - case "Win10-x86": case "Win10": { fileName = $"Install-{platformID}.ps1"; diff --git a/Server/API/CommandsController.cs b/Server/API/CommandsController.cs index 23a36f7e..85922399 100644 --- a/Server/API/CommandsController.cs +++ b/Server/API/CommandsController.cs @@ -28,16 +28,13 @@ namespace Remotely.Server.API // GET: api/ [HttpGet("{fileExt}")] - [Authorize] + [ServiceFilter(typeof(ApiAuthorizationFilter))] public ActionResult DownloadAll(string fileExt) { - if (!DataService.GetUserByName(User.Identity.Name).IsAdministrator) - { - return Unauthorized(); - } + Request.Headers.TryGetValue("OrganizationID", out var orgID); var content = ""; - var commandContexts = DataService.GetAllCommandContexts(User.Identity.Name); + var commandContexts = DataService.GetAllCommandContexts(orgID); switch (fileExt.ToUpper()) { case "JSON": @@ -59,11 +56,13 @@ namespace Remotely.Server.API } [HttpGet("{fileExt}/{commandID}")] - [Authorize] + [ServiceFilter(typeof(ApiAuthorizationFilter))] public FileResult DownloadResults(string fileExt, string commandID) { + Request.Headers.TryGetValue("OrganizationID", out var orgID); + var content = ""; - var commandContext = DataService.GetCommandContext(commandID, User.Identity.Name); + var commandContext = DataService.GetCommandContext(commandID, orgID); switch (fileExt.ToUpper()) { case "JSON": @@ -85,10 +84,11 @@ namespace Remotely.Server.API } [HttpGet("PSCoreResult/{commandID}/{deviceID}")] - [Authorize] + [ServiceFilter(typeof(ApiAuthorizationFilter))] public PSCoreCommandResult PSCoreResult(string commandID, string deviceID) { - return DataService.GetCommandContext(commandID, User.Identity.Name).PSCoreResults.FirstOrDefault(x => x.DeviceID == deviceID); + Request.Headers.TryGetValue("OrganizationID", out var orgID); + return DataService.GetCommandContext(commandID, orgID).PSCoreResults.FirstOrDefault(x => x.DeviceID == deviceID); } [HttpGet("GenericResult/{commandID}/{deviceID}")] diff --git a/Server/API/CoreVersionController.cs b/Server/API/CoreVersionController.cs deleted file mode 100644 index b4a047e8..00000000 --- a/Server/API/CoreVersionController.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Diagnostics; -using System.IO; -using System.IO.Compression; - -namespace Remotely.Server.API -{ - // TODO: Delete after a few versions. - [Route("api/[controller]")] - [ApiController] - public class CoreVersionController : ControllerBase - { - public CoreVersionController(IWebHostEnvironment hostingEnv) - { - this.HostingEnv = hostingEnv; - } - - public IWebHostEnvironment HostingEnv { get; } - - // GET: api/ - [HttpGet()] - public string Get() - { - var filePath = Path.Combine(HostingEnv.ContentRootPath, "CurrentVersion.txt"); - if (!System.IO.File.Exists(filePath)) - { - return "0.0.0.0"; - } - return System.IO.File.ReadAllText(filePath).Trim(); - } - } -} diff --git a/Server/API/DevicesController.cs b/Server/API/DevicesController.cs index e74ffd1c..d24ff07e 100644 --- a/Server/API/DevicesController.cs +++ b/Server/API/DevicesController.cs @@ -12,7 +12,6 @@ using Microsoft.AspNetCore.Mvc; namespace Remotely.Server.API { - [Authorize] [ApiController] [Route("api/[controller]")] public class DevicesController : ControllerBase @@ -26,21 +25,35 @@ namespace Remotely.Server.API private DataService DataService { get; set; } private UserManager UserManager { get; set; } - // GET: api/ + [HttpGet] - public async Task> Get() + [ServiceFilter(typeof(ApiAuthorizationFilter))] + public IEnumerable Get() { - var user = await UserManager.GetUserAsync(User); - var devices = DataService.GetAllDevicesForUser(user.Id); - return devices; + Request.Headers.TryGetValue("OrganizationID", out var orgID); + + if (User.Identity.IsAuthenticated) + { + return DataService.GetDevicesForUser(User.Identity.Name); + } + + return DataService.GetAllDevices(orgID); } - // GET api//5 + [ServiceFilter(typeof(ApiAuthorizationFilter))] [HttpGet("{id}")] - public async Task Get(string id) + public Device Get(string id) { - var user = await UserManager.GetUserAsync(User); - return DataService.GetDeviceForUser(user.Id, id); + Request.Headers.TryGetValue("OrganizationID", out var orgID); + + var device = DataService.GetDevice(orgID, id); + + if (User.Identity.IsAuthenticated && + !DataService.DoesUserHaveAccessToDevice(id, User.Identity.Name)) + { + return null; + } + return device; } } } diff --git a/Server/API/OrganizationManagementController.cs b/Server/API/OrganizationManagementController.cs index 91bbf918..ae74d4bd 100644 --- a/Server/API/OrganizationManagementController.cs +++ b/Server/API/OrganizationManagementController.cs @@ -20,7 +20,6 @@ using Microsoft.AspNetCore.WebUtilities; namespace Remotely.Server.API { [Route("api/[controller]")] - [Authorize] [ApiController] public class OrganizationManagementController : ControllerBase { @@ -37,37 +36,48 @@ namespace Remotely.Server.API [HttpPost("ChangeIsAdmin/{userID}")] + [ServiceFilter(typeof(ApiAuthorizationFilter))] public IActionResult ChangeIsAdmin(string userID, [FromBody]bool isAdmin) { - if (!DataService.GetUserByName(User.Identity.Name).IsAdministrator) + if (User.Identity.IsAuthenticated && + !DataService.GetUserByName(User.Identity.Name).IsAdministrator) { return Unauthorized(); } - if (DataService.GetUserByName(User.Identity.Name).Id == userID) + if (User.Identity.IsAuthenticated && + DataService.GetUserByName(User.Identity.Name).Id == userID) { return BadRequest("You can't remove administrator rights from yourself."); } - DataService.ChangeUserIsAdmin(User.Identity.Name, userID, isAdmin); + Request.Headers.TryGetValue("OrganizationID", out var orgID); + + DataService.ChangeUserIsAdmin(orgID, userID, isAdmin); return Ok("ok"); } [HttpDelete("DeleteInvite/{inviteID}")] + [ServiceFilter(typeof(ApiAuthorizationFilter))] public IActionResult DeleteInvite(string inviteID) { - if (!DataService.GetUserByName(User.Identity.Name).IsAdministrator) + if (User.Identity.IsAuthenticated && + !DataService.GetUserByName(User.Identity.Name).IsAdministrator) { return Unauthorized(); } - DataService.DeleteInvite(User.Identity.Name, inviteID); + + Request.Headers.TryGetValue("OrganizationID", out var orgID); + DataService.DeleteInvite(orgID, inviteID); return Ok("ok"); } [HttpPut("Name")] + [ServiceFilter(typeof(ApiAuthorizationFilter))] public IActionResult Name([FromBody]string organizationName) { - if (!DataService.GetUserByName(User.Identity.Name).IsAdministrator) + if (User.Identity.IsAuthenticated && + !DataService.GetUserByName(User.Identity.Name).IsAdministrator) { return Unauthorized(); } @@ -75,56 +85,72 @@ namespace Remotely.Server.API { return BadRequest(); } - DataService.UpdateOrganizationName(User.Identity.Name, organizationName.Trim()); + + Request.Headers.TryGetValue("OrganizationID", out var orgID); + DataService.UpdateOrganizationName(orgID, organizationName.Trim()); return Ok("ok"); } [HttpDelete("DeviceGroup")] + [ServiceFilter(typeof(ApiAuthorizationFilter))] public IActionResult DeviceGroup([FromBody]string deviceGroupID) { - if (!DataService.GetUserByName(User.Identity.Name).IsAdministrator) + if (User.Identity.IsAuthenticated && + !DataService.GetUserByName(User.Identity.Name).IsAdministrator) { return Unauthorized(); } - DataService.DeleteDeviceGroup(User.Identity.Name, deviceGroupID.Trim()); + Request.Headers.TryGetValue("OrganizationID", out var orgID); + DataService.DeleteDeviceGroup(orgID, deviceGroupID.Trim()); return Ok("ok"); } [HttpPost("DeviceGroup")] + [ServiceFilter(typeof(ApiAuthorizationFilter))] public IActionResult DeviceGroup([FromBody]DeviceGroup deviceGroup) { - if (!DataService.GetUserByName(User.Identity.Name).IsAdministrator) + if (User.Identity.IsAuthenticated && + !DataService.GetUserByName(User.Identity.Name).IsAdministrator) { return Unauthorized(); } + if (!ModelState.IsValid) { return BadRequest(); } - var result = DataService.AddDeviceGroup(User.Identity.Name, deviceGroup, out var deviceGroupID, out var errorMessage); + + Request.Headers.TryGetValue("OrganizationID", out var orgID); + var result = DataService.AddDeviceGroup(orgID, deviceGroup, out var deviceGroupID, out var errorMessage); if (!result) { return BadRequest(errorMessage); } return Ok(deviceGroupID); } + [HttpDelete("RemoveUserFromOrganization/{userID}")] + [ServiceFilter(typeof(ApiAuthorizationFilter))] public IActionResult RemoveUserFromOrganization(string userID) { - if (!DataService.GetUserByName(User.Identity.Name).IsAdministrator) + if (User.Identity.IsAuthenticated && + !DataService.GetUserByName(User.Identity.Name).IsAdministrator) { return Unauthorized(); } - DataService.RemoveUserFromOrganization(User.Identity.Name, userID); + Request.Headers.TryGetValue("OrganizationID", out var orgID); + DataService.RemoveUserFromOrganization(orgID, userID); return Ok("ok"); } [HttpPost("SendInvite")] + [ServiceFilter(typeof(ApiAuthorizationFilter))] public async Task SendInvite([FromBody]Invite invite) { - if (!DataService.GetUserByName(User.Identity.Name).IsAdministrator) + if (User.Identity.IsAuthenticated && + !DataService.GetUserByName(User.Identity.Name).IsAdministrator) { return Unauthorized(); } @@ -165,7 +191,9 @@ namespace Remotely.Server.API } } - var newInvite = DataService.AddInvite(User.Identity.Name, invite); + Request.Headers.TryGetValue("OrganizationID", out var orgID); + + var newInvite = DataService.AddInvite(orgID, invite); var inviteURL = $"{Request.Scheme}://{Request.Host}/Invite?id={newInvite.ID}"; await EmailSender.SendEmailAsync(invite.InvitedUser, "Invitation to Organization in Remotely", @@ -173,7 +201,7 @@ namespace Remotely.Server.API

Hello!

- You've been invited by {User.Identity.Name} to join an organization in Remotely. + You've been invited to join an organization in Remotely. {newUserMessage}

You can join the organization by clicking here."); diff --git a/Server/API/RemoteControlController.cs b/Server/API/RemoteControlController.cs index 73e62e9b..a76b55c6 100644 --- a/Server/API/RemoteControlController.cs +++ b/Server/API/RemoteControlController.cs @@ -35,10 +35,11 @@ namespace Remotely.Server.API public SignInManager SignInManager { get; } [HttpGet("{deviceID}")] - [Authorize] + [ServiceFilter(typeof(ApiAuthorizationFilter))] public async Task Get(string deviceID) { - return await InitiateRemoteControl(deviceID, HttpContext.User.Identity.Name); + Request.Headers.TryGetValue("OrganizationID", out var orgID); + return await InitiateRemoteControl(deviceID, orgID); } [HttpPost] @@ -71,47 +72,46 @@ namespace Remotely.Server.API return BadRequest(); } - private async Task InitiateRemoteControl(string deviceID, string userName) + private async Task InitiateRemoteControl(string deviceID, string orgID) { - var remotelyUser = DataService.GetUserByName(userName); var targetDevice = DeviceSocketHub.ServiceConnections.FirstOrDefault(x => - x.Value.OrganizationID == remotelyUser.OrganizationID && + x.Value.OrganizationID == orgID && x.Value.ID.ToLower() == deviceID.ToLower()); if (targetDevice.Value != null) { - if (DataService.DoesUserHaveAccessToDevice(targetDevice.Value.ID, remotelyUser)) + if (User.Identity.IsAuthenticated && + !DataService.DoesUserHaveAccessToDevice(targetDevice.Value.ID, User.Identity.Name)) { - var currentUsers = RCDeviceSocketHub.SessionInfoList.Count(x => x.Value.OrganizationID == remotelyUser.OrganizationID); - if (currentUsers >= AppConfig.RemoteControlSessionLimit) - { - return BadRequest("There are already the maximum amount of active remote control sessions for your organization."); - } + return Unauthorized(); + } - var existingSessions = RCDeviceSocketHub.SessionInfoList.Where(x => x.Value.DeviceID == targetDevice.Value.ID); - await DeviceHub.Clients.Client(targetDevice.Key).SendAsync("RemoteControl", Request.HttpContext.Connection.Id, targetDevice.Key); + var currentUsers = RCDeviceSocketHub.SessionInfoList.Count(x => x.Value.OrganizationID == orgID); + if (currentUsers >= AppConfig.RemoteControlSessionLimit) + { + return BadRequest("There are already the maximum amount of active remote control sessions for your organization."); + } - var stopWatch = Stopwatch.StartNew(); + var existingSessions = RCDeviceSocketHub.SessionInfoList.Where(x => x.Value.DeviceID == targetDevice.Value.ID); - while (!RCDeviceSocketHub.SessionInfoList.Values.Any(x=>x.DeviceID == targetDevice.Value.ID && !existingSessions.Any(y=>y.Key != x.RCDeviceSocketID)) && stopWatch.Elapsed.TotalSeconds < 5) - { - await Task.Delay(10); - } + await DeviceHub.Clients.Client(targetDevice.Key).SendAsync("RemoteControl", Request.HttpContext.Connection.Id, targetDevice.Key); - if (!RCDeviceSocketHub.SessionInfoList.Values.Any(x => x.DeviceID == targetDevice.Value.ID && !existingSessions.Any(y => y.Key != x.RCDeviceSocketID))) - { - return StatusCode(408, "The remote control process failed to start in time on the remote device."); - } - else - { - var rcSession = RCDeviceSocketHub.SessionInfoList.Values.FirstOrDefault(x=>x.DeviceID == targetDevice.Value.ID && !existingSessions.Any(y=>y.Key != x.RCDeviceSocketID)); - return Ok($"{HttpContext.Request.Scheme}://{Request.Host}/RemoteControl?clientID={rcSession.RCDeviceSocketID}&serviceID={targetDevice.Key}&fromApi=true"); - } + var stopWatch = Stopwatch.StartNew(); + + while (!RCDeviceSocketHub.SessionInfoList.Values.Any(x => x.DeviceID == targetDevice.Value.ID && !existingSessions.Any(y => y.Key != x.RCDeviceSocketID)) && stopWatch.Elapsed.TotalSeconds < 5) + { + await Task.Delay(10); + } + + if (!RCDeviceSocketHub.SessionInfoList.Values.Any(x => x.DeviceID == targetDevice.Value.ID && !existingSessions.Any(y => y.Key != x.RCDeviceSocketID))) + { + return StatusCode(408, "The remote control process failed to start in time on the remote device."); } else { - return Unauthorized(); + var rcSession = RCDeviceSocketHub.SessionInfoList.Values.FirstOrDefault(x => x.DeviceID == targetDevice.Value.ID && !existingSessions.Any(y => y.Key != x.RCDeviceSocketID)); + return Ok($"{HttpContext.Request.Scheme}://{Request.Host}/RemoteControl?clientID={rcSession.RCDeviceSocketID}&serviceID={targetDevice.Key}&fromApi=true"); } } else diff --git a/Server/API/ScriptingController.cs b/Server/API/ScriptingController.cs index 83d5b443..e036d665 100644 --- a/Server/API/ScriptingController.cs +++ b/Server/API/ScriptingController.cs @@ -31,7 +31,7 @@ namespace Remotely.Server.API private IHubContext DeviceHub { get; } private UserManager UserManager { get; } - [Authorize] + [ServiceFilter(typeof(ApiAuthorizationFilter))] [HttpPost("[action]/{mode}/{deviceID}")] public async Task> ExecuteCommand(string mode, string deviceID) { @@ -40,16 +40,24 @@ namespace Remotely.Server.API { command = await sr.ReadToEndAsync(); } - var username = Request.HttpContext.User.Identity.Name; - var user = await UserManager.FindByNameAsync(username); - if (!DataService.DoesUserHaveAccessToDevice(deviceID, user)) + + var userID = string.Empty; + if (Request.HttpContext.User.Identity.IsAuthenticated) { - return Unauthorized(); + var username = Request.HttpContext.User.Identity.Name; + var user = await UserManager.FindByNameAsync(username); + userID = user.Id; + if (!DataService.DoesUserHaveAccessToDevice(deviceID, user)) + { + return Unauthorized(); + } + } + Request.Headers.TryGetValue("OrganizationID", out var orgID); KeyValuePair connection = DeviceSocketHub.ServiceConnections.FirstOrDefault(x => - x.Value.OrganizationID == user.OrganizationID && + x.Value.OrganizationID == orgID && x.Value.ID == deviceID); if (string.IsNullOrWhiteSpace(connection.Key)) @@ -62,13 +70,13 @@ namespace Remotely.Server.API CommandMode = "PSCore", CommandText = command, SenderConnectionID = string.Empty, - SenderUserID = user.Id, + SenderUserID = userID, TargetDeviceIDs = new string[] { deviceID }, - OrganizationID = user.OrganizationID + OrganizationID = orgID }; DataService.AddOrUpdateCommandContext(commandContext); var requestID = Guid.NewGuid().ToString(); - await DeviceHub.Clients.Client(connection.Key).SendAsync("ExecuteCommandFromApi", mode, requestID, command, commandContext.ID, username); + await DeviceHub.Clients.Client(connection.Key).SendAsync("ExecuteCommandFromApi", mode, requestID, command, commandContext.ID, Guid.NewGuid().ToString()); var success = await TaskHelper.DelayUntil(() => DeviceSocketHub.ApiScriptResults.TryGetValue(requestID, out _), TimeSpan.FromSeconds(30)); if (!success) { @@ -77,7 +85,7 @@ namespace Remotely.Server.API DeviceSocketHub.ApiScriptResults.TryGetValue(requestID, out var commandID); DeviceSocketHub.ApiScriptResults.Remove(requestID); DataService.DetachEntity(commandContext); - var result = DataService.GetCommandContext(commandID.ToString(), username); + var result = DataService.GetCommandContext(commandID.ToString(), orgID); return result; } } diff --git a/Server/API/ServerLogsController.cs b/Server/API/ServerLogsController.cs index fdd4da3d..a517c180 100644 --- a/Server/API/ServerLogsController.cs +++ b/Server/API/ServerLogsController.cs @@ -15,7 +15,6 @@ namespace Remotely.Server.API { [Route("api/[controller]")] [ApiController] - [Authorize] public class ServerLogsController : ControllerBase { @@ -25,11 +24,12 @@ namespace Remotely.Server.API } public DataService DataService { get; set; } - // GET: api/ServerLogs + [ServiceFilter(typeof(ApiAuthorizationFilter))] [HttpGet("Download")] public ActionResult Download() { - var logs = DataService.GetAllEventLogs(HttpContext.User.Identity.Name); + Request.Headers.TryGetValue("OrganizationID", out var orgID); + var logs = DataService.GetAllEventLogs(orgID); var fileBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(logs)); return File(fileBytes, "application/octet-stream", "ServerLogs.json"); } diff --git a/Server/Areas/Identity/Pages/Account/Manage/ApiTokens.cshtml b/Server/Areas/Identity/Pages/Account/Manage/ApiTokens.cshtml index 506f11e7..5c7eab3d 100644 --- a/Server/Areas/Identity/Pages/Account/Manage/ApiTokens.cshtml +++ b/Server/Areas/Identity/Pages/Account/Manage/ApiTokens.cshtml @@ -2,7 +2,7 @@ @model Remotely.Server.Areas.Identity.Pages.Account.Manage.ApiTokensModel @inject DataService DataService @{ - ViewData["Title"] = "API Tokens"; + ViewData["Title"] = "API Access Tokens"; }

@ViewData["Title"]

diff --git a/Server/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml b/Server/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml index 7a83f880..11b591a7 100644 --- a/Server/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml +++ b/Server/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml @@ -8,7 +8,7 @@ - + @if (hasExternalLogins) { diff --git a/Server/Data/ApplicationDbContext.cs b/Server/Data/ApplicationDbContext.cs index ba2342f0..14e0f647 100644 --- a/Server/Data/ApplicationDbContext.cs +++ b/Server/Data/ApplicationDbContext.cs @@ -104,6 +104,12 @@ namespace Remotely.Server.Data .HasConversion( x => JsonConvert.SerializeObject(x), x => JsonConvert.DeserializeObject>(x)); + + builder.Entity() + .HasIndex(x => x.DeviceName); + + builder.Entity() + .HasIndex(x => x.Token); } } } diff --git a/Server/Migrations/20200216015743_ApiToken and Device indexes.Designer.cs b/Server/Migrations/20200216015743_ApiToken and Device indexes.Designer.cs new file mode 100644 index 00000000..8c3b16f8 --- /dev/null +++ b/Server/Migrations/20200216015743_ApiToken and Device indexes.Designer.cs @@ -0,0 +1,620 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Remotely.Server.Data; + +namespace Remotely.Server.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20200216015743_ApiToken and Device indexes")] + partial class ApiTokenandDeviceindexes + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.1"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("RemotelyUsers"); + + b.HasDiscriminator("Discriminator").HasValue("IdentityUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasColumnType("TEXT") + .HasMaxLength(128); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT") + .HasMaxLength(128); + + b.Property("Name") + .HasColumnType("TEXT") + .HasMaxLength(128); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.ApiToken", b => + { + b.Property("ID") + .HasColumnType("TEXT"); + + b.Property("LastUsed") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT") + .HasMaxLength(200); + + b.Property("OrganizationID") + .HasColumnType("TEXT"); + + b.Property("Secret") + .HasColumnType("TEXT"); + + b.Property("Token") + .HasColumnType("TEXT"); + + b.HasKey("ID"); + + b.HasIndex("OrganizationID"); + + b.HasIndex("Token"); + + b.ToTable("ApiTokens"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.CommandContext", b => + { + b.Property("ID") + .HasColumnType("TEXT"); + + b.Property("CommandMode") + .HasColumnType("TEXT"); + + b.Property("CommandResults") + .HasColumnType("TEXT"); + + b.Property("CommandText") + .HasColumnType("TEXT"); + + b.Property("OrganizationID") + .HasColumnType("TEXT"); + + b.Property("PSCoreResults") + .HasColumnType("TEXT"); + + b.Property("SenderConnectionID") + .HasColumnType("TEXT"); + + b.Property("SenderUserID") + .HasColumnType("TEXT"); + + b.Property("TargetDeviceIDs") + .HasColumnType("TEXT"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("ID"); + + b.HasIndex("OrganizationID"); + + b.ToTable("CommandContexts"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.Device", b => + { + b.Property("ID") + .HasColumnType("TEXT"); + + b.Property("AgentVersion") + .HasColumnType("TEXT"); + + b.Property("Alias") + .HasColumnType("TEXT") + .HasMaxLength(100); + + b.Property("CurrentUser") + .HasColumnType("TEXT"); + + b.Property("DeviceGroupID") + .HasColumnType("TEXT"); + + b.Property("DeviceName") + .HasColumnType("TEXT"); + + b.Property("Drives") + .HasColumnType("TEXT"); + + b.Property("FreeMemory") + .HasColumnType("REAL"); + + b.Property("FreeStorage") + .HasColumnType("REAL"); + + b.Property("Is64Bit") + .HasColumnType("INTEGER"); + + b.Property("IsOnline") + .HasColumnType("INTEGER"); + + b.Property("LastOnline") + .HasColumnType("TEXT"); + + b.Property("OSArchitecture") + .HasColumnType("INTEGER"); + + b.Property("OSDescription") + .HasColumnType("TEXT"); + + b.Property("OrganizationID") + .HasColumnType("TEXT"); + + b.Property("Platform") + .HasColumnType("TEXT"); + + b.Property("ProcessorCount") + .HasColumnType("INTEGER"); + + b.Property("ServerVerificationToken") + .HasColumnType("TEXT"); + + b.Property("Tags") + .HasColumnType("TEXT") + .HasMaxLength(200); + + b.Property("TotalMemory") + .HasColumnType("REAL"); + + b.Property("TotalStorage") + .HasColumnType("REAL"); + + b.HasKey("ID"); + + b.HasIndex("DeviceGroupID"); + + b.HasIndex("DeviceName"); + + b.HasIndex("OrganizationID"); + + b.ToTable("Devices"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.DeviceGroup", b => + { + b.Property("ID") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT") + .HasMaxLength(200); + + b.Property("OrganizationID") + .HasColumnType("TEXT"); + + b.HasKey("ID"); + + b.HasIndex("OrganizationID"); + + b.ToTable("DeviceGroups"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.EventLog", b => + { + b.Property("ID") + .HasColumnType("TEXT"); + + b.Property("EventType") + .HasColumnType("INTEGER"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("OrganizationID") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("TEXT"); + + b.Property("StackTrace") + .HasColumnType("TEXT"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("ID"); + + b.HasIndex("OrganizationID"); + + b.ToTable("EventLogs"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.InviteLink", b => + { + b.Property("ID") + .HasColumnType("TEXT"); + + b.Property("DateSent") + .HasColumnType("TEXT"); + + b.Property("InvitedUser") + .HasColumnType("TEXT"); + + b.Property("IsAdmin") + .HasColumnType("INTEGER"); + + b.Property("OrganizationID") + .HasColumnType("TEXT"); + + b.Property("ResetUrl") + .HasColumnType("TEXT"); + + b.HasKey("ID"); + + b.HasIndex("OrganizationID"); + + b.ToTable("InviteLinks"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.Organization", b => + { + b.Property("ID") + .HasColumnType("TEXT"); + + b.Property("OrganizationName") + .HasColumnType("TEXT") + .HasMaxLength(25); + + b.HasKey("ID"); + + b.ToTable("Organizations"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.SharedFile", b => + { + b.Property("ID") + .HasColumnType("TEXT"); + + b.Property("ContentType") + .HasColumnType("TEXT"); + + b.Property("FileContents") + .HasColumnType("BLOB"); + + b.Property("FileName") + .HasColumnType("TEXT"); + + b.Property("OrganizationID") + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("ID"); + + b.HasIndex("OrganizationID"); + + b.ToTable("SharedFiles"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.RemotelyUser", b => + { + b.HasBaseType("Microsoft.AspNetCore.Identity.IdentityUser"); + + b.Property("IsAdministrator") + .HasColumnType("INTEGER"); + + b.Property("OrganizationID") + .HasColumnType("TEXT"); + + b.Property("UserOptions") + .HasColumnType("TEXT"); + + b.HasIndex("OrganizationID"); + + b.HasIndex("UserName"); + + b.HasDiscriminator().HasValue("RemotelyUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Remotely.Shared.Models.ApiToken", b => + { + b.HasOne("Remotely.Shared.Models.Organization", "Organization") + .WithMany("ApiTokens") + .HasForeignKey("OrganizationID"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.CommandContext", b => + { + b.HasOne("Remotely.Shared.Models.Organization", "Organization") + .WithMany("CommandContexts") + .HasForeignKey("OrganizationID"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.Device", b => + { + b.HasOne("Remotely.Shared.Models.DeviceGroup", "DeviceGroup") + .WithMany("Devices") + .HasForeignKey("DeviceGroupID"); + + b.HasOne("Remotely.Shared.Models.Organization", "Organization") + .WithMany("Devices") + .HasForeignKey("OrganizationID"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.DeviceGroup", b => + { + b.HasOne("Remotely.Shared.Models.Organization", "Organization") + .WithMany("DeviceGroups") + .HasForeignKey("OrganizationID"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.EventLog", b => + { + b.HasOne("Remotely.Shared.Models.Organization", "Organization") + .WithMany("EventLogs") + .HasForeignKey("OrganizationID"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.InviteLink", b => + { + b.HasOne("Remotely.Shared.Models.Organization", "Organization") + .WithMany("InviteLinks") + .HasForeignKey("OrganizationID"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.SharedFile", b => + { + b.HasOne("Remotely.Shared.Models.Organization", "Organization") + .WithMany("SharedFiles") + .HasForeignKey("OrganizationID"); + }); + + modelBuilder.Entity("Remotely.Shared.Models.RemotelyUser", b => + { + b.HasOne("Remotely.Shared.Models.Organization", "Organization") + .WithMany("RemotelyUsers") + .HasForeignKey("OrganizationID"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Server/Migrations/20200216015743_ApiToken and Device indexes.cs b/Server/Migrations/20200216015743_ApiToken and Device indexes.cs new file mode 100644 index 00000000..a56233aa --- /dev/null +++ b/Server/Migrations/20200216015743_ApiToken and Device indexes.cs @@ -0,0 +1,47 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Remotely.Server.Migrations +{ + public partial class ApiTokenandDeviceindexes : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "LastUsed", + table: "ApiTokens", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "TEXT"); + + migrationBuilder.CreateIndex( + name: "IX_Devices_DeviceName", + table: "Devices", + column: "DeviceName"); + + migrationBuilder.CreateIndex( + name: "IX_ApiTokens_Token", + table: "ApiTokens", + column: "Token"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Devices_DeviceName", + table: "Devices"); + + migrationBuilder.DropIndex( + name: "IX_ApiTokens_Token", + table: "ApiTokens"); + + migrationBuilder.AlterColumn( + name: "LastUsed", + table: "ApiTokens", + type: "TEXT", + nullable: false, + oldClrType: typeof(DateTime), + oldNullable: true); + } + } +} diff --git a/Server/Migrations/ApplicationDbContextModelSnapshot.cs b/Server/Migrations/ApplicationDbContextModelSnapshot.cs index fb985c26..4332ddf8 100644 --- a/Server/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Server/Migrations/ApplicationDbContextModelSnapshot.cs @@ -223,7 +223,7 @@ namespace Remotely.Server.Migrations b.Property("ID") .HasColumnType("TEXT"); - b.Property("LastUsed") + b.Property("LastUsed") .HasColumnType("TEXT"); b.Property("Name") @@ -243,6 +243,8 @@ namespace Remotely.Server.Migrations b.HasIndex("OrganizationID"); + b.HasIndex("Token"); + b.ToTable("ApiTokens"); }); @@ -356,6 +358,8 @@ namespace Remotely.Server.Migrations b.HasIndex("DeviceGroupID"); + b.HasIndex("DeviceName"); + b.HasIndex("OrganizationID"); b.ToTable("Devices"); diff --git a/Server/Pages/EditDevice.cshtml.cs b/Server/Pages/EditDevice.cshtml.cs index 128395b5..7a71cdd9 100644 --- a/Server/Pages/EditDevice.cshtml.cs +++ b/Server/Pages/EditDevice.cshtml.cs @@ -55,7 +55,7 @@ namespace Remotely.Server.Pages var user = DataService.GetUserByName(User.Identity.Name); if (user != null) { - var device = DataService.GetDeviceForUser(user.Id, deviceID); + var device = DataService.GetDevice(user.OrganizationID, deviceID); DeviceName = device?.DeviceName; AgentVersion = device.AgentVersion; Input.Alias = device?.Alias; diff --git a/Server/Server.csproj b/Server/Server.csproj index 4f0a7db4..3c47e997 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -19,6 +19,11 @@ + + + + + diff --git a/Server/Services/ApiAuthorizationFilter.cs b/Server/Services/ApiAuthorizationFilter.cs new file mode 100644 index 00000000..8f75df9c --- /dev/null +++ b/Server/Services/ApiAuthorizationFilter.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Remotely.Server.Services +{ + public class ApiAuthorizationFilter : ActionFilterAttribute, IAuthorizationFilter + { + public ApiAuthorizationFilter(DataService dataService) + { + DataService = dataService; + } + + private DataService DataService { get; } + + public void OnAuthorization(AuthorizationFilterContext context) + { + + if (context.HttpContext.User.Identity.IsAuthenticated) + { + var orgID = DataService.GetUserByName(context.HttpContext.User.Identity.Name)?.OrganizationID; + context.HttpContext.Request.Headers.Add("OrganizationID", orgID); + return; + } + + if (context.HttpContext.Request.Headers.TryGetValue("Authorization", out var result)) + { + var apiToken = result.ToString().Split(":")[0]; + var apiSecret = result.ToString().Split(":")[1]; + if (DataService.ValidateApiToken(apiToken, apiSecret)) + { + var orgID = DataService.GetApiToken(apiToken)?.OrganizationID; + context.HttpContext.Request.Headers.Add("OrganizationID", orgID); + return; + } + } + + context.Result = new UnauthorizedResult(); + } + } +} diff --git a/Server/Services/DataService.cs b/Server/Services/DataService.cs index 44b743e2..3889e65a 100644 --- a/Server/Services/DataService.cs +++ b/Server/Services/DataService.cs @@ -27,31 +27,47 @@ namespace Remotely.Server.Services private IHostEnvironment HostEnvironment { get; } private ApplicationDbContext RemotelyContext { get; } - public bool AddDeviceGroup(string userName, DeviceGroup deviceGroup, out string deviceGroupID, out string errorMessage) + public bool AddDeviceGroup(string orgID, DeviceGroup deviceGroup, out string deviceGroupID, out string errorMessage) { deviceGroupID = null; errorMessage = null; - var organization = RemotelyContext.Users - .Include(x => x.Organization) - .ThenInclude(x => x.DeviceGroups) - .FirstOrDefault(x => x.UserName == userName) - .Organization; - if (organization.DeviceGroups.Any(x => x.Name.ToLower() == deviceGroup.Name.ToLower())) + + var organization = RemotelyContext.Organizations + .Include(x => x.DeviceGroups) + .FirstOrDefault(x => x.ID == orgID); + + if (RemotelyContext.DeviceGroups.Any(x => + x.OrganizationID == orgID && + x.Name.ToLower() == deviceGroup.Name.ToLower())) { errorMessage = "Device group already exists."; return false; } + var newDeviceGroup = new DeviceGroup() { Name = deviceGroup.Name, - Organization = organization + Organization = organization, + OrganizationID = orgID }; + organization.DeviceGroups.Add(newDeviceGroup); RemotelyContext.SaveChanges(); deviceGroupID = newDeviceGroup.ID; return true; } + public ApiToken GetApiToken(string apiToken) + { + return RemotelyContext.ApiTokens.FirstOrDefault(x => x.Token == apiToken); + } + + public bool ValidateApiToken(string apiToken, string apiSecret) + { + var token = RemotelyContext.ApiTokens.FirstOrDefault(x => x.Token == apiToken); + return token?.Secret == apiSecret; + } + public async Task RenameApiToken(string userName, string tokenId, string tokenName) { var user = RemotelyContext.Users.FirstOrDefault(x => x.UserName == userName); @@ -89,14 +105,13 @@ namespace Remotely.Server.Services await RemotelyContext.SaveChangesAsync(); } - public InviteLink AddInvite(string requesterUserName, Invite invite) + public InviteLink AddInvite(string orgID, Invite invite) { invite.InvitedUser = invite.InvitedUser.ToLower(); - var requester = RemotelyContext.Users.FirstOrDefault(x => x.UserName == requesterUserName); var organization = RemotelyContext.Organizations .Include(x => x.InviteLinks) - .FirstOrDefault(x => x.ID == requester.OrganizationID); + .FirstOrDefault(x => x.ID == orgID); var newInvite = new InviteLink() { @@ -205,15 +220,17 @@ namespace Remotely.Server.Services return newEntity.Entity.ID; } - public void ChangeUserIsAdmin(string requesterUserName, string targetUserID, bool isAdmin) + public void ChangeUserIsAdmin(string organizationID, string targetUserID, bool isAdmin) { - var requester = RemotelyContext.Users - .Include(x => x.Organization) - .ThenInclude(x => x.RemotelyUsers) - .FirstOrDefault(x => x.UserName == requesterUserName); + var targetUser = RemotelyContext.Users.FirstOrDefault(x => + x.OrganizationID == organizationID && + x.Id == targetUserID); - requester.Organization.RemotelyUsers.FirstOrDefault(x => x.Id == targetUserID).IsAdministrator = isAdmin; - RemotelyContext.SaveChanges(); + if (targetUser != null) + { + targetUser.IsAdministrator = isAdmin; + RemotelyContext.SaveChanges(); + } } public void CleanupOldRecords() @@ -242,32 +259,29 @@ namespace Remotely.Server.Services } } - public void DeleteDeviceGroup(string userName, string deviceGroupId) + public void DeleteDeviceGroup(string orgID, string deviceGroupId) { - var organization = RemotelyContext.Users - .Include(x => x.Organization) - .ThenInclude(x => x.DeviceGroups) - .ThenInclude(x => x.Devices) - .FirstOrDefault(x => x.UserName == userName) - .Organization; + var deviceGroup = RemotelyContext.DeviceGroups.FirstOrDefault(x => + x.ID == deviceGroupId && + x.OrganizationID == orgID); - var deviceGroup = organization.DeviceGroups.FirstOrDefault(x => x.ID == deviceGroupId); deviceGroup.Devices.ForEach(x => { x.DeviceGroup = null; }); + RemotelyContext.DeviceGroups.Remove(deviceGroup); RemotelyContext.SaveChanges(); } - public void DeleteInvite(string requesterUserName, string inviteID) + public void DeleteInvite(string orgID, string inviteID) { - var requester = RemotelyContext.Users - .Include(x => x.Organization) - .ThenInclude(x => x.InviteLinks) - .FirstOrDefault(x => x.UserName == requesterUserName); - var invite = requester.Organization.InviteLinks.FirstOrDefault(x => x.ID == inviteID); + var invite = RemotelyContext.InviteLinks.FirstOrDefault(x => + x.OrganizationID == orgID && + x.ID == inviteID); + var user = RemotelyContext.Users.FirstOrDefault(x => x.UserName == invite.InvitedUser); + if (user != null && string.IsNullOrWhiteSpace(user.PasswordHash)) { RemotelyContext.Remove(user); @@ -324,12 +338,8 @@ namespace Remotely.Server.Services .ToArray(); } - public IEnumerable GetAllCommandContexts(string userName) + public IEnumerable GetAllCommandContexts(string orgID) { - var orgID = RemotelyContext.Users - .FirstOrDefault(x => x.UserName == userName) - ?.OrganizationID; - return RemotelyContext.CommandContexts .Where(x => x.OrganizationID == orgID) .OrderByDescending(x => x.TimeStamp); @@ -345,19 +355,19 @@ namespace Remotely.Server.Services } - public IEnumerable GetAllDevicesForUser(string userID) + public IEnumerable GetAllDevices(string orgID) { - var user = RemotelyContext.Users.FirstOrDefault(x => x.Id == userID); - - return RemotelyContext.Devices.Where(x => x.OrganizationID == user.OrganizationID); + return RemotelyContext.Devices.Where(x => x.OrganizationID == orgID); } - public IEnumerable GetAllEventLogs(string userName) + public IEnumerable GetDevicesForUser(string userName) + { + // TODO: Add authorization groups. + var user = RemotelyContext.Users.FirstOrDefault(x => x.UserName == userName); + return RemotelyContext.Devices.Where(x => x.OrganizationID == user.OrganizationID); + } + public IEnumerable GetAllEventLogs(string orgID) { - var orgID = RemotelyContext.Users - .FirstOrDefault(x => x.UserName == userName) - ?.OrganizationID; - return RemotelyContext.EventLogs .Where(x => x.OrganizationID == orgID) .OrderByDescending(x => x.TimeStamp); @@ -391,13 +401,11 @@ namespace Remotely.Server.Services return RemotelyContext.Users.Where(x => x.OrganizationID == user.OrganizationID); } - public CommandContext GetCommandContext(string commandContextID, string userName) + public CommandContext GetCommandContext(string commandContextID, string orgID) { - var user = GetUserByName(userName); return RemotelyContext.CommandContexts .FirstOrDefault(x => - (user.IsAdministrator || x.SenderUserID == user.Id) && - x.OrganizationID == user.OrganizationID && + x.OrganizationID == orgID && x.ID == commandContextID); } @@ -422,12 +430,10 @@ namespace Remotely.Server.Services return RemotelyContext.Devices.Count(); } - public Device GetDeviceForUser(string userID, string deviceID) + public Device GetDevice(string orgID, string deviceID) { - var user = RemotelyContext.Users.FirstOrDefault(x => x.Id == userID); - return RemotelyContext.Devices.FirstOrDefault(x => - x.OrganizationID == user.OrganizationID && + x.OrganizationID == orgID && x.ID == deviceID); } @@ -521,13 +527,11 @@ namespace Remotely.Server.Services RemotelyContext.SaveChanges(); } - public void RemoveUserFromOrganization(string requesterUserName, string targetUserID) + public void RemoveUserFromOrganization(string orgID, string targetUserID) { - var requester = RemotelyContext.Users - .Include(x => x.Organization) - .ThenInclude(x => x.RemotelyUsers) - .FirstOrDefault(x => x.UserName == requesterUserName); - var target = requester.Organization.RemotelyUsers.FirstOrDefault(x => x.Id == targetUserID); + var target = RemotelyContext.Users.FirstOrDefault(x => + x.Id == targetUserID && + x.OrganizationID == orgID); var newOrganization = new Organization(); target.Organization = newOrganization; @@ -589,12 +593,10 @@ namespace Remotely.Server.Services RemotelyContext.SaveChanges(); } - public void UpdateOrganizationName(string userName, string organizationName) + public void UpdateOrganizationName(string orgID, string organizationName) { - RemotelyContext.Users - .Include(x => x.Organization) - .FirstOrDefault(x => x.UserName == userName) - .Organization + RemotelyContext.Organizations + .FirstOrDefault(x => x.ID == orgID) .OrganizationName = organizationName; RemotelyContext.SaveChanges(); } diff --git a/Server/Startup.cs b/Server/Startup.cs index c2e3ae46..a599ca60 100644 --- a/Server/Startup.cs +++ b/Server/Startup.cs @@ -157,6 +157,7 @@ namespace Remotely.Server services.AddScoped(); services.AddSingleton(); services.AddSingleton(); + services.AddScoped(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.