Ensures that all back office controllers are authenticated under the back office scheme

This commit is contained in:
Shannon
2020-11-27 13:32:41 +11:00
parent 7d143ec240
commit 313a2e6f7c
13 changed files with 74 additions and 24 deletions

View File

@@ -45,7 +45,7 @@ namespace Umbraco.Web.BackOffice.Controllers
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)] // TODO: Maybe this could be applied with our Application Model conventions
//[ValidationFilter] // TODO: I don't actually think this is required with our custom Application Model conventions applied
[AngularJsonOnlyConfiguration] // TODO: This could be applied with our Application Model conventions
[IsBackOffice] // TODO: This could be applied with our Application Model conventions
[IsBackOffice]
public class AuthenticationController : UmbracoApiControllerBase
{
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;

View File

@@ -41,6 +41,7 @@ namespace Umbraco.Web.BackOffice.Controllers
[DisableBrowserCache]
//[UmbracoRequireHttps] //TODO Reintroduce
[PluginController(Constants.Web.Mvc.BackOfficeArea)]
[IsBackOffice]
public class BackOfficeController : UmbracoController
{
private readonly IBackOfficeUserManager _userManager;

View File

@@ -8,8 +8,7 @@ using Umbraco.Web.PublishedCache;
namespace Umbraco.Web.BackOffice.Controllers
{
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[IsBackOffice]
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
public class PublishedSnapshotCacheStatusController : UmbracoAuthorizedApiController
{
private readonly IPublishedSnapshotService _publishedSnapshotService;

View File

@@ -32,15 +32,9 @@ namespace Umbraco.Extensions
builder.Services.AddAntiforgery();
builder.Services.AddSingleton<IFilterProvider, OverrideAuthorizationFilterProvider>();
// TODO: We need to see if we are 'allowed' to do this, the docs say:
// "The call to AddIdentity configures the default scheme settings. The AddAuthentication(String) overload sets the DefaultScheme property. The AddAuthentication(Action<AuthenticationOptions>) overload allows configuring authentication options, which can be used to set up default authentication schemes for different purposes. Subsequent calls to AddAuthentication override previously configured AuthenticationOptions properties."
// So if someone calls services.AddAuthentication() ... in Startup does that overwrite all of this?
// It also says "When the app requires multiple providers, chain the provider extension methods behind AddAuthentication"
// Which leads me to believe it all gets overwritten? :/
// UPDATE: I have tested this breifly in Startup doing Services.AddAuthentication().AddGoogle() ... and the back office auth
// still seems to work. We'll see how it goes i guess.
builder.Services
.AddAuthentication(Core.Constants.Security.BackOfficeAuthenticationType)
.AddAuthentication() // This just creates a builder, nothing more
// Add our custom schemes which are cookie handlers
.AddCookie(Core.Constants.Security.BackOfficeAuthenticationType)
.AddCookie(Core.Constants.Security.BackOfficeExternalAuthenticationType, o =>
{

View File

@@ -26,6 +26,11 @@ namespace Umbraco.Web.BackOffice.Security
_loginProviderOptions = loginProviderOptions;
}
public string SchemeForBackOffice(string scheme)
{
return Constants.Security.BackOfficeExternalAuthenticationTypePrefix + scheme;
}
/// <summary>
/// Overridden to track the final authenticationScheme being registered for the external login
/// </summary>
@@ -36,11 +41,11 @@ namespace Umbraco.Web.BackOffice.Security
/// <param name="configureOptions"></param>
/// <returns></returns>
public override AuthenticationBuilder AddRemoteScheme<TOptions, THandler>(string authenticationScheme, string displayName, Action<TOptions> configureOptions)
{
//Ensure the prefix is set
{
// Validate that the prefix is set
if (!authenticationScheme.StartsWith(Constants.Security.BackOfficeExternalAuthenticationTypePrefix))
{
authenticationScheme = Constants.Security.BackOfficeExternalAuthenticationTypePrefix + authenticationScheme;
throw new InvalidOperationException($"The {nameof(authenticationScheme)} is not prefixed with {Constants.Security.BackOfficeExternalAuthenticationTypePrefix}. The scheme must be created with a call to the method {nameof(SchemeForBackOffice)}");
}
// add our login provider to the container along with a custom options configuration

View File

@@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Umbraco.Web.Common.Filters;
namespace Umbraco.Web.Common.ApplicationModels
{
/// <summary>
/// Ensures all requests with this convention are authenticated with the back office scheme
/// </summary>
public class AuthenticateAsBackOfficeSchemeConvention : IActionModelConvention
{
public void Apply(ActionModel action)
{
action.Filters.Add(new EnsureUmbracoBackOfficeAuthentication());
}
}
}

View File

@@ -6,6 +6,8 @@ using Umbraco.Web.Common.Attributes;
namespace Umbraco.Web.Common.ApplicationModels
{
// TODO: This should just exist in the back office project
/// <summary>
/// An application model provider for all Umbraco Back Office controllers
/// </summary>
@@ -15,7 +17,8 @@ namespace Umbraco.Web.Common.ApplicationModels
{
ActionModelConventions = new List<IActionModelConvention>()
{
new BackOfficeIdentityCultureConvention()
new BackOfficeIdentityCultureConvention(),
new AuthenticateAsBackOfficeSchemeConvention()
};
}
@@ -49,12 +52,7 @@ namespace Umbraco.Web.Common.ApplicationModels
}
private bool IsBackOfficeController(ControllerModel controller)
{
var pluginControllerAttribute = controller.Attributes.OfType<PluginControllerAttribute>().FirstOrDefault();
return pluginControllerAttribute != null
&& (pluginControllerAttribute.AreaName == Core.Constants.Web.Mvc.BackOfficeArea
|| pluginControllerAttribute.AreaName == Core.Constants.Web.Mvc.BackOfficeApiArea
|| pluginControllerAttribute.AreaName == Core.Constants.Web.Mvc.BackOfficeTreeArea);
}
=> controller.Attributes.OfType<IsBackOfficeAttribute>().Any();
}
}

View File

@@ -3,6 +3,9 @@ using Umbraco.Web.Common.Filters;
namespace Umbraco.Web.Common.ApplicationModels
{
// TODO: This should just exist in the back office project
public class BackOfficeIdentityCultureConvention : IActionModelConvention
{
public void Apply(ActionModel action)

View File

@@ -81,6 +81,7 @@ namespace Umbraco.Web.Common.ApplicationModels
}
}
private bool IsUmbracoApiController(ControllerModel controller) => controller.Attributes.OfType<UmbracoApiControllerAttribute>().Any();
private bool IsUmbracoApiController(ControllerModel controller)
=> controller.Attributes.OfType<UmbracoApiControllerAttribute>().Any();
}
}

View File

@@ -2,6 +2,9 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Umbraco.Web.Common.ModelBinding;
using System.Linq;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Actions;
using Umbraco.Web.Common.Filters;
namespace Umbraco.Web.Common.ApplicationModels
{
@@ -21,4 +24,6 @@ namespace Umbraco.Web.Common.ApplicationModels
}
}
}
}

View File

@@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
using Umbraco.Web.Common.ApplicationModels;
using Umbraco.Web.Common.Attributes;
namespace Umbraco.Web.Common.Filters
{
/// <summary>
/// Assigned as part of the umbraco back office application model <see cref="UmbracoApiBehaviorApplicationModelProvider"/>
/// to always ensure that back office authentication occurs for all controller/actions with
/// <see cref="IsBackOfficeAttribute"/> applied.
/// </summary>
public class EnsureUmbracoBackOfficeAuthentication : IAuthorizationFilter, IAuthorizeData
{
// Implements IAuthorizeData only to return the back office scheme
public string AuthenticationSchemes { get; set; } = Umbraco.Core.Constants.Security.BackOfficeAuthenticationType;
public string Policy { get; set; }
public string Roles { get; set; }
public void OnAuthorization(AuthorizationFilterContext context)
{
}
}
}

View File

@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Umbraco.Web.Common.Filters
{

View File

@@ -12,6 +12,8 @@ using IHostingEnvironment = Umbraco.Core.Hosting.IHostingEnvironment;
namespace Umbraco.Web.Common.Filters
{
/// <summary>
/// Ensures authorization is successful for a back office user.
/// </summary>