Always render antiforgery tokens if `!CanRenderAtEndOfForm`
- #5005 - also add `FormContext` doc comments
This commit is contained in:
parent
581a5ea573
commit
8a6e99c7c0
|
|
@ -156,15 +156,19 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
throw new ArgumentNullException(nameof(viewContext));
|
throw new ArgumentNullException(nameof(viewContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're inside a BeginForm/BeginRouteForm, the antiforgery token might have already been
|
var formContext = viewContext.FormContext;
|
||||||
// created and appended to the 'end form' content OR the form tag helper might have already generated
|
if (formContext.CanRenderAtEndOfForm)
|
||||||
// an antiforgery token.
|
|
||||||
if (viewContext.FormContext.HasAntiforgeryToken)
|
|
||||||
{
|
{
|
||||||
return HtmlString.Empty;
|
// Inside a BeginForm/BeginRouteForm or a <form> tag helper. So, the antiforgery token might have
|
||||||
}
|
// already been created and appended to the 'end form' content (the AntiForgeryToken HTML helper does
|
||||||
|
// this) OR the <form> tag helper might have already generated an antiforgery token.
|
||||||
|
if (formContext.HasAntiforgeryToken)
|
||||||
|
{
|
||||||
|
return HtmlString.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
viewContext.FormContext.HasAntiforgeryToken = true;
|
formContext.HasAntiforgeryToken = true;
|
||||||
|
}
|
||||||
|
|
||||||
return _antiforgery.GetHtml(viewContext.HttpContext);
|
return _antiforgery.GetHtml(viewContext.HttpContext);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,13 @@ using Microsoft.AspNetCore.Html;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Information about the current <form>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Literal <form> elements in a view will share the default <see cref="FormContext"/> instance unless tag
|
||||||
|
/// helpers are enabled.
|
||||||
|
/// </remarks>
|
||||||
public class FormContext
|
public class FormContext
|
||||||
{
|
{
|
||||||
private Dictionary<string, bool> _renderedFields;
|
private Dictionary<string, bool> _renderedFields;
|
||||||
|
|
@ -14,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
private IList<IHtmlContent> _endOfFormContent;
|
private IList<IHtmlContent> _endOfFormContent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Property bag for any information you wish to associate with a <form/> in an
|
/// Gets a property bag for any information you wish to associate with a <form/> in an
|
||||||
/// <see cref="Rendering.IHtmlHelper"/> implementation or extension method.
|
/// <see cref="Rendering.IHtmlHelper"/> implementation or extension method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IDictionary<string, object> FormData
|
public IDictionary<string, object> FormData
|
||||||
|
|
@ -30,12 +37,36 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets an indication the current <form> element contains an antiforgery token. Do not use
|
||||||
|
/// unless <see cref="CanRenderAtEndOfForm"/> is <c>true</c>.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// <c>true</c> if the current <form> element contains an antiforgery token; <c>false</c> otherwise.
|
||||||
|
/// </value>
|
||||||
public bool HasAntiforgeryToken { get; set; }
|
public bool HasAntiforgeryToken { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an indication the <see cref="FormData"/> property bag has been used and likely contains entries.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// <c>true</c> if the backing field for <see cref="FormData"/> is non-<c>null</c>; <c>false</c> otherwise.
|
||||||
|
/// </value>
|
||||||
public bool HasFormData => _formData != null;
|
public bool HasFormData => _formData != null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an indication the <see cref="EndOfFormContent"/> collection has been used and likely contains entries.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// <c>true</c> if the backing field for <see cref="EndOfFormContent"/> is non-<c>null</c>; <c>false</c>
|
||||||
|
/// otherwise.
|
||||||
|
/// </value>
|
||||||
public bool HasEndOfFormContent => _endOfFormContent != null;
|
public bool HasEndOfFormContent => _endOfFormContent != null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an <see cref="IHtmlContent"/> collection that should be rendered just prior to the next </form>
|
||||||
|
/// end tag. Do not use unless <see cref="CanRenderAtEndOfForm"/> is <c>true</c>.
|
||||||
|
/// </summary>
|
||||||
public IList<IHtmlContent> EndOfFormContent
|
public IList<IHtmlContent> EndOfFormContent
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
@ -49,8 +80,22 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets an indication whether extra content can be rendered at the end of the content of this
|
||||||
|
/// <form> element. That is, <see cref="EndOfFormContent"/> will be rendered just prior to the
|
||||||
|
/// </form> end tag.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// <c>true</c> if the framework will render <see cref="EndOfFormContent"/>; <c>false</c> otherwise. In
|
||||||
|
/// particular, <c>true</c> if the current <form> is associated with a tag helper or will be generated by
|
||||||
|
/// an HTML helper; <c>false</c> when using the default <see cref="FormContext"/> instance.
|
||||||
|
/// </value>
|
||||||
public bool CanRenderAtEndOfForm { get; set; }
|
public bool CanRenderAtEndOfForm { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a dictionary mapping full HTML field names to indications that the named field has been rendered in
|
||||||
|
/// this <form>.
|
||||||
|
/// </summary>
|
||||||
private Dictionary<string, bool> RenderedFields
|
private Dictionary<string, bool> RenderedFields
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
@ -64,6 +109,14 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an indication based on <see cref="RenderedFields"/> that the given <paramref name="fieldName"/> has
|
||||||
|
/// been rendered in this <form>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fieldName">The full HTML name of a field that may have been rendered.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the given <paramref name="fieldName"/> has been rendered; <c>false</c> otherwise.
|
||||||
|
/// </returns>
|
||||||
public bool RenderedField(string fieldName)
|
public bool RenderedField(string fieldName)
|
||||||
{
|
{
|
||||||
if (fieldName == null)
|
if (fieldName == null)
|
||||||
|
|
@ -77,6 +130,12 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates <see cref="RenderedFields"/> to indicate <paramref name="fieldName"/> has been rendered in this
|
||||||
|
/// <form>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fieldName">The full HTML name of a field that may have been rendered.</param>
|
||||||
|
/// <param name="value">If <c>true</c>, the given <paramref name="fieldName"/> has been rendered.</param>
|
||||||
public void RenderedField(string fieldName, bool value)
|
public void RenderedField(string fieldName, bool value)
|
||||||
{
|
{
|
||||||
if (fieldName == null)
|
if (fieldName == null)
|
||||||
|
|
|
||||||
|
|
@ -641,6 +641,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var htmlGenerator = GetGenerator(metadataProvider);
|
var htmlGenerator = GetGenerator(metadataProvider);
|
||||||
var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider);
|
var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider);
|
||||||
|
viewContext.FormContext.CanRenderAtEndOfForm = true;
|
||||||
viewContext.FormContext.HasAntiforgeryToken = hasAntiforgeryToken;
|
viewContext.FormContext.HasAntiforgeryToken = hasAntiforgeryToken;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -651,6 +652,30 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
Assert.Equal(expectedAntiforgeryHtmlField, antiforgeryField);
|
Assert.Equal(expectedAntiforgeryHtmlField, antiforgeryField);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test covers use of the helper within literal <form> tags when tag helpers are not enabled e.g.
|
||||||
|
// <form action="/Home/Create">
|
||||||
|
// @Html.AntiForgeryToken()
|
||||||
|
// </form>
|
||||||
|
[Theory]
|
||||||
|
[InlineData(false)]
|
||||||
|
[InlineData(true)]
|
||||||
|
public void GenerateAntiforgery_AlwaysGeneratesAntiforgeryToken_IfCannotRenderAtEnd(bool hasAntiforgeryToken)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = "<input name=\"formFieldName\" type=\"hidden\" value=\"requestToken\" />";
|
||||||
|
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
var htmlGenerator = GetGenerator(metadataProvider);
|
||||||
|
var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider);
|
||||||
|
viewContext.FormContext.HasAntiforgeryToken = hasAntiforgeryToken;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = htmlGenerator.GenerateAntiforgery(viewContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var antiforgeryField = HtmlContentUtilities.HtmlContentToString(result, HtmlEncoder.Default);
|
||||||
|
Assert.Equal(expected, antiforgeryField);
|
||||||
|
}
|
||||||
|
|
||||||
// GetCurrentValues uses only the IModelMetadataProvider passed to the DefaultHtmlGenerator constructor.
|
// GetCurrentValues uses only the IModelMetadataProvider passed to the DefaultHtmlGenerator constructor.
|
||||||
private static IHtmlGenerator GetGenerator(IModelMetadataProvider metadataProvider)
|
private static IHtmlGenerator GetGenerator(IModelMetadataProvider metadataProvider)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue