Serialize cookie token at most once
- #23 part 3 - `Get[AndStore]Tokens()` would deserialize cookie token from request even if `IsRequestValidAsync()` already had - `GetAndStoreTokens()` serialized an old (never saved) cookie token once and a new one twice - refactor serialization from `DefaultAntiforgeryTokenStore` to `DefaultAntiforgery` - divide responsibilities and ease overall fix - above refactoring took `IAntiforgeryContextAccessor` responsibilities along to `DefaultAntiforgery` as well - store all tokens in `IAntiforgeryContextAccessor` to avoid repeated (de)serializations - remove `AntiforgeryTokenSetInternal` nits: - bit more parameter renaming to `httpContext` - remove argument checks in helper methods - did _not_ do a sweep through the repo; just files in this PR
This commit is contained in:
parent
c8a9ecc0c1
commit
73695fc443
|
|
@ -4,10 +4,31 @@
|
||||||
namespace Microsoft.AspNetCore.Antiforgery.Internal
|
namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used as a per request state.
|
/// Used to hold per-request state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AntiforgeryContext
|
public class AntiforgeryContext
|
||||||
{
|
{
|
||||||
|
public bool HaveDeserializedCookieToken { get; set; }
|
||||||
|
|
||||||
public AntiforgeryToken CookieToken { get; set; }
|
public AntiforgeryToken CookieToken { get; set; }
|
||||||
|
|
||||||
|
public bool HaveDeserializedRequestToken { get; set; }
|
||||||
|
|
||||||
|
public AntiforgeryToken RequestToken { get; set; }
|
||||||
|
|
||||||
|
public bool HaveGeneratedNewCookieToken { get; set; }
|
||||||
|
|
||||||
|
// After HaveGeneratedNewCookieToken is true, remains null if CookieToken is valid.
|
||||||
|
public AntiforgeryToken NewCookieToken { get; set; }
|
||||||
|
|
||||||
|
// After HaveGeneratedNewCookieToken is true, remains null if CookieToken is valid.
|
||||||
|
public string NewCookieTokenString { get; set; }
|
||||||
|
|
||||||
|
public AntiforgeryToken NewRequestToken { get; set; }
|
||||||
|
|
||||||
|
public string NewRequestTokenString { get; set; }
|
||||||
|
|
||||||
|
// Always false if NewCookieToken is null. Never store null cookie token or re-store cookie token from request.
|
||||||
|
public bool HaveStoredNewCookieToken { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
|
@ -46,10 +47,17 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
|
|
||||||
CheckSSLConfig(httpContext);
|
CheckSSLConfig(httpContext);
|
||||||
|
|
||||||
var tokenSet = GetTokensInternal(httpContext);
|
var antiforgeryContext = GetTokensInternal(httpContext);
|
||||||
if (tokenSet.IsNewCookieToken)
|
var tokenSet = Serialize(antiforgeryContext);
|
||||||
|
|
||||||
|
if (!antiforgeryContext.HaveStoredNewCookieToken && antiforgeryContext.NewCookieToken != null)
|
||||||
{
|
{
|
||||||
SaveCookieTokenAndHeader(httpContext, tokenSet.CookieToken);
|
// Serialize handles the new cookie token string.
|
||||||
|
Debug.Assert(antiforgeryContext.NewCookieTokenString != null);
|
||||||
|
|
||||||
|
SaveCookieTokenAndHeader(httpContext, antiforgeryContext.NewCookieTokenString);
|
||||||
|
antiforgeryContext.HaveStoredNewCookieToken = true;
|
||||||
|
|
||||||
_logger.NewCookieToken();
|
_logger.NewCookieToken();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -57,7 +65,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
_logger.ReusedCookieToken();
|
_logger.ReusedCookieToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Serialize(tokenSet);
|
return tokenSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -70,8 +78,8 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
|
|
||||||
CheckSSLConfig(httpContext);
|
CheckSSLConfig(httpContext);
|
||||||
|
|
||||||
var tokenSet = GetTokensInternal(httpContext);
|
var antiforgeryContext = GetTokensInternal(httpContext);
|
||||||
return Serialize(tokenSet);
|
return Serialize(antiforgeryContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -90,6 +98,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
_logger.MissingCookieToken(_options.CookieName);
|
_logger.MissingCookieToken(_options.CookieName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokens.RequestToken == null)
|
if (tokens.RequestToken == null)
|
||||||
{
|
{
|
||||||
_logger.MissingRequestToken(_options.FormFieldName, _options.HeaderName);
|
_logger.MissingRequestToken(_options.FormFieldName, _options.HeaderName);
|
||||||
|
|
@ -97,8 +106,9 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract cookie & request tokens
|
// Extract cookie & request tokens
|
||||||
var deserializedCookieToken = _tokenSerializer.Deserialize(tokens.CookieToken);
|
AntiforgeryToken deserializedCookieToken;
|
||||||
var deserializedRequestToken = _tokenSerializer.Deserialize(tokens.RequestToken);
|
AntiforgeryToken deserializedRequestToken;
|
||||||
|
DeserializeTokens(httpContext, tokens, out deserializedCookieToken, out deserializedRequestToken);
|
||||||
|
|
||||||
// Validate
|
// Validate
|
||||||
string message;
|
string message;
|
||||||
|
|
@ -165,30 +175,17 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
|
|
||||||
private void ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet)
|
private void ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet)
|
||||||
{
|
{
|
||||||
if (httpContext == null)
|
Debug.Assert(!string.IsNullOrEmpty(antiforgeryTokenSet.CookieToken));
|
||||||
{
|
Debug.Assert(!string.IsNullOrEmpty(antiforgeryTokenSet.RequestToken));
|
||||||
throw new ArgumentNullException(nameof(httpContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckSSLConfig(httpContext);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(antiforgeryTokenSet.CookieToken))
|
|
||||||
{
|
|
||||||
throw new ArgumentException(
|
|
||||||
Resources.Antiforgery_CookieToken_MustBeProvided_Generic,
|
|
||||||
nameof(antiforgeryTokenSet));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(antiforgeryTokenSet.RequestToken))
|
|
||||||
{
|
|
||||||
throw new ArgumentException(
|
|
||||||
Resources.Antiforgery_RequestToken_MustBeProvided_Generic,
|
|
||||||
nameof(antiforgeryTokenSet));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract cookie & request tokens
|
// Extract cookie & request tokens
|
||||||
var deserializedCookieToken = _tokenSerializer.Deserialize(antiforgeryTokenSet.CookieToken);
|
AntiforgeryToken deserializedCookieToken;
|
||||||
var deserializedRequestToken = _tokenSerializer.Deserialize(antiforgeryTokenSet.RequestToken);
|
AntiforgeryToken deserializedRequestToken;
|
||||||
|
DeserializeTokens(
|
||||||
|
httpContext,
|
||||||
|
antiforgeryTokenSet,
|
||||||
|
out deserializedCookieToken,
|
||||||
|
out deserializedRequestToken);
|
||||||
|
|
||||||
// Validate
|
// Validate
|
||||||
string message;
|
string message;
|
||||||
|
|
@ -212,38 +209,26 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
|
|
||||||
CheckSSLConfig(httpContext);
|
CheckSSLConfig(httpContext);
|
||||||
|
|
||||||
var cookieToken = GetCookieTokenDoesNotThrow(httpContext);
|
var antiforgeryContext = GetCookieTokens(httpContext);
|
||||||
cookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
|
if (!antiforgeryContext.HaveStoredNewCookieToken && antiforgeryContext.NewCookieToken != null)
|
||||||
SaveCookieTokenAndHeader(httpContext, cookieToken);
|
{
|
||||||
|
if (antiforgeryContext.NewCookieTokenString == null)
|
||||||
|
{
|
||||||
|
antiforgeryContext.NewCookieTokenString =
|
||||||
|
_tokenSerializer.Serialize(antiforgeryContext.NewCookieToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveCookieTokenAndHeader(httpContext, antiforgeryContext.NewCookieTokenString);
|
||||||
|
antiforgeryContext.HaveStoredNewCookieToken = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method returns null if oldCookieToken is valid.
|
private void SaveCookieTokenAndHeader(HttpContext httpContext, string cookieToken)
|
||||||
private AntiforgeryToken ValidateAndGenerateNewCookieToken(AntiforgeryToken cookieToken)
|
|
||||||
{
|
{
|
||||||
if (!_tokenGenerator.IsCookieTokenValid(cookieToken))
|
|
||||||
{
|
|
||||||
// Need to make sure we're always operating with a good cookie token.
|
|
||||||
var newCookieToken = _tokenGenerator.GenerateCookieToken();
|
|
||||||
Debug.Assert(_tokenGenerator.IsCookieTokenValid(newCookieToken));
|
|
||||||
return newCookieToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SaveCookieTokenAndHeader(
|
|
||||||
HttpContext context,
|
|
||||||
AntiforgeryToken cookieToken)
|
|
||||||
{
|
|
||||||
if (context == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cookieToken != null)
|
if (cookieToken != null)
|
||||||
{
|
{
|
||||||
// Persist the new cookie if it is not null.
|
// Persist the new cookie if it is not null.
|
||||||
_tokenStore.SaveCookieToken(context, cookieToken);
|
_tokenStore.SaveCookieToken(httpContext, cookieToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_options.SuppressXFrameOptionsHeader)
|
if (!_options.SuppressXFrameOptionsHeader)
|
||||||
|
|
@ -251,7 +236,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
// Adding X-Frame-Options header to prevent ClickJacking. See
|
// Adding X-Frame-Options header to prevent ClickJacking. See
|
||||||
// http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-10
|
// http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-10
|
||||||
// for more information.
|
// for more information.
|
||||||
context.Response.Headers["X-Frame-Options"] = "SAMEORIGIN";
|
httpContext.Response.Headers["X-Frame-Options"] = "SAMEORIGIN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -266,11 +251,64 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AntiforgeryToken GetCookieTokenDoesNotThrow(HttpContext context)
|
private AntiforgeryContext GetCookieTokens(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
var services = httpContext.RequestServices;
|
||||||
|
var contextAccessor = services.GetRequiredService<IAntiforgeryContextAccessor>();
|
||||||
|
if (contextAccessor.Value == null)
|
||||||
|
{
|
||||||
|
contextAccessor.Value = new AntiforgeryContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
var antiforgeryContext = contextAccessor.Value;
|
||||||
|
if (antiforgeryContext.HaveGeneratedNewCookieToken)
|
||||||
|
{
|
||||||
|
Debug.Assert(antiforgeryContext.HaveDeserializedCookieToken);
|
||||||
|
|
||||||
|
// Have executed this method earlier in the context of this request.
|
||||||
|
return antiforgeryContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
AntiforgeryToken cookieToken;
|
||||||
|
if (antiforgeryContext.HaveDeserializedCookieToken)
|
||||||
|
{
|
||||||
|
cookieToken = antiforgeryContext.CookieToken;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cookieToken = GetCookieTokenDoesNotThrow(httpContext);
|
||||||
|
|
||||||
|
antiforgeryContext.CookieToken = cookieToken;
|
||||||
|
antiforgeryContext.HaveDeserializedCookieToken = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AntiforgeryToken newCookieToken;
|
||||||
|
if (_tokenGenerator.IsCookieTokenValid(cookieToken))
|
||||||
|
{
|
||||||
|
// No need for the cookie token from the request after it has been verified.
|
||||||
|
newCookieToken = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Need to make sure we're always operating with a good cookie token.
|
||||||
|
newCookieToken = _tokenGenerator.GenerateCookieToken();
|
||||||
|
Debug.Assert(_tokenGenerator.IsCookieTokenValid(newCookieToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
antiforgeryContext.HaveGeneratedNewCookieToken = true;
|
||||||
|
antiforgeryContext.NewCookieToken = newCookieToken;
|
||||||
|
|
||||||
|
return antiforgeryContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AntiforgeryToken GetCookieTokenDoesNotThrow(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return _tokenStore.GetCookieToken(context);
|
var serializedToken = _tokenStore.GetCookieToken(httpContext);
|
||||||
|
var token = _tokenSerializer.Deserialize(serializedToken);
|
||||||
|
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
@ -279,43 +317,80 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AntiforgeryTokenSetInternal GetTokensInternal(HttpContext httpContext)
|
private AntiforgeryContext GetTokensInternal(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
var cookieToken = GetCookieTokenDoesNotThrow(httpContext);
|
var antiforgeryContext = GetCookieTokens(httpContext);
|
||||||
var newCookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
|
if (antiforgeryContext.NewRequestToken == null)
|
||||||
if (newCookieToken != null)
|
|
||||||
{
|
{
|
||||||
cookieToken = newCookieToken;
|
var cookieToken = antiforgeryContext.NewCookieToken ?? antiforgeryContext.CookieToken;
|
||||||
|
antiforgeryContext.NewRequestToken = _tokenGenerator.GenerateRequestToken(httpContext, cookieToken);
|
||||||
}
|
}
|
||||||
var requestToken = _tokenGenerator.GenerateRequestToken(
|
|
||||||
httpContext,
|
|
||||||
cookieToken);
|
|
||||||
|
|
||||||
return new AntiforgeryTokenSetInternal()
|
return antiforgeryContext;
|
||||||
{
|
|
||||||
// Note : The new cookie would be null if the old cookie is valid.
|
|
||||||
CookieToken = cookieToken,
|
|
||||||
RequestToken = requestToken,
|
|
||||||
IsNewCookieToken = newCookieToken != null
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AntiforgeryTokenSet Serialize(AntiforgeryTokenSetInternal tokenSet)
|
private AntiforgeryTokenSet Serialize(AntiforgeryContext antiforgeryContext)
|
||||||
{
|
{
|
||||||
|
// Should only be called after new tokens have been generated.
|
||||||
|
Debug.Assert(antiforgeryContext.HaveGeneratedNewCookieToken);
|
||||||
|
Debug.Assert(antiforgeryContext.NewRequestToken != null);
|
||||||
|
|
||||||
|
if (antiforgeryContext.NewRequestTokenString == null)
|
||||||
|
{
|
||||||
|
antiforgeryContext.NewRequestTokenString =
|
||||||
|
_tokenSerializer.Serialize(antiforgeryContext.NewRequestToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (antiforgeryContext.NewCookieTokenString == null && antiforgeryContext.NewCookieToken != null)
|
||||||
|
{
|
||||||
|
antiforgeryContext.NewCookieTokenString =
|
||||||
|
_tokenSerializer.Serialize(antiforgeryContext.NewCookieToken);
|
||||||
|
}
|
||||||
|
|
||||||
return new AntiforgeryTokenSet(
|
return new AntiforgeryTokenSet(
|
||||||
_tokenSerializer.Serialize(tokenSet.RequestToken),
|
antiforgeryContext.NewRequestTokenString,
|
||||||
_tokenSerializer.Serialize(tokenSet.CookieToken),
|
antiforgeryContext.NewCookieTokenString,
|
||||||
_options.FormFieldName,
|
_options.FormFieldName,
|
||||||
_options.HeaderName);
|
_options.HeaderName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AntiforgeryTokenSetInternal
|
private void DeserializeTokens(
|
||||||
|
HttpContext httpContext,
|
||||||
|
AntiforgeryTokenSet antiforgeryTokenSet,
|
||||||
|
out AntiforgeryToken cookieToken,
|
||||||
|
out AntiforgeryToken requestToken)
|
||||||
{
|
{
|
||||||
public AntiforgeryToken RequestToken { get; set; }
|
var services = httpContext.RequestServices;
|
||||||
|
var contextAccessor = services.GetRequiredService<IAntiforgeryContextAccessor>();
|
||||||
|
if (contextAccessor.Value == null)
|
||||||
|
{
|
||||||
|
contextAccessor.Value = new AntiforgeryContext();
|
||||||
|
}
|
||||||
|
|
||||||
public AntiforgeryToken CookieToken { get; set; }
|
var antiforgeryContext = contextAccessor.Value;
|
||||||
|
if (antiforgeryContext.HaveDeserializedCookieToken)
|
||||||
|
{
|
||||||
|
cookieToken = antiforgeryContext.CookieToken;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cookieToken = _tokenSerializer.Deserialize(antiforgeryTokenSet.CookieToken);
|
||||||
|
|
||||||
public bool IsNewCookieToken { get; set; }
|
antiforgeryContext.CookieToken = cookieToken;
|
||||||
|
antiforgeryContext.HaveDeserializedCookieToken = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (antiforgeryContext.HaveDeserializedRequestToken)
|
||||||
|
{
|
||||||
|
requestToken = antiforgeryContext.RequestToken;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requestToken = _tokenSerializer.Deserialize(antiforgeryTokenSet.RequestToken);
|
||||||
|
|
||||||
|
antiforgeryContext.RequestToken = requestToken;
|
||||||
|
antiforgeryContext.HaveDeserializedRequestToken = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,7 +5,6 @@ using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
|
|
@ -14,39 +13,20 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
public class DefaultAntiforgeryTokenStore : IAntiforgeryTokenStore
|
public class DefaultAntiforgeryTokenStore : IAntiforgeryTokenStore
|
||||||
{
|
{
|
||||||
private readonly AntiforgeryOptions _options;
|
private readonly AntiforgeryOptions _options;
|
||||||
private readonly IAntiforgeryTokenSerializer _tokenSerializer;
|
|
||||||
|
|
||||||
public DefaultAntiforgeryTokenStore(
|
public DefaultAntiforgeryTokenStore(IOptions<AntiforgeryOptions> optionsAccessor)
|
||||||
IOptions<AntiforgeryOptions> optionsAccessor,
|
|
||||||
IAntiforgeryTokenSerializer tokenSerializer)
|
|
||||||
{
|
{
|
||||||
if (optionsAccessor == null)
|
if (optionsAccessor == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(optionsAccessor));
|
throw new ArgumentNullException(nameof(optionsAccessor));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokenSerializer == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(tokenSerializer));
|
|
||||||
}
|
|
||||||
|
|
||||||
_options = optionsAccessor.Value;
|
_options = optionsAccessor.Value;
|
||||||
_tokenSerializer = tokenSerializer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AntiforgeryToken GetCookieToken(HttpContext httpContext)
|
public string GetCookieToken(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
if (httpContext == null)
|
Debug.Assert(httpContext != null);
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(httpContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
var services = httpContext.RequestServices;
|
|
||||||
var contextAccessor = services.GetRequiredService<IAntiforgeryContextAccessor>();
|
|
||||||
if (contextAccessor.Value != null)
|
|
||||||
{
|
|
||||||
return contextAccessor.Value.CookieToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
var requestCookie = httpContext.Request.Cookies[_options.CookieName];
|
var requestCookie = httpContext.Request.Cookies[_options.CookieName];
|
||||||
if (string.IsNullOrEmpty(requestCookie))
|
if (string.IsNullOrEmpty(requestCookie))
|
||||||
|
|
@ -55,15 +35,12 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _tokenSerializer.Deserialize(requestCookie);
|
return requestCookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AntiforgeryTokenSet> GetRequestTokensAsync(HttpContext httpContext)
|
public async Task<AntiforgeryTokenSet> GetRequestTokensAsync(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
if (httpContext == null)
|
Debug.Assert(httpContext != null);
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(httpContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
var cookieToken = httpContext.Request.Cookies[_options.CookieName];
|
var cookieToken = httpContext.Request.Cookies[_options.CookieName];
|
||||||
|
|
||||||
|
|
@ -85,27 +62,11 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
return new AntiforgeryTokenSet(requestToken, cookieToken, _options.FormFieldName, _options.HeaderName);
|
return new AntiforgeryTokenSet(requestToken, cookieToken, _options.FormFieldName, _options.HeaderName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveCookieToken(HttpContext httpContext, AntiforgeryToken token)
|
public void SaveCookieToken(HttpContext httpContext, string token)
|
||||||
{
|
{
|
||||||
if (httpContext == null)
|
Debug.Assert(httpContext != null);
|
||||||
{
|
Debug.Assert(token != null);
|
||||||
throw new ArgumentNullException(nameof(httpContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(token));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the cookie to the request based context.
|
|
||||||
// This is useful if the cookie needs to be reloaded in the context of the same request.
|
|
||||||
|
|
||||||
var services = httpContext.RequestServices;
|
|
||||||
var contextAccessor = services.GetRequiredService<IAntiforgeryContextAccessor>();
|
|
||||||
Debug.Assert(contextAccessor.Value == null, "AntiforgeryContext should be set only once per request.");
|
|
||||||
contextAccessor.Value = new AntiforgeryContext() { CookieToken = token };
|
|
||||||
|
|
||||||
var serializedToken = _tokenSerializer.Serialize(token);
|
|
||||||
var options = new CookieOptions() { HttpOnly = true };
|
var options = new CookieOptions() { HttpOnly = true };
|
||||||
|
|
||||||
// Note: don't use "newCookie.Secure = _options.RequireSSL;" since the default
|
// Note: don't use "newCookie.Secure = _options.RequireSSL;" since the default
|
||||||
|
|
@ -115,7 +76,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
options.Secure = true;
|
options.Secure = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
httpContext.Response.Cookies.Append(_options.CookieName, serializedToken, options);
|
httpContext.Response.Cookies.Append(_options.CookieName, token, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
{
|
{
|
||||||
public interface IAntiforgeryTokenStore
|
public interface IAntiforgeryTokenStore
|
||||||
{
|
{
|
||||||
AntiforgeryToken GetCookieToken(HttpContext httpContext);
|
string GetCookieToken(HttpContext httpContext);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the cookie and request tokens from the request.
|
/// Gets the cookie and request tokens from the request.
|
||||||
|
|
@ -17,6 +17,6 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
/// <returns>The <see cref="AntiforgeryTokenSet"/>.</returns>
|
/// <returns>The <see cref="AntiforgeryTokenSet"/>.</returns>
|
||||||
Task<AntiforgeryTokenSet> GetRequestTokensAsync(HttpContext httpContext);
|
Task<AntiforgeryTokenSet> GetRequestTokensAsync(HttpContext httpContext);
|
||||||
|
|
||||||
void SaveCookieToken(HttpContext httpContext, AntiforgeryToken token);
|
void SaveCookieToken(HttpContext httpContext, string token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -125,19 +125,30 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
public void GetTokens_ExistingInvalidCookieToken_GeneratesANewCookieTokenAndANewFormToken()
|
public void GetTokens_ExistingInvalidCookieToken_GeneratesANewCookieTokenAndANewFormToken()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var contextAccessor = new DefaultAntiforgeryContextAccessor();
|
||||||
// Generate a new cookie.
|
// Generate a new cookie.
|
||||||
var context = CreateMockContext(
|
var context = CreateMockContext(
|
||||||
new AntiforgeryOptions(),
|
new AntiforgeryOptions(),
|
||||||
useOldCookie: false,
|
useOldCookie: false,
|
||||||
isOldCookieValid: false);
|
isOldCookieValid: false,
|
||||||
|
contextAccessor: contextAccessor);
|
||||||
var antiforgery = GetAntiforgery(context);
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var tokenset = antiforgery.GetTokens(context.HttpContext);
|
var tokenset = antiforgery.GetTokens(context.HttpContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal("serialized-new-cookie-token", tokenset.CookieToken);
|
Assert.Equal(context.TestTokenSet.NewCookieTokenString, tokenset.CookieToken);
|
||||||
Assert.Equal("serialized-form-token", tokenset.RequestToken);
|
Assert.Equal(context.TestTokenSet.FormTokenString, tokenset.RequestToken);
|
||||||
|
|
||||||
|
Assert.NotNull(contextAccessor.Value);
|
||||||
|
Assert.True(contextAccessor.Value.HaveDeserializedCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.OldCookieToken, contextAccessor.Value.CookieToken);
|
||||||
|
Assert.True(contextAccessor.Value.HaveGeneratedNewCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.NewCookieToken, contextAccessor.Value.NewCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.NewCookieTokenString, contextAccessor.Value.NewCookieTokenString);
|
||||||
|
Assert.Equal(context.TestTokenSet.RequestToken, contextAccessor.Value.NewRequestToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.FormTokenString, contextAccessor.Value.NewRequestTokenString);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -150,10 +161,13 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
useOldCookie: false,
|
useOldCookie: false,
|
||||||
isOldCookieValid: false);
|
isOldCookieValid: false);
|
||||||
|
|
||||||
// This will cause the cookieToken to be null.
|
// Exception will cause the cookieToken to be null.
|
||||||
context.TokenSerializer
|
context.TokenSerializer
|
||||||
.Setup(o => o.Deserialize("serialized-old-cookie-token"))
|
.Setup(o => o.Deserialize(context.TestTokenSet.OldCookieTokenString))
|
||||||
.Throws(new Exception("should be swallowed"));
|
.Throws(new Exception("should be swallowed"));
|
||||||
|
context.TokenGenerator
|
||||||
|
.Setup(o => o.IsCookieTokenValid(null))
|
||||||
|
.Returns(false);
|
||||||
|
|
||||||
var antiforgery = GetAntiforgery(context);
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
|
@ -161,36 +175,88 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
var tokenset = antiforgery.GetTokens(context.HttpContext);
|
var tokenset = antiforgery.GetTokens(context.HttpContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal("serialized-new-cookie-token", tokenset.CookieToken);
|
Assert.Equal(context.TestTokenSet.NewCookieTokenString, tokenset.CookieToken);
|
||||||
Assert.Equal("serialized-form-token", tokenset.RequestToken);
|
Assert.Equal(context.TestTokenSet.FormTokenString, tokenset.RequestToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetTokens_ExistingValidCookieToken_GeneratesANewFormToken()
|
public void GetTokens_ExistingValidCookieToken_GeneratesANewFormToken()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var contextAccessor = new DefaultAntiforgeryContextAccessor();
|
||||||
var context = CreateMockContext(
|
var context = CreateMockContext(
|
||||||
new AntiforgeryOptions(),
|
new AntiforgeryOptions(),
|
||||||
useOldCookie: true,
|
useOldCookie: true,
|
||||||
isOldCookieValid: true);
|
isOldCookieValid: true,
|
||||||
|
contextAccessor: contextAccessor);
|
||||||
var antiforgery = GetAntiforgery(context);
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var tokenset = antiforgery.GetTokens(context.HttpContext);
|
var tokenset = antiforgery.GetTokens(context.HttpContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal("serialized-old-cookie-token", tokenset.CookieToken);
|
Assert.Null(tokenset.CookieToken);
|
||||||
Assert.Equal("serialized-form-token", tokenset.RequestToken);
|
Assert.Equal(context.TestTokenSet.FormTokenString, tokenset.RequestToken);
|
||||||
|
|
||||||
|
Assert.NotNull(contextAccessor.Value);
|
||||||
|
Assert.True(contextAccessor.Value.HaveDeserializedCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.OldCookieToken, contextAccessor.Value.CookieToken);
|
||||||
|
Assert.True(contextAccessor.Value.HaveGeneratedNewCookieToken);
|
||||||
|
Assert.Null(contextAccessor.Value.NewCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.RequestToken, contextAccessor.Value.NewRequestToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.FormTokenString, contextAccessor.Value.NewRequestTokenString);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTokens_DoesNotSerializeTwice()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var contextAccessor = new DefaultAntiforgeryContextAccessor
|
||||||
|
{
|
||||||
|
Value = new AntiforgeryContext
|
||||||
|
{
|
||||||
|
HaveDeserializedCookieToken = true,
|
||||||
|
HaveGeneratedNewCookieToken = true,
|
||||||
|
NewRequestToken = new AntiforgeryToken(),
|
||||||
|
NewRequestTokenString = "serialized-form-token-from-context",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
var context = CreateMockContext(
|
||||||
|
new AntiforgeryOptions(),
|
||||||
|
useOldCookie: true,
|
||||||
|
isOldCookieValid: true,
|
||||||
|
contextAccessor: contextAccessor);
|
||||||
|
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tokenset = antiforgery.GetTokens(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(tokenset.CookieToken);
|
||||||
|
Assert.Equal("serialized-form-token-from-context", tokenset.RequestToken);
|
||||||
|
|
||||||
|
Assert.Null(contextAccessor.Value.NewCookieToken);
|
||||||
|
|
||||||
|
// Token serializer not used.
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Deserialize(It.IsAny<string>()),
|
||||||
|
Times.Never);
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Serialize(It.IsAny<AntiforgeryToken>()),
|
||||||
|
Times.Never);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetAndStoreTokens_ExistingValidCookieToken_NotOverriden()
|
public void GetAndStoreTokens_ExistingValidCookieToken_NotOverriden()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var contextAccessor = new DefaultAntiforgeryContextAccessor();
|
||||||
var context = CreateMockContext(
|
var context = CreateMockContext(
|
||||||
new AntiforgeryOptions(),
|
new AntiforgeryOptions(),
|
||||||
useOldCookie: true,
|
useOldCookie: true,
|
||||||
isOldCookieValid: true);
|
isOldCookieValid: true,
|
||||||
|
contextAccessor: contextAccessor);
|
||||||
var antiforgery = GetAntiforgery(context);
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -199,20 +265,31 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
// Assert
|
// Assert
|
||||||
// We shouldn't have saved the cookie because it already existed.
|
// We shouldn't have saved the cookie because it already existed.
|
||||||
context.TokenStore.Verify(
|
context.TokenStore.Verify(
|
||||||
t => t.SaveCookieToken(It.IsAny<HttpContext>(), It.IsAny<AntiforgeryToken>()), Times.Never);
|
t => t.SaveCookieToken(It.IsAny<HttpContext>(), It.IsAny<string>()),
|
||||||
|
Times.Never);
|
||||||
|
|
||||||
Assert.Equal("serialized-old-cookie-token", tokenSet.CookieToken);
|
Assert.Null(tokenSet.CookieToken);
|
||||||
Assert.Equal("serialized-form-token", tokenSet.RequestToken);
|
Assert.Equal(context.TestTokenSet.FormTokenString, tokenSet.RequestToken);
|
||||||
|
|
||||||
|
Assert.NotNull(contextAccessor.Value);
|
||||||
|
Assert.True(contextAccessor.Value.HaveDeserializedCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.OldCookieToken, contextAccessor.Value.CookieToken);
|
||||||
|
Assert.True(contextAccessor.Value.HaveGeneratedNewCookieToken);
|
||||||
|
Assert.Null(contextAccessor.Value.NewCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.RequestToken, contextAccessor.Value.NewRequestToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.FormTokenString, contextAccessor.Value.NewRequestTokenString);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetAndStoreTokens_NoExistingCookieToken_Saved()
|
public void GetAndStoreTokens_NoExistingCookieToken_Saved()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var contextAccessor = new DefaultAntiforgeryContextAccessor();
|
||||||
var context = CreateMockContext(
|
var context = CreateMockContext(
|
||||||
new AntiforgeryOptions(),
|
new AntiforgeryOptions(),
|
||||||
useOldCookie: false,
|
useOldCookie: false,
|
||||||
isOldCookieValid: false);
|
isOldCookieValid: false,
|
||||||
|
contextAccessor: contextAccessor);
|
||||||
var antiforgery = GetAntiforgery(context);
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -220,17 +297,125 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
context.TokenStore.Verify(
|
context.TokenStore.Verify(
|
||||||
t => t.SaveCookieToken(It.IsAny<HttpContext>(), It.IsAny<AntiforgeryToken>()), Times.Once);
|
t => t.SaveCookieToken(It.IsAny<HttpContext>(), context.TestTokenSet.NewCookieTokenString),
|
||||||
|
Times.Once);
|
||||||
|
|
||||||
Assert.Equal("serialized-new-cookie-token", tokenSet.CookieToken);
|
Assert.Equal(context.TestTokenSet.NewCookieTokenString, tokenSet.CookieToken);
|
||||||
Assert.Equal("serialized-form-token", tokenSet.RequestToken);
|
Assert.Equal(context.TestTokenSet.FormTokenString, tokenSet.RequestToken);
|
||||||
|
|
||||||
|
Assert.NotNull(contextAccessor.Value);
|
||||||
|
Assert.True(contextAccessor.Value.HaveDeserializedCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.OldCookieToken, contextAccessor.Value.CookieToken);
|
||||||
|
Assert.True(contextAccessor.Value.HaveGeneratedNewCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.NewCookieToken, contextAccessor.Value.NewCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.NewCookieTokenString, contextAccessor.Value.NewCookieTokenString);
|
||||||
|
Assert.Equal(context.TestTokenSet.RequestToken, contextAccessor.Value.NewRequestToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.FormTokenString, contextAccessor.Value.NewRequestTokenString);
|
||||||
|
Assert.True(contextAccessor.Value.HaveStoredNewCookieToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetAndStoreTokens_DoesNotSerializeTwice()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var contextAccessor = new DefaultAntiforgeryContextAccessor
|
||||||
|
{
|
||||||
|
Value = new AntiforgeryContext
|
||||||
|
{
|
||||||
|
HaveDeserializedCookieToken = true,
|
||||||
|
HaveGeneratedNewCookieToken = true,
|
||||||
|
NewCookieToken = new AntiforgeryToken(),
|
||||||
|
NewCookieTokenString = "serialized-cookie-token-from-context",
|
||||||
|
NewRequestToken = new AntiforgeryToken(),
|
||||||
|
NewRequestTokenString = "serialized-form-token-from-context",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
var context = CreateMockContext(
|
||||||
|
new AntiforgeryOptions(),
|
||||||
|
useOldCookie: true,
|
||||||
|
isOldCookieValid: true,
|
||||||
|
contextAccessor: contextAccessor);
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
context.TokenStore
|
||||||
|
.Setup(t => t.SaveCookieToken(context.HttpContext, "serialized-cookie-token-from-context"))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tokenset = antiforgery.GetAndStoreTokens(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// Token store used once, with expected arguments.
|
||||||
|
// Passed context's cookie token though request's cookie token was valid.
|
||||||
|
context.TokenStore.Verify(
|
||||||
|
t => t.SaveCookieToken(context.HttpContext, "serialized-cookie-token-from-context"),
|
||||||
|
Times.Once);
|
||||||
|
|
||||||
|
// Token serializer not used.
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Deserialize(It.IsAny<string>()),
|
||||||
|
Times.Never);
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Serialize(It.IsAny<AntiforgeryToken>()),
|
||||||
|
Times.Never);
|
||||||
|
|
||||||
|
Assert.Equal("serialized-cookie-token-from-context", tokenset.CookieToken);
|
||||||
|
Assert.Equal("serialized-form-token-from-context", tokenset.RequestToken);
|
||||||
|
|
||||||
|
Assert.True(contextAccessor.Value.HaveStoredNewCookieToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetAndStoreTokens_DoesNotStoreTwice()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var contextAccessor = new DefaultAntiforgeryContextAccessor
|
||||||
|
{
|
||||||
|
Value = new AntiforgeryContext
|
||||||
|
{
|
||||||
|
HaveDeserializedCookieToken = true,
|
||||||
|
HaveGeneratedNewCookieToken = true,
|
||||||
|
HaveStoredNewCookieToken = true,
|
||||||
|
NewCookieToken = new AntiforgeryToken(),
|
||||||
|
NewCookieTokenString = "serialized-cookie-token-from-context",
|
||||||
|
NewRequestToken = new AntiforgeryToken(),
|
||||||
|
NewRequestTokenString = "serialized-form-token-from-context",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
var context = CreateMockContext(
|
||||||
|
new AntiforgeryOptions(),
|
||||||
|
useOldCookie: true,
|
||||||
|
isOldCookieValid: true,
|
||||||
|
contextAccessor: contextAccessor);
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tokenset = antiforgery.GetAndStoreTokens(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// Token store not used.
|
||||||
|
context.TokenStore.Verify(
|
||||||
|
t => t.SaveCookieToken(It.IsAny<HttpContext>(), It.IsAny<string>()),
|
||||||
|
Times.Never);
|
||||||
|
|
||||||
|
// Token serializer not used.
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Deserialize(It.IsAny<string>()),
|
||||||
|
Times.Never);
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Serialize(It.IsAny<AntiforgeryToken>()),
|
||||||
|
Times.Never);
|
||||||
|
|
||||||
|
Assert.Equal("serialized-cookie-token-from-context", tokenset.CookieToken);
|
||||||
|
Assert.Equal("serialized-form-token-from-context", tokenset.RequestToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task IsRequestValidAsync_FromStore_Failure()
|
public async Task IsRequestValidAsync_FromStore_Failure()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var context = CreateMockContext(new AntiforgeryOptions());
|
var contextAccessor = new DefaultAntiforgeryContextAccessor();
|
||||||
|
var context = CreateMockContext(new AntiforgeryOptions(), contextAccessor: contextAccessor);
|
||||||
|
|
||||||
string message;
|
string message;
|
||||||
context.TokenGenerator
|
context.TokenGenerator
|
||||||
|
|
@ -249,13 +434,21 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result);
|
Assert.False(result);
|
||||||
context.TokenGenerator.Verify();
|
context.TokenGenerator.Verify();
|
||||||
|
|
||||||
|
// Failed _after_ updating the AntiforgeryContext.
|
||||||
|
Assert.NotNull(contextAccessor.Value);
|
||||||
|
Assert.True(contextAccessor.Value.HaveDeserializedCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.OldCookieToken, contextAccessor.Value.CookieToken);
|
||||||
|
Assert.True(contextAccessor.Value.HaveDeserializedRequestToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.RequestToken, contextAccessor.Value.RequestToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task IsRequestValidAsync_FromStore_Success()
|
public async Task IsRequestValidAsync_FromStore_Success()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var context = CreateMockContext(new AntiforgeryOptions());
|
var contextAccessor = new DefaultAntiforgeryContextAccessor();
|
||||||
|
var context = CreateMockContext(new AntiforgeryOptions(), contextAccessor: contextAccessor);
|
||||||
|
|
||||||
string message;
|
string message;
|
||||||
context.TokenGenerator
|
context.TokenGenerator
|
||||||
|
|
@ -275,13 +468,64 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result);
|
Assert.True(result);
|
||||||
context.TokenGenerator.Verify();
|
context.TokenGenerator.Verify();
|
||||||
|
|
||||||
|
Assert.NotNull(contextAccessor.Value);
|
||||||
|
Assert.True(contextAccessor.Value.HaveDeserializedCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.OldCookieToken, contextAccessor.Value.CookieToken);
|
||||||
|
Assert.True(contextAccessor.Value.HaveDeserializedRequestToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.RequestToken, contextAccessor.Value.RequestToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IsRequestValidAsync_DoesNotDeserializeTwice()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var contextAccessor = new DefaultAntiforgeryContextAccessor
|
||||||
|
{
|
||||||
|
Value = new AntiforgeryContext
|
||||||
|
{
|
||||||
|
HaveDeserializedCookieToken = true,
|
||||||
|
CookieToken = new AntiforgeryToken(),
|
||||||
|
HaveDeserializedRequestToken = true,
|
||||||
|
RequestToken = new AntiforgeryToken(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
var context = CreateMockContext(new AntiforgeryOptions(), contextAccessor: contextAccessor);
|
||||||
|
|
||||||
|
string message;
|
||||||
|
context.TokenGenerator
|
||||||
|
.Setup(o => o.TryValidateTokenSet(
|
||||||
|
context.HttpContext,
|
||||||
|
contextAccessor.Value.CookieToken,
|
||||||
|
contextAccessor.Value.RequestToken,
|
||||||
|
out message))
|
||||||
|
.Returns(true)
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await antiforgery.IsRequestValidAsync(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
context.TokenGenerator.Verify();
|
||||||
|
|
||||||
|
// Token serializer not used.
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Deserialize(It.IsAny<string>()),
|
||||||
|
Times.Never);
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Serialize(It.IsAny<AntiforgeryToken>()),
|
||||||
|
Times.Never);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ValidateRequestAsync_FromStore_Failure()
|
public async Task ValidateRequestAsync_FromStore_Failure()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var context = CreateMockContext(new AntiforgeryOptions());
|
var contextAccessor = new DefaultAntiforgeryContextAccessor();
|
||||||
|
var context = CreateMockContext(new AntiforgeryOptions(), contextAccessor: contextAccessor);
|
||||||
|
|
||||||
var message = "my-message";
|
var message = "my-message";
|
||||||
context.TokenGenerator
|
context.TokenGenerator
|
||||||
|
|
@ -299,13 +543,21 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
() => antiforgery.ValidateRequestAsync(context.HttpContext));
|
() => antiforgery.ValidateRequestAsync(context.HttpContext));
|
||||||
Assert.Equal("my-message", exception.Message);
|
Assert.Equal("my-message", exception.Message);
|
||||||
context.TokenGenerator.Verify();
|
context.TokenGenerator.Verify();
|
||||||
|
|
||||||
|
// Failed _after_ updating the AntiforgeryContext.
|
||||||
|
Assert.NotNull(contextAccessor.Value);
|
||||||
|
Assert.True(contextAccessor.Value.HaveDeserializedCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.OldCookieToken, contextAccessor.Value.CookieToken);
|
||||||
|
Assert.True(contextAccessor.Value.HaveDeserializedRequestToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.RequestToken, contextAccessor.Value.RequestToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ValidateRequestAsync_FromStore_Success()
|
public async Task ValidateRequestAsync_FromStore_Success()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var context = CreateMockContext(new AntiforgeryOptions());
|
var contextAccessor = new DefaultAntiforgeryContextAccessor();
|
||||||
|
var context = CreateMockContext(new AntiforgeryOptions(), contextAccessor: contextAccessor);
|
||||||
|
|
||||||
string message;
|
string message;
|
||||||
context.TokenGenerator
|
context.TokenGenerator
|
||||||
|
|
@ -324,6 +576,12 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
context.TokenGenerator.Verify();
|
context.TokenGenerator.Verify();
|
||||||
|
|
||||||
|
Assert.NotNull(contextAccessor.Value);
|
||||||
|
Assert.True(contextAccessor.Value.HaveDeserializedCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.OldCookieToken, contextAccessor.Value.CookieToken);
|
||||||
|
Assert.True(contextAccessor.Value.HaveDeserializedRequestToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.RequestToken, contextAccessor.Value.RequestToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -429,6 +687,49 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
exception.Message);
|
exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ValidateRequestAsync_DoesNotDeserializeTwice()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var contextAccessor = new DefaultAntiforgeryContextAccessor
|
||||||
|
{
|
||||||
|
Value = new AntiforgeryContext
|
||||||
|
{
|
||||||
|
HaveDeserializedCookieToken = true,
|
||||||
|
CookieToken = new AntiforgeryToken(),
|
||||||
|
HaveDeserializedRequestToken = true,
|
||||||
|
RequestToken = new AntiforgeryToken(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
var context = CreateMockContext(new AntiforgeryOptions(), contextAccessor: contextAccessor);
|
||||||
|
|
||||||
|
string message;
|
||||||
|
context.TokenGenerator
|
||||||
|
.Setup(o => o.TryValidateTokenSet(
|
||||||
|
context.HttpContext,
|
||||||
|
contextAccessor.Value.CookieToken,
|
||||||
|
contextAccessor.Value.RequestToken,
|
||||||
|
out message))
|
||||||
|
.Returns(true)
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await antiforgery.ValidateRequestAsync(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert (does not throw)
|
||||||
|
context.TokenGenerator.Verify();
|
||||||
|
|
||||||
|
// Token serializer not used.
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Deserialize(It.IsAny<string>()),
|
||||||
|
Times.Never);
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Serialize(It.IsAny<AntiforgeryToken>()),
|
||||||
|
Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(false, "SAMEORIGIN")]
|
[InlineData(false, "SAMEORIGIN")]
|
||||||
[InlineData(true, null)]
|
[InlineData(true, null)]
|
||||||
|
|
@ -441,9 +742,14 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
{
|
{
|
||||||
SuppressXFrameOptionsHeader = suppressXFrameOptions
|
SuppressXFrameOptionsHeader = suppressXFrameOptions
|
||||||
};
|
};
|
||||||
|
var contextAccessor = new DefaultAntiforgeryContextAccessor();
|
||||||
|
|
||||||
// Generate a new cookie.
|
// Generate a new cookie.
|
||||||
var context = CreateMockContext(options, useOldCookie: false, isOldCookieValid: false);
|
var context = CreateMockContext(
|
||||||
|
options,
|
||||||
|
useOldCookie: false,
|
||||||
|
isOldCookieValid: false,
|
||||||
|
contextAccessor: contextAccessor);
|
||||||
var antiforgery = GetAntiforgery(context);
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -452,6 +758,102 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
// Assert
|
// Assert
|
||||||
var xFrameOptions = context.HttpContext.Response.Headers["X-Frame-Options"];
|
var xFrameOptions = context.HttpContext.Response.Headers["X-Frame-Options"];
|
||||||
Assert.Equal(expectedHeaderValue, xFrameOptions);
|
Assert.Equal(expectedHeaderValue, xFrameOptions);
|
||||||
|
|
||||||
|
Assert.NotNull(contextAccessor.Value);
|
||||||
|
Assert.True(contextAccessor.Value.HaveDeserializedCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.OldCookieToken, contextAccessor.Value.CookieToken);
|
||||||
|
Assert.True(contextAccessor.Value.HaveGeneratedNewCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.NewCookieToken, contextAccessor.Value.NewCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.NewCookieTokenString, contextAccessor.Value.NewCookieTokenString);
|
||||||
|
Assert.True(contextAccessor.Value.HaveStoredNewCookieToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetCookieTokenAndHeader_DoesNotDeserializeTwice()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var contextAccessor = new DefaultAntiforgeryContextAccessor
|
||||||
|
{
|
||||||
|
Value = new AntiforgeryContext
|
||||||
|
{
|
||||||
|
HaveDeserializedCookieToken = true,
|
||||||
|
HaveGeneratedNewCookieToken = true,
|
||||||
|
NewCookieToken = new AntiforgeryToken(),
|
||||||
|
NewCookieTokenString = "serialized-cookie-token-from-context",
|
||||||
|
NewRequestToken = new AntiforgeryToken(),
|
||||||
|
NewRequestTokenString = "serialized-form-token-from-context",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
var context = CreateMockContext(
|
||||||
|
new AntiforgeryOptions(),
|
||||||
|
useOldCookie: true,
|
||||||
|
isOldCookieValid: true,
|
||||||
|
contextAccessor: contextAccessor);
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
context.TokenStore
|
||||||
|
.Setup(t => t.SaveCookieToken(context.HttpContext, "serialized-cookie-token-from-context"))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
antiforgery.SetCookieTokenAndHeader(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// Token store used once, with expected arguments.
|
||||||
|
// Passed context's cookie token though request's cookie token was valid.
|
||||||
|
context.TokenStore.Verify(
|
||||||
|
t => t.SaveCookieToken(context.HttpContext, "serialized-cookie-token-from-context"),
|
||||||
|
Times.Once);
|
||||||
|
|
||||||
|
// Token serializer not used.
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Deserialize(It.IsAny<string>()),
|
||||||
|
Times.Never);
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Serialize(It.IsAny<AntiforgeryToken>()),
|
||||||
|
Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetCookieTokenAndHeader_DoesNotStoreTwice()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var contextAccessor = new DefaultAntiforgeryContextAccessor
|
||||||
|
{
|
||||||
|
Value = new AntiforgeryContext
|
||||||
|
{
|
||||||
|
HaveDeserializedCookieToken = true,
|
||||||
|
HaveGeneratedNewCookieToken = true,
|
||||||
|
HaveStoredNewCookieToken = true,
|
||||||
|
NewCookieToken = new AntiforgeryToken(),
|
||||||
|
NewCookieTokenString = "serialized-cookie-token-from-context",
|
||||||
|
NewRequestToken = new AntiforgeryToken(),
|
||||||
|
NewRequestTokenString = "serialized-form-token-from-context",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
var context = CreateMockContext(
|
||||||
|
new AntiforgeryOptions(),
|
||||||
|
useOldCookie: true,
|
||||||
|
isOldCookieValid: true,
|
||||||
|
contextAccessor: contextAccessor);
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
antiforgery.SetCookieTokenAndHeader(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// Token serializer not used.
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Deserialize(It.IsAny<string>()),
|
||||||
|
Times.Never);
|
||||||
|
context.TokenSerializer.Verify(
|
||||||
|
o => o.Serialize(It.IsAny<AntiforgeryToken>()),
|
||||||
|
Times.Never);
|
||||||
|
|
||||||
|
// Token store not used.
|
||||||
|
context.TokenStore.Verify(
|
||||||
|
t => t.SaveCookieToken(It.IsAny<HttpContext>(), It.IsAny<string>()),
|
||||||
|
Times.Never);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DefaultAntiforgery GetAntiforgery(
|
private DefaultAntiforgery GetAntiforgery(
|
||||||
|
|
@ -484,13 +886,20 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
return builder.BuildServiceProvider();
|
return builder.BuildServiceProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpContext GetHttpContext()
|
private HttpContext GetHttpContext(IAntiforgeryContextAccessor contextAccessor)
|
||||||
{
|
{
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
||||||
httpContext.RequestServices = GetServices();
|
httpContext.RequestServices = GetServices();
|
||||||
|
|
||||||
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity("some-auth"));
|
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity("some-auth"));
|
||||||
|
|
||||||
|
contextAccessor = contextAccessor ?? new DefaultAntiforgeryContextAccessor();
|
||||||
|
|
||||||
|
var serviceCollection = new ServiceCollection();
|
||||||
|
serviceCollection.AddSingleton<IAntiforgeryContextAccessor>(contextAccessor);
|
||||||
|
httpContext.RequestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
return httpContext;
|
return httpContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -509,24 +918,27 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
TestTokenSet testTokenSet,
|
TestTokenSet testTokenSet,
|
||||||
bool saveNewCookie = true)
|
bool saveNewCookie = true)
|
||||||
{
|
{
|
||||||
var oldCookieToken = testTokenSet.OldCookieToken;
|
var oldCookieToken = testTokenSet.OldCookieTokenString;
|
||||||
var formToken = testTokenSet.RequestToken;
|
var formToken = testTokenSet.FormTokenString;
|
||||||
var mockTokenStore = new Mock<IAntiforgeryTokenStore>(MockBehavior.Strict);
|
var mockTokenStore = new Mock<IAntiforgeryTokenStore>(MockBehavior.Strict);
|
||||||
mockTokenStore.Setup(o => o.GetCookieToken(context))
|
mockTokenStore
|
||||||
.Returns(oldCookieToken);
|
.Setup(o => o.GetCookieToken(context))
|
||||||
|
.Returns(oldCookieToken);
|
||||||
|
|
||||||
mockTokenStore.Setup(o => o.GetRequestTokensAsync(context))
|
mockTokenStore
|
||||||
.Returns(() => Task.FromResult(new AntiforgeryTokenSet(
|
.Setup(o => o.GetRequestTokensAsync(context))
|
||||||
testTokenSet.FormTokenString,
|
.Returns(() => Task.FromResult(new AntiforgeryTokenSet(
|
||||||
testTokenSet.OldCookieTokenString,
|
formToken,
|
||||||
"form",
|
oldCookieToken,
|
||||||
"header")));
|
"form",
|
||||||
|
"header")));
|
||||||
|
|
||||||
if (saveNewCookie)
|
if (saveNewCookie)
|
||||||
{
|
{
|
||||||
var newCookieToken = testTokenSet.NewCookieToken;
|
var newCookieToken = testTokenSet.NewCookieTokenString;
|
||||||
mockTokenStore.Setup(o => o.SaveCookieToken(context, newCookieToken))
|
mockTokenStore
|
||||||
.Verifiable();
|
.Setup(o => o.SaveCookieToken(context, newCookieToken))
|
||||||
|
.Verifiable();
|
||||||
}
|
}
|
||||||
|
|
||||||
return mockTokenStore;
|
return mockTokenStore;
|
||||||
|
|
@ -554,10 +966,11 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
private AntiforgeryMockContext CreateMockContext(
|
private AntiforgeryMockContext CreateMockContext(
|
||||||
AntiforgeryOptions options,
|
AntiforgeryOptions options,
|
||||||
bool useOldCookie = false,
|
bool useOldCookie = false,
|
||||||
bool isOldCookieValid = true)
|
bool isOldCookieValid = true,
|
||||||
|
IAntiforgeryContextAccessor contextAccessor = null)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var httpContext = GetHttpContext();
|
var httpContext = GetHttpContext(contextAccessor);
|
||||||
var testTokenSet = GetTokenSet();
|
var testTokenSet = GetTokenSet();
|
||||||
|
|
||||||
var mockSerializer = GetTokenSerializer(testTokenSet);
|
var mockSerializer = GetTokenSerializer(testTokenSet);
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Internal;
|
using Microsoft.AspNetCore.Http.Internal;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.ObjectPool;
|
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -17,8 +14,6 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
{
|
{
|
||||||
public class DefaultAntiforgeryTokenStoreTest
|
public class DefaultAntiforgeryTokenStoreTest
|
||||||
{
|
{
|
||||||
private static readonly ObjectPool<AntiforgerySerializationContext> _pool =
|
|
||||||
new DefaultObjectPoolProvider().Create(new AntiforgerySerializationContextPooledObjectPolicy());
|
|
||||||
private readonly string _cookieName = "cookie-name";
|
private readonly string _cookieName = "cookie-name";
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -31,9 +26,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
CookieName = _cookieName
|
CookieName = _cookieName
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokenStore = new DefaultAntiforgeryTokenStore(
|
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
|
||||||
optionsAccessor: new TestOptionsManager(options),
|
|
||||||
tokenSerializer: Mock.Of<IAntiforgeryTokenSerializer>());
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var token = tokenStore.GetCookieToken(httpContext);
|
var token = tokenStore.GetCookieToken(httpContext);
|
||||||
|
|
@ -42,33 +35,6 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
Assert.Null(token);
|
Assert.Null(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCookieToken_CookieIsMissingInRequest_LooksUpCookieInAntiforgeryContext()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var contextAccessor = new DefaultAntiforgeryContextAccessor();
|
|
||||||
var httpContext = GetHttpContext(_cookieName, string.Empty, contextAccessor);
|
|
||||||
|
|
||||||
// add a cookie explicitly.
|
|
||||||
var cookie = new AntiforgeryToken();
|
|
||||||
contextAccessor.Value = new AntiforgeryContext() { CookieToken = cookie };
|
|
||||||
|
|
||||||
var options = new AntiforgeryOptions
|
|
||||||
{
|
|
||||||
CookieName = _cookieName
|
|
||||||
};
|
|
||||||
|
|
||||||
var tokenStore = new DefaultAntiforgeryTokenStore(
|
|
||||||
optionsAccessor: new TestOptionsManager(options),
|
|
||||||
tokenSerializer: Mock.Of<IAntiforgeryTokenSerializer>());
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var token = tokenStore.GetCookieToken(httpContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(cookie, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetCookieToken_CookieIsEmpty_ReturnsNull()
|
public void GetCookieToken_CookieIsEmpty_ReturnsNull()
|
||||||
{
|
{
|
||||||
|
|
@ -79,9 +45,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
CookieName = _cookieName
|
CookieName = _cookieName
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokenStore = new DefaultAntiforgeryTokenStore(
|
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
|
||||||
optionsAccessor: new TestOptionsManager(options),
|
|
||||||
tokenSerializer: Mock.Of<IAntiforgeryTokenSerializer>());
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var token = tokenStore.GetCookieToken(httpContext);
|
var token = tokenStore.GetCookieToken(httpContext);
|
||||||
|
|
@ -91,57 +55,24 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetCookieToken_CookieIsInvalid_PropagatesException()
|
public void GetCookieToken_CookieIsNotEmpty_ReturnsToken()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var httpContext = GetHttpContext(_cookieName, "invalid-value");
|
var expectedToken = "valid-value";
|
||||||
|
var httpContext = GetHttpContext(_cookieName, expectedToken);
|
||||||
var expectedException = new AntiforgeryValidationException("some exception");
|
|
||||||
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>();
|
|
||||||
mockSerializer
|
|
||||||
.Setup(o => o.Deserialize("invalid-value"))
|
|
||||||
.Throws(expectedException);
|
|
||||||
|
|
||||||
var options = new AntiforgeryOptions()
|
var options = new AntiforgeryOptions()
|
||||||
{
|
{
|
||||||
CookieName = _cookieName
|
CookieName = _cookieName
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokenStore = new DefaultAntiforgeryTokenStore(
|
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
|
||||||
optionsAccessor: new TestOptionsManager(options),
|
|
||||||
tokenSerializer: mockSerializer.Object);
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<AntiforgeryValidationException>(() => tokenStore.GetCookieToken(httpContext));
|
|
||||||
Assert.Same(expectedException, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCookieToken_CookieIsValid_ReturnsToken()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expectedToken = new AntiforgeryToken();
|
|
||||||
var httpContext = GetHttpContext(_cookieName, "valid-value");
|
|
||||||
|
|
||||||
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>();
|
|
||||||
mockSerializer
|
|
||||||
.Setup(o => o.Deserialize("valid-value"))
|
|
||||||
.Returns(expectedToken);
|
|
||||||
|
|
||||||
var options = new AntiforgeryOptions()
|
|
||||||
{
|
|
||||||
CookieName = _cookieName
|
|
||||||
};
|
|
||||||
|
|
||||||
var tokenStore = new DefaultAntiforgeryTokenStore(
|
|
||||||
optionsAccessor: new TestOptionsManager(options),
|
|
||||||
tokenSerializer: mockSerializer.Object);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var token = tokenStore.GetCookieToken(httpContext);
|
var token = tokenStore.GetCookieToken(httpContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Same(expectedToken, token);
|
Assert.Equal(expectedToken, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -157,9 +88,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
FormFieldName = "form-field-name",
|
FormFieldName = "form-field-name",
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokenStore = new DefaultAntiforgeryTokenStore(
|
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
|
||||||
optionsAccessor: new TestOptionsManager(options),
|
|
||||||
tokenSerializer: Mock.Of<IAntiforgeryTokenSerializer>());
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext);
|
var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext);
|
||||||
|
|
@ -186,9 +115,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
HeaderName = null,
|
HeaderName = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokenStore = new DefaultAntiforgeryTokenStore(
|
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
|
||||||
optionsAccessor: new TestOptionsManager(options),
|
|
||||||
tokenSerializer: new DefaultAntiforgeryTokenSerializer(new EphemeralDataProtectionProvider(), _pool));
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext);
|
var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext);
|
||||||
|
|
@ -214,9 +141,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
HeaderName = "header-name",
|
HeaderName = "header-name",
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokenStore = new DefaultAntiforgeryTokenStore(
|
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
|
||||||
optionsAccessor: new TestOptionsManager(options),
|
|
||||||
tokenSerializer: new DefaultAntiforgeryTokenSerializer(new EphemeralDataProtectionProvider(), _pool));
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var tokens = await tokenStore.GetRequestTokensAsync(httpContext);
|
var tokens = await tokenStore.GetRequestTokensAsync(httpContext);
|
||||||
|
|
@ -244,9 +169,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
HeaderName = "header-name",
|
HeaderName = "header-name",
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokenStore = new DefaultAntiforgeryTokenStore(
|
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
|
||||||
optionsAccessor: new TestOptionsManager(options),
|
|
||||||
tokenSerializer: new DefaultAntiforgeryTokenSerializer(new EphemeralDataProtectionProvider(), _pool));
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var tokens = await tokenStore.GetRequestTokensAsync(httpContext);
|
var tokens = await tokenStore.GetRequestTokensAsync(httpContext);
|
||||||
|
|
@ -273,9 +196,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
HeaderName = "header-name",
|
HeaderName = "header-name",
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokenStore = new DefaultAntiforgeryTokenStore(
|
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
|
||||||
optionsAccessor: new TestOptionsManager(options),
|
|
||||||
tokenSerializer: new DefaultAntiforgeryTokenSerializer(new EphemeralDataProtectionProvider(), _pool));
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext);
|
var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext);
|
||||||
|
|
@ -300,9 +221,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
HeaderName = "header-name",
|
HeaderName = "header-name",
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokenStore = new DefaultAntiforgeryTokenStore(
|
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
|
||||||
optionsAccessor: new TestOptionsManager(options),
|
|
||||||
tokenSerializer: Mock.Of<IAntiforgeryTokenSerializer>());
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext);
|
var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext);
|
||||||
|
|
@ -331,9 +250,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
HeaderName = "header-name",
|
HeaderName = "header-name",
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokenStore = new DefaultAntiforgeryTokenStore(
|
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
|
||||||
optionsAccessor: new TestOptionsManager(options),
|
|
||||||
tokenSerializer: Mock.Of<IAntiforgeryTokenSerializer>());
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var tokens = await tokenStore.GetRequestTokensAsync(httpContext);
|
var tokens = await tokenStore.GetRequestTokensAsync(httpContext);
|
||||||
|
|
@ -349,7 +266,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
public void SaveCookieToken(bool requireSsl, bool? expectedCookieSecureFlag)
|
public void SaveCookieToken(bool requireSsl, bool? expectedCookieSecureFlag)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var token = new AntiforgeryToken();
|
var token = "serialized-value";
|
||||||
bool defaultCookieSecureValue = expectedCookieSecureFlag ?? false; // pulled from config; set by ctor
|
bool defaultCookieSecureValue = expectedCookieSecureFlag ?? false; // pulled from config; set by ctor
|
||||||
var cookies = new MockResponseCookieCollection();
|
var cookies = new MockResponseCookieCollection();
|
||||||
|
|
||||||
|
|
@ -358,32 +275,19 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
.Setup(o => o.Response.Cookies)
|
.Setup(o => o.Response.Cookies)
|
||||||
.Returns(cookies);
|
.Returns(cookies);
|
||||||
|
|
||||||
var contextAccessor = new DefaultAntiforgeryContextAccessor();
|
|
||||||
mockHttpContext
|
|
||||||
.SetupGet(o => o.RequestServices)
|
|
||||||
.Returns(GetServiceProvider(contextAccessor));
|
|
||||||
|
|
||||||
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>();
|
|
||||||
mockSerializer
|
|
||||||
.Setup(o => o.Serialize(token))
|
|
||||||
.Returns("serialized-value");
|
|
||||||
|
|
||||||
var options = new AntiforgeryOptions()
|
var options = new AntiforgeryOptions()
|
||||||
{
|
{
|
||||||
CookieName = _cookieName,
|
CookieName = _cookieName,
|
||||||
RequireSsl = requireSsl
|
RequireSsl = requireSsl
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokenStore = new DefaultAntiforgeryTokenStore(
|
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
|
||||||
optionsAccessor: new TestOptionsManager(options),
|
|
||||||
tokenSerializer: mockSerializer.Object);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
tokenStore.SaveCookieToken(mockHttpContext.Object, token);
|
tokenStore.SaveCookieToken(mockHttpContext.Object, token);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(1, cookies.Count);
|
Assert.Equal(1, cookies.Count);
|
||||||
Assert.NotNull(contextAccessor.Value.CookieToken);
|
|
||||||
Assert.NotNull(cookies);
|
Assert.NotNull(cookies);
|
||||||
Assert.Equal(_cookieName, cookies.Key);
|
Assert.Equal(_cookieName, cookies.Key);
|
||||||
Assert.Equal("serialized-value", cookies.Value);
|
Assert.Equal("serialized-value", cookies.Value);
|
||||||
|
|
@ -391,39 +295,24 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
Assert.Equal(defaultCookieSecureValue, cookies.Options.Secure);
|
Assert.Equal(defaultCookieSecureValue, cookies.Options.Secure);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpContext GetHttpContext(
|
private HttpContext GetHttpContext(string cookieName, string cookieValue)
|
||||||
string cookieName,
|
|
||||||
string cookieValue,
|
|
||||||
IAntiforgeryContextAccessor contextAccessor = null)
|
|
||||||
{
|
{
|
||||||
var cookies = new RequestCookieCollection(new Dictionary<string, string>
|
var cookies = new RequestCookieCollection(new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
{ cookieName, cookieValue },
|
{ cookieName, cookieValue },
|
||||||
});
|
});
|
||||||
|
|
||||||
return GetHttpContext(cookies, contextAccessor);
|
return GetHttpContext(cookies);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpContext GetHttpContext(
|
private HttpContext GetHttpContext(IRequestCookieCollection cookies)
|
||||||
IRequestCookieCollection cookies,
|
|
||||||
IAntiforgeryContextAccessor contextAccessor = null)
|
|
||||||
{
|
{
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
httpContext.Request.Cookies = cookies;
|
httpContext.Request.Cookies = cookies;
|
||||||
|
|
||||||
contextAccessor = contextAccessor ?? new DefaultAntiforgeryContextAccessor();
|
|
||||||
httpContext.RequestServices = GetServiceProvider(contextAccessor);
|
|
||||||
|
|
||||||
return httpContext;
|
return httpContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IServiceProvider GetServiceProvider(IAntiforgeryContextAccessor contextAccessor)
|
|
||||||
{
|
|
||||||
var serviceCollection = new ServiceCollection();
|
|
||||||
serviceCollection.AddSingleton(contextAccessor);
|
|
||||||
return serviceCollection.BuildServiceProvider();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MockResponseCookieCollection : IResponseCookies
|
private class MockResponseCookieCollection : IResponseCookies
|
||||||
{
|
{
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue