Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Change FormTagHelper to apply to all form tags. #6156

Merged
merged 1 commit into from Apr 25, 2017
Merged

Conversation

NTaylorMullen
Copy link
Member

  • Added functional test to validate that non-attributed form tags have an antiforgery input generated. Re-generated baseline to reflect changes.
  • Added a unit test to validate that parameterless FormTagHelpers behave as expected.

#6006

TagBuilder tagBuilder;
if (Route == null)
TagBuilder tagBuilder = null;
if (Action == null && Controller == null && Route == null && _routeValues == null && Fragment == null && Area == null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there ever be a case where _routeValues is non-null but empty? What gets generated in that case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that could happen if a user did <form asp-all-route-data="new Dictionary<string, string>()">. In that case the user is being explicit and providing attributes; we'd want to follow the normal FormTagHelper rendering path.

Copy link
Member

@dougbu dougbu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will look at a few more things later...

@@ -1,4 +1,4 @@
<html>
<html>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No clue. :thuglife:

// null which is interpreted as true unless element includes an action attribute.
services.AddMvc().InitializeTagHelper<FormTagHelper>((helper, _) => helper.Antiforgery = false);
// Add MVC services to the services container.
services.AddMvc();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Undo this. The site is intentionally testing e2e behaviour with the Antiforgery property set to false.

<form></form>
<form action="/HtmlGeneration_Home/Form" method="post"></form>
<form><input name="__RequestVerificationToken" type="hidden" value="{0}" /></form>
<form action="/HtmlGeneration_Home/Form" method="post"><input name="__RequestVerificationToken" type="hidden" value="{0}" /></form>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazed we only have two <form> elements without attributes we recognized. That's sad.

metadataProvider: metadataProvider);
var expectedPostContent = HtmlContentUtilities.HtmlContentToString(
htmlGenerator.GenerateAntiforgery(viewContext),
HtmlEncoder.Default);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

de-indent

// Assert
Assert.Empty(output.Attributes);
Assert.Empty(output.PreContent.GetContent());
Assert.True(output.Content.GetContent().Length == 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert.Empty(output.Content.GetContent())

Assert.Empty(output.Attributes);
Assert.Empty(output.PreContent.GetContent());
Assert.True(output.Content.GetContent().Length == 0);
Assert.Equal(expectedPostContent, output.PostContent.GetContent());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no confirmation that PreElement and PostElement remain empty?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just staying consistent with the other tests. I'll add though.

@NTaylorMullen
Copy link
Member Author

🆙 📅

{
if (string.Equals(attribute.Name.LocalName, "action", StringComparison.OrdinalIgnoreCase))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree confirming the action attribute exists was useless. Nice your new tests found that 👍

#endif
}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blankety Blank Blank?

@@ -0,0 +1,5 @@
<form><input name="__RequestVerificationToken" type="hidden" value="{0}" /></form>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a bug here. The default method is get and so no antiforgery token should be added by default. Line 248 in the tag helper needs to handle null or empty too.

<form method="get"></form>
<form method="post"><input name="__RequestVerificationToken" type="hidden" value="{0}" /></form>
<form action="/Foo/Bar/Baz.html" method="get"></form>
<form action="/Foo/Bar/Baz.html" method="post"></form>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tag helper should have added an antiforgery token, changing the behaviour when user includes action attribute. Need to make a change around line 168 in the tag helper to match the updated line 248.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Talked with Damian about this. Since the user specified action, they're opting out of our TagHelper ways. Therefore, don't generate a token.

[HtmlTargetElement("form", Attributes = ControllerAttributeName)]
[HtmlTargetElement("form", Attributes = RouteAttributeName)]
[HtmlTargetElement("form", Attributes = RouteValuesDictionaryName)]
[HtmlTargetElement("form", Attributes = RouteValuesPrefix + "*")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's be explicit rather than use the naming convention to target <form> elements: Stick with [HtmlTargetElement("form")].

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx

@NTaylorMullen
Copy link
Member Author

🆙 📅

<form method="get"></form>
<form method="post"><input name="__RequestVerificationToken" type="hidden" value="{0}" /></form>
<form action="/Foo/Bar/Baz.html" method="get"></form>
<form action="/Foo/Bar/Baz.html" method="post"></form>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dougbu

Tag helper should have added an antiforgery token, changing the behaviour when user includes action attribute. Need to make a change around line 168 in the tag helper to match the updated line 248.

@NTaylorMullen

Talked with Damian about this. Since the user specified action, they're opting out of our TagHelper ways. Therefore, don't generate a token.

Let's chat quickly tomorrow. I agree the user has opted out of the tag helper generating an action in this case. But, I'm not sure why <form action="" method="post"> is any more an opt out of antiforgery than <form method="post"> is -- both submit the same data to the same URL.

@NTaylorMullen
Copy link
Member Author

🆙 📅

Copy link
Member

@dougbu dougbu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few small comments. But, let's wait for Monday to get this in.

{
// User is using the FormTagHelper like a normal <form> tag that has an empty action attribute.
// i.e. <form action="" method="post">
antiforgeryDefault = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Don't set antiforgeryDefault to its default again.
  • Mention the action="@Url.Action(action: "Index", controller: "Home") possibility in the comments.
  • nit: "e.g." not "i.e." -- don't know anything more than action isn't "/A/Non-empty/String".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's special about the @Url.Action case? It's also an HtmlString

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shoot. It actually returns a plain string. I thought (hoped) it avoided concatenation and returned a more-segmented IHtmlContent.

Just mention the non-string / non-HtmlString possibility. It's not necessarily an empty string or HtmlString.Empty.

{
// User is using the FormTagHelper like a normal <form> tag. Antiforgery default should be false to
// not force the antiforgery token on the user.
antiforgeryDefault = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we leave this alone (pending Monday's discussion), update the comments to better distinguish things from the if case e.g. User is likely using the <form> element to submit to another site. Do not send an antiforgery token to unknown sites.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting this in. If the decision is changed we can account for that afterwards.

@@ -4,6 +4,8 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What needs System.IO?

- Added functional test to validate that non-attributed form tags have an antiforgery input generated. Re-generated baseline to reflect changes.
- Added a unit test to validate that parameterless `FormTagHelper`s behave as expected.

#6006
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants