public void UpdateShellSettings(ShellSettings settings) { ShellContext context; _shellSettingsManager.SaveSettings(settings); _runningShellTable.Update(settings); _shellContexts.TryRemove(settings.Name, out context); context = CreateShellContext(settings); ActivateShell(context); }
public void SingleSettingsFileShouldComeBackAsExpected() { ShellSettingsManager.SaveSettings(new ShellSettings { Name = "Default", DataProvider = "SQLCe", DataConnectionString = "something else" }); var settings = ShellSettingsManager.LoadSettings().Single(); Assert.That(settings, Is.Not.Null); Assert.That(settings.Name, Is.EqualTo("Default")); Assert.That(settings.DataProvider, Is.EqualTo("SQLCe")); Assert.That(settings.DataConnectionString, Is.EqualTo("something else")); }
public void UpdateShellSettings(ShellSettings settings) { ShellContext context; _shellSettingsManager.SaveSettings(settings); _runningShellTable.Remove(settings); if (_shellContexts.TryRemove(settings.Name, out context)) { context.Dispose(); } GetOrCreateShellContext(settings); }
public string Setup(SetupContext context) { string executionId = Guid.NewGuid().ToString(); // The vanilla Orchard distibution has the following features enabled. string[] hardcoded = { // Framework "Orchard.Hosting", // Core "Settings", // Test Modules "Orchard.Demo", "Orchard.Test1" }; context.EnabledFeatures = hardcoded.Union(context.EnabledFeatures ?? Enumerable.Empty <string>()).Distinct().ToList(); var shellSettings = new ShellSettings(); shellSettings.Name = context.SiteName; //if (shellSettings.DataProviders.Any()) { // DataProvider provider = new DataProvider(); //shellSettings.DataProvider = context.DatabaseProvider; //shellSettings.DataConnectionString = context.DatabaseConnectionString; //shellSettings.DataTablePrefix = context.DatabaseTablePrefix; //} // TODO: Add Encryption Settings in var shellDescriptor = new ShellDescriptor { Features = context.EnabledFeatures.Select(name => new ShellFeature { Name = name }) }; // creating a standalone environment. // in theory this environment can be used to resolve any normal components by interface, and those // components will exist entirely in isolation - no crossover between the safemode container currently in effect // must mark state as Running - otherwise standalone enviro is created "for setup" shellSettings.State = TenantState.Running; // TODO: Remove and mirror Orchard Setup shellSettings.RequestUrlHost = _httpContextAccessor.HttpContext.Request.Host.Value; shellSettings.RequestUrlPrefix = string.Empty; //shellSettings.DataProvider = "InMemory"; _shellSettingsManager.SaveSettings(shellSettings); return(executionId); }
public async Task <IActionResult> Create(EditTenantViewModel model) { if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTenants)) { return(Unauthorized()); } if (!IsDefaultShell()) { return(Unauthorized()); } if (ModelState.IsValid) { ValidateViewModel(model, true); } if (ModelState.IsValid) { // Creates a default shell settings based on the configuration. var shellSettings = _shellSettingsManager.CreateDefaultSettings(); shellSettings.Name = model.Name; shellSettings.RequestUrlHost = model.RequestUrlHost; shellSettings.RequestUrlPrefix = model.RequestUrlPrefix; shellSettings.State = TenantState.Uninitialized; shellSettings["ConnectionString"] = model.ConnectionString; shellSettings["TablePrefix"] = model.TablePrefix; shellSettings["DatabaseProvider"] = model.DatabaseProvider; shellSettings["Secret"] = Guid.NewGuid().ToString(); shellSettings["RecipeName"] = model.RecipeName; _shellSettingsManager.SaveSettings(shellSettings); var shellContext = await _shellHost.GetOrCreateShellContextAsync(shellSettings); return(RedirectToAction(nameof(Index))); } var recipeCollections = await Task.WhenAll(_recipeHarvesters.Select(x => x.HarvestRecipesAsync())); var recipes = recipeCollections.SelectMany(x => x).Where(x => x.IsSetupRecipe).ToArray(); model.Recipes = recipes; // If we got this far, something failed, redisplay form return(View(model)); }
public async Task <IActionResult> Create(EditTenantViewModel model) { if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTenants)) { return(Unauthorized()); } if (!IsDefaultShell()) { return(Unauthorized()); } if (ModelState.IsValid) { await ValidateViewModel(model, true); } if (ModelState.IsValid) { var shellSettings = new ShellSettings { Name = model.Name, RequestUrlPrefix = model.RequestUrlPrefix?.Trim(), RequestUrlHost = model.RequestUrlHost, ConnectionString = model.ConnectionString, TablePrefix = model.TablePrefix, DatabaseProvider = model.DatabaseProvider, State = TenantState.Uninitialized, Secret = Guid.NewGuid().ToString(), RecipeName = model.RecipeName }; _shellSettingsManager.SaveSettings(shellSettings); var shellContext = await _orchardHost.GetOrCreateShellContextAsync(shellSettings); return(RedirectToAction(nameof(Index))); } var recipeCollections = await Task.WhenAll(_recipeHarvesters.Select(x => x.HarvestRecipesAsync())); var recipes = recipeCollections.SelectMany(x => x).Where(x => x.IsSetupRecipe).ToArray(); model.Recipes = recipes; // If we got this far, something failed, redisplay form return(View(model)); }
public async Task <IActionResult> Create(EditTenantViewModel model) { if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTenants)) { return(Unauthorized()); } if (!IsDefaultShell()) { return(Unauthorized()); } if (ModelState.IsValid) { ValidateViewModel(model, true); } if (ModelState.IsValid) { var shellSettings = new ShellSettings { Name = model.Name, RequestUrlPrefix = model.RequestUrlPrefix, RequestUrlHost = model.RequestUrlHost, ConnectionString = model.ConnectionString, TablePrefix = model.TablePrefix, DatabaseProvider = model.DatabaseProvider, State = TenantState.Uninitialized }; _shellSettingsManager.SaveSettings(shellSettings); var shellContext = _orchardHost.GetOrCreateShellContext(shellSettings); return(RedirectToAction(nameof(Index))); } // If we got this far, something failed, redisplay form return(View(model)); }
public async Task <IActionResult> IndexPost(RegisterUserViewModel viewModel) { if (ModelState.IsValid) { var shellSettings = new ShellSettings { Name = viewModel.Handle, RequestUrlPrefix = viewModel.Handle, RequestUrlHost = "", // This should be a setting in the SaaS module. ConnectionString = "", TablePrefix = "", DatabaseProvider = "Sqlite", State = TenantState.Uninitialized, Secret = Guid.NewGuid().ToString(), RecipeName = "Blog" }; _shellSettingsManager.SaveSettings(shellSettings); var confirmationLink = Url.Action(nameof(HomeController.Confirm), "Home", new { email = viewModel.Email, handle = viewModel.Handle, siteName = viewModel.SiteName }, Request.Scheme); var message = new MailMessage { From = new MailAddress("*****@*****.**", "Orchard SaaS"), IsBodyHtml = true, Body = $"Click <a href=\"{HttpUtility.HtmlEncode(confirmationLink)}\">this link</a>" }; message.To.Add(viewModel.Email); await _smtpService.SendAsync(message); return(RedirectToAction(nameof(Success))); } return(View(nameof(Index), viewModel)); }
public string Setup(SetupContext context) { string executionId; Logger.Information("Running setup for tenant '{0}'.", _shellSettings.Name); // The vanilla Orchard distibution has the following features enabled. string[] hardcoded = { // Framework "Orchard.Framework", // Core "Common", "Containers", "Contents", "Dashboard", "Feeds", "Navigation", "Scheduling", "Settings", "Shapes", "Title", // Modules "Orchard.Pages", "Orchard.ContentPicker", "Orchard.Themes", "Orchard.Users", "Orchard.Roles", "Orchard.Modules", "PackagingServices","Orchard.Packaging", "Gallery", "Orchard.Recipes" }; context.EnabledFeatures = hardcoded.Union(context.EnabledFeatures ?? Enumerable.Empty <string>()).Distinct().ToList(); var shellSettings = new ShellSettings(_shellSettings); if (String.IsNullOrEmpty(shellSettings.DataProvider)) { shellSettings.DataProvider = context.DatabaseProvider; shellSettings.DataConnectionString = context.DatabaseConnectionString; shellSettings.DataTablePrefix = context.DatabaseTablePrefix; } shellSettings.EncryptionAlgorithm = "AES"; shellSettings.EncryptionKey = SymmetricAlgorithm.Create(shellSettings.EncryptionAlgorithm).Key.ToHexString(); shellSettings.HashAlgorithm = "HMACSHA256"; shellSettings.HashKey = HMAC.Create(shellSettings.HashAlgorithm).Key.ToHexString(); var shellDescriptor = new ShellDescriptor { Features = context.EnabledFeatures.Select(name => new ShellFeature { Name = name }) }; var shellBlueprint = _compositionStrategy.Compose(shellSettings, shellDescriptor); // Initialize database explicitly, and store shell descriptor. using (var bootstrapLifetimeScope = _shellContainerFactory.CreateContainer(shellSettings, shellBlueprint)) { using (var environment = bootstrapLifetimeScope.CreateWorkContextScope()) { // Workaround to avoid a Transaction issue with PostgreSQL. environment.Resolve <ITransactionManager>().RequireNew(); var schemaBuilder = new SchemaBuilder(environment.Resolve <IDataMigrationInterpreter>()); schemaBuilder.CreateTable("Orchard_Framework_DataMigrationRecord", table => table .Column <int>("Id", column => column.PrimaryKey().Identity()) .Column <string>("DataMigrationClass") .Column <int>("Version")); schemaBuilder.AlterTable("Orchard_Framework_DataMigrationRecord", table => table.AddUniqueConstraint("UC_DMR_DataMigrationClass_Version", "DataMigrationClass", "Version")); var dataMigrationManager = environment.Resolve <IDataMigrationManager>(); dataMigrationManager.Update("Settings"); foreach (var feature in context.EnabledFeatures) { dataMigrationManager.Update(feature); } var descriptorManager = environment.Resolve <IShellDescriptorManager>(); descriptorManager.UpdateShellDescriptor(0, shellDescriptor.Features, shellDescriptor.Parameters); } } // In effect "pump messages" see PostMessage circa 1980. while (_processingEngine.AreTasksPending()) { _processingEngine.ExecuteNextTask(); } // Create a standalone environment. // Must mark state as Running - otherwise standalone environment is created "for setup". shellSettings.State = TenantState.Running; using (var environment = _orchardHost.CreateStandaloneEnvironment(shellSettings)) { try { executionId = CreateTenantData(context, environment); } catch { environment.Resolve <ITransactionManager>().Cancel(); throw; } } _shellSettingsManager.SaveSettings(shellSettings); return(executionId); }
public async Task <IActionResult> IndexPost(RegisterUserViewModel model) { if (!model.AcceptTerms) { ModelState.AddModelError(nameof(RegisterUserViewModel.AcceptTerms), S["Please, accept the terms and conditions."]); } if (!string.IsNullOrEmpty(model.Handle) && !Regex.IsMatch(model.Handle, @"^\w+$")) { ModelState.AddModelError(nameof(RegisterUserViewModel.Handle), S["Invalid tenant name. Must contain characters only and no spaces."]); } if (ModelState.IsValid) { if (_shellHost.TryGetSettings(model.Handle, out var shellSettings)) { ModelState.AddModelError(nameof(RegisterUserViewModel.Handle), S["This site name already exists."]); } else { shellSettings = new ShellSettings { Name = model.Handle, RequestUrlPrefix = model.Handle.ToLower(), RequestUrlHost = null, State = TenantState.Uninitialized }; shellSettings["RecipeName"] = model.RecipeName; shellSettings["DatabaseProvider"] = "Sqlite"; _shellSettingsManager.SaveSettings(shellSettings); var shellContext = await _shellHost.GetOrCreateShellContextAsync(shellSettings); var recipes = await _setupService.GetSetupRecipesAsync(); var recipe = recipes.FirstOrDefault(x => x.Name == model.RecipeName); if (recipe == null) { ModelState.AddModelError(nameof(RegisterUserViewModel.RecipeName), S["Invalid recipe name."]); } var adminName = defaultAdminName; var adminPassword = GenerateRandomPassword(); var siteName = model.SiteName; var siteUrl = GetTenantUrl(shellSettings); var dataProtector = _dataProtectionProvider.CreateProtector(dataProtectionPurpose).ToTimeLimitedDataProtector(); var encryptedPassword = dataProtector.Protect(adminPassword, _clock.UtcNow.Add(new TimeSpan(24, 0, 0))); var confirmationLink = Url.Action(nameof(Confirm), "Home", new { email = model.Email, handle = model.Handle, siteName = model.SiteName, ep = encryptedPassword }, Request.Scheme); var message = new MailMessage(); if (emailToBcc) { message.Bcc.Add(new MailAddress(_smtpSettingsOptions.Value.DefaultSender)); } message.To.Add(model.Email); message.IsBodyHtml = true; message.Subject = emailSubject; message.Body = S[$"Hello,<br><br>Your demo site '{siteName}' has been created.<br><br>1) Setup your site by opening <a href=\"{confirmationLink}\">this link</a>.<br><br>2) Log into the <a href=\"{siteUrl}/admin\">admin</a> with these credentials:<br>Username: {adminName}<br>Password: {adminPassword}"]; await _smtpService.SendAsync(message); return(RedirectToAction(nameof(Success))); } } return(View(nameof(Index), model)); }
public void UpdateShellSettings(ShellSettings settings) { _shellSettingsManager.SaveSettings(settings); ReloadShellContext(settings); }
public async Task <IActionResult> Create(CreateApiViewModel model) { if (!IsDefaultShell()) { return(Unauthorized()); } if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTenants)) { return(Unauthorized()); } if (!string.IsNullOrEmpty(model.Name) && !Regex.IsMatch(model.Name, @"^\w+$")) { ModelState.AddModelError(nameof(CreateApiViewModel.Name), S["Invalid tenant name. Must contain characters only and no spaces."]); } // Creates a default shell settings based on the configuration. var shellSettings = _shellSettingsManager.CreateDefaultSettings(); shellSettings.Name = model.Name; shellSettings.RequestUrlHost = model.RequestUrlHost; shellSettings.RequestUrlPrefix = model.RequestUrlPrefix; shellSettings.State = TenantState.Uninitialized; shellSettings["ConnectionString"] = model.ConnectionString; shellSettings["TablePrefix"] = model.TablePrefix; shellSettings["DatabaseProvider"] = model.DatabaseProvider; shellSettings["Secret"] = Guid.NewGuid().ToString(); shellSettings["RecipeName"] = model.RecipeName; if (!IsDefaultShell() && string.IsNullOrWhiteSpace(shellSettings.RequestUrlHost) && string.IsNullOrWhiteSpace(shellSettings.RequestUrlPrefix)) { ModelState.AddModelError(nameof(CreateApiViewModel.RequestUrlPrefix), S["Host and url prefix can not be empty at the same time."]); } if (!string.IsNullOrWhiteSpace(shellSettings.RequestUrlPrefix)) { if (shellSettings.RequestUrlPrefix.Contains('/')) { ModelState.AddModelError(nameof(CreateApiViewModel.RequestUrlPrefix), S["The url prefix can not contain more than one segment."]); } } if (ModelState.IsValid) { if (_shellHost.TryGetSettings(model.Name, out var settings)) { // Site already exists, return 200 for indempotency purpose var token = CreateSetupToken(settings); return(StatusCode(201, GetTenantUrl(settings, token))); } else { _shellSettingsManager.SaveSettings(shellSettings); var shellContext = await _shellHost.GetOrCreateShellContextAsync(shellSettings); var token = CreateSetupToken(shellSettings); return(Ok(GetTenantUrl(shellSettings, token))); } } return(BadRequest(ModelState)); }
public Task UpdateShellSettingsAsync(ShellSettings settings) { _shellSettingsManager.SaveSettings(settings); return(ReloadShellContextAsync(settings)); }
public string Setup(SetupContext context) { string executionId; // The vanilla Orchard distibution has the following features enabled. string[] hardcoded = { // Framework "Orchard.Framework", // Core "Common", "Containers", "Contents", "Dashboard", "Feeds", "Navigation", "Reports", "Scheduling", "Settings", "Shapes", "Title", // Modules "Orchard.Pages", "Orchard.ContentPicker", "Orchard.Themes", "Orchard.Users", "Orchard.Roles", "Orchard.Modules", "PackagingServices","Orchard.Packaging", "Gallery", "Orchard.Recipes" }; context.EnabledFeatures = hardcoded.Union(context.EnabledFeatures ?? Enumerable.Empty <string>()).Distinct().ToList(); var shellSettings = new ShellSettings(_shellSettings); if (string.IsNullOrEmpty(shellSettings.DataProvider)) { shellSettings.DataProvider = context.DatabaseProvider; shellSettings.DataConnectionString = context.DatabaseConnectionString; shellSettings.DataTablePrefix = context.DatabaseTablePrefix; } #region Encryption Settings shellSettings.EncryptionAlgorithm = "AES"; // randomly generated key shellSettings.EncryptionKey = SymmetricAlgorithm.Create(shellSettings.EncryptionAlgorithm).Key.ToHexString(); shellSettings.HashAlgorithm = "HMACSHA256"; // randomly generated key shellSettings.HashKey = HMAC.Create(shellSettings.HashAlgorithm).Key.ToHexString(); #endregion var shellDescriptor = new ShellDescriptor { Features = context.EnabledFeatures.Select(name => new ShellFeature { Name = name }) }; var shellBlueprint = _compositionStrategy.Compose(shellSettings, shellDescriptor); // initialize database explicitly, and store shell descriptor using (var bootstrapLifetimeScope = _shellContainerFactory.CreateContainer(shellSettings, shellBlueprint)) { using (var environment = bootstrapLifetimeScope.CreateWorkContextScope()) { // check if the database is already created (in case an exception occured in the second phase) var schemaBuilder = new SchemaBuilder(environment.Resolve <IDataMigrationInterpreter>()); try { var tablePrefix = String.IsNullOrEmpty(shellSettings.DataTablePrefix) ? "" : shellSettings.DataTablePrefix + "_"; schemaBuilder.ExecuteSql("SELECT * FROM " + tablePrefix + "Settings_ShellDescriptorRecord"); } catch { var reportsCoordinator = environment.Resolve <IReportsCoordinator>(); reportsCoordinator.Register("Data Migration", "Setup", "Orchard installation"); schemaBuilder.CreateTable("Orchard_Framework_DataMigrationRecord", table => table .Column <int>("Id", column => column.PrimaryKey().Identity()) .Column <string>("DataMigrationClass") .Column <int>("Version")); var dataMigrationManager = environment.Resolve <IDataMigrationManager>(); dataMigrationManager.Update("Settings"); foreach (var feature in context.EnabledFeatures) { dataMigrationManager.Update(feature); } environment.Resolve <IShellDescriptorManager>().UpdateShellDescriptor( 0, shellDescriptor.Features, shellDescriptor.Parameters); } } } // in effect "pump messages" see PostMessage circa 1980 while (_processingEngine.AreTasksPending()) { _processingEngine.ExecuteNextTask(); } // creating a standalone environment. // in theory this environment can be used to resolve any normal components by interface, and those // components will exist entirely in isolation - no crossover between the safemode container currently in effect // must mark state as Running - otherwise standalone enviro is created "for setup" shellSettings.State = TenantState.Running; using (var environment = _orchardHost.CreateStandaloneEnvironment(shellSettings)) { try { executionId = CreateTenantData(context, environment); } catch { environment.Resolve <ITransactionManager>().Cancel(); throw; } } _shellSettingsManager.SaveSettings(shellSettings); return(executionId); }
public IPlatoHost UpdateShell(IShellSettings settings) { _shellSettingsManager.SaveSettings(settings); return(this); }
public override void Execute(RecipeExecutionContext context) { _shellSettings.State = TenantState.Running; _shellSettingsManager.SaveSettings(_shellSettings); }
public void CreateTenant(ShellSettings settings) { _shellSettingsManager.SaveSettings(settings); }
private string SetupInternal(SetupContext context) { string executionId; Logger.Information("Running setup for tenant '{0}'.", _shellSettings.Name); // The vanilla Orchard distibution has the following features enabled. string[] hardcoded = { // Framework "Orchard.Framework", // Core "Common", "Containers", "Contents", "Dashboard", "Feeds", "Navigation", "Scheduling", "Settings", "Shapes", "Title", // Modules "Orchard.Pages", "Orchard.ContentPicker", "Orchard.Themes", "Orchard.Users", "Orchard.Roles", "Orchard.Modules", "PackagingServices","Orchard.Packaging", "Gallery", "Orchard.Recipes" }; context.EnabledFeatures = hardcoded.Union(context.EnabledFeatures ?? Enumerable.Empty <string>()).Distinct().ToList(); // Set shell state to "Initializing" so that subsequent HTTP requests are responded to with "Service Unavailable" while Orchard is setting up. _shellSettings.State = TenantState.Initializing; var shellSettings = new ShellSettings(_shellSettings); if (String.IsNullOrEmpty(shellSettings.DataProvider)) { shellSettings.DataProvider = context.DatabaseProvider; shellSettings.DataConnectionString = context.DatabaseConnectionString; shellSettings.DataTablePrefix = context.DatabaseTablePrefix; } shellSettings.EncryptionAlgorithm = "AES"; // Randomly generated key. shellSettings.EncryptionKey = SymmetricAlgorithm.Create(shellSettings.EncryptionAlgorithm).Key.ToHexString(); shellSettings.HashAlgorithm = "HMACSHA256"; // Randomly generated key. shellSettings.HashKey = HMAC.Create(shellSettings.HashAlgorithm).Key.ToHexString(); var shellDescriptor = new ShellDescriptor { Features = context.EnabledFeatures.Select(name => new ShellFeature { Name = name }) }; var shellBlueprint = _compositionStrategy.Compose(shellSettings, shellDescriptor); // Initialize database explicitly, and store shell descriptor. using (var bootstrapLifetimeScope = _shellContainerFactory.CreateContainer(shellSettings, shellBlueprint)) { using (var environment = bootstrapLifetimeScope.CreateWorkContextScope()) { // Check if the database is already created (in case an exception occured in the second phase). var schemaBuilder = new SchemaBuilder(environment.Resolve <IDataMigrationInterpreter>()); var installationPresent = true; try { var tablePrefix = String.IsNullOrEmpty(shellSettings.DataTablePrefix) ? "" : shellSettings.DataTablePrefix + "_"; schemaBuilder.ExecuteSql("SELECT * FROM " + tablePrefix + "Settings_ShellDescriptorRecord"); } catch { installationPresent = false; } if (installationPresent) { throw new OrchardException(T("A previous Orchard installation was detected in this database with this table prefix.")); } // Workaround to avoid some Transaction issue for PostgreSQL. environment.Resolve <ITransactionManager>().RequireNew(); schemaBuilder.CreateTable("Orchard_Framework_DataMigrationRecord", table => table .Column <int>("Id", column => column.PrimaryKey().Identity()) .Column <string>("DataMigrationClass") .Column <int>("Version")); schemaBuilder.AlterTable("Orchard_Framework_DataMigrationRecord", table => table.AddUniqueConstraint("UC_DMR_DataMigrationClass_Version", "DataMigrationClass", "Version")); var dataMigrationManager = environment.Resolve <IDataMigrationManager>(); dataMigrationManager.Update("Settings"); foreach (var feature in context.EnabledFeatures) { dataMigrationManager.Update(feature); } environment.Resolve <IShellDescriptorManager>().UpdateShellDescriptor( 0, shellDescriptor.Features, shellDescriptor.Parameters); } } // In effect "pump messages" see PostMessage circa 1980. while (_processingEngine.AreTasksPending()) { _processingEngine.ExecuteNextTask(); } // Creating a standalone environment. // in theory this environment can be used to resolve any normal components by interface, and those // components will exist entirely in isolation - no crossover between the safemode container currently in effect. using (var environment = _orchardHost.CreateStandaloneEnvironment(shellSettings)) { try { executionId = CreateTenantData(context, environment); } catch { environment.Resolve <ITransactionManager>().Cancel(); throw; } } _shellSettingsManager.SaveSettings(shellSettings); return(executionId); }
public void UpdateShellSettings(IShellSettings settings) { _shellSettingsManager.SaveSettings(settings); RecycleShellContext(settings); }