Merge pull request #9150 from umbraco/netcore/feature/migrate-authorizedApiController-attributes

Netcore: Migrate authorized api controller attributes
This commit is contained in:
Bjarke Berg
2020-10-14 06:56:23 +02:00
committed by GitHub
11 changed files with 109 additions and 111 deletions

View File

@@ -1,4 +1,5 @@
using Umbraco.Web.BackOffice.Filters;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Common.Controllers;
using Umbraco.Web.Common.Filters;
@@ -14,13 +15,12 @@ namespace Umbraco.Web.BackOffice.Controllers
/// before their timeout expires.
/// </remarks>
[IsBackOffice]
//[UmbracoUserTimeoutFilter] //TODO reintroduce
[UmbracoUserTimeoutFilter]
[UmbracoAuthorize]
[DisableBrowserCache]
[UmbracoWebApiRequireHttps]
[CheckIfUserTicketDataIsStale]
//[UnhandedExceptionLoggerConfiguration] //TODO reintroduce
//[EnableDetailedErrors] //TODO reintroduce
[MiddlewareFilter(typeof(UnhandledExceptionLoggerFilter))]
public abstract class UmbracoAuthorizedApiController : UmbracoApiController
{

View File

@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Builder;
namespace Umbraco.Web.BackOffice.Filters
{
/// <summary>
/// Applies the UnhandledExceptionLoggerMiddleware to a specific controller
/// when used with this attribute [MiddlewareFilter(typeof(UnhandledExceptionLoggerFilter))]
/// The middleware will run in the filter pipeline, at the same stage as resource filters
/// </summary>
public class UnhandledExceptionLoggerFilter
{
public void Configure(IApplicationBuilder applicationBuilder)
{
applicationBuilder.UseMiddleware<UnhandledExceptionLoggerMiddleware>();
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Logging;
using Umbraco.Core;
namespace Umbraco.Web.BackOffice.Filters
{
/// <summary>
/// Logs any unhandled exception.
/// </summary>
public class UnhandledExceptionLoggerMiddleware : IMiddleware
{
private readonly ILogger<UnhandledExceptionLoggerMiddleware> _logger;
public UnhandledExceptionLoggerMiddleware(ILogger<UnhandledExceptionLoggerMiddleware> logger)
{
_logger = logger;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var requestUri = new Uri(context.Request.GetEncodedUrl(), UriKind.RelativeOrAbsolute);
// If it's a client side request just call next and don't try to log anything
if (requestUri.IsClientSideRequest())
{
await next(context);
}
else
{
// Call the next middleware, and catch any errors that occurs in the rest of the pipeline
try
{
await next(context);
}
catch (Exception e)
{
_logger.LogError(e, "Unhandled controller exception occurred for request '{RequestUrl}'", requestUri.AbsoluteUri);
// Throw the error again, just in case it gets handled
throw;
}
}
}
}
}

View File

@@ -7,6 +7,7 @@ using Umbraco.Core.IO;
using Umbraco.Core.Services;
using Umbraco.Extensions;
using Umbraco.Web.BackOffice.Controllers;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.BackOffice.Routing;
using Umbraco.Web.BackOffice.Security;
using Umbraco.Web.BackOffice.Services;
@@ -48,6 +49,7 @@ namespace Umbraco.Web.BackOffice.Runtime
"~/"));
composition.RegisterUnique<IIconService, IconService>();
composition.RegisterUnique<UnhandledExceptionLoggerMiddleware>();
}
}
}

View File

@@ -275,7 +275,7 @@ namespace Umbraco.Extensions
loggingConfiguration,
configuration, out var ioHelper, out var hostingEnvironment, out var backOfficeInfo, out var profiler);
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
var loggerFactory = services.BuildServiceProvider().GetService<ILoggerFactory>();
var umbracoVersion = new UmbracoVersion();
var typeFinder = CreateTypeFinder(loggerFactory, profiler, webHostEnvironment, entryAssembly, typeFinderSettings);

View File

@@ -0,0 +1,36 @@
using System.Globalization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Umbraco.Extensions;
namespace Umbraco.Web.Common.Filters
{
/// <summary>
/// This will check if the user making the request is authenticated and if there's an auth ticket tied to the user
/// we will add a custom header to the response indicating how many seconds are remaining for the
/// user's session. This allows us to keep track of a user's session effectively in the back office.
/// </summary>
public class UmbracoUserTimeoutFilterAttribute : TypeFilterAttribute
{
public UmbracoUserTimeoutFilterAttribute() : base(typeof(UmbracoUserTimeoutFilter))
{
}
private class UmbracoUserTimeoutFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
//this can occur if an error has already occurred.
if (context.HttpContext.Response is null) return;
var remainingSeconds = context.HttpContext.User.GetRemainingAuthSeconds();
context.HttpContext.Response.Headers.Add("X-Umb-User-Seconds", remainingSeconds.ToString(CultureInfo.InvariantCulture));
}
public void OnActionExecuting(ActionExecutingContext context)
{
// Noop
}
}
}
}

