Merge branch 'v9/dev' into v9/task/more-flexible-startup

# Conflicts:
#	src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs
This commit is contained in:
Shannon
2021-08-10 13:55:55 -06:00
215 changed files with 3180 additions and 1858 deletions

View File

@@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Infrastructure.DependencyInjection;
using Umbraco.Cms.Web.Common.Middleware;
using Umbraco.Cms.Web.Common.Routing;
using Umbraco.Cms.Web.Website.Collections;
using Umbraco.Cms.Web.Website.Controllers;
@@ -47,6 +48,7 @@ namespace Umbraco.Extensions
builder.Services.AddSingleton<MemberModelBuilderFactory>();
builder.Services.AddSingleton<PublicAccessMiddleware>();
builder.Services.AddSingleton<BasicAuthenticationMiddleware>();
builder
.AddDistributedCache()

View File

@@ -2,6 +2,7 @@ using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Web.Common.ApplicationBuilder;
using Umbraco.Cms.Web.Common.Middleware;
using Umbraco.Cms.Web.Website.Middleware;
using Umbraco.Cms.Web.Website.Routing;
@@ -20,6 +21,7 @@ namespace Umbraco.Extensions
public static IUmbracoApplicationBuilderContext UseWebsite(this IUmbracoApplicationBuilderContext builder)
{
builder.AppBuilder.UseMiddleware<PublicAccessMiddleware>();
builder.AppBuilder.UseMiddleware<BasicAuthenticationMiddleware>();
return builder;
}

View File

@@ -16,10 +16,11 @@ namespace Umbraco.Extensions
/// </summary>
/// <typeparam name="T">The type of the content last chance finder.</typeparam>
/// <param name="builder">The builder.</param>
public static void SetContentLastChanceFinder<T>(this IUmbracoBuilder builder)
public static IUmbracoBuilder SetContentLastChanceFinder<T>(this IUmbracoBuilder builder)
where T : class, IContentLastChanceFinder
{
builder.Services.AddUnique<IContentLastChanceFinder, T>();
return builder;
}
/// <summary>
@@ -27,9 +28,10 @@ namespace Umbraco.Extensions
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a last chance finder.</param>
public static void SetContentLastChanceFinder(this IUmbracoBuilder builder, Func<IServiceProvider, IContentLastChanceFinder> factory)
public static IUmbracoBuilder SetContentLastChanceFinder(this IUmbracoBuilder builder, Func<IServiceProvider, IContentLastChanceFinder> factory)
{
builder.Services.AddUnique(factory);
return builder;
}
/// <summary>
@@ -37,9 +39,10 @@ namespace Umbraco.Extensions
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="finder">A last chance finder.</param>
public static void SetContentLastChanceFinder(this IUmbracoBuilder builder, IContentLastChanceFinder finder)
public static IUmbracoBuilder SetContentLastChanceFinder(this IUmbracoBuilder builder, IContentLastChanceFinder finder)
{
builder.Services.AddUnique(finder);
return builder;
}
/// <summary>
@@ -47,10 +50,11 @@ namespace Umbraco.Extensions
/// </summary>
/// <typeparam name="T">The type of the site domain helper.</typeparam>
/// <param name="builder"></param>
public static void SetSiteDomainHelper<T>(this IUmbracoBuilder builder)
public static IUmbracoBuilder SetSiteDomainHelper<T>(this IUmbracoBuilder builder)
where T : class, ISiteDomainMapper
{
builder.Services.AddUnique<ISiteDomainMapper, T>();
return builder;
}
/// <summary>
@@ -58,9 +62,10 @@ namespace Umbraco.Extensions
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a helper.</param>
public static void SetSiteDomainHelper(this IUmbracoBuilder builder, Func<IServiceProvider, ISiteDomainMapper> factory)
public static IUmbracoBuilder SetSiteDomainHelper(this IUmbracoBuilder builder, Func<IServiceProvider, ISiteDomainMapper> factory)
{
builder.Services.AddUnique(factory);
return builder;
}
/// <summary>
@@ -68,9 +73,10 @@ namespace Umbraco.Extensions
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="helper">A helper.</param>
public static void SetSiteDomainHelper(this IUmbracoBuilder builder, ISiteDomainMapper helper)
public static IUmbracoBuilder SetSiteDomainHelper(this IUmbracoBuilder builder, ISiteDomainMapper helper)
{
builder.Services.AddUnique(helper);
return builder;
}
#endregion

View File

@@ -0,0 +1,90 @@
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.BackOffice.Security;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.Common.Middleware
{
/// <summary>
/// Provides basic authentication via back-office credentials for public website access if configured for use and the client IP is not allow listed.
/// </summary>
public class BasicAuthenticationMiddleware : IMiddleware
{
private readonly IRuntimeState _runtimeState;
private readonly IBasicAuthService _basicAuthService;
public BasicAuthenticationMiddleware(
IRuntimeState runtimeState,
IBasicAuthService basicAuthService)
{
_runtimeState = runtimeState;
_basicAuthService = basicAuthService;
}
/// <inheritdoc />
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (_runtimeState.Level < RuntimeLevel.Run || context.Request.IsBackOfficeRequest() || !_basicAuthService.IsBasicAuthEnabled())
{
await next(context);
return;
}
IPAddress clientIPAddress = context.Connection.RemoteIpAddress;
if (_basicAuthService.IsIpAllowListed(clientIPAddress))
{
await next(context);
return;
}
AuthenticateResult authenticateResult = await context.AuthenticateBackOfficeAsync();
if (authenticateResult.Succeeded)
{
await next(context);
return;
}
if (context.TryGetBasicAuthCredentials(out var username, out var password))
{
IBackOfficeSignInManager backOfficeSignInManager =
context.RequestServices.GetService<IBackOfficeSignInManager>();
if (backOfficeSignInManager is not null)
{
SignInResult signInResult =
await backOfficeSignInManager.PasswordSignInAsync(username, password, false, true);
if (signInResult.Succeeded)
{
await next.Invoke(context);
}
else
{
SetUnauthorizedHeader(context);
}
}
else
{
SetUnauthorizedHeader(context);
}
}
else
{
// no authorization header
SetUnauthorizedHeader(context);
}
}
private static void SetUnauthorizedHeader(HttpContext context)
{
context.Response.StatusCode = 401;
context.Response.Headers.Add("WWW-Authenticate", "Basic realm=\"Umbraco login\"");
}
}
}