Merge pull request #1205 from sbwalker/dev

Ensure Install Wizard will only be displayed if the Master database connection string in appsettings.json is not specified. This addresses a potential security issue where the Install Wizard could be displayed in an existing installation if the Master database connection failed during startup.
This commit is contained in:
Shaun Walker 2021-03-30 17:46:42 -04:00 committed by GitHub
commit c037614917
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 63 additions and 32 deletions

View File

@ -1,31 +1,39 @@
@inject IInstallationService InstallationService @inject IInstallationService InstallationService
@if (_initialized) @if (_initialized)
{ {
@if (!_installed) @if (!_installation.Success)
{ {
<Installer /> <Installer />
} }
else else
{ {
<CascadingAuthenticationState> @if (string.IsNullOrEmpty(_installation.Message))
<CascadingValue Value="@PageState"> {
<SiteRouter OnStateChange="@ChangeState" /> <CascadingAuthenticationState>
</CascadingValue> <CascadingValue Value="@PageState">
</CascadingAuthenticationState> <SiteRouter OnStateChange="@ChangeState" />
</CascadingValue>
</CascadingAuthenticationState>
}
else
{
<div class="app-alert">
@_installation.Message
</div>
}
} }
} }
@code { @code {
private Installation _installation;
private bool _initialized; private bool _initialized;
private bool _installed;
private PageState PageState { get; set; } private PageState PageState { get; set; }
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
var installation = await InstallationService.IsInstalled(); _installation = await InstallationService.IsInstalled();
_installed = installation.Success;
_initialized = true; _initialized = true;
} }

View File

@ -22,7 +22,7 @@ else
<Row> <Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.AliasId.ToString())" ResourceKey="EditSite" /></td> <td><ActionLink Action="Edit" Parameters="@($"id=" + context.AliasId.ToString())" ResourceKey="EditSite" /></td>
<td><ActionDialog Header="Delete Site" Message="@Localizer["Are You Sure You Wish To Delete The {0} Site?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteSite(context))" ResourceKey="DeleteSite" /></td> <td><ActionDialog Header="Delete Site" Message="@Localizer["Are You Sure You Wish To Delete The {0} Site?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteSite(context))" ResourceKey="DeleteSite" /></td>
<td><a href="@(_scheme + context.Name)">@context.Name</a></td> <td><a href="@(_scheme + context.Name +"?reload")">@context.Name</a></td>
</Row> </Row>
</Pager> </Pager>
} }

View File

@ -55,8 +55,7 @@ namespace Oqtane.Controllers
[HttpGet("installed")] [HttpGet("installed")]
public Installation IsInstalled() public Installation IsInstalled()
{ {
bool isInstalled = _databaseManager.IsInstalled(); return _databaseManager.IsInstalled();
return new Installation {Success = isInstalled, Message = string.Empty};
} }
[HttpGet("upgrade")] [HttpGet("upgrade")]

View File

