parent
3a2d09b066
commit
33e3001d53
|
|
@ -2,7 +2,6 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Html;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Antiforgery
|
namespace Microsoft.AspNetCore.Antiforgery
|
||||||
|
|
@ -13,20 +12,6 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IAntiforgery
|
public interface IAntiforgery
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Generates an <input type="hidden"> element for an antiforgery token.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// A <see cref="IHtmlContent"/> containing an <input type="hidden"> element. This element should be put
|
|
||||||
/// inside a <form>.
|
|
||||||
/// </returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// This method has a side effect:
|
|
||||||
/// A response cookie is set if there is no valid cookie associated with the request.
|
|
||||||
/// </remarks>
|
|
||||||
IHtmlContent GetHtml(HttpContext httpContext);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates an <see cref="AntiforgeryTokenSet"/> for this request and stores the cookie token
|
/// Generates an <see cref="AntiforgeryTokenSet"/> for this request and stores the cookie token
|
||||||
/// in the response.
|
/// in the response.
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
|
||||||
using System.Text.Encodings.Web;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Html;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
|
@ -35,20 +32,6 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
_tokenStore = tokenStore;
|
_tokenStore = tokenStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IHtmlContent GetHtml(HttpContext httpContext)
|
|
||||||
{
|
|
||||||
if (httpContext == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(httpContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckSSLConfig(httpContext);
|
|
||||||
|
|
||||||
var tokenSet = GetAndStoreTokens(httpContext);
|
|
||||||
return new InputContent(_options.FormFieldName, tokenSet.RequestToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public AntiforgeryTokenSet GetAndStoreTokens(HttpContext httpContext)
|
public AntiforgeryTokenSet GetAndStoreTokens(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
|
|
@ -307,42 +290,5 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
|
|
||||||
public bool IsNewCookieToken { get; set; }
|
public bool IsNewCookieToken { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InputContent : IHtmlContent
|
|
||||||
{
|
|
||||||
private readonly string _fieldName;
|
|
||||||
private readonly string _requestToken;
|
|
||||||
|
|
||||||
public InputContent(string fieldName, string requestToken)
|
|
||||||
{
|
|
||||||
_fieldName = fieldName;
|
|
||||||
_requestToken = requestToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Though _requestToken normally contains only US-ASCII letters, numbers, '-', and '_', must assume the
|
|
||||||
// IAntiforgeryTokenSerializer implementation has been overridden. Similarly, users may choose a
|
|
||||||
// _fieldName containing almost any character.
|
|
||||||
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
|
|
||||||
{
|
|
||||||
var builder = writer as IHtmlContentBuilder;
|
|
||||||
if (builder != null)
|
|
||||||
{
|
|
||||||
// If possible, defer encoding until we're writing to the response.
|
|
||||||
// But there's little reason to keep this IHtmlContent instance around.
|
|
||||||
builder
|
|
||||||
.AppendHtml("<input name=\"")
|
|
||||||
.Append(_fieldName)
|
|
||||||
.AppendHtml("\" type=\"hidden\" value=\"")
|
|
||||||
.Append(_requestToken)
|
|
||||||
.AppendHtml("\" />");
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write("<input name=\"");
|
|
||||||
encoder.Encode(writer, _fieldName);
|
|
||||||
writer.Write("\" type=\"hidden\" value=\"");
|
|
||||||
encoder.Encode(writer, _requestToken);
|
|
||||||
writer.Write("\" />");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.AspNetCore.DataProtection": "1.0.0-*",
|
"Microsoft.AspNetCore.DataProtection": "1.0.0-*",
|
||||||
"Microsoft.AspNetCore.Html.Abstractions": "1.0.0-*",
|
|
||||||
"Microsoft.AspNetCore.Http.Abstractions": "1.0.0-*",
|
"Microsoft.AspNetCore.Http.Abstractions": "1.0.0-*",
|
||||||
"Microsoft.AspNetCore.WebUtilities": "1.0.0-*",
|
"Microsoft.AspNetCore.WebUtilities": "1.0.0-*",
|
||||||
"Microsoft.Extensions.ObjectPool": "1.0.0-*"
|
"Microsoft.Extensions.ObjectPool": "1.0.0-*"
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,12 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Internal;
|
using Microsoft.AspNetCore.Http.Internal;
|
||||||
using Microsoft.AspNetCore.Testing;
|
using Microsoft.AspNetCore.Testing;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.Extensions.WebEncoders.Testing;
|
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -81,26 +79,6 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
exception.Message);
|
exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ChecksSSL_GetHtml_Throws()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var httpContext = new DefaultHttpContext();
|
|
||||||
var options = new AntiforgeryOptions()
|
|
||||||
{
|
|
||||||
RequireSsl = true
|
|
||||||
};
|
|
||||||
|
|
||||||
var antiforgery = GetAntiforgery(options);
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
var exception = Assert.Throws<InvalidOperationException>(() => antiforgery.GetHtml(httpContext));
|
|
||||||
Assert.Equal(
|
|
||||||
@"The antiforgery system has the configuration value AntiforgeryOptions.RequireSsl = true, " +
|
|
||||||
"but the current request is not an SSL request.",
|
|
||||||
exception.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ChecksSSL_GetAndStoreTokens_Throws()
|
public void ChecksSSL_GetAndStoreTokens_Throws()
|
||||||
{
|
{
|
||||||
|
|
@ -164,136 +142,6 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
exception.Message);
|
exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetHtml_ExistingInvalidCookieToken_GeneratesANewCookieAndAnAntiforgeryToken()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var options = new AntiforgeryOptions()
|
|
||||||
{
|
|
||||||
FormFieldName = "form-field-name"
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make sure the existing cookie is invalid.
|
|
||||||
var context = CreateMockContext(options, isOldCookieValid: false);
|
|
||||||
var antiforgery = GetAntiforgery(context);
|
|
||||||
var encoder = new HtmlTestEncoder();
|
|
||||||
|
|
||||||
// Setup so that the null cookie token returned is treated as invalid.
|
|
||||||
context.TokenGenerator
|
|
||||||
.Setup(o => o.IsCookieTokenValid(null))
|
|
||||||
.Returns(false);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var inputElement = antiforgery.GetHtml(context.HttpContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
using (var writer = new StringWriter())
|
|
||||||
{
|
|
||||||
inputElement.WriteTo(writer, encoder);
|
|
||||||
|
|
||||||
Assert.Equal(
|
|
||||||
@"<input name=""HtmlEncode[[form-field-name]]"" type=""hidden"" " +
|
|
||||||
@"value=""HtmlEncode[[serialized-form-token]]"" />",
|
|
||||||
writer.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
context.TokenStore.Verify();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetHtml_ExistingInvalidCookieToken_SwallowsExceptions()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var options = new AntiforgeryOptions()
|
|
||||||
{
|
|
||||||
FormFieldName = "form-field-name"
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make sure the existing cookie is invalid.
|
|
||||||
var context = CreateMockContext(options, isOldCookieValid: false);
|
|
||||||
var antiforgery = GetAntiforgery(context);
|
|
||||||
|
|
||||||
// This will cause the cookieToken to be null.
|
|
||||||
context.TokenStore
|
|
||||||
.Setup(o => o.GetCookieToken(context.HttpContext))
|
|
||||||
.Throws(new Exception("should be swallowed"));
|
|
||||||
|
|
||||||
// Setup so that the null cookie token returned is treated as invalid.
|
|
||||||
context.TokenGenerator
|
|
||||||
.Setup(o => o.IsCookieTokenValid(null))
|
|
||||||
.Returns(false);
|
|
||||||
|
|
||||||
var encoder = new HtmlTestEncoder();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var inputElement = antiforgery.GetHtml(context.HttpContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
using (var writer = new StringWriter())
|
|
||||||
{
|
|
||||||
inputElement.WriteTo(writer, encoder);
|
|
||||||
|
|
||||||
Assert.Equal(
|
|
||||||
@"<input name=""HtmlEncode[[form-field-name]]"" type=""hidden"" " +
|
|
||||||
@"value=""HtmlEncode[[serialized-form-token]]"" />",
|
|
||||||
writer.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
context.TokenStore.Verify();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetHtml_ExistingValidCookieToken_GeneratesAnAntiforgeryToken()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var options = new AntiforgeryOptions()
|
|
||||||
{
|
|
||||||
FormFieldName = "form-field-name"
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make sure the existing cookie is valid and use the same cookie for the mock Token Provider.
|
|
||||||
var context = CreateMockContext(options, useOldCookie: true, isOldCookieValid: true);
|
|
||||||
var antiforgery = GetAntiforgery(context);
|
|
||||||
var encoder = new HtmlTestEncoder();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var inputElement = antiforgery.GetHtml(context.HttpContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
using (var writer = new StringWriter())
|
|
||||||
{
|
|
||||||
inputElement.WriteTo(writer, encoder);
|
|
||||||
|
|
||||||
Assert.Equal(
|
|
||||||
@"<input name=""HtmlEncode[[form-field-name]]"" type=""hidden"" " +
|
|
||||||
@"value=""HtmlEncode[[serialized-form-token]]"" />",
|
|
||||||
writer.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(false, "SAMEORIGIN")]
|
|
||||||
[InlineData(true, null)]
|
|
||||||
public void GetHtml_AddsXFrameOptionsHeader(bool suppressXFrameOptions, string expectedHeaderValue)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var options = new AntiforgeryOptions()
|
|
||||||
{
|
|
||||||
SuppressXFrameOptionsHeader = suppressXFrameOptions
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generate a new cookie.
|
|
||||||
var context = CreateMockContext(options, useOldCookie: false, isOldCookieValid: false);
|
|
||||||
var antiforgery = GetAntiforgery(context);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
antiforgery.GetHtml(context.HttpContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
string xFrameOptions = context.HttpContext.Response.Headers["X-Frame-Options"];
|
|
||||||
Assert.Equal(expectedHeaderValue, xFrameOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetTokens_ExistingInvalidCookieToken_GeneratesANewCookieTokenAndANewFormToken()
|
public void GetTokens_ExistingInvalidCookieToken_GeneratesANewCookieTokenAndANewFormToken()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue