-
Notifications
You must be signed in to change notification settings - Fork 10.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
315 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Http.Abstractions.Metadata; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace Microsoft.AspNetCore.Antiforgery; | ||
|
||
internal sealed partial class AntiforgeryMiddleware | ||
{ | ||
private readonly IAntiforgery _antiforgery; | ||
private readonly RequestDelegate _next; | ||
private readonly ILogger<AntiforgeryMiddleware> _logger; | ||
|
||
public AntiforgeryMiddleware(IAntiforgery antiforgery, RequestDelegate next, ILogger<AntiforgeryMiddleware> logger) | ||
{ | ||
_antiforgery = antiforgery; | ||
_next = next; | ||
_logger = logger; | ||
} | ||
|
||
public Task Invoke(HttpContext context) | ||
{ | ||
var endpoint = context.GetEndpoint(); | ||
if (endpoint is null) | ||
{ | ||
return _next(context); | ||
} | ||
|
||
var antiforgeryMetadata = endpoint.Metadata.GetMetadata<IAntiforgeryMetadata>(); | ||
if (antiforgeryMetadata is null) | ||
{ | ||
Log.NoAntiforgeryMetadataFound(_logger); | ||
return _next(context); | ||
} | ||
|
||
if (antiforgeryMetadata is not IValidateAntiforgeryMetadata validateAntiforgeryMetadata) | ||
{ | ||
Log.IgnoreAntiforgeryMetadataFound(_logger); | ||
return _next(context); | ||
} | ||
|
||
if (_antiforgery is DefaultAntiforgery defaultAntiforgery) | ||
{ | ||
var valueTask = defaultAntiforgery.TryValidateAsync(context, validateAntiforgeryMetadata.ValidateIdempotentRequests); | ||
if (valueTask.IsCompletedSuccessfully) | ||
{ | ||
var (success, message) = valueTask.GetAwaiter().GetResult(); | ||
if (success) | ||
{ | ||
Log.AntiforgeryValidationSucceeded(_logger); | ||
return _next(context); | ||
} | ||
else | ||
{ | ||
Log.AntiforgeryValidationFailed(_logger, message); | ||
return WriteAntiforgeryInvalidResponseAsync(context, message); | ||
} | ||
} | ||
|
||
return TryValidateAsyncAwaited(context, valueTask); | ||
} | ||
else | ||
{ | ||
return ValidateNonDefaultAntiforgery(context); | ||
} | ||
} | ||
|
||
private async Task TryValidateAsyncAwaited(HttpContext context, ValueTask<(bool success, string? message)> tryValidateTask) | ||
{ | ||
var (success, message) = await tryValidateTask; | ||
if (success) | ||
{ | ||
Log.AntiforgeryValidationSucceeded(_logger); | ||
await _next(context); | ||
} | ||
else | ||
{ | ||
Log.AntiforgeryValidationFailed(_logger, message); | ||
await context.Response.WriteAsJsonAsync(new ProblemDetails | ||
{ | ||
Status = StatusCodes.Status400BadRequest, | ||
Title = "Antiforgery validation failed", | ||
Detail = message, | ||
}); | ||
} | ||
} | ||
|
||
private async Task ValidateNonDefaultAntiforgery(HttpContext context) | ||
{ | ||
if (await _antiforgery.IsRequestValidAsync(context)) | ||
{ | ||
Log.AntiforgeryValidationSucceeded(_logger); | ||
await _next(context); | ||
} | ||
else | ||
{ | ||
Log.AntiforgeryValidationFailed(_logger, message: null); | ||
await WriteAntiforgeryInvalidResponseAsync(context, message: null); | ||
} | ||
} | ||
|
||
private static Task WriteAntiforgeryInvalidResponseAsync(HttpContext context, string? message) | ||
{ | ||
context.Response.StatusCode = StatusCodes.Status400BadRequest; | ||
return context.Response.WriteAsJsonAsync(new ProblemDetails | ||
{ | ||
Status = StatusCodes.Status400BadRequest, | ||
Title = "Antiforgery validation failed", | ||
Detail = message, | ||
}); | ||
} | ||
|
||
private static partial class Log | ||
{ | ||
[LoggerMessage(1, LogLevel.Debug, "No antiforgery metadata found on the endpoint.", EventName = "NoAntiforgeryMetadataFound")] | ||
public static partial void NoAntiforgeryMetadataFound(ILogger logger); | ||
|
||
[LoggerMessage(2, LogLevel.Debug, $"Antiforgery validation suppressed on endpoint because {nameof(IValidateAntiforgeryMetadata)} was not found.", EventName = "IgnoreAntiforgeryMetadataFound")] | ||
public static partial void IgnoreAntiforgeryMetadataFound(ILogger logger); | ||
|
||
[LoggerMessage(3, LogLevel.Debug, "Antiforgery validation completed successfully.", EventName = "AntiforgeryValidationSucceeded")] | ||
public static partial void AntiforgeryValidationSucceeded(ILogger logger); | ||
|
||
[LoggerMessage(4, LogLevel.Debug, "Antiforgery validation failed with message '{message}'.", EventName = "AntiforgeryValidationFailed")] | ||
public static partial void AntiforgeryValidationFailed(ILogger logger, string? message); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using Microsoft.AspNetCore.Antiforgery; | ||
using Microsoft.AspNetCore.Cors.Infrastructure; | ||
|
||
namespace Microsoft.AspNetCore.Builder; | ||
|
||
/// <summary> | ||
/// The <see cref="IApplicationBuilder"/> extensions for adding Antiforgery middleware support. | ||
/// </summary> | ||
public static class AntiforgeryMiddlewareExtensions | ||
{ | ||
/// <summary> | ||
/// Adds the Antiforgery middleware to the middleware pipeline. | ||
/// </summary> | ||
/// <param name="app">The <see cref="IApplicationBuilder"/>.</param> | ||
/// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns> | ||
public static IApplicationBuilder UseAntiforgery(this IApplicationBuilder app) | ||
=> app.UseMiddleware<AntiforgeryMiddleware>(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
#nullable enable | ||
Microsoft.AspNetCore.Builder.AntiforgeryMiddlewareExtensions | ||
static Microsoft.AspNetCore.Builder.AntiforgeryMiddlewareExtensions.UseAntiforgery(this Microsoft.AspNetCore.Builder.IApplicationBuilder! app) -> Microsoft.AspNetCore.Builder.IApplicationBuilder! |
11 changes: 11 additions & 0 deletions
11
src/Http/Http.Abstractions/src/Metadata/IAntiforgeryMetadata.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
namespace Microsoft.AspNetCore.Http.Abstractions.Metadata; | ||
|
||
/// <summary> | ||
/// A marker interface which can be used to identify Antiforgery metadata. | ||
/// </summary> | ||
public interface IAntiforgeryMetadata | ||
{ | ||
} |
15 changes: 15 additions & 0 deletions
15
src/Http/Http.Abstractions/src/Metadata/IValidateAntiforgeryMetadata.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
namespace Microsoft.AspNetCore.Http.Abstractions.Metadata; | ||
|
||
/// <summary> | ||
/// A marker interface which can be used to identify a resource with Antiforgery validation enabled. | ||
/// </summary> | ||
public interface IValidateAntiforgeryMetadata : IAntiforgeryMetadata | ||
{ | ||
/// <summary> | ||
/// Gets a value that determines if idempotent HTTP methods (<c>GET</c>, <c>HEAD</c>, <c>OPTIONS</c> and <c>TRACE</c>) are validated. | ||
/// </summary> | ||
bool ValidateIdempotentRequests { get; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
#nullable enable | ||
*REMOVED*abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string! | ||
Microsoft.AspNetCore.Http.Abstractions.Metadata.IAntiforgeryMetadata | ||
Microsoft.AspNetCore.Http.Abstractions.Metadata.IValidateAntiforgeryMetadata | ||
Microsoft.AspNetCore.Http.Abstractions.Metadata.IValidateAntiforgeryMetadata.ValidateIdempotentRequests.get -> bool | ||
abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string? |
59 changes: 59 additions & 0 deletions
59
src/Mvc/Mvc.Core/src/ApplicationModels/AntiforgeryApplicationModelProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Linq; | ||
using Microsoft.AspNetCore.Http.Abstractions.Metadata; | ||
using Microsoft.AspNetCore.Mvc.Filters; | ||
using Microsoft.Extensions.Options; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels; | ||
|
||
/// <summary> | ||
/// An <see cref="IApplicationModelProvider"/> that removes antiforgery filters that appears as endpoint metadata. | ||
/// </summary> | ||
internal sealed class AntiforgeryApplicationModelProvider : IApplicationModelProvider | ||
{ | ||
private readonly MvcOptions _mvcOptions; | ||
|
||
public AntiforgeryApplicationModelProvider(IOptions<MvcOptions> mvcOptions) | ||
{ | ||
_mvcOptions = mvcOptions.Value; | ||
} | ||
|
||
// Run late in the pipeline so that we can pick up user configured AntiforgeryTokens. | ||
public int Order { get; } = 1000; | ||
|
||
public void OnProvidersExecuted(ApplicationModelProviderContext context) | ||
{ | ||
} | ||
|
||
public void OnProvidersExecuting(ApplicationModelProviderContext context) | ||
{ | ||
if (!_mvcOptions.EnableEndpointRouting) | ||
{ | ||
return; | ||
} | ||
|
||
foreach (var controller in context.Result.Controllers) | ||
{ | ||
RemoveAntiforgeryFilters(controller.Filters, controller.Selectors); | ||
|
||
foreach (var action in controller.Actions) | ||
{ | ||
RemoveAntiforgeryFilters(action.Filters, action.Selectors); | ||
} | ||
} | ||
} | ||
|
||
private static void RemoveAntiforgeryFilters(IList<IFilterMetadata> filters, IList<SelectorModel> selectorModels) | ||
{ | ||
for (var i = filters.Count - 1; i >= 0; i--) | ||
{ | ||
if (filters[i] is IAntiforgeryMetadata antiforgeryMetadata && | ||
selectorModels.All(s => s.EndpointMetadata.Contains(antiforgeryMetadata))) | ||
{ | ||
filters.RemoveAt(i); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.