View File

@@ -237,8 +237,6 @@
<Compile Include="WebApi\Filters\FeatureAuthorizeAttribute.cs" />
<Compile Include="WebApi\SessionHttpControllerRouteHandler.cs" />
<Compile Include="WebApi\UmbracoApiControllerTypeCollectionBuilder.cs" />
<Compile Include="WebApi\UnhandedExceptionLoggerConfigurationAttribute.cs" />
<Compile Include="WebApi\UnhandledExceptionLogger.cs" />
<Compile Include="Runtime\WebInitialComponent.cs" />
<Compile Include="Mvc\ControllerContextExtensions.cs" />
<Compile Include="Mvc\DisableBrowserCacheAttribute.cs" />
@@ -372,7 +370,6 @@
<Compile Include="WebApi\UmbracoAuthorizeAttribute.cs" />
<Compile Include="WebApi\UmbracoAuthorizedApiController.cs" />
<Compile Include="WebApi\Filters\ValidationFilterAttribute.cs" />
<Compile Include="WebApi\Filters\UmbracoUserTimeoutFilterAttribute.cs" />
<Compile Include="Mvc\ControllerExtensions.cs" />
<Compile Include="TypeLoaderExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs">

View File

@@ -1,36 +0,0 @@
using System;
using System.Globalization;
using System.Web.Http.Filters;
using Umbraco.Core.Security;
using Umbraco.Web.Security;
namespace Umbraco.Web.WebApi.Filters
{
/// <summary>
/// This will check if the request is authenticated and if there's an auth ticket present we will
/// add a custom header to the response indicating how many seconds are remaining for the current
/// user's session. This allows us to keep track of a user's session effectively in the back office.
/// </summary>
public sealed class UmbracoUserTimeoutFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
//this can occur if an error has already occurred.
if (actionExecutedContext.Response == null) return;
var httpContextAttempt = actionExecutedContext.Request.TryGetHttpContext();
if (httpContextAttempt.Success)
{
var ticket = httpContextAttempt.Result.GetUmbracoAuthTicket();
if (ticket?.Properties.ExpiresUtc != null && ticket.Properties.ExpiresUtc.Value < DateTimeOffset.UtcNow)
{
var remainingSeconds = httpContextAttempt.Result.GetRemainingAuthSeconds();
actionExecutedContext.Response.Headers.Add("X-Umb-User-Seconds", remainingSeconds.ToString(CultureInfo.InvariantCulture));
}
}
}
}
}

View File

@@ -21,12 +21,12 @@ namespace Umbraco.Web.WebApi
/// before their timeout expires.
/// </remarks>
[IsBackOffice]
[UmbracoUserTimeoutFilter]
// [UmbracoUserTimeoutFilter] has been migrated to netcore
[UmbracoAuthorize]
[DisableBrowserCache]
// [UmbracoWebApiRequireHttps]
// [CheckIfUserTicketDataIsStale]
[UnhandedExceptionLoggerConfiguration]
// [UnhandedExceptionLoggerConfiguration]
[EnableDetailedErrors]
public abstract class UmbracoAuthorizedApiController : UmbracoApiController
{

View File

@@ -1,29 +0,0 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.ExceptionHandling;
using System.Web.Http.Filters;
using Umbraco.Core;
using Umbraco.Core.Logging;
namespace Umbraco.Web.WebApi
{
/// <summary>
/// Adds our unhandled exception logger to the controller's services
/// </summary>
/// <remarks>
/// Important to note that the <see cref="UnhandledExceptionLogger"/> will only be called if the controller has an ExceptionFilter applied
/// to it, so to kill two birds with one stone, this class inherits from ExceptionFilterAttribute purely to force webapi to use the
/// IExceptionLogger (strange)
/// </remarks>
public class UnhandedExceptionLoggerConfigurationAttribute : ExceptionFilterAttribute, IControllerConfiguration
{
public virtual void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
{
controllerSettings.Services.Add(typeof(IExceptionLogger), new UnhandledExceptionLogger());
}
}
}

View File

@@ -1,36 +0,0 @@
using System.Web.Http.ExceptionHandling;
using Umbraco.Web.Composing;
using Microsoft.Extensions.Logging;
namespace Umbraco.Web.WebApi
{
/// <summary>
/// Used to log unhandled exceptions in webapi controllers
/// </summary>
public class UnhandledExceptionLogger : ExceptionLogger
{
private readonly ILogger _logger;
public UnhandledExceptionLogger()
: this(Current.Logger)
{
}
public UnhandledExceptionLogger(ILogger logger)
{
_logger = logger;
}
public override void Log(ExceptionLoggerContext context)
{
if (context != null && context.Exception != null)
{
var requestUrl = context.ExceptionContext?.ControllerContext?.Request?.RequestUri?.AbsoluteUri;
var controllerType = context.ExceptionContext?.ActionContext?.ControllerContext?.Controller?.GetType();
_logger.LogError(context.Exception, "Unhandled controller exception occurred for request '{RequestUrl}'", requestUrl);
}
}
}
}