@ -33,27 +33,30 @@ namespace Oqtane.Infrastructure
_cache = cache; _cache = cache;
} }
public bool IsInstalled() public Installation IsInstalled()
{ {
var defaultConnectionString = NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)); var result = new Installation { Success = false, Message = string.Empty };
var result = !string.IsNullOrEmpty(defaultConnectionString); if (!string.IsNullOrEmpty(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))
if (result)
{ {
result.Success = true;
using (var scope = _serviceScopeFactory.CreateScope()) using (var scope = _serviceScopeFactory.CreateScope())
{ {
var db = scope.ServiceProvider.GetRequiredService<MasterDBContext>(); var db = scope.ServiceProvider.GetRequiredService<MasterDBContext>();
result = db.Database.CanConnect(); if (db.Database.CanConnect())
if (result)
{ {
try try
{ {
result = db.Tenant.Any(); var provisioned = db.Tenant.Any();
} }
catch catch
{ {
result = false; result.Message = "Master Database Not Installed Correctly";
} }
} }
else
{
result.Message = "Cannot Connect To Master Database";
}
} }
} }
return result; return result;
@ -74,7 +77,8 @@ namespace Oqtane.Infrastructure
// startup or silent installation // startup or silent installation
install = new InstallConfig { ConnectionString = _config.GetConnectionString(SettingKeys.ConnectionStringKey), TenantName = TenantNames.Master, IsNewTenant = false }; install = new InstallConfig { ConnectionString = _config.GetConnectionString(SettingKeys.ConnectionStringKey), TenantName = TenantNames.Master, IsNewTenant = false };
if (!IsInstalled()) var installation = IsInstalled();
if (!installation.Success)
{ {
install.Aliases = GetInstallationConfig(SettingKeys.DefaultAliasKey, string.Empty); install.Aliases = GetInstallationConfig(SettingKeys.DefaultAliasKey, string.Empty);
install.HostPassword = GetInstallationConfig(SettingKeys.HostPasswordKey, string.Empty); install.HostPassword = GetInstallationConfig(SettingKeys.HostPasswordKey, string.Empty);
@ -97,6 +101,14 @@ namespace Oqtane.Infrastructure
install.ConnectionString = ""; install.ConnectionString = "";
} }
} }
else
{
if (!string.IsNullOrEmpty(installation.Message))
{
// problem with prior installation
install.ConnectionString = "";
}
}
} }
else else
{ {

View File

@ -1,11 +1,11 @@
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Shared; using Oqtane.Shared;
namespace Oqtane.Infrastructure namespace Oqtane.Infrastructure
{ {
public interface IDatabaseManager public interface IDatabaseManager
{ {
bool IsInstalled(); Installation IsInstalled();
Installation Install(); Installation Install();
Installation Install(InstallConfig install); Installation Install(InstallConfig install);
} }

View File

@ -25,14 +25,12 @@ namespace Oqtane.Infrastructure
public void Upgrade(Tenant tenant, string version) public void Upgrade(Tenant tenant, string version)
{ {
// core framework upgrade logic - note that you can check if current tenant is Master if you only want to execute logic once // core framework upgrade logic - note that you can check if current tenant is Master if you only want to execute the logic once
var pageTemplates = new List<PageTemplate>();
switch (version) switch (version)
{ {
case "0.9.0": case "0.9.0":
// add a page to all existing sites on upgrade // this code is commented out on purpose - it provides an example of how to programmatically add a page to all existing sites on upgrade
var pageTemplates = new List<PageTemplate>();
//pageTemplates.Add(new PageTemplate //pageTemplates.Add(new PageTemplate
//{ //{
// Name = "Test", // Name = "Test",
@ -68,7 +66,12 @@ namespace Oqtane.Infrastructure
case "2.0.2": case "2.0.2":
if (tenant.Name == TenantNames.Master) if (tenant.Name == TenantNames.Master)
{ {
Directory.Delete(Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", "Internal", Path.DirectorySeparatorChar.ToString()), true); // remove Internal module template files as they are no longer supported
var internalTemplatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", "Internal", Path.DirectorySeparatorChar.ToString());
if (Directory.Exists(internalTemplatePath))
{
Directory.Delete(internalTemplatePath, true);
}
} }
break; break;
} }

View File

@ -37,7 +37,9 @@
@if (Model.Message != "") @if (Model.Message != "")
{ {
@Model.Message <div class="app-alert">
@Model.Message
</div>
} }
<script src="js/interop.js"></script> <script src="js/interop.js"></script>

View File

@ -1,4 +1,4 @@
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); @import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
html, body { html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
@ -125,6 +125,13 @@ app {
vertical-align: inherit; vertical-align: inherit;
} }
.app-alert {
padding: 20px;
background-color: #f44336; /* red */
color: white;
margin-bottom: 15px;
}
/* Tooltips */ /* Tooltips */
.app-tooltip { .app-tooltip {
cursor: help; cursor: help;