Moved the application url to HostingEnvironment and set it in the request middleware

This commit is contained in:
Bjarke Berg
2021-02-08 11:00:15 +01:00
parent cf81f9b706
commit fea86bbf7a
26 changed files with 168 additions and 86 deletions

View File

@@ -8,6 +8,7 @@ using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Umbraco.Core.Hosting;
using Umbraco.Core.Services;
using Umbraco.Web;
@@ -18,18 +19,18 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
/// </summary>
public abstract class BaseHttpHeaderCheck : HealthCheck
{
private readonly IHostingEnvironment _hostingEnvironment;
private readonly string _header;
private readonly string _value;
private readonly string _localizedTextPrefix;
private readonly bool _metaTagOptionAvailable;
private readonly IRequestAccessor _requestAccessor;
private static HttpClient s_httpClient;
/// <summary>
/// Initializes a new instance of the <see cref="BaseHttpHeaderCheck"/> class.
/// </summary>
protected BaseHttpHeaderCheck(
IRequestAccessor requestAccessor,
IHostingEnvironment hostingEnvironment,
ILocalizedTextService textService,
string header,
string value,
@@ -37,7 +38,7 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
bool metaTagOptionAvailable)
{
LocalizedTextService = textService ?? throw new ArgumentNullException(nameof(textService));
_requestAccessor = requestAccessor;
_hostingEnvironment = hostingEnvironment;
_header = header;
_value = value;
_localizedTextPrefix = localizedTextPrefix;
@@ -78,7 +79,7 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
var success = false;
// Access the site home page and check for the click-jack protection header or meta tag
Uri url = _requestAccessor.GetApplicationUrl();
Uri url = _hostingEnvironment.ApplicationMainUrl;
try
{

View File

@@ -1,8 +1,8 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using Umbraco.Core.Hosting;
using Umbraco.Core.Services;
using Umbraco.Web;
namespace Umbraco.Core.HealthChecks.Checks.Security
{
@@ -19,8 +19,8 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
/// <summary>
/// Initializes a new instance of the <see cref="ClickJackingCheck"/> class.
/// </summary>
public ClickJackingCheck(IRequestAccessor requestAccessor, ILocalizedTextService textService)
: base(requestAccessor, textService, "X-Frame-Options", "sameorigin", "clickJacking", true)
public ClickJackingCheck(IHostingEnvironment hostingEnvironment, ILocalizedTextService textService)
: base(hostingEnvironment, textService, "X-Frame-Options", "sameorigin", "clickJacking", true)
{
}

View File

@@ -6,8 +6,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Umbraco.Core.Hosting;
using Umbraco.Core.Services;
using Umbraco.Web;
namespace Umbraco.Core.HealthChecks.Checks.Security
{
@@ -22,16 +22,16 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
public class ExcessiveHeadersCheck : HealthCheck
{
private readonly ILocalizedTextService _textService;
private readonly IRequestAccessor _requestAccessor;
private readonly IHostingEnvironment _hostingEnvironment;
private static HttpClient s_httpClient;
/// <summary>
/// Initializes a new instance of the <see cref="ExcessiveHeadersCheck"/> class.
/// </summary>
public ExcessiveHeadersCheck(ILocalizedTextService textService, IRequestAccessor requestAccessor)
public ExcessiveHeadersCheck(ILocalizedTextService textService, IHostingEnvironment hostingEnvironment)
{
_textService = textService;
_requestAccessor = requestAccessor;
_hostingEnvironment = hostingEnvironment;
}
private static HttpClient HttpClient => s_httpClient ??= new HttpClient();
@@ -52,7 +52,7 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
{
string message;
var success = false;
Uri url = _requestAccessor.GetApplicationUrl();
Uri url = _hostingEnvironment.ApplicationMainUrl;
// Access the site home page and check for the headers
var request = new HttpRequestMessage(HttpMethod.Head, url);

View File

@@ -1,8 +1,8 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using Umbraco.Core.Hosting;
using Umbraco.Core.Services;
using Umbraco.Web;
namespace Umbraco.Core.HealthChecks.Checks.Security
{
@@ -26,8 +26,8 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
/// If you want do to it perfectly, you have to submit it https://hstspreload.org/,
/// but then you should include subdomains and I wouldn't suggest to do that for Umbraco-sites.
/// </remarks>
public HstsCheck(IRequestAccessor requestAccessor, ILocalizedTextService textService)
: base(requestAccessor, textService, "Strict-Transport-Security", "max-age=10886400", "hSTS", true)
public HstsCheck(IHostingEnvironment hostingEnvironment, ILocalizedTextService textService)
: base(hostingEnvironment, textService, "Strict-Transport-Security", "max-age=10886400", "hSTS", true)
{
}

View File

@@ -10,8 +10,8 @@ using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Hosting;
using Umbraco.Core.Services;
using Umbraco.Web;
namespace Umbraco.Core.HealthChecks.Checks.Security
{
@@ -27,7 +27,7 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
{
private readonly ILocalizedTextService _textService;
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
private readonly IRequestAccessor _requestAccessor;
private readonly IHostingEnvironment _hostingEnvironment;
private static HttpClient s_httpClient;
private static HttpClientHandler s_httpClientHandler;
@@ -39,11 +39,11 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
public HttpsCheck(
ILocalizedTextService textService,
IOptionsMonitor<GlobalSettings> globalSettings,
IRequestAccessor requestAccessor)
IHostingEnvironment hostingEnvironment)
{
_textService = textService;
_globalSettings = globalSettings;
_requestAccessor = requestAccessor;
_hostingEnvironment = hostingEnvironment;
}
private static HttpClient HttpClient => s_httpClient ??= new HttpClient(HttpClientHandler);
@@ -85,7 +85,7 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
// Attempt to access the site over HTTPS to see if it HTTPS is supported
// and a valid certificate has been configured
var url = _requestAccessor.GetApplicationUrl().ToString().Replace("http:", "https:");
var url = _hostingEnvironment.ApplicationMainUrl.ToString().Replace("http:", "https:");
var request = new HttpRequestMessage(HttpMethod.Head, url);
@@ -148,7 +148,7 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
private Task<HealthCheckStatus> CheckIfCurrentSchemeIsHttps()
{
Uri uri = _requestAccessor.GetApplicationUrl();
Uri uri = _hostingEnvironment.ApplicationMainUrl;
var success = uri.Scheme == "https";
return Task.FromResult(new HealthCheckStatus(_textService.Localize("healthcheck/httpsCheckIsCurrentSchemeHttps", new[] { success ? string.Empty : "not" }))
@@ -161,7 +161,7 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
private Task<HealthCheckStatus> CheckHttpsConfigurationSetting()
{
bool httpsSettingEnabled = _globalSettings.CurrentValue.UseHttps;
Uri uri = _requestAccessor.GetApplicationUrl();
Uri uri = _hostingEnvironment.ApplicationMainUrl;
string resultMessage;
StatusResultType resultType;

View File

@@ -1,8 +1,8 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using Umbraco.Core.Hosting;
using Umbraco.Core.Services;
using Umbraco.Web;
namespace Umbraco.Core.HealthChecks.Checks.Security
{
@@ -19,8 +19,8 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
/// <summary>
/// Initializes a new instance of the <see cref="NoSniffCheck"/> class.
/// </summary>
public NoSniffCheck(IRequestAccessor requestAccessor, ILocalizedTextService textService)
: base(requestAccessor, textService, "X-Content-Type-Options", "nosniff", "noSniff", false)
public NoSniffCheck(IHostingEnvironment hostingEnvironment, ILocalizedTextService textService)
: base(hostingEnvironment, textService, "X-Content-Type-Options", "nosniff", "noSniff", false)
{
}

View File

@@ -1,8 +1,8 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using Umbraco.Core.Hosting;
using Umbraco.Core.Services;
using Umbraco.Web;
namespace Umbraco.Core.HealthChecks.Checks.Security
{
@@ -26,8 +26,8 @@ namespace Umbraco.Core.HealthChecks.Checks.Security
/// If you want do to it perfectly, you have to submit it https://hstspreload.appspot.com/,
/// but then you should include subdomains and I wouldn't suggest to do that for Umbraco-sites.
/// </remarks>
public XssProtectionCheck(IRequestAccessor requestAccessor, ILocalizedTextService textService)
: base(requestAccessor, textService, "X-XSS-Protection", "1; mode=block", "xssProtection", true)
public XssProtectionCheck(IHostingEnvironment hostingEnvironment, ILocalizedTextService textService)
: base(hostingEnvironment, textService, "X-XSS-Protection", "1; mode=block", "xssProtection", true)
{
}

View File

@@ -2,10 +2,10 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Hosting;
using Umbraco.Core.Mail;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web;
namespace Umbraco.Core.HealthChecks.NotificationMethods
{
@@ -13,7 +13,7 @@ namespace Umbraco.Core.HealthChecks.NotificationMethods
public class EmailNotificationMethod : NotificationMethodBase
{
private readonly ILocalizedTextService _textService;
private readonly IRequestAccessor _requestAccessor;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IEmailSender _emailSender;
private readonly IMarkdownToHtmlConverter _markdownToHtmlConverter;
@@ -21,7 +21,7 @@ namespace Umbraco.Core.HealthChecks.NotificationMethods
public EmailNotificationMethod(
ILocalizedTextService textService,
IRequestAccessor requestAccessor,
IHostingEnvironment hostingEnvironment,
IEmailSender emailSender,
IOptions<HealthChecksSettings> healthChecksSettings,
IOptions<ContentSettings> contentSettings,
@@ -38,7 +38,7 @@ namespace Umbraco.Core.HealthChecks.NotificationMethods
RecipientEmail = recipientEmail;
_textService = textService ?? throw new ArgumentNullException(nameof(textService));
_requestAccessor = requestAccessor;
_hostingEnvironment = hostingEnvironment;
_emailSender = emailSender;
_markdownToHtmlConverter = markdownToHtmlConverter;
_contentSettings = contentSettings.Value ?? throw new ArgumentNullException(nameof(contentSettings));
@@ -67,9 +67,9 @@ namespace Umbraco.Core.HealthChecks.NotificationMethods
// Include the umbraco Application URL host in the message subject so that
// you can identify the site that these results are for.
var host = _requestAccessor.GetApplicationUrl();
var host = _hostingEnvironment.ApplicationMainUrl?.ToString();
var subject = _textService.Localize("healthcheck/scheduledHealthCheckEmailSubject", new[] { host.ToString() });
var subject = _textService.Localize("healthcheck/scheduledHealthCheckEmailSubject", new[] { host });
var mailMessage = CreateMailMessage(subject, message);

View File

@@ -31,6 +31,11 @@ namespace Umbraco.Core.Hosting
/// </summary>
bool IsHosted { get; }
/// <summary>
/// Gets the main application url.
/// </summary>
Uri ApplicationMainUrl { get; }
/// <summary>
/// Maps a virtual path to a physical path to the application's web root
/// </summary>
@@ -61,5 +66,10 @@ namespace Umbraco.Core.Hosting
/// If virtualPath does not start with ~/ or /
/// </exception>
string ToAbsolute(string virtualPath);
/// <summary>
/// Ensures that the application know its main Url.
/// </summary>
void EnsureApplicationMainUrl(Uri currentApplicationUrl);
}
}

View File

@@ -1,5 +1,4 @@
using System;
using Umbraco.Web.Routing;
namespace Umbraco.Web
{
@@ -19,8 +18,5 @@ namespace Umbraco.Web
/// Returns the current request uri
/// </summary>
Uri GetRequestUrl();
// TODO: This doesn't belongs here
Uri GetApplicationUrl();
}
}

View File

@@ -2,11 +2,12 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Hosting;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Models.Membership;
@@ -185,7 +186,7 @@ namespace Umbraco.Web.Compose
public sealed class Notifier
{
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
private readonly IRequestAccessor _requestAccessor;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly INotificationService _notificationService;
private readonly IUserService _userService;
private readonly ILocalizedTextService _textService;
@@ -193,18 +194,11 @@ namespace Umbraco.Web.Compose
private readonly ILogger<Notifier> _logger;
/// <summary>
/// Constructor
/// Initializes a new instance of the <see cref="Notifier"/> class.
/// </summary>
/// <param name="backOfficeSecurityAccessor"></param>
/// <param name="requestAccessor"></param>
/// <param name="notificationService"></param>
/// <param name="userService"></param>
/// <param name="textService"></param>
/// <param name="globalSettings"></param>
/// <param name="logger"></param>
public Notifier(
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
IRequestAccessor requestAccessor,
IHostingEnvironment hostingEnvironment,
INotificationService notificationService,
IUserService userService,
ILocalizedTextService textService,
@@ -212,7 +206,7 @@ namespace Umbraco.Web.Compose
ILogger<Notifier> logger)
{
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
_requestAccessor = requestAccessor;
_hostingEnvironment = hostingEnvironment;
_notificationService = notificationService;
_userService = userService;
_textService = textService;
@@ -236,7 +230,7 @@ namespace Umbraco.Web.Compose
}
}
SendNotification(user, entities, action, _requestAccessor.GetApplicationUrl());
SendNotification(user, entities, action, _hostingEnvironment.ApplicationMainUrl);
}
private void SendNotification(IUser sender, IEnumerable<IContent> entities, IAction action, Uri siteUri)

View File

@@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Core;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Hosting;
using Umbraco.Core.Logging;
using Umbraco.Core.Sync;
using Umbraco.Web;
@@ -19,7 +20,7 @@ namespace Umbraco.Infrastructure.HostedServices
/// </summary>
public class KeepAlive : RecurringHostedServiceBase
{
private readonly IRequestAccessor _requestAccessor;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IMainDom _mainDom;
private readonly KeepAliveSettings _keepAliveSettings;
private readonly ILogger<KeepAlive> _logger;
@@ -38,7 +39,7 @@ namespace Umbraco.Infrastructure.HostedServices
/// <param name="serverRegistrar">Provider of server registrations to the distributed cache.</param>
/// <param name="httpClientFactory">Factory for <see cref="HttpClient" /> instances.</param>
public KeepAlive(
IRequestAccessor requestAccessor,
IHostingEnvironment hostingEnvironment,
IMainDom mainDom,
IOptions<KeepAliveSettings> keepAliveSettings,
ILogger<KeepAlive> logger,
@@ -47,7 +48,7 @@ namespace Umbraco.Infrastructure.HostedServices
IHttpClientFactory httpClientFactory)
: base(TimeSpan.FromMinutes(5), DefaultDelay)
{
_requestAccessor = requestAccessor;
_hostingEnvironment = hostingEnvironment;
_mainDom = mainDom;
_keepAliveSettings = keepAliveSettings.Value;
_logger = logger;
@@ -88,7 +89,7 @@ namespace Umbraco.Infrastructure.HostedServices
{
if (keepAlivePingUrl.Contains("{umbracoApplicationUrl}"))
{
var umbracoAppUrl = _requestAccessor.GetApplicationUrl().ToString();
var umbracoAppUrl = _hostingEnvironment.ApplicationMainUrl.ToString();
if (umbracoAppUrl.IsNullOrWhiteSpace())
{
_logger.LogWarning("No umbracoApplicationUrl for service (yet), skip.");

View File

@@ -7,8 +7,8 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Core;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Hosting;
using Umbraco.Core.Services;
using Umbraco.Web;
namespace Umbraco.Infrastructure.HostedServices.ServerRegistration
{
@@ -19,7 +19,7 @@ namespace Umbraco.Infrastructure.HostedServices.ServerRegistration
{
private readonly IRuntimeState _runtimeState;
private readonly IServerRegistrationService _serverRegistrationService;
private readonly IRequestAccessor _requestAccessor;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly ILogger<TouchServerTask> _logger;
private readonly GlobalSettings _globalSettings;
@@ -31,12 +31,17 @@ namespace Umbraco.Infrastructure.HostedServices.ServerRegistration
/// <param name="requestAccessor">Accessor for the current request.</param>
/// <param name="logger">The typed logger.</param>
/// <param name="globalSettings">The configuration for global settings.</param>
public TouchServerTask(IRuntimeState runtimeState, IServerRegistrationService serverRegistrationService, IRequestAccessor requestAccessor, ILogger<TouchServerTask> logger, IOptions<GlobalSettings> globalSettings)
public TouchServerTask(
IRuntimeState runtimeState,
IServerRegistrationService serverRegistrationService,
IHostingEnvironment hostingEnvironment,
ILogger<TouchServerTask> logger,
IOptions<GlobalSettings> globalSettings)
: base(globalSettings.Value.DatabaseServerRegistrar.WaitTimeBetweenCalls, TimeSpan.FromSeconds(15))
{
_runtimeState = runtimeState;
_serverRegistrationService = serverRegistrationService ?? throw new ArgumentNullException(nameof(serverRegistrationService));
_requestAccessor = requestAccessor;
_hostingEnvironment = hostingEnvironment;
_logger = logger;
_globalSettings = globalSettings.Value;
}
@@ -48,7 +53,7 @@ namespace Umbraco.Infrastructure.HostedServices.ServerRegistration
return Task.CompletedTask;
}
var serverAddress = _requestAccessor.GetApplicationUrl()?.ToString();
var serverAddress = _hostingEnvironment.ApplicationMainUrl?.ToString();
if (serverAddress.IsNullOrWhiteSpace())
{
_logger.LogWarning("No umbracoApplicationUrl for service (yet), skip.");

View File

@@ -141,6 +141,7 @@ namespace Umbraco.Tests.Integration.Implementations
public override IHostingEnvironment GetHostingEnvironment()
=> _hostingEnvironment ??= new TestHostingEnvironment(
GetIOptionsMonitorOfHostingSettings(),
GetIOptionsMonitorOfWebRoutingSettings(),
_hostEnvironment);
private IOptionsMonitor<HostingSettings> GetIOptionsMonitorOfHostingSettings()
@@ -149,6 +150,12 @@ namespace Umbraco.Tests.Integration.Implementations
return Mock.Of<IOptionsMonitor<HostingSettings>>(x => x.CurrentValue == hostingSettings);
}
private IOptionsMonitor<WebRoutingSettings> GetIOptionsMonitorOfWebRoutingSettings()
{
var webRoutingSettings = new WebRoutingSettings();
return Mock.Of<IOptionsMonitor<WebRoutingSettings>>(x => x.CurrentValue == webRoutingSettings);
}
public override IApplicationShutdownRegistry GetHostingEnvironmentLifetime() => _hostingLifetime;
public override IIpResolver GetIpResolver() => _ipResolver;

View File

@@ -11,8 +11,8 @@ namespace Umbraco.Tests.Integration.Implementations
{
public class TestHostingEnvironment : AspNetCoreHostingEnvironment, IHostingEnvironment
{
public TestHostingEnvironment(IOptionsMonitor<HostingSettings> hostingSettings, IWebHostEnvironment webHostEnvironment)
: base(hostingSettings, webHostEnvironment)
public TestHostingEnvironment(IOptionsMonitor<HostingSettings> hostingSettings,IOptionsMonitor<WebRoutingSettings> webRoutingSettings, IWebHostEnvironment webHostEnvironment)
: base(hostingSettings,webRoutingSettings, webHostEnvironment)
{
}

View File

@@ -71,6 +71,7 @@ namespace Umbraco.Tests.TestHelpers
var testPath = TestContext.CurrentContext.TestDirectory.Split("bin")[0];
return new AspNetCoreHostingEnvironment(
Mock.Of<IOptionsMonitor<HostingSettings>>(x => x.CurrentValue == new HostingSettings()),
Mock.Of<IOptionsMonitor<WebRoutingSettings>>(x => x.CurrentValue == new WebRoutingSettings()),
Mock.Of<IWebHostEnvironment>(
x =>
x.WebRootPath == "/" &&

View File

@@ -28,8 +28,10 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Extensions
private AspNetCoreHostingEnvironment CreateHostingEnvironment(string virtualPath = "")
{
var hostingSettings = new HostingSettings { ApplicationVirtualPath = virtualPath };
var webRoutingSettings = new WebRoutingSettings();
var mockedOptionsMonitorOfHostingSettings = Mock.Of<IOptionsMonitor<HostingSettings>>(x => x.CurrentValue == hostingSettings);
return new AspNetCoreHostingEnvironment(mockedOptionsMonitorOfHostingSettings, _hostEnvironment);
var mockedOptionsMonitorOfWebRoutingSettings = Mock.Of<IOptionsMonitor<WebRoutingSettings>>(x => x.CurrentValue == webRoutingSettings);
return new AspNetCoreHostingEnvironment(mockedOptionsMonitorOfHostingSettings, mockedOptionsMonitorOfWebRoutingSettings, _hostEnvironment);
}
[TestCase("http://www.domain.com/foo/bar", "/", "http://www.domain.com/")]

View File

@@ -25,9 +25,12 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing
private AspNetCoreHostingEnvironment CreateHostingEnvironment(string virtualPath = "")
{
var hostingSettings = new HostingSettings { ApplicationVirtualPath = virtualPath };
var webRoutingSettings = new WebRoutingSettings();
var mockedOptionsMonitorOfHostingSettings = Mock.Of<IOptionsMonitor<HostingSettings>>(x => x.CurrentValue == hostingSettings);
return new AspNetCoreHostingEnvironment(mockedOptionsMonitorOfHostingSettings, _hostEnvironment);
var mockedOptionsMonitorOfWebRoutingSettings = Mock.Of<IOptionsMonitor<WebRoutingSettings>>(x => x.CurrentValue == webRoutingSettings);
return new AspNetCoreHostingEnvironment(mockedOptionsMonitorOfHostingSettings, mockedOptionsMonitorOfWebRoutingSettings, _hostEnvironment);
}
[TestCase("/favicon.ico", true)]
@@ -65,7 +68,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing
[TestCase("http://www.domain.com/Umbraco/", "", true)]
[TestCase("http://www.domain.com/umbraco/default.aspx", "", true)]
[TestCase("http://www.domain.com/umbraco/test/test", "", false)]
[TestCase("http://www.domain.com/umbraco/test/test/test", "", false)]
[TestCase("http://www.domain.com/umbraco/test/test/test", "", false)]
[TestCase("http://www.domain.com/umbrac", "", false)]
[TestCase("http://www.domain.com/test", "", false)]
[TestCase("http://www.domain.com/test/umbraco", "", false)]
@@ -84,7 +87,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing
var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment);
Assert.AreEqual(expected, umbracoRequestPaths.IsBackOfficeRequest(source.AbsolutePath));
}
[TestCase("http://www.domain.com/install", true)]
[TestCase("http://www.domain.com/Install/", true)]
[TestCase("http://www.domain.com/install/default.aspx", true)]

View File

@@ -13,6 +13,7 @@ using Moq.Protected;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Hosting;
using Umbraco.Core.Logging;
using Umbraco.Core.Scoping;
using Umbraco.Core.Sync;
@@ -78,8 +79,8 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.HostedServices
DisableKeepAliveTask = !enabled,
};
var mockRequestAccessor = new Mock<IRequestAccessor>();
mockRequestAccessor.Setup(x => x.GetApplicationUrl()).Returns(new Uri(ApplicationUrl));
var mockRequestAccessor = new Mock<IHostingEnvironment>();
mockRequestAccessor.SetupGet(x => x.ApplicationMainUrl).Returns(new Uri(ApplicationUrl));
var mockServerRegistrar = new Mock<IServerRoleAccessor>();
mockServerRegistrar.Setup(x => x.CurrentServerRole).Returns(serverRole);

View File

@@ -9,9 +9,9 @@ using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Hosting;
using Umbraco.Core.Services;
using Umbraco.Infrastructure.HostedServices.ServerRegistration;
using Umbraco.Web;
namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.HostedServices.ServerRegistration
{
@@ -53,8 +53,8 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.HostedServices.ServerRe
private TouchServerTask CreateTouchServerTask(RuntimeLevel runtimeLevel = RuntimeLevel.Run, string applicationUrl = ApplicationUrl)
{
var mockRequestAccessor = new Mock<IRequestAccessor>();
mockRequestAccessor.Setup(x => x.GetApplicationUrl()).Returns(!string.IsNullOrEmpty(applicationUrl) ? new Uri(ApplicationUrl) : null);
var mockRequestAccessor = new Mock<IHostingEnvironment>();
mockRequestAccessor.SetupGet(x => x.ApplicationMainUrl).Returns(!string.IsNullOrEmpty(applicationUrl) ? new Uri(ApplicationUrl) : null);
var mockRunTimeState = new Mock<IRuntimeState>();
mockRunTimeState.SetupGet(x => x.Level).Returns(runtimeLevel);
@@ -62,7 +62,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.HostedServices.ServerRe
var mockLogger = new Mock<ILogger<TouchServerTask>>();
_mockServerRegistrationService = new Mock<IServerRegistrationService>();
var settings = new GlobalSettings
{
DatabaseServerRegistrar = new DatabaseServerRegistrarSettings

View File

@@ -64,7 +64,6 @@ namespace Umbraco.Web.BackOffice.Controllers
private readonly IEmailSender _emailSender;
private readonly ISmsSender _smsSender;
private readonly Core.Hosting.IHostingEnvironment _hostingEnvironment;
private readonly IRequestAccessor _requestAccessor;
private readonly LinkGenerator _linkGenerator;
private readonly IBackOfficeExternalLoginProviders _externalAuthenticationOptions;
private readonly IBackOfficeTwoFactorOptions _backOfficeTwoFactorOptions;
@@ -86,7 +85,6 @@ namespace Umbraco.Web.BackOffice.Controllers
IEmailSender emailSender,
ISmsSender smsSender,
Core.Hosting.IHostingEnvironment hostingEnvironment,
IRequestAccessor requestAccessor,
LinkGenerator linkGenerator,
IBackOfficeExternalLoginProviders externalAuthenticationOptions,
IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions)
@@ -105,7 +103,6 @@ namespace Umbraco.Web.BackOffice.Controllers
_emailSender = emailSender;
_smsSender = smsSender;
_hostingEnvironment = hostingEnvironment;
_requestAccessor = requestAccessor;
_linkGenerator = linkGenerator;
_externalAuthenticationOptions = externalAuthenticationOptions;
_backOfficeTwoFactorOptions = backOfficeTwoFactorOptions;
@@ -624,7 +621,7 @@ namespace Umbraco.Web.BackOffice.Controllers
});
// Construct full URL using configured application URL (which will fall back to request)
var applicationUri = _requestAccessor.GetApplicationUrl();
var applicationUri = _hostingEnvironment.ApplicationMainUrl;
var callbackUri = new Uri(applicationUri, action);
return callbackUri.ToString();
}

View File

@@ -54,7 +54,6 @@ namespace Umbraco.Web.BackOffice.Controllers
private readonly ISqlContext _sqlContext;
private readonly IImageUrlGenerator _imageUrlGenerator;
private readonly SecuritySettings _securitySettings;
private readonly IRequestAccessor _requestAccessor;
private readonly IEmailSender _emailSender;
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
private readonly AppCaches _appCaches;
@@ -77,7 +76,6 @@ namespace Umbraco.Web.BackOffice.Controllers
ISqlContext sqlContext,
IImageUrlGenerator imageUrlGenerator,
IOptions<SecuritySettings> securitySettings,
IRequestAccessor requestAccessor,
IEmailSender emailSender,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
AppCaches appCaches,
@@ -98,7 +96,6 @@ namespace Umbraco.Web.BackOffice.Controllers
_sqlContext = sqlContext;
_imageUrlGenerator = imageUrlGenerator;
_securitySettings = securitySettings.Value;
_requestAccessor = requestAccessor;
_emailSender = emailSender;
_backofficeSecurityAccessor = backofficeSecurityAccessor;
_appCaches = appCaches;
@@ -566,7 +563,7 @@ namespace Umbraco.Web.BackOffice.Controllers
});
// Construct full URL using configured application URL (which will fall back to request)
var applicationUri = _requestAccessor.GetApplicationUrl();
var applicationUri = _hostingEnvironment.ApplicationMainUrl;
var inviteUri = new Uri(applicationUri, action);
var emailSubject = _localizedTextService.Localize("user/inviteEmailCopySubject",

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Options;
@@ -10,14 +11,20 @@ namespace Umbraco.Web.Common.AspNetCore
{
public class AspNetCoreHostingEnvironment : Core.Hosting.IHostingEnvironment
{
private IOptionsMonitor<HostingSettings> _hostingSettings;
private readonly ISet<Uri> _applicationUrls = new HashSet<Uri>();
private readonly IOptionsMonitor<HostingSettings> _hostingSettings;
private readonly IOptionsMonitor<WebRoutingSettings> _webRoutingSettings;
private readonly IWebHostEnvironment _webHostEnvironment;
private string _localTempPath;
public AspNetCoreHostingEnvironment(IOptionsMonitor<HostingSettings> hostingSettings, IWebHostEnvironment webHostEnvironment)
public AspNetCoreHostingEnvironment(
IOptionsMonitor<HostingSettings> hostingSettings,
IOptionsMonitor<WebRoutingSettings> webRoutingSettings,
IWebHostEnvironment webHostEnvironment)
{
_hostingSettings = hostingSettings ?? throw new ArgumentNullException(nameof(hostingSettings));
_webRoutingSettings = webRoutingSettings ?? throw new ArgumentNullException(nameof(webRoutingSettings));
_webHostEnvironment = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment));
SiteName = webHostEnvironment.ApplicationName;
@@ -28,6 +35,9 @@ namespace Umbraco.Web.Common.AspNetCore
/// <inheritdoc/>
public bool IsHosted { get; } = true;
/// <inheritdoc/>
public Uri ApplicationMainUrl { get; private set; }
/// <inheritdoc/>
public string SiteName { get; }
@@ -37,8 +47,6 @@ namespace Umbraco.Web.Common.AspNetCore
/// <inheritdoc/>
public string ApplicationPhysicalPath { get; }
public string ApplicationServerAddress { get; }
// TODO how to find this, This is a server thing, not application thing.
public string ApplicationVirtualPath => _hostingSettings.CurrentValue.ApplicationVirtualPath?.EnsureStartsWith('/') ?? "/";
@@ -123,6 +131,35 @@ namespace Umbraco.Web.Common.AspNetCore
return fullPath;
}
public void EnsureApplicationMainUrl(Uri currentApplicationUrl)
{
// Fixme: This causes problems with site swap on azure because azure pre-warms a site by calling into `localhost` and when it does that
// it changes the URL to `localhost:80` which actually doesn't work for pinging itself, it only works internally in Azure. The ironic part
// about this is that this is here specifically for the slot swap scenario https://issues.umbraco.org/issue/U4-10626
// see U4-10626 - in some cases we want to reset the application url
// (this is a simplified version of what was in 7.x)
// note: should this be optional? is it expensive?
if (currentApplicationUrl == null)
{
return;
}
if (!(_webRoutingSettings.CurrentValue.UmbracoApplicationUrl is null))
{
return;
}
var change = currentApplicationUrl != null && !_applicationUrls.Contains(currentApplicationUrl);
if (change)
{
_applicationUrls.Add(currentApplicationUrl);
ApplicationMainUrl = currentApplicationUrl;
}
}
}

View File

@@ -382,9 +382,11 @@ namespace Umbraco.Web.Common.DependencyInjection
private static IHostingEnvironment GetTemporaryHostingEnvironment(IWebHostEnvironment webHostEnvironment, IConfiguration config)
{
var hostingSettings = config.GetSection(Core.Constants.Configuration.ConfigHosting).Get<HostingSettings>() ?? new HostingSettings();
var webRoutingSettings = config.GetSection(Core.Constants.Configuration.ConfigWebRouting).Get<WebRoutingSettings>() ?? new WebRoutingSettings();
var wrappedHostingSettings = new OptionsMonitorAdapter<HostingSettings>(hostingSettings);
var wrappedWebRoutingSettings = new OptionsMonitorAdapter<WebRoutingSettings>(webRoutingSettings);
return new AspNetCoreHostingEnvironment(wrappedHostingSettings, webHostEnvironment);
return new AspNetCoreHostingEnvironment(wrappedHostingSettings,wrappedWebRoutingSettings, webHostEnvironment);
}
}
}

View File

@@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Events;
using Umbraco.Core.Hosting;
using Umbraco.Core.Logging;
using Umbraco.Extensions;
using Umbraco.Web.Common.Profiler;
@@ -36,6 +37,7 @@ namespace Umbraco.Web.Common.Middleware
private readonly IBackOfficeSecurityFactory _backofficeSecurityFactory;
private readonly PublishedSnapshotServiceEventHandler _publishedSnapshotServiceEventHandler;
private readonly IEventAggregator _eventAggregator;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly WebProfiler _profiler;
private static bool s_cacheInitialized = false;
private static bool s_cacheInitializedFlag = false;
@@ -51,7 +53,8 @@ namespace Umbraco.Web.Common.Middleware
IBackOfficeSecurityFactory backofficeSecurityFactory,
PublishedSnapshotServiceEventHandler publishedSnapshotServiceEventHandler,
IEventAggregator eventAggregator,
IProfiler profiler)
IProfiler profiler,
IHostingEnvironment hostingEnvironment)
{
_logger = logger;
_umbracoContextFactory = umbracoContextFactory;
@@ -59,6 +62,7 @@ namespace Umbraco.Web.Common.Middleware
_backofficeSecurityFactory = backofficeSecurityFactory;
_publishedSnapshotServiceEventHandler = publishedSnapshotServiceEventHandler;
_eventAggregator = eventAggregator;
_hostingEnvironment = hostingEnvironment;
_profiler = profiler as WebProfiler; // Ignore if not a WebProfiler
}
@@ -81,6 +85,10 @@ namespace Umbraco.Web.Common.Middleware
_backofficeSecurityFactory.EnsureBackOfficeSecurity(); // Needs to be before UmbracoContext, TODO: Why?
UmbracoContextReference umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext();
Uri currentApplicationUrl = GetApplicationUrlFromCurrentRequest(context.Request);
_hostingEnvironment.EnsureApplicationMainUrl(currentApplicationUrl);
bool isFrontEndRequest = umbracoContextReference.UmbracoContext.IsFrontEndUmbracoRequest();
var pathAndQuery = context.Request.GetEncodedPathAndQuery();
@@ -138,6 +146,18 @@ namespace Umbraco.Web.Common.Middleware
_profiler?.UmbracoApplicationEndRequest(context);
}
private Uri GetApplicationUrlFromCurrentRequest(HttpRequest request)
{
// We only consider GET and POST.
// Especially the DEBUG sent when debugging the application is annoying because it uses http, even when the https is available.
if (request.Method == "GET" || request.Method == "POST")
{
return new Uri($"{request.Scheme}://{request.Host}{request.PathBase}", UriKind.Absolute);
}
return null;
}
/// <summary>
/// Any object that is in the HttpContext.Items collection that is IDisposable will get disposed on the end of the request
/// </summary>

View File

@@ -17,6 +17,7 @@ namespace Umbraco.Web.Hosting
private readonly HostingSettings _hostingSettings;
private string _localTempPath;
private Uri _applicationMainUrl;
public AspNetHostingEnvironment(IOptions<HostingSettings> hostingSettings)
@@ -41,6 +42,12 @@ namespace Umbraco.Web.Hosting
/// <inheritdoc/>
public bool IsHosted => (HttpContext.Current != null || HostingEnvironment.IsHosted);
public Uri ApplicationMainUrl
{
get => _applicationMainUrl;
set => _applicationMainUrl = value;
}
public string MapPathWebRoot(string path)
{
if (HostingEnvironment.IsHosted)
@@ -54,6 +61,7 @@ namespace Umbraco.Web.Hosting
public string MapPathContentRoot(string path) => MapPathWebRoot(path);
public string ToAbsolute(string virtualPath) => VirtualPathUtility.ToAbsolute(virtualPath, ApplicationVirtualPath);
public void EnsureApplicationMainUrl(Uri currentApplicationUrl) => throw new NotImplementedException();
public string LocalTempPath