diff --git a/README.md b/README.md index 5ff165c1..55661912 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,9 @@ The following steps will configure your Windows 10 machine for building the Remo * Download and install the .NET Core Runtime (not the SDK). * Link: https://dotnet.microsoft.com/download * This includes the Hosting Bundle for Windows, which allows you to run ASP.NET Core in IIS. - * Important: If you installed .NET Core Runtime before installing all the required IIS features (which is done in the script), you may need to run a repair on the .NET Core Runtime installation. + * Important: If you installed .NET Core Runtime before installing all the required IIS features, you may need to run a repair on the .NET Core Runtime installation. * Change values in appsettings.json for your environment. * If using SQLite configuration, make sure the ApplicationPool's account has write access to the DB file location. - * The script will do this for you, assuming all default settings. -* After creating your account on the website, you can set "AllowSelfRegistration" to false in appsettings.json to disable registration. * If the site will be public-facing, configure your bindings in IIS. * An SSL certificate for HTTPS is recommended. You can install one for free using Let's Encrypt. * Resources: https://letsencrypt.org/, https://certifytheweb.com/ @@ -100,7 +98,8 @@ Note: To retain your settings between upgrades, copy your settings to appsetting * DefaultPrompt: The default prompt string you'll see for each line on the console. * DBProvider: Determines which of the three connection strings (at the top) will be used. The appropriate DB provider for the database type is automatically loaded in code. -* AllowSelfRegistration: Enable/disable the ability for people to create accounts. +* MaxOrganizationCount: By default, one organization can exist on the server, which is created automatically when the first account is registered. Afterward, self-registration will be disabled. + * Set this to -1 or increase it to a specific number to allow multi-tenancy. * RecordRemoteControlSessions: Whether or not to record remote control sessions. * RedirectToHTTPS: Whether ASP.NET Core will redirect all traffic from HTTP to HTTPS. This is independent of Nginx and IIS configurations that do the same. * UseHSTS: Whether ASP.NET Core will use HTTP Strict Transport Security. diff --git a/Server/Areas/Identity/Pages/Account/Register.cshtml b/Server/Areas/Identity/Pages/Account/Register.cshtml index 4f48e90e..390528b8 100644 --- a/Server/Areas/Identity/Pages/Account/Register.cshtml +++ b/Server/Areas/Identity/Pages/Account/Register.cshtml @@ -1,14 +1,16 @@ @page @inject Remotely.Server.Services.ApplicationConfig AppConfig +@inject Remotely.Server.Data.DataService DataService @model RegisterModel @{ ViewData["Title"] = "Register"; + var organizationCount = DataService.GetOrganizationCount(); }

@ViewData["Title"]

- @if (AppConfig.AllowSelfRegistration) + @if (AppConfig.MaxOrganizationCount < 0 || organizationCount < AppConfig.MaxOrganizationCount) {
@@ -37,7 +39,7 @@ else {
-
Self-registration is disabled.
+
Registration is disabled.
} diff --git a/Server/Areas/Identity/Pages/Account/Register.cshtml.cs b/Server/Areas/Identity/Pages/Account/Register.cshtml.cs index bc0b1db7..0e1629af 100644 --- a/Server/Areas/Identity/Pages/Account/Register.cshtml.cs +++ b/Server/Areas/Identity/Pages/Account/Register.cshtml.cs @@ -13,6 +13,8 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Logging; +using Remotely.Server.Data; +using Remotely.Server.Services; using Remotely.Shared.Models; namespace Remotely.Server.Areas.Identity.Pages.Account @@ -24,17 +26,23 @@ namespace Remotely.Server.Areas.Identity.Pages.Account private readonly UserManager _userManager; private readonly ILogger _logger; private readonly IEmailSender _emailSender; + private readonly DataService _dataService; + private readonly ApplicationConfig _appConfig; public RegisterModel( UserManager userManager, SignInManager signInManager, ILogger logger, - IEmailSender emailSender) + IEmailSender emailSender, + DataService dataService, + ApplicationConfig appConfig) { _userManager = userManager; _signInManager = signInManager; _logger = logger; _emailSender = emailSender; + _dataService = dataService; + _appConfig = appConfig; } [BindProperty] @@ -71,6 +79,11 @@ namespace Remotely.Server.Areas.Identity.Pages.Account public async Task OnPostAsync(string returnUrl = null) { + var organizationCount = _dataService.GetOrganizationCount(); + if (_appConfig.MaxOrganizationCount > 0 && organizationCount >= _appConfig.MaxOrganizationCount) + { + return NotFound(); + } returnUrl = returnUrl ?? Url.Content("~/"); ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); if (ModelState.IsValid) diff --git a/Server/Data/DataService.cs b/Server/Data/DataService.cs index 985c6f5c..84a86914 100644 --- a/Server/Data/DataService.cs +++ b/Server/Data/DataService.cs @@ -385,6 +385,11 @@ namespace Remotely.Server.Data .RemotelyUsers; } + internal int GetOrganizationCount() + { + return RemotelyContext.Organizations.Count(); + } + internal string GetOrganizationName(string userName) { return RemotelyContext.Users diff --git a/Server/Services/ApplicationConfig.cs b/Server/Services/ApplicationConfig.cs index 85fa2000..b103c673 100644 --- a/Server/Services/ApplicationConfig.cs +++ b/Server/Services/ApplicationConfig.cs @@ -15,12 +15,12 @@ namespace Remotely.Server.Services Config = config; } public bool AllowApiLogin => bool.Parse(Config["ApplicationOptions:AllowApiLogin"]); - public bool AllowSelfRegistration => bool.Parse(Config["ApplicationOptions:AllowSelfRegistration"]); public double DataRetentionInDays => double.Parse(Config["ApplicationOptions:DataRetentionInDays"]); public string DBProvider => Config["ApplicationOptions:DBProvider"]; public string DefaultPrompt => Config["ApplicationOptions:DefaultPrompt"]; public bool EnableWindowsEventLog => bool.Parse(Config["ApplicationOptions:EnableWindowsEventLog"]); public string[] KnownProxies => Config.GetSection("ApplicationOptions:KnownProxies").Get(); + public int MaxOrganizationCount => int.Parse(Config["ApplicationOptions:MaxOrganizationCount"]); public bool RecordRemoteControlSessions => bool.Parse(Config["ApplicationOptions:RecordRemoteControlSessions"]); public bool RedirectToHTTPS => bool.Parse(Config["ApplicationOptions:RedirectToHTTPS"]); public bool RemoteControlRequiresAuthentication => bool.Parse(Config["ApplicationOptions:RemoteControlRequiresAuthentication"]); diff --git a/Server/appsettings.json b/Server/appsettings.json index 28ce303d..a4ad3630 100644 --- a/Server/appsettings.json +++ b/Server/appsettings.json @@ -11,7 +11,7 @@ }, "ApplicationOptions": { "AllowApiLogin": false, - "AllowSelfRegistration": true, + "MaxOrganizationCount": 1, "DataRetentionInDays": 90, "DBProvider": "SQLite", "DefaultPrompt": "~